mirror of
https://github.com/BluemediaGER/muse.git
synced 2024-09-20 03:25:29 +02:00
110 lines
3.4 KiB
TypeScript
110 lines
3.4 KiB
TypeScript
import {inject, injectable} from 'inversify';
|
|
import * as spotifyURI from 'spotify-uri';
|
|
import {SongMetadata, QueuedPlaylist, MediaSource} from '../services/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';
|
|
|
|
@injectable()
|
|
export default class {
|
|
private readonly youtubeAPI: YoutubeAPI;
|
|
private readonly spotifyAPI: SpotifyAPI;
|
|
|
|
constructor(
|
|
@inject(TYPES.Services.YoutubeAPI) youtubeAPI: YoutubeAPI,
|
|
@inject(TYPES.Services.SpotifyAPI) spotifyAPI: SpotifyAPI) {
|
|
this.youtubeAPI = youtubeAPI;
|
|
this.spotifyAPI = spotifyAPI;
|
|
}
|
|
|
|
async youtubeVideoSearch(query: string, shouldSplitChapters: boolean): Promise<SongMetadata[]> {
|
|
return this.youtubeAPI.search(query, shouldSplitChapters);
|
|
}
|
|
|
|
async youtubeVideo(url: string, shouldSplitChapters: boolean): Promise<SongMetadata[]> {
|
|
return this.youtubeAPI.getVideo(url, shouldSplitChapters);
|
|
}
|
|
|
|
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]> {
|
|
const parsed = spotifyURI.parse(url);
|
|
|
|
switch (parsed.type) {
|
|
case 'album': {
|
|
const [tracks, playlist] = await this.spotifyAPI.getAlbum(url, playlistLimit);
|
|
return this.spotifyToYouTube(tracks, shouldSplitChapters, playlist);
|
|
}
|
|
|
|
case 'playlist': {
|
|
const [tracks, playlist] = await this.spotifyAPI.getPlaylist(url, playlistLimit);
|
|
return this.spotifyToYouTube(tracks, shouldSplitChapters, playlist);
|
|
}
|
|
|
|
case 'track': {
|
|
const tracks = [await this.spotifyAPI.getTrack(url)];
|
|
return this.spotifyToYouTube(tracks, shouldSplitChapters);
|
|
}
|
|
|
|
case 'artist': {
|
|
const tracks = await this.spotifyAPI.getArtist(url, playlistLimit);
|
|
return this.spotifyToYouTube(tracks, shouldSplitChapters);
|
|
}
|
|
|
|
default: {
|
|
return [[], 0, 0];
|
|
}
|
|
}
|
|
}
|
|
|
|
async httpLiveStream(url: string): Promise<SongMetadata> {
|
|
return new Promise((resolve, reject) => {
|
|
ffmpeg(url).ffprobe((err, _) => {
|
|
if (err) {
|
|
reject();
|
|
}
|
|
|
|
resolve({
|
|
url,
|
|
source: MediaSource.HLS,
|
|
isLive: true,
|
|
title: url,
|
|
artist: url,
|
|
length: 0,
|
|
offset: 0,
|
|
playlist: null,
|
|
thumbnailUrl: null,
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
private async spotifyToYouTube(tracks: SpotifyTrack[], shouldSplitChapters: boolean, playlist?: QueuedPlaylist | undefined): Promise<[SongMetadata[], number, number]> {
|
|
const promisedResults = tracks.map(async track => this.youtubeAPI.search(`"${track.name}" "${track.artist}"`, shouldSplitChapters));
|
|
const searchResults = await Promise.allSettled(promisedResults);
|
|
|
|
let nSongsNotFound = 0;
|
|
|
|
// Count songs that couldn't be found
|
|
const songs: SongMetadata[] = searchResults.reduce((accum: SongMetadata[], result) => {
|
|
if (result.status === 'fulfilled') {
|
|
for (const v of result.value) {
|
|
accum.push({
|
|
...v,
|
|
...(playlist ? {playlist} : {}),
|
|
});
|
|
}
|
|
} else {
|
|
nSongsNotFound++;
|
|
}
|
|
|
|
return accum;
|
|
}, []);
|
|
|
|
return [songs, nSongsNotFound, tracks.length];
|
|
}
|
|
}
|