stations.js 8.3 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. if (!station.paused) {
  95. // notify all the sockets on this station to go to the next song
  96. async.waterfall([
  97. (next) => {
  98. if (station.playlist.length > 0) {
  99. function func() {
  100. if (station.currentSongIndex < station.playlist.length - 1) {
  101. station.currentSongIndex++;
  102. songs.getSong(station.playlist[station.currentSongIndex], (err, song) => {
  103. if (!err) {
  104. station.currentSong = {
  105. _id: song._id,
  106. title: song.title,
  107. artists: song.artists,
  108. duration: song.duration,
  109. likes: song.likes,
  110. dislikes: song.dislikes,
  111. skipDuration: song.skipDuration,
  112. thumbnail: song.thumbnail
  113. };
  114. station.startedAt = Date.now();
  115. station.timePaused = 0;
  116. next(null, station);
  117. } else {
  118. station.currentSongIndex++;
  119. func();
  120. }
  121. });
  122. } else {
  123. station.currentSongIndex = 0;
  124. _this.calculateSongForStation(station, (err, newPlaylist) => {
  125. console.log('New playlist: ', newPlaylist);
  126. if (!err) {
  127. songs.getSong(newPlaylist[0], (err, song) => {
  128. if (song) {
  129. station.currentSong = {
  130. _id: song._id,
  131. title: song.title,
  132. artists: song.artists,
  133. duration: song.duration,
  134. likes: song.likes,
  135. dislikes: song.dislikes,
  136. skipDuration: song.skipDuration,
  137. thumbnail: song.thumbnail
  138. };
  139. station.playlist = newPlaylist;
  140. } else {
  141. station.currentSong = _this.defaultSong;
  142. }
  143. station.startedAt = Date.now();
  144. station.timePaused = 0;
  145. next(null, station);
  146. });
  147. } else {
  148. station.currentSong = _this.defaultSong;
  149. station.startedAt = Date.now();
  150. station.timePaused = 0;
  151. next(null, station);
  152. }
  153. })
  154. }
  155. }
  156. func();
  157. } else {
  158. _this.calculateSongForStation(station, (err, playlist) => {
  159. if (!err && playlist.length === 0) {
  160. station.currentSongIndex = 0;
  161. station.currentSong = _this.defaultSong;
  162. station.startedAt = Date.now();
  163. station.timePaused = 0;
  164. next(null, station);
  165. } else {
  166. songs.getSong(playlist[0], (err, song) => {
  167. if (!err) {
  168. station.currentSong = {
  169. _id: song._id,
  170. title: song.title,
  171. artists: song.artists,
  172. duration: song.duration,
  173. likes: song.likes,
  174. dislikes: song.dislikes,
  175. skipDuration: song.skipDuration,
  176. thumbnail: song.thumbnail
  177. };
  178. } else {
  179. station.currentSong = _this.defaultSong;
  180. }
  181. station.currentSongIndex = 0;
  182. station.startedAt = Date.now();
  183. station.timePaused = 0;
  184. station.playlist = playlist;
  185. next(null, station);
  186. });
  187. }
  188. });
  189. }
  190. },
  191. (station, next) => {
  192. cache.hset('stations', station._id, station, (err) => next(err, station));
  193. //TODO Also save to DB
  194. },
  195. ], (err, station) => {
  196. io.io.to(`station.${stationId}`).emit("event:songs.next", {
  197. currentSong: station.currentSong,
  198. startedAt: station.startedAt,
  199. paused: station.paused,
  200. timePaused: 0
  201. });
  202. utils.socketsJoinSongRoom(io.io.to(`station.${stationId}`).sockets, `song.${station.currentSong._id}`);
  203. // schedule a notification to be dispatched when the next song ends
  204. console.log("NEXT SONG!!!");
  205. notifications.schedule(`stations.nextSong?id=${station._id}`, station.currentSong.duration * 1000);
  206. //skipTimeout = setTimeout(skipSongTemp, station.currentSong.duration * 1000);
  207. });
  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. };