queueSongs.js 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. 'use strict';
  2. const config = require('config');
  3. const async = require('async');
  4. const request = require('request');
  5. const hooks = require('./hooks');
  6. const moduleManager = require("../../index");
  7. const db = moduleManager.modules["db"];
  8. const utils = moduleManager.modules["utils"];
  9. const logger = moduleManager.modules["logger"];
  10. const cache = moduleManager.modules["cache"];
  11. cache.sub('queue.newSong', songId => {
  12. db.models.queueSong.findOne({_id: songId}, (err, song) => {
  13. utils.emitToRoom('admin.queue', 'event:admin.queueSong.added', song);
  14. });
  15. });
  16. cache.sub('queue.removedSong', songId => {
  17. utils.emitToRoom('admin.queue', 'event:admin.queueSong.removed', songId);
  18. });
  19. cache.sub('queue.update', songId => {
  20. db.models.queueSong.findOne({_id: songId}, (err, song) => {
  21. utils.emitToRoom('admin.queue', 'event:admin.queueSong.updated', song);
  22. });
  23. });
  24. let lib = {
  25. /**
  26. * Returns the length of the queue songs list
  27. *
  28. * @param session
  29. * @param cb
  30. */
  31. length: hooks.adminRequired((session, cb) => {
  32. async.waterfall([
  33. (next) => {
  34. db.models.queueSong.countDocuments({}, next);
  35. }
  36. ], async (err, count) => {
  37. if (err) {
  38. err = await utils.getError(err);
  39. logger.error("QUEUE_SONGS_LENGTH", `Failed to get length from queue songs. "${err}"`);
  40. return cb({'status': 'failure', 'message': err});
  41. }
  42. logger.success("QUEUE_SONGS_LENGTH", `Got length from queue songs successfully.`);
  43. cb(count);
  44. });
  45. }),
  46. /**
  47. * Gets a set of queue songs
  48. *
  49. * @param session
  50. * @param set - the set number to return
  51. * @param cb
  52. */
  53. getSet: hooks.adminRequired((session, set, cb) => {
  54. async.waterfall([
  55. (next) => {
  56. db.models.queueSong.find({}).skip(15 * (set - 1)).limit(15).exec(next);
  57. },
  58. ], async (err, songs) => {
  59. if (err) {
  60. err = await utils.getError(err);
  61. logger.error("QUEUE_SONGS_GET_SET", `Failed to get set from queue songs. "${err}"`);
  62. return cb({'status': 'failure', 'message': err});
  63. }
  64. logger.success("QUEUE_SONGS_GET_SET", `Got set from queue songs successfully.`);
  65. cb(songs);
  66. });
  67. }),
  68. /**
  69. * Updates a queuesong
  70. *
  71. * @param {Object} session - the session object automatically added by socket.io
  72. * @param {String} songId - the id of the queuesong that gets updated
  73. * @param {Object} updatedSong - the object of the updated queueSong
  74. * @param {Function} cb - gets called with the result
  75. */
  76. update: hooks.adminRequired((session, songId, updatedSong, cb) => {
  77. async.waterfall([
  78. (next) => {
  79. db.models.queueSong.findOne({_id: songId}, next);
  80. },
  81. (song, next) => {
  82. if(!song) return next('Song not found');
  83. let updated = false;
  84. let $set = {};
  85. for (let prop in updatedSong) if (updatedSong[prop] !== song[prop]) $set[prop] = updatedSong[prop]; updated = true;
  86. if (!updated) return next('No properties changed');
  87. db.models.queueSong.updateOne({_id: songId}, {$set}, {runValidators: true}, next);
  88. }
  89. ], async (err) => {
  90. if (err) {
  91. err = await utils.getError(err);
  92. logger.error("QUEUE_UPDATE", `Updating queuesong "${songId}" failed for user ${session.userId}. "${err}"`);
  93. return cb({status: 'failure', message: err});
  94. }
  95. cache.pub('queue.update', songId);
  96. logger.success("QUEUE_UPDATE", `User "${session.userId}" successfully update queuesong "${songId}".`);
  97. return cb({status: 'success', message: 'Successfully updated song.'});
  98. });
  99. }),
  100. /**
  101. * Removes a queuesong
  102. *
  103. * @param {Object} session - the session object automatically added by socket.io
  104. * @param {String} songId - the id of the queuesong that gets removed
  105. * @param {Function} cb - gets called with the result
  106. */
  107. remove: hooks.adminRequired((session, songId, cb, userId) => {
  108. async.waterfall([
  109. (next) => {
  110. db.models.queueSong.deleteOne({_id: songId}, next);
  111. }
  112. ], async (err) => {
  113. if (err) {
  114. err = await utils.getError(err);
  115. logger.error("QUEUE_REMOVE", `Removing queuesong "${songId}" failed for user ${session.userId}. "${err}"`);
  116. return cb({status: 'failure', message: err});
  117. }
  118. cache.pub('queue.removedSong', songId);
  119. logger.success("QUEUE_REMOVE", `User "${session.userId}" successfully removed queuesong "${songId}".`);
  120. return cb({status: 'success', message: 'Successfully updated song.'});
  121. });
  122. }),
  123. /**
  124. * Creates a queuesong
  125. *
  126. * @param {Object} session - the session object automatically added by socket.io
  127. * @param {String} songId - the id of the song that gets added
  128. * @param {Function} cb - gets called with the result
  129. */
  130. add: hooks.loginRequired((session, songId, cb) => {
  131. let requestedAt = Date.now();
  132. async.waterfall([
  133. (next) => {
  134. db.models.queueSong.findOne({songId}, next);
  135. },
  136. (song, next) => {
  137. if (song) return next('This song is already in the queue.');
  138. db.models.song.findOne({songId}, next);
  139. },
  140. // Get YouTube data from id
  141. (song, next) => {
  142. if (song) return next('This song has already been added.');
  143. //TODO Add err object as first param of callback
  144. utils.getSongFromYouTube(songId, (song) => {
  145. song.duration = -1;
  146. song.artists = [];
  147. song.genres = [];
  148. song.skipDuration = 0;
  149. song.thumbnail = `${config.get("domain")}/assets/notes.png`;
  150. song.explicit = false;
  151. song.requestedBy = session.userId;
  152. song.requestedAt = requestedAt;
  153. next(null, song);
  154. });
  155. },
  156. /*(newSong, next) => {
  157. utils.getSongFromSpotify(newSong, (err, song) => {
  158. if (!song) next(null, newSong);
  159. else next(err, song);
  160. });
  161. },*/
  162. (newSong, next) => {
  163. const song = new db.models.queueSong(newSong);
  164. song.save({ validateBeforeSave: false }, (err, song) => {
  165. if (err) return next(err);
  166. next(null, song);
  167. });
  168. },
  169. (newSong, next) => {
  170. db.models.user.findOne({ _id: session.userId }, (err, user) => {
  171. if (err) next(err, newSong);
  172. else {
  173. user.statistics.songsRequested = user.statistics.songsRequested + 1;
  174. user.save(err => {
  175. if (err) return next(err, newSong);
  176. else next(null, newSong);
  177. });
  178. }
  179. });
  180. }
  181. ], async (err, newSong) => {
  182. if (err) {
  183. err = await utils.getError(err);
  184. logger.error("QUEUE_ADD", `Adding queuesong "${songId}" failed for user ${session.userId}. "${err}"`);
  185. return cb({status: 'failure', message: err});
  186. }
  187. cache.pub('queue.newSong', newSong._id);
  188. logger.success("QUEUE_ADD", `User "${session.userId}" successfully added queuesong "${songId}".`);
  189. return cb({ status: 'success', message: 'Successfully added that song to the queue' });
  190. });
  191. }),
  192. /**
  193. * Adds a set of songs to the queue
  194. *
  195. * @param {Object} session - the session object automatically added by socket.io
  196. * @param {String} url - the url of the the YouTube playlist
  197. * @param {Function} cb - gets called with the result
  198. */
  199. addSetToQueue: hooks.loginRequired((session, url, cb) => {
  200. async.waterfall([
  201. (next) => {
  202. utils.getPlaylistFromYouTube(url, songs => {
  203. next(null, songs);
  204. });
  205. },
  206. (songs, next) => {
  207. let processed = 0;
  208. function checkDone() {
  209. if (processed === songs.length) next();
  210. }
  211. for (let s = 0; s < songs.length; s++) {
  212. lib.add(session, songs[s].contentDetails.videoId, () => {
  213. processed++;
  214. checkDone();
  215. });
  216. }
  217. }
  218. ], async (err) => {
  219. if (err) {
  220. err = await utils.getError(err);
  221. logger.error("QUEUE_IMPORT", `Importing a YouTube playlist to the queue failed for user "${session.userId}". "${err}"`);
  222. return cb({ status: 'failure', message: err});
  223. } else {
  224. logger.success("QUEUE_IMPORT", `Successfully imported a YouTube playlist to the queue for user "${session.userId}".`);
  225. cb({ status: 'success', message: 'Playlist has been successfully imported.' });
  226. }
  227. });
  228. })
  229. };
  230. module.exports = lib;