| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449 | import config from "config";import async from "async";import { isAdminRequired, isLoginRequired } from "./hooks";import moduleManager from "../../index";const DBModule = moduleManager.modules.db;const UtilsModule = moduleManager.modules.utils;const IOModule = moduleManager.modules.io;const YouTubeModule = moduleManager.modules.youtube;const CacheModule = moduleManager.modules.cache;CacheModule.runJob("SUB", {	channel: "queue.newSong",	cb: async songId => {		const queueSongModel = await DBModule.runJob("GET_MODEL", {			modelName: "queueSong"		});		queueSongModel.findOne({ _id: songId }, (err, song) => {			IOModule.runJob("EMIT_TO_ROOM", {				room: "admin.queue",				args: ["event:admin.queueSong.added", song]			});		});	}});CacheModule.runJob("SUB", {	channel: "queue.removedSong",	cb: songId => {		IOModule.runJob("EMIT_TO_ROOM", {			room: "admin.queue",			args: ["event:admin.queueSong.removed", songId]		});	}});CacheModule.runJob("SUB", {	channel: "queue.update",	cb: async songId => {		const queueSongModel = await DBModule.runJob("GET_MODEL", {			modelName: "queueSong"		});		queueSongModel.findOne({ _id: songId }, (err, song) => {			IOModule.runJob("EMIT_TO_ROOM", {				room: "admin.queue",				args: ["event:admin.queueSong.updated", song]			});		});	}});export default {	/**	 * Returns the length of the queue songs list	 *	 * @param session	 * @param cb	 */	length: isAdminRequired(async function length(session, cb) {		const queueSongModel = await DBModule.runJob(			"GET_MODEL",			{				modelName: "queueSong"			},			this		);		async.waterfall(			[				next => {					queueSongModel.countDocuments({}, next);				}			],			async (err, count) => {				if (err) {					err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);					this.log("ERROR", "QUEUE_SONGS_LENGTH", `Failed to get length from queue songs. "${err}"`);					return cb({ status: "failure", message: err });				}				this.log("SUCCESS", "QUEUE_SONGS_LENGTH", `Got length from queue songs successfully.`);				return cb(count);			}		);	}),	/**	 * Gets a set of queue songs	 *	 * @param session	 * @param set - the set number to return	 * @param cb	 */	getSet: isAdminRequired(async function getSet(session, set, cb) {		const queueSongModel = await DBModule.runJob(			"GET_MODEL",			{				modelName: "queueSong"			},			this		);		async.waterfall(			[				next => {					queueSongModel						.find({})						.skip(15 * (set - 1))						.limit(15)						.exec(next);				}			],			async (err, songs) => {				if (err) {					err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);					this.log("ERROR", "QUEUE_SONGS_GET_SET", `Failed to get set from queue songs. "${err}"`);					return cb({ status: "failure", message: err });				}				this.log("SUCCESS", "QUEUE_SONGS_GET_SET", `Got set from queue songs successfully.`);				return cb(songs);			}		);	}),	/**	 * Gets a song from the Musare song id	 *	 * @param {object} session - the session object automatically added by socket.io	 * @param {string} songId - the Musare song id	 * @param {Function} cb	 */	getSongFromMusareId: isAdminRequired(async function getSong(session, songId, cb) {		const queueSongModel = await DBModule.runJob(			"GET_MODEL",			{				modelName: "queueSong"			},			this		);		async.waterfall(			[				next => {					queueSongModel.findOne({ _id: songId }, next);				}			],			async (err, song) => {				if (err) {					err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);					this.log("ERROR", "QUEUE_SONGS_GET_SONG_FROM_MUSARE_ID", `Failed to get song ${songId}. "${err}"`);					return cb({ status: "failure", message: err });				}				this.log("SUCCESS", "QUEUE_SONGS_GET_SONG_FROM_MUSARE_ID", `Got song ${songId} successfully.`);				return cb({ status: "success", data: { song } });			}		);	}),	/**	 * 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: isAdminRequired(async function update(session, songId, updatedSong, cb) {		const queueSongModel = await DBModule.runJob(			"GET_MODEL",			{				modelName: "queueSong"			},			this		);		async.waterfall(			[				next => {					queueSongModel.findOne({ _id: songId }, next);				},				(song, next) => {					if (!song) return next("Song not found");					let updated = false;					const $set = {};					Object.keys(updatedSong).forEach(prop => {						if (updatedSong[prop] !== song[prop]) $set[prop] = updatedSong[prop];					});					updated = true;					if (!updated) return next("No properties changed");					return queueSongModel.updateOne({ _id: songId }, { $set }, { runValidators: true }, next);				}			],			async err => {				if (err) {					err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);					this.log(						"ERROR",						"QUEUE_UPDATE",						`Updating queuesong "${songId}" failed for user ${session.userId}. "${err}"`					);					return cb({ status: "failure", message: err });				}				CacheModule.runJob("PUB", { channel: "queue.update", value: songId });				this.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: isAdminRequired(async function remove(session, songId, cb) {		const queueSongModel = await DBModule.runJob(			"GET_MODEL",			{				modelName: "queueSong"			},			this		);		async.waterfall(			[				next => {					queueSongModel.deleteOne({ _id: songId }, next);				}			],			async err => {				if (err) {					err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);					this.log(						"ERROR",						"QUEUE_REMOVE",						`Removing queuesong "${songId}" failed for user ${session.userId}. "${err}"`					);					return cb({ status: "failure", message: err });				}				CacheModule.runJob("PUB", {					channel: "queue.removedSong",					value: songId				});				this.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: isLoginRequired(async function add(session, songId, cb) {		const requestedAt = Date.now();		const songModel = await DBModule.runJob("GET_MODEL", { modelName: "song" }, this);		const userModel = await DBModule.runJob("GET_MODEL", { modelName: "user" }, this);		const QueueSongModel = await DBModule.runJob(			"GET_MODEL",			{				modelName: "queueSong"			},			this		);		async.waterfall(			[				next => {					QueueSongModel.findOne({ songId }, next);				},				(song, next) => {					if (song) return next("This song is already in the queue.");					return 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					return YouTubeModule.runJob("GET_SONG", { songId }, this)						.then(response => {							const { song } = response;							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) => {					const song = new QueueSongModel(newSong);					song.save({ validateBeforeSave: false }, (err, song) => {						if (err) return next(err);						return next(null, song);					});				},				(newSong, next) => {					userModel.findOne({ _id: session.userId }, (err, user) => {						if (err) return next(err, newSong);						user.statistics.songsRequested += 1;						return user.save(err => {							if (err) return next(err, newSong);							return next(null, newSong);						});					});				}			],			async (err, newSong) => {				if (err) {					err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);					this.log(						"ERROR",						"QUEUE_ADD",						`Adding queuesong "${songId}" failed for user ${session.userId}. "${err}"`					);					return cb({ status: "failure", message: err });				}				CacheModule.runJob("PUB", {					channel: "queue.newSong",					value: newSong._id				});				this.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 {boolean} musicOnly - whether to only get music from the playlist	 * @param {Function} cb - gets called with the result	 */	addSetToQueue: isLoginRequired(function addSetToQueue(session, url, musicOnly, cb) {		async.waterfall(			[				next => {					YouTubeModule.runJob(						"GET_PLAYLIST",						{							url,							musicOnly						},						this					)						.then(res => {							next(null, res.songs);						})						.catch(next);				},				(songIds, next) => {					let successful = 0;					let failed = 0;					let alreadyInQueue = 0;					let alreadyAdded = 0;					if (songIds.length === 0) next();					async.eachLimit(						songIds,						1,						(songId, next) => {							IOModule.runJob(								"RUN_ACTION2",								{									session,									namespace: "queueSongs",									action: "add",									args: [songId]								},								this							)								.then(res => {									if (res.status === "success") successful += 1;									else failed += 1;									if (res.message === "This song is already in the queue.") alreadyInQueue += 1;									if (res.message === "This song has already been added.") alreadyAdded += 1;								})								.catch(() => {									failed += 1;								})								.finally(() => {									next();								});						},						() => {							next(null, { successful, failed, alreadyInQueue, alreadyAdded });						}					);				}			],			async (err, response) => {				if (err) {					err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);					this.log(						"ERROR",						"QUEUE_IMPORT",						`Importing a YouTube playlist to the queue failed for user "${session.userId}". "${err}"`					);					return cb({ status: "failure", message: err });				}				this.log(					"SUCCESS",					"QUEUE_IMPORT",					`Successfully imported a YouTube playlist to the queue for user "${session.userId}".`				);				return cb({					status: "success",					message: `Playlist is done importing. ${response.successful} were added succesfully, ${response.failed} failed (${response.alreadyInQueue} were already in queue, ${response.alreadyAdded} were already added)`				});			}		);	})};
 |