mirror of
https://github.com/LuanRT/YouTube.js.git
synced 2026-06-17 19:42:14 +00:00
Merge branch 'main' of https://github.com/LuanRT/YouTube.js
This commit is contained in:
@@ -8,6 +8,7 @@ import Explore from '../parser/ytmusic/Explore';
|
||||
import Library from '../parser/ytmusic/Library';
|
||||
import Artist from '../parser/ytmusic/Artist';
|
||||
import Album from '../parser/ytmusic/Album';
|
||||
import Playlist from '../parser/ytmusic/Playlist';
|
||||
|
||||
import Parser from '../parser/index';
|
||||
import { observe, YTNode } from '../parser/helpers';
|
||||
@@ -108,6 +109,16 @@ class Music {
|
||||
return new Album(response, this.#actions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves playlist.
|
||||
*/
|
||||
async getPlaylist(playlist_id: string) {
|
||||
throwIfMissing({ playlist_id });
|
||||
|
||||
const response = await this.#actions.browse(`VL${playlist_id.replace(/VL/g, '')}`, { client: 'YTMUSIC' });
|
||||
return new Playlist(response, this.#actions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves song lyrics.
|
||||
*/
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
import Parser from '../index';
|
||||
import { YTNode } from '../helpers';
|
||||
|
||||
class MusicPlaylistShelf extends YTNode {
|
||||
static type = 'MusicPlaylistShelf';
|
||||
|
||||
#continuations;
|
||||
|
||||
constructor(data) {
|
||||
super();
|
||||
this.playlist_id = data.playlistId;
|
||||
this.contents = Parser.parse(data.contents);
|
||||
this.collapsed_item_count = data.collapsedItemCount;
|
||||
this.#continuations = data.continuations;
|
||||
}
|
||||
|
||||
get continuation() {
|
||||
return this.#continuations?.[0]?.nextContinuationData;
|
||||
}
|
||||
}
|
||||
|
||||
export default MusicPlaylistShelf;
|
||||
24
src/parser/classes/MusicPlaylistShelf.ts
Normal file
24
src/parser/classes/MusicPlaylistShelf.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import Parser from '../index';
|
||||
import MusicResponsiveListItem from './MusicResponsiveListItem';
|
||||
|
||||
import { YTNode } from '../helpers';
|
||||
|
||||
class MusicPlaylistShelf extends YTNode {
|
||||
static type = 'MusicPlaylistShelf';
|
||||
|
||||
playlist_id: string;
|
||||
contents;
|
||||
collapsed_item_count: number;
|
||||
continuation: string | null;
|
||||
|
||||
constructor(data: any) {
|
||||
super();
|
||||
|
||||
this.playlist_id = data.playlistId;
|
||||
this.contents = Parser.parseArray<MusicResponsiveListItem>(data.contents, MusicResponsiveListItem);
|
||||
this.collapsed_item_count = data.collapsedItemCount;
|
||||
this.continuation = data.continuations?.[0]?.nextContinuationData?.continuation || null;
|
||||
}
|
||||
}
|
||||
|
||||
export default MusicPlaylistShelf;
|
||||
@@ -43,7 +43,20 @@ export class SectionListContinuation extends YTNode {
|
||||
constructor(data: any) {
|
||||
super();
|
||||
this.contents = Parser.parse(data.contents, true);
|
||||
this.continuation = data.continuations[0].nextContinuationData.continuation;
|
||||
this.continuation = data.continuations?.[0].nextContinuationData.continuation || null;
|
||||
}
|
||||
}
|
||||
|
||||
export class MusicPlaylistShelfContinuation extends YTNode {
|
||||
static readonly type = 'musicPlaylistShelfContinuation';
|
||||
|
||||
continuation: string;
|
||||
contents: ObservedArray<YTNode> | null;
|
||||
|
||||
constructor(data: any) {
|
||||
super();
|
||||
this.contents = Parser.parse(data.contents, true);
|
||||
this.continuation = data.continuations?.[0].nextContinuationData.continuation || null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,6 +233,8 @@ export default class Parser {
|
||||
return new SectionListContinuation(data.sectionListContinuation);
|
||||
if (data.liveChatContinuation)
|
||||
return new LiveChatContinuation(data.liveChatContinuation);
|
||||
if (data.musicPlaylistShelfContinuation)
|
||||
return new MusicPlaylistShelfContinuation(data.musicPlaylistShelfContinuation);
|
||||
}
|
||||
|
||||
static parseRR(actions: any[]) {
|
||||
|
||||
80
src/parser/ytmusic/Playlist.ts
Normal file
80
src/parser/ytmusic/Playlist.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import Parser, { MusicPlaylistShelfContinuation, ParsedResponse, SectionListContinuation } from '../index';
|
||||
import Actions, { AxioslikeResponse } from '../../core/Actions';
|
||||
|
||||
import MusicDetailHeader from '../classes/MusicDetailHeader';
|
||||
import MusicCarouselShelf from '../classes/MusicCarouselShelf';
|
||||
import MusicPlaylistShelf from '../classes/MusicPlaylistShelf';
|
||||
import SectionList from '../classes/SectionList';
|
||||
import { InnertubeError } from '../../utils/Utils';
|
||||
|
||||
class Playlist {
|
||||
#page;
|
||||
#actions;
|
||||
#continuation;
|
||||
|
||||
header;
|
||||
items;
|
||||
|
||||
constructor(response: AxioslikeResponse, actions: Actions) {
|
||||
this.#actions = actions;
|
||||
this.#page = Parser.parseResponse(response.data);
|
||||
this.#actions = actions;
|
||||
|
||||
if (this.#page.continuation_contents) {
|
||||
const data = this.#page.continuation_contents?.as(MusicPlaylistShelfContinuation);
|
||||
this.items = data.contents;
|
||||
this.#continuation = data.continuation;
|
||||
} else {
|
||||
this.header = this.#page.header.item().as(MusicDetailHeader);
|
||||
this.items = this.#page.contents_memo.get('MusicPlaylistShelf')?.[0].as(MusicPlaylistShelf).contents;
|
||||
this.#continuation = this.#page.contents_memo.get('MusicPlaylistShelf')?.[0].as(MusicPlaylistShelf).continuation || null;
|
||||
}
|
||||
}
|
||||
|
||||
get page(): ParsedResponse {
|
||||
return this.#page;
|
||||
}
|
||||
|
||||
get has_continuation() {
|
||||
return !!this.#continuation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves playlist item continuation.
|
||||
*/
|
||||
async getContinuation() {
|
||||
if (this.#continuation) {
|
||||
const response = await this.#actions.browse(this.#continuation, { is_ctoken: true, client: 'YTMUSIC' });
|
||||
return new Playlist(response, this.#actions);
|
||||
}
|
||||
|
||||
throw new InnertubeError('Continuation not found.');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves related playlists
|
||||
*/
|
||||
async getRelated() {
|
||||
let section_continuation = this.#page.contents_memo.get('SectionList')?.[0].as(SectionList).continuation;
|
||||
|
||||
while (section_continuation) {
|
||||
const response = await this.#actions.browse(section_continuation, { is_ctoken: true, client: 'YTMUSIC' });
|
||||
const data = Parser.parseResponse(response.data);
|
||||
const section_list = data.continuation_contents?.as(SectionListContinuation);
|
||||
const sections = section_list?.contents?.as(MusicCarouselShelf);
|
||||
const related = sections?.filter((section) => section.header?.title === 'Related playlists')[0];
|
||||
if (related) {
|
||||
return related.contents || [];
|
||||
}
|
||||
|
||||
section_continuation = section_list?.continuation;
|
||||
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default Playlist;
|
||||
@@ -62,6 +62,11 @@ describe('YouTube.js Tests', () => {
|
||||
const playlist = await this.session.getPlaylist('PLLw0AzOz95FU7w2juhPECP9NyGhbZmz_t', { client: 'YOUTUBE' });
|
||||
expect(playlist.items.length).toBeLessThanOrEqual(100);
|
||||
});
|
||||
|
||||
it('Should retrieve playlist with YouTube Music', async () => {
|
||||
const playlist = await this.session.music.getPlaylist('PLVbEymL-83SyVXXqT7fYX5sEvELvyGjL7');
|
||||
expect(playlist.items.length).toBeLessThanOrEqual(100);
|
||||
});
|
||||
|
||||
it('Should retrieve home feed', async () => {
|
||||
const homefeed = await this.session.getHomeFeed();
|
||||
|
||||
Reference in New Issue
Block a user