Browse Source

Merge branch 'kris-station-local-skipping' into polishing

Owen Diffey 3 years ago
parent
commit
b6ab11327e

+ 31 - 0
backend/logic/actions/songs.js

@@ -322,6 +322,37 @@ export default {
 		);
 	}),
 
+	/**
+	 * Updates all songs
+	 *
+	 * @param {object} session - the session object automatically added by the websocket
+	 * @param cb
+	 */
+	updateAll: isAdminRequired(async function length(session, cb) {
+		async.waterfall(
+			[
+				next => {
+					SongsModule.runJob("UPDATE_ALL_SONGS", {})
+						.then(() => {
+							next();
+						})
+						.catch(err => {
+							next(err);
+						});
+				}
+			],
+			async err => {
+				if (err) {
+					err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);
+					this.log("ERROR", "SONGS_UPDATE_ALL", `Failed to update all songs. "${err}"`);
+					return cb({ status: "error", message: err });
+				}
+				this.log("SUCCESS", "SONGS_UPDATE_ALL", `Updated all songs successfully.`);
+				return cb({ status: "success", message: "Successfully updated all songs." });
+			}
+		);
+	}),
+
 	/**
 	 * Gets a song from the YouTube song id
 	 *

+ 7 - 4
backend/logic/actions/stations.js

@@ -1013,6 +1013,7 @@ export default {
 		);
 	},
 
+
 	/**
 	 * Gets a station by id
 	 *
@@ -1392,7 +1393,9 @@ export default {
 					value: stationId
 				});
 
-				if (shouldSkip) StationsModule.runJob("SKIP_STATION", { stationId });
+				if (shouldSkip) {
+					StationsModule.runJob("SKIP_STATION", { stationId, natural: false });
+				}
 
 				return cb({
 					status: "success",
@@ -1431,7 +1434,7 @@ export default {
 					this.log("ERROR", "STATIONS_FORCE_SKIP", `Force skipping station "${stationId}" failed. "${err}"`);
 					return cb({ status: "error", message: err });
 				}
-				StationsModule.runJob("SKIP_STATION", { stationId });
+				StationsModule.runJob("SKIP_STATION", { stationId, natural: false });
 				this.log("SUCCESS", "STATIONS_FORCE_SKIP", `Force skipped station "${stationId}" successfully.`);
 				return cb({
 					status: "success",
@@ -2207,7 +2210,7 @@ export default {
 					}
 				});
 
-				StationsModule.runJob("SKIP_STATION", { stationId });
+				StationsModule.runJob("SKIP_STATION", { stationId, natural: false });
 
 				return cb({
 					status: "success",
@@ -2285,7 +2288,7 @@ export default {
 						playMode: newPlayMode
 					}
 				});
-				StationsModule.runJob("SKIP_STATION", { stationId });
+				StationsModule.runJob("SKIP_STATION", { stationId, natural: false });
 				return cb({
 					status: "success",
 					message: "Successfully updated the play mode."

+ 1 - 1
backend/logic/playlists.js

@@ -812,7 +812,7 @@ class _PlaylistsModule extends CoreClass {
 
 					(includedSongs, next) => {
 						if (originalPlaylist.songs.length === 0 && includedSongs.length > 0)
-							StationsModule.runJob("SKIP_STATION", { stationId: payload.stationId });
+							StationsModule.runJob("SKIP_STATION", { stationId: payload.stationId, natural: false });
 						next();
 					}
 				],

+ 45 - 0
backend/logic/songs.js

@@ -371,6 +371,51 @@ class _SongsModule extends CoreClass {
 		);
 	}
 
+	/**
+	 * Updates all songs
+	 *
+	 * @returns {Promise} - returns a promise (resolve, reject)
+	 */
+	UPDATE_ALL_SONGS() {
+		return new Promise((resolve, reject) =>
+			async.waterfall(
+				[
+					next => {
+						return next("Currently disabled since it's broken due to the backend memory leak issue.");
+						SongsModule.SongModel.find({}, next);
+					},
+
+					(songs, next) => {
+						let index = 0;
+						const { length } = songs;
+						async.eachLimit(
+							songs,
+							10,
+							(song, next) => {
+								index += 1;
+								console.log(`Updating song #${index} out of ${length}: ${song._id}`);
+								SongsModule.runJob("UPDATE_SONG", { songId: song._id }, this, 9)
+									.then(() => {
+										next();
+									})
+									.catch(err => {
+										next(err);
+									});
+							},
+							err => {
+								next(err);
+							}
+						);
+					}
+				],
+				err => {
+					if (err && err !== true) return reject(new Error(err));
+					return resolve();
+				}
+			)
+		);
+	}
+
 	/**
 	 * Deletes song from id from Mongo and cache
 	 *

+ 9 - 4
backend/logic/stations.js

@@ -242,7 +242,8 @@ class _StationsModule extends CoreClass {
 									name: `stations.nextSong?id=${station._id}`,
 									cb: () =>
 										StationsModule.runJob("SKIP_STATION", {
-											stationId: station._id
+											stationId: station._id,
+											natural: true
 										}),
 									unique: true,
 									station
@@ -260,7 +261,8 @@ class _StationsModule extends CoreClass {
 							return StationsModule.runJob(
 								"SKIP_STATION",
 								{
-									stationId: station._id
+									stationId: station._id,
+									natural: false
 								},
 								this
 							)
@@ -280,7 +282,8 @@ class _StationsModule extends CoreClass {
 							return StationsModule.runJob(
 								"SKIP_STATION",
 								{
-									stationId: station._id
+									stationId: station._id,
+									natural: false
 								},
 								this
 							)
@@ -695,6 +698,7 @@ class _StationsModule extends CoreClass {
 	 *
 	 * @param {object} payload - object that contains the payload
 	 * @param {string} payload.stationId - the id of the station to skip
+	 * @param {string} payload.natural - whether to skip naturally or forcefully
 	 * @returns {Promise} - returns a promise (resolve, reject)
 	 */
 	SKIP_STATION(payload) {
@@ -953,7 +957,8 @@ class _StationsModule extends CoreClass {
 										currentSong: station.currentSong,
 										startedAt: station.startedAt,
 										paused: station.paused,
-										timePaused: 0
+										timePaused: 0,
+										natural: payload.natural
 									}
 								}
 							]

