소스 검색

Merge branch 'polishing' of github.com:Musare/MusareNode into polishing

Jonathan Graham 3 년 전
부모
커밋
32b9ec513c

+ 71 - 3
backend/logic/actions/playlists.js

@@ -1461,9 +1461,13 @@ export default {
 				},
 
 				(res, next) => {
-					PlaylistsModule.runJob("UPDATE_PLAYLIST", { playlistId }, this)
-						.then(playlist => next(null, playlist))
-						.catch(next);
+					if (res.n === 0) next("No user playlist found with that id and owned by you.");
+					else if (res.nModified === 0) next(`Nothing changed, the playlist was already ${privacy}.`);
+					else {
+						PlaylistsModule.runJob("UPDATE_PLAYLIST", { playlistId }, this)
+							.then(playlist => next(null, playlist))
+							.catch(next);
+					}
 				}
 			],
 			async (err, playlist) => {
@@ -1510,6 +1514,70 @@ export default {
 		);
 	}),
 
+	/**
+	 * Updates the privacy of a playlist
+	 *
+	 * @param {object} session - the session object automatically added by the websocket
+	 * @param {string} playlistId - the id of the playlist we are updating the privacy for
+	 * @param {string} privacy - what the new privacy of the playlist should be e.g. public
+	 * @param {Function} cb - gets called with the result
+	 */
+	updatePrivacyAdmin: isAdminRequired(async function updatePrivacyAdmin(session, playlistId, privacy, cb) {
+		const playlistModel = await DBModule.runJob("GET_MODEL", { modelName: "playlist" }, this);
+
+		async.waterfall(
+			[
+				next => {
+					playlistModel.updateOne({ _id: playlistId }, { $set: { privacy } }, { runValidators: true }, next);
+				},
+
+				(res, next) => {
+					if (res.n === 0) next("No playlist found with that id.");
+					else if (res.nModified === 0) next(`Nothing changed, the playlist was already ${privacy}.`);
+					else {
+						PlaylistsModule.runJob("UPDATE_PLAYLIST", { playlistId }, this)
+							.then(playlist => next(null, playlist))
+							.catch(next);
+					}
+				}
+			],
+			async (err, playlist) => {
+				if (err) {
+					err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);
+
+					this.log(
+						"ERROR",
+						"PLAYLIST_UPDATE_PRIVACY_ADMIN",
+						`Updating privacy to "${privacy}" for playlist "${playlistId}" failed for user "${session.userId}". "${err}"`
+					);
+
+					return cb({ status: "error", message: err });
+				}
+
+				this.log(
+					"SUCCESS",
+					"PLAYLIST_UPDATE_PRIVACY_ADMIn",
+					`Successfully updated privacy to "${privacy}" for playlist "${playlistId}" for user "${session.userId}".`
+				);
+
+				if (playlist.type === "user") {
+					CacheModule.runJob("PUB", {
+						channel: "playlist.updatePrivacy",
+						value: {
+							userId: playlist.createdBy,
+							playlist
+						}
+					});
+				}
+
+				return cb({
+					status: "success",
+					message: "Playlist has been successfully updated"
+				});
+			}
+		);
+	}),
+
 	/**
 	 * Deletes all orphaned station playlists
 	 *

+ 3 - 1
backend/logic/actions/stations.js

@@ -48,6 +48,7 @@ CacheModule.runJob("SUB", {
 
 				sockets.forEach(async socketId => {
 					const socket = await WSModule.runJob("SOCKET_FROM_SOCKET_ID", { socketId }, this);
+					if (!socket) return;
 					const { session } = socket;
 
 					if (session.sessionId) {
@@ -513,6 +514,7 @@ CacheModule.runJob("SUB", {
 
 				sockets.forEach(async socketId => {
 					const socket = await WSModule.runJob("SOCKET_FROM_SOCKET_ID", { socketId }, this);
+					if (!socket) return;
 					const { session } = socket;
 
 					if (session.sessionId) {
@@ -1326,7 +1328,7 @@ export default {
 						(socketId, next) => {
 							WSModule.runJob("SOCKET_FROM_SOCKET_ID", { socketId }, this)
 								.then(socket => {
-									if (socket.session && socket.session.userId) {
+									if (socket && socket.session && socket.session.userId) {
 										if (!users.includes(socket.session.userId)) users.push(socket.session.userId);
 									} else users.push(socketId);
 									return next();

+ 2 - 1
backend/logic/stations.js

@@ -1006,6 +1006,7 @@ class _StationsModule extends CoreClass {
 
 						sockets.forEach(async socketId => {
 							const socket = await WSModule.runJob("SOCKET_FROM_SOCKET_ID", { socketId });
+							if (!socket) return;
 							const { session } = socket;
 
 							if (session.sessionId) {
@@ -1175,7 +1176,7 @@ class _StationsModule extends CoreClass {
 						(socketId, next) => {
 							WSModule.runJob("SOCKET_FROM_SOCKET_ID", { socketId }, this)
 								.then(socket => {
-									sockets.push(socket);
+									if (socket) sockets.push(socket);
 									next();
 								})
 								.catch(err => {

+ 1 - 1
backend/logic/ws.js

@@ -317,7 +317,7 @@ class _WSModule extends CoreClass {
 				return WSModule.rooms[payload.room].forEach(async socketId => {
 					// get every socketId (and thus every socket) in the room, and dispatch to each
 					const socket = await WSModule.runJob("SOCKET_FROM_SOCKET_ID", { socketId }, this);
-					socket.dispatch(...payload.args);
+					if (socket) socket.dispatch(...payload.args);
 					return resolve();
 				});
 

+ 3 - 3
frontend/package-lock.json

@@ -8233,9 +8233,9 @@
       "integrity": "sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw=="
     },
     "tar": {
-      "version": "6.1.0",
-      "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.0.tgz",
-      "integrity": "sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA==",
+      "version": "6.1.5",
+      "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.5.tgz",
+      "integrity": "sha512-FiK6MQyyaqd5vHuUjbg/NpO8BuEGeSXcmlH7Pt/JkugWS8s0w8nKybWjHDJiwzCAIKZ66uof4ghm4tBADjcqRA==",
       "dev": true,
       "requires": {
         "chownr": "^2.0.0",

+ 0 - 7
frontend/src/App.vue

@@ -82,13 +82,6 @@ export default {
 		}
 	},
 	async mounted() {
-		// if (
-		// 	window.matchMedia &&
-		// 	window.matchMedia("(prefers-color-scheme: dark)").matches
-		// ) {
-		// 	if (!this.nightmode) this.toggleNightMode();
-		// } else if (this.nightmode) this.toggleNightMode();
-
 		window
 			.matchMedia("(prefers-color-scheme: dark)")
 			.addEventListener("change", e => {

+ 3 - 1
frontend/src/components/modals/EditPlaylist/Tabs/Settings.vue

@@ -117,7 +117,9 @@ export default {
 			const { privacy } = this.playlist;
 			if (privacy === "public" || privacy === "private") {
 				this.socket.dispatch(
-					"playlists.updatePrivacy",
+					this.playlist.type === "genre"
+						? "playlists.updatePrivacyAdmin"
+						: "playlists.updatePrivacy",
 					this.playlist._id,
 					privacy,
 					res => {

+ 14 - 1
frontend/src/components/modals/EditPlaylist/index.vue

@@ -195,6 +195,12 @@
 									</div>
 								</template>
 							</draggable>
+							<p
+								v-else-if="gettingSongs"
+								class="nothing-here-text"
+							>
+								Loading songs...
+							</p>
 							<p v-else class="nothing-here-text">
 								This playlist doesn't have any songs.
 							</p>
@@ -283,7 +289,8 @@ export default {
 		return {
 			utils,
 			drag: false,
-			apiDomain: ""
+			apiDomain: "",
+			gettingSongs: false
 		};
 	},
 	computed: {
@@ -325,12 +332,14 @@ export default {
 		})
 	},
 	mounted() {
+		this.gettingSongs = true;
 		this.socket.dispatch("playlists.getPlaylist", this.editing, res => {
 			if (res.status === "success") {
 				// this.playlist = res.data.playlist;
 				// this.playlist.songs.sort((a, b) => a.position - b.position);
 				this.setPlaylist(res.data.playlist);
 			} else new Toast(res.message);
+			this.gettingSongs = false;
 		});
 
 		this.socket.on(
@@ -390,6 +399,9 @@ export default {
 			{ modal: "editPlaylist" }
 		);
 	},
+	beforeUnmount() {
+		this.clearPlaylist();
+	},
 	methods: {
 		isEditable() {
 			return (
@@ -575,6 +587,7 @@ export default {
 		}),
 		...mapActions("modals/editPlaylist", [
 			"setPlaylist",
+			"clearPlaylist",
 			"addSong",
 			"removeSong",
 			"repositionedSong"

+ 71 - 94
frontend/src/pages/Station/index.vue

@@ -42,7 +42,8 @@
 				currentSong &&
 				(currentSong.youtubeId === 'l9PxOanFjxQ' ||
 					currentSong.youtubeId === 'xKVcVSYmesU' ||
-					currentSong.youtubeId === '60ItHLz5WEA')
+					currentSong.youtubeId === '60ItHLz5WEA' ||
+					currentSong.youtubeId === 'e6vkFbtSGm0')
 			"
 			class="bg-bubbles"
 		>
@@ -256,7 +257,9 @@
 											currentSong.youtubeId ===
 												'xKVcVSYmesU' ||
 											currentSong.youtubeId ===
-												'60ItHLz5WEA')
+												'60ItHLz5WEA' ||
+											currentSong.youtubeId ===
+												'e6vkFbtSGm0')
 									"
 									src="/assets/old_logo.png"
 									:style="{
@@ -853,7 +856,10 @@ export default {
 			activityWatchVideoLastStartDuration: "",
 			nextCurrentSong: null,
 			editSongModalWatcher: null,
-			beforeEditSongModalLocalPaused: null
+			beforeEditSongModalLocalPaused: null,
+			socketConnected: null,
+			persistentToastCheckerInterval: null,
+			persistentToasts: []
 		};
 	},
 	computed: {
@@ -932,9 +938,48 @@ export default {
 		this.activityWatchVideoDataInterval = setInterval(() => {
 			this.sendActivityWatchVideoData();
 		}, 1000);
+		this.persistentToastCheckerInterval = setInterval(() => {
+			this.persistentToasts.filter(
+				persistentToast => !persistentToast.checkIfCanRemove()
+			);
+		}, 1000);
 
 		if (this.socket.readyState === 1) this.join();
-		ws.onConnect(() => this.join());
+		ws.onConnect(() => {
+			this.socketConnected = true;
+			clearTimeout(window.stationNextSongTimeout);
+			this.join();
+		});
+
+		ws.onDisconnect(true, () => {
+			this.socketConnected = false;
+			const { currentSong } = this.currentSong;
+			if (this.nextSong)
+				this.setNextCurrentSong(
+					{
+						currentSong: this.nextSong,
+						startedAt: Date.now() + this.getTimeRemaining(),
+						paused: false,
+						timePaused: 0
+					},
+					true
+				);
+			else
+				this.setNextCurrentSong(
+					{
+						currentSong: null,
+						startedAt: 0,
+						paused: false,
+						timePaused: 0,
+						pausedAt: 0
+					},
+					true
+				);
+			window.stationNextSongTimeout = setTimeout(() => {
+				if (!this.noSong && this.currentSong._id === currentSong._id)
+					this.skipSong("window.stationNextSongTimeout 2");
+			}, this.getTimeRemaining());
+		});
 
 		this.frontendDevMode = await lofig.get("mode");
 
@@ -959,32 +1004,15 @@ export default {
 		);
 
 		this.socket.on("event:station.nextSong", res => {
-			const { currentSong, startedAt, paused, timePaused, natural } =
-				res.data;
-
-			if (this.noSong || !natural) {
-				this.setCurrentSong({
-					currentSong,
-					startedAt,
-					paused,
-					timePaused,
-					pausedAt: 0
-				});
-			} else if (this.currentSong._id === currentSong._id) {
-				if (this.currentSong.skipVotesLoaded !== true) {
-					this.updateCurrentSongSkipVotes({
-						skipVotes: currentSong.skipVotes,
-						skipVotesCurrent: true
-					});
-				}
-			} else {
-				this.setNextCurrentSong({
-					currentSong,
-					startedAt,
-					paused,
-					timePaused
-				});
-			}
+			const { currentSong, startedAt, paused, timePaused } = res.data;
+
+			this.setCurrentSong({
+				currentSong,
+				startedAt,
+				paused,
+				timePaused,
+				pausedAt: 0
+			});
 		});
 
 		this.socket.on("event:station.pause", res => {
@@ -999,29 +1027,6 @@ export default {
 			this.timePaused = res.data.timePaused;
 			this.updateStationPaused(false);
 			if (!this.localPaused) this.resumeLocalPlayer();
-
-			if (this.currentSong) {
-				const currentSongId = this.currentSong._id;
-				if (this.nextSong)
-					this.setNextCurrentSong({
-						currentSong: this.nextSong,
-						startedAt: Date.now() + this.getTimeRemaining(),
-						paused: false,
-						timePaused: 0
-					});
-				else
-					this.setNextCurrentSong({
-						currentSong: null,
-						startedAt: 0,
-						paused: false,
-						timePaused: 0,
-						pausedAt: 0
-					});
-				window.stationNextSongTimeout = setTimeout(() => {
-					if (!this.noSong && this.currentSong._id === currentSongId)
-						this.skipSong("window.stationNextSongTimeout 2");
-				}, this.getTimeRemaining());
-			}
 		});
 
 		this.socket.on("event:station.deleted", () => {
@@ -1080,22 +1085,6 @@ export default {
 
 			this.updateNextSong(nextSong);
 
-			if (nextSong)
-				this.setNextCurrentSong({
-					currentSong: nextSong,
-					startedAt: Date.now() + this.getTimeRemaining(),
-					paused: false,
-					timePaused: 0
-				});
-			else
-				this.setNextCurrentSong({
-					currentSong: null,
-					startedAt: 0,
-					paused: false,
-					timePaused: 0,
-					pausedAt: 0
-				});
-
 			this.addPartyPlaylistSongToQueue();
 		});
 
@@ -1109,13 +1098,6 @@ export default {
 					: null;
 
 			this.updateNextSong(nextSong);
-
-			this.setNextCurrentSong({
-				currentSong: nextSong,
-				startedAt: Date.now() + this.getTimeRemaining(),
-				paused: false,
-				timePaused: 0
-			});
 		});
 
 		this.socket.on("event:station.voteSkipSong", () => {
@@ -1238,6 +1220,10 @@ export default {
 
 		clearInterval(this.activityWatchVideoDataInterval);
 		clearTimeout(window.stationNextSongTimeout);
+		clearTimeout(this.persistentToastCheckerInterval);
+		this.persistentToasts.forEach(persistentToast => {
+			persistentToast.toast.destroy();
+		});
 
 		this.socket.dispatch("stations.leave", this.station._id, () => {});
 
@@ -1338,7 +1324,8 @@ export default {
 				if (!this.playerReady) this.youtubeReady();
 				else this.playVideo();
 
-				if (!this.stationPaused) {
+				// If the station is playing and the backend is not connected, set the next song to skip to after this song and set a timer to skip
+				if (!this.stationPaused && !this.socketConnected) {
 					if (this.nextSong)
 						this.setNextCurrentSong(
 							{
@@ -1501,22 +1488,19 @@ export default {
 								const erroredYoutubeId =
 									this.currentSong.youtubeId;
 
-								// remove persistent toast if video has finished
-								window.isSongErroredInterval = setInterval(
-									() => {
+								this.persistentToasts.push({
+									toast: persistentToast,
+									checkIfCanRemove: () => {
 										if (
 											this.currentSong.youtubeId !==
 											erroredYoutubeId
 										) {
 											persistentToast.destroy();
-
-											clearInterval(
-												window.isSongErroredInterval
-											);
+											return true;
 										}
-									},
-									150
-								);
+										return false;
+									}
+								});
 							} else {
 								new Toast(
 									"There has been an error with the YouTube Embed"
@@ -2021,13 +2005,6 @@ export default {
 								const [nextSong] = queue;
 
 								this.updateNextSong(nextSong);
-								this.setNextCurrentSong({
-									currentSong: nextSong,
-									startedAt:
-										Date.now() + this.getTimeRemaining(),
-									paused: false,
-									timePaused: 0
-								});
 							}
 						});
 

+ 4 - 0
frontend/src/store/modules/modals/editPlaylist.js

@@ -10,6 +10,7 @@ export default {
 	actions: {
 		showTab: ({ commit }, tab) => commit("showTab", tab),
 		setPlaylist: ({ commit }, playlist) => commit("setPlaylist", playlist),
+		clearPlaylist: ({ commit }) => commit("clearPlaylist"),
 		addSong: ({ commit }, song) => commit("addSong", song),
 		removeSong: ({ commit }, youtubeId) => commit("removeSong", youtubeId),
 		updatePlaylistSongs: ({ commit }, playlistSongs) =>
@@ -24,6 +25,9 @@ export default {
 			state.playlist = { ...playlist };
 			state.playlist.songs.sort((a, b) => a.position - b.position);
 		},
+		clearPlaylist(state) {
+			state.playlist = { songs: [] };
+		},
 		addSong(state, song) {
 			state.playlist.songs.push(song);
 		},