소스 검색

feat: worked further on SoundCloud implementation

Kristian Vos 1 년 전
부모
커밋
c1b22a5846

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

@@ -305,8 +305,6 @@ export default {
 			async.waterfall(
 				[
 					next => {
-						console.log(687889, mediaSources);
-
 						SongsModule.runJob(
 							"GET_SONGS",
 							{

+ 1 - 0
backend/logic/db/index.js

@@ -205,6 +205,7 @@ class _DBModule extends CoreClass {
 					// Song
 					this.schemas.song.path("mediaSource").validate(mediaSource => {
 						if (mediaSource.startsWith("youtube:")) return true;
+						if (mediaSource.startsWith("soundcloud:")) return true;
 						return false;
 					});
 

+ 104 - 42
backend/logic/songs.js

@@ -7,6 +7,7 @@ let CacheModule;
 let DBModule;
 let UtilsModule;
 let YouTubeModule;
+let SoundCloudModule;
 let StationsModule;
 let PlaylistsModule;
 let MediaModule;
@@ -32,6 +33,7 @@ class _SongsModule extends CoreClass {
 		DBModule = this.moduleManager.modules.db;
 		UtilsModule = this.moduleManager.modules.utils;
 		YouTubeModule = this.moduleManager.modules.youtube;
+		SoundCloudModule = this.moduleManager.modules.soundcloud;
 		StationsModule = this.moduleManager.modules.stations;
 		PlaylistsModule = this.moduleManager.modules.playlists;
 		MediaModule = this.moduleManager.modules.media;
@@ -185,51 +187,111 @@ class _SongsModule extends CoreClass {
 							mediaSource => !songs.find(song => song.mediaSource === mediaSource)
 						);
 
-						console.log(536546, songs, payload, mediaSources);
+						if (mediaSources.length === 0) return next(null, songs);
 
-						// TODO support spotify here
-						return YouTubeModule.youtubeVideoModel.find(
-							{
-								youtubeId: {
-									$in: mediaSources
-										.filter(mediaSource => mediaSource.startsWith("youtube:"))
-										.map(mediaSource => mediaSource.split(":")[1])
+						const mediaSourceTypes = [];
+						mediaSources.forEach(mediaSource => {
+							const mediaSourceType = mediaSource.split(":")[0];
+							if (mediaSourceTypes.indexOf(mediaSourceType) === -1)
+								mediaSourceTypes.push(mediaSourceType);
+						});
+
+						if (mediaSourceTypes.length !== 1)
+							return next(`Expected 1 media source types, got ${mediaSourceTypes.length}.`);
+						const [mediaSourceType] = mediaSourceTypes;
+
+						if (mediaSourceType === "youtube")
+							return YouTubeModule.youtubeVideoModel.find(
+								{
+									youtubeId: {
+										$in: mediaSources
+											.filter(mediaSource => mediaSource.startsWith("youtube:"))
+											.map(mediaSource => mediaSource.split(":")[1])
+									}
+								},
+								(err, videos) => {
+									if (err) next(err);
+									else {
+										const youtubeVideos = videos.map(video => {
+											const { youtubeId, title, author, duration, thumbnail } = video;
+											return {
+												mediaSource: `youtube:${youtubeId}`,
+												title,
+												artists: [author],
+												genres: [],
+												tags: [],
+												duration,
+												skipDuration: 0,
+												thumbnail:
+													thumbnail ||
+													`https://img.youtube.com/vi/${youtubeId}/mqdefault.jpg`,
+												requestedBy: null,
+												requestedAt: Date.now(),
+												verified: false,
+												youtubeVideoId: video._id
+											};
+										});
+										next(
+											null,
+											payload.mediaSources
+												.map(
+													mediaSource =>
+														songs.find(song => song.mediaSource === mediaSource) ||
+														youtubeVideos.find(video => video.mediaSource === mediaSource)
+												)
+												.filter(song => !!song)
+										);
+									}
 								}
-							},
-							(err, videos) => {
-								if (err) next(err);
-								else {
-									const youtubeVideos = videos.map(video => {
-										const { youtubeId, title, author, duration, thumbnail } = video;
-										return {
-											mediaSource: `youtube:${youtubeId}`,
-											title,
-											artists: [author],
-											genres: [],
-											tags: [],
-											duration,
-											skipDuration: 0,
-											thumbnail:
-												thumbnail || `https://img.youtube.com/vi/${youtubeId}/mqdefault.jpg`,
-											requestedBy: null,
-											requestedAt: Date.now(),
-											verified: false,
-											youtubeVideoId: video._id
-										};
-									});
-									next(
-										null,
-										payload.mediaSources
-											.map(
-												mediaSource =>
-													songs.find(song => song.mediaSource === mediaSource) ||
-													youtubeVideos.find(video => video.mediaSource === mediaSource)
-											)
-											.filter(song => !!song)
-									);
+							);
+
+						if (mediaSourceType === "soundcloud")
+							return SoundCloudModule.soundcloudTrackModel.find(
+								{
+									trackId: {
+										$in: mediaSources
+											.filter(mediaSource => mediaSource.startsWith("soundcloud:"))
+											.map(mediaSource => mediaSource.split(":")[1])
+									}
+								},
+								(err, tracks) => {
+									if (err) next(err);
+									else {
+										const soundcloudSongs = tracks.map(track => {
+											const { trackId, title, username, duration, artworkUrl } = track;
+											return {
+												mediaSource: `soundcloud:${trackId}`,
+												title,
+												artists: [username],
+												genres: [],
+												tags: [],
+												duration,
+												skipDuration: 0,
+												thumbnail: artworkUrl,
+												requestedBy: null,
+												requestedAt: Date.now(),
+												verified: false,
+												soundcloudTrackId: track._id
+											};
+										});
+
+										next(
+											null,
+											payload.mediaSources
+												.map(
+													mediaSource =>
+														songs.find(song => song.mediaSource === mediaSource) ||
+														soundcloudSongs.find(
+															soundcloudSong => soundcloudSong.mediaSource === mediaSource
+														)
+												)
+												.filter(song => !!song)
+										);
+									}
 								}
-							}
-						);
+							);
+
+						return next(`Unknown media source specified: ${mediaSourceType}.`);
 					}
 				],
 				(err, songs) => {

+ 0 - 5
backend/logic/soundcloud.js

@@ -97,13 +97,8 @@ class _SoundCloudModule extends CoreClass {
 	 * @returns {Promise} - returns promise (reject, resolve)
 	 */
 	async initialize() {
-		CacheModule = this.moduleManager.modules.cache;
 		DBModule = this.moduleManager.modules.db;
 		MediaModule = this.moduleManager.modules.media;
-		SongsModule = this.moduleManager.modules.songs;
-		StationsModule = this.moduleManager.modules.stations;
-		PlaylistsModule = this.moduleManager.modules.playlists;
-		WSModule = this.moduleManager.modules.ws;
 
 		// this.youtubeApiRequestModel = this.YoutubeApiRequestModel = await DBModule.runJob("GET_MODEL", {
 		// 	modelName: "youtubeApiRequest"

+ 1 - 1
backend/logic/stations.js

@@ -1030,7 +1030,7 @@ class _StationsModule extends CoreClass {
 						if (!station) return next("Station not found.");
 
 						const { currentSong } = station;
-						if (!currentSong) return next(null, station);
+						if (!currentSong || !currentSong.mediaSource) return next(null, station);
 
 						const stationId = station._id;
 						const skippedAt = new Date();

+ 33 - 5
frontend/src/components/SongItem.vue

@@ -1,5 +1,11 @@
 <script setup lang="ts">
-import { defineAsyncComponent, ref, onMounted, onUnmounted } from "vue";
+import {
+	defineAsyncComponent,
+	ref,
+	computed,
+	onMounted,
+	onUnmounted
+} from "vue";
 import { formatDistance, parseISO } from "date-fns";
 import { storeToRefs } from "pinia";
 import AddToPlaylistDropdown from "./AddToPlaylistDropdown.vue";
@@ -56,6 +62,25 @@ const { hasPermission } = userAuthStore;
 
 const { openModal } = useModalsStore();
 
+const songMediaType = computed(() => {
+	if (
+		!props.song ||
+		!props.song.mediaSource ||
+		props.song.mediaSource.indexOf(":") === -1
+	)
+		return "none";
+	return props.song.mediaSource.split(":")[0];
+});
+const songMediaValue = computed(() => {
+	if (
+		!props.song ||
+		!props.song.mediaSource ||
+		props.song.mediaSource.indexOf(":") === -1
+	)
+		return null;
+	return props.song.mediaSource.split(":")[1];
+});
+
 const formatRequestedAt = () => {
 	if (props.requestedBy && props.song.requestedAt)
 		formatedRequestedAt.value = formatDistance(
@@ -95,12 +120,12 @@ const hoverTippy = () => {
 	hoveredTippy.value = true;
 };
 
-const viewYoutubeVideo = mediaSource => {
+const viewYoutubeVideo = youtubeId => {
 	hideTippyElements();
 	openModal({
 		modal: "viewYoutubeVideo",
 		props: {
-			mediaSource: mediaSource.split(":")[1]
+			youtubeId
 		}
 	});
 };
@@ -244,8 +269,11 @@ onUnmounted(() => {
 					<template #content>
 						<div class="icons-group">
 							<i
-								v-if="disabledActions.indexOf('youtube') === -1"
-								@click="viewYoutubeVideo(song.mediaSource)"
+								v-if="
+									disabledActions.indexOf('youtube') === -1 &&
+									songMediaType === 'youtube'
+								"
+								@click="viewYoutubeVideo(songMediaValue)"
 								content="View YouTube Video"
 								v-tippy
 							>

+ 0 - 1
frontend/src/components/modals/EditSong/index.vue

@@ -230,7 +230,6 @@ const unloadSong = (_mediaSource, songId?) => {
 
 const loadSong = (_mediaSource: string, reset?: boolean) => {
 	songNotFound.value = false;
-	console.log(58890, _mediaSource);
 	socket.dispatch(`songs.getSongsFromMediaSources`, [_mediaSource], res => {
 		const { songs } = res.data;
 		if (res.status === "success" && songs.length > 0) {

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

@@ -592,7 +592,7 @@ const playerStop = () => {
 		youtubePlayer.value.stopVideo();
 	}
 
-	soundcloudPause();
+	soundcloudDestroy();
 };
 const playerPause = () => {
 	if (youtubePlayerReady.value) {
@@ -826,6 +826,8 @@ const setCurrentSong = data => {
 	timePaused.value = _timePaused;
 	pausedAt.value = _pausedAt;
 
+	playerStop();
+
 	if (_currentSong) {
 		updateNoSong(false);
 
@@ -925,7 +927,6 @@ const setCurrentSong = data => {
 			);
 		}
 	} else {
-		playerStop();
 		updateNoSong(true);
 	}