فهرست منبع

Station skip for official playlists now uses official playlists

Kristian Vos 4 سال پیش
والد
کامیت
edfbc07ac6
2فایلهای تغییر یافته به همراه218 افزوده شده و 235 حذف شده
  1. 24 3
      backend/logic/playlists.js
  2. 194 232
      backend/logic/stations.js

+ 24 - 3
backend/logic/playlists.js

@@ -211,6 +211,29 @@ class _PlaylistsModule extends CoreClass {
 		});
 	}
 
+	/**
+	 * Gets a station playlist
+	 *
+	 * @param {object} payload - object that contains the payload
+	 * @param {string} payload.staationId - the station id
+	 * @param {string} payload.includeSongs - include the songs
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
+	GET_STATION_PLAYLIST(payload) {
+		return new Promise((resolve, reject) => {
+			const includeObject = payload.includeSongs ? null : { songs: false };
+			PlaylistsModule.playlistModel.findOne(
+				{ type: "station", createdFor: payload.stationId },
+				includeObject,
+				(err, playlist) => {
+					if (err) reject(new Error(err));
+					else if (!playlist) reject(new Error("Playlist not found"));
+					else resolve({ playlist });
+				}
+			);
+		});
+	}
+
 	/**
 	 * Adds a song to a playlist
 	 *
@@ -372,9 +395,7 @@ class _PlaylistsModule extends CoreClass {
 						StationsModule.runJob("GET_STATIONS_THAT_INCLUDE_OR_EXCLUDE_PLAYLIST", { playlistId })
 							.then(response => {
 								response.stationIds.forEach(stationId => {
-									PlaylistsModule.runJob("AUTOFILL_STATION_PLAYLIST", { stationId })
-										.then()
-										.catch();
+									PlaylistsModule.runJob("AUTOFILL_STATION_PLAYLIST", { stationId }).then().catch();
 								});
 							})
 							.catch();

+ 194 - 232
backend/logic/stations.js

@@ -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) => {