"use strict"; const config = require("config"); const async = require("async"); const request = require("request"); const hooks = require("./hooks"); const db = require("../db"); const utils = require("../utils"); const cache = require("../cache"); // const logger = moduleManager.modules["logger"]; cache.runJob("SUB", { channel: "queue.newSong", cb: async (songId) => { const queueSongModel = await db.runJob("GET_MODEL", { modelName: "queueSong", }); queueSongModel.findOne({ _id: songId }, (err, song) => { utils.runJob("EMIT_TO_ROOM", { room: "admin.queue", args: ["event:admin.queueSong.added", song], }); }); }, }); cache.runJob("SUB", { channel: "queue.removedSong", cb: (songId) => { utils.runJob("EMIT_TO_ROOM", { room: "admin.queue", args: ["event:admin.queueSong.removed", songId], }); }, }); cache.runJob("SUB", { channel: "queue.update", cb: async (songId) => { const queueSongModel = await db.runJob("GET_MODEL", { modelName: "queueSong", }); queueSongModel.findOne({ _id: songId }, (err, song) => { utils.runJob("EMIT_TO_ROOM", { room: "admin.queue", args: ["event:admin.queueSong.updated", song], }); }); }, }); let lib = { /** * Returns the length of the queue songs list * * @param session * @param cb */ length: hooks.adminRequired(async (session, cb) => { const queueSongModel = await db.runJob("GET_MODEL", { modelName: "queueSong", }); async.waterfall( [ (next) => { queueSongModel.countDocuments({}, next); }, ], async (err, count) => { if (err) { err = await utils.runJob("GET_ERROR", { error: err }); console.log( "ERROR", "QUEUE_SONGS_LENGTH", `Failed to get length from queue songs. "${err}"` ); return cb({ status: "failure", message: err }); } console.log( "SUCCESS", "QUEUE_SONGS_LENGTH", `Got length from queue songs successfully.` ); cb(count); } ); }), /** * Gets a set of queue songs * * @param session * @param set - the set number to return * @param cb */ getSet: hooks.adminRequired(async (session, set, cb) => { const queueSongModel = await db.runJob("GET_MODEL", { modelName: "queueSong", }); async.waterfall( [ (next) => { queueSongModel .find({}) .skip(15 * (set - 1)) .limit(15) .exec(next); }, ], async (err, songs) => { if (err) { err = await utils.runJob("GET_ERROR", { error: err }); console.log( "ERROR", "QUEUE_SONGS_GET_SET", `Failed to get set from queue songs. "${err}"` ); return cb({ status: "failure", message: err }); } console.log( "SUCCESS", "QUEUE_SONGS_GET_SET", `Got set from queue songs successfully.` ); cb(songs); } ); }), /** * Updates a queuesong * * @param {Object} session - the session object automatically added by socket.io * @param {String} songId - the id of the queuesong that gets updated * @param {Object} updatedSong - the object of the updated queueSong * @param {Function} cb - gets called with the result */ update: hooks.adminRequired(async (session, songId, updatedSong, cb) => { const queueSongModel = await db.runJob("GET_MODEL", { modelName: "queueSong", }); async.waterfall( [ (next) => { queueSongModel.findOne({ _id: songId }, next); }, (song, next) => { if (!song) return next("Song not found"); let updated = false; let $set = {}; for (let prop in updatedSong) if (updatedSong[prop] !== song[prop]) $set[prop] = updatedSong[prop]; updated = true; if (!updated) return next("No properties changed"); queueSongModel.updateOne( { _id: songId }, { $set }, { runValidators: true }, next ); }, ], async (err) => { if (err) { err = await utils.runJob("GET_ERROR", { error: err }); console.log( "ERROR", "QUEUE_UPDATE", `Updating queuesong "${songId}" failed for user ${session.userId}. "${err}"` ); return cb({ status: "failure", message: err }); } cache.runJob("PUB", { channel: "queue.update", value: songId }); console.log( "SUCCESS", "QUEUE_UPDATE", `User "${session.userId}" successfully update queuesong "${songId}".` ); return cb({ status: "success", message: "Successfully updated song.", }); } ); }), /** * Removes a queuesong * * @param {Object} session - the session object automatically added by socket.io * @param {String} songId - the id of the queuesong that gets removed * @param {Function} cb - gets called with the result */ remove: hooks.adminRequired(async (session, songId, cb, userId) => { const queueSongModel = await db.runJob("GET_MODEL", { modelName: "queueSong", }); async.waterfall( [ (next) => { queueSongModel.deleteOne({ _id: songId }, next); }, ], async (err) => { if (err) { err = await utils.runJob("GET_ERROR", { error: err }); console.log( "ERROR", "QUEUE_REMOVE", `Removing queuesong "${songId}" failed for user ${session.userId}. "${err}"` ); return cb({ status: "failure", message: err }); } cache.runJob("PUB", { channel: "queue.removedSong", value: songId, }); console.log( "SUCCESS", "QUEUE_REMOVE", `User "${session.userId}" successfully removed queuesong "${songId}".` ); return cb({ status: "success", message: "Successfully updated song.", }); } ); }), /** * Creates a queuesong * * @param {Object} session - the session object automatically added by socket.io * @param {String} songId - the id of the song that gets added * @param {Function} cb - gets called with the result */ add: hooks.loginRequired(async (session, songId, cb) => { let requestedAt = Date.now(); const songModel = await db.runJob("GET_MODEL", { modelName: "song" }); const userModel = await db.runJob("GET_MODEL", { modelName: "user" }); const queueSongModel = await db.runJob("GET_MODEL", { modelName: "queueSong", }); async.waterfall( [ (next) => { queueSongModel.findOne({ songId }, next); }, (song, next) => { if (song) return next("This song is already in the queue."); songModel.findOne({ songId }, next); }, // Get YouTube data from id (song, next) => { if (song) return next("This song has already been added."); //TODO Add err object as first param of callback utils .runJob("GET_SONG_FROM_YOUTUBE", { songId }) .then((response) => { const song = response.song; song.duration = -1; song.artists = []; song.genres = []; song.skipDuration = 0; song.thumbnail = `${config.get( "domain" )}/assets/notes.png`; song.explicit = false; song.requestedBy = session.userId; song.requestedAt = requestedAt; next(null, song); }) .catch(next); }, /*(newSong, next) => { utils.getSongFromSpotify(newSong, (err, song) => { if (!song) next(null, newSong); else next(err, song); }); },*/ (newSong, next) => { const song = new queueSongModel(newSong); song.save({ validateBeforeSave: false }, (err, song) => { if (err) return next(err); next(null, song); }); }, (newSong, next) => { userModel.findOne({ _id: session.userId }, (err, user) => { if (err) next(err, newSong); else { user.statistics.songsRequested = user.statistics.songsRequested + 1; user.save((err) => { if (err) return next(err, newSong); else next(null, newSong); }); } }); }, ], async (err, newSong) => { if (err) { err = await utils.runJob("GET_ERROR", { error: err }); console.log( "ERROR", "QUEUE_ADD", `Adding queuesong "${songId}" failed for user ${session.userId}. "${err}"` ); return cb({ status: "failure", message: err }); } cache.runJob("PUB", { channel: "queue.newSong", value: newSong._id, }); console.log( "SUCCESS", "QUEUE_ADD", `User "${session.userId}" successfully added queuesong "${songId}".` ); return cb({ status: "success", message: "Successfully added that song to the queue", }); } ); }), /** * Adds a set of songs to the queue * * @param {Object} session - the session object automatically added by socket.io * @param {String} url - the url of the the YouTube playlist * @param {Function} cb - gets called with the result */ addSetToQueue: hooks.loginRequired((session, url, cb) => { async.waterfall( [ (next) => { utils .runJob("GET_PLAYLIST_FROM_YOUTUBE", { url, musicOnly: false, }) .then((songIds) => next(null, songIds)) .catch(next); }, (songIds, next) => { let processed = 0; function checkDone() { if (processed === songIds.length) next(); } for (let s = 0; s < songIds.length; s++) { lib.add(session, songIds[s], () => { processed++; checkDone(); }); } }, ], async (err) => { if (err) { err = await utils.runJob("GET_ERROR", { error: err }); console.log( "ERROR", "QUEUE_IMPORT", `Importing a YouTube playlist to the queue failed for user "${session.userId}". "${err}"` ); return cb({ status: "failure", message: err }); } else { console.log( "SUCCESS", "QUEUE_IMPORT", `Successfully imported a YouTube playlist to the queue for user "${session.userId}".` ); cb({ status: "success", message: "Playlist has been successfully imported.", }); } } ); }), }; module.exports = lib;