|
@@ -313,114 +313,6 @@ class _StationsModule extends CoreClass {
|
|
|
});
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * Calculates the next song for the station
|
|
|
- *
|
|
|
- * @param {object} payload - object that contains the payload
|
|
|
- * @param {string} payload.station - station object to calculate song for
|
|
|
- * @returns {Promise} - returns a promise (resolve, reject)
|
|
|
- */
|
|
|
- async CALCULATE_SONG_FOR_STATION(payload) {
|
|
|
- // station, bypassValidate = false
|
|
|
- const songModel = await DBModule.runJob("GET_MODEL", { modelName: "song" }, this);
|
|
|
-
|
|
|
- return new Promise((resolve, reject) => {
|
|
|
- const songList = [];
|
|
|
-
|
|
|
- return async.waterfall(
|
|
|
- [
|
|
|
- next => {
|
|
|
- if (payload.station.genres.length === 0) return next();
|
|
|
-
|
|
|
- const genresDone = [];
|
|
|
- const blacklistedGenres = payload.station.blacklistedGenres.map(blacklistedGenre =>
|
|
|
- blacklistedGenre.toLowerCase()
|
|
|
- );
|
|
|
-
|
|
|
- return payload.station.genres.forEach(genre => {
|
|
|
- songModel.find({ genres: { $regex: genre, $options: "i" } }, (err, songs) => {
|
|
|
- if (!err) {
|
|
|
- songs.forEach(song => {
|
|
|
- if (songList.indexOf(song._id) === -1) {
|
|
|
- let found = false;
|
|
|
- song.genres.forEach(songGenre => {
|
|
|
- if (blacklistedGenres.indexOf(songGenre.toLowerCase()) !== -1)
|
|
|
- found = true;
|
|
|
- });
|
|
|
- if (!found) {
|
|
|
- songList.push(song._id);
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
- genresDone.push(genre);
|
|
|
- if (genresDone.length === payload.station.genres.length) next();
|
|
|
- });
|
|
|
- });
|
|
|
- },
|
|
|
-
|
|
|
- next => {
|
|
|
- const playlist = [];
|
|
|
- songList.forEach(songId => {
|
|
|
- if (payload.station.playlist.indexOf(songId) === -1) playlist.push(songId);
|
|
|
- });
|
|
|
-
|
|
|
- // eslint-disable-next-line array-callback-return
|
|
|
- payload.station.playlist.filter(songId => {
|
|
|
- if (songList.indexOf(songId) !== -1) playlist.push(songId);
|
|
|
- });
|
|
|
-
|
|
|
- UtilsModule.runJob("SHUFFLE", { array: playlist })
|
|
|
- .then(result => {
|
|
|
- next(null, result.array);
|
|
|
- }, this)
|
|
|
- .catch(next);
|
|
|
- },
|
|
|
-
|
|
|
- (playlist, next) => {
|
|
|
- StationsModule.runJob(
|
|
|
- "CALCULATE_OFFICIAL_PLAYLIST_LIST",
|
|
|
- {
|
|
|
- stationId: payload.station._id,
|
|
|
- songList: playlist
|
|
|
- },
|
|
|
- this
|
|
|
- )
|
|
|
- .then(() => {
|
|
|
- next(null, playlist);
|
|
|
- })
|
|
|
- .catch(next);
|
|
|
- },
|
|
|
-
|
|
|
- (playlist, next) => {
|
|
|
- StationsModule.stationModel.updateOne(
|
|
|
- { _id: payload.station._id },
|
|
|
- { $set: { playlist } },
|
|
|
- { runValidators: true },
|
|
|
- () => {
|
|
|
- StationsModule.runJob(
|
|
|
- "UPDATE_STATION",
|
|
|
- {
|
|
|
- stationId: payload.station._id
|
|
|
- },
|
|
|
- this
|
|
|
- )
|
|
|
- .then(() => {
|
|
|
- next(null, playlist);
|
|
|
- })
|
|
|
- .catch(next);
|
|
|
- }
|
|
|
- );
|
|
|
- }
|
|
|
- ],
|
|
|
- (err, newPlaylist) => {
|
|
|
- if (err) return reject(new Error(err));
|
|
|
- return resolve(newPlaylist);
|
|
|
- }
|
|
|
- );
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
/**
|
|
|
* Attempts to get the station from Redis. If it's not in Redis, get it from Mongo and add it to Redis.
|
|
|
*
|
|
@@ -454,14 +346,6 @@ class _StationsModule extends CoreClass {
|
|
|
|
|
|
(station, next) => {
|
|
|
if (station) {
|
|
|
- if (station.type === "official") {
|
|
|
- StationsModule.runJob("CALCULATE_OFFICIAL_PLAYLIST_LIST", {
|
|
|
- stationId: station._id,
|
|
|
- songList: station.playlist
|
|
|
- })
|
|
|
- .then()
|
|
|
- .catch();
|
|
|
- }
|
|
|
station = StationsModule.stationSchema(station);
|
|
|
CacheModule.runJob("HSET", {
|
|
|
table: "stations",
|
|
@@ -507,12 +391,6 @@ class _StationsModule extends CoreClass {
|
|
|
|
|
|
(station, next) => {
|
|
|
if (station) {
|
|
|
- if (station.type === "official") {
|
|
|
- StationsModule.runJob("CALCULATE_OFFICIAL_PLAYLIST_LIST", {
|
|
|
- stationId: station._id,
|
|
|
- songList: station.playlist
|
|
|
- });
|
|
|
- }
|
|
|
station = StationsModule.stationSchema(station);
|
|
|
CacheModule.runJob("HSET", {
|
|
|
table: "stations",
|
|
@@ -589,60 +467,184 @@ class _StationsModule extends CoreClass {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Creates the official playlist for a station
|
|
|
+ * Fills up the official station playlist queue using the songs from the official station playlist
|
|
|
*
|
|
|
* @param {object} payload - object that contains the payload
|
|
|
* @param {string} payload.stationId - the id of the station
|
|
|
- * @param {Array} payload.songList - list of songs to put in official playlist
|
|
|
* @returns {Promise} - returns a promise (resolve, reject)
|
|
|
*/
|
|
|
- async CALCULATE_OFFICIAL_PLAYLIST_LIST(payload) {
|
|
|
- const officialPlaylistSchema = await CacheModule.runJob("GET_SCHEMA", { schemaName: "officialPlaylist" }, this);
|
|
|
-
|
|
|
- console.log(typeof payload.songList, payload.songList);
|
|
|
-
|
|
|
- return new Promise(resolve => {
|
|
|
- const lessInfoPlaylist = [];
|
|
|
-
|
|
|
- return async.each(
|
|
|
- payload.songList,
|
|
|
- (song, next) => {
|
|
|
- SongsModule.runJob("GET_SONG", { id: song }, this)
|
|
|
- .then(response => {
|
|
|
- const { song } = response;
|
|
|
- if (song) {
|
|
|
- const newSong = {
|
|
|
- _id: song._id,
|
|
|
- songId: song.songId,
|
|
|
- title: song.title,
|
|
|
- artists: song.artists,
|
|
|
- duration: song.duration,
|
|
|
- thumbnail: song.thumbnail,
|
|
|
- requestedAt: song.requestedAt
|
|
|
- };
|
|
|
- lessInfoPlaylist.push(newSong);
|
|
|
- }
|
|
|
- })
|
|
|
- .finally(() => {
|
|
|
- next();
|
|
|
- });
|
|
|
- },
|
|
|
- () => {
|
|
|
- CacheModule.runJob(
|
|
|
- "HSET",
|
|
|
- {
|
|
|
- table: "officialPlaylists",
|
|
|
- key: payload.stationId,
|
|
|
- value: officialPlaylistSchema(payload.stationId, lessInfoPlaylist)
|
|
|
- },
|
|
|
- this
|
|
|
- ).finally(() => {
|
|
|
- CacheModule.runJob("PUB", {
|
|
|
- channel: "station.newOfficialPlaylist",
|
|
|
- value: payload.stationId
|
|
|
+ FILL_UP_OFFICIAL_STATION_PLAYLIST_QUEUE(payload) {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ const { stationId } = payload;
|
|
|
+
|
|
|
+ async.waterfall(
|
|
|
+ [
|
|
|
+ next => {
|
|
|
+ PlaylistsModule.runJob("GET_STATION_PLAYLIST", { stationId, includeSongs: true }, this)
|
|
|
+ .then(response => {
|
|
|
+ next(null, response.playlist);
|
|
|
+ })
|
|
|
+ .catch(next);
|
|
|
+ },
|
|
|
+
|
|
|
+ (playlist, next) => {
|
|
|
+ UtilsModule.runJob("SHUFFLE", { array: playlist.songs }, this)
|
|
|
+ .then(response => {
|
|
|
+ next(null, response.array);
|
|
|
+ })
|
|
|
+ .catch(next);
|
|
|
+ },
|
|
|
+
|
|
|
+ (playlistSongs, next) => {
|
|
|
+ StationsModule.runJob("GET_STATION", { stationId }, this)
|
|
|
+ .then(station => {
|
|
|
+ next(null, playlistSongs, station);
|
|
|
+ })
|
|
|
+ .catch(next);
|
|
|
+ },
|
|
|
+
|
|
|
+ (playlistSongs, station, next) => {
|
|
|
+ const songsStillNeeded = 50 - station.playlist.length;
|
|
|
+ const currentSongs = station.playlist;
|
|
|
+ const currentSongIds = station.playlist.map(song => song._id);
|
|
|
+ const songsToAdd = [];
|
|
|
+ playlistSongs.forEach(song => {
|
|
|
+ if (
|
|
|
+ songsToAdd.length < songsStillNeeded &&
|
|
|
+ currentSongIds.indexOf(song._id.toString()) === -1
|
|
|
+ )
|
|
|
+ songsToAdd.push(song);
|
|
|
});
|
|
|
- resolve();
|
|
|
- });
|
|
|
+
|
|
|
+ next(null, [...currentSongs, ...songsToAdd]);
|
|
|
+ },
|
|
|
+
|
|
|
+ (newPlaylist, next) => {
|
|
|
+ StationsModule.stationModel.updateOne(
|
|
|
+ { _id: stationId },
|
|
|
+ { $set: { playlist: newPlaylist } },
|
|
|
+ { runValidators: true },
|
|
|
+ () => {
|
|
|
+ StationsModule.runJob(
|
|
|
+ "UPDATE_STATION",
|
|
|
+ {
|
|
|
+ stationId
|
|
|
+ },
|
|
|
+ this
|
|
|
+ )
|
|
|
+ .then(() => {
|
|
|
+ next(null);
|
|
|
+ })
|
|
|
+ .catch(next);
|
|
|
+ }
|
|
|
+ );
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ err => {
|
|
|
+ if (err) reject(err);
|
|
|
+ else resolve();
|
|
|
+ }
|
|
|
+ );
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Gets next official station song
|
|
|
+ *
|
|
|
+ * @param {object} payload - object that contains the payload
|
|
|
+ * @param {string} payload.stationId - the id of the station
|
|
|
+ * @returns {Promise} - returns a promise (resolve, reject)
|
|
|
+ */
|
|
|
+ GET_NEXT_OFFICIAL_STATION_SONG(payload) {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ const { stationId } = payload;
|
|
|
+
|
|
|
+ async.waterfall(
|
|
|
+ [
|
|
|
+ next => {
|
|
|
+ StationsModule.runJob("GET_STATION", { stationId }, this)
|
|
|
+ .then(station => {
|
|
|
+ next(null, station);
|
|
|
+ })
|
|
|
+ .catch(next);
|
|
|
+ },
|
|
|
+
|
|
|
+ (station, next) => {
|
|
|
+ if (station.playlist.length === 0) next("No songs available.");
|
|
|
+ else {
|
|
|
+ next(null, station.playlist[0]);
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ (song, next) => {
|
|
|
+ SongsModule.runJob("GET_SONG", { id: song._id }, this)
|
|
|
+ .then(response => {
|
|
|
+ const { song } = response;
|
|
|
+ if (song) {
|
|
|
+ const newSong = {
|
|
|
+ _id: song._id,
|
|
|
+ songId: song.songId,
|
|
|
+ title: song.title,
|
|
|
+ artists: song.artists,
|
|
|
+ duration: song.duration,
|
|
|
+ thumbnail: song.thumbnail,
|
|
|
+ requestedAt: song.requestedAt
|
|
|
+ };
|
|
|
+ next(null, newSong);
|
|
|
+ } else {
|
|
|
+ next(null, song);
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .catch(next);
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ (err, song) => {
|
|
|
+ if (err) reject(err);
|
|
|
+ else resolve({ song });
|
|
|
+ }
|
|
|
+ );
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Removes first official playlist queue song
|
|
|
+ *
|
|
|
+ * @param {object} payload - object that contains the payload
|
|
|
+ * @param {string} payload.stationId - the id of the station
|
|
|
+ * @returns {Promise} - returns a promise (resolve, reject)
|
|
|
+ */
|
|
|
+ REMOVE_FIRST_OFFICIAL_PLAYLIST_QUEUE_SONG(payload) {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ const { stationId } = payload;
|
|
|
+
|
|
|
+ async.waterfall(
|
|
|
+ [
|
|
|
+ next => {
|
|
|
+ StationsModule.stationModel.updateOne(
|
|
|
+ { _id: stationId },
|
|
|
+ { $pop: { playlist: -1 } },
|
|
|
+ { runValidators: true },
|
|
|
+ err => {
|
|
|
+ if (err) next(err);
|
|
|
+ else
|
|
|
+ StationsModule.runJob(
|
|
|
+ "UPDATE_STATION",
|
|
|
+ {
|
|
|
+ stationId
|
|
|
+ },
|
|
|
+ this
|
|
|
+ )
|
|
|
+ .then(() => {
|
|
|
+ next(null);
|
|
|
+ })
|
|
|
+ .catch(next);
|
|
|
+ }
|
|
|
+ );
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ err => {
|
|
|
+ if (err) reject(err);
|
|
|
+ else resolve();
|
|
|
}
|
|
|
);
|
|
|
});
|
|
@@ -771,72 +773,32 @@ class _StationsModule extends CoreClass {
|
|
|
);
|
|
|
}
|
|
|
|
|
|
- if (station.type === "official" && station.playlist.length === 0) {
|
|
|
- return StationsModule.runJob("CALCULATE_SONG_FOR_STATION", { station }, this)
|
|
|
- .then(playlist => {
|
|
|
- if (playlist.length === 0)
|
|
|
- return next(null, StationsModule.defaultSong, 0, station);
|
|
|
-
|
|
|
- return SongsModule.runJob(
|
|
|
- "GET_SONG",
|
|
|
- {
|
|
|
- id: playlist[0]
|
|
|
- },
|
|
|
+ if (station.type === "official") {
|
|
|
+ StationsModule.runJob(
|
|
|
+ "REMOVE_FIRST_OFFICIAL_PLAYLIST_QUEUE_SONG",
|
|
|
+ { stationId: station._id },
|
|
|
+ this
|
|
|
+ )
|
|
|
+ .then(() => {
|
|
|
+ StationsModule.runJob(
|
|
|
+ "FILL_UP_OFFICIAL_STATION_PLAYLIST_QUEUE",
|
|
|
+ { stationId: station._id },
|
|
|
this
|
|
|
)
|
|
|
- .then(response => {
|
|
|
- next(null, response.song, 0, station);
|
|
|
+ .then(() => {
|
|
|
+ StationsModule.runJob(
|
|
|
+ "GET_NEXT_OFFICIAL_STATION_SONG",
|
|
|
+ { stationId: station._id },
|
|
|
+ this
|
|
|
+ )
|
|
|
+ .then(response => {
|
|
|
+ next(null, response.song, 0, station);
|
|
|
+ })
|
|
|
+ .catch(next);
|
|
|
})
|
|
|
- .catch(() => next(null, StationsModule.defaultSong, 0, station));
|
|
|
+ .catch(next);
|
|
|
})
|
|
|
- .catch(err => {
|
|
|
- next(err);
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- if (station.type === "official" && station.playlist.length > 0) {
|
|
|
- return async.doUntil(
|
|
|
- next => {
|
|
|
- if (station.currentSongIndex < station.playlist.length - 1) {
|
|
|
- SongsModule.runJob(
|
|
|
- "GET_SONG",
|
|
|
- {
|
|
|
- id: station.playlist[station.currentSongIndex + 1]
|
|
|
- },
|
|
|
- this
|
|
|
- )
|
|
|
- .then(response => next(null, response.song, station.currentSongIndex + 1))
|
|
|
- .catch(() => {
|
|
|
- station.currentSongIndex += 1;
|
|
|
- next(null, null, null);
|
|
|
- });
|
|
|
- } else {
|
|
|
- StationsModule.runJob(
|
|
|
- "CALCULATE_SONG_FOR_STATION",
|
|
|
- {
|
|
|
- station
|
|
|
- },
|
|
|
- this
|
|
|
- )
|
|
|
- .then(newPlaylist => {
|
|
|
- SongsModule.runJob("GET_SONG", { id: newPlaylist[0] }, this)
|
|
|
- .then(response => {
|
|
|
- station.playlist = newPlaylist;
|
|
|
- next(null, response.song, 0);
|
|
|
- })
|
|
|
- .catch(() => next(null, StationsModule.defaultSong, 0));
|
|
|
- })
|
|
|
- .catch(() => {
|
|
|
- next(null, StationsModule.defaultSong, 0);
|
|
|
- });
|
|
|
- }
|
|
|
- },
|
|
|
- (song, currentSongIndex, next) => {
|
|
|
- if (song) return next(null, true, currentSongIndex);
|
|
|
- return next(null, false);
|
|
|
- },
|
|
|
- (err, song, currentSongIndex) => next(err, song, currentSongIndex, station)
|
|
|
- );
|
|
|
+ .catch(next);
|
|
|
}
|
|
|
},
|
|
|
(song, currentSongIndex, station, next) => {
|