203 lines
4.4 KiB
JavaScript
203 lines
4.4 KiB
JavaScript
const { Track } = require('./track')
|
|
const { from_seconds_to_string } = require('./helpers/duration')
|
|
|
|
// side effect
|
|
function shuffle_array(arr) {
|
|
for (let i = arr.length - 1; i > 0; i--) {
|
|
const j = Math.floor(Math.random() * (i + 1));
|
|
[arr[i], arr[j]] = [arr[j], arr[i]];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @class
|
|
*/
|
|
class Playlist {
|
|
#current_index
|
|
#tracks
|
|
#name
|
|
#touched
|
|
|
|
/**
|
|
* @constructor
|
|
* @param {Object} parameters={}
|
|
* @param {Track[]} [parameters.tracks=[]] The tracks that compose the playlist
|
|
* @param {number} [parameters.current_index=0] The id of the currently played track
|
|
*/
|
|
constructor({ tracks = [], current_index = 0, name = null } = {}) {
|
|
this.#current_index = current_index
|
|
this.#tracks = tracks.map(t => t instanceof Track ? t : new Track(t))
|
|
this.#name = name
|
|
this.#touched = false
|
|
}
|
|
|
|
/**
|
|
* @method
|
|
* @returns {Playlist} A copy of the current playlist
|
|
*/
|
|
clone() {
|
|
return new Playlist({
|
|
current_index: this.#current_index,
|
|
tracks: this.#tracks.map(t => t.clone()),
|
|
name: this.#name
|
|
})
|
|
}
|
|
|
|
/**
|
|
* @method
|
|
* @desc Shuffle the track list
|
|
* @returns {Playlist} The current playlist
|
|
*/
|
|
shuffle() {
|
|
shuffle_array(this.#tracks)
|
|
this.#touched = true
|
|
return this
|
|
}
|
|
|
|
/**
|
|
* @method
|
|
* @returns {Object} A JSON object that describe the tracklist
|
|
*/
|
|
toJSON() {
|
|
return this.#tracks
|
|
}
|
|
|
|
map_tracks(callback) {
|
|
const current = this.get_current_track()
|
|
return this.#tracks.map((t, idx, all) => callback(t, idx, all, current))
|
|
}
|
|
|
|
next({ loop = 0 }) {
|
|
this.#current_index = (this.#current_index + 1)
|
|
if (loop > 0) {
|
|
this.#current_index %= this.#tracks.length
|
|
}
|
|
return this
|
|
}
|
|
|
|
previous({ loop = 0 }) {
|
|
this.#current_index = this.#current_index - 1
|
|
if (this.#current_index < 0) this.#current_index = 0
|
|
if (loop > 0) {
|
|
this.#current_index %= this.#tracks.length
|
|
}
|
|
return this
|
|
}
|
|
|
|
goto(idx) {
|
|
if (this.#tracks[idx - 1] == null) {
|
|
return false
|
|
}
|
|
this.#current_index = idx - 1
|
|
return true
|
|
}
|
|
|
|
/**
|
|
* @returns {Track|null} The track that is currenly played, or <code>null</code> if it doesn't exist
|
|
*/
|
|
get_current_track() {
|
|
return this.#tracks[this.#current_index] ?? null
|
|
}
|
|
|
|
/**
|
|
* @param {number} track_index The index of the track to be retrieved
|
|
* @returns {Track|null} The track that is looked for, or <code>null</code> if it doesn't exist
|
|
*/
|
|
get_track_by_index(track_index) {
|
|
return this.#tracks[track_index] ?? null
|
|
}
|
|
|
|
/**
|
|
* @method
|
|
* @desc Add a new track a the end of the playlist
|
|
* @param {Track}
|
|
* @returns {Playlist} The current playlist
|
|
*/
|
|
push(track) {
|
|
this.#tracks.push(track)
|
|
this.#touched = true
|
|
return this
|
|
}
|
|
|
|
/**
|
|
* @method
|
|
* @desc Remove a track from the playlist
|
|
* @param {number} The index of the track to remove
|
|
* @returns {boolean} <code>true</code> if the element was successfully removed, <code>false</code> otherwise
|
|
*/
|
|
delete(track_index) {
|
|
if (track_index >= this.#tracks.length) {
|
|
return false
|
|
}
|
|
if (track_index < this.#current_index) {
|
|
this.#current_index -= 1
|
|
}
|
|
delete this.#tracks[track_index]
|
|
this.#tracks = this.#tracks.filter(Boolean)
|
|
this.#touched = true
|
|
return true
|
|
}
|
|
|
|
/**
|
|
* @method
|
|
* @returns {boolean} <code>true</code> if the playlist is empty, <code>false</code> otherwise
|
|
*/
|
|
is_empty() {
|
|
return this.#tracks.length === 0
|
|
}
|
|
|
|
/**
|
|
* @method
|
|
* @returns {number} The number of tracks in the playlist
|
|
*/
|
|
get_length() {
|
|
return this.#tracks.length
|
|
}
|
|
|
|
/**
|
|
* @method
|
|
* @return {number} The duration of the playlist, in seconds
|
|
*/
|
|
get_duration() {
|
|
return this.#tracks.reduce((a, t) => a + t.get_length(), 0)
|
|
}
|
|
|
|
/**
|
|
* @method
|
|
* @return {durationString} The duration of the playlist, as a string
|
|
*/
|
|
get_duration_as_string() {
|
|
return from_seconds_to_string(this.get_duration())
|
|
}
|
|
|
|
/**
|
|
* @method
|
|
* @returns {string} a string representation of the playlist
|
|
*/
|
|
toString() {
|
|
return this.#tracks.join(', ')
|
|
}
|
|
|
|
get_name() {
|
|
return this.#name || "(unamed)"
|
|
}
|
|
|
|
set_name(name = null) {
|
|
this.#name = name
|
|
}
|
|
|
|
is_named() {
|
|
return Boolean(this.#name)
|
|
}
|
|
|
|
untouch() {
|
|
this.#touched = false
|
|
}
|
|
|
|
is_touched() {
|
|
return this.#touched
|
|
}
|
|
}
|
|
|
|
module.exports = { Playlist }
|