Skip to content

Commit

Permalink
feat: add support for YouTube Kids (#291)
Browse files Browse the repository at this point in the history
* dev: add `WEB_KIDS` innertube client

* refactor: move DASH manifest stuff out of `VideoInfo`
This makes it easier to use these functions elsewhere.

* feat(ytkids): add `Kids#getInfo()` & `Kids#search()`

* feat: add `Innertube#kids.getHomeFeed()`

* docs: add YouTube Kids API ref

* docs: fix typo

* docs: fix yet another typo

* docs: update YouTube Music API ref
Unrelated but required to reflect changes made to the DASH manifest generation functions

* chore: lint

* chore: add tests

* feat: include `captions` in `VideoInfo`

* chore: fix tests
  • Loading branch information
LuanRT authored Jan 23, 2023
1 parent 13ad377 commit 2bbefef
Show file tree
Hide file tree
Showing 25 changed files with 1,114 additions and 384 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

<h1 align=center>YouTube.js</h1>

<p align=center>A full-featured wrapper around the InnerTube API, which is what YouTube itself uses</p>
<p align=center>A full-featured wrapper around the InnerTube API</p>

<div align="center">

Expand Down Expand Up @@ -84,7 +84,7 @@ ___

## Description

InnerTube is an API used by all YouTube clients. It was created to simplify the deployment of new features and experiments across the platform[^1]. This library handles all the low-level communication with InnerTube, providing a simple, and efficient way to interact with YouTube programmatically. It is designed to emulate an actual client as closely as possible, including how API responses are [parsed](https://github.com/LuanRT/YouTube.js/tree/main/src/parser#how-it-works).
InnerTube is an API used by all YouTube clients. It was created to simplify the deployment of new features and experiments across the platform[^1]. This library handles all the low-level communication with InnerTube, providing a simple, and efficient way to interact with YouTube programmatically. It is designed to emulate an actual client as closely as possible, including how API responses are parsed.

If you have any questions or need help, feel free to reach out to us on our [Discord server][discord] or open an issue [here](https://github.com/LuanRT/YouTube.js/issues).

Expand Down Expand Up @@ -229,6 +229,7 @@ const yt = await Innertube.create({
* [.playlist](https://github.com/LuanRT/YouTube.js/blob/main/docs/API/playlist.md)
* [.music](https://github.com/LuanRT/YouTube.js/blob/main/docs/API/music.md)
* [.studio](https://github.com/LuanRT/YouTube.js/blob/main/docs/API/studio.md)
* [.kids](https://github.com/LuanRT/YouTube.js/blob/main/docs/API/kids.md)

</p>
</details>
Expand Down
9 changes: 6 additions & 3 deletions docs/API/account.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ Retrieves account information.
<p>

- `<accountinfo>#page`
- Returns original InnerTube response (sanitized).
- Returns the original InnerTube response(s), parsed and sanitized.

</p>
</details>
Expand All @@ -63,7 +63,7 @@ Retrieves time watched statistics.
<p>

- `<timewatched>#page`
- Returns original InnerTube response (sanitized).
- Returns the original InnerTube response(s), parsed and sanitized.

</p>
</details>
Expand Down Expand Up @@ -91,6 +91,9 @@ Retrieves YouTube settings.
- `<settings>#sidebar_items`
- Returns options available in the sidebar menu.

- `<settings>#page`
- Returns the original InnerTube response(s), parsed and sanitized.

</p>
</details>

Expand All @@ -106,7 +109,7 @@ Retrieves basic channel analytics.
<p>

- `<analytics>#page`
- Returns original InnerTube response (sanitized).
- Returns the original InnerTube response(s), parsed and sanitized.

</p>
</details>
85 changes: 85 additions & 0 deletions docs/API/kids.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# YouTube Kids

YouTube Kids is a modified version of the YouTube app, with a simplified interface and curated content. This class allows you to interact with its API.

## API

* Kids
* [.search(query)](#search)
* [.getInfo(video_id)](#getinfo)
* [.getHomeFeed()](#gethomefeed)

<a name="search"></a>
### search(query)

Searches the given query on YouTube Kids.

**Returns:** `Promise.<Search>`

| Param | Type | Description |
| --- | --- | --- |
| query | `string` | The query to search |


<details>
<summary>Methods & Getters</summary>
<p>

- `<search>#page`
- Returns the original InnerTube response(s), parsed and sanitized.

</p>
</details>

<a name="getinfo"></a>
### getInfo(video_id)

Retrieves video info.

**Returns:** `Promise.<VideoInfo>`

| Param | Type | Description |
| --- | --- | --- |
| video_id | `string` | The video id |

<details>
<summary>Methods & Getters</summary>
<p>

- `<info>#toDash(url_transformer?)`
- Generates a DASH manifest from the streaming data.

- `<info>#chooseFormat(options)`
- Selects the format that best matches the given options. This method is used internally by `#download`.

- `<info>#download(options?)`
- Downloads the video.

- `<info>#addToWatchHistory()`
- Adds the video to the watch history.

- `<info>#page`
- Returns the original InnerTube response(s), parsed and sanitized.

</p>
</details>

<a name="gethomefeed"></a>
### getHomeFeed()

Retrieves the home feed.

**Returns:** `Promise.<HomeFeed>`

<details>
<summary>Methods & Getters</summary>
<p>

- `<feed>#selectCategoryTab(tab: string | KidsCategoryTab)`
- Selects the given category tab.

- `<feed>#categories`
- Returns available categories.

- `<feed>#page`
- Returns the original InnerTube response(s), parsed and sanitized.
36 changes: 27 additions & 9 deletions docs/API/music.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Music
# YouTube Music

YouTube Music class.
YouTube Music is a music streaming service developed by YouTube, a subsidiary of Google. It provides a tailored interface for the service oriented towards music streaming, with a greater emphasis on browsing and discovery compared to its main service. This class allows you to interact with its API.

## API

Expand Down Expand Up @@ -49,6 +49,21 @@ Retrieves track info.
- `<info>#available_tabs`
- Returns available tabs.

- `<info>#toDash(url_transformer?)`
- Generates a DASH manifest from the streaming data.

- `<info>#chooseFormat(options)`
- Selects the format that best matches the given options. This method is used internally by `#download`.

- `<info>#download(options?)`
- Downloads the track.

- `<info>#addToWatchHistory()`
- Adds the song to the watch history.

- `<info>#page`
- Returns the original InnerTube response(s), parsed and sanitized.

</p>
</details>

Expand Down Expand Up @@ -99,7 +114,7 @@ Searches on YouTube Music.
- Returns songs shelf.

- `<search>#page`
- Returns original InnerTube response (sanitized).
- Returns the original InnerTube response(s), parsed and sanitized.

</p>
</details>
Expand All @@ -124,6 +139,9 @@ Retrieves home feed.
- `<homefeed>#page`
- Returns original InnerTube response (sanitized).

- `<homefeed>#page`
- Returns the original InnerTube response(s), parsed and sanitized.

</p>
</details>

Expand All @@ -139,7 +157,7 @@ Retrieves “Explore” feed.
<p>

- `<explore>#page`
- Returns original InnerTube response (sanitized).
- Returns the original InnerTube response(s), parsed and sanitized.

</p>
</details>
Expand Down Expand Up @@ -174,7 +192,7 @@ Retrieves library.
- Returns available sort options.

- `<library>#page`
- Returns original InnerTube response (sanitized).
- Returns the original InnerTube response(s), parsed and sanitized.

</p>
</details>
Expand All @@ -195,7 +213,7 @@ Retrieves artist's info & content.
<p>

- `<artist>#page`
- Returns original InnerTube response (sanitized).
- Returns the original InnerTube response(s), parsed and sanitized.

</p>
</details>
Expand All @@ -216,7 +234,7 @@ Retrieves given album.
<p>

- `<album>#page`
- Returns original InnerTube response (sanitized).
- Returns the original InnerTube response(s), parsed and sanitized.

</p>
</details>
Expand Down Expand Up @@ -249,7 +267,7 @@ Retrieves given playlist.
- Checks if continuation is available.

- `<playlist>#page`
- Returns original InnerTube response (sanitized).
- Returns the original InnerTube response(s), parsed and sanitized.

</p>
</details>
Expand Down Expand Up @@ -303,7 +321,7 @@ Retrieves your YouTube Music recap.
- Retrieves recap playlist.

- `<recap>#page`
- Returns original InnerTube response (sanitized).
- Returns the original InnerTube response(s), parsed and sanitized.

</p>
</details>
Expand Down
14 changes: 9 additions & 5 deletions src/Innertube.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@ import Library from './parser/youtube/Library';
import NotificationsMenu from './parser/youtube/NotificationsMenu';
import Playlist from './parser/youtube/Playlist';
import Search from './parser/youtube/Search';
import VideoInfo, { DownloadOptions, FormatOptions } from './parser/youtube/VideoInfo';
import VideoInfo from './parser/youtube/VideoInfo';

import AccountManager from './core/AccountManager';
import Feed from './core/Feed';
import InteractionManager from './core/InteractionManager';
import YTMusic from './core/Music';
import PlaylistManager from './core/PlaylistManager';
import Studio from './core/Studio';
import YTStudio from './core/Studio';
import YTKids from './core/Kids';
import TabbedFeed from './core/TabbedFeed';
import HomeFeed from './parser/youtube/HomeFeed';
import Proto from './proto/index';
Expand All @@ -28,6 +29,7 @@ import type Actions from './core/Actions';
import type Format from './parser/classes/misc/Format';

import { generateRandomString, throwIfMissing } from './utils/Utils';
import type { FormatOptions, DownloadOptions } from './utils/FormatUtils';

export type InnertubeConfig = SessionOptions;

Expand All @@ -39,15 +41,16 @@ export interface SearchFilters {
features?: ('hd' | 'subtitles' | 'creative_commons' | '3d' | 'live' | 'purchased' | '4k' | '360' | 'location' | 'hdr' | 'vr180')[];
}

export type InnerTubeClient = 'WEB' | 'ANDROID' | 'YTMUSIC_ANDROID' | 'YTMUSIC' | 'YTSTUDIO_ANDROID' | 'TV_EMBEDDED';
export type InnerTubeClient = 'WEB' | 'ANDROID' | 'YTMUSIC_ANDROID' | 'YTMUSIC' | 'YTSTUDIO_ANDROID' | 'TV_EMBEDDED' | 'YTKIDS'

class Innertube {
session: Session;
account: AccountManager;
playlist: PlaylistManager;
interact: InteractionManager;
music: YTMusic;
studio: Studio;
studio: YTStudio;
kids: YTKids;
actions: Actions;

constructor(session: Session) {
Expand All @@ -56,7 +59,8 @@ class Innertube {
this.playlist = new PlaylistManager(this.session.actions);
this.interact = new InteractionManager(this.session.actions);
this.music = new YTMusic(this.session);
this.studio = new Studio(this.session);
this.studio = new YTStudio(this.session);
this.kids = new YTKids(this.session);
this.actions = this.session.actions;
}

Expand Down
57 changes: 57 additions & 0 deletions src/core/Kids.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import Search from '../parser/ytkids/Search';
import HomeFeed from '../parser/ytkids/HomeFeed';
import VideoInfo from '../parser/ytkids/VideoInfo';
import type Session from './Session';
import { generateRandomString } from '../utils/Utils';

class Kids {
#session: Session;

constructor(session: Session) {
this.#session = session;
}

/**
* Searches the given query.
* @param query - The query.
*/
async search(query: string): Promise<Search> {
const response = await this.#session.actions.execute('/search', { query, client: 'YTKIDS' });
return new Search(this.#session.actions, response.data);
}

/**
* Retrieves video info.
* @param video_id - The video id.
*/
async getInfo(video_id: string): Promise<VideoInfo> {
const cpn = generateRandomString(16);

const initial_info = this.#session.actions.execute('/player', {
cpn,
client: 'YTKIDS',
videoId: video_id,
playbackContext: {
contentPlaybackContext: {
signatureTimestamp: this.#session.player?.sts || 0
}
}
});

const continuation = this.#session.actions.execute('/next', { videoId: video_id, client: 'YTKIDS' });

const response = await Promise.all([ initial_info, continuation ]);

return new VideoInfo(response, this.#session.actions, cpn);
}

/**
* Retrieves the home feed.
*/
async getHomeFeed(): Promise<HomeFeed> {
const response = await this.#session.actions.execute('/browse', { browseId: 'FEkids_home', client: 'YTKIDS' });
return new HomeFeed(this.#session.actions, response.data);
}
}

export default Kids;
10 changes: 10 additions & 0 deletions src/core/Session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import Proto from '../proto';

export enum ClientType {
WEB = 'WEB',
KIDS = 'WEB_KIDS',
MUSIC = 'WEB_REMIX',
ANDROID = 'ANDROID',
ANDROID_MUSIC = 'ANDROID_MUSIC',
Expand Down Expand Up @@ -45,6 +46,15 @@ export interface Context {
deviceMake: string;
deviceModel: string;
utcOffsetMinutes: number;
kidsAppInfo?: {
categorySettings: {
enabledCategories: string[];
};
contentSettings: {
corpusPreference: string;
kidsNoSearchMode: string;
};
};
};
user: {
enableSafetyMode: boolean;
Expand Down
Loading

0 comments on commit 2bbefef

Please sign in to comment.