mirror of
https://github.com/LuanRT/YouTube.js.git
synced 2026-06-24 07:11:48 +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 Library from '../parser/ytmusic/Library';
|
||||||
import Artist from '../parser/ytmusic/Artist';
|
import Artist from '../parser/ytmusic/Artist';
|
||||||
import Album from '../parser/ytmusic/Album';
|
import Album from '../parser/ytmusic/Album';
|
||||||
|
import Playlist from '../parser/ytmusic/Playlist';
|
||||||
|
|
||||||
import Parser from '../parser/index';
|
import Parser from '../parser/index';
|
||||||
import { observe, YTNode } from '../parser/helpers';
|
import { observe, YTNode } from '../parser/helpers';
|
||||||
@@ -108,6 +109,16 @@ class Music {
|
|||||||
return new Album(response, this.#actions);
|
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.
|
* 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) {
|
constructor(data: any) {
|
||||||
super();
|
super();
|
||||||
this.contents = Parser.parse(data.contents, true);
|
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);
|
return new SectionListContinuation(data.sectionListContinuation);
|
||||||
if (data.liveChatContinuation)
|
if (data.liveChatContinuation)
|
||||||
return new LiveChatContinuation(data.liveChatContinuation);
|
return new LiveChatContinuation(data.liveChatContinuation);
|
||||||
|
if (data.musicPlaylistShelfContinuation)
|
||||||
|
return new MusicPlaylistShelfContinuation(data.musicPlaylistShelfContinuation);
|
||||||
}
|
}
|
||||||
|
|
||||||
static parseRR(actions: any[]) {
|
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' });
|
const playlist = await this.session.getPlaylist('PLLw0AzOz95FU7w2juhPECP9NyGhbZmz_t', { client: 'YOUTUBE' });
|
||||||
expect(playlist.items.length).toBeLessThanOrEqual(100);
|
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 () => {
|
it('Should retrieve home feed', async () => {
|
||||||
const homefeed = await this.session.getHomeFeed();
|
const homefeed = await this.session.getHomeFeed();
|
||||||
|
|||||||
Reference in New Issue
Block a user