├── .DS_Store
├── .gitignore
├── README.md
├── output
├── Authenticator.d.ts
├── Authenticator.js
├── IYoutube.d.ts
├── IYoutube.js
├── WrappedHTTPClient.d.ts
├── WrappedHTTPClient.js
├── adapters
│ ├── NodeFSStorageAdapter.d.ts
│ ├── NodeFSStorageAdapter.js
│ ├── NodeFetchClientAdapter.d.ts
│ └── NodeFetchClientAdapter.js
├── constants.d.ts
├── constants.js
├── fetchers
│ ├── CommentContinuatedList.d.ts
│ ├── CommentContinuatedList.js
│ ├── CommentSectionContinuatedList.d.ts
│ ├── CommentSectionContinuatedList.js
│ ├── CommentThreadRepliesContinuatedList.d.ts
│ ├── CommentThreadRepliesContinuatedList.js
│ ├── ContinuatedList.d.ts
│ ├── ContinuatedList.js
│ ├── Explorer.d.ts
│ ├── Explorer.js
│ ├── PlaylistContinuatedList.d.ts
│ ├── PlaylistContinuatedList.js
│ ├── SearchContinuatedList.d.ts
│ ├── SearchContinuatedList.js
│ ├── SubscriptionFeedContinuatedList.d.ts
│ ├── SubscriptionFeedContinuatedList.js
│ ├── User.d.ts
│ ├── User.js
│ ├── helpers.d.ts
│ └── helpers.js
├── formatsChipher.d.ts
├── formatsChipher.js
├── interfaces
│ ├── Badge.d.ts
│ ├── Badge.js
│ ├── Badge.ts
│ ├── CaptionTrack.d.ts
│ ├── CaptionTrack.js
│ ├── Channel.d.ts
│ ├── Channel.js
│ ├── ChannelBadge.d.ts
│ ├── ChannelBadge.js
│ ├── ChannelLink.d.ts
│ ├── ChannelLink.js
│ ├── Comment.d.ts
│ ├── Comment.js
│ ├── CommentThread.d.ts
│ ├── CommentThread.js
│ ├── Format.d.ts
│ ├── Format.js
│ ├── HTTPClient.d.ts
│ ├── HTTPClient.js
│ ├── List.d.ts
│ ├── List.js
│ ├── Playlist.d.ts
│ ├── Playlist.js
│ ├── SearchProposal.d.ts
│ ├── SearchProposal.js
│ ├── SponsorBlockSegment.d.ts
│ ├── SponsorBlockSegment.js
│ ├── StorageAdapter.d.ts
│ ├── StorageAdapter.js
│ ├── Thumbnail.d.ts
│ ├── Thumbnail.js
│ ├── Video.d.ts
│ └── Video.js
├── main.d.ts
├── main.js
├── nodeDefault.d.ts
├── nodeDefault.js
├── nodeInst.d.ts
└── nodeInst.js
├── package-lock.json
├── package.json
├── src
├── Authenticator.ts
├── IYoutube.ts
├── WrappedHTTPClient.ts
├── adapters
│ ├── NodeFSStorageAdapter.ts
│ └── NodeFetchClientAdapter.ts
├── constants.ts
├── fetchers
│ ├── CommentSectionContinuatedList.ts
│ ├── CommentThreadRepliesContinuatedList.ts
│ ├── ContinuatedList.ts
│ ├── Explorer.ts
│ ├── PlaylistContinuatedList.ts
│ ├── SearchContinuatedList.ts
│ ├── SubscriptionFeedContinuatedList.ts
│ ├── User.ts
│ └── helpers.ts
├── formatsChipher.ts
├── interfaces
│ ├── Badge.ts
│ ├── CaptionTrack.ts
│ ├── Channel.ts
│ ├── ChannelBadge.ts
│ ├── ChannelLink.ts
│ ├── Comment.ts
│ ├── CommentThread.ts
│ ├── Format.ts
│ ├── HTTPClient.ts
│ ├── List.ts
│ ├── Playlist.ts
│ ├── SearchProposal.ts
│ ├── SponsorBlockSegment.ts
│ ├── StorageAdapter.ts
│ ├── Thumbnail.ts
│ └── Video.ts
├── main.ts
└── nodeDefault.ts
├── test
└── test.js
├── todo.md
└── tsconfig.json
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ToBiDi0410/IYoutube/0cfb13dfd4fc8cce3e5501869cc1509319d994b0/.DS_Store
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | *tokens.json
3 | datastorage
4 | testIgnored.js
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
IYoutube
2 |
3 |
4 |
5 | The ultimate dirty API Client for the internal YouTube API (Innertube API) with a big and growing coverage of actions and endpoints
6 |
7 |
8 |
9 | #### Features
10 | - Video information and statistics fetching
11 | - Playlist information and videos
12 | - Search functions (with type filters)
13 | - Like & subscriptions API coverage (videos, channels, playlists)
14 | - Comment API coverage (partial, only for videos)
15 | - Simple User Authentication using OAuth2 (allows Private Videos) *1
16 | - Built on one of the latest versions
17 | - Typescript types (for most things)
18 | - Support for a wide variety of plattforms, due to optional adapter support for HTTP fetching and storage access
19 |
20 | *1 This does not require an API Key. It uses extracted keys from the YouTube TV App.
21 |
22 | #### Usage
23 |
24 | ###### Please look at the Basic [Example](./test/test.js)
25 |
26 | #### To-Do
27 | I will do my best to keep up the work on this project, any pull requests or contributions in any way (e.g feature suggestions) are welcome.
28 |
29 | [Current Todo List](./todo.md)
30 |
31 | This API was originally designed for [IonicTube](https://github.com/ToBiDi0410/IonicTube) (an alternative YT App)
32 | #### Credits
33 | - @pytube for the authentication method: https://github.com/pytube/pytube
34 | - @ytdl-org for the consent confirmation: https://github.com/ytdl-org/youtube-dl/
35 | - @Microsoft with Visual Studio Code for development: https://code.visualstudio.com/
36 | - @Google for their API and Chrome Dev Tools to analyse it :)
37 | - @TeamNewPipe for the signature code (from: https://github.com/TeamNewPipe/NewPipeExtractor)
38 | - @Anarios for the Return-YT-Dislike API (https://github.com/Anarios/return-youtube-dislike)
39 | - @Ajayyy for the Sponsorblock API (https://sponsor.ajay.app/)
40 |
41 | #### Disclaimers
42 | - This project is not affiliated or linked to Google/YouTube in any way
43 | - This project does not contain any unofficial source code
44 | - This project is created for educational use only!
45 | - All mentioned brands and trademarks (e.g "Google" & "YouTube") are the property of their lawful owners
46 |
--------------------------------------------------------------------------------
/output/Authenticator.d.ts:
--------------------------------------------------------------------------------
1 | import { HTTPClient } from "./interfaces/HTTPClient";
2 | import { StorageAdapter } from "./interfaces/StorageAdapter";
3 | export declare class Authenticator {
4 | httpClient: HTTPClient;
5 | storageAdapter: StorageAdapter;
6 | token: any;
7 | constructor(httpclient: HTTPClient, storage: StorageAdapter);
8 | init(): Promise;
9 | requiresLogin(): boolean;
10 | getNewLoginCode(): Promise<{
11 | userCode: any;
12 | deviceCode: any;
13 | userUrl: any;
14 | expiresIn: any;
15 | interval: number;
16 | }>;
17 | loadTokensWithDeviceCode(deviceCode: string): Promise;
18 | getToken(): Promise;
19 | getAuthorizationHeader(): Promise;
20 | }
21 |
--------------------------------------------------------------------------------
/output/Authenticator.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4 | return new (P || (P = Promise))(function (resolve, reject) {
5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8 | step((generator = generator.apply(thisArg, _arguments || [])).next());
9 | });
10 | };
11 | Object.defineProperty(exports, "__esModule", { value: true });
12 | exports.Authenticator = void 0;
13 | const constants_1 = require("./constants");
14 | const HTTPClient_1 = require("./interfaces/HTTPClient");
15 | const CLIENT_ID = "861556708454-d6dlm3lh05idd8npek18k6be8ba3oc68.apps.googleusercontent.com";
16 | const CLIENT_SECRET = "SboVhoG9s0rNafixCSGGKXAT";
17 | class Authenticator {
18 | constructor(httpclient, storage) {
19 | this.token = {
20 | access: "NULL",
21 | refresh: "NULL",
22 | type: "NULL",
23 | expireDate: new Date()
24 | };
25 | this.httpClient = httpclient;
26 | this.storageAdapter = storage;
27 | }
28 | init() {
29 | return __awaiter(this, void 0, void 0, function* () {
30 | if (constants_1.DEBUG)
31 | console.log(constants_1.CONSOLE_COLORS.fg.cyan + "[AUTHENTICATOR] Initializing Authenticator...", constants_1.CONSOLE_COLORS.reset);
32 | if ((yield this.storageAdapter.exists(TOKEN_FILE))) {
33 | if (constants_1.DEBUG)
34 | console.log(constants_1.CONSOLE_COLORS.fg.magenta + "[AUTHENTICATOR] Found Token File in Storage, reading it...", constants_1.CONSOLE_COLORS.reset);
35 | var str = yield this.storageAdapter.get(TOKEN_FILE);
36 | if (str) {
37 | this.token = JSON.parse(str);
38 | if (constants_1.DEBUG)
39 | console.log(constants_1.CONSOLE_COLORS.bright + constants_1.CONSOLE_COLORS.fg.green + "[AUTHENTICATOR] Now using Token from Storage (expires: " + new Date(this.token.expireDate).toLocaleString() + ")", constants_1.CONSOLE_COLORS.reset);
40 | }
41 | }
42 | });
43 | }
44 | requiresLogin() {
45 | return Object.values(this.token).includes("NULL");
46 | }
47 | getNewLoginCode() {
48 | return __awaiter(this, void 0, void 0, function* () {
49 | const res = yield this.httpClient.request({
50 | url: "https://oauth2.googleapis.com/device/code",
51 | method: HTTPClient_1.HTTPRequestMethod.POST,
52 | data: JSON.stringify({
53 | client_id: CLIENT_ID,
54 | scope: "https://www.googleapis.com/auth/youtube"
55 | }),
56 | headers: {
57 | "content-type": "text/plain;charset=UTF-8",
58 | "user-agent": constants_1.DEFAULT_USER_AGENT
59 | }
60 | });
61 | if (res.status != 200)
62 | throw new Error("Failed to get new Google Login Code");
63 | const resJSON = JSON.parse(res.data);
64 | if (constants_1.DEBUG)
65 | console.log(constants_1.CONSOLE_COLORS.bright + constants_1.CONSOLE_COLORS.fg.yellow + "[AUTHENTICATOR] Started Authentication Method: Device Code", constants_1.CONSOLE_COLORS.reset);
66 | return { userCode: resJSON.user_code, deviceCode: resJSON.device_code, userUrl: resJSON.verification_url, expiresIn: resJSON.expires_in, interval: 5 };
67 | });
68 | }
69 | loadTokensWithDeviceCode(deviceCode) {
70 | return __awaiter(this, void 0, void 0, function* () {
71 | var res;
72 | while (!res || !res.refresh_token) {
73 | try {
74 | res = yield this.httpClient.request({
75 | method: HTTPClient_1.HTTPRequestMethod.POST,
76 | url: "https://oauth2.googleapis.com/token",
77 | data: JSON.stringify({
78 | client_id: CLIENT_ID,
79 | client_secret: CLIENT_SECRET,
80 | device_code: deviceCode,
81 | grant_type: "urn:ietf:params:oauth:grant-type:device_code"
82 | }),
83 | headers: {
84 | "content-type": "text/plain;charset=UTF-8",
85 | "user-agent": constants_1.DEFAULT_USER_AGENT
86 | }
87 | });
88 | res = JSON.parse(res.data);
89 | yield new Promise(resolve => setTimeout(resolve, 5000));
90 | }
91 | catch (err) {
92 | if (constants_1.DEBUG)
93 | console.log("[AUTHENTICATOR] Failed to recieve Token with Code:\n", err);
94 | }
95 | }
96 | this.token = { type: null, access: null, refresh: res.refresh_token, expireDate: null };
97 | if (constants_1.DEBUG)
98 | console.log(constants_1.CONSOLE_COLORS.bright + constants_1.CONSOLE_COLORS.fg.yellow + "[AUTHENTICATOR] Authentication Method successfull: Device Code", constants_1.CONSOLE_COLORS.reset);
99 | yield this.getToken();
100 | });
101 | }
102 | getToken() {
103 | return __awaiter(this, void 0, void 0, function* () {
104 | if (this.token.access == null || (Date.now() - this.token.expireDate) > 0) {
105 | let res = yield this.httpClient.request({
106 | method: HTTPClient_1.HTTPRequestMethod.POST,
107 | url: "https://oauth2.googleapis.com/token",
108 | data: JSON.stringify({
109 | client_id: CLIENT_ID,
110 | client_secret: CLIENT_SECRET,
111 | grant_type: "refresh_token",
112 | refresh_token: this.token.refresh
113 | }),
114 | headers: {
115 | "content-type": "text/plain;charset=UTF-8",
116 | "user-agent": constants_1.DEFAULT_USER_AGENT
117 | }
118 | });
119 | res = JSON.parse(res.data);
120 | this.token = { type: res.token_type, access: res.access_token, refresh: this.token.refresh, expireDate: (new Date().getTime() + 1000 * res.expires_in) };
121 | if (constants_1.DEBUG)
122 | console.log(constants_1.CONSOLE_COLORS.bright + constants_1.CONSOLE_COLORS.fg.green + "[AUTHENTICATOR] Refreshed the Access Token using the refresh Token", constants_1.CONSOLE_COLORS.reset);
123 | yield this.storageAdapter.set(TOKEN_FILE, JSON.stringify(Object.assign({}, this.token)));
124 | if (constants_1.DEBUG)
125 | console.log(constants_1.CONSOLE_COLORS.bright + constants_1.CONSOLE_COLORS.fg.yellow + "[AUTHENTICATOR] Current Token written to Storage", constants_1.CONSOLE_COLORS.reset);
126 | }
127 | return this.token;
128 | });
129 | }
130 | getAuthorizationHeader() {
131 | return __awaiter(this, void 0, void 0, function* () {
132 | var currentToken = yield this.getToken();
133 | return currentToken.type + " " + currentToken.access;
134 | });
135 | }
136 | }
137 | exports.Authenticator = Authenticator;
138 | const TOKEN_FILE = "IYoutubeTokens.json";
139 |
--------------------------------------------------------------------------------
/output/IYoutube.d.ts:
--------------------------------------------------------------------------------
1 | import { Authenticator } from "./Authenticator";
2 | import { HTTPClient } from "./interfaces/HTTPClient";
3 | import { StorageAdapter } from "./interfaces/StorageAdapter";
4 | import { WrappedHTTPClient } from "./WrappedHTTPClient";
5 | import { SearchType } from "./fetchers/SearchContinuatedList";
6 | import { Explorer } from "./fetchers/Explorer";
7 | import { ContinuatedList } from "./fetchers/ContinuatedList";
8 | import { User } from "./fetchers/User";
9 | import { Playlist } from "./interfaces/Playlist";
10 | import { Channel, SearchProposal, Video } from "./main";
11 | export default class IYoutube {
12 | rawHttpClient: HTTPClient;
13 | wrappedHttpClient: WrappedHTTPClient;
14 | storageAdapter: StorageAdapter;
15 | authenticator: Authenticator;
16 | explorer: Explorer;
17 | user: User;
18 | constructor(httpClient: HTTPClient, storageAdapater: StorageAdapter);
19 | init(): Promise;
20 | search(term: string, type: SearchType): Promise;
21 | searchProposals(term: string): Promise>;
22 | getPlaylist(playlistId: string): Promise;
23 | getChannel(channelId: string): Promise;
24 | getVideo(videoId: string): Promise