feat(Channel): add support for sorting the playlist tab (#295)

This commit is contained in:
LuanRT
2023-01-27 06:37:35 -03:00
committed by GitHub
parent d6c5a9b971
commit 50ef71284d
6 changed files with 103 additions and 3 deletions

View File

@@ -482,8 +482,13 @@ Retrieves contents for a given channel.
- `<channel>#getChannels()`
- `<channel>#getAbout()`
- `<channel>#search(query)`
- `<channel>#applyFilter(filter)`
- `<channel>#applyContentTypeFilter(content_type_filter)`
- `<channel>#applySort(sort)`
- `<channel>#getContinuation()`
- `<channel>#filters`
- `<channel>#content_type_filters`
- `<channel>#sort_filters`
- `<channel>#page`
</p>

View File

@@ -0,0 +1,27 @@
import Parser from '..';
import NavigationEndpoint from './NavigationEndpoint';
import { YTNode } from '../helpers';
class ChannelSubMenu extends YTNode {
static type = 'ChannelSubMenu';
content_type_sub_menu_items: {
endpoint: NavigationEndpoint;
selected: boolean;
title: string;
}[];
sort_setting;
constructor(data: any) {
super();
this.content_type_sub_menu_items = data.contentTypeSubMenuItems.map((item: any) => ({
endpoint: new NavigationEndpoint(item.navigationEndpoint || item.endpoint),
selected: item.selected,
title: item.title
}));
this.sort_setting = Parser.parseItem(data.sortSetting);
}
}
export default ChannelSubMenu;

View File

@@ -8,6 +8,7 @@ class SectionList extends YTNode {
contents;
continuation?: string;
header;
sub_menu;
constructor(data: any) {
super();
@@ -29,6 +30,10 @@ class SectionList extends YTNode {
if (data.header) {
this.header = Parser.parse(data.header);
}
if (data.subMenu) {
this.sub_menu = Parser.parseItem(data.subMenu);
}
}
}

View File

@@ -41,7 +41,7 @@ class SortFilterSubMenu extends YTNode {
title: item.title,
selected: item.selected,
continuation: item.continuation?.reloadContinuationData?.continuation,
endpoint: new NavigationEndpoint(item.serviceEndpoint),
endpoint: new NavigationEndpoint(item.serviceEndpoint || item.navigationEndpoint),
subtitle: item.subtitle || null
}));
}

View File

@@ -37,6 +37,7 @@ import { default as ChannelHeaderLinks } from './classes/ChannelHeaderLinks';
import { default as ChannelMetadata } from './classes/ChannelMetadata';
import { default as ChannelMobileHeader } from './classes/ChannelMobileHeader';
import { default as ChannelOptions } from './classes/ChannelOptions';
import { default as ChannelSubMenu } from './classes/ChannelSubMenu';
import { default as ChannelThumbnailWithLink } from './classes/ChannelThumbnailWithLink';
import { default as ChannelVideoPlayer } from './classes/ChannelVideoPlayer';
import { default as Chapter } from './classes/Chapter';
@@ -354,6 +355,7 @@ export const YTNodes = {
ChannelMetadata,
ChannelMobileHeader,
ChannelOptions,
ChannelSubMenu,
ChannelThumbnailWithLink,
ChannelVideoPlayer,
Chapter,

View File

@@ -7,13 +7,17 @@ import ChannelMetadata from '../classes/ChannelMetadata';
import InteractiveTabbedHeader from '../classes/InteractiveTabbedHeader';
import MicroformatData from '../classes/MicroformatData';
import SubscribeButton from '../classes/SubscribeButton';
import ExpandableTab from '../classes/ExpandableTab';
import SectionList from '../classes/SectionList';
import Tab from '../classes/Tab';
import Feed from '../../core/Feed';
import FilterableFeed from '../../core/FilterableFeed';
import ChipCloudChip from '../classes/ChipCloudChip';
import ExpandableTab from '../classes/ExpandableTab';
import FeedFilterChipBar from '../classes/FeedFilterChipBar';
import ChannelSubMenu from '../classes/ChannelSubMenu';
import SortFilterSubMenu from '../classes/SortFilterSubMenu';
import { InnertubeError } from '../../utils/Utils';
import type { AppendContinuationItemsAction, ReloadContinuationItemsCommand } from '..';
@@ -49,7 +53,7 @@ export default class Channel extends TabbedFeed {
async applyFilter(filter: string | ChipCloudChip): Promise<FilteredChannelList> {
let target_filter: ChipCloudChip | undefined;
const filter_chipbar = this.memo.getType(FeedFilterChipBar)?.[0];
const filter_chipbar = this.memo.getType(FeedFilterChipBar).first();
if (typeof filter === 'string') {
target_filter = filter_chipbar?.contents.get({ text: filter });
@@ -63,13 +67,70 @@ export default class Channel extends TabbedFeed {
throw new InnertubeError('Invalid filter', filter);
const page = await target_filter.endpoint?.call(this.actions, { parse: true });
return new FilteredChannelList(this.actions, page, true);
}
/**
* Applies given sort filter to the list. Use {@link sort_filters} to get available filters.
* @param sort - The sort filter to apply
*/
async applySort(sort: string): Promise<Channel> {
const sort_filter_sub_menu = this.memo.getType(SortFilterSubMenu).first();
if (!sort_filter_sub_menu)
throw new InnertubeError('No sort filter sub menu found');
const target_sort = sort_filter_sub_menu?.sub_menu_items?.find((item) => item.title === sort);
if (!target_sort)
throw new InnertubeError(`Sort filter ${sort} not found`, { available_sort_filters: this.sort_filters });
if (target_sort.selected)
return this;
const page = await target_sort.endpoint?.call(this.actions, { parse: true });
return new Channel(this.actions, page, true);
}
/**
* Applies given content type filter to the list. Use {@link content_type_filters} to get available filters.
* @param content_type_filter - The content type filter to apply
*/
async applyContentTypeFilter(content_type_filter: string): Promise<Channel> {
const sub_menu = this.current_tab?.content?.as(SectionList).sub_menu?.as(ChannelSubMenu);
if (!sub_menu)
throw new InnertubeError('Sub menu not found');
const item = sub_menu.content_type_sub_menu_items.find((item) => item.title === content_type_filter);
if (!item)
throw new InnertubeError(`Sub menu item ${content_type_filter} not found`, { available_filters: this.content_type_filters });
if (item.selected)
return this;
const page = await item.endpoint?.call(this.actions, { parse: true });
return new Channel(this.actions, page, true);
}
get filters(): string[] {
return this.memo.getType(FeedFilterChipBar)?.[0]?.contents.filterType(ChipCloudChip).map((chip) => chip.text) || [];
}
get sort_filters(): string[] {
const sort_filter_sub_menu = this.memo.getType(SortFilterSubMenu).first();
return sort_filter_sub_menu?.sub_menu_items?.map((item) => item.title) || [];
}
get content_type_filters(): string[] {
const sub_menu = this.current_tab?.content?.as(SectionList).sub_menu?.as(ChannelSubMenu);
return sub_menu?.content_type_sub_menu_items.map((item) => item.title) || [];
}
async getHome(): Promise<Channel> {
const tab = await this.getTabByURL('featured');
return new Channel(this.actions, tab.page, true);