aboutsummaryrefslogtreecommitdiff
path: root/src/services/get-songs.ts
diff options
context:
space:
mode:
authorBobby <[email protected]>2024-11-06 09:42:25 -0500
committerGitHub <[email protected]>2024-11-06 09:42:25 -0500
commitd92fd2a29796c9c5d6ddeb9718bf6fcd6ee5958a (patch)
tree81961cc995661448794669617f1ed439cbe84a3d /src/services/get-songs.ts
parentb605bf220859acd767533e0ab9436ced771bb8e2 (diff)
parent716d6d9f4f2cd1a6872e463e9877203a259478a3 (diff)
downloadmuse-master.tar.xz
muse-master.zip
Merge branch 'museofficial:master' into masterHEADmaster
Diffstat (limited to 'src/services/get-songs.ts')
-rw-r--r--src/services/get-songs.ts102
1 files changed, 94 insertions, 8 deletions
diff --git a/src/services/get-songs.ts b/src/services/get-songs.ts
index b957734..c48d87d 100644
--- a/src/services/get-songs.ts
+++ b/src/services/get-songs.ts
@@ -1,34 +1,120 @@
-import {inject, injectable} from 'inversify';
+import {inject, injectable, optional} from 'inversify';
import * as spotifyURI from 'spotify-uri';
import {SongMetadata, QueuedPlaylist, MediaSource} from './player.js';
import {TYPES} from '../types.js';
import ffmpeg from 'fluent-ffmpeg';
import YoutubeAPI from './youtube-api.js';
import SpotifyAPI, {SpotifyTrack} from './spotify-api.js';
+import {URL} from 'node:url';
@injectable()
export default class {
private readonly youtubeAPI: YoutubeAPI;
- private readonly spotifyAPI: SpotifyAPI;
+ private readonly spotifyAPI?: SpotifyAPI;
- constructor(@inject(TYPES.Services.YoutubeAPI) youtubeAPI: YoutubeAPI, @inject(TYPES.Services.SpotifyAPI) spotifyAPI: SpotifyAPI) {
+ constructor(@inject(TYPES.Services.YoutubeAPI) youtubeAPI: YoutubeAPI, @inject(TYPES.Services.SpotifyAPI) @optional() spotifyAPI?: SpotifyAPI) {
this.youtubeAPI = youtubeAPI;
this.spotifyAPI = spotifyAPI;
}
- async youtubeVideoSearch(query: string, shouldSplitChapters: boolean): Promise<SongMetadata[]> {
+ async getSongs(query: string, playlistLimit: number, shouldSplitChapters: boolean): Promise<[SongMetadata[], string]> {
+ const newSongs: SongMetadata[] = [];
+ let extraMsg = '';
+
+ // Test if it's a complete URL
+ try {
+ const url = new URL(query);
+
+ const YOUTUBE_HOSTS = [
+ 'www.youtube.com',
+ 'youtu.be',
+ 'youtube.com',
+ 'music.youtube.com',
+ 'www.music.youtube.com',
+ ];
+
+ if (YOUTUBE_HOSTS.includes(url.host)) {
+ // YouTube source
+ if (url.searchParams.get('list')) {
+ // YouTube playlist
+ newSongs.push(...await this.youtubePlaylist(url.searchParams.get('list')!, shouldSplitChapters));
+ } else {
+ const songs = await this.youtubeVideo(url.href, shouldSplitChapters);
+
+ if (songs) {
+ newSongs.push(...songs);
+ } else {
+ throw new Error('that doesn\'t exist');
+ }
+ }
+ } else if (url.protocol === 'spotify:' || url.host === 'open.spotify.com') {
+ if (this.spotifyAPI === undefined) {
+ throw new Error('Spotify is not enabled!');
+ }
+
+ const [convertedSongs, nSongsNotFound, totalSongs] = await this.spotifySource(query, playlistLimit, shouldSplitChapters);
+
+ if (totalSongs > playlistLimit) {
+ extraMsg = `a random sample of ${playlistLimit} songs was taken`;
+ }
+
+ if (totalSongs > playlistLimit && nSongsNotFound !== 0) {
+ extraMsg += ' and ';
+ }
+
+ if (nSongsNotFound !== 0) {
+ if (nSongsNotFound === 1) {
+ extraMsg += '1 song was not found';
+ } else {
+ extraMsg += `${nSongsNotFound.toString()} songs were not found`;
+ }
+ }
+
+ newSongs.push(...convertedSongs);
+ } else {
+ const song = await this.httpLiveStream(query);
+
+ if (song) {
+ newSongs.push(song);
+ } else {
+ throw new Error('that doesn\'t exist');
+ }
+ }
+ } catch (err: any) {
+ if (err instanceof Error && err.message === 'Spotify is not enabled!') {
+ throw err;
+ }
+
+ // Not a URL, must search YouTube
+ const songs = await this.youtubeVideoSearch(query, shouldSplitChapters);
+
+ if (songs) {
+ newSongs.push(...songs);
+ } else {
+ throw new Error('that doesn\'t exist');
+ }
+ }
+
+ return [newSongs, extraMsg];
+ }
+
+ private async youtubeVideoSearch(query: string, shouldSplitChapters: boolean): Promise<SongMetadata[]> {
return this.youtubeAPI.search(query, shouldSplitChapters);
}
- async youtubeVideo(url: string, shouldSplitChapters: boolean): Promise<SongMetadata[]> {
+ private async youtubeVideo(url: string, shouldSplitChapters: boolean): Promise<SongMetadata[]> {
return this.youtubeAPI.getVideo(url, shouldSplitChapters);
}
- async youtubePlaylist(listId: string, shouldSplitChapters: boolean): Promise<SongMetadata[]> {
+ private async youtubePlaylist(listId: string, shouldSplitChapters: boolean): Promise<SongMetadata[]> {
return this.youtubeAPI.getPlaylist(listId, shouldSplitChapters);
}
- async spotifySource(url: string, playlistLimit: number, shouldSplitChapters: boolean): Promise<[SongMetadata[], number, number]> {
+ private async spotifySource(url: string, playlistLimit: number, shouldSplitChapters: boolean): Promise<[SongMetadata[], number, number]> {
+ if (this.spotifyAPI === undefined) {
+ return [[], 0, 0];
+ }
+
const parsed = spotifyURI.parse(url);
switch (parsed.type) {
@@ -58,7 +144,7 @@ export default class {
}
}
- async httpLiveStream(url: string): Promise<SongMetadata> {
+ private async httpLiveStream(url: string): Promise<SongMetadata> {
return new Promise((resolve, reject) => {
ffmpeg(url).ffprobe((err, _) => {
if (err) {