queueSongs.js 7.4 KB

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