pipebomb.js
Version:
Library for interacting with Pipe Bomb servers
251 lines • 9.39 kB
JavaScript
import Track from "../music/Track.js";
export default class Playlist {
constructor(context, collectionID, name, owner, trackList) {
this.trackList = null;
this.isDeleted = false;
this.updateCallbacks = [];
this.suggestedTracks = [];
this.suggestedTracksUpdated = null;
this.checkTimer = null;
this.lastChecked = Date.now();
this.context = context;
this.rawCollectionID = collectionID;
this.collectionID = context.prefixAddress(collectionID);
this.name = name;
this.owner = owner;
this.trackList = trackList;
}
async getTrackList() {
this.checkDeletion();
if (this.trackList == null) {
const info = await this.context.makeRequest("get", `v1/playlists/${this.rawCollectionID}`);
if (info.statusCode != 200)
return null;
const newCollection = Playlist.convertJsonToPlaylist(this.context, info.response);
if (!newCollection)
return null;
this.trackList = newCollection.trackList;
}
return Array.from(this.trackList);
}
async getSuggestedTracks() {
this.checkDeletion();
const trackCache = this.context.instance.trackCache;
if (this.suggestedTracksUpdated && this.suggestedTracksUpdated < Date.now() / 1000 - 600) {
return Array.from(this.suggestedTracks);
}
try {
const suggestions = await this.context.makeRequest("get", `v1/playlists/${this.rawCollectionID}/suggested`);
if (suggestions.statusCode != 200 || !Array.isArray(suggestions.response))
throw "invalid response";
const out = [];
for (let json of suggestions.response) {
const track = Track.convertJsonToTrack(this.context, json);
if (track) {
trackCache.updateTrack(track);
out.push(track);
}
}
this.suggestedTracks = out;
this.suggestedTracksUpdated = Math.floor(Date.now() / 1000);
return Array.from(out);
}
catch { }
return [];
}
getName() {
return this.name;
}
async addTracks(...tracks) {
this.checkDeletion();
const response = await this.context.makeRequest("patch", `v1/playlists/${this.rawCollectionID}`, {
tracks: {
add: tracks.map(track => {
return track.trackID;
})
}
});
if (response.statusCode != 200)
throw response;
const newCollection = Playlist.convertJsonToPlaylist(this.context, response.response);
if (!newCollection)
return;
this.copyFromOtherPlaylist(newCollection);
}
async removeTracks(...tracks) {
this.checkDeletion();
const response = await this.context.makeRequest("patch", `v1/playlists/${this.rawCollectionID}`, {
tracks: {
remove: tracks.map(track => {
return track.trackID;
})
}
});
if (response.statusCode != 200)
throw response;
const newCollection = Playlist.convertJsonToPlaylist(this.context, response.response);
if (!newCollection)
return;
this.copyFromOtherPlaylist(newCollection);
}
async setName(name) {
this.checkDeletion();
const response = await this.context.makeRequest("patch", `v1/playlists/${this.rawCollectionID}`, {
name
});
if (response.statusCode != 200)
throw response;
const newCollection = Playlist.convertJsonToPlaylist(this.context, response.response);
if (!newCollection)
return;
this.copyFromOtherPlaylist(newCollection);
}
async deleteCollection() {
const response = await this.context.makeRequest("delete", `v1/playlists/${this.rawCollectionID}`);
if (response.statusCode != 204)
throw response;
this.isDeleted = true;
this.trackList = [];
this.pushToCallbacks();
}
copyFromOtherPlaylist(collection) {
this.checkDeletion();
if (this.collectionID != collection.collectionID)
return;
let changed = false;
if (this.name != collection.name) {
changed = true;
this.name = collection.name;
}
if (collection.trackList !== null) {
if (this.trackList !== null && this.trackList.length == collection.trackList.length) {
for (let i = 0; i < this.trackList.length; i++) {
if (this.trackList[i].trackID != collection.trackList[i].trackID) {
changed = true;
break;
}
}
}
else {
changed = true;
}
this.trackList = collection.trackList;
}
if (changed) {
this.pushToCallbacks();
}
}
static convertJsonToPlaylist(context, json) {
const trackCache = context.instance.trackCache;
const collectionCache = context.instance.collectionCache;
const criteria = [
["string", "number"].includes(typeof json?.collectionID),
typeof json?.name == "string",
json?.owner == null || (typeof json?.owner?.userID == "string" && typeof json?.owner?.username == "string"),
!(json?.trackList) || (() => {
if (!Array.isArray(json.trackList))
return false;
for (let track of json.trackList) {
if (typeof track?.trackID != "string")
return false;
}
return true;
})()
];
for (let option of criteria) {
if (!option)
return null;
}
let owner = json?.owner ? {
userID: context.prefixAddress(json.owner.userID),
username: json.owner.username,
rawID: json.owner.userID
} : null;
let trackList = null;
if (json?.trackList) {
trackList = [];
for (let track of json.trackList) {
let trackObject = Track.convertJsonToTrack(context, track);
if (!trackObject)
continue;
trackList.push(trackObject);
trackCache.updateTrack(trackObject);
}
}
const collection = new Playlist(context, json.collectionID, json.name, owner, trackList);
const existingCollection = collectionCache.getCollection(collection.collectionID);
if (existingCollection instanceof Playlist) {
existingCollection.copyFromOtherPlaylist(collection);
return existingCollection;
}
const output = collectionCache.setCollection(collection);
if (output instanceof Playlist)
return output;
return collection;
}
checkDeletion() {
if (this.isDeleted)
throw "Collection is deleted";
}
pushToCallbacks() {
for (let callback of this.updateCallbacks) {
callback(this);
}
if (this.updateCallbacks.length)
this.setLoop();
}
isCollectionDeleted() {
return this.isDeleted;
}
registerUpdateCallback(callback) {
if (this.updateCallbacks.indexOf(callback) >= 0)
return;
this.updateCallbacks.push(callback);
if (this.updateCallbacks.length == 1)
this.setLoop();
}
unregisterUpdateCallback(callback) {
const index = this.updateCallbacks.indexOf(callback);
if (index >= 0)
this.updateCallbacks.splice(index, 1);
if (!this.updateCallbacks.length && this.checkTimer) {
clearTimeout(this.checkTimer);
this.checkTimer = null;
}
}
setLoop() {
if (this.checkTimer)
clearTimeout(this.checkTimer);
this.checkTimer = setTimeout(async () => {
try {
await this.checkForUpdates(0);
}
catch (e) { }
}, this.context.playlistUpdateFrequency * 1000);
}
async checkForUpdates(outOfDateThreshold) {
if (outOfDateThreshold && Date.now() - outOfDateThreshold * 1000 < this.lastChecked)
return;
if (this.checkTimer)
clearTimeout(this.checkTimer);
try {
const response = await this.context.makeRequest("get", `v1/playlists/${this.rawCollectionID}`);
this.lastChecked = Date.now();
if (response.statusCode != 200)
throw response;
const collection = Playlist.convertJsonToPlaylist(this.context, response.response);
this.copyFromOtherPlaylist(collection);
if (this.updateCallbacks.length)
this.setLoop();
}
catch (e) {
if (this.updateCallbacks.length)
this.setLoop();
throw e;
}
}
getThumbnailUrl() {
return `${this.context.getHost()}/v1/playlists/${this.rawCollectionID}/thumbnail`;
}
}
//# sourceMappingURL=Playlist.js.map