| 
					
				 | 
			
			
				@@ -1,3 +1,251 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+<script setup lang="ts"> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import { ref } from "vue"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import { useStore } from "vuex"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import Toast from "toasters"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import AdvancedTable from "@/components/AdvancedTable.vue"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import RunJobDropdown from "@/components/RunJobDropdown.vue"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const store = useStore(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const { socket } = store.state.websockets; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const columnDefault = ref({ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	sortable: true, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	hidable: true, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	defaultVisibility: "shown", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	draggable: true, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	resizable: true, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	minWidth: 200, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	maxWidth: 600 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+}); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const columns = ref([ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		name: "options", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		displayName: "Options", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		properties: ["_id", "youtubeId"], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		sortable: false, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		hidable: false, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		resizable: false, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		minWidth: 129, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		defaultWidth: 129 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		name: "thumbnailImage", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		displayName: "Thumb", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		properties: ["youtubeId"], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		sortable: false, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		minWidth: 75, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		defaultWidth: 75, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		maxWidth: 75, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		resizable: false 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		name: "youtubeId", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		displayName: "YouTube ID", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		properties: ["youtubeId"], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		sortProperty: "youtubeId", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		minWidth: 120, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		defaultWidth: 120 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		name: "_id", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		displayName: "Video ID", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		properties: ["_id"], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		sortProperty: "_id", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		minWidth: 215, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		defaultWidth: 215 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		name: "title", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		displayName: "Title", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		properties: ["title"], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		sortProperty: "title" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		name: "author", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		displayName: "Author", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		properties: ["author"], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		sortProperty: "author" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		name: "duration", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		displayName: "Duration", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		properties: ["duration"], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		sortProperty: "duration", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		defaultWidth: 200 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		name: "createdAt", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		displayName: "Created At", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		properties: ["createdAt"], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		sortProperty: "createdAt", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		defaultWidth: 200, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		defaultVisibility: "hidden" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+]); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const filters = ref([ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		name: "_id", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		displayName: "Video ID", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		property: "_id", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		filterTypes: ["exact"], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		defaultFilterType: "exact" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		name: "youtubeId", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		displayName: "YouTube ID", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		property: "youtubeId", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		filterTypes: ["contains", "exact", "regex"], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		defaultFilterType: "contains" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		name: "title", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		displayName: "Title", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		property: "title", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		filterTypes: ["contains", "exact", "regex"], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		defaultFilterType: "contains" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		name: "author", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		displayName: "Author", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		property: "author", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		filterTypes: ["contains", "exact", "regex"], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		defaultFilterType: "contains" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		name: "duration", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		displayName: "Duration", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		property: "duration", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		filterTypes: [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			"numberLesserEqual", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			"numberLesser", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			"numberGreater", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			"numberGreaterEqual", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			"numberEquals" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		defaultFilterType: "numberLesser" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		name: "createdAt", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		displayName: "Created At", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		property: "createdAt", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		filterTypes: ["datetimeBefore", "datetimeAfter"], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		defaultFilterType: "datetimeBefore" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		name: "importJob", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		displayName: "Import Job", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		property: "importJob", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		filterTypes: ["special"], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		defaultFilterType: "special" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+]); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const events = ref({ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	adminRoom: "youtubeVideos", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	updated: { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		event: "admin.youtubeVideo.updated", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		id: "youtubeVideo._id", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		item: "youtubeVideo" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	removed: { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		event: "admin.youtubeVideo.removed", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		id: "videoId" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+}); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const jobs = ref([ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		name: "Recalculate all ratings", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		socket: "media.recalculateAllRatings" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+]); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const openModal = payload => 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	store.dispatch("modalVisibility/openModal", payload); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const editOne = song => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	openModal({ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		modal: "editSong", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		data: { song } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	}); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const editMany = selectedRows => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	if (selectedRows.length === 1) editOne(selectedRows[0]); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		const songs = selectedRows.map(row => ({ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			youtubeId: row.youtubeId 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		})); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		openModal({ modal: "editSongs", data: { songs } }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const importAlbum = selectedRows => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	const youtubeIds = selectedRows.map(({ youtubeId }) => youtubeId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	socket.dispatch("songs.getSongsFromYoutubeIds", youtubeIds, res => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		if (res.status === "success") { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			openModal({ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				modal: "importAlbum", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				data: { songs: res.data.songs } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			}); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		} else new Toast("Could not get songs."); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	}); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const removeVideos = videoIds => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	let id; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	let title; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	socket.dispatch("youtube.removeVideos", videoIds, { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		cb: () => {}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		onProgress: res => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			if (res.status === "started") { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				id = res.id; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				title = res.title; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			if (id) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				// TODO fix 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				setJob({ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					name: title, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					...res 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				}); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	}); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const getDateFormatted = createdAt => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	const date = new Date(createdAt); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	const year = date.getFullYear(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	const month = `${date.getMonth() + 1}`.padStart(2, 0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	const day = `${date.getDate()}`.padStart(2, 0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	const hour = `${date.getHours()}`.padStart(2, 0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	const minute = `${date.getMinutes()}`.padStart(2, 0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	return `${year}-${month}-${day} ${hour}:${minute}`; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const handleConfirmed = ({ action, params }) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	if (typeof action === "function") { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		if (params) action(params); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		else action(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const confirmAction = ({ message, action, params }) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	openModal({ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		modal: "confirm", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		data: { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			message, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			action, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			params, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			onCompleted: handleConfirmed 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	}); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+</script> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 <template> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	<div class="admin-tab container"> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		<page-metadata title="Admin | YouTube | Videos" /> 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -53,7 +301,7 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 							confirmAction({ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 								message: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 									'Removing this video will remove it from all playlists and cause a ratings recalculation.', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-								action: 'removeVideos', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+								action: removeVideos, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 								params: slotProps.item._id 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 							}) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 						" 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -130,7 +378,7 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 							confirmAction({ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 								message: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 									'Removing these videos will remove them from all playlists and cause a ratings recalculation.', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-								action: 'removeVideos', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+								action: removeVideos, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 								params: slotProps.item.map(video => video._id) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 							}) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 						" 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -146,260 +394,6 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	</div> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 </template> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-<script> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-import { mapActions, mapGetters } from "vuex"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-import Toast from "toasters"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-import AdvancedTable from "@/components/AdvancedTable.vue"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-import RunJobDropdown from "@/components/RunJobDropdown.vue"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-export default { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	components: { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		AdvancedTable, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		RunJobDropdown 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	data() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		return { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			columnDefault: { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				sortable: true, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				hidable: true, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				defaultVisibility: "shown", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				draggable: true, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				resizable: true, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				minWidth: 200, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				maxWidth: 600 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			columns: [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					name: "options", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					displayName: "Options", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					properties: ["_id", "youtubeId"], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					sortable: false, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					hidable: false, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					resizable: false, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					minWidth: 129, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					defaultWidth: 129 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					name: "thumbnailImage", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					displayName: "Thumb", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					properties: ["youtubeId"], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					sortable: false, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					minWidth: 75, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					defaultWidth: 75, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					maxWidth: 75, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					resizable: false 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					name: "youtubeId", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					displayName: "YouTube ID", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					properties: ["youtubeId"], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					sortProperty: "youtubeId", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					minWidth: 120, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					defaultWidth: 120 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					name: "_id", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					displayName: "Video ID", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					properties: ["_id"], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					sortProperty: "_id", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					minWidth: 215, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					defaultWidth: 215 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					name: "title", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					displayName: "Title", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					properties: ["title"], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					sortProperty: "title" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					name: "author", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					displayName: "Author", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					properties: ["author"], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					sortProperty: "author" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					name: "duration", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					displayName: "Duration", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					properties: ["duration"], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					sortProperty: "duration", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					defaultWidth: 200 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					name: "createdAt", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					displayName: "Created At", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					properties: ["createdAt"], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					sortProperty: "createdAt", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					defaultWidth: 200, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					defaultVisibility: "hidden" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			filters: [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					name: "_id", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					displayName: "Video ID", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					property: "_id", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					filterTypes: ["exact"], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					defaultFilterType: "exact" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					name: "youtubeId", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					displayName: "YouTube ID", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					property: "youtubeId", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					filterTypes: ["contains", "exact", "regex"], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					defaultFilterType: "contains" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					name: "title", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					displayName: "Title", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					property: "title", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					filterTypes: ["contains", "exact", "regex"], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					defaultFilterType: "contains" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					name: "author", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					displayName: "Author", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					property: "author", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					filterTypes: ["contains", "exact", "regex"], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					defaultFilterType: "contains" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					name: "duration", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					displayName: "Duration", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					property: "duration", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					filterTypes: [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-						"numberLesserEqual", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-						"numberLesser", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-						"numberGreater", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-						"numberGreaterEqual", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-						"numberEquals" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					defaultFilterType: "numberLesser" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					name: "createdAt", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					displayName: "Created At", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					property: "createdAt", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					filterTypes: ["datetimeBefore", "datetimeAfter"], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					defaultFilterType: "datetimeBefore" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					name: "importJob", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					displayName: "Import Job", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					property: "importJob", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					filterTypes: ["special"], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					defaultFilterType: "special" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			events: { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				adminRoom: "youtubeVideos", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				updated: { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					event: "admin.youtubeVideo.updated", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					id: "youtubeVideo._id", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					item: "youtubeVideo" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				removed: { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					event: "admin.youtubeVideo.removed", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					id: "videoId" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			jobs: [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					name: "Recalculate all ratings", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					socket: "media.recalculateAllRatings" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	computed: { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		...mapGetters({ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			socket: "websockets/getSocket" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		}) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	methods: { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		editOne(song) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			this.openModal({ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				modal: "editSong", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				data: { song } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			}); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		editMany(selectedRows) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			if (selectedRows.length === 1) this.editOne(selectedRows[0]); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				const songs = selectedRows.map(row => ({ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					youtubeId: row.youtubeId 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				})); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				this.openModal({ modal: "editSongs", data: { songs } }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		importAlbum(selectedRows) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			const youtubeIds = selectedRows.map(({ youtubeId }) => youtubeId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			this.socket.dispatch( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				"songs.getSongsFromYoutubeIds", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				youtubeIds, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				res => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					if (res.status === "success") { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-						this.openModal({ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-							modal: "importAlbum", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-							data: { songs: res.data.songs } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-						}); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					} else new Toast("Could not get songs."); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		removeVideos(videoIds) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			let id; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			let title; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			this.socket.dispatch("youtube.removeVideos", videoIds, { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				cb: () => {}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				onProgress: res => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					if (res.status === "started") { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-						id = res.id; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-						title = res.title; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					if (id) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-						this.setJob({ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-							id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-							name: title, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-							...res 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-						}); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			}); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		getDateFormatted(createdAt) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			const date = new Date(createdAt); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			const year = date.getFullYear(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			const month = `${date.getMonth() + 1}`.padStart(2, 0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			const day = `${date.getDate()}`.padStart(2, 0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			const hour = `${date.getHours()}`.padStart(2, 0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			const minute = `${date.getMinutes()}`.padStart(2, 0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			return `${year}-${month}-${day} ${hour}:${minute}`; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		confirmAction({ message, action, params }) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			this.openModal({ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				modal: "confirm", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				data: { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					message, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					action, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					params, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					onCompleted: this.handleConfirmed 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			}); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		handleConfirmed({ action, params }) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			if (typeof this[action] === "function") { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				if (params) this[action](params); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				else this[action](); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		...mapActions("modalVisibility", ["openModal"]) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-</script> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 <style lang="less" scoped> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 :deep(.song-thumbnail) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	width: 50px; 
			 |