stations.js 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. 'use strict';
  2. const cache = require('./cache');
  3. const db = require('./db');
  4. const io = require('./io');
  5. const utils = require('./utils');
  6. const songs = require('./songs');
  7. const notifications = require('./notifications');
  8. const async = require('async');
  9. let skipTimeout = null;
  10. //TEMP
  11. cache.sub('station.pause', (stationId) => {
  12. notifications.remove(`stations.nextSong?id=${stationId}`);
  13. });
  14. cache.sub('station.resume', (stationId) => {
  15. module.exports.initializeAndReturnStation(stationId, (err, station) => {})
  16. });
  17. module.exports = {
  18. init: function(cb) {
  19. let _this = this;
  20. db.models.station.find({}, (err, stations) => {
  21. if (!err) {
  22. stations.forEach((station) => {
  23. console.info("Initializing Station: " + station._id);
  24. _this.initializeAndReturnStation(station._id, (err, station) => {
  25. //TODO Emit to homepage and admin station list
  26. });
  27. });
  28. cb();
  29. }
  30. });
  31. },
  32. calculateSongForStation: (station, cb) => {
  33. let songList = [];
  34. async.waterfall([
  35. (next) => {
  36. let genresDone = [];
  37. station.genres.forEach((genre) => {
  38. db.models.song.find({genres: genre}, (err, songs) => {
  39. if (!err) {
  40. songs.forEach((song) => {
  41. if (songList.indexOf(song._id) === -1) songList.push(song._id);
  42. });
  43. }
  44. genresDone.push(genre);
  45. if (genresDone.length === station.genres.length) {
  46. next();
  47. }
  48. });
  49. });
  50. },
  51. (next) => {
  52. let playlist = [];
  53. songList.forEach(function(songId) {
  54. if(station.playlist.indexOf(songId) === -1) playlist.push(songId);
  55. });
  56. station.playlist.filter((songId) => {
  57. if (songList.indexOf(songId) !== -1) playlist.push(songId);
  58. });
  59. db.models.station.update({_id: station._id}, {$set: {playlist: playlist}}, (err, result) => {
  60. next(err, playlist);
  61. });
  62. }
  63. ], (err, newPlaylist) => {
  64. cb(err, newPlaylist);
  65. });
  66. },
  67. initializeAndReturnStation: function(stationId, cb) {
  68. console.log("INITIALIZE", stationId);
  69. let _this = this;
  70. async.waterfall([
  71. // first check the cache for the station
  72. (next) => cache.hget('stations', stationId, next),
  73. // if the cached version exist
  74. (station, next) => {
  75. if (station) return next(true, station);
  76. db.models.station.findOne({ _id: stationId }, next);
  77. },
  78. // if the station exists in the DB, add it to the cache
  79. (station, next) => {
  80. if (!station) return cb('Station by that id does not exist');
  81. station = cache.schemas.station(station);
  82. cache.hset('stations', station._id, station, (err) => next(err, station));
  83. }
  84. ], (err, station) => {
  85. if (err && err !== true) return cb(err);
  86. // get notified when the next song for this station should play, so that we can notify our sockets
  87. let notification = notifications.subscribe(`stations.nextSong?id=${station._id}`, () => {
  88. console.log("NOTIFICATION!!!");
  89. //function skipSongTemp() {
  90. // get the station from the cache
  91. //TODO Recalculate songs if the last song of the station playlist is getting played
  92. cache.hget('stations', station._id, (err, station) => {
  93. if (station) {
  94. // notify all the sockets on this station to go to the next song
  95. async.waterfall([
  96. (next) => {
  97. if (station.playlist.length > 0) {
  98. function func() {
  99. if (station.currentSongIndex < station.playlist.length - 1) {
  100. station.currentSongIndex++;
  101. songs.getSong(station.playlist[station.currentSongIndex], (err, song) => {
  102. if (!err) {
  103. station.currentSong = {
  104. _id: song._id,
  105. title: song.title,
  106. artists: song.artists,
  107. duration: song.duration,
  108. likes: song.likes,
  109. dislikes: song.dislikes,
  110. skipDuration: song.skipDuration,
  111. thumbnail: song.thumbnail
  112. };
  113. station.startedAt = Date.now();
  114. station.timePaused = 0;
  115. next(null, station);
  116. } else {
  117. station.currentSongIndex++;
  118. func();
  119. }
  120. });
  121. } else {
  122. station.currentSongIndex = 0;
  123. _this.calculateSongForStation(station, (err, newPlaylist) => {
  124. console.log('New playlist: ', newPlaylist);
  125. if (!err) {
  126. songs.getSong(newPlaylist[0], (err, song) => {
  127. if (song) {
  128. station.currentSong = {
  129. _id: song._id,
  130. title: song.title,
  131. artists: song.artists,
  132. duration: song.duration,
  133. likes: song.likes,
  134. dislikes: song.dislikes,
  135. skipDuration: song.skipDuration,
  136. thumbnail: song.thumbnail
  137. };
  138. station.playlist = newPlaylist;
  139. } else {
  140. station.currentSong = _this.defaultSong;
  141. }
  142. station.startedAt = Date.now();
  143. station.timePaused = 0;
  144. next(null, station);
  145. });
  146. } else {
  147. station.currentSong = _this.defaultSong;
  148. station.startedAt = Date.now();
  149. station.timePaused = 0;
  150. next(null, station);
  151. }
  152. })
  153. }
  154. }
  155. func();
  156. } else {
  157. _this.calculateSongForStation(station, (err, playlist) => {
  158. if (!err && playlist.length === 0) {
  159. station.currentSongIndex = 0;
  160. station.currentSong = _this.defaultSong;
  161. station.startedAt = Date.now();
  162. station.timePaused = 0;
  163. next(null, station);
  164. } else {
  165. songs.getSong(playlist[0], (err, song) => {
  166. if (!err) {
  167. station.currentSong = {
  168. _id: song._id,
  169. title: song.title,
  170. artists: song.artists,
  171. duration: song.duration,
  172. likes: song.likes,
  173. dislikes: song.dislikes,
  174. skipDuration: song.skipDuration,
  175. thumbnail: song.thumbnail
  176. };
  177. } else {
  178. station.currentSong = _this.defaultSong;
  179. }
  180. station.currentSongIndex = 0;
  181. station.startedAt = Date.now();
  182. station.timePaused = 0;
  183. station.playlist = playlist;
  184. next(null, station);
  185. });
  186. }
  187. });
  188. }
  189. },
  190. (station, next) => {
  191. cache.hset('stations', station._id, station, (err) => next(err, station));
  192. //TODO Also save to DB
  193. },
  194. ], (err, station) => {
  195. io.io.to(`station.${stationId}`).emit("event:songs.next", {
  196. currentSong: station.currentSong,
  197. startedAt: station.startedAt,
  198. paused: station.paused,
  199. timePaused: 0
  200. });
  201. utils.socketsJoinSongRoom(io.io.to(`station.${stationId}`).sockets, `song.${station.currentSong._id}`);
  202. // schedule a notification to be dispatched when the next song ends
  203. console.log("NEXT SONG!!!");
  204. if (!station.paused) {
  205. notifications.schedule(`stations.nextSong?id=${station._id}`, station.currentSong.duration * 1000);
  206. }
  207. //skipTimeout = setTimeout(skipSongTemp, station.currentSong.duration * 1000);
  208. });
  209. }
  210. // the station doesn't exist anymore or is paused, unsubscribe from it
  211. else {
  212. notifications.remove(notification);
  213. }
  214. });
  215. }, true);
  216. console.log(station.paused);
  217. if (!station.paused) {
  218. if (!station.startedAt) {
  219. station.startedAt = Date.now();
  220. station.timePaused = 0;
  221. cache.hset('stations', stationId, station);
  222. }
  223. let timeLeft = ((station.currentSong.duration * 1000) - (Date.now() - station.startedAt - station.timePaused));
  224. console.log(timeLeft);
  225. if (station.currentSong.duration * 1000 < timeLeft || timeLeft < 0) {
  226. console.log("Test");
  227. notifications.schedule(`stations.nextSong?id=${station._id}`, 1);
  228. } else {
  229. notifications.schedule(`stations.nextSong?id=${station._id}`, timeLeft);
  230. }
  231. } else {
  232. notifications.unschedule(`stations.nextSong?id${station._id}`);
  233. }
  234. return cb(null, station);
  235. });
  236. },
  237. defaultSong: {
  238. _id: '60ItHLz5WEA',
  239. title: 'Faded',
  240. artists: ['Alan Walker'],
  241. duration: 212,
  242. skipDuration: 0,
  243. thumbnail: 'https://i.scdn.co/image/2ddde58427f632037093857ebb71a67ddbdec34b'
  244. }
  245. };