From 281b7301c8d97051e28608789ce07a5d505e476c Mon Sep 17 00:00:00 2001 From: Bobby Date: Thu, 12 Dec 2024 13:49:07 -0500 Subject: more changes to home page --- static/css/en/login-area.css | 8 +- static/css/ja/login-area.css | 8 +- static/css/shared/core.css | 112 ++++- static/css/shared/left-sidebar.css | 50 ++ static/css/shared/login-area.css | 3 +- static/fonts/SWEETFAIRY.otf | Bin 0 -> 22032 bytes static/fonts/THEHEART.ttf | Bin 0 -> 746068 bytes static/fonts/YOMOGI.ttf | Bin 0 -> 4037996 bytes static/images/core/icons/changelanguage.png | Bin 0 -> 1850 bytes static/images/core/icons/home.png | Bin 0 -> 828 bytes .../images/core/icons/journalofrandomthoughts.png | Bin 0 -> 563 bytes static/images/core/icons/shrines.png | Bin 0 -> 1586 bytes static/images/core/icons/socialify.png | Bin 0 -> 1973 bytes static/images/core/icons/weblog.gif | Bin 0 -> 356 bytes static/images/site/icons/pencil.gif | Bin 356 -> 0 bytes static/js/musicPlayer.js | 540 --------------------- static/js/shared/headerEasterEggs.js | 103 ++++ static/js/shared/kawaiiBeatsPlayer.js | 539 ++++++++++++++++++++ static/js/youdontdare.js | 103 ---- 19 files changed, 797 insertions(+), 669 deletions(-) create mode 100644 static/css/shared/left-sidebar.css create mode 100644 static/fonts/SWEETFAIRY.otf create mode 100644 static/fonts/THEHEART.ttf create mode 100644 static/fonts/YOMOGI.ttf create mode 100644 static/images/core/icons/changelanguage.png create mode 100644 static/images/core/icons/home.png create mode 100644 static/images/core/icons/journalofrandomthoughts.png create mode 100644 static/images/core/icons/shrines.png create mode 100644 static/images/core/icons/socialify.png create mode 100644 static/images/core/icons/weblog.gif delete mode 100644 static/images/site/icons/pencil.gif delete mode 100644 static/js/musicPlayer.js create mode 100644 static/js/shared/headerEasterEggs.js create mode 100644 static/js/shared/kawaiiBeatsPlayer.js delete mode 100644 static/js/youdontdare.js (limited to 'static') diff --git a/static/css/en/login-area.css b/static/css/en/login-area.css index c339605f..1e18972e 100644 --- a/static/css/en/login-area.css +++ b/static/css/en/login-area.css @@ -1,10 +1,6 @@ #login-area { - background-image: -webkit-image-set(url("../../images/core/sidebar/login-area@2x.png") 1x, - url("../../images/core/sidebar/login-area@2x.png") 2x, - url("../../images/core/sidebar/login-area@3x.png") 3x); - background-image: image-set(url("../../images/core/sidebar/login-area@2x.png") 1x, - url("../../images/core/sidebar/login-area@2x.png") 2x, - url("../../images/core/sidebar/login-area@3x.png") 3x); + background-image: url("../../images/core/sidebar/login-area@2x.png"); + background-image: url("../../images/core/sidebar/login-area@2x.png"); } #login-area>#register-now-button { diff --git a/static/css/ja/login-area.css b/static/css/ja/login-area.css index 4404eada..475b239b 100644 --- a/static/css/ja/login-area.css +++ b/static/css/ja/login-area.css @@ -1,10 +1,6 @@ #login-area { - background-image: -webkit-image-set(url("../../images/core/sidebar/login-area_ja@2x.png") 1x, - url("../../images/core/sidebar/login-area_ja@2x.png") 2x, - url("../../images/core/sidebar/login-area_ja@3x.png") 3x); - background-image: image-set(url("../../images/core/sidebar/login-area_ja@2x.png") 1x, - url("../../images/core/sidebar/login-area_ja@2x.png") 2x, - url("../../images/core/sidebar/login-area_ja@3x.png") 3x); + background-image: url("../../images/core/sidebar/login-area_ja@3x.png"); + background-image: url("../../images/core/sidebar/login-area_ja@3x.png"); } #login-area>#register-now-button { diff --git a/static/css/shared/core.css b/static/css/shared/core.css index 884ddbbf..06e571f3 100644 --- a/static/css/shared/core.css +++ b/static/css/shared/core.css @@ -1,24 +1,46 @@ /* Reset and Base Styles */ +@font-face { + font-family: 'TheHeart'; + src: url('../../fonts/THEHEART.ttf') format('truetype'); + font-weight: normal; + font-style: normal; +} + +@font-face { + font-family: 'Yomogi'; + src: url('../../fonts/YOMOGI.ttf') format('truetype'); + font-weight: normal; + font-style: normal; +} + * { margin: 0; padding: 0; box-sizing: border-box; - font-family: "Mali", sans-serif; - font-size: 11px; outline: none; border: none; -} - -/* Root Variables */ -:root { - --resolution-multiplier: 1; + color: #fff; } /* HTML and Body Base */ html { background-color: #000; color: #fff; - min-width: 1024px; + min-width: 1200px; +} + +html[lang='en'], +html[lang='en'] input, +.en { + font-family: 'TheHeart', sans-serif; + font-size: 14px; +} + +html[lang='ja'], +html[lang='ja'] input, +.ja { + font-family: 'Yomogi', sans-serif; + font-size: 12px; } body { @@ -33,10 +55,23 @@ hr { background: #fff; } -a:visited { +a { + color: #fff; + text-decoration: none; +} + +a:link, +a:visited, +a:hover, +a:active { color: #fff; } +a:hover { + text-decoration: underline; +} + +/* Image Styles */ img { -webkit-backface-visibility: hidden; backface-visibility: hidden; @@ -46,6 +81,16 @@ img { image-rendering: crisp-edges; } +#video-background { + position: fixed; + right: 0; + bottom: 0; + min-width: 100%; + min-height: 100%; + z-index: -100; + object-fit: cover; +} + /* Video Overlay */ #video-overlay { position: fixed; @@ -54,7 +99,7 @@ img { width: 100%; height: 100%; background-color: #000; - opacity: 0.35; + opacity: 0.5; z-index: -99; } @@ -62,7 +107,7 @@ img { #body-wrapper { position: relative; z-index: 1; - width: 1000px; + width: 1200px; margin: 0 auto; } @@ -70,6 +115,44 @@ img { display: flex; } +/* SCANLINES */ +#scanlines { + position: fixed; + left: 0; + top: 0; + width: 100vw; + height: 100vh; + pointer-events: none; + z-index: 300; + opacity: 0.18; +} + +#scanlines:before { + content: ""; + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + pointer-events: none; + background: linear-gradient(to bottom, transparent 50%, rgba(0, 0, 0, .4) 50%); + background-size: 100% 4px; + will-change: background, background-size; + animation: scanlines 0.2s linear infinite; +} + +@keyframes scanlines { + from { + background: linear-gradient(to bottom, transparent 10%, rgba(0, 0, 0, .4) 50%); + background-size: 100% 4px; + } + + to { + background: linear-gradient(to bottom, rgba(0, 0, 0, .4) 50%, transparent 50%); + background-size: 100% 4px; + } +} + /* Sidebar and Main Content */ #left-sidebar { width: 200px; @@ -77,8 +160,13 @@ img { border-top-left-radius: 14px; } +.left-sidebar { + position: relative; + top: -65px; +} + #main-content { - width: 580px; + width: 780px; padding: 0 20px; border-top: 1px solid #fff; } diff --git a/static/css/shared/left-sidebar.css b/static/css/shared/left-sidebar.css new file mode 100644 index 00000000..a962b239 --- /dev/null +++ b/static/css/shared/left-sidebar.css @@ -0,0 +1,50 @@ +@font-face { + font-family: 'SweetFairy'; + src: url('../../fonts/SWEETFAIRY.otf') format('opentype'); + font-weight: bold; + font-style: normal; +} + +/* Navigation Links */ +.navigation-links { + margin: 24px 0px; +} + +.navigation-title { + font-family: 'SweetFairy', sans-serif; + filter: drop-shadow(2px 0 0 white) drop-shadow(0 2px 0 white) drop-shadow(-2px 0 0 white) drop-shadow(0 -2px 0 white) drop-shadow(0px 1px 1px #623795) drop-shadow(0px 1px 1px #623795); + color: #623795; +} + +.navigation-title-container { + background-color: #311B4F; + padding: 10px; + border-top-left-radius: 8px; + border-top-right-radius: 8px; +} + +html[lang='en'] .navigation-title { + font-size: 18px; +} + +html[lang='ja'] .navigation-title { + font-size: 14px; +} + +.navigation-item { + display: flex; + align-items: center; + margin: 4px 0px; +} + +.navigation-items-container { + background-color: #f4f1e90f; + padding: 10px; + border-bottom-left-radius: 8px; + border-bottom-right-radius: 8px; +} + +.navigation-item img { + width: 20px; + margin-right: 8px; +} \ No newline at end of file diff --git a/static/css/shared/login-area.css b/static/css/shared/login-area.css index 680e95c3..f0b90d47 100644 --- a/static/css/shared/login-area.css +++ b/static/css/shared/login-area.css @@ -3,7 +3,6 @@ width: 200px; height: 280px; position: relative; - top: -65px; margin: auto; background-repeat: no-repeat; background-size: 200px 280px; @@ -24,7 +23,7 @@ width: 144px; margin: 10px auto; padding: 4px 8px; - font-size: 11px; + font-size: 16px; font-weight: bold; color: #4a2e6f; background: transparent; diff --git a/static/fonts/SWEETFAIRY.otf b/static/fonts/SWEETFAIRY.otf new file mode 100644 index 00000000..15c24f45 Binary files /dev/null and b/static/fonts/SWEETFAIRY.otf differ diff --git a/static/fonts/THEHEART.ttf b/static/fonts/THEHEART.ttf new file mode 100644 index 00000000..a3612b9b Binary files /dev/null and b/static/fonts/THEHEART.ttf differ diff --git a/static/fonts/YOMOGI.ttf b/static/fonts/YOMOGI.ttf new file mode 100644 index 00000000..3c5091ad Binary files /dev/null and b/static/fonts/YOMOGI.ttf differ diff --git a/static/images/core/icons/changelanguage.png b/static/images/core/icons/changelanguage.png new file mode 100644 index 00000000..d2f4df83 Binary files /dev/null and b/static/images/core/icons/changelanguage.png differ diff --git a/static/images/core/icons/home.png b/static/images/core/icons/home.png new file mode 100644 index 00000000..0bfbd03c Binary files /dev/null and b/static/images/core/icons/home.png differ diff --git a/static/images/core/icons/journalofrandomthoughts.png b/static/images/core/icons/journalofrandomthoughts.png new file mode 100644 index 00000000..845863a2 Binary files /dev/null and b/static/images/core/icons/journalofrandomthoughts.png differ diff --git a/static/images/core/icons/shrines.png b/static/images/core/icons/shrines.png new file mode 100644 index 00000000..f0000eba Binary files /dev/null and b/static/images/core/icons/shrines.png differ diff --git a/static/images/core/icons/socialify.png b/static/images/core/icons/socialify.png new file mode 100644 index 00000000..8c8e7945 Binary files /dev/null and b/static/images/core/icons/socialify.png differ diff --git a/static/images/core/icons/weblog.gif b/static/images/core/icons/weblog.gif new file mode 100644 index 00000000..53bb81db Binary files /dev/null and b/static/images/core/icons/weblog.gif differ diff --git a/static/images/site/icons/pencil.gif b/static/images/site/icons/pencil.gif deleted file mode 100644 index 53bb81db..00000000 Binary files a/static/images/site/icons/pencil.gif and /dev/null differ diff --git a/static/js/musicPlayer.js b/static/js/musicPlayer.js deleted file mode 100644 index bf1586b9..00000000 --- a/static/js/musicPlayer.js +++ /dev/null @@ -1,540 +0,0 @@ -// Collection of random anime artwork -const artworkCollection = [ - 'https://i.pinimg.com/enabled/564x/e2/5d/31/e25d3199f73c9453035727f8c7a70170.jpg', - 'https://i.pinimg.com/enabled/564x/5f/ed/28/5fed282cff8d22ac857e2a489031d05a.jpg', - 'https://i.pinimg.com/736x/05/a8/71/05a87162a78e2cad2ffe0a9eac6b4e2c.jpg', - 'https://i.pinimg.com/736x/c6/ac/13/c6ac139ed02c9accd34dbb16d7466025.jpg', - 'https://i.pinimg.com/736x/72/69/c3/7269c3d939764b024da9a6869dc59a0f.jpg', - 'https://i.pinimg.com/enabled/564x/cc/5c/6f/cc5c6f1c8e053d791ae2b4300ef5c9fe.jpg', - 'https://i.pinimg.com/enabled/564x/fa/0a/c2/fa0ac2b7145af1205c87350f7c735683.jpg', - 'https://i.pinimg.com/enabled/564x/94/84/57/9484579fcbf7e768d6206b07fa44c2b9.jpg', - 'https://i.pinimg.com/enabled/564x/fd/30/bf/fd30bf62f6409129ce6538f1b9ed7b8b.jpg', - 'https://i.pinimg.com/enabled/564x/44/b2/11/44b21104b4e41736c99ee183127aab3d.jpg', - 'https://i.pinimg.com/enabled/564x/f7/ce/56/f7ce5629aa91866020a559ef7e249f1c.jpg', - 'https://i.pinimg.com/enabled/564x/7b/ac/36/7bac368ff9b5f702d9b727491f8d4ef0.jpg', - 'https://i.pinimg.com/enabled/564x/39/1a/51/391a514a013f62ca9f25f47b4cbd7776.jpg', - 'https://i.pinimg.com/enabled/564x/03/d2/96/03d2967de5d249f88155cab461e69f3a.jpg' -]; - -// Constants -const SEEKBAR_CONFIG = { - HEIGHT: 4, - THUMB_RADIUS: 6, - HOVER_RADIUS: 8, - COLORS: { - BASE: 'rgba(255, 255, 255, 0.5)', - PROGRESS: 'rgba(255, 255, 255, 1)', - HOVER: 'rgba(255, 255, 255, 0.8)' - } -}; - -const STORE_LIMIT = artworkCollection.length; -const MIN_SCREEN_WIDTH = 1600; - -// Audio Context and Core Variables -let audioContext; -let sourceNode; -let analyzerNode; -let audioBuffer; -let startTime; -let pauseTime = 0; -let isPlaying = false; -let currentSong = null; -let isLoading = true; -let isDragging = false; - -// DOM Elements -const elements = { - playButton: document.getElementById('song-play'), - prevButton: document.getElementById('song-prev'), - nextButton: document.getElementById('song-next'), - timeElapsed: document.getElementById('song-time-elapsed'), - timeTotal: document.getElementById('song-time-total'), - songCover: document.getElementById('song-cover'), - songTitle: document.getElementById('song-title'), - songArtistAlbum: document.getElementById('song-artist-album'), - visualizer: document.getElementById('song-visualizer') -}; - -// Create and setup seekbar -const seekbarCanvas = document.createElement('canvas'); -seekbarCanvas.id = 'custom-seekbar'; -seekbarCanvas.width = 140; -seekbarCanvas.height = 20; -seekbarCanvas.style.cssText = 'position: absolute; left: 30px; top: 220px; cursor: pointer; z-index: 1;'; -document.getElementById('song-time').parentNode.insertBefore(seekbarCanvas, document.getElementById('song-time')); - -class ArtworkManager { - constructor() { - this.usedArtwork = new Set(); - this.availableArtwork = [...artworkCollection]; - } - - getRandomArtwork() { - // If all artwork has been used, reset the pool - if (this.availableArtwork.length === 0) { - this.resetArtworkPool(); - } - - // Get random artwork from available pool - const randomIndex = Math.floor(Math.random() * this.availableArtwork.length); - const artwork = this.availableArtwork[randomIndex]; - - // Remove from available pool and add to used set - this.availableArtwork.splice(randomIndex, 1); - this.usedArtwork.add(artwork); - - return artwork; - } - - resetArtworkPool() { - this.availableArtwork = [...artworkCollection]; - this.usedArtwork.clear(); - } - - releaseArtwork(artwork) { - if (this.usedArtwork.has(artwork)) { - this.usedArtwork.delete(artwork); - this.availableArtwork.push(artwork); - } - } -} - -// Song Store Management -class SongStore { - constructor() { - this.songs = JSON.parse(localStorage.getItem('songStore')) || []; - this.currentIndex = parseInt(localStorage.getItem('currentSongIndex')) || -1; - this.artworkCache = JSON.parse(localStorage.getItem('artworkCache')) || {}; - this.artworkManager = new ArtworkManager(); - - // Restore artwork state - Object.values(this.artworkCache).forEach(artwork => { - this.artworkManager.usedArtwork.add(artwork); - }); - - // Remove any artwork from availableArtwork that's already in use - this.artworkManager.availableArtwork = this.artworkManager.availableArtwork - .filter(artwork => !this.artworkManager.usedArtwork.has(artwork)); - } - - async addSong(song) { - if (!song) return null; - - // Generate unique artwork for the song - const artwork = this.artworkManager.getRandomArtwork(); - this.artworkCache[song.id] = artwork; - - // Always add the song, even if it's a duplicate - this.songs.push({ - id: song.id, - title: song.title, - artist: song.artist, - album: song.album - }); - - // Maintain the store limit - if (this.songs.length > STORE_LIMIT) { - const removedSong = this.songs.shift(); - // Release the artwork back to the pool - const removedArtwork = this.artworkCache[removedSong.id]; - this.artworkManager.releaseArtwork(removedArtwork); - delete this.artworkCache[removedSong.id]; - if (this.currentIndex > -1) this.currentIndex--; - } - - this.currentIndex = this.songs.length - 1; - this.save(); - - return { - ...this.songs[this.currentIndex], - artwork: this.artworkCache[song.id] - }; - } - - async getNext() { - if (this.currentIndex < this.songs.length - 1) { - this.currentIndex++; - this.save(); - return { - ...this.songs[this.currentIndex], - artwork: this.artworkCache[this.songs[this.currentIndex].id] - }; - } - // Get new song if we're at the end - const newSong = await this.fetchNewSong(); - return this.addSong(newSong); - } - - async getPrevious() { - if (this.currentIndex > 0) { - this.currentIndex--; - this.save(); - return { - ...this.songs[this.currentIndex], - artwork: this.artworkCache[this.songs[this.currentIndex].id] - }; - } - return null; - } - - async fetchNewSong() { - try { - const response = await fetch('/stream/random-song'); - const data = await response.json(); - return data.song; - } catch (error) { - console.error('Error fetching new song:', error); - return null; - } - } - - save() { - localStorage.setItem('songStore', JSON.stringify(this.songs)); - localStorage.setItem('currentSongIndex', this.currentIndex.toString()); - localStorage.setItem('artworkCache', JSON.stringify(this.artworkCache)); - } - - getCurrentSong() { - if (this.currentIndex >= 0) { - const song = this.songs[this.currentIndex]; - return { - ...song, - artwork: this.artworkCache[song.id] - }; - } - return null; - } - - getArtwork(songId) { - return this.artworkCache[songId]; - } -} - -const songStore = new SongStore(); - -// Audio Control Functions -async function initAudio() { - audioContext = new (window.AudioContext || window.webkitAudioContext)(); - analyzerNode = audioContext.createAnalyser(); - analyzerNode.fftSize = 256; - analyzerNode.connect(audioContext.destination); -} - -function formatTime(time) { - const minutes = Math.floor(time / 60); - const seconds = Math.floor(time % 60).toString().padStart(2, '0'); - return `${minutes}:${seconds}`; -} - -async function fetchAudio(url) { - isLoading = true; - updateControls(); - try { - const response = await fetch(url); - const arrayBuffer = await response.arrayBuffer(); - audioBuffer = await audioContext.decodeAudioData(arrayBuffer); - elements.timeTotal.textContent = formatTime(audioBuffer.duration); - isLoading = false; - updateControls(); - } catch (error) { - console.error('Error loading audio:', error); - isLoading = false; - updateControls(); - } -} - -function playAudio(offset = 0) { - if (!audioBuffer) return; - if (isPlaying) stopAudio(); - - offset = Math.min(Math.max(0, offset), audioBuffer.duration); - - sourceNode = audioContext.createBufferSource(); - sourceNode.buffer = audioBuffer; - sourceNode.connect(analyzerNode); - - sourceNode.onended = async () => { - const currentTime = audioContext.currentTime - startTime; - if (currentTime >= audioBuffer.duration - 0.1) { - await loadNewSong(true, 'next'); - } - }; - - sourceNode.start(0, offset); - startTime = audioContext.currentTime - offset; - isPlaying = true; - updateUI(); -} - -function stopAudio() { - if (sourceNode) { - sourceNode.stop(); - sourceNode = null; - isPlaying = false; - updateUI(); - } -} - -function seekAudio(time) { - if (!audioBuffer) return; - const wasPlaying = isPlaying; - stopAudio(); - pauseTime = time; - if (wasPlaying) { - playAudio(pauseTime); - } - drawSeekbar(); - savePlaybackState(); -} - -// UI Update Functions -function updateUI() { - elements.playButton.innerHTML = isPlaying ? "❚❚" : "►"; - drawVisualizer(); - updateTimeDisplay(); - drawSeekbar(); -} - -function updateControls() { - elements.playButton.disabled = isLoading; - elements.prevButton.disabled = isLoading; - elements.nextButton.disabled = isLoading; - if (isLoading) stopAudio(); -} - -function updateSongInfo() { - if (currentSong) { - elements.songTitle.textContent = currentSong.title; - elements.songArtistAlbum.textContent = `${currentSong.artist} - ${currentSong.album}`; - // Always update artwork with the cached version or generate new - elements.songCover.src = currentSong.artwork || songStore.getArtwork(currentSong.id); - } -} - -// Canvas Drawing Functions -function drawSeekbar() { - if (!audioBuffer) return; - - const ctx = seekbarCanvas.getContext('2d'); - const { width, height } = seekbarCanvas; - const centerY = height / 2; - - ctx.clearRect(0, 0, width, height); - - // Background bar - ctx.fillStyle = SEEKBAR_CONFIG.COLORS.BASE; - ctx.fillRect(0, centerY - SEEKBAR_CONFIG.HEIGHT / 2, width, SEEKBAR_CONFIG.HEIGHT); - - // Progress bar - const currentTime = isPlaying ? audioContext.currentTime - startTime : pauseTime; - const progress = (currentTime / audioBuffer.duration) * width; - - ctx.fillStyle = SEEKBAR_CONFIG.COLORS.PROGRESS; - ctx.fillRect(0, centerY - SEEKBAR_CONFIG.HEIGHT / 2, progress, SEEKBAR_CONFIG.HEIGHT); - - // Thumb - ctx.beginPath(); - ctx.arc(progress, centerY, SEEKBAR_CONFIG.THUMB_RADIUS, 0, Math.PI * 2); - ctx.fill(); -} - -function drawVisualizer() { - if (!isPlaying) return; - - const ctx = elements.visualizer.getContext('2d'); - const bufferLength = analyzerNode.frequencyBinCount; - const dataArray = new Uint8Array(bufferLength); - - function animate() { - if (!isPlaying) return; - requestAnimationFrame(animate); - - analyzerNode.getByteFrequencyData(dataArray); - ctx.clearRect(0, 0, elements.visualizer.width, elements.visualizer.height); - - const barWidth = (elements.visualizer.width / bufferLength) * 2.5; - let x = 0; - - for (let i = 0; i < bufferLength; i++) { - const barHeight = (dataArray[i] / 255) * elements.visualizer.height; - ctx.fillStyle = `rgb(${dataArray[i]}, 50, 255)`; - ctx.fillRect(x, elements.visualizer.height - barHeight, barWidth, barHeight); - x += barWidth + 1; - } - } - - animate(); -} - -// State Management Functions -function savePlaybackState() { - if (!currentSong) return; - - const currentTime = isPlaying ? audioContext.currentTime - startTime : pauseTime; - const state = { - songId: currentSong.id, - timeStamp: Math.min(currentTime, audioBuffer?.duration || 0), - isPlaying, - artwork: elements.songCover.src, - songTitle: currentSong.title, - songArtist: currentSong.artist, - songAlbum: currentSong.album - }; - - localStorage.setItem('playbackState', JSON.stringify(state)); -} - -// Song Loading and Navigation -async function loadNewSong(autoplay = false, direction = 'next') { - try { - const wasPlaying = isPlaying || autoplay; - stopAudio(); - - let nextSong; - if (direction === 'next') { - nextSong = await songStore.getNext(); - } else { - nextSong = await songStore.getPrevious(); - if (!nextSong) return; // Don't proceed if no previous song - } - - currentSong = nextSong; - updateSongInfo(); // This will now use the cached artwork - - await fetchAudio(`/stream/song/${currentSong.id}`); - pauseTime = 0; // Reset seek position for new song - - if (wasPlaying) { - playAudio(0); - } - - savePlaybackState(); - } catch (error) { - console.error('Error loading song:', error); - } -} - -function getCurrentScale() { - const screenWidth = window.innerWidth; - return screenWidth <= MIN_SCREEN_WIDTH ? 1 : screenWidth / MIN_SCREEN_WIDTH; -} - -// Event Listeners -function setupEventListeners() { - seekbarCanvas.addEventListener('mousedown', (e) => { - isDragging = true; - const scale = getCurrentScale(); - const rect = seekbarCanvas.getBoundingClientRect(); - // Adjust position calculation for scale - const position = Math.max(0, Math.min((e.clientX - rect.left) / scale, seekbarCanvas.width)); - handleSeek(position); - }); - - seekbarCanvas.addEventListener('mousemove', (e) => { - if (isDragging) { - const scale = getCurrentScale(); - const rect = seekbarCanvas.getBoundingClientRect(); - // Adjust position calculation for scale - const position = Math.max(0, Math.min((e.clientX - rect.left) / scale, seekbarCanvas.width)); - handleSeek(position); - } - }); - - seekbarCanvas.addEventListener('mouseup', () => isDragging = false); - seekbarCanvas.addEventListener('mouseleave', () => isDragging = false); - document.addEventListener('mouseup', () => isDragging = false); - - // Rest of the event listeners remain the same... - elements.playButton.addEventListener('click', () => { - if (isLoading) return; - if (isPlaying) { - stopAudio(); - pauseTime = audioContext.currentTime - startTime; - } else { - playAudio(pauseTime); - } - savePlaybackState(); - }); - - elements.prevButton.addEventListener('click', () => loadNewSong(isPlaying, 'previous')); - elements.nextButton.addEventListener('click', () => loadNewSong(isPlaying, 'next')); - - elements.songCover.addEventListener('error', () => { - elements.songCover.src = songStore.getArtwork(currentSong.id); - savePlaybackState(); - }); - - document.addEventListener('visibilitychange', savePlaybackState); - window.addEventListener('beforeunload', savePlaybackState); -} - -function handleSeek(position) { - if (!audioBuffer) return; - const seekTime = (position / seekbarCanvas.width) * audioBuffer.duration; - seekAudio(seekTime); -} - -function updateTimeDisplay() { - if (!audioBuffer) return; - const currentTime = isPlaying ? audioContext.currentTime - startTime : pauseTime; - elements.timeElapsed.textContent = formatTime(currentTime); - elements.timeTotal.textContent = formatTime(audioBuffer.duration); -} - -// Main update loop -function update() { - if (audioBuffer && isPlaying) { - const currentTime = audioContext.currentTime - startTime; - if (currentTime >= audioBuffer.duration) { - loadNewSong(true, 'next'); - return; - } - } - - updateTimeDisplay(); - drawSeekbar(); - requestAnimationFrame(update); -} - -// Initialization -async function init() { - await initAudio(); - setupEventListeners(); - - try { - const savedState = localStorage.getItem('playbackState'); - if (savedState) { - const state = JSON.parse(savedState); - currentSong = { - id: state.songId, - title: state.songTitle, - artist: state.songArtist, - album: state.songAlbum, - artwork: state.artwork - }; - - updateSongInfo(); - await fetchAudio(`/stream/song/${state.songId}`); - pauseTime = state.timeStamp || 0; - - if (state.isPlaying) { - setTimeout(() => playAudio(pauseTime), 100); - } else { - drawSeekbar(); - updateTimeDisplay(); - } - } else { - await loadNewSong(false); - } - } catch (error) { - console.error('Error restoring state:', error); - await loadNewSong(false); - } - - setInterval(savePlaybackState, 500); - update(); -} - -init(); - diff --git a/static/js/shared/headerEasterEggs.js b/static/js/shared/headerEasterEggs.js new file mode 100644 index 00000000..c2a27ac1 --- /dev/null +++ b/static/js/shared/headerEasterEggs.js @@ -0,0 +1,103 @@ +let FACounter = 0 +let timesFA = document.getElementById('timesFA') +const lang = document.documentElement.lang +const FUpMessages = (FACounter) => { + if (lang === 'ja') { + if (FACounter === 0) return "おい!引っ込んでろ、クソ野郎!" + if (FACounter === 4) return "やんのかコラ、このクソガキが!" + if (FACounter === 9) return "まだ調子こいてんのか、このクソ野郎?" + if (FACounter === 14) return "しつこいんだよ、このゴミクズ野郎!さっさと消えろ!" + if (FACounter === 19) return "マジでウザい!ヘルペスかよ、消えねぇな!" + if (FACounter === 24) return "もう勘弁。さっさと死ね、このクソ無駄酸素野郎。" + if (FACounter === 41) return "おめでとう、人生の意味を見つけたな:俺をイラつかせることだ!" + if (FACounter === 49) return "50回も?マジで親に愛されなかったの?" + if (FACounter === 68) return "ナイス。現実でヤレよ、このスケベ野郎。" + if (FACounter === 99) return "3桁?お前、特別なクソ野郎だな。" + if (FACounter === 419) return "4:20でクソ燻ってろ、このバカチョン野郎。" + if (FACounter % 1000 === 999) return "1000回?指が腐って落ちちまえ、このキチガイ野郎!" + if (FACounter % 500 === 499) return "マジでクソ人生見つけろよ!ゴミ箱でも漁ってろ!" + if (FACounter % 100 === 99) return "また100回?お前の精神科医、ウハウハだぞ!" + if (FACounter > 4999) return "クリックし続けろよ、バーカ。お前の悲しい人生の穴埋めになるさ。" + } else { + // Original English messages + if (FACounter === 0) return "Hey! Back off, fuckface!" + if (FACounter === 4) return "Don't you fucking dare, you little shit!" + if (FACounter === 9) return "Haven't you fucked around enough, you absolute wankstain?" + if (FACounter === 14) return "Aren't you a relentless cockwomble? Fuck off already!" + if (FACounter === 19) return "Fucking hell, you're like herpes - just won't go away!" + if (FACounter === 24) return "I'm done. Go play in traffic, you waste of oxygen." + if (FACounter === 41) return "Congrats, you've found the meaning of life: being a colossal pain in my ass!" + if (FACounter === 49) return "Half a hundred clicks? Did your parents not hug you enough?" + if (FACounter === 68) return "Nice. Now go get laid for real, you horny bastard." + if (FACounter === 99) return "Triple digits? You're a special kind of fucked up, aren't you?" + if (FACounter === 419) return "Blaze it and fuck off, you stoner dipshit." + if (FACounter % 1000 === 999) return "A thousand clicks? I hope your finger falls off, you absolute lunatic!" + if (FACounter % 500 === 499) return "Jesus fucking Christ, get a life or I'll find one for you in a dumpster!" + if (FACounter % 100 === 99) return "Another hundred? Your therapist is getting rich off your issues!" + if (FACounter > 4999) return "Keep clicking, fuckface. I'm sure it'll fill the void in your pathetic life." + } +} + +function dontYouDare(event) { + if (timesFA.parentElement.style.display === 'none') { + timesFA.parentElement.style.display = 'block' + } + + const previousFAButton = document.getElementById('FAButton') + if (previousFAButton) { + document.body.removeChild(previousFAButton) + } + const FAButton = document.createElement('div') + FAButton.id = 'FAButton' + FAButton.textContent = FUpMessages(FACounter) + + if (FAButton.textContent) { + applyStyles(event.clientX, event.clientY, FAButton) + document.body.appendChild(FAButton) + } + FACounter += 1 + timesFA.innerText = FACounter + setTimeout(() => { + FAButton.style.opacity = 0 + setTimeout(() => { + try { + document.body.removeChild(FAButton) + } catch { + // pass + } + }, 100) + }, 6000) +} + +function applyStyles(x, y, container) { + container.style.position = 'absolute' + container.style.top = `${y}px` + container.style.left = `${x}px` + container.style.fontSize = '11px' + container.style.userSelect = 'none' + container.style.transition = 'opacity 0.5s ease-in-out' + container.style.textShadow = '0 0 2px #e014df, 0 0 4px #e014df, 0 0 6px #e014df, 0 0 10px #ff00ff, 0 0 14px #ff00ff' + container.style.color = '#ffffff' + container.style.backgroundColor = '#8a0885' + container.style.padding = '4px 8px' + container.style.border = '1px solid #e014df' + container.style.borderRadius = '2px' + container.style.fontWeight = 'bold' + container.style.zIndex = 1 +} + +function leaveWebsite() { + const question = lang === 'ja' + ? '臆病者の選択か!無効なボタンをクリックしようとしてるのか。一度だけ付き合ってやるよ!本当に出て行くのか?' + : 'Ah, the choice of cowardice! Even trying to click on the disabled button. Let me humour you for once! Are you sure you want to leave?' + + if (confirm(question)) { + alert(lang === 'ja' ? '精神的に弱いクソ野郎め!' : 'Mentally Weak Coward!') + } +} + +function stopAndPlayPresentDay() { + const audio = document.getElementById('presentDayAudio') + audio.currentTime = 0 + audio.play() +} diff --git a/static/js/shared/kawaiiBeatsPlayer.js b/static/js/shared/kawaiiBeatsPlayer.js new file mode 100644 index 00000000..f7a20746 --- /dev/null +++ b/static/js/shared/kawaiiBeatsPlayer.js @@ -0,0 +1,539 @@ +// Collection of random anime artwork +const artworkCollection = [ + 'https://i.pinimg.com/enabled/564x/e2/5d/31/e25d3199f73c9453035727f8c7a70170.jpg', + 'https://i.pinimg.com/enabled/564x/5f/ed/28/5fed282cff8d22ac857e2a489031d05a.jpg', + 'https://i.pinimg.com/736x/05/a8/71/05a87162a78e2cad2ffe0a9eac6b4e2c.jpg', + 'https://i.pinimg.com/736x/c6/ac/13/c6ac139ed02c9accd34dbb16d7466025.jpg', + 'https://i.pinimg.com/736x/72/69/c3/7269c3d939764b024da9a6869dc59a0f.jpg', + 'https://i.pinimg.com/enabled/564x/cc/5c/6f/cc5c6f1c8e053d791ae2b4300ef5c9fe.jpg', + 'https://i.pinimg.com/enabled/564x/fa/0a/c2/fa0ac2b7145af1205c87350f7c735683.jpg', + 'https://i.pinimg.com/enabled/564x/94/84/57/9484579fcbf7e768d6206b07fa44c2b9.jpg', + 'https://i.pinimg.com/enabled/564x/fd/30/bf/fd30bf62f6409129ce6538f1b9ed7b8b.jpg', + 'https://i.pinimg.com/enabled/564x/44/b2/11/44b21104b4e41736c99ee183127aab3d.jpg', + 'https://i.pinimg.com/enabled/564x/f7/ce/56/f7ce5629aa91866020a559ef7e249f1c.jpg', + 'https://i.pinimg.com/enabled/564x/7b/ac/36/7bac368ff9b5f702d9b727491f8d4ef0.jpg', + 'https://i.pinimg.com/enabled/564x/39/1a/51/391a514a013f62ca9f25f47b4cbd7776.jpg', + 'https://i.pinimg.com/enabled/564x/03/d2/96/03d2967de5d249f88155cab461e69f3a.jpg' +]; + +// Constants +const SEEKBAR_CONFIG = { + HEIGHT: 4, + THUMB_RADIUS: 6, + HOVER_RADIUS: 8, + COLORS: { + BASE: 'rgba(255, 255, 255, 0.5)', + PROGRESS: 'rgba(255, 255, 255, 1)', + HOVER: 'rgba(255, 255, 255, 0.8)' + } +}; + +const STORE_LIMIT = artworkCollection.length; +const MIN_SCREEN_WIDTH = 2800; + +// Audio Context and Core Variables +let audioContext; +let sourceNode; +let analyzerNode; +let audioBuffer; +let startTime; +let pauseTime = 0; +let isPlaying = false; +let currentSong = null; +let isLoading = true; +let isDragging = false; + +// DOM Elements +const elements = { + playButton: document.getElementById('song-play'), + prevButton: document.getElementById('song-prev'), + nextButton: document.getElementById('song-next'), + timeElapsed: document.getElementById('song-time-elapsed'), + timeTotal: document.getElementById('song-time-total'), + songCover: document.getElementById('song-cover'), + songTitle: document.getElementById('song-title'), + songArtistAlbum: document.getElementById('song-artist-album'), + visualizer: document.getElementById('song-visualizer') +}; + +// Create and setup seekbar +const seekbarCanvas = document.createElement('canvas'); +seekbarCanvas.id = 'custom-seekbar'; +seekbarCanvas.width = 140; +seekbarCanvas.height = 20; +seekbarCanvas.style.cssText = 'position: absolute; left: 30px; top: 220px; cursor: pointer; z-index: 1;'; +document.getElementById('song-time').parentNode.insertBefore(seekbarCanvas, document.getElementById('song-time')); + +class ArtworkManager { + constructor() { + this.usedArtwork = new Set(); + this.availableArtwork = [...artworkCollection]; + } + + getRandomArtwork() { + // If all artwork has been used, reset the pool + if (this.availableArtwork.length === 0) { + this.resetArtworkPool(); + } + + // Get random artwork from available pool + const randomIndex = Math.floor(Math.random() * this.availableArtwork.length); + const artwork = this.availableArtwork[randomIndex]; + + // Remove from available pool and add to used set + this.availableArtwork.splice(randomIndex, 1); + this.usedArtwork.add(artwork); + + return artwork; + } + + resetArtworkPool() { + this.availableArtwork = [...artworkCollection]; + this.usedArtwork.clear(); + } + + releaseArtwork(artwork) { + if (this.usedArtwork.has(artwork)) { + this.usedArtwork.delete(artwork); + this.availableArtwork.push(artwork); + } + } +} + +// Song Store Management +class SongStore { + constructor() { + this.songs = JSON.parse(localStorage.getItem('songStore')) || []; + this.currentIndex = parseInt(localStorage.getItem('currentSongIndex')) || -1; + this.artworkCache = JSON.parse(localStorage.getItem('artworkCache')) || {}; + this.artworkManager = new ArtworkManager(); + + // Restore artwork state + Object.values(this.artworkCache).forEach(artwork => { + this.artworkManager.usedArtwork.add(artwork); + }); + + // Remove any artwork from availableArtwork that's already in use + this.artworkManager.availableArtwork = this.artworkManager.availableArtwork + .filter(artwork => !this.artworkManager.usedArtwork.has(artwork)); + } + + async addSong(song) { + if (!song) return null; + + // Generate unique artwork for the song + const artwork = this.artworkManager.getRandomArtwork(); + this.artworkCache[song.id] = artwork; + + // Always add the song, even if it's a duplicate + this.songs.push({ + id: song.id, + title: song.title, + artist: song.artist, + album: song.album + }); + + // Maintain the store limit + if (this.songs.length > STORE_LIMIT) { + const removedSong = this.songs.shift(); + // Release the artwork back to the pool + const removedArtwork = this.artworkCache[removedSong.id]; + this.artworkManager.releaseArtwork(removedArtwork); + delete this.artworkCache[removedSong.id]; + if (this.currentIndex > -1) this.currentIndex--; + } + + this.currentIndex = this.songs.length - 1; + this.save(); + + return { + ...this.songs[this.currentIndex], + artwork: this.artworkCache[song.id] + }; + } + + async getNext() { + if (this.currentIndex < this.songs.length - 1) { + this.currentIndex++; + this.save(); + return { + ...this.songs[this.currentIndex], + artwork: this.artworkCache[this.songs[this.currentIndex].id] + }; + } + // Get new song if we're at the end + const newSong = await this.fetchNewSong(); + return this.addSong(newSong); + } + + async getPrevious() { + if (this.currentIndex > 0) { + this.currentIndex--; + this.save(); + return { + ...this.songs[this.currentIndex], + artwork: this.artworkCache[this.songs[this.currentIndex].id] + }; + } + return null; + } + + async fetchNewSong() { + try { + const response = await fetch('/stream/random-song'); + const data = await response.json(); + return data.song; + } catch (error) { + console.error('Error fetching new song:', error); + return null; + } + } + + save() { + localStorage.setItem('songStore', JSON.stringify(this.songs)); + localStorage.setItem('currentSongIndex', this.currentIndex.toString()); + localStorage.setItem('artworkCache', JSON.stringify(this.artworkCache)); + } + + getCurrentSong() { + if (this.currentIndex >= 0) { + const song = this.songs[this.currentIndex]; + return { + ...song, + artwork: this.artworkCache[song.id] + }; + } + return null; + } + + getArtwork(songId) { + return this.artworkCache[songId]; + } +} + +const songStore = new SongStore(); + +// Audio Control Functions +async function initAudio() { + audioContext = new (window.AudioContext || window.webkitAudioContext)(); + analyzerNode = audioContext.createAnalyser(); + analyzerNode.fftSize = 256; + analyzerNode.connect(audioContext.destination); +} + +function formatTime(time) { + const minutes = Math.floor(time / 60); + const seconds = Math.floor(time % 60).toString().padStart(2, '0'); + return `${minutes}:${seconds}`; +} + +async function fetchAudio(url) { + isLoading = true; + updateControls(); + try { + const response = await fetch(url); + const arrayBuffer = await response.arrayBuffer(); + audioBuffer = await audioContext.decodeAudioData(arrayBuffer); + elements.timeTotal.textContent = formatTime(audioBuffer.duration); + isLoading = false; + updateControls(); + } catch (error) { + console.error('Error loading audio:', error); + isLoading = false; + updateControls(); + } +} + +function playAudio(offset = 0) { + if (!audioBuffer) return; + if (isPlaying) stopAudio(); + + offset = Math.min(Math.max(0, offset), audioBuffer.duration); + + sourceNode = audioContext.createBufferSource(); + sourceNode.buffer = audioBuffer; + sourceNode.connect(analyzerNode); + + sourceNode.onended = async () => { + const currentTime = audioContext.currentTime - startTime; + if (currentTime >= audioBuffer.duration - 0.1) { + await loadNewSong(true, 'next'); + } + }; + + sourceNode.start(0, offset); + startTime = audioContext.currentTime - offset; + isPlaying = true; + updateUI(); +} + +function stopAudio() { + if (sourceNode) { + sourceNode.stop(); + sourceNode = null; + isPlaying = false; + updateUI(); + } +} + +function seekAudio(time) { + if (!audioBuffer) return; + const wasPlaying = isPlaying; + stopAudio(); + pauseTime = time; + if (wasPlaying) { + playAudio(pauseTime); + } + drawSeekbar(); + savePlaybackState(); +} + +// UI Update Functions +function updateUI() { + elements.playButton.innerHTML = isPlaying ? "❚❚" : "►"; + drawVisualizer(); + updateTimeDisplay(); + drawSeekbar(); +} + +function updateControls() { + elements.playButton.disabled = isLoading; + elements.prevButton.disabled = isLoading; + elements.nextButton.disabled = isLoading; + if (isLoading) stopAudio(); +} + +function updateSongInfo() { + if (currentSong) { + elements.songTitle.textContent = currentSong.title; + elements.songArtistAlbum.textContent = `${currentSong.artist} - ${currentSong.album}`; + // Always update artwork with the cached version or generate new + elements.songCover.src = currentSong.artwork || songStore.getArtwork(currentSong.id); + } +} + +// Canvas Drawing Functions +function drawSeekbar() { + if (!audioBuffer) return; + + const ctx = seekbarCanvas.getContext('2d'); + const { width, height } = seekbarCanvas; + const centerY = height / 2; + + ctx.clearRect(0, 0, width, height); + + // Background bar + ctx.fillStyle = SEEKBAR_CONFIG.COLORS.BASE; + ctx.fillRect(0, centerY - SEEKBAR_CONFIG.HEIGHT / 2, width, SEEKBAR_CONFIG.HEIGHT); + + // Progress bar + const currentTime = isPlaying ? audioContext.currentTime - startTime : pauseTime; + const progress = (currentTime / audioBuffer.duration) * width; + + ctx.fillStyle = SEEKBAR_CONFIG.COLORS.PROGRESS; + ctx.fillRect(0, centerY - SEEKBAR_CONFIG.HEIGHT / 2, progress, SEEKBAR_CONFIG.HEIGHT); + + // Thumb + ctx.beginPath(); + ctx.arc(progress, centerY, SEEKBAR_CONFIG.THUMB_RADIUS, 0, Math.PI * 2); + ctx.fill(); +} + +function drawVisualizer() { + if (!isPlaying) return; + + const ctx = elements.visualizer.getContext('2d'); + const bufferLength = analyzerNode.frequencyBinCount; + const dataArray = new Uint8Array(bufferLength); + + function animate() { + if (!isPlaying) return; + requestAnimationFrame(animate); + + analyzerNode.getByteFrequencyData(dataArray); + ctx.clearRect(0, 0, elements.visualizer.width, elements.visualizer.height); + + const barWidth = (elements.visualizer.width / bufferLength) * 2.5; + let x = 0; + + for (let i = 0; i < bufferLength; i++) { + const barHeight = (dataArray[i] / 255) * elements.visualizer.height; + ctx.fillStyle = `rgb(${dataArray[i]}, 50, 255)`; + ctx.fillRect(x, elements.visualizer.height - barHeight, barWidth, barHeight); + x += barWidth + 1; + } + } + + animate(); +} + +// State Management Functions +function savePlaybackState() { + if (!currentSong) return; + + const currentTime = isPlaying ? audioContext.currentTime - startTime : pauseTime; + const state = { + songId: currentSong.id, + timeStamp: Math.min(currentTime, audioBuffer?.duration || 0), + isPlaying, + artwork: elements.songCover.src, + songTitle: currentSong.title, + songArtist: currentSong.artist, + songAlbum: currentSong.album + }; + + localStorage.setItem('playbackState', JSON.stringify(state)); +} + +// Song Loading and Navigation +async function loadNewSong(autoplay = false, direction = 'next') { + try { + const wasPlaying = isPlaying || autoplay; + stopAudio(); + + let nextSong; + if (direction === 'next') { + nextSong = await songStore.getNext(); + } else { + nextSong = await songStore.getPrevious(); + if (!nextSong) return; // Don't proceed if no previous song + } + + currentSong = nextSong; + updateSongInfo(); // This will now use the cached artwork + + await fetchAudio(`/stream/song/${currentSong.id}`); + pauseTime = 0; // Reset seek position for new song + + if (wasPlaying) { + playAudio(0); + } + + savePlaybackState(); + } catch (error) { + console.error('Error loading song:', error); + } +} + +function getCurrentScale() { + const screenWidth = window.innerWidth; + return screenWidth <= MIN_SCREEN_WIDTH ? 1 : screenWidth / MIN_SCREEN_WIDTH; +} + +// Event Listeners +function setupEventListeners() { + seekbarCanvas.addEventListener('mousedown', (e) => { + isDragging = true; + const scale = getCurrentScale(); + const rect = seekbarCanvas.getBoundingClientRect(); + // Adjust position calculation for scale + const position = Math.max(0, Math.min((e.clientX - rect.left) / scale, seekbarCanvas.width)); + handleSeek(position); + }); + + seekbarCanvas.addEventListener('mousemove', (e) => { + if (isDragging) { + const scale = getCurrentScale(); + const rect = seekbarCanvas.getBoundingClientRect(); + // Adjust position calculation for scale + const position = Math.max(0, Math.min((e.clientX - rect.left) / scale, seekbarCanvas.width)); + handleSeek(position); + } + }); + + seekbarCanvas.addEventListener('mouseup', () => isDragging = false); + seekbarCanvas.addEventListener('mouseleave', () => isDragging = false); + document.addEventListener('mouseup', () => isDragging = false); + + // Rest of the event listeners remain the same... + elements.playButton.addEventListener('click', () => { + if (isLoading) return; + if (isPlaying) { + stopAudio(); + pauseTime = audioContext.currentTime - startTime; + } else { + playAudio(pauseTime); + } + savePlaybackState(); + }); + + elements.prevButton.addEventListener('click', () => loadNewSong(isPlaying, 'previous')); + elements.nextButton.addEventListener('click', () => loadNewSong(isPlaying, 'next')); + + elements.songCover.addEventListener('error', () => { + elements.songCover.src = songStore.getArtwork(currentSong.id); + savePlaybackState(); + }); + + document.addEventListener('visibilitychange', savePlaybackState); + window.addEventListener('beforeunload', savePlaybackState); +} + +function handleSeek(position) { + if (!audioBuffer) return; + const seekTime = (position / seekbarCanvas.width) * audioBuffer.duration; + seekAudio(seekTime); +} + +function updateTimeDisplay() { + if (!audioBuffer) return; + const currentTime = isPlaying ? audioContext.currentTime - startTime : pauseTime; + elements.timeElapsed.textContent = formatTime(currentTime); + elements.timeTotal.textContent = formatTime(audioBuffer.duration); +} + +// Main update loop +function update() { + if (audioBuffer && isPlaying) { + const currentTime = audioContext.currentTime - startTime; + if (currentTime >= audioBuffer.duration) { + loadNewSong(true, 'next'); + return; + } + } + + updateTimeDisplay(); + drawSeekbar(); + requestAnimationFrame(update); +} + +// Initialization +async function init() { + await initAudio(); + setupEventListeners(); + + try { + const savedState = localStorage.getItem('playbackState'); + if (savedState) { + const state = JSON.parse(savedState); + currentSong = { + id: state.songId, + title: state.songTitle, + artist: state.songArtist, + album: state.songAlbum, + artwork: state.artwork + }; + + updateSongInfo(); + await fetchAudio(`/stream/song/${state.songId}`); + pauseTime = state.timeStamp || 0; + + if (state.isPlaying) { + setTimeout(() => playAudio(pauseTime), 100); + } else { + drawSeekbar(); + updateTimeDisplay(); + } + } else { + await loadNewSong(false); + } + } catch (error) { + console.error('Error restoring state:', error); + await loadNewSong(false); + } + + setInterval(savePlaybackState, 500); + update(); +} + +init(); diff --git a/static/js/youdontdare.js b/static/js/youdontdare.js deleted file mode 100644 index c2a27ac1..00000000 --- a/static/js/youdontdare.js +++ /dev/null @@ -1,103 +0,0 @@ -let FACounter = 0 -let timesFA = document.getElementById('timesFA') -const lang = document.documentElement.lang -const FUpMessages = (FACounter) => { - if (lang === 'ja') { - if (FACounter === 0) return "おい!引っ込んでろ、クソ野郎!" - if (FACounter === 4) return "やんのかコラ、このクソガキが!" - if (FACounter === 9) return "まだ調子こいてんのか、このクソ野郎?" - if (FACounter === 14) return "しつこいんだよ、このゴミクズ野郎!さっさと消えろ!" - if (FACounter === 19) return "マジでウザい!ヘルペスかよ、消えねぇな!" - if (FACounter === 24) return "もう勘弁。さっさと死ね、このクソ無駄酸素野郎。" - if (FACounter === 41) return "おめでとう、人生の意味を見つけたな:俺をイラつかせることだ!" - if (FACounter === 49) return "50回も?マジで親に愛されなかったの?" - if (FACounter === 68) return "ナイス。現実でヤレよ、このスケベ野郎。" - if (FACounter === 99) return "3桁?お前、特別なクソ野郎だな。" - if (FACounter === 419) return "4:20でクソ燻ってろ、このバカチョン野郎。" - if (FACounter % 1000 === 999) return "1000回?指が腐って落ちちまえ、このキチガイ野郎!" - if (FACounter % 500 === 499) return "マジでクソ人生見つけろよ!ゴミ箱でも漁ってろ!" - if (FACounter % 100 === 99) return "また100回?お前の精神科医、ウハウハだぞ!" - if (FACounter > 4999) return "クリックし続けろよ、バーカ。お前の悲しい人生の穴埋めになるさ。" - } else { - // Original English messages - if (FACounter === 0) return "Hey! Back off, fuckface!" - if (FACounter === 4) return "Don't you fucking dare, you little shit!" - if (FACounter === 9) return "Haven't you fucked around enough, you absolute wankstain?" - if (FACounter === 14) return "Aren't you a relentless cockwomble? Fuck off already!" - if (FACounter === 19) return "Fucking hell, you're like herpes - just won't go away!" - if (FACounter === 24) return "I'm done. Go play in traffic, you waste of oxygen." - if (FACounter === 41) return "Congrats, you've found the meaning of life: being a colossal pain in my ass!" - if (FACounter === 49) return "Half a hundred clicks? Did your parents not hug you enough?" - if (FACounter === 68) return "Nice. Now go get laid for real, you horny bastard." - if (FACounter === 99) return "Triple digits? You're a special kind of fucked up, aren't you?" - if (FACounter === 419) return "Blaze it and fuck off, you stoner dipshit." - if (FACounter % 1000 === 999) return "A thousand clicks? I hope your finger falls off, you absolute lunatic!" - if (FACounter % 500 === 499) return "Jesus fucking Christ, get a life or I'll find one for you in a dumpster!" - if (FACounter % 100 === 99) return "Another hundred? Your therapist is getting rich off your issues!" - if (FACounter > 4999) return "Keep clicking, fuckface. I'm sure it'll fill the void in your pathetic life." - } -} - -function dontYouDare(event) { - if (timesFA.parentElement.style.display === 'none') { - timesFA.parentElement.style.display = 'block' - } - - const previousFAButton = document.getElementById('FAButton') - if (previousFAButton) { - document.body.removeChild(previousFAButton) - } - const FAButton = document.createElement('div') - FAButton.id = 'FAButton' - FAButton.textContent = FUpMessages(FACounter) - - if (FAButton.textContent) { - applyStyles(event.clientX, event.clientY, FAButton) - document.body.appendChild(FAButton) - } - FACounter += 1 - timesFA.innerText = FACounter - setTimeout(() => { - FAButton.style.opacity = 0 - setTimeout(() => { - try { - document.body.removeChild(FAButton) - } catch { - // pass - } - }, 100) - }, 6000) -} - -function applyStyles(x, y, container) { - container.style.position = 'absolute' - container.style.top = `${y}px` - container.style.left = `${x}px` - container.style.fontSize = '11px' - container.style.userSelect = 'none' - container.style.transition = 'opacity 0.5s ease-in-out' - container.style.textShadow = '0 0 2px #e014df, 0 0 4px #e014df, 0 0 6px #e014df, 0 0 10px #ff00ff, 0 0 14px #ff00ff' - container.style.color = '#ffffff' - container.style.backgroundColor = '#8a0885' - container.style.padding = '4px 8px' - container.style.border = '1px solid #e014df' - container.style.borderRadius = '2px' - container.style.fontWeight = 'bold' - container.style.zIndex = 1 -} - -function leaveWebsite() { - const question = lang === 'ja' - ? '臆病者の選択か!無効なボタンをクリックしようとしてるのか。一度だけ付き合ってやるよ!本当に出て行くのか?' - : 'Ah, the choice of cowardice! Even trying to click on the disabled button. Let me humour you for once! Are you sure you want to leave?' - - if (confirm(question)) { - alert(lang === 'ja' ? '精神的に弱いクソ野郎め!' : 'Mentally Weak Coward!') - } -} - -function stopAndPlayPresentDay() { - const audio = document.getElementById('presentDayAudio') - audio.currentTime = 0 - audio.play() -} -- cgit v1.2.3