diff options
| author | Bobby <[email protected]> | 2025-05-22 10:45:29 +0530 |
|---|---|---|
| committer | Bobby <[email protected]> | 2025-05-22 10:45:29 +0530 |
| commit | 8ba8b93f2f88990f5569babbf94dec7dc4ad85ef (patch) | |
| tree | 077f61e0f83fb9822c7bafc56a5d058bd1b932d9 /static | |
| parent | fa720c6ac44e4abda6c4c2fe420d8b6903577ea4 (diff) | |
| download | thatcomputerscientist-8ba8b93f2f88990f5569babbf94dec7dc4ad85ef.tar.xz thatcomputerscientist-8ba8b93f2f88990f5569babbf94dec7dc4ad85ef.zip | |
kawaiibeats player complete ported
Diffstat (limited to 'static')
| -rw-r--r-- | static/css/kawaiibeats/kawaiibeats.css | 6 | ||||
| -rw-r--r-- | static/js/core/base.js | 21 | ||||
| -rw-r--r-- | static/js/kawaiibeats/kawaiiBeatsPlayer.js | 206 |
3 files changed, 93 insertions, 140 deletions
diff --git a/static/css/kawaiibeats/kawaiibeats.css b/static/css/kawaiibeats/kawaiibeats.css index 68e5ee8e..89510050 100644 --- a/static/css/kawaiibeats/kawaiibeats.css +++ b/static/css/kawaiibeats/kawaiibeats.css @@ -26,9 +26,9 @@ /* Track Information */ #song-title { position: absolute; - top: 25px; - left: 20px; - width: 160px; + top: 24px; + left: 24px; + width: 158px; font-weight: bold; text-align: center; white-space: nowrap; diff --git a/static/js/core/base.js b/static/js/core/base.js index 333c34b1..5f029791 100644 --- a/static/js/core/base.js +++ b/static/js/core/base.js @@ -1,4 +1,23 @@ new Pamphlet({ server: '/services/pamphlet', refreshInterval: 120000 // 2 minutes -});
\ No newline at end of file +}); + +// KawaiiBeats Player +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') +}; + +const player = new AudioPlayer(elements, { + storeLimit: STORE_LIMIT, + serverURL: window.location.origin, +}); +player.init();
\ No newline at end of file diff --git a/static/js/kawaiibeats/kawaiiBeatsPlayer.js b/static/js/kawaiibeats/kawaiiBeatsPlayer.js index 1040fe4d..d1a68ce4 100644 --- a/static/js/kawaiibeats/kawaiiBeatsPlayer.js +++ b/static/js/kawaiibeats/kawaiiBeatsPlayer.js @@ -4,31 +4,16 @@ * @fileoverview Audio player implementation with artwork management and persistent state * @version 1.0.0 */ -/** @type {string[]} Artwork collection for random assignment */ -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", -]; /** * @typedef {Object} Song - * @property {string} id - Unique identifier for the song + * @property {string} id - Unique identifier for the song (spotify_id) * @property {string} title - Song title * @property {string} artist - Artist name * @property {string} album - Album name - * @property {string} [artwork] - URL to artwork image + * @property {string} [album_art_url] - URL to album artwork image + * @property {string} [custom_album_art] - URL to custom album artwork image + * @property {string} [streaming_url] - URL to the audio stream */ /** @@ -45,7 +30,7 @@ const artworkCollection = [ */ // Configuration -const STORE_LIMIT = artworkCollection.length; +const STORE_LIMIT = 20; const SEEKBAR_CONFIG = { HEIGHT: 4, THUMB_RADIUS: 6, @@ -58,69 +43,16 @@ const SEEKBAR_CONFIG = { }; /** - * Manages artwork selection and rotation for songs - */ -class ArtworkManager { - /** - * @param {string[]} artworkCollection - Collection of artwork URLs - */ - constructor(artworkCollection) { - this.collection = artworkCollection; - this.usedArtwork = new Set(); - this.availableArtwork = [...artworkCollection]; - } - - /** - * @returns {string} Random unused artwork URL - */ - getRandomArtwork() { - if (this.availableArtwork.length === 0) { - this.resetArtworkPool(); - } - const randomIndex = Math.floor(Math.random() * this.availableArtwork.length); - const artwork = this.availableArtwork[randomIndex]; - this.availableArtwork.splice(randomIndex, 1); - this.usedArtwork.add(artwork); - return artwork; - } - - resetArtworkPool() { - this.availableArtwork = [...this.collection]; - this.usedArtwork.clear(); - } - - /** - * @param {string} artwork - Artwork URL to release back to pool - */ - releaseArtwork(artwork) { - if (this.usedArtwork.has(artwork)) { - this.usedArtwork.delete(artwork); - this.availableArtwork.push(artwork); - } - } -} - -/** * Manages song queue and persistence */ class SongStore { /** * @param {number} limit - Maximum number of songs to store - * @param {ArtworkManager} artworkManager - Artwork management instance */ - constructor(limit, artworkManager) { + constructor(limit) { this.limit = limit; - this.artworkManager = artworkManager; this.songs = JSON.parse(localStorage.getItem('songStore')) || []; this.currentIndex = parseInt(localStorage.getItem('currentSongIndex')) || -1; - this.artworkCache = JSON.parse(localStorage.getItem('artworkCache')) || {}; - - // Restore artwork state - Object.values(this.artworkCache).forEach(artwork => { - this.artworkManager.usedArtwork.add(artwork); - }); - this.artworkManager.availableArtwork = this.artworkManager.availableArtwork - .filter(artwork => !this.artworkManager.usedArtwork.has(artwork)); } /** @@ -130,41 +62,40 @@ class SongStore { async addSong(song) { if (!song) return null; - const artwork = this.artworkManager.getRandomArtwork(); - this.artworkCache[song.id] = artwork; - this.songs.push({ - id: song.id, + id: song.spotify_id, + spotify_id: song.spotify_id, title: song.title, artist: song.artist, - album: song.album + album: song.album, + album_art_url: song.album_art_url, + custom_album_art: song.custom_album_art, + streaming_url: song.streaming_url }); if (this.songs.length > this.limit) { - const removedSong = this.songs.shift(); - const removedArtwork = this.artworkCache[removedSong.id]; - this.artworkManager.releaseArtwork(removedArtwork); - delete this.artworkCache[removedSong.id]; + this.songs.shift(); if (this.currentIndex > -1) this.currentIndex--; } this.currentIndex = this.songs.length - 1; this.save(); - return { ...song, artwork }; + return song; } /** + * @param {string} [serverURL=''] - Base URL for server requests * @returns {Promise<Song|null>} Next song in queue or new song */ - async getNext() { + async getNext(serverURL = '') { if (this.currentIndex < this.songs.length - 1) { this.currentIndex++; this.save(); - return this._getSongWithArtwork(this.currentIndex); + return this.songs[this.currentIndex]; } const nextSongId = this.songs[this.currentIndex]?.id; - const newSong = await this._fetchSong(nextSongId); + const newSong = await this._fetchSong(nextSongId, serverURL); return this.addSong(newSong); } @@ -175,7 +106,7 @@ class SongStore { if (this.currentIndex > 0) { this.currentIndex--; this.save(); - return this._getSongWithArtwork(this.currentIndex); + return this.songs[this.currentIndex]; } return null; } @@ -184,56 +115,67 @@ class SongStore { * @private * @param {string} [nextSongId] - ID of current song for continuity * @returns {Promise<Song|null>} Fetched song data - */ - async _fetchSong(nextSongId = null) { + */ /** +* @private +* @param {string} [nextSongId] - ID of current song for continuity +* @param {string} [serverURL=''] - Base URL for server requests +* @returns {Promise<Song|null>} Fetched song data +*/ + async _fetchSong(nextSongId = null, serverURL = '') { try { - const url = nextSongId ? - `/services/stream/random-song?next=${nextSongId}` : - '/services/stream/random-song'; - const response = await fetch(url); - return await response.json(); + const endpoint = nextSongId ? + `${serverURL}/services/kawaiibeats?next=${nextSongId}` : + `${serverURL}/services/kawaiibeats`; + const response = await fetch(endpoint); + const song = await response.json(); + + if (song && song.spotify_id && !song.id) { + song.id = song.spotify_id; + } + + return song; } catch (error) { console.error('Error fetching song:', error); return null; } } - /** - * @private - * @param {number} index - Index of song to retrieve - * @returns {Song|null} Song with artwork - */ - _getSongWithArtwork(index) { - const song = this.songs[index]; - return song ? { ...song, artwork: this.artworkCache[song.id] } : null; - } - getCurrentSong() { - return this.currentIndex >= 0 ? this._getSongWithArtwork(this.currentIndex) : null; + return this.currentIndex >= 0 ? this.songs[this.currentIndex] : null; } getArtwork(songId) { - return this.artworkCache[songId]; + const song = this.songs.find(s => s.id === songId); + return song ? (song.custom_album_art || song.album_art_url) : null; } save() { localStorage.setItem('songStore', JSON.stringify(this.songs)); localStorage.setItem('currentSongIndex', this.currentIndex.toString()); - localStorage.setItem('artworkCache', JSON.stringify(this.artworkCache)); } } /** + * @typedef {Object} PlayerConfig + * @property {number} [storeLimit=20] - Maximum number of songs to store + * @property {string} [serverURL=''] - Base URL for server requests + */ + +/** * Manages audio playback and visualization */ class AudioPlayer { /** * @param {UIElements} elements - DOM elements - * @param {SongStore} songStore - Song management instance + * @param {PlayerConfig} [config={}] - Configuration options */ - constructor(elements, songStore) { + constructor(elements, config = {}) { this.elements = elements; - this.songStore = songStore; + this.config = { + storeLimit: config.storeLimit || STORE_LIMIT, + serverURL: config.serverURL || 'https://shi.foo' + }; + this.songStore = new SongStore(this.config.storeLimit); this.audioContext = null; this.sourceNode = null; this.analyzerNode = null; @@ -442,7 +384,7 @@ class AudioPlayer { this.elements.songArtistAlbum.textContent = `${this.currentSong.artist} - ${this.currentSong.album}`; this.elements.songCover.src = - this.currentSong.artwork || this.songStore.getArtwork(this.currentSong.id); + this.currentSong.custom_album_art || this.currentSong.album_art_url || ''; } } @@ -520,9 +462,12 @@ class AudioPlayer { this.audioContext.currentTime - this.startTime : this.pauseTime; const state = { songId: this.currentSong.id, + spotify_id: this.currentSong.spotify_id || this.currentSong.id, timeStamp: Math.min(currentTime, this.audioBuffer?.duration || 0), isPlaying: this.isPlaying, - artwork: this.elements.songCover.src, + album_art_url: this.currentSong.album_art_url, + custom_album_art: this.currentSong.custom_album_art, + streaming_url: this.currentSong.streaming_url, songTitle: this.currentSong.title, songArtist: this.currentSong.artist, songAlbum: this.currentSong.album @@ -543,7 +488,7 @@ class AudioPlayer { this.stop(); const nextSong = direction === 'next' ? - await this.songStore.getNext() : + await this.songStore.getNext(this.config.serverURL) : await this.songStore.getPrevious(); if (!nextSong && direction === 'previous') return; @@ -551,7 +496,7 @@ class AudioPlayer { this.currentSong = nextSong; this.updateSongInfo(); - await this.loadAudio(`/services/stream/song/${this.currentSong.id}`); + await this.loadAudio(this.currentSong.streaming_url); this.pauseTime = 0; if (wasPlaying) { @@ -593,7 +538,11 @@ class AudioPlayer { this.elements.nextButton.addEventListener('click', () => this.loadNewSong(this.isPlaying, 'next')); this.elements.songCover.addEventListener('error', () => { - this.elements.songCover.src = this.songStore.getArtwork(this.currentSong.id); + if (this.currentSong.custom_album_art && this.elements.songCover.src !== this.currentSong.custom_album_art) { + this.elements.songCover.src = this.currentSong.custom_album_art; + } else if (this.currentSong.album_art_url && this.elements.songCover.src !== this.currentSong.album_art_url) { + this.elements.songCover.src = this.currentSong.album_art_url; + } this.saveState(); }); @@ -698,14 +647,17 @@ class AudioPlayer { const state = JSON.parse(savedState); this.currentSong = { id: state.songId, + spotify_id: state.songId, // Use songId as spotify_id for consistency title: state.songTitle, artist: state.songArtist, album: state.songAlbum, - artwork: state.artwork + album_art_url: state.album_art_url, + custom_album_art: state.custom_album_art, + streaming_url: state.streaming_url }; this.updateSongInfo(); - await this.loadAudio(`/services/stream/song/${state.songId}`); + await this.loadAudio(state.streaming_url); this.pauseTime = state.timeStamp || 0; if (state.isPlaying) { @@ -723,21 +675,3 @@ class AudioPlayer { } } } - -// Initialize player -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') -}; - -const artworkManager = new ArtworkManager(artworkCollection); -const songStore = new SongStore(STORE_LIMIT, artworkManager); -const player = new AudioPlayer(elements, songStore); -player.init();
\ No newline at end of file |
