mirror of
https://github.com/LuanRT/YouTube.js.git
synced 2026-06-26 16:18:51 +00:00
fix: search continuations should return a Search class
Why? To keep things consistent.
This commit is contained in:
105
lib/Innertube.js
105
lib/Innertube.js
@@ -1,8 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
const Parser = require('./parser');
|
||||
const EventEmitter = require('events');
|
||||
|
||||
const OAuth = require('./core/OAuth');
|
||||
const Actions = require('./core/Actions');
|
||||
const Livechat = require('./core/Livechat');
|
||||
@@ -10,19 +7,27 @@ const SessionBuilder = require('./core/SessionBuilder');
|
||||
const AccountManager = require('./core/AccountManager');
|
||||
const PlaylistManager = require('./core/PlaylistManager');
|
||||
const InteractionManager = require('./core/InteractionManager');
|
||||
const YTMusic = require('./core/Music');
|
||||
|
||||
const VideoInfo = require('./parser/youtube/VideoInfo');
|
||||
const Search = require('./parser/youtube/Search');
|
||||
|
||||
const Utils = require('./utils/Utils');
|
||||
const Request = require('./utils/Request');
|
||||
|
||||
const Proto = require('./proto');
|
||||
const Search = require('./parser/youtube/Search');
|
||||
const VideoInfo = require('./parser/youtube/VideoInfo');
|
||||
const Channel = require('./parser/youtube/Channel');
|
||||
const Playlist = require('./parser/youtube/Playlist');
|
||||
const Library = require('./parser/youtube/Library');
|
||||
const History = require('./parser/youtube/History');
|
||||
|
||||
const YTMusic = require('./core/Music');
|
||||
const FilterableFeed = require('./core/FilterableFeed');
|
||||
const TabbedFeed = require('./core/TabbedFeed');
|
||||
|
||||
const Parser = require('./parser/contents');
|
||||
const OldParser = require('./parser');
|
||||
|
||||
const Proto = require('./proto');
|
||||
|
||||
const EventEmitter = require('events');
|
||||
const { PassThrough } = require('stream');
|
||||
|
||||
class Innertube {
|
||||
@@ -159,7 +164,7 @@ class Innertube {
|
||||
async getBasicInfo(video_id) {
|
||||
Utils.throwIfMissing({ video_id });
|
||||
const cpn = Utils.generateRandomString(16);
|
||||
|
||||
|
||||
const response = await this.actions.getVideoInfo(video_id, cpn);
|
||||
|
||||
return new VideoInfo([ response, {} ], this.actions, this.#player, cpn);
|
||||
@@ -197,7 +202,7 @@ class Innertube {
|
||||
const response = await this.actions.getSearchSuggestions(options.client, query);
|
||||
if (options.client === 'YTMUSIC' && !response.data.contents) return [];
|
||||
|
||||
const suggestions = new Parser(this, response.data, {
|
||||
const suggestions = new OldParser(this, response.data, {
|
||||
client: options.client,
|
||||
data_type: 'SEARCH_SUGGESTIONS'
|
||||
}).parse();
|
||||
@@ -220,7 +225,7 @@ class Innertube {
|
||||
const continuation = await this.actions.next({ video_id });
|
||||
response.continuation = continuation.data;
|
||||
|
||||
const details = new Parser(this, response, {
|
||||
const details = new OldParser(this, response, {
|
||||
client: 'YOUTUBE',
|
||||
data_type: 'VIDEO_INFO'
|
||||
}).parse();
|
||||
@@ -257,7 +262,7 @@ class Innertube {
|
||||
});
|
||||
|
||||
const response = await this.actions.next({ ctoken: payload });
|
||||
const comments = new Parser(this, response.data, {
|
||||
const comments = new OldParser(this, response.data, {
|
||||
video_id,
|
||||
client: 'YOUTUBE',
|
||||
data_type: 'COMMENTS'
|
||||
@@ -266,36 +271,6 @@ class Innertube {
|
||||
return comments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves contents for a given channel. (WIP)
|
||||
*
|
||||
* @param {string} id - channel id
|
||||
* @returns {Promise<Channel>}
|
||||
*/
|
||||
async getChannel(id) {
|
||||
Utils.throwIfMissing({ id });
|
||||
|
||||
const response = await this.actions.browse(id);
|
||||
|
||||
return new Channel(response.data, this.actions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves watch history.
|
||||
*
|
||||
* @returns {Promise.<{ items: Array.<{ date: string, videos: object[] }>}>}
|
||||
*/
|
||||
async getHistory() {
|
||||
const response = await this.actions.browse('FEhistory');
|
||||
|
||||
const history = new Parser(this, response, {
|
||||
client: 'YOUTUBE',
|
||||
data_type: 'HISTORY'
|
||||
}).parse();
|
||||
|
||||
return history;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves YouTube's home feed (aka recommendations).
|
||||
*
|
||||
@@ -303,10 +278,29 @@ class Innertube {
|
||||
*/
|
||||
async getHomeFeed() {
|
||||
const response = await this.actions.browse('FEwhat_to_watch');
|
||||
if (!response.success) throw new Utils.InnertubeError('Could not retrieve home feed', response);
|
||||
|
||||
return new FilterableFeed(this.actions, response.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the account's library.
|
||||
*
|
||||
* @returns {Promise.<Library>}
|
||||
*/
|
||||
async getLibrary() {
|
||||
const response = await this.actions.browse('FElibrary');
|
||||
return new Library(response.data, this.actions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves watch history.
|
||||
* Which can also be achieved through {@link getLibrary()}.
|
||||
*
|
||||
* @returns {Promise.<History>}
|
||||
*/
|
||||
async getHistory() {
|
||||
const response = await this.actions.browse('FEhistory');
|
||||
return new History(Parser.parseResponse(response.data), this.actions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves trending content.
|
||||
@@ -315,8 +309,6 @@ class Innertube {
|
||||
*/
|
||||
async getTrending() {
|
||||
const response = await this.actions.browse('FEtrending');
|
||||
if (!response.success) throw new Utils.InnertubeError('Could not retrieve trending content', response);
|
||||
|
||||
return new TabbedFeed(this.actions, response.data);
|
||||
}
|
||||
|
||||
@@ -328,14 +320,26 @@ class Innertube {
|
||||
async getSubscriptionsFeed() {
|
||||
const response = await this.actions.browse('FEsubscriptions');
|
||||
|
||||
const subsfeed = new Parser(this, response, {
|
||||
const subsfeed = new OldParser(this, response, {
|
||||
client: 'YOUTUBE',
|
||||
data_type: 'SUBSFEED'
|
||||
}).parse();
|
||||
|
||||
return subsfeed;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves contents for a given channel. (WIP)
|
||||
*
|
||||
* @param {string} id - channel id
|
||||
* @returns {Promise<Channel>}
|
||||
*/
|
||||
async getChannel(id) {
|
||||
Utils.throwIfMissing({ id });
|
||||
const response = await this.actions.browse(id);
|
||||
return new Channel(response.data, this.actions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves notifications.
|
||||
*
|
||||
@@ -344,7 +348,7 @@ class Innertube {
|
||||
async getNotifications() {
|
||||
const response = await this.actions.notifications('get_notification_menu');
|
||||
|
||||
const notifications = new Parser(this, response.data, {
|
||||
const notifications = new OldParser(this, response.data, {
|
||||
client: 'YOUTUBE',
|
||||
data_type: 'NOTIFICATIONS'
|
||||
}).parse();
|
||||
@@ -374,10 +378,7 @@ class Innertube {
|
||||
*/
|
||||
async getPlaylist(playlist_id, options = { client: 'YOUTUBE' }) {
|
||||
Utils.throwIfMissing({ playlist_id });
|
||||
|
||||
const response = await this.actions.browse(`VL${playlist_id}`, { client: options.client });
|
||||
if (!response.success) throw new Utils.InnertubeError('Could not get playlist', response);
|
||||
|
||||
return new Playlist(this.actions, response.data);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
const Utils = require('../utils/Utils');
|
||||
const Constants = require('../utils/Constants');
|
||||
const Library = require('../parser/youtube/Library');
|
||||
const Analytics = require('../parser/youtube/Analytics');
|
||||
const Proto = require('../proto');
|
||||
|
||||
@@ -210,16 +209,6 @@ class AccountManager {
|
||||
|
||||
return new Analytics(response.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the account's library.
|
||||
*
|
||||
* @returns {Promise.<Library>}
|
||||
*/
|
||||
async getLibrary() {
|
||||
const response = await this.#actions.browse('FElibrary');
|
||||
return new Library(response.data, this.#actions);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AccountManager;
|
||||
292
lib/core/Feed.js
292
lib/core/Feed.js
@@ -1,153 +1,167 @@
|
||||
'use strict';
|
||||
|
||||
const ResultsParser = require('../parser/contents');
|
||||
const { InnertubeError } = require('../utils/Utils');
|
||||
|
||||
// TODO: add a way subdivide into sections and return subfeeds?
|
||||
|
||||
class Feed {
|
||||
#page;
|
||||
/**
|
||||
* @type {import('../parser/contents/classes/ContinuationItem')[]}
|
||||
*/
|
||||
#continuation;
|
||||
/**
|
||||
* @type {import('../core/Actions')}
|
||||
*/
|
||||
#actions;
|
||||
|
||||
memo;
|
||||
constructor(actions, data, already_parsed = false) {
|
||||
if (data.on_response_received_actions || data.on_response_received_endpoints || already_parsed)
|
||||
this.#page = data;
|
||||
else
|
||||
this.#page = ResultsParser.parseResponse(data);
|
||||
this.memo =
|
||||
this.#page.on_response_received_actions ?
|
||||
this.#page.on_response_received_actions_memo :
|
||||
this.#page.on_response_received_endpoints ?
|
||||
this.#page.on_response_received_endpoints_memo :
|
||||
this.#page.contents_memo;
|
||||
|
||||
this.#actions = actions;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the original page data
|
||||
*/
|
||||
get page() {
|
||||
return this.#page;
|
||||
}
|
||||
|
||||
get actions() {
|
||||
return this.#actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all videos on a given page via memo
|
||||
*
|
||||
* @param {Map<string, any[]>} memo
|
||||
* @returns {Array<import('../parser/contents/classes/Video') | import('../parser/contents/classes/GridVideo') | import('../parser/contents/classes/CompactVideo') | import('../parser/contents/classes/PlaylistVideo') | import('../parser/contents/classes/PlaylistPanelVideo') | import('../parser/contents/classes/WatchCardCompactVideo')>}
|
||||
*/
|
||||
static getVideosFromMemo(memo) {
|
||||
const videos = memo.get('Video') || [];
|
||||
const grid_videos = memo.get('GridVideo') || [];
|
||||
const compact_videos = memo.get('CompactVideo') || [];
|
||||
const playlist_videos = memo.get('PlaylistVideo') || [];
|
||||
const playlist_panel_videos = memo.get('PlaylistPanelVideo') || [];
|
||||
const watch_card_compact_videos = memo.get('WatchCardCompactVideo') || [];
|
||||
return [...videos, ...grid_videos, ...compact_videos, ...playlist_videos, ...playlist_panel_videos, ...watch_card_compact_videos];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all playlists on a given page via memo
|
||||
*
|
||||
* @param {Map<string, any[]>} memo
|
||||
* @returns {Array<import('../parser/contents/classes/Playlist') | import('../parser/contents/classes/GridPlaylist')>}
|
||||
*/
|
||||
static getPlaylistsFromMemo(memo) {
|
||||
const playlists = memo.get('Playlist') || [];
|
||||
const grid_playlists = memo.get('GridPlaylist') || [];
|
||||
return [...playlists, ...grid_playlists];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the videos in the feed
|
||||
*/
|
||||
get videos() {
|
||||
return Feed.getVideosFromMemo(this.memo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all playlists in the feed
|
||||
*
|
||||
* @returns {Array<import('../parser/contents/classes/Playlist') | import('../parser/contents/classes/GridPlaylist')>}
|
||||
*/
|
||||
get playlists() {
|
||||
return Feed.getPlaylistsFromPage(this.memo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the community posts in the feed
|
||||
*
|
||||
* @returns {import('../parser/contents/classes/BackstagePost')[]}
|
||||
*/
|
||||
get backstage_posts() {
|
||||
return this.memo.get('BackstagePost');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the channels in the feed
|
||||
*
|
||||
* @returns {Array<import('../parser/contents/Channel') | import('../parser/contents/GridChannel')>}
|
||||
*/
|
||||
get channels() {
|
||||
const channels = this.memo.get('Channel') || [];
|
||||
const grid_channels = this.memo.get('GridChannel') || [];
|
||||
return [...channels, ...grid_channels];
|
||||
}
|
||||
|
||||
get has_continuation() {
|
||||
return (this.memo.get('ContinuationItem') || []).length > 0;
|
||||
}
|
||||
|
||||
async getContinuationData() {
|
||||
if (this.#continuation) {
|
||||
if (this.#continuation.length > 1)
|
||||
throw new InnertubeError('There are too many continuations, you\'ll need to find the correct one yourself in this.page');
|
||||
if (this.#continuation.length === 0)
|
||||
throw new InnertubeError('There are no continuations');
|
||||
const continuation = this.#continuation[0];
|
||||
return await continuation.endpoint.call(this.#actions);
|
||||
}
|
||||
|
||||
this.#continuation = this.memo.get('ContinuationItem');
|
||||
|
||||
if (this.#continuation)
|
||||
return this.getContinuationData();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
get shelves() {
|
||||
return this.#page.contents_memo.get('Shelf');
|
||||
}
|
||||
#page;
|
||||
|
||||
getShelf(title) {
|
||||
return this.shelves.find(shelf => shelf.title.toString() === title);
|
||||
}
|
||||
/** @type {import('../parser/contents/classes/ContinuationItem')[]} */
|
||||
#continuation;
|
||||
|
||||
get shelf_content() {
|
||||
return this.shelves.map(shelf => ({
|
||||
title: shelf.title.toString(),
|
||||
content: shelf.content.contents,
|
||||
}));
|
||||
/** @type {import('../core/Actions')} */
|
||||
#actions;
|
||||
|
||||
memo;
|
||||
|
||||
constructor(actions, data, already_parsed = false) {
|
||||
if (data.on_response_received_actions || data.on_response_received_endpoints || already_parsed) {
|
||||
this.#page = data;
|
||||
} else {
|
||||
this.#page = ResultsParser.parseResponse(data);
|
||||
}
|
||||
|
||||
async getContinuation() {
|
||||
const continuation_data = await this.getContinuationData();
|
||||
this.memo =
|
||||
this.#page.on_response_received_commands ?
|
||||
this.#page.on_response_received_commands_memo:
|
||||
this.#page.on_response_received_actions ?
|
||||
this.#page.on_response_received_actions_memo:
|
||||
this.#page.on_response_received_endpoints ?
|
||||
this.#page.on_response_received_endpoints_memo:
|
||||
this.#page.contents_memo;
|
||||
|
||||
return new Feed(this.actions, continuation_data, true);
|
||||
this.#actions = actions;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the original page data
|
||||
*/
|
||||
get page() {
|
||||
return this.#page;
|
||||
}
|
||||
|
||||
get actions() {
|
||||
return this.#actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all videos on a given page via memo
|
||||
*
|
||||
* @param {Map<string, any[]>} memo
|
||||
* @returns {Array<import('../parser/contents/classes/Video') | import('../parser/contents/classes/GridVideo') | import('../parser/contents/classes/CompactVideo') | import('../parser/contents/classes/PlaylistVideo') | import('../parser/contents/classes/PlaylistPanelVideo') | import('../parser/contents/classes/WatchCardCompactVideo')>}
|
||||
*/
|
||||
static getVideosFromMemo(memo) {
|
||||
const videos = memo.get('Video') || [];
|
||||
const grid_videos = memo.get('GridVideo') || [];
|
||||
const compact_videos = memo.get('CompactVideo') || [];
|
||||
const playlist_videos = memo.get('PlaylistVideo') || [];
|
||||
const playlist_panel_videos = memo.get('PlaylistPanelVideo') || [];
|
||||
const watch_card_compact_videos = memo.get('WatchCardCompactVideo') || [];
|
||||
|
||||
return [
|
||||
...videos,
|
||||
...grid_videos,
|
||||
...compact_videos,
|
||||
...playlist_videos,
|
||||
...playlist_panel_videos,
|
||||
...watch_card_compact_videos
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all playlists on a given page via memo
|
||||
*
|
||||
* @param {Map<string, any[]>} memo
|
||||
* @returns {Array<import('../parser/contents/classes/Playlist') | import('../parser/contents/classes/GridPlaylist')>}
|
||||
*/
|
||||
static getPlaylistsFromMemo(memo) {
|
||||
const playlists = memo.get('Playlist') || [];
|
||||
const grid_playlists = memo.get('GridPlaylist') || [];
|
||||
return [...playlists, ...grid_playlists];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the videos in the feed
|
||||
*/
|
||||
get videos() {
|
||||
return Feed.getVideosFromMemo(this.memo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all playlists in the feed
|
||||
*
|
||||
* @returns {Array<import('../parser/contents/classes/Playlist') | import('../parser/contents/classes/GridPlaylist')>}
|
||||
*/
|
||||
get playlists() {
|
||||
return Feed.getPlaylistsFromPage(this.memo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the community posts in the feed
|
||||
*
|
||||
* @returns {import('../parser/contents/classes/BackstagePost')[]}
|
||||
*/
|
||||
get backstage_posts() {
|
||||
return this.memo.get('BackstagePost');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the channels in the feed
|
||||
*
|
||||
* @returns {Array<import('../parser/contents/Channel') | import('../parser/contents/GridChannel')>}
|
||||
*/
|
||||
get channels() {
|
||||
const channels = this.memo.get('Channel') || [];
|
||||
const grid_channels = this.memo.get('GridChannel') || [];
|
||||
return [...channels, ...grid_channels];
|
||||
}
|
||||
|
||||
get has_continuation() {
|
||||
return (this.memo.get('ContinuationItem') || []).length > 0;
|
||||
}
|
||||
|
||||
async getContinuationData() {
|
||||
if (this.#continuation) {
|
||||
if (this.#continuation.length > 1)
|
||||
throw new InnertubeError('There are too many continuations, you\'ll need to find the correct one yourself in this.page');
|
||||
if (this.#continuation.length === 0)
|
||||
throw new InnertubeError('There are no continuations');
|
||||
|
||||
const response = await this.#continuation[0].endpoint.call(this.#actions);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
this.#continuation = this.memo.get('ContinuationItem');
|
||||
|
||||
if (this.#continuation)
|
||||
return this.getContinuationData();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
get shelves() {
|
||||
return this.#page.contents_memo.get('Shelf');
|
||||
}
|
||||
|
||||
getShelf(title) {
|
||||
return this.shelves.find(shelf => shelf.title.toString() === title);
|
||||
}
|
||||
|
||||
get shelf_content() {
|
||||
return this.shelves.map(shelf => ({
|
||||
title: shelf.title.toString(),
|
||||
content: shelf.content.contents,
|
||||
}));
|
||||
}
|
||||
|
||||
async getContinuation() {
|
||||
const continuation_data = await this.getContinuationData();
|
||||
return new Feed(this.actions, continuation_data, true);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Feed;
|
||||
module.exports = Feed;
|
||||
@@ -113,7 +113,7 @@ class Music {
|
||||
/** @type {boolean} */
|
||||
is_editable: upnext_content.is_editable,
|
||||
/** @type {import('../parser/contents/classes/PlaylistPanelVideo')[]} */
|
||||
items: observe(upnext_content.contents)
|
||||
contents: observe(upnext_content.contents)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,11 +135,8 @@ class Music {
|
||||
const info = page.contents.contents.get({ type: 'MusicDescriptionShelf' });
|
||||
|
||||
return {
|
||||
/** @type {Array.<{ header: import('../parser/contents/classes/MusicCarouselShelfBasicHeader'), items: object[] }>} */
|
||||
sections: observe(shelves.map((shelf) => ({
|
||||
header: shelf.header,
|
||||
items: shelf.contents
|
||||
}))),
|
||||
/** @type {import('../parser/contents/classes/MusicCarouselShelf')[]} */
|
||||
sections: shelves,
|
||||
/** @type {string} */
|
||||
info: info?.description.toString() || ''
|
||||
}
|
||||
|
||||
@@ -8,9 +8,16 @@ class Button {
|
||||
|
||||
constructor(data) {
|
||||
this.text = new Text(data.text).toString();
|
||||
this.label = data.accessibility?.label || null;
|
||||
this.tooltip = data.tooltip || null;
|
||||
this.icon_type = data.icon?.iconType || null;
|
||||
|
||||
data.accessibility?.label &&
|
||||
(this.label = data.accessibility?.label);
|
||||
|
||||
data.tooltip &&
|
||||
(this.tooltip = data.tooltip);
|
||||
|
||||
data.icon?.iconType &&
|
||||
(this.icon_type = data.icon?.iconType);
|
||||
|
||||
this.endpoint = new NavigationEndpoint(data.navigationEndpoint || data.serviceEndpoint);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +61,12 @@ class Parser {
|
||||
const on_response_received_endpoints = data.onResponseReceivedEndpoints && Parser.parseRR(data.onResponseReceivedEndpoints) || null;
|
||||
const on_response_received_endpoints_memo = Parser.#memo;
|
||||
this.#clearMemo();
|
||||
|
||||
|
||||
this.#createMemo();
|
||||
const on_response_received_commands = data.onResponseReceivedCommands && Parser.parseRR(data.onResponseReceivedCommands) || null;
|
||||
const on_response_received_commands_memo = Parser.#memo;
|
||||
this.#clearMemo();
|
||||
|
||||
return {
|
||||
contents,
|
||||
contents_memo,
|
||||
@@ -69,7 +74,8 @@ class Parser {
|
||||
on_response_received_actions_memo,
|
||||
on_response_received_endpoints,
|
||||
on_response_received_endpoints_memo,
|
||||
on_response_received_commands: data.onResponseReceivedCommands && Parser.parseRR(data.onResponseReceivedCommands) || null,
|
||||
on_response_received_commands,
|
||||
on_response_received_commands_memo,
|
||||
/** @type {*} */
|
||||
continuation_contents: data.continuationContents && Parser.parseLC(data.continuationContents) || null,
|
||||
metadata: Parser.parse(data.metadata),
|
||||
|
||||
@@ -29,8 +29,8 @@ class History {
|
||||
this.feed_actions = secondary_contents?.contents || null;
|
||||
|
||||
this.sections = observe(contents.map((section) => ({
|
||||
title: section.header.title,
|
||||
items: section.contents
|
||||
header: section.header,
|
||||
contents: section.contents
|
||||
})));
|
||||
}
|
||||
|
||||
|
||||
@@ -26,9 +26,9 @@ class Library {
|
||||
this.profile = { stats, user_info };
|
||||
|
||||
this.sections = observe(shelves.map((shelf) => ({
|
||||
title: shelf.title.toString(),
|
||||
items: shelf.content.items,
|
||||
type: shelf.icon_type,
|
||||
title: shelf.title.toString(),
|
||||
contents: shelf.content.items,
|
||||
getAll: () => this.#getAll(shelf)
|
||||
})));
|
||||
}
|
||||
|
||||
@@ -41,11 +41,11 @@ class Search extends Feed {
|
||||
header: card_list?.header || null,
|
||||
/** @type {import('../contents/classes/SearchRefinementCard')} */
|
||||
cards: card_list?.cards || []
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies given refinement card and returns a new {@link Feed} object.
|
||||
* Applies given refinement card and returns a new {@link Search} object.
|
||||
*
|
||||
* @param {import('../contents/classes/SearchRefinementCard') | string} card - refinement card object or query
|
||||
* @returns {Promise.<Feed>}
|
||||
@@ -66,13 +66,23 @@ class Search extends Feed {
|
||||
}
|
||||
|
||||
const page = await target_card.endpoint.call(this.actions);
|
||||
return new Feed(this.actions, page, true);
|
||||
return new Search(this.actions, page, true);
|
||||
}
|
||||
|
||||
/** @type {string[]} */
|
||||
get refinement_card_queries() {
|
||||
return this.refinement_cards.cards.map((card) => card.query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves next batch of results.
|
||||
*
|
||||
* @returns {Promise.<Search>}
|
||||
*/
|
||||
async getContinuation() {
|
||||
const continuation = await this.getContinuationData();
|
||||
return new Search(this.actions, continuation, true);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Search;
|
||||
Reference in New Issue
Block a user