Browse Source

feat: started working on getting alternative album sources in Spotify convert functionality

Kristian Vos 2 years ago
parent
commit
4a4a274e92

+ 68 - 0
backend/logic/actions/apis.js

@@ -536,6 +536,74 @@ export default {
 		}
 	),
 
+	/**
+	 *
+	 *
+	 * @param session
+	 * @param trackId - the trackId
+	 * @param {Function} cb
+	 */
+	getAlternativeAlbumSourcesForAlbums: useHasPermission(
+		"admin.view.spotify",
+		function getAlternativeAlbumSourcesForAlbums(session, albumIds, collectAlternativeAlbumSourcesOrigins, cb) {
+			async.waterfall(
+				[
+					next => {
+						if (!albumIds) {
+							next("Invalid albumIds provided.");
+							return;
+						}
+
+						next();
+					},
+
+					async () => {
+						this.keepLongJob();
+						this.publishProgress({
+							status: "started",
+							title: "Getting alternative album sources for Spotify albums",
+							message: "Starting up",
+							id: this.toString()
+						});
+						console.log("KRIS@4", this.toString());
+						// await CacheModule.runJob(
+						// 	"RPUSH",
+						// 	{ key: `longJobs.${session.userId}`, value: this.toString() },
+						// 	this
+						// );
+
+						SpotifyModule.runJob(
+							"GET_ALTERNATIVE_ALBUM_SOURCES_FOR_ALBUMS",
+							{ albumIds, collectAlternativeAlbumSourcesOrigins },
+							this
+						);
+					}
+				],
+				async err => {
+					if (err) {
+						err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);
+						this.log(
+							"ERROR",
+							"APIS_GET_ALTERNATIVE_ALBUM_SOURCES",
+							`Getting alternative album sources failed for "${albumIds.join(", ")}". "${err}"`
+						);
+						return cb({ status: "error", message: err });
+					}
+					this.log(
+						"SUCCESS",
+						"APIS_GET_ALTERNATIVE_ALBUM_SOURCES",
+						`User "${session.userId}" started getting alternative album spirces for "${albumIds.join(
+							", "
+						)}".`
+					);
+					return cb({
+						status: "success"
+					});
+				}
+			);
+		}
+	),
+
 	/**
 	 * Joins a room
 	 *

+ 99 - 0
backend/logic/spotify.js

@@ -557,6 +557,25 @@ class _SpotifyModule extends CoreClass {
 		});
 	}
 
+	/**
+	 * Get Spotify album
+	 *
+	 * @param {object} payload - an object containing the payload
+	 * @param {string} payload.identifier - the spotify album ObjectId or track id
+	 * @returns {Promise} - returns a promise (resolve, reject)
+	 */
+	async GET_ALBUM(payload) {
+		const query = mongoose.isObjectIdOrHexString(payload.identifier)
+			? { _id: payload.identifier }
+			: { albumId: payload.identifier };
+
+		const album = await SpotifyModule.spotifyAlbumModel.findOne(query);
+
+		if (album) return album._doc;
+
+		return null;
+	}
+
 	/**
 	 * Returns an array of songs taken from a Spotify playlist
 	 *
@@ -654,6 +673,86 @@ class _SpotifyModule extends CoreClass {
 		});
 	}
 
+	/**
+	 *
+	 * @param {*} payload
+	 * @returns
+	 */
+	async GET_ALTERNATIVE_ALBUM_SOURCES_FOR_ALBUMS(payload) {
+		const { albumIds, collectAlternativeAlbumSourcesOrigins } = payload;
+
+		await async.eachLimit(albumIds, 1, async albumId => {
+			try {
+				const result = await SpotifyModule.runJob(
+					"GET_ALTERNATIVE_ALBUM_SOURCES_FOR_ALBUM",
+					{ albumId, collectAlternativeAlbumSourcesOrigins },
+					this
+				);
+				this.publishProgress({
+					status: "working",
+					message: `Got alternative album source for ${albumId}`,
+					data: {
+						albumId,
+						status: "success",
+						result
+					}
+				});
+			} catch (err) {
+				console.log("ERROR", err);
+				this.publishProgress({
+					status: "working",
+					message: `Failed to get alternative album source for ${albumId}`,
+					data: {
+						albumId,
+						status: "error"
+					}
+				});
+			}
+		});
+
+		console.log("Done!");
+
+		this.publishProgress({
+			status: "finished",
+			message: `Finished getting alternative album sources`
+		});
+	}
+
+	/**
+	 *
+	 * @param {*} payload
+	 * @returns
+	 */
+	async GET_ALTERNATIVE_ALBUM_SOURCES_FOR_ALBUM(payload) {
+		const { albumId, collectAlternativeAlbumSourcesOrigins } = payload;
+
+		if (!albumId) throw new Error("Album id provided is not valid.");
+
+		// const album = await SpotifyModule.runJob(
+		// 	"GET_ALBUM",
+		// 	{
+		// 		identifier: albumId
+		// 	},
+		// 	this
+		// );
+
+		const wikiDataResponse = await WikiDataModule.runJob(
+			"API_GET_DATA_FROM_SPOTIFY_ALBUM",
+			{ spotifyAlbumId: albumId },
+			this
+		);
+
+		const youtubePlaylistIds = Array.from(
+			new Set(
+				wikiDataResponse.results.bindings
+					.filter(binding => !!binding.YouTube_playlist_ID)
+					.map(binding => binding.YouTube_playlist_ID.value)
+			)
+		);
+
+		return youtubePlaylistIds;
+	}
+
 	/**
 	 *
 	 * @param {*} payload

+ 65 - 26
backend/logic/wikidata.js

@@ -189,19 +189,19 @@ class _WikiDataModule extends CoreClass {
 		const { workId } = payload;
 
 		const sparqlQuery =
-			`SELECT DISTINCT ?item ?itemLabel ?YouTube_video_ID ?SoundCloud_track_ID ?Music_video_entity_URL WHERE {
-								SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE]". }
-								{
-									SELECT DISTINCT ?item WHERE {
-										?item p:P435 ?statement0.
-										?statement0 ps:P435 "${workId}".
-									}
-									LIMIT 100
-								}
-								OPTIONAL { ?item wdt:P1651 ?YouTube_video_ID. }
-								OPTIONAL { ?item wdt:P3040 ?SoundCloud_track_ID. }
-								OPTIONAL { ?item wdt:P6718 ?Music_video_entity_URL. }
-							}`
+			`SELECT DISTINCT ?item ?itemLabel ?YouTube_video_ID ?SoundCloud_ID ?Music_video_entity_URL WHERE {
+				SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE]". }
+				{
+					SELECT DISTINCT ?item WHERE {
+						?item p:P435 ?statement0.
+						?statement0 ps:P435 "${workId}".
+					}
+					LIMIT 100
+				}
+				OPTIONAL { ?item wdt:P1651 ?YouTube_video_ID. }
+				OPTIONAL { ?item wdt:P3040 ?SoundCloud_ID. }
+				OPTIONAL { ?item wdt:P6718 ?Music_video_entity_URL. }
+			}`
 				.replaceAll("\n", "")
 				.replaceAll("\t", "");
 
@@ -228,19 +228,19 @@ class _WikiDataModule extends CoreClass {
 		const { releaseGroupId } = payload;
 
 		const sparqlQuery =
-			`SELECT DISTINCT ?item ?itemLabel ?YouTube_video_ID ?SoundCloud_track_ID ?Music_video_entity_URL WHERE {
-								SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE]". }
-								{
-									SELECT DISTINCT ?item WHERE {
-										?item p:P436 ?statement0.
-										?statement0 ps:P436 "${releaseGroupId}".
-									}
-									LIMIT 100
-								}
-								OPTIONAL { ?item wdt:P1651 ?YouTube_video_ID. }
-								OPTIONAL { ?item wdt:P3040 ?SoundCloud_track_ID. }
-								OPTIONAL { ?item wdt:P6718 ?Music_video_entity_URL. }
-							}`
+			`SELECT DISTINCT ?item ?itemLabel ?YouTube_video_ID ?SoundCloud_ID ?Music_video_entity_URL WHERE {
+				SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE]". }
+				{
+					SELECT DISTINCT ?item WHERE {
+						?item p:P436 ?statement0.
+						?statement0 ps:P436 "${releaseGroupId}".
+					}
+					LIMIT 100
+				}
+				OPTIONAL { ?item wdt:P1651 ?YouTube_video_ID. }
+				OPTIONAL { ?item wdt:P3040 ?SoundCloud_ID. }
+				OPTIONAL { ?item wdt:P6718 ?Music_video_entity_URL. }
+			}`
 				.replaceAll("\n", "")
 				.replaceAll("\t", "");
 
@@ -256,6 +256,45 @@ class _WikiDataModule extends CoreClass {
 		);
 	}
 
+	/**
+	 * Get WikiData data from Spotify album id
+	 *
+	 * @param {object} payload - object that contains the payload
+	 * @param {object} payload.spotifyAlbumId - Spotify album id
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
+	async API_GET_DATA_FROM_SPOTIFY_ALBUM(payload) {
+		const { spotifyAlbumId } = payload;
+
+		if (!spotifyAlbumId) throw new Error("Invalid Spotify album ID provided.");
+
+		const sparqlQuery = `SELECT DISTINCT ?item ?itemLabel ?YouTube_playlist_ID ?SoundCloud_ID WHERE {
+				SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE]". }
+				{
+					SELECT DISTINCT ?item WHERE {
+						?item p:P2205 ?statement0.
+						?statement0 ps:P2205 "${spotifyAlbumId}".
+					}
+					LIMIT 100
+				}
+				OPTIONAL { ?item wdt:P4300 ?YouTube_playlist_ID. }
+				OPTIONAL { ?item wdt:P3040 ?SoundCloud_ID. }
+			}`
+			.replaceAll("\n", "")
+			.replaceAll("\t", "");
+
+		return WikiDataModule.runJob(
+			"API_CALL",
+			{
+				url: "https://query.wikidata.org/sparql",
+				params: {
+					query: sparqlQuery
+				}
+			},
+			this
+		);
+	}
+
 	/**
 	 * Perform WikiData API call
 	 *

+ 91 - 1
frontend/src/components/modals/ConvertSpotifySongs.vue

@@ -52,6 +52,10 @@ const gettingAllAlternativeMediaPerTrack = ref(false);
 const gotAllAlternativeMediaPerTrack = ref(false);
 const alternativeMediaPerTrack = reactive({});
 
+const gettingAllAlternativeAlbums = ref(false);
+const gotAllAlternativeAlbums = ref(false);
+const alternativeAlbumsPerAlbum = reactive({});
+
 const alternativeMediaMap = reactive({});
 const alternativeMediaFailedMap = reactive({});
 
@@ -329,6 +333,50 @@ const getMissingAlternativeMedia = () => {
 	);
 };
 
+const getAlternativeAlbums = () => {
+	if (gettingAllAlternativeAlbums.value || gotAllAlternativeAlbums.value)
+		return;
+
+	gettingAllAlternativeAlbums.value = true;
+
+	const albumIds = filteredSpotifyAlbums.value.map(album => album.albumId);
+
+	socket.dispatch(
+		"apis.getAlternativeAlbumSourcesForAlbums",
+		albumIds,
+		collectAlternativeMediaSourcesOrigins.value,
+		{
+			cb: res => {
+				console.log(
+					"apis.getAlternativeAlbumSourcesForAlbums response",
+					res
+				);
+			},
+			onProgress: data => {
+				console.log(
+					"apis.getAlternativeAlbumSourcesForAlbums onProgress",
+					data
+				);
+
+				if (data.status === "working") {
+					if (data.data.status === "success") {
+						const { albumId, result } = data.data;
+
+						if (!spotifyAlbums[albumId]) return;
+
+						alternativeAlbumsPerAlbum[albumId] = {
+							youtubePlaylistIds: result
+						};
+					}
+				} else if (data.status === "finished") {
+					gotAllAlternativeAlbums.value = true;
+					gettingAllAlternativeAlbums.value = false;
+				}
+			}
+		}
+	);
+};
+
 const getAlternativeMedia = () => {
 	if (
 		gettingAllAlternativeMediaPerTrack.value ||
@@ -666,6 +714,19 @@ onMounted(() => {
 							>
 								Get Spotify albums
 							</button>
+							<button
+								v-if="
+									loadedSpotifyTracks &&
+									loadedSpotifyAlbums &&
+									!gettingAllAlternativeAlbums &&
+									!gotAllAlternativeAlbums &&
+									currentConvertType === 'album'
+								"
+								class="button is-primary"
+								@click="getAlternativeAlbums()"
+							>
+								Get alternative albums
+							</button>
 						</div>
 
 						<div class="options">
@@ -1215,7 +1276,36 @@ onMounted(() => {
 							<div
 								class="convert-table-cell convert-table-cell-right"
 							>
-								<p>Test</p>
+								<div
+									class="alternative-album-items"
+									v-if="
+										alternativeAlbumsPerAlbum[
+											spotifyAlbum.albumId
+										]
+									"
+								>
+									<div
+										class="alternative-album-item"
+										v-for="youtubePlaylistId in alternativeAlbumsPerAlbum[
+											spotifyAlbum.albumId
+										].youtubePlaylistIds"
+										:key="
+											spotifyAlbum.albumId +
+											youtubePlaylistId
+										"
+									>
+										<p>
+											YouTube Playlist
+											{{ youtubePlaylistId }} has been
+											automatically found
+										</p>
+										<button
+											class="button is-primary is-fullwidth"
+										>
+											Match songs using this playlist
+										</button>
+									</div>
+								</div>
 							</div>
 						</template>
 					</div>