Owen Diffey 2 роки тому
батько
коміт
ee7594042c

+ 18 - 0
CHANGELOG.md

@@ -1,5 +1,23 @@
 # Changelog
 
+## [v3.5.1] - 2022-05-06
+
+Upgrade instructions can be found at [.wiki/Upgrading](.wiki/Upgrading.md).
+
+### Fixed
+- fix: Songs requestSet could return null songs
+- fix: Prevent adding duplicate items with bulk actions
+- fix: Throw error if unknown job is called
+- fix: EditSongs missing modalUuid socker parameter
+- fix: Unable to focus/blur AdvancedTable rows
+- fix: EditSong song duration can reset on video state change
+- fix: Backend exception if an empty playlist is updated
+- fix: False-positive like/dislike playlist activity
+- fix: Removed debug console.log
+- fix: Station current/next song items requestedAt not updated on song changing
+- fix: AdvancedTable max-width not a number
+- fix: AdvancedTable history.replaceState throws warning
+
 ## [v3.5.0] - 2022-04-28
 
 This release includes all changes from v3.5.0-rc1 and v3.5.0-rc2, in addition to the following. Upgrade instructions can be found at [.wiki/Upgrading](.wiki/Upgrading.md).

+ 4 - 0
backend/core.js

@@ -548,6 +548,8 @@ export default class CoreClass {
 
 			if (previousStatus === "QUEUED") {
 				if (!options.isQuiet) this.log("INFO", `Job ${job.name} (${job.toString()}) is queued, so calling it`);
+
+				if (this[job.name])
 				this[job.name]
 					.apply(job, [job.payload])
 					.then(response => {
@@ -625,6 +627,8 @@ export default class CoreClass {
 						}
 						resolve();
 					});
+				else
+					this.log("ERROR", `Job not found! ${job.name}`)
 			} else {
 				this.log(
 					"INFO",

+ 11 - 6
backend/logic/actions/playlists.js

@@ -244,9 +244,12 @@ CacheModule.runJob("SUB", {
 				const newPlaylist = {
 					...playlist._doc,
 					songsCount: playlist.songs.length,
-					songsLength: playlist.songs.reduce((previous, current) => ({
-						duration: previous.duration + current.duration
-					})).duration
+					songsLength: playlist.songs.reduce(
+						(previous, current) => ({
+							duration: previous.duration + current.duration
+						}),
+						{ duration: 0 }
+					).duration
 				};
 				delete newPlaylist.songs;
 				WSModule.runJob("EMIT_TO_ROOMS", {
@@ -1511,9 +1514,11 @@ export default {
 
 				// update cache representation of the playlist
 				(res, next) => {
-					PlaylistsModule.runJob("UPDATE_PLAYLIST", { playlistId }, this)
-						.then(playlist => next(null, playlist))
-						.catch(next);
+					if (res.modifiedCount === 1)
+						PlaylistsModule.runJob("UPDATE_PLAYLIST", { playlistId }, this)
+							.then(playlist => next(null, playlist))
+							.catch(next);
+					else next("Song wasn't in playlist.");
 				},
 
 				(playlist, next) => {

+ 7 - 8
backend/logic/actions/songs.js

@@ -1191,7 +1191,6 @@ export default {
 									else failed += 1;
 									if (res.message === "This song is already in the database.") alreadyInDatabase += 1;
 									if (res.song) songs[index] = res.song;
-									else songs[index] = null;
 								})
 								.catch(() => {
 									failed += 1;
@@ -1272,7 +1271,7 @@ export default {
 							this
 						)
 						.then(res => {
-							if (res.status === "error")
+							if (res.status === "error" && res.message !== "Song wasn't in playlist.")
 								return next("Unable to remove song from the 'Disliked Songs' playlist.");
 							return next(null, song, user.likedSongsPlaylist);
 						})
@@ -1390,7 +1389,7 @@ export default {
 							this
 						)
 						.then(res => {
-							if (res.status === "error")
+							if (res.status === "error" && res.message !== "Song wasn't in playlist.")
 								return next("Unable to remove song from the 'Liked Songs' playlist.");
 							return next(null, song, user.dislikedSongsPlaylist);
 						})
@@ -1528,7 +1527,7 @@ export default {
 							this
 						)
 						.then(res => {
-							if (res.status === "error")
+							if (res.status === "error" && res.message !== "Song wasn't in playlist.")
 								return next("Unable to remove song from the 'Liked Songs' playlist.");
 							return next(null, song);
 						})
@@ -1625,7 +1624,7 @@ export default {
 							this
 						)
 						.then(res => {
-							if (res.status === "error")
+							if (res.status === "error" && res.message !== "Song wasn't in playlist.")
 								return next("Unable to remove song from the 'Disliked Songs' playlist.");
 							return next(null, song, user.likedSongsPlaylist);
 						})
@@ -1893,7 +1892,7 @@ export default {
 				(songsFound, next) => {
 					const query = {};
 					if (method === "add") {
-						query.$push = { genres: { $each: genres } };
+						query.$addToSet = { genres: { $each: genres } };
 					} else if (method === "remove") {
 						query.$pullAll = { genres };
 					} else if (method === "replace") {
@@ -1991,7 +1990,7 @@ export default {
 				(songsFound, next) => {
 					const query = {};
 					if (method === "add") {
-						query.$push = { artists: { $each: artists } };
+						query.$addToSet = { artists: { $each: artists } };
 					} else if (method === "remove") {
 						query.$pullAll = { artists };
 					} else if (method === "replace") {
@@ -2089,7 +2088,7 @@ export default {
 				(songsFound, next) => {
 					const query = {};
 					if (method === "add") {
-						query.$push = { tags: { $each: tags } };
+						query.$addToSet = { tags: { $each: tags } };
 					} else if (method === "remove") {
 						query.$pullAll = { tags };
 					} else if (method === "replace") {

+ 0 - 1
backend/logic/stations.js

@@ -657,7 +657,6 @@ class _StationsModule extends CoreClass {
 					}
 				],
 				(err, song) => {
-					if (err) console.log(33333, err, payload);
 					if (err) reject(err);
 					else resolve({ song });
 				}

+ 2 - 2
backend/package-lock.json

@@ -1,12 +1,12 @@
 {
   "name": "musare-backend",
-  "version": "3.5.0",
+  "version": "3.5.1",
   "lockfileVersion": 2,
   "requires": true,
   "packages": {
     "": {
       "name": "musare-backend",
-      "version": "3.5.0",
+      "version": "3.5.1",
       "license": "GPL-3.0",
       "dependencies": {
         "async": "^3.2.3",

+ 1 - 1
backend/package.json

@@ -1,7 +1,7 @@
 {
   "name": "musare-backend",
   "private": true,
-  "version": "3.5.0",
+  "version": "3.5.1",
   "type": "module",
   "description": "An open-source collaborative music listening and catalogue curation application. Currently supporting YouTube based content.",
   "main": "index.js",

+ 2 - 2
frontend/package-lock.json

@@ -1,12 +1,12 @@
 {
   "name": "musare-frontend",
-  "version": "3.5.0",
+  "version": "3.5.1",
   "lockfileVersion": 2,
   "requires": true,
   "packages": {
     "": {
       "name": "musare-frontend",
-      "version": "3.5.0",
+      "version": "3.5.1",
       "license": "GPL-3.0",
       "dependencies": {
         "@babel/runtime": "^7.17.9",

+ 1 - 1
frontend/package.json

@@ -5,7 +5,7 @@
     "*.vue"
   ],
   "private": true,
-  "version": "3.5.0",
+  "version": "3.5.1",
   "description": "An open-source collaborative music listening and catalogue curation application. Currently supporting YouTube based content.",
   "main": "main.js",
   "author": "Musare Team",

+ 11 - 3
frontend/src/components/AdvancedTable.vue

@@ -1535,7 +1535,9 @@ export default {
 			this.highlightRow(newItemIndex);
 		},
 		highlightRow(itemIndex) {
-			const rowElement = this.$refs[`row-${itemIndex}`];
+			const rowElement = this.$refs[`row-${itemIndex}`]
+				? this.$refs[`row-${itemIndex}`][0]
+				: null;
 			// Set the last clicked item to no longer be highlighted, if it exists
 			if (this.lastSelectedItemIndex >= 0)
 				this.rows[this.lastSelectedItemIndex].highlighted = false;
@@ -1547,7 +1549,9 @@ export default {
 			this.rows[itemIndex].highlighted = true;
 		},
 		unhighlightRow(itemIndex) {
-			const rowElement = this.$refs[`row-${itemIndex}`];
+			const rowElement = this.$refs[`row-${itemIndex}`]
+				? this.$refs[`row-${itemIndex}`][0]
+				: null;
 			if (rowElement)
 				this.$nextTick(() => {
 					rowElement.blur();
@@ -1900,7 +1904,11 @@ export default {
 				.map(key => `${key}=${queryObject[key]}`)
 				.join("&")}`;
 
-			window.history.replaceState(null, null, queryString);
+			window.history.replaceState(
+				window.history.state,
+				null,
+				queryString
+			);
 		},
 		setLocalStorage() {
 			localStorage.setItem(

+ 66 - 32
frontend/src/components/modals/EditSong/index.vue

@@ -1104,12 +1104,22 @@ export default {
 					if (currentTime !== undefined)
 						this.youtubeVideoCurrentTime = currentTime.toFixed(3);
 
-					if (this.youtubeVideoDuration === "0.000") {
+					if (this.youtubeVideoDuration.indexOf(".000") !== -1) {
 						const duration = this.video.player.getDuration();
 
 						if (duration !== undefined) {
+							if (
+								`${this.youtubeVideoDuration}` ===
+								`${Number(this.song.duration).toFixed(3)}`
+							)
+								this.song.duration = duration.toFixed(3);
+
 							this.youtubeVideoDuration = duration.toFixed(3);
-							this.youtubeVideoNote = "(~)";
+							if (
+								this.youtubeVideoDuration.indexOf(".000") !== -1
+							)
+								this.youtubeVideoNote = "(~)";
+							else this.youtubeVideoNote = "";
 
 							this.drawCanvas();
 						}
@@ -1164,39 +1174,56 @@ export default {
 								const newYoutubeVideoDuration =
 									youtubeDuration.toFixed(3);
 
-								const songDurationNumber = Number(
-									this.song.duration
-								);
-								const songDurationNumber2 =
-									Number(this.song.duration) + 1;
-								const songDurationNumber3 =
-									Number(this.song.duration) - 1;
-								const fixedSongDuration =
-									songDurationNumber.toFixed(3);
-								const fixedSongDuration2 =
-									songDurationNumber2.toFixed(3);
-								const fixedSongDuration3 =
-									songDurationNumber3.toFixed(3);
-
 								if (
-									this.youtubeVideoDuration !==
-										newYoutubeVideoDuration &&
-									(fixedSongDuration ===
-										this.youtubeVideoDuration ||
-										fixedSongDuration2 ===
+									this.youtubeVideoDuration.indexOf(
+										".000"
+									) !== -1 &&
+									`${this.youtubeVideoDuration}` !==
+										`${newYoutubeVideoDuration}`
+								) {
+									const songDurationNumber = Number(
+										this.song.duration
+									);
+									const songDurationNumber2 =
+										Number(this.song.duration) + 1;
+									const songDurationNumber3 =
+										Number(this.song.duration) - 1;
+									const fixedSongDuration =
+										songDurationNumber.toFixed(3);
+									const fixedSongDuration2 =
+										songDurationNumber2.toFixed(3);
+									const fixedSongDuration3 =
+										songDurationNumber3.toFixed(3);
+
+									if (
+										`${this.youtubeVideoDuration}` ===
+											`${Number(
+												this.song.duration
+											).toFixed(3)}` &&
+										(fixedSongDuration ===
 											this.youtubeVideoDuration ||
-										fixedSongDuration3 ===
-											this.youtubeVideoDuration)
-								)
-									this.song.duration =
+											fixedSongDuration2 ===
+												this.youtubeVideoDuration ||
+											fixedSongDuration3 ===
+												this.youtubeVideoDuration)
+									)
+										this.song.duration =
+											newYoutubeVideoDuration;
+
+									this.youtubeVideoDuration =
 										newYoutubeVideoDuration;
-
-								this.youtubeVideoDuration =
-									newYoutubeVideoDuration;
-								this.youtubeVideoNote = "";
+									if (
+										this.youtubeVideoDuration.indexOf(
+											".000"
+										) !== -1
+									)
+										this.youtubeVideoNote = "(~)";
+									else this.youtubeVideoNote = "";
+								}
 
 								if (this.song.duration === -1)
-									this.song.duration = youtubeDuration;
+									this.song.duration =
+										this.youtubeVideoDuration;
 
 								youtubeDuration -= this.song.skipDuration;
 								if (this.song.duration > youtubeDuration + 1) {
@@ -1269,6 +1296,7 @@ export default {
 			this.thumbnailHeight = null;
 			this.youtubeVideoCurrentTime = "0.000";
 			this.youtubeVideoDuration = "0.000";
+			this.youtubeVideoNote = "";
 			this.socket.dispatch("apis.leaveRoom", `edit-song.${songId}`);
 			if (this.$refs.saveButton) this.$refs.saveButton.status = "default";
 		},
@@ -1820,10 +1848,16 @@ export default {
 		},
 		onCloseModal() {
 			const songStringified = JSON.stringify({
-				...this.song
+				...this.song,
+				...{
+					duration: Number(this.song.duration).toFixed(3)
+				}
 			});
 			const originalSongStringified = JSON.stringify({
-				...this.originalSong
+				...this.originalSong,
+				...{
+					duration: Number(this.originalSong.duration).toFixed(3)
+				}
 			});
 			const unsavedChanges = songStringified !== originalSongStringified;
 

+ 26 - 18
frontend/src/components/modals/EditSongs.vue

@@ -1,8 +1,8 @@
 <template>
 	<div>
 		<edit-song
-			:modal-module-path="`modals/editSongs/${this.modalUuid}/editSong`"
-			:modal-uuid="this.modalUuid"
+			:modal-module-path="`modals/editSongs/${modalUuid}/editSong`"
+			:modal-uuid="modalUuid"
 			:bulk="true"
 			:flagged="currentSongFlagged"
 			v-if="currentSong"
@@ -258,23 +258,31 @@ export default {
 			} else this.editNextSong();
 		});
 
-		this.socket.on(`event:admin.song.updated`, res => {
-			const index = this.items
-				.map(item => item.song._id)
-				.indexOf(res.data.song._id);
-			this.items[index].song = {
-				...this.items[index].song,
-				...res.data.song,
-				updated: true
-			};
-		});
+		this.socket.on(
+			`event:admin.song.updated`,
+			res => {
+				const index = this.items
+					.map(item => item.song._id)
+					.indexOf(res.data.song._id);
+				this.items[index].song = {
+					...this.items[index].song,
+					...res.data.song,
+					updated: true
+				};
+			},
+			{ modalUuid: this.modalUuid }
+		);
 
-		this.socket.on(`event:admin.song.removed`, res => {
-			const index = this.items
-				.map(item => item.song._id)
-				.indexOf(res.data.songId);
-			this.items[index].song.removed = true;
-		});
+		this.socket.on(
+			`event:admin.song.removed`,
+			res => {
+				const index = this.items
+					.map(item => item.song._id)
+					.indexOf(res.data.songId);
+				this.items[index].song.removed = true;
+			},
+			{ modalUuid: this.modalUuid }
+		);
 	},
 	beforeUnmount() {
 		this.socket.dispatch("apis.leaveRoom", "edit-songs");

+ 1 - 1
frontend/src/pages/Admin/News.vue

@@ -21,7 +21,7 @@
 				:filters="filters"
 				data-action="news.getData"
 				name="admin-news"
-				max-width="1200"
+				:max-width="1200"
 				:events="events"
 			>
 				<template #column-options="slotProps">

+ 1 - 1
frontend/src/pages/Admin/Punishments.vue

@@ -8,7 +8,7 @@
 				:filters="filters"
 				data-action="punishments.getData"
 				name="admin-punishments"
-				max-width="1200"
+				:max-width="1200"
 			>
 				<template #column-options="slotProps">
 					<div class="row-options">

+ 1 - 1
frontend/src/pages/Admin/Reports.vue

@@ -8,7 +8,7 @@
 				:filters="filters"
 				data-action="reports.getData"
 				name="admin-reports"
-				max-width="1200"
+				:max-width="1200"
 				:events="events"
 			>
 				<template #column-options="slotProps">

+ 1 - 1
frontend/src/pages/Admin/Users/DataRequests.vue

@@ -8,7 +8,7 @@
 				:filters="filters"
 				data-action="dataRequests.getData"
 				name="admin-data-requests"
-				max-width="1200"
+				:max-width="1200"
 				:events="events"
 			>
 				<template #column-options="slotProps">

+ 1 - 1
frontend/src/pages/Admin/Users/index.vue

@@ -8,7 +8,7 @@
 				:filters="filters"
 				data-action="users.getData"
 				name="admin-users"
-				max-width="1200"
+				:max-width="1200"
 				:events="events"
 			>
 				<template #column-options="slotProps">

+ 2 - 0
frontend/src/pages/Station/index.vue

@@ -531,6 +531,7 @@
 								:class="{ 'no-currently-playing': noSong }"
 							>
 								<song-item
+									:key="`songItem-currentSong-${currentSong._id}`"
 									:song="currentSong"
 									:duration="false"
 									:requested-by="true"
@@ -543,6 +544,7 @@
 								class="quadrant"
 							>
 								<song-item
+									:key="`songItem-nextSong-${nextSong._id}`"
 									:song="nextSong"
 									:duration="false"
 									:requested-by="true"