mirror of
https://github.com/LuanRT/YouTube.js.git
synced 2026-06-27 00:29:16 +00:00
feat(ytmusic): add support for retrieving albums
This commit is contained in:
@@ -6,6 +6,7 @@ const HomeFeed = require('../parser/ytmusic/HomeFeed');
|
||||
const Explore = require('../parser/ytmusic/Explore');
|
||||
const Library = require('../parser/ytmusic/Library');
|
||||
const Artist = require('../parser/ytmusic/Artist');
|
||||
const Album = require('../parser/ytmusic/Album');
|
||||
|
||||
const { InnertubeError, observe } = require('../utils/Utils');
|
||||
|
||||
@@ -68,13 +69,25 @@ class Music {
|
||||
/**
|
||||
* Retrieves artist's info & content.
|
||||
*
|
||||
* @param {string} id
|
||||
* @param {string} artist_id
|
||||
* @returns {Promise.<Artist>}
|
||||
*/
|
||||
async getArtist(artist_id) {
|
||||
const response = await this.#actions.browse(artist_id, { client: 'YTMUSIC' });
|
||||
return new Artist(response, this.#actions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves album.
|
||||
*
|
||||
* @param {string} album_id
|
||||
* @returns {Promise.<Album>}
|
||||
*/
|
||||
async getAlbum(album_id) {
|
||||
const response = await this.#actions.browse(album_id, { client: 'YTMUSIC' });
|
||||
return new Album(response, this.#actions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves song lyrics.
|
||||
*
|
||||
|
||||
33
lib/parser/contents/classes/MusicDetailHeader.js
Normal file
33
lib/parser/contents/classes/MusicDetailHeader.js
Normal file
@@ -0,0 +1,33 @@
|
||||
'use strict';
|
||||
|
||||
const Text = require('./Text');
|
||||
const Thumbnail = require('./Thumbnail');
|
||||
const Parser = require('..');
|
||||
|
||||
class MusicDetailHeader {
|
||||
type = 'MusicDetailHeader';
|
||||
|
||||
constructor(data) {
|
||||
this.title = new Text(data.title);
|
||||
this.description = new Text(data.description);
|
||||
this.subtitle = new Text(data.subtitle);
|
||||
this.second_subtitle = new Text(data.secondSubtitle);
|
||||
this.year = this.subtitle.runs.find((run) => /^[12][0-9]{3}$/.test(run.text)).text;
|
||||
this.song_count = this.second_subtitle.runs[0].text;
|
||||
this.total_duration = this.second_subtitle.runs[2].text;
|
||||
this.thumbnails = Thumbnail.fromResponse(data.thumbnail.croppedSquareThumbnailRenderer.thumbnail);
|
||||
this.badges = Parser.parse(data.subtitleBadges);
|
||||
|
||||
const author = this.subtitle.runs.find((run) => run.endpoint.browse?.id.startsWith('UC'));
|
||||
|
||||
author && (this.author = {
|
||||
name: author.text,
|
||||
channel_id: author.endpoint.browse.id,
|
||||
endpoint: author.endpoint
|
||||
});
|
||||
|
||||
this.menu = Parser.parse(data.menu);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = MusicDetailHeader;
|
||||
@@ -16,7 +16,7 @@ class MusicImmersiveHeader {
|
||||
this.menu = Parser.parse(data.menu);
|
||||
this.play_button = Parser.parse(data.playButton);
|
||||
this.start_radio_button = Parser.parse(data.startRadioButton);
|
||||
*/
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,19 +1,26 @@
|
||||
'use strict';
|
||||
|
||||
const Parser = require('..');
|
||||
const Text = require('./Text');
|
||||
const Utils = require('../../../utils/Utils');
|
||||
const Thumbnail = require('./Thumbnail');
|
||||
const NavigationEndpoint = require('./NavigationEndpoint');
|
||||
|
||||
class MusicResponsiveListItem {
|
||||
#flex_columns;
|
||||
#fixed_columns;
|
||||
#playlist_item_data;
|
||||
|
||||
constructor(data) {
|
||||
this.type = null;
|
||||
|
||||
this.#flex_columns = Parser.parse(data.flexColumns);
|
||||
this.#playlist_item_data = { video_id: data?.playlistItemData?.videoId || null };
|
||||
this.#fixed_columns = Parser.parse(data.fixedColumns);
|
||||
|
||||
this.#playlist_item_data = {
|
||||
video_id: data?.playlistItemData?.videoId || null,
|
||||
playlist_set_video_id: data?.playlistItemData?.playlistSetVideoId || null
|
||||
};
|
||||
|
||||
this.endpoint = data.navigationEndpoint &&
|
||||
new NavigationEndpoint(data.navigationEndpoint) || null;
|
||||
@@ -36,7 +43,10 @@ class MusicResponsiveListItem {
|
||||
break;
|
||||
}
|
||||
|
||||
this.thumbnails = Thumbnail.fromResponse(data.thumbnail.musicThumbnailRenderer.thumbnail);
|
||||
data.index &&
|
||||
(this.index = new Text(data.index));
|
||||
|
||||
this.thumbnails = data.thumbnail && Thumbnail.fromResponse(data.thumbnail.musicThumbnailRenderer.thumbnail) || [];
|
||||
this.badges = Parser.parse(data.badges) || [];
|
||||
|
||||
this.menu = Parser.parse(data.menu);
|
||||
@@ -45,7 +55,7 @@ class MusicResponsiveListItem {
|
||||
|
||||
#parseVideoOrSong() {
|
||||
const is_video = this.#flex_columns[1].title.runs
|
||||
.some((run) => run.text.match(/(.*?) views/));
|
||||
?.some((run) => run.text.match(/(.*?) views/));
|
||||
|
||||
if (is_video) {
|
||||
this.type = 'video';
|
||||
@@ -60,15 +70,17 @@ class MusicResponsiveListItem {
|
||||
this.id = this.#playlist_item_data.video_id;
|
||||
this.title = this.#flex_columns[0].title.toString();
|
||||
|
||||
const duration_text = this.#flex_columns[1].title.runs
|
||||
.find((run) => /^\d+$/.test(run.text.replace(/:/g, '')))?.text;
|
||||
const duration_text =
|
||||
this.#flex_columns[1].title.runs?.find(
|
||||
(run) => /^\d+$/.test(run.text.replace(/:/g, '')))?.text ||
|
||||
this.#fixed_columns[0].title.text;
|
||||
|
||||
duration_text && (this.duration = {
|
||||
text: duration_text,
|
||||
seconds: Utils.timeToSeconds(duration_text)
|
||||
});
|
||||
|
||||
const album = this.#flex_columns[1].title.runs.find((run) => run.endpoint.browse?.id.startsWith('MPR'));
|
||||
const album = this.#flex_columns[1].title.runs?.find((run) => run.endpoint.browse?.id.startsWith('MPR'));
|
||||
|
||||
album && (this.album = {
|
||||
id: album.endpoint.browse.id,
|
||||
@@ -76,7 +88,7 @@ class MusicResponsiveListItem {
|
||||
endpoint: album.endpoint
|
||||
});
|
||||
|
||||
const artists = this.#flex_columns[1].title.runs.filter((run) => run.endpoint.browse?.id.startsWith('UC'));
|
||||
const artists = this.#flex_columns[1].title.runs?.filter((run) => run.endpoint.browse?.id.startsWith('UC'));
|
||||
|
||||
artists && (this.artists = artists.map((artist) => ({
|
||||
name: artist.text,
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
'use strict';
|
||||
|
||||
const Text = require('./Text');
|
||||
|
||||
class MusicResponsiveListItemFixedColumn {
|
||||
type = 'musicResponsiveListItemFlexColumnRenderer';
|
||||
|
||||
constructor(data) {
|
||||
this.title = new Text(data.text);
|
||||
this.display_priority = data.displayPriority;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = MusicResponsiveListItemFixedColumn;
|
||||
33
lib/parser/ytmusic/Album.js
Normal file
33
lib/parser/ytmusic/Album.js
Normal file
@@ -0,0 +1,33 @@
|
||||
'use strict';
|
||||
|
||||
const Parser = require('../contents');
|
||||
|
||||
/** @namespace */
|
||||
class Album {
|
||||
#page;
|
||||
#actions;
|
||||
|
||||
/**
|
||||
* @param {object} response - API response.
|
||||
* @param {import('../../core/Actions')} actions
|
||||
*/
|
||||
constructor(response, actions) {
|
||||
this.#page = Parser.parseResponse(response.data);
|
||||
this.#actions = actions;
|
||||
|
||||
/** @type {import('../contents/classes/MusicDetailHeader')[]} */
|
||||
this.header = this.#page.header;
|
||||
|
||||
/** @type {string} */
|
||||
this.url = this.#page.microformat.url_canonical;
|
||||
|
||||
/** @type {import('../contents/classes/MusicResponsiveListItem')[]} */
|
||||
this.contents = this.#page.contents_memo.get('MusicShelf')?.[0].contents;
|
||||
}
|
||||
|
||||
get page() {
|
||||
return this.#page;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Album;
|
||||
Reference in New Issue
Block a user