diff --git a/README.md b/README.md index 66c2b347d..97537d972 100644 --- a/README.md +++ b/README.md @@ -479,6 +479,7 @@ Retrieves contents for a given channel. - `#getCommunity()` - `#getChannels()` - `#getAbout()` +- `#search(query)` - `#getContinuation()` - `#filters` - `#page` diff --git a/examples/channel/basic-info.ts b/examples/channel/basic-info.ts index 90f7cbd34..8f6ae7605 100644 --- a/examples/channel/basic-info.ts +++ b/examples/channel/basic-info.ts @@ -14,28 +14,34 @@ import { Innertube, UniversalCache, YTNodes } from 'youtubei.js'; console.info('Country:', about.country.toString()); - console.info('\nLists the following videos:'); + console.info('\nVideos:'); const videos = await channel.getVideos(); for (const video of videos.videos) { console.info('Video:', video.title.toString()); } - console.info('\nLists the following playlists:'); + console.info('\nPopular videos:'); + const popular_videos = await videos.applyFilter('Popular'); + for (const video of popular_videos.videos) { + console.info('Video:', video.title.toString()); + } + + console.info('\nPlaylists:'); const playlists = await channel.getPlaylists(); for (const playlist of playlists.playlists) { console.info('Playlist:', playlist.title.toString()); } - console.info('\nLists the following channels:'); + console.info('\nChannels:'); const channels = await channel.getChannels(); for (const channel of channels.channels) { console.info('Channel:', channel.author.name); } - console.info('\nLists the following community posts:'); + console.info('\nCommunity posts:'); const posts = await channel.getCommunity(); for (const post of posts.posts) { diff --git a/src/parser/classes/ExpandableTab.ts b/src/parser/classes/ExpandableTab.ts index cb963e513..12dc17836 100644 --- a/src/parser/classes/ExpandableTab.ts +++ b/src/parser/classes/ExpandableTab.ts @@ -15,7 +15,7 @@ class ExpandableTab extends YTNode { this.title = data.title; this.endpoint = new NavigationEndpoint(data.endpoint); this.selected = data.selected; // If this.selected then we may have content else we do not - this.content = data.content ? Parser.parse(data.content) : null; + this.content = data.content ? Parser.parseItem(data.content) : null; } } diff --git a/src/parser/youtube/Channel.ts b/src/parser/youtube/Channel.ts index 858b412eb..4ea4d6692 100644 --- a/src/parser/youtube/Channel.ts +++ b/src/parser/youtube/Channel.ts @@ -15,6 +15,7 @@ import FilterableFeed from '../../core/FilterableFeed'; import Feed from '../../core/Feed'; import { InnertubeError } from '../../utils/Utils'; +import ExpandableTab from '../classes/ExpandableTab'; export default class Channel extends TabbedFeed { header; @@ -37,9 +38,7 @@ export default class Channel extends TabbedFeed { this.subscribe_button = this.page.header_memo.getType(SubscribeButton)?.[0]; - const tab = this.page.contents.item().key('tabs').parsed().array().filterType(Tab).get({ selected: true }); - - this.current_tab = tab; + this.current_tab = this.page.contents.item().key('tabs').parsed().array().filterType(Tab, ExpandableTab).get({ selected: true }); } /** @@ -114,6 +113,20 @@ export default class Channel extends TabbedFeed { return tab.memo.getType(ChannelAboutFullMetadata)?.[0]; } + /** + * Searches within the channel. + */ + async search(query: string) { + const tab = this.memo.getType(ExpandableTab)?.[0]; + + if (!tab) + throw new InnertubeError('Search tab not found', this); + + const page = await tab.endpoint?.call(this.actions, { query, parse: true }); + + return new Channel(this.actions, page, true); + } + /** * Retrives list continuation. */ diff --git a/test/main.test.ts b/test/main.test.ts index f7667f38d..3b16a7c97 100644 --- a/test/main.test.ts +++ b/test/main.test.ts @@ -114,6 +114,9 @@ describe('YouTube.js Tests', () => { const filtered_list = await videos_tab.applyFilter('Popular'); expect(filtered_list.videos.length).toBeGreaterThan(0); + + const search = await channel.search('e-ink'); + expect(search.videos.length).toBeGreaterThan(0); }); it('should retrieve home feed', async () => {