2024-04-17 14:23:54 -04:00
import { writable , readable , readonly , type Writable , type Readable } from 'svelte/store'
2024-01-25 03:05:13 -05:00
import type { AlertType } from '$lib/components/util/alert.svelte'
2024-01-21 23:51:15 -05:00
2024-01-27 01:38:04 -05:00
export const pageWidth : Writable < number > = writable ( )
2024-01-21 23:51:15 -05:00
2024-01-27 01:38:04 -05:00
export const newestAlert : Writable < [ AlertType , string ] > = writable ( )
2024-01-21 23:51:15 -05:00
const youtubeMusicBackground : string = 'https://www.gstatic.com/youtube/media/ytm/images/sbg/wsbg@4000x2250.png' // Default Youtube music background
export const backgroundImage : Writable < string > = writable ( youtubeMusicBackground )
2024-04-16 10:05:11 -04:00
2024-06-04 22:37:19 -04:00
function fisherYatesShuffle < T > ( items : T [ ] ) {
for ( let currentIndex = items . length - 1 ; currentIndex >= 0 ; currentIndex -- ) {
let randomIndex = Math . floor ( Math . random ( ) * ( currentIndex + 1 ) )
; [ items [ currentIndex ] , items [ randomIndex ] ] = [ items [ randomIndex ] , items [ currentIndex ] ]
}
return items
}
// ? New idea for how to handle mixing. Keep originalSongs and currentSongs but also add playedSongs. Add the previous song to played songs whenever next() is called.
// ? Whenever a song is mixed, set currentSongs = [...playedSongs, currentSongs[currentPosition], ...fisherYatesShuffle(everything else)]. Reorder method would stay the same.
// ? IDK it's a thought
2024-04-16 10:05:11 -04:00
class Queue {
2024-06-04 22:37:19 -04:00
private currentPosition : number // -1 means no song is playing
private originalSongs : Song [ ]
private currentSongs : Song [ ]
private shuffled : boolean
2024-04-16 10:05:11 -04:00
constructor ( ) {
2024-04-22 14:18:42 -04:00
this . currentPosition = - 1
2024-06-04 22:37:19 -04:00
this . originalSongs = [ ]
this . currentSongs = [ ]
this . shuffled = false
2024-04-16 10:05:11 -04:00
}
2024-04-22 14:18:42 -04:00
get current() {
2024-06-04 22:37:19 -04:00
if ( this . currentSongs . length === 0 ) return null
2024-05-16 21:45:39 -04:00
if ( this . currentPosition === - 1 ) this . currentPosition = 0
2024-06-04 22:37:19 -04:00
return this . currentSongs [ this . currentPosition ]
2024-04-16 10:05:11 -04:00
}
2024-06-04 22:37:19 -04:00
/** Sets the currently playing song to the song provided as long as it is in the current playlist */
2024-04-22 14:18:42 -04:00
set current ( newSong : Song | null ) {
if ( newSong === null ) {
this . currentPosition = - 1
2024-04-17 14:23:54 -04:00
} else {
2024-06-04 22:37:19 -04:00
const queuePosition = this . currentSongs . findIndex ( ( song ) = > song === newSong )
if ( queuePosition >= 0 ) this . currentPosition = queuePosition
2024-04-16 10:05:11 -04:00
}
2024-06-04 22:37:19 -04:00
2024-04-22 14:18:42 -04:00
writableQueue . set ( this )
}
2024-04-16 10:05:11 -04:00
2024-04-22 14:18:42 -04:00
get list() {
2024-06-04 22:37:19 -04:00
return this . currentSongs
}
get isShuffled() {
return this . shuffled
}
/** Shuffles all songs in the queue after the currently playing song */
public shuffle() {
const shuffledSongs = fisherYatesShuffle ( this . currentSongs . slice ( this . currentPosition + 1 ) )
this . currentSongs = this . currentSongs . slice ( 0 , this . currentPosition + 1 ) . concat ( shuffledSongs )
this . shuffled = true
writableQueue . set ( this )
}
/** Restores the queue to its original ordered state, while maintaining whatever song is currently playing */
public reorder() {
const originalPosition = this . originalSongs . findIndex ( ( song ) = > song === this . currentSongs [ this . currentPosition ] )
this . currentSongs = [ . . . this . originalSongs ]
this . currentPosition = originalPosition
this . shuffled = false
writableQueue . set ( this )
2024-04-16 10:05:11 -04:00
}
2024-06-04 22:37:19 -04:00
/** Starts the next song */
2024-04-22 14:18:42 -04:00
public next() {
2024-06-04 22:37:19 -04:00
if ( this . currentSongs . length === 0 || this . currentSongs . length <= this . currentPosition + 1 ) return
2024-04-22 14:18:42 -04:00
this . currentPosition += 1
writableQueue . set ( this )
2024-04-17 14:23:54 -04:00
}
2024-04-16 10:05:11 -04:00
2024-06-04 22:37:19 -04:00
/** Plays the previous song */
2024-04-22 14:18:42 -04:00
public previous() {
2024-06-04 22:37:19 -04:00
if ( this . currentSongs . length === 0 || this . currentPosition <= 0 ) return
2024-04-22 14:18:42 -04:00
this . currentPosition -= 1
writableQueue . set ( this )
}
2024-06-04 22:37:19 -04:00
/** Add songs to the end of the queue */
2024-04-22 14:18:42 -04:00
public enqueue ( . . . songs : Song [ ] ) {
2024-06-04 22:37:19 -04:00
this . originalSongs . push ( . . . songs )
this . currentSongs . push ( . . . songs )
2024-04-22 14:18:42 -04:00
writableQueue . set ( this )
}
2024-06-04 22:37:19 -04:00
/ * *
* @param songs An ordered array of Songs
* @param shuffled Whether or not to shuffle the queue before starting playback . False if not specified
* /
public setQueue ( params : { songs : Song [ ] ; shuffled? : boolean } ) {
if ( params . songs . length === 0 ) return // Should not set a queue with no songs, use clear()
this . originalSongs = params . songs
this . currentSongs = params . shuffled ? fisherYatesShuffle ( params . songs ) : params . songs
this . currentPosition = 0
this . shuffled = params . shuffled ? ? false
2024-05-28 00:46:34 -04:00
writableQueue . set ( this )
}
2024-06-04 22:37:19 -04:00
/** Clears all items from the queue */
2024-04-22 14:18:42 -04:00
public clear() {
this . currentPosition = - 1
2024-06-04 22:37:19 -04:00
this . originalSongs = [ ]
this . currentSongs = [ ]
2024-04-22 14:18:42 -04:00
writableQueue . set ( this )
2024-04-16 10:05:11 -04:00
}
}
2024-04-22 14:18:42 -04:00
const writableQueue : Writable < Queue > = writable ( new Queue ( ) )
export const queue : Readable < Queue > = readonly ( writableQueue )