mirror of
https://github.com/BluemediaDev/muse.git
synced 2025-01-19 11:28:56 +01:00
Support playlists with more than 50 items
This commit is contained in:
parent
633349ad75
commit
6e0d4c7927
|
@ -4,7 +4,7 @@ import {toSeconds, parse} from 'iso8601-duration';
|
||||||
import got from 'got';
|
import got from 'got';
|
||||||
import spotifyURI from 'spotify-uri';
|
import spotifyURI from 'spotify-uri';
|
||||||
import Spotify from 'spotify-web-api-node';
|
import Spotify from 'spotify-web-api-node';
|
||||||
import YouTube from 'youtube.ts';
|
import YouTube, {YoutubePlaylistItem, YoutubePlaylistItemsSearch} from 'youtube.ts';
|
||||||
import pLimit from 'p-limit';
|
import pLimit from 'p-limit';
|
||||||
import uniqueRandomArray from 'unique-random-array';
|
import uniqueRandomArray from 'unique-random-array';
|
||||||
import {QueuedSong, QueuedPlaylist} from '../services/player';
|
import {QueuedSong, QueuedPlaylist} from '../services/player';
|
||||||
|
@ -52,9 +52,8 @@ export default class {
|
||||||
async youtubePlaylist(listId: string): Promise<QueuedSong[]> {
|
async youtubePlaylist(listId: string): Promise<QueuedSong[]> {
|
||||||
// YouTube playlist
|
// YouTube playlist
|
||||||
const playlist = await this.youtube.playlists.get(listId);
|
const playlist = await this.youtube.playlists.get(listId);
|
||||||
const {items} = await this.youtube.playlists.items(listId, {maxResults: '50'});
|
|
||||||
|
|
||||||
interface videoResult {
|
interface VideoDetailsResponse {
|
||||||
id: string;
|
id: string;
|
||||||
contentDetails: {
|
contentDetails: {
|
||||||
videoId: string;
|
videoId: string;
|
||||||
|
@ -62,27 +61,58 @@ export default class {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unfortunately, package doesn't provide a method for this
|
const playlistVideos: YoutubePlaylistItem[] = [];
|
||||||
const {items: videos}: {items: videoResult[]} = await got('https://www.googleapis.com/youtube/v3/videos', {searchParams: {
|
const videoDetailsPromises: Array<Promise<void>> = [];
|
||||||
part: 'contentDetails',
|
const videoDetails: VideoDetailsResponse[] = [];
|
||||||
id: items.map(item => item.contentDetails.videoId).join(','),
|
|
||||||
key: this.youtubeKey
|
let nextToken: string | undefined;
|
||||||
}}).json();
|
|
||||||
|
while (playlistVideos.length !== playlist.contentDetails.itemCount) {
|
||||||
|
// TODO: https://github.com/Tenpi/youtube.ts/pull/7
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
const {items, nextPageToken} = (await this.youtube.playlists.items(listId, {maxResults: '50', pageToken: nextToken})) as YoutubePlaylistItemsSearch & {nextPageToken: string | undefined};
|
||||||
|
|
||||||
|
nextToken = nextPageToken;
|
||||||
|
|
||||||
|
playlistVideos.push(...items);
|
||||||
|
|
||||||
|
// Start fetching extra details about videos
|
||||||
|
videoDetailsPromises.push((async () => {
|
||||||
|
// Unfortunately, package doesn't provide a method for this
|
||||||
|
const {items: videoDetailItems}: {items: VideoDetailsResponse[]} = await got('https://www.googleapis.com/youtube/v3/videos', {searchParams: {
|
||||||
|
part: 'contentDetails',
|
||||||
|
id: items.map(item => item.contentDetails.videoId).join(','),
|
||||||
|
key: this.youtubeKey
|
||||||
|
}}).json();
|
||||||
|
|
||||||
|
videoDetails.push(...videoDetailItems);
|
||||||
|
})());
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(videoDetailsPromises);
|
||||||
|
|
||||||
const queuedPlaylist = {title: playlist.snippet.title, source: playlist.id};
|
const queuedPlaylist = {title: playlist.snippet.title, source: playlist.id};
|
||||||
|
|
||||||
return items.map(video => {
|
const songsToReturn: QueuedSong[] = [];
|
||||||
const length = toSeconds(parse(videos.find((i: { id: string }) => i.id === video.contentDetails.videoId)!.contentDetails.duration));
|
|
||||||
|
|
||||||
return {
|
for (let video of playlistVideos) {
|
||||||
title: video.snippet.title,
|
try {
|
||||||
artist: video.snippet.channelTitle,
|
const length = toSeconds(parse(videoDetails.find((i: { id: string }) => i.id === video.contentDetails.videoId)!.contentDetails.duration));
|
||||||
length,
|
|
||||||
url: video.contentDetails.videoId,
|
songsToReturn.push({
|
||||||
playlist: queuedPlaylist,
|
title: video.snippet.title,
|
||||||
isLive: false
|
artist: video.snippet.channelTitle,
|
||||||
};
|
length,
|
||||||
});
|
url: video.contentDetails.videoId,
|
||||||
|
playlist: queuedPlaylist,
|
||||||
|
isLive: false
|
||||||
|
});
|
||||||
|
} catch (_: unknown) {
|
||||||
|
// Private and deleted videos are sometimes in playlists, duration of these is not returned and they should not be added to the queue.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return songsToReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
async spotifySource(url: string): Promise<[QueuedSong[], number, number]> {
|
async spotifySource(url: string): Promise<[QueuedSong[], number, number]> {
|
||||||
|
|
Loading…
Reference in a new issue