songs.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  1. 'use strict';
  2. const async = require('async');
  3. const hooks = require('./hooks');
  4. const queueSongs = require('./queueSongs');
  5. const moduleManager = require("../../index");
  6. const db = moduleManager.modules["db"];
  7. const songs = moduleManager.modules["songs"];
  8. const cache = moduleManager.modules["cache"];
  9. const utils = moduleManager.modules["utils"];
  10. const logger = moduleManager.modules["logger"];
  11. cache.sub('song.removed', songId => {
  12. utils.emitToRoom('admin.songs', 'event:admin.song.removed', songId);
  13. });
  14. cache.sub('song.added', songId => {
  15. db.models.song.findOne({_id: songId}, (err, song) => {
  16. utils.emitToRoom('admin.songs', 'event:admin.song.added', song);
  17. });
  18. });
  19. cache.sub('song.updated', songId => {
  20. db.models.song.findOne({_id: songId}, (err, song) => {
  21. utils.emitToRoom('admin.songs', 'event:admin.song.updated', song);
  22. });
  23. });
  24. cache.sub('song.like', (data) => {
  25. utils.emitToRoom(`song.${data.songId}`, 'event:song.like', {songId: data.songId, likes: data.likes, dislikes: data.dislikes});
  26. utils.socketsFromUser(data.userId, (sockets) => {
  27. sockets.forEach((socket) => {
  28. socket.emit('event:song.newRatings', {songId: data.songId, liked: true, disliked: false});
  29. });
  30. });
  31. });
  32. cache.sub('song.dislike', (data) => {
  33. utils.emitToRoom(`song.${data.songId}`, 'event:song.dislike', {songId: data.songId, likes: data.likes, dislikes: data.dislikes});
  34. utils.socketsFromUser(data.userId, (sockets) => {
  35. sockets.forEach((socket) => {
  36. socket.emit('event:song.newRatings', {songId: data.songId, liked: false, disliked: true});
  37. });
  38. });
  39. });
  40. cache.sub('song.unlike', (data) => {
  41. utils.emitToRoom(`song.${data.songId}`, 'event:song.unlike', {songId: data.songId, likes: data.likes, dislikes: data.dislikes});
  42. utils.socketsFromUser(data.userId, (sockets) => {
  43. sockets.forEach((socket) => {
  44. socket.emit('event:song.newRatings', {songId: data.songId, liked: false, disliked: false});
  45. });
  46. });
  47. });
  48. cache.sub('song.undislike', (data) => {
  49. utils.emitToRoom(`song.${data.songId}`, 'event:song.undislike', {songId: data.songId, likes: data.likes, dislikes: data.dislikes});
  50. utils.socketsFromUser(data.userId, (sockets) => {
  51. sockets.forEach((socket) => {
  52. socket.emit('event:song.newRatings', {songId: data.songId, liked: false, disliked: false});
  53. });
  54. });
  55. });
  56. module.exports = {
  57. /**
  58. * Returns the length of the songs list
  59. *
  60. * @param session
  61. * @param cb
  62. */
  63. length: hooks.adminRequired((session, cb) => {
  64. async.waterfall([
  65. (next) => {
  66. db.models.song.countDocuments({}, next);
  67. }
  68. ], async (err, count) => {
  69. if (err) {
  70. err = await utils.getError(err);
  71. logger.error("SONGS_LENGTH", `Failed to get length from songs. "${err}"`);
  72. return cb({'status': 'failure', 'message': err});
  73. }
  74. logger.success("SONGS_LENGTH", `Got length from songs successfully.`);
  75. cb(count);
  76. });
  77. }),
  78. /**
  79. * Gets a set of songs
  80. *
  81. * @param session
  82. * @param set - the set number to return
  83. * @param cb
  84. */
  85. getSet: hooks.adminRequired((session, set, cb) => {
  86. async.waterfall([
  87. (next) => {
  88. db.models.song.find({}).skip(15 * (set - 1)).limit(15).exec(next);
  89. },
  90. ], async (err, songs) => {
  91. if (err) {
  92. err = await utils.getError(err);
  93. logger.error("SONGS_GET_SET", `Failed to get set from songs. "${err}"`);
  94. return cb({'status': 'failure', 'message': err});
  95. }
  96. logger.success("SONGS_GET_SET", `Got set from songs successfully.`);
  97. cb(songs);
  98. });
  99. }),
  100. /**
  101. * Gets a song
  102. *
  103. * @param session
  104. * @param songId - the song id
  105. * @param cb
  106. */
  107. getSong: hooks.adminRequired((session, songId, cb) => {
  108. console.log(songId);
  109. async.waterfall([
  110. (next) => {
  111. db.models.song.findOne({ songId }).exec(next);
  112. }
  113. ], async (err, song) => {
  114. if (err) {
  115. err = await utils.getError(err);
  116. logger.error("SONGS_GET_SONG", `Failed to get song ${songId}. "${err}"`);
  117. return cb({ status: 'failure', message: err });
  118. } else {
  119. logger.success("SONGS_GET_SONG", `Got song ${songId} successfully.`);
  120. cb({ status: "success", data: song });
  121. }
  122. });
  123. }),
  124. /**
  125. * Updates a song
  126. *
  127. * @param session
  128. * @param songId - the song id
  129. * @param song - the updated song object
  130. * @param cb
  131. */
  132. update: hooks.adminRequired((session, songId, song, cb) => {
  133. async.waterfall([
  134. (next) => {
  135. db.models.song.updateOne({_id: songId}, song, {runValidators: true}, next);
  136. },
  137. (res, next) => {
  138. songs.updateSong(songId, next);
  139. }
  140. ], async (err, song) => {
  141. if (err) {
  142. err = await utils.getError(err);
  143. logger.error("SONGS_UPDATE", `Failed to update song "${songId}". "${err}"`);
  144. return cb({'status': 'failure', 'message': err});
  145. }
  146. logger.success("SONGS_UPDATE", `Successfully updated song "${songId}".`);
  147. cache.pub('song.updated', song.songId);
  148. cb({ status: 'success', message: 'Song has been successfully updated', data: song });
  149. });
  150. }),
  151. /**
  152. * Removes a song
  153. *
  154. * @param session
  155. * @param songId - the song id
  156. * @param cb
  157. */
  158. remove: hooks.adminRequired((session, songId, cb) => {
  159. async.waterfall([
  160. (next) => {
  161. db.models.song.deleteOne({_id: songId}, next);
  162. },
  163. (res, next) => {//TODO Check if res gets returned from above
  164. cache.hdel('songs', songId, next);
  165. }
  166. ], async (err) => {
  167. if (err) {
  168. err = await utils.getError(err);
  169. logger.error("SONGS_UPDATE", `Failed to remove song "${songId}". "${err}"`);
  170. return cb({'status': 'failure', 'message': err});
  171. }
  172. logger.success("SONGS_UPDATE", `Successfully remove song "${songId}".`);
  173. cache.pub('song.removed', songId);
  174. cb({status: 'success', message: 'Song has been successfully updated'});
  175. });
  176. }),
  177. /**
  178. * Adds a song
  179. *
  180. * @param session
  181. * @param song - the song object
  182. * @param cb
  183. */
  184. add: hooks.adminRequired((session, song, cb) => {
  185. async.waterfall([
  186. (next) => {
  187. db.models.song.findOne({songId: song.songId}, next);
  188. },
  189. (existingSong, next) => {
  190. if (existingSong) return next('Song is already in rotation.');
  191. next();
  192. },
  193. (next) => {
  194. const newSong = new db.models.song(song);
  195. newSong.acceptedBy = session.userId;
  196. newSong.acceptedAt = Date.now();
  197. newSong.save(next);
  198. },
  199. (res, next) => {
  200. queueSongs.remove(session, song._id, () => {
  201. next();
  202. });
  203. },
  204. ], async (err) => {
  205. if (err) {
  206. err = await utils.getError(err);
  207. logger.error("SONGS_ADD", `User "${session.userId}" failed to add song. "${err}"`);
  208. return cb({'status': 'failure', 'message': err});
  209. }
  210. logger.success("SONGS_ADD", `User "${session.userId}" successfully added song "${song.songId}".`);
  211. cache.pub('song.added', song.songId);
  212. cb({status: 'success', message: 'Song has been moved from the queue successfully.'});
  213. });
  214. //TODO Check if video is in queue and Add the song to the appropriate stations
  215. }),
  216. /**
  217. * Likes a song
  218. *
  219. * @param session
  220. * @param songId - the song id
  221. * @param cb
  222. */
  223. like: hooks.loginRequired((session, songId, cb) => {
  224. async.waterfall([
  225. (next) => {
  226. db.models.song.findOne({songId}, next);
  227. },
  228. (song, next) => {
  229. if (!song) return next('No song found with that id.');
  230. next(null, song);
  231. }
  232. ], async (err, song) => {
  233. if (err) {
  234. err = await utils.getError(err);
  235. logger.error("SONGS_LIKE", `User "${session.userId}" failed to like song ${songId}. "${err}"`);
  236. return cb({'status': 'failure', 'message': err});
  237. }
  238. let oldSongId = songId;
  239. songId = song._id;
  240. db.models.user.findOne({ _id: session.userId }, (err, user) => {
  241. if (user.liked.indexOf(songId) !== -1) return cb({ status: 'failure', message: 'You have already liked this song.' });
  242. db.models.user.updateOne({_id: session.userId}, {$push: {liked: songId}, $pull: {disliked: songId}}, err => {
  243. if (!err) {
  244. db.models.user.countDocuments({"liked": songId}, (err, likes) => {
  245. if (err) return cb({ status: 'failure', message: 'Something went wrong while liking this song.' });
  246. db.models.user.countDocuments({"disliked": songId}, (err, dislikes) => {
  247. if (err) return cb({ status: 'failure', message: 'Something went wrong while liking this song.' });
  248. db.models.song.update({_id: songId}, {$set: {likes: likes, dislikes: dislikes}}, (err) => {
  249. if (err) return cb({ status: 'failure', message: 'Something went wrong while liking this song.' });
  250. songs.updateSong(songId, (err, song) => {});
  251. cache.pub('song.like', JSON.stringify({ songId: oldSongId, userId: session.userId, likes: likes, dislikes: dislikes }));
  252. return cb({ status: 'success', message: 'You have successfully liked this song.' });
  253. });
  254. });
  255. });
  256. } else return cb({ status: 'failure', message: 'Something went wrong while liking this song.' });
  257. });
  258. });
  259. });
  260. }),
  261. /**
  262. * Dislikes a song
  263. *
  264. * @param session
  265. * @param songId - the song id
  266. * @param cb
  267. */
  268. dislike: hooks.loginRequired((session, songId, cb) => {
  269. async.waterfall([
  270. (next) => {
  271. db.models.song.findOne({songId}, next);
  272. },
  273. (song, next) => {
  274. if (!song) return next('No song found with that id.');
  275. next(null, song);
  276. }
  277. ], async (err, song) => {
  278. if (err) {
  279. err = await utils.getError(err);
  280. logger.error("SONGS_DISLIKE", `User "${session.userId}" failed to like song ${songId}. "${err}"`);
  281. return cb({'status': 'failure', 'message': err});
  282. }
  283. let oldSongId = songId;
  284. songId = song._id;
  285. db.models.user.findOne({ _id: session.userId }, (err, user) => {
  286. if (user.disliked.indexOf(songId) !== -1) return cb({ status: 'failure', message: 'You have already disliked this song.' });
  287. db.models.user.updateOne({_id: session.userId}, {$push: {disliked: songId}, $pull: {liked: songId}}, err => {
  288. if (!err) {
  289. db.models.user.countDocuments({"liked": songId}, (err, likes) => {
  290. if (err) return cb({ status: 'failure', message: 'Something went wrong while disliking this song.' });
  291. db.models.user.countDocuments({"disliked": songId}, (err, dislikes) => {
  292. if (err) return cb({ status: 'failure', message: 'Something went wrong while disliking this song.' });
  293. db.models.song.update({_id: songId}, {$set: {likes: likes, dislikes: dislikes}}, (err, res) => {
  294. if (err) return cb({ status: 'failure', message: 'Something went wrong while disliking this song.' });
  295. songs.updateSong(songId, (err, song) => {});
  296. cache.pub('song.dislike', JSON.stringify({ songId: oldSongId, userId: session.userId, likes: likes, dislikes: dislikes }));
  297. return cb({ status: 'success', message: 'You have successfully disliked this song.' });
  298. });
  299. });
  300. });
  301. } else return cb({ status: 'failure', message: 'Something went wrong while disliking this song.' });
  302. });
  303. });
  304. });
  305. }),
  306. /**
  307. * Undislikes a song
  308. *
  309. * @param session
  310. * @param songId - the song id
  311. * @param cb
  312. */
  313. undislike: hooks.loginRequired((session, songId, cb) => {
  314. async.waterfall([
  315. (next) => {
  316. db.models.song.findOne({songId}, next);
  317. },
  318. (song, next) => {
  319. if (!song) return next('No song found with that id.');
  320. next(null, song);
  321. }
  322. ], async (err, song) => {
  323. if (err) {
  324. err = await utils.getError(err);
  325. logger.error("SONGS_UNDISLIKE", `User "${session.userId}" failed to like song ${songId}. "${err}"`);
  326. return cb({'status': 'failure', 'message': err});
  327. }
  328. let oldSongId = songId;
  329. songId = song._id;
  330. db.models.user.findOne({_id: session.userId}, (err, user) => {
  331. if (user.disliked.indexOf(songId) === -1) return cb({
  332. status: 'failure',
  333. message: 'You have not disliked this song.'
  334. });
  335. db.models.user.updateOne({_id: session.userId}, {$pull: {liked: songId, disliked: songId}}, err => {
  336. if (!err) {
  337. db.models.user.countDocuments({"liked": songId}, (err, likes) => {
  338. if (err) return cb({
  339. status: 'failure',
  340. message: 'Something went wrong while undisliking this song.'
  341. });
  342. db.models.user.countDocuments({"disliked": songId}, (err, dislikes) => {
  343. if (err) return cb({
  344. status: 'failure',
  345. message: 'Something went wrong while undisliking this song.'
  346. });
  347. db.models.song.update({_id: songId}, {$set: {likes: likes, dislikes: dislikes}}, (err) => {
  348. if (err) return cb({
  349. status: 'failure',
  350. message: 'Something went wrong while undisliking this song.'
  351. });
  352. songs.updateSong(songId, (err, song) => {
  353. });
  354. cache.pub('song.undislike', JSON.stringify({
  355. songId: oldSongId,
  356. userId: session.userId,
  357. likes: likes,
  358. dislikes: dislikes
  359. }));
  360. return cb({
  361. status: 'success',
  362. message: 'You have successfully undisliked this song.'
  363. });
  364. });
  365. });
  366. });
  367. } else return cb({status: 'failure', message: 'Something went wrong while undisliking this song.'});
  368. });
  369. });
  370. });
  371. }),
  372. /**
  373. * Unlikes a song
  374. *
  375. * @param session
  376. * @param songId - the song id
  377. * @param cb
  378. */
  379. unlike: hooks.loginRequired((session, songId, cb) => {
  380. async.waterfall([
  381. (next) => {
  382. db.models.song.findOne({songId}, next);
  383. },
  384. (song, next) => {
  385. if (!song) return next('No song found with that id.');
  386. next(null, song);
  387. }
  388. ], async (err, song) => {
  389. if (err) {
  390. err = await utils.getError(err);
  391. logger.error("SONGS_UNLIKE", `User "${session.userId}" failed to like song ${songId}. "${err}"`);
  392. return cb({'status': 'failure', 'message': err});
  393. }
  394. let oldSongId = songId;
  395. songId = song._id;
  396. db.models.user.findOne({ _id: session.userId }, (err, user) => {
  397. if (user.liked.indexOf(songId) === -1) return cb({ status: 'failure', message: 'You have not liked this song.' });
  398. db.models.user.updateOne({_id: session.userId}, {$pull: {liked: songId, disliked: songId}}, err => {
  399. if (!err) {
  400. db.models.user.countDocuments({"liked": songId}, (err, likes) => {
  401. if (err) return cb({ status: 'failure', message: 'Something went wrong while unliking this song.' });
  402. db.models.user.countDocuments({"disliked": songId}, (err, dislikes) => {
  403. if (err) return cb({ status: 'failure', message: 'Something went wrong while undiking this song.' });
  404. db.models.song.updateOne({_id: songId}, {$set: {likes: likes, dislikes: dislikes}}, (err) => {
  405. if (err) return cb({ status: 'failure', message: 'Something went wrong while unliking this song.' });
  406. songs.updateSong(songId, (err, song) => {});
  407. cache.pub('song.unlike', JSON.stringify({ songId: oldSongId, userId: session.userId, likes: likes, dislikes: dislikes }));
  408. return cb({ status: 'success', message: 'You have successfully unliked this song.' });
  409. });
  410. });
  411. });
  412. } else return cb({ status: 'failure', message: 'Something went wrong while unliking this song.' });
  413. });
  414. });
  415. });
  416. }),
  417. /**
  418. * Gets user's own song ratings
  419. *
  420. * @param session
  421. * @param songId - the song id
  422. * @param cb
  423. */
  424. getOwnSongRatings: hooks.loginRequired((session, songId, cb) => {
  425. async.waterfall([
  426. (next) => {
  427. db.models.song.findOne({songId}, next);
  428. },
  429. (song, next) => {
  430. if (!song) return next('No song found with that id.');
  431. next(null, song);
  432. }
  433. ], async (err, song) => {
  434. if (err) {
  435. err = await utils.getError(err);
  436. logger.error("SONGS_GET_OWN_RATINGS", `User "${session.userId}" failed to get ratings for ${songId}. "${err}"`);
  437. return cb({'status': 'failure', 'message': err});
  438. }
  439. let newSongId = song._id;
  440. db.models.user.findOne({_id: session.userId}, (err, user) => {
  441. if (!err && user) {
  442. return cb({
  443. status: 'success',
  444. songId: songId,
  445. liked: (user.liked.indexOf(newSongId) !== -1),
  446. disliked: (user.disliked.indexOf(newSongId) !== -1)
  447. });
  448. } else {
  449. return cb({
  450. status: 'failure',
  451. message: utils.getError(err)
  452. });
  453. }
  454. });
  455. });
  456. })
  457. };