+ 15 - 0
frontend/src/pages/Admin/tabs/VerifiedSongs.vue

@@ -27,6 +27,15 @@
 			>
 				Keyboard shortcuts helper
 			</button>
+			<!-- <confirm placement="bottom" @confirm="updateAllSongs()">
+				<button
+					class="button is-danger"
+					content="Update all songs"
+					v-tippy
+				>
+					Update all songs
+				</button>
+			</confirm> -->
 			<br />
 			<div>
 				<input
@@ -353,6 +362,12 @@ export default {
 				new Toast(res.message);
 			});
 		},
+		updateAllSongs() {
+			this.socket.dispatch("songs.updateAll", res => {
+				if (res.status === "success") new Toast(res.message);
+				else new Toast(res.message);
+			});
+		},
 		getSet() {
 			if (this.isGettingSet) return;
 			if (this.position >= this.maxPosition) return;

+ 142 - 102
frontend/src/pages/Station/index.vue

@@ -691,7 +691,8 @@ export default {
 			activityWatchVideoDataInterval: null,
 			activityWatchVideoLastStatus: "",
 			activityWatchVideoLastYouTubeId: "",
-			activityWatchVideoLastStartDuration: ""
+			activityWatchVideoLastStartDuration: "",
+			nextCurrentSong: null
 		};
 	},
 	computed: {
@@ -761,54 +762,28 @@ export default {
 		);
 
 		this.socket.on("event:songs.next", res => {
-			const { currentSong } = res.data;
-
-			this.updateCurrentSong(currentSong || {});
-
-			let nextSong = null;
-			if (this.songsList[1]) {
-				nextSong = this.songsList[1].youtubeId
-					? this.songsList[1]
-					: null;
-			}
-			this.updateNextSong(nextSong);
-
-			this.startedAt = res.data.startedAt;
-			this.updateStationPaused(res.data.paused);
-			this.timePaused = res.data.timePaused;
-
-			if (currentSong) {
-				this.updateNoSong(false);
-
-				if (!this.playerReady) this.youtubeReady();
-				else this.playVideo();
-
-				this.socket.dispatch(
-					"songs.getOwnSongRatings",
-					currentSong.youtubeId,
-					res => {
-						if (
-							res.status === "success" &&
-							this.currentSong.youtubeId === res.data.youtubeId
-						) {
-							this.liked = res.data.liked;
-							this.disliked = res.data.disliked;
-
-							if (
-								this.autoSkipDisliked &&
-								res.data.disliked === true
-							) {
-								this.voteSkipStation();
-								new Toast(
-									"Automatically voted to skip disliked song."
-								);
-							}
-						}
-					}
-				);
+			const {
+				currentSong,
+				startedAt,
+				paused,
+				timePaused,
+				naturel
+			} = res.data;
+			if (this.noSong || !naturel) {
+				this.setCurrentSong({
+					currentSong,
+					startedAt,
+					paused,
+					timePaused,
+					pausedAt: 0
+				});
 			} else {
-				if (this.playerReady) this.player.pauseVideo();
-				this.updateNoSong(true);
+				this.setNextCurrentSong({
+					currentSong,
+					startedAt,
+					paused,
+					timePaused
+				});
 			}
 		});
 
@@ -816,12 +791,20 @@ export default {
 			this.pausedAt = res.data.pausedAt;
 			this.updateStationPaused(true);
 			this.pauseLocalPlayer();
+
+			clearTimeout(window.stationNextSongTimeout);
 		});
 
 		this.socket.on("event:stations.resume", res => {
 			this.timePaused = res.data.timePaused;
 			this.updateStationPaused(false);
 			if (!this.localPaused) this.resumeLocalPlayer();
+
+			if (this.currentSong)
+				window.stationNextSongTimeout = setTimeout(
+					this.skipSong,
+					this.getTimeRemaining()
+				);
 		});
 
 		this.socket.on("event:stations.remove", () => {
@@ -1011,6 +994,7 @@ export default {
 		});
 
 		clearInterval(this.activityWatchVideoDataInterval);
+		clearTimeout(window.stationNextSongTimeout);
 
 		this.socket.dispatch("stations.leave", this.station._id, () => {});
 
@@ -1038,6 +1022,94 @@ export default {
 				}
 			);
 		},
