├── .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