Forráskód Böngészése

feat: Re-added ability to delete songs

Owen Diffey 3 éve
szülő
commit
f54452fc74

+ 133 - 80
backend/logic/actions/songs.js

@@ -12,6 +12,7 @@ const SongsModule = moduleManager.modules.songs;
 const ActivitiesModule = moduleManager.modules.activities;
 const YouTubeModule = moduleManager.modules.youtube;
 const PlaylistsModule = moduleManager.modules.playlists;
+const StationsModule = moduleManager.modules.stations;
 
 CacheModule.runJob("SUB", {
 	channel: "song.updated",
@@ -22,19 +23,23 @@ CacheModule.runJob("SUB", {
 
 		songModel.findOne({ _id: data.songId }, (err, song) => {
 			WSModule.runJob("EMIT_TO_ROOMS", {
-				rooms: [
-					"import-album",
-					"admin.songs",
-					"admin.unverifiedSongs",
-					"admin.hiddenSongs",
-					`edit-song.${data.songId}`
-				],
+				rooms: ["import-album", "admin.songs", `edit-song.${data.songId}`],
 				args: ["event:admin.song.updated", { data: { song, oldStatus: data.oldStatus } }]
 			});
 		});
 	}
 });
 
+CacheModule.runJob("SUB", {
+	channel: "song.removed",
+	cb: async data => {
+		WSModule.runJob("EMIT_TO_ROOMS", {
+			rooms: ["import-album", "admin.songs", `edit-song.${data.songId}`],
+			args: ["event:admin.song.removed", { data }]
+		});
+	}
+});
+
 CacheModule.runJob("SUB", {
 	channel: "song.like",
 	cb: data => {
@@ -371,79 +376,127 @@ export default {
 		);
 	}),
 
-	// /**
-	//  * Removes a song
-	//  *
-	//  * @param session
-	//  * @param songId - the song id
-	//  * @param cb
-	//  */
-	// remove: isAdminRequired(async function remove(session, songId, cb) {
-	// 	const songModel = await DBModule.runJob("GET_MODEL", { modelName: "song" }, this);
-	// 	let song = null;
-	// 	async.waterfall(
-	// 		[
-	// 			next => {
-	// 				songModel.findOne({ _id: songId }, next);
-	// 			},
-
-	// 			(_song, next) => {
-	// 				song = _song;
-	// 				songModel.deleteOne({ _id: songId }, next);
-	// 			},
-
-	// 			(res, next) => {
-	// 				CacheModule.runJob("HDEL", { table: "songs", key: songId }, this)
-	// 					.then(() => {
-	// 						next();
-	// 					})
-	// 					.catch(next)
-	// 					.finally(() => {
-	// 						song.genres.forEach(genre => {
-	// 							PlaylistsModule.runJob("AUTOFILL_GENRE_PLAYLIST", { genre })
-	// 								.then(() => {})
-	// 								.catch(() => {});
-	// 						});
-	// 					});
-	// 			}
-	// 		],
-	// 		async err => {
-	// 			if (err) {
-	// 				err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);
-
-	// 				this.log("ERROR", "SONGS_REMOVE", `Failed to remove song "${songId}". "${err}"`);
-
-	// 				return cb({ status: "error", message: err });
-	// 			}
-
-	// 			this.log("SUCCESS", "SONGS_REMOVE", `Successfully removed song "${songId}".`);
-
-	// 			if (song.status === "verified") {
-	// 				CacheModule.runJob("PUB", {
-	// 					channel: "song.removedVerifiedSong",
-	// 					value: songId
-	// 				});
-	// 			}
-	// 			if (song.status === "unverified") {
-	// 				CacheModule.runJob("PUB", {
-	// 					channel: "song.removedUnverifiedSong",
-	// 					value: songId
-	// 				});
-	// 			}
-	// 			if (song.status === "hidden") {
-	// 				CacheModule.runJob("PUB", {
-	// 					channel: "song.removedHiddenSong",
-	// 					value: songId
-	// 				});
-	// 			}
-
-	// 			return cb({
-	// 				status: "success",
-	// 				message: "Song has been successfully removed"
-	// 			});
-	// 		}
-	// 	);
-	// }),
+	/**
+	 * Removes a song
+	 *
+	 * @param session
+	 * @param songId - the song id
+	 * @param cb
+	 */
+	remove: isAdminRequired(async function remove(session, songId, cb) {
+		const songModel = await DBModule.runJob("GET_MODEL", { modelName: "song" }, this);
+		const playlistModel = await DBModule.runJob("GET_MODEL", { modelName: "playlist" }, this);
+		const stationModel = await DBModule.runJob("GET_MODEL", { modelName: "station" }, this);
+
+		async.waterfall(
+			[
+				next => {
+					songModel.findOne({ _id: songId }, next);
+				},
+
+				(song, next) => {
+					playlistModel.find({ "songs._id": songId }, (err, playlists) => {
+						if (err) next(err);
+						else {
+							playlistModel.updateMany(
+								{ "songs._id": songId },
+								{ $pull: { songs: { _id: songId } } },
+								err => {
+									if (err) this.log("ERROR", err);
+									else {
+										async.eachLimit(playlists, 1, (playlist, next) => {
+											PlaylistsModule.runJob(
+												"UPDATE_PLAYLIST",
+												{ playlistId: playlist._id },
+												next
+											);
+										});
+										next(null, song);
+									}
+								}
+							);
+						}
+					});
+				},
+
+				(song, next) => {
+					stationModel.find({ "queue._id": songId }, (err, stations) => {
+						if (err) next(err);
+						else {
+							stationModel.updateMany(
+								{ "queue._id": songId },
+								{ $pull: { queue: { _id: songId } } },
+								err => {
+									if (err) this.log("ERROR", err);
+									else {
+										async.eachLimit(stations, 1, (station, next) => {
+											StationsModule.runJob("UPDATE_STATION", { stationId: station._id }, next);
+										});
+										next(null, song);
+									}
+								}
+							);
+						}
+					});
+				},
+
+				(song, next) => {
+					stationModel.find({ "currentSong._id": songId }, (err, stations) => {
+						if (err) next(err);
+						else {
+							async.eachLimit(stations, 1, (station, next) => {
+								StationsModule.runJob("SKIP_STATION", { stationId: station._id, natural: false }, next);
+							});
+							next(null, song);
+						}
+					});
+				},
+
+				(song, next) => {
+					songModel.deleteOne({ _id: songId }, err => {
+						if (err) next(err);
+						else next(null, song);
+					});
+				},
+
+				(song, next) => {
+					CacheModule.runJob("HDEL", { table: "songs", key: songId }, this)
+						.then(() => {
+							next();
+						})
+						.catch(next)
+						.finally(() => {
+							song.genres.forEach(genre => {
+								PlaylistsModule.runJob("AUTOFILL_GENRE_PLAYLIST", { genre })
+									.then(() => {})
+									.catch(() => {});
+							});
+						});
+				}
+			],
+			async err => {
+				if (err) {
+					err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);
+
+					this.log("ERROR", "SONGS_REMOVE", `Failed to remove song "${songId}". "${err}"`);
+
+					return cb({ status: "error", message: err });
+				}
+
+				this.log("SUCCESS", "SONGS_REMOVE", `Successfully removed song "${songId}".`);
+
+				CacheModule.runJob("PUB", {
+					channel: "song.removed",
+					value: { songId }
+				});
+
+				return cb({
+					status: "success",
+					message: "Song has been successfully removed"
+				});
+			}
+		);
+	}),
 
 	/**
 	 * Searches through official songs

+ 29 - 13
frontend/src/components/modals/EditSong/index.vue

@@ -506,15 +506,18 @@
 						>
 							<i class="material-icons">visibility</i>
 						</button>
-						<!-- <quick-confirm placement="left" @confirm="remove(song._id)">
-						<button
-							class="button is-danger"
-							content="Remove Song"
-							v-tippy
+						<quick-confirm
+							placement="left"
+							@confirm="remove(song._id)"
 						>
-							<i class="material-icons">delete</i>
-						</button>
-					</quick-confirm> -->
+							<button
+								class="button is-danger"
+								content="Remove Song"
+								v-tippy
+							>
+								<i class="material-icons">delete</i>
+							</button>
+						</quick-confirm>
 					</div>
 				</div>
 			</template>
@@ -686,6 +689,19 @@ export default {
 			{ modal: "editSong" }
 		);
 
+		this.socket.on(
+			"event:admin.song.removed",
+			res => {
+				if (res.data.songId === this.song._id) {
+					this.closeModal("editSong");
+					setTimeout(() => {
+						window.focusedElementBefore.focus();
+					}, 500);
+				}
+			},
+			{ modal: "editSong" }
+		);
+
 		keyboardShortcuts.registerShortcut("editSong.pauseResumeVideo", {
 			keyCode: 101,
 			preventDefault: true,
@@ -1548,11 +1564,11 @@ export default {
 				new Toast(res.message);
 			});
 		},
-		// remove(id) {
-		// 	this.socket.dispatch("songs.remove", id, res => {
-		// 		new Toast(res.message);
-		// 	});
-		// },
+		remove(id) {
+			this.socket.dispatch("songs.remove", id, res => {
+				new Toast(res.message);
+			});
+		},
 		...mapActions("modals/importAlbum", [
 			"selectDiscogsAlbum",
 			"updateEditingSongs"

+ 39 - 3
frontend/src/pages/Admin/tabs/Songs.vue

@@ -109,6 +109,16 @@
 						slotProps.item.status
 					}}</span>
 				</template>
+				<template #column-duration="slotProps">
+					<span :title="slotProps.item.duration">{{
+						slotProps.item.duration
+					}}</span>
+				</template>
+				<template #column-skipDuration="slotProps">
+					<span :title="slotProps.item.skipDuration">{{
+						slotProps.item.skipDuration
+					}}</span>
+				</template>
 				<template #column-requestedBy="slotProps">
 					<user-id-to-username
 						:user-id="slotProps.item.requestedBy"
@@ -412,6 +422,22 @@ export default {
 					sortProperty: "thumbnail",
 					defaultVisibility: "hidden"
 				},
+				{
+					name: "duration",
+					displayName: "Duration",
+					properties: ["duration"],
+					sortProperty: "duration",
+					defaultWidth: 200,
+					defaultVisibility: "hidden"
+				},
+				{
+					name: "skipDuration",
+					displayName: "Skip Duration",
+					properties: ["skipDuration"],
+					sortProperty: "skipDuration",
+					defaultWidth: 200,
+					defaultVisibility: "hidden"
+				},
 				{
 					name: "requestedBy",
 					displayName: "Requested By",
@@ -586,7 +612,7 @@ export default {
 				},
 				removed: {
 					event: "admin.song.removed",
-					id: "song._id"
+					id: "songId"
 				}
 			},
 			jobs: [
@@ -708,8 +734,18 @@ export default {
 		setGenres() {
 			new Toast("Bulk setting genres not yet implemented.");
 		},
-		deleteMany() {
-			new Toast("Bulk deleting not yet implemented.");
+		deleteMany(selectedRows) {
+			if (selectedRows.length === 1) {
+				this.socket.dispatch(
+					"songs.remove",
+					selectedRows[0]._id,
+					res => {
+						new Toast(res.message);
+					}
+				);
+			} else {
+				new Toast("Bulk deleting not yet implemented.");
+			}
 		},
 		toggleKeyboardShortcutsHelper() {
 			this.$refs.keyboardShortcutsHelper.toggleBox();