playlists.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. 'use strict';
  2. const db = require('../db');
  3. const io = require('../io');
  4. const cache = require('../cache');
  5. const utils = require('../utils');
  6. const logger = require('../logger');
  7. const hooks = require('./hooks');
  8. const async = require('async');
  9. const playlists = require('../playlists');
  10. const songs = require('../songs');
  11. cache.sub('playlist.create', playlistId => {
  12. playlists.getPlaylist(playlistId, (err, playlist) => {
  13. if (!err) {
  14. utils.socketsFromUser(playlist.createdBy, (sockets) => {
  15. sockets.forEach((socket) => {
  16. socket.emit('event:playlist.create', playlist);
  17. });
  18. });
  19. }
  20. });
  21. });
  22. cache.sub('playlist.delete', res => {
  23. utils.socketsFromUser(res.userId, (sockets) => {
  24. sockets.forEach((socket) => {
  25. socket.emit('event:playlist.delete', res.playlistId);
  26. });
  27. });
  28. });
  29. cache.sub('playlist.moveSongToTop', res => {
  30. utils.socketsFromUser(res.userId, (sockets) => {
  31. sockets.forEach((socket) => {
  32. socket.emit('event:playlist.moveSongToTop', {playlistId: res.playlistId, songId: res.songId});
  33. });
  34. });
  35. });
  36. cache.sub('playlist.moveSongToBottom', res => {
  37. utils.socketsFromUser(res.userId, (sockets) => {
  38. sockets.forEach((socket) => {
  39. socket.emit('event:playlist.moveSongToBottom', {playlistId: res.playlistId, songId: res.songId});
  40. });
  41. });
  42. });
  43. cache.sub('playlist.addSong', res => {
  44. utils.socketsFromUser(res.userId, (sockets) => {
  45. sockets.forEach((socket) => {
  46. socket.emit('event:playlist.addSong', {playlistId: res.playlistId, song: res.song});
  47. });
  48. });
  49. });
  50. cache.sub('playlist.removeSong', res => {
  51. utils.socketsFromUser(res.userId, (sockets) => {
  52. sockets.forEach((socket) => {
  53. socket.emit('event:playlist.removeSong', {playlistId: res.playlistId, songId: res.songId});
  54. });
  55. });
  56. });
  57. cache.sub('playlist.updateDisplayName', res => {
  58. utils.socketsFromUser(res.userId, (sockets) => {
  59. sockets.forEach((socket) => {
  60. socket.emit('event:playlist.updateDisplayName', {playlistId: res.playlistId, displayName: res.displayName});
  61. });
  62. });
  63. });
  64. let lib = {
  65. getFirstSong: hooks.loginRequired((session, playlistId, cb, userId) => {
  66. async.waterfall([
  67. (next) => {
  68. playlists.getPlaylist(playlistId, next);
  69. },
  70. (playlist, next) => {
  71. if (!playlist || playlist.createdBy !== userId) return next('Playlist not found.');
  72. next(null, playlist.songs[0]);
  73. }
  74. ], (err, song) => {
  75. if (err) {
  76. let error = 'An error occurred.';
  77. if (typeof err === "string") error = err;
  78. else if (err.message) error = err.message;
  79. logger.log("PLAYLIST_GET_FIRST_SONG", "ERROR", `Getting the first song of playlist "${playlistId}" failed for user "${userId}". "${error}"`);
  80. return cb({ status: 'failure', message: 'Something went wrong when getting the playlist'});
  81. }
  82. logger.log("PLAYLIST_GET_FIRST_SONG", "SUCCESS", `Successfully got the first song of playlist "${playlistId}" for user "${userId}".`);
  83. cb({
  84. status: 'success',
  85. song: song
  86. });
  87. });
  88. }),
  89. indexForUser: hooks.loginRequired((session, cb, userId) => {
  90. db.models.playlist.find({ createdBy: userId }, (err, playlists) => {
  91. if (err) return cb({ status: 'failure', message: 'Something went wrong when getting the playlists'});
  92. cb({
  93. status: 'success',
  94. data: playlists
  95. });
  96. });
  97. }),
  98. create: hooks.loginRequired((session, data, cb, userId) => {
  99. async.waterfall([
  100. (next) => {
  101. return (data) ? next() : cb({ 'status': 'failure', 'message': 'Invalid data' });
  102. },
  103. (next) => {
  104. const { name, displayName, songs } = data;
  105. db.models.playlist.create({
  106. _id: utils.generateRandomString(17),//TODO Check if exists
  107. displayName,
  108. songs,
  109. createdBy: userId,
  110. createdAt: Date.now()
  111. }, next);
  112. }
  113. ], (err, playlist) => {
  114. console.log(err, playlist);
  115. if (err) return cb({ 'status': 'failure', 'message': 'Something went wrong'});
  116. cache.pub('playlist.create', playlist._id);
  117. return cb({ 'status': 'success', 'message': 'Successfully created playlist' });
  118. });
  119. }),
  120. getPlaylist: hooks.loginRequired((session, id, cb, userId) => {
  121. playlists.getPlaylist(id, (err, playlist) => {
  122. if (err || playlist.createdBy !== userId) return cb({status: 'success', message: 'Playlist not found'});
  123. if (err == null) return cb({
  124. status: 'success',
  125. data: playlist
  126. });
  127. });
  128. }),
  129. //TODO Remove this
  130. update: hooks.loginRequired((session, _id, playlist, cb, userId) => {
  131. db.models.playlist.update({ _id, createdBy: userId }, playlist, (err, data) => {
  132. if (err) return cb({ status: 'failure', message: 'Something went wrong.' });
  133. playlists.updatePlaylist(_id, (err) => {
  134. if (err) return cb({ status: 'failure', message: 'Something went wrong.' });
  135. return cb({ status: 'success', message: 'Playlist has been successfully updated', data });
  136. });
  137. });
  138. }),
  139. addSongToPlaylist: hooks.loginRequired((session, songId, playlistId, cb, userId) => {
  140. console.log(songId);
  141. async.waterfall([
  142. (next) => {
  143. playlists.getPlaylist(playlistId, (err, playlist) => {
  144. if (err || !playlist || playlist.createdBy !== userId) return next('Something went wrong when trying to get the playlist');
  145. let found = false;
  146. playlist.songs.forEach((song) => {
  147. if (songId === song._id) found = true;
  148. });
  149. if (found) return next('That song is already in the playlist');
  150. return next(null);
  151. });
  152. },
  153. (next) => {
  154. songs.getSong(songId, (err, song) => {
  155. if (err) {
  156. utils.getSongFromYouTube(songId, (song) => {
  157. next(null, song);
  158. });
  159. } else {
  160. next(null, {
  161. _id: songId,
  162. title: song.title,
  163. duration: song.duration
  164. });
  165. }
  166. });
  167. },
  168. (newSong, next) => {
  169. db.models.playlist.update({ _id: playlistId }, { $push: { songs: newSong } }, (err) => {
  170. if (err) {
  171. console.error(err);
  172. return next('Failed to add song to playlist');
  173. }
  174. playlists.updatePlaylist(playlistId, (err, playlist) => {
  175. next(err, playlist, newSong);
  176. });
  177. });
  178. }
  179. ],
  180. (err, playlist, newSong) => {
  181. if (err) return cb({ status: 'error', message: err });
  182. else if (playlist.songs) {
  183. cache.pub('playlist.addSong', { playlistId: playlist._id, song: newSong, userId: userId });
  184. return cb({ status: 'success', message: 'Song has been successfully added to the playlist', data: playlist.songs });
  185. }
  186. });
  187. }),
  188. addSetToPlaylist: hooks.loginRequired((session, url, playlistId, cb, userId) => {
  189. async.waterfall([
  190. (next) => {
  191. utils.getPlaylistFromYouTube(url, songs => {
  192. next(null, songs);
  193. });
  194. },
  195. (songs, next) => {
  196. let processed = 0;
  197. function checkDone() {
  198. if (processed === songs.length) next();
  199. }
  200. for (let s = 0; s < songs.length; s++) {
  201. lib.addSongToPlaylist(session, songs[s].contentDetails.videoId, playlistId, () => {
  202. processed++;
  203. checkDone();
  204. });
  205. }
  206. },
  207. (next) => {
  208. playlists.getPlaylist(playlistId, (err, playlist) => {
  209. if (err || !playlist || playlist.createdBy !== userId) return next('Something went wrong while trying to get the playlist');
  210. next(null, playlist);
  211. });
  212. }
  213. ],
  214. (err, playlist) => {
  215. if (err) return cb({ status: 'failure', message: err });
  216. else if (playlist.songs) return cb({ status: 'success', message: 'Playlist has been successfully imported.', data: playlist.songs });
  217. });
  218. }),
  219. removeSongFromPlaylist: hooks.loginRequired((session, songId, playlistId, cb, userId) => {
  220. playlists.getPlaylist(playlistId, (err, playlist) => {
  221. if (err || !playlist || playlist.createdBy !== userId) return cb({ status: 'failure', message: 'Something went wrong when getting the playlist'});
  222. for (let z = 0; z < playlist.songs.length; z++) {
  223. if (playlist.songs[z]._id == songId) playlist.songs.shift(playlist.songs[z]);
  224. }
  225. db.models.playlist.update({_id: playlistId}, {$pull: {songs: {_id: songId}}}, (err) => {
  226. if (err) {
  227. console.error(err);
  228. return cb({ status: 'failure', message: 'Something went wrong when saving the playlist.'});
  229. }
  230. playlists.updatePlaylist(playlistId, (err, playlist) => {
  231. cache.pub('playlist.removeSong', {playlistId: playlist._id, songId: songId, userId: userId});
  232. return cb({ status: 'success', message: 'Song has been successfully removed from playlist', data: playlist.songs });
  233. });
  234. });
  235. });
  236. }),
  237. updateDisplayName: hooks.loginRequired((session, _id, displayName, cb, userId) => {
  238. db.models.playlist.update({ _id, createdBy: userId }, { displayName }, (err, res) => {
  239. if (err) return cb({ status: 'failure', message: 'Something went wrong when saving the playlist.'});
  240. playlists.updatePlaylist(_id, (err) => {
  241. if (err) return cb({ status: 'failure', message: err});
  242. cache.pub('playlist.updateDisplayName', {playlistId: _id, displayName: displayName, userId: userId});
  243. return cb({ status: 'success', message: 'Playlist has been successfully updated' });
  244. })
  245. });
  246. }),
  247. moveSongToTop: hooks.loginRequired((session, playlistId, songId, cb, userId) => {
  248. playlists.getPlaylist(playlistId, (err, playlist) => {
  249. if (err || !playlist || playlist.createdBy !== userId) return cb({ status: 'failure', message: 'Something went wrong when getting the playlist'});
  250. let found = false;
  251. let foundSong;
  252. playlist.songs.forEach((song) => {
  253. if (song._id === songId) {
  254. foundSong = song;
  255. found = true;
  256. }
  257. });
  258. if (found) {
  259. db.models.playlist.update({_id: playlistId}, {$pull: {songs: {_id: songId}}}, (err) => {
  260. console.log(err);
  261. if (err) return cb({status: 'failure', message: 'Something went wrong when moving the song'});
  262. db.models.playlist.update({_id: playlistId}, {
  263. $push: {
  264. songs: {
  265. $each: [foundSong],
  266. $position: 0
  267. }
  268. }
  269. }, (err) => {
  270. console.log(err);
  271. if (err) return cb({status: 'failure', message: 'Something went wrong when moving the song'});
  272. playlists.updatePlaylist(playlistId, (err) => {
  273. if (err) return cb({ status: 'failure', message: err});
  274. cache.pub('playlist.moveSongToTop', {playlistId, songId, userId: userId});
  275. return cb({ status: 'success', message: 'Playlist has been successfully updated' });
  276. })
  277. });
  278. });
  279. } else {
  280. return cb({status: 'failure', message: 'Song not found.'});
  281. }
  282. });
  283. }),
  284. moveSongToBottom: hooks.loginRequired((session, playlistId, songId, cb, userId) => {
  285. playlists.getPlaylist(playlistId, (err, playlist) => {
  286. if (err || !playlist || playlist.createdBy !== userId) return cb({ status: 'failure', message: 'Something went wrong when getting the playlist'});
  287. let found = false;
  288. let foundSong;
  289. playlist.songs.forEach((song) => {
  290. if (song._id === songId) {
  291. foundSong = song;
  292. found = true;
  293. }
  294. });
  295. if (found) {
  296. db.models.playlist.update({_id: playlistId}, {$pull: {songs: {_id: songId}}}, (err) => {
  297. console.log(err);
  298. if (err) return cb({status: 'failure', message: 'Something went wrong when moving the song'});
  299. db.models.playlist.update({_id: playlistId}, {
  300. $push: { songs: foundSong }
  301. }, (err) => {
  302. console.log(err);
  303. if (err) return cb({status: 'failure', message: 'Something went wrong when moving the song'});
  304. playlists.updatePlaylist(playlistId, (err) => {
  305. if (err) return cb({ status: 'failure', message: err });
  306. cache.pub('playlist.moveSongToBottom', { playlistId, songId, userId: userId });
  307. return cb({ status: 'success', message: 'Playlist has been successfully updated' });
  308. })
  309. });
  310. });
  311. } else return cb({status: 'failure', message: 'Song not found'});
  312. });
  313. }),
  314. /*
  315. promoteSong: hooks.loginRequired((session, playlistId, fromIndex, cb, userId) => {
  316. db.models.playlist.findOne({ _id: playlistId }, (err, playlist) => {
  317. if (err || !playlist || playlist.createdBy !== userId) return cb({ status: 'failure', message: 'Something went wrong when getting the playlist.'});
  318. let song = playlist.songs[fromIndex];
  319. playlist.songs.splice(fromIndex, 1);
  320. playlist.songs.splice((fromIndex + 1), 0, song);
  321. playlist.save(err => {
  322. if (err) {
  323. console.error(err);
  324. return cb({ status: 'failure', message: 'Something went wrong when saving the playlist.'});
  325. }
  326. playlists.updatePlaylist(playlistId, (err) => {
  327. if (err) return cb({ status: 'failure', message: 'Something went wrong when saving the playlist.'});
  328. return cb({ status: 'success', data: playlist.songs });
  329. });
  330. });
  331. });
  332. }),
  333. demoteSong: hooks.loginRequired((session, playlistId, fromIndex, cb, userId) => {
  334. db.models.playlist.findOne({ _id: playlistId }, (err, playlist) => {
  335. if (err || !playlist || playlist.createdBy !== userId) return cb({ status: 'failure', message: 'Something went wrong when getting the playlist.'});
  336. let song = playlist.songs[fromIndex];
  337. playlist.songs.splice(fromIndex, 1);
  338. playlist.songs.splice((fromIndex - 1), 0, song);
  339. playlist.save(err => {
  340. if (err) {
  341. console.error(err);
  342. return cb({ status: 'failure', message: 'Something went wrong when saving the playlist.'});
  343. }
  344. playlists.updatePlaylist(playlistId, (err) => {
  345. if (err) return cb({ status: 'failure', message: 'Something went wrong when saving the playlist.'});
  346. return cb({ status: 'success', data: playlist.songs });
  347. });
  348. });
  349. });
  350. }),*/
  351. remove: hooks.loginRequired((session, _id, cb, userId) => {
  352. console.log(_id, userId);
  353. db.models.playlist.remove({ _id, createdBy: userId }).exec(err => {
  354. if (err) return cb({ status: 'failure', message: 'Something went wrong when removing the playlist.'});
  355. cache.hdel('playlists', _id, () => {
  356. cache.pub('playlist.delete', {userId: userId, playlistId: _id});
  357. return cb({ status: 'success', message: 'Playlist successfully removed' });
  358. });
  359. });
  360. })
  361. };
  362. module.exports = lib;