+		setNextCurrentSong(nextCurrentSong) {
+			this.nextCurrentSong = nextCurrentSong;
+			if (this.getTimeRemaining() <= 0) {
+				this.skipSong();
+			}
+		},
+		skipSong() {
+			console.log("SKIP_SONG_FN", this.nextCurrentSong);
+			if (this.nextCurrentSong && this.nextCurrentSong.currentSong) {
+				this.setCurrentSong(this.nextCurrentSong);
+			}
+		},
+		setCurrentSong(data) {
+			const {
+				currentSong,
+				startedAt,
+				paused,
+				timePaused,
+				pausedAt
+			} = data;
+
+			this.updateCurrentSong(currentSong || {});
+
+			let nextSong = null;
+			if (this.songsList[0])
+				nextSong = this.songsList[0].youtubeId
+					? this.songsList[0]
+					: null;
+			this.updateNextSong(nextSong);
+			this.nextCurrentSong = {
+				currentSong: null,
+				startedAt: 0,
+				paused,
+				timePaused: 0,
+				pausedAt: 0
+			};
+
+			clearTimeout(window.stationNextSongTimeout);
+
+			this.startedAt = startedAt;
+			this.updateStationPaused(paused);
+			this.timePaused = timePaused;
+			this.pausedAt = pausedAt;
+
+			if (currentSong) {
+				this.updateNoSong(false);
+
+				if (!this.playerReady) this.youtubeReady();
+				else this.playVideo();
+
+				if (!this.stationPaused) {
+					window.stationNextSongTimeout = setTimeout(
+						this.skipSong,
+						this.getTimeRemaining()
+					);
+				}
+
+				this.socket.dispatch(
+					"songs.getOwnSongRatings",
+					currentSong.youtubeId,
+					res => {
+						if (
+							res.status === "success" &&
+							this.currentSong.youtubeId === res.data.youtubeId
+						) {
+							this.liked = res.data.liked;
+							this.disliked = res.data.disliked;
+
+							if (
+								this.autoSkipDisliked &&
+								res.data.disliked === true
+							) {
+								this.voteSkipStation();
+								new Toast(
+									"Automatically voted to skip disliked song."
+								);
+							}
+						}
+					}
+				);
+			} else {
+				if (this.playerReady) this.player.pauseVideo();
+				this.updateNoSong(true);
+			}
+
+			this.calculateTimeElapsed();
+			this.resizeSeekerbar();
+		},
 		youtubeReady() {
 			if (!this.player) {
 				this.player = new window.YT.Player("stationPlayer", {
@@ -1174,6 +1246,12 @@ export default {
 			}
 			return 0;
 		},
+		getTimeRemaining() {
+			if (this.currentSong) {
+				return this.currentSong.duration * 1000 - this.getTimeElapsed();
+			}
+			return 0;
+		},
 		playVideo() {
 			if (this.playerReady) {
 				this.videoLoading = true;
@@ -1185,18 +1263,17 @@ export default {
 				if (window.stationInterval !== 0)
 					clearInterval(window.stationInterval);
 				window.stationInterval = setInterval(() => {
-					this.resizeSeekerbar();
-					this.calculateTimeElapsed();
+					if (!this.stationPaused) {
+						this.resizeSeekerbar();
+						this.calculateTimeElapsed();
+					}
 				}, 150);
 			}
 		},
 		resizeSeekerbar() {
-			if (!this.stationPaused) {
-				this.seekerbarPercentage = parseFloat(
-					(this.getTimeElapsed() / 1000 / this.currentSong.duration) *
-						100
-				);
-			}
+			this.seekerbarPercentage = parseFloat(
+				(this.getTimeElapsed() / 1000 / this.currentSong.duration) * 100
+			);
 		},
 		calculateTimeElapsed() {
 			if (
@@ -1225,7 +1302,7 @@ export default {
 				}
 			}
 
-			if (!this.stationPaused && !this.localPaused) {
+			if (!this.stationPaused && !this.localPaused && this.playerReady) {
 				const timeElapsed = this.getTimeElapsed();
 				const currentPlayerTime =
 					Math.max(
@@ -1298,7 +1375,7 @@ export default {
 
 			const songDuration = this.currentSong.duration;
 			if (songDuration <= duration) this.player.pauseVideo();
-			if (!this.stationPaused && duration <= songDuration)
+			if (duration <= songDuration)
 				this.timeElapsed = utils.formatTime(duration);
 		},
 		toggleLock() {
@@ -1560,53 +1637,16 @@ export default {
 
 						document.body.style.cssText = `--primary-color: var(--${res.data.theme})`;
 
-						const currentSong = res.data.currentSong
-							? res.data.currentSong
-							: {};
-
-						this.updateCurrentSong(currentSong);
+						this.setCurrentSong({
+							currentSong: res.data.currentSong,
+							startedAt: res.data.startedAt,
+							paused: res.data.paused,
+							timePaused: res.data.timePaused,
+							pausedAt: res.data.pausedAt
+						});
 
-						this.startedAt = res.data.startedAt;
-						this.updateStationPaused(res.data.paused);
-						this.timePaused = res.data.timePaused;
 						this.updateUserCount(res.data.userCount);
 						this.updateUsers(res.data.users);
-						this.pausedAt = res.data.pausedAt;
-
-						if (res.data.currentSong) {
-							this.updateNoSong(false);
-							this.youtubeReady();
-							this.playVideo();
-							this.socket.dispatch(
-								"songs.getOwnSongRatings",
-								res.data.currentSong.youtubeId,
-								res => {
-									if (
-										res.status === "success" &&
-										this.currentSong.youtubeId ===
-											res.data.youtubeId
-									) {
-										this.liked = res.data.liked;
-										this.disliked = res.data.disliked;
-									}
-								}
-							);
-						} else {
-							if (this.playerReady) this.player.pauseVideo();
-							this.updateNoSong(true);
-						}
-
-						this.socket.dispatch(
-							"stations.getStationIncludedPlaylistsById",
-							this.station._id,
-							res => {
-								if (res.status === "success") {
-									this.setIncludedPlaylists(
-										res.data.playlists
-									);
-								}
-							}
-						);
 
 						this.socket.dispatch(
 							"stations.getStationExcludedPlaylistsById",

+ 14 - 0
frontend/src/store/modules/station.js

@@ -79,6 +79,20 @@ const mutations = {
 	leaveStation(state) {
 		state.station = {};
 		state.partyPlaylists = [];
+		state.editing = {};
+		state.userCount = 0;
+		state.users = {
+			loggedIn: [],
+			loggedOut: []
+		};
+		state.currentSong = {};
+		state.nextSong = null;
+		state.songsList = [];
+		state.stationPaused = true;
+		state.localPaused = false;
+		state.noSong = true;
+		state.includedPlaylists = [];
+		state.excludedPlaylists = [];
 	},
 	editStation(state, station) {
 		state.editing = { ...station };