From 0fc29f0bbf965215146a6ae192494c74e6cefcbb Mon Sep 17 00:00:00 2001
From: LuanRT
Date: Mon, 23 Jan 2023 05:38:53 -0300
Subject: [PATCH] feat(ytkids): add `getChannel()` (#292)
---
docs/API/kids.md | 28 +++++++++++++++++++++++++
src/core/Feed.ts | 1 +
src/core/Kids.ts | 11 ++++++++++
src/parser/classes/ItemSection.ts | 7 ++++++-
src/parser/index.ts | 19 ++++++++++++++++-
src/parser/ytkids/Channel.ts | 34 +++++++++++++++++++++++++++++++
test/constants.ts | 4 ++++
test/main.test.ts | 5 +++++
8 files changed, 107 insertions(+), 2 deletions(-)
create mode 100644 src/parser/ytkids/Channel.ts
diff --git a/docs/API/kids.md b/docs/API/kids.md
index ec1ad7825..42913ea18 100644
--- a/docs/API/kids.md
+++ b/docs/API/kids.md
@@ -7,6 +7,7 @@ YouTube Kids is a modified version of the YouTube app, with a simplified interfa
* Kids
* [.search(query)](#search)
* [.getInfo(video_id)](#getinfo)
+ * [.getChannel(channel_id)](#getchannel)
* [.getHomeFeed()](#gethomefeed)
@@ -64,6 +65,33 @@ Retrieves video info.
+
+### getChannel(channel_id)
+
+Retrieves channel info.
+
+**Returns:** `Promise.`
+
+| Param | Type | Description |
+| --- | --- | --- |
+| channel_id | `string` | The channel id |
+
+
+Methods & Getters
+
+
+- `#getContinuation()`
+ - Retrieves next batch of videos.
+
+- `#has_continuation`
+ - Returns whether there are more videos to retrieve.
+
+- `#page`
+ - Returns the original InnerTube response(s), parsed and sanitized.
+
+
+
+
### getHomeFeed()
diff --git a/src/core/Feed.ts b/src/core/Feed.ts
index 7e56c315e..12d34757d 100644
--- a/src/core/Feed.ts
+++ b/src/core/Feed.ts
@@ -45,6 +45,7 @@ class Feed {
const memo = concatMemos(
this.#page.contents_memo,
+ this.#page.continuation_contents_memo,
this.#page.on_response_received_commands_memo,
this.#page.on_response_received_endpoints_memo,
this.#page.on_response_received_actions_memo,
diff --git a/src/core/Kids.ts b/src/core/Kids.ts
index f1c8f7d90..a5a2962a3 100644
--- a/src/core/Kids.ts
+++ b/src/core/Kids.ts
@@ -1,7 +1,9 @@
import Search from '../parser/ytkids/Search';
import HomeFeed from '../parser/ytkids/HomeFeed';
import VideoInfo from '../parser/ytkids/VideoInfo';
+import Channel from '../parser/ytkids/Channel';
import type Session from './Session';
+
import { generateRandomString } from '../utils/Utils';
class Kids {
@@ -45,6 +47,15 @@ class Kids {
return new VideoInfo(response, this.#session.actions, cpn);
}
+ /**
+ * Retrieves the contents of the given channel.
+ * @param channel_id - The channel id.
+ */
+ async getChannel(channel_id: string): Promise {
+ const response = await this.#session.actions.execute('/browse', { browseId: channel_id, client: 'YTKIDS' });
+ return new Channel(this.#session.actions, response.data);
+ }
+
/**
* Retrieves the home feed.
*/
diff --git a/src/parser/classes/ItemSection.ts b/src/parser/classes/ItemSection.ts
index d111057ce..1e5f3fde1 100644
--- a/src/parser/classes/ItemSection.ts
+++ b/src/parser/classes/ItemSection.ts
@@ -10,7 +10,8 @@ class ItemSection extends YTNode {
header: CommentsHeader | ItemSectionHeader | ItemSectionTabbedHeader | null;
contents;
- target_id;
+ target_id?: string;
+ continuation?: string;
constructor(data: any) {
super();
@@ -20,6 +21,10 @@ class ItemSection extends YTNode {
if (data.targetId || data.sectionIdentifier) {
this.target_id = data?.target_id || data?.sectionIdentifier;
}
+
+ if (data.continuations) {
+ this.continuation = data.continuations?.at(0)?.nextContinuationData?.continuation;
+ }
}
}
diff --git a/src/parser/index.ts b/src/parser/index.ts
index 87b010d23..fb57f95f6 100644
--- a/src/parser/index.ts
+++ b/src/parser/index.ts
@@ -269,6 +269,8 @@ export default class Parser {
}
static parseLC(data: any) {
+ if (data.itemSectionContinuation)
+ return new ItemSectionContinuation(data.itemSectionContinuation);
if (data.sectionListContinuation)
return new SectionListContinuation(data.sectionListContinuation);
if (data.liveChatContinuation)
@@ -387,7 +389,22 @@ export default class Parser {
export type ParsedResponse = ReturnType;
-// Continuation nodes
+// Continuation
+
+export class ItemSectionContinuation extends YTNode {
+ static readonly type = 'itemSectionContinuation';
+
+ contents: ObservedArray | null;
+ continuation?: string;
+
+ constructor(data: any) {
+ super();
+ this.contents = Parser.parseArray(data.contents);
+ if (data.continuations) {
+ this.continuation = data.continuations?.at(0)?.nextContinuationData?.continuation;
+ }
+ }
+}
export class AppendContinuationItemsAction extends YTNode {
static readonly type = 'appendContinuationItemsAction';
diff --git a/src/parser/ytkids/Channel.ts b/src/parser/ytkids/Channel.ts
new file mode 100644
index 000000000..4ece04685
--- /dev/null
+++ b/src/parser/ytkids/Channel.ts
@@ -0,0 +1,34 @@
+import Feed from '../../core/Feed';
+import Actions from '../../core/Actions';
+import C4TabbedHeader from '../classes/C4TabbedHeader';
+import ItemSection from '../classes/ItemSection';
+import { ItemSectionContinuation } from '..';
+
+class Channel extends Feed {
+ header?: C4TabbedHeader;
+ contents?: ItemSection | ItemSectionContinuation;
+
+ constructor(actions: Actions, data: any, already_parsed = false) {
+ super(actions, data, already_parsed);
+ this.header = this.page.header?.item().as(C4TabbedHeader);
+ this.contents = this.memo.getType(ItemSection).first() || this.page.continuation_contents?.as(ItemSectionContinuation);
+ }
+
+ /**
+ * Retrieves next batch of videos.
+ */
+ async getContinuation(): Promise {
+ const response = await this.actions.execute('/browse', {
+ continuation: this.contents?.continuation,
+ client: 'YTKIDS'
+ });
+
+ return new Channel(this.actions, response.data);
+ }
+
+ get has_continuation(): boolean {
+ return !!this.contents?.continuation;
+ }
+}
+
+export default Channel;
\ No newline at end of file
diff --git a/test/constants.ts b/test/constants.ts
index 4e43962a5..4e138b622 100644
--- a/test/constants.ts
+++ b/test/constants.ts
@@ -33,5 +33,9 @@ export const CHANNELS = [
{
ID: 'UCXuqSBlHAE6Xw-yeJA0Tunw',
NAME: 'Linus Tech Tips'
+ },
+ {
+ ID: 'UCpbpfcZfo-hoDAx2m1blFhg',
+ NAME: 'Learning Blocks'
}
];
\ No newline at end of file
diff --git a/test/main.test.ts b/test/main.test.ts
index 1e726eb43..bc1d41e58 100644
--- a/test/main.test.ts
+++ b/test/main.test.ts
@@ -238,6 +238,11 @@ describe('YouTube.js Tests', () => {
const info = await yt.kids.getInfo(VIDEOS[6].ID);
expect(info.basic_info?.id).toBe(VIDEOS[6].ID);
});
+
+ it('should retrieve a channel', async () => {
+ const channel = await yt.kids.getChannel(CHANNELS[1].ID);
+ expect(channel.videos.length).toBeGreaterThan(0);
+ });
});
});