youtubei 0.0.1-rc.3 → 0.0.1-rc.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -5
- package/dist/classes/Base.d.ts +10 -0
- package/dist/classes/{client/types.js → Base.js} +3 -0
- package/dist/classes/BaseVideo.d.ts +59 -0
- package/dist/classes/BaseVideo.js +121 -0
- package/dist/classes/Channel.d.ts +27 -31
- package/dist/classes/Channel.js +59 -135
- package/dist/classes/ChannelCompact.d.ts +74 -23
- package/dist/classes/ChannelCompact.js +114 -103
- package/dist/classes/Chat.d.ts +29 -0
- package/dist/classes/Chat.js +31 -0
- package/dist/classes/Client.d.ts +49 -0
- package/dist/classes/Client.js +97 -0
- package/dist/classes/Comment.d.ts +50 -0
- package/dist/classes/Comment.js +84 -0
- package/dist/classes/LiveVideo.d.ts +47 -0
- package/dist/classes/LiveVideo.js +94 -0
- package/dist/classes/MixPlaylist.d.ts +32 -0
- package/dist/classes/MixPlaylist.js +44 -0
- package/dist/classes/Playlist.d.ts +35 -18
- package/dist/classes/Playlist.js +80 -116
- package/dist/classes/PlaylistCompact.d.ts +27 -15
- package/dist/classes/PlaylistCompact.js +42 -46
- package/dist/classes/Reply.d.ts +38 -0
- package/dist/classes/Reply.js +35 -0
- package/dist/classes/SearchResult.d.ts +52 -8
- package/dist/classes/SearchResult.js +101 -122
- package/dist/classes/Thumbnails.d.ts +42 -0
- package/dist/classes/Thumbnails.js +66 -0
- package/dist/classes/Video.d.ts +44 -37
- package/dist/classes/Video.js +74 -83
- package/dist/classes/VideoCompact.d.ts +38 -19
- package/dist/classes/VideoCompact.js +53 -53
- package/dist/classes/index.d.ts +10 -2
- package/dist/classes/index.js +20 -4
- package/dist/common/HTTP.d.ts +26 -0
- package/dist/common/HTTP.js +85 -0
- package/dist/common/decorators.js +6 -26
- package/dist/common/helper.d.ts +4 -0
- package/dist/common/helper.js +37 -18
- package/dist/common/index.d.ts +2 -1
- package/dist/common/index.js +5 -3
- package/dist/common/mixins.d.ts +2 -0
- package/dist/common/mixins.js +12 -0
- package/dist/common/types.d.ts +0 -5
- package/dist/constants.d.ts +5 -2
- package/dist/constants.js +6 -3
- package/package.json +23 -20
- package/.prettierrc +0 -8
- package/.vscode/settings.json +0 -4
- package/CHANGELOG.md +0 -6
- package/debug.log +0 -47
- package/dist/classes/BaseCompact.d.ts +0 -10
- package/dist/classes/BaseCompact.js +0 -27
- package/dist/classes/client/Client.d.ts +0 -23
- package/dist/classes/client/Client.js +0 -128
- package/dist/classes/client/index.d.ts +0 -2
- package/dist/classes/client/index.js +0 -19
- package/dist/classes/client/types.d.ts +0 -12
- package/dist/common/axios.d.ts +0 -4
- package/dist/common/axios.js +0 -44
- package/jest.config.js +0 -5
package/README.md
CHANGED
|
@@ -2,11 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
`Youtubei` is made to replace my other library [scrape-yt](https://github.com/SuspiciousLookingOwl/scrape-yt/). Instead of scrapping data from Youtube page, `youtubei` fetches data by sending a request directly to `https://www.youtube.com/youtubei/v1`, which should be faster and provide more reliable result.
|
|
4
4
|
|
|
5
|
+
<b>Requires Node >= 12</b>
|
|
6
|
+
|
|
7
|
+
#### [Documentation](https://youtubei.netlify.app/docs)
|
|
8
|
+
|
|
5
9
|
## Installation
|
|
6
10
|
```
|
|
7
11
|
npm i youtubei
|
|
8
12
|
```
|
|
9
13
|
|
|
14
|
+
|
|
10
15
|
## Example
|
|
11
16
|
```js
|
|
12
17
|
const { Client } = require("youtubei");
|
|
@@ -41,8 +46,4 @@ const run = async () => {
|
|
|
41
46
|
};
|
|
42
47
|
|
|
43
48
|
run();
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
## Documentation
|
|
47
|
-
|
|
48
|
-
Coming soon.
|
|
49
|
+
```
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { PlaylistCompact, VideoCompact, ChannelCompact, Base, BaseAttributes, Thumbnails } from ".";
|
|
2
|
+
import { YoutubeRawData } from "../common";
|
|
3
|
+
/** @hidden */
|
|
4
|
+
export interface BaseVideoAttributes extends BaseAttributes {
|
|
5
|
+
title: string;
|
|
6
|
+
thumbnails: Thumbnails;
|
|
7
|
+
description: string;
|
|
8
|
+
channel: ChannelCompact;
|
|
9
|
+
uploadDate: string;
|
|
10
|
+
viewCount: number | null;
|
|
11
|
+
likeCount: number | null;
|
|
12
|
+
isLiveContent: boolean;
|
|
13
|
+
tags: string[];
|
|
14
|
+
upNext: VideoCompact | PlaylistCompact | null;
|
|
15
|
+
related: (VideoCompact | PlaylistCompact)[];
|
|
16
|
+
relatedContinuation?: string;
|
|
17
|
+
}
|
|
18
|
+
/** Represents a Video */
|
|
19
|
+
export default class BaseVideo extends Base implements BaseVideoAttributes {
|
|
20
|
+
/** The title of this video */
|
|
21
|
+
title: string;
|
|
22
|
+
/** Thumbnails of the video with different sizes */
|
|
23
|
+
thumbnails: Thumbnails;
|
|
24
|
+
/** The description of this video */
|
|
25
|
+
description: string;
|
|
26
|
+
/** The channel that uploaded this video */
|
|
27
|
+
channel: ChannelCompact;
|
|
28
|
+
/** The date this video is uploaded at */
|
|
29
|
+
uploadDate: string;
|
|
30
|
+
/** How many view does this video have, null if the view count is hidden */
|
|
31
|
+
viewCount: number | null;
|
|
32
|
+
/** How many like does this video have, null if the like count hidden */
|
|
33
|
+
likeCount: number | null;
|
|
34
|
+
/** Whether this video is a live content or not */
|
|
35
|
+
isLiveContent: boolean;
|
|
36
|
+
/** The tags of this video */
|
|
37
|
+
tags: string[];
|
|
38
|
+
/** Next video / playlist recommended by Youtube */
|
|
39
|
+
upNext: VideoCompact | PlaylistCompact | null;
|
|
40
|
+
/** Videos / playlists related to this video */
|
|
41
|
+
related: (VideoCompact | PlaylistCompact)[];
|
|
42
|
+
/** Current continuation token to load next related content */
|
|
43
|
+
relatedContinuation?: string;
|
|
44
|
+
/** @hidden */
|
|
45
|
+
constructor(video?: Partial<BaseVideoAttributes>);
|
|
46
|
+
/**
|
|
47
|
+
* Load this instance with raw data from Youtube
|
|
48
|
+
*
|
|
49
|
+
* @hidden
|
|
50
|
+
*/
|
|
51
|
+
load(data: YoutubeRawData): BaseVideo;
|
|
52
|
+
/** Load next related videos / playlists */
|
|
53
|
+
nextRelated(count?: number): Promise<(VideoCompact | PlaylistCompact)[]>;
|
|
54
|
+
/** @hidden */
|
|
55
|
+
static parseRawData(data: YoutubeRawData): YoutubeRawData;
|
|
56
|
+
private static parseRelated;
|
|
57
|
+
private static parseCompactRenderer;
|
|
58
|
+
private static parseButtonRenderer;
|
|
59
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
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
|
+
const _1 = require(".");
|
|
13
|
+
const common_1 = require("../common");
|
|
14
|
+
const constants_1 = require("../constants");
|
|
15
|
+
/** Represents a Video */
|
|
16
|
+
class BaseVideo extends _1.Base {
|
|
17
|
+
/** @hidden */
|
|
18
|
+
constructor(video = {}) {
|
|
19
|
+
super();
|
|
20
|
+
/** Videos / playlists related to this video */
|
|
21
|
+
this.related = [];
|
|
22
|
+
Object.assign(this, video);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Load this instance with raw data from Youtube
|
|
26
|
+
*
|
|
27
|
+
* @hidden
|
|
28
|
+
*/
|
|
29
|
+
load(data) {
|
|
30
|
+
var _a, _b, _c, _d;
|
|
31
|
+
const videoInfo = BaseVideo.parseRawData(data);
|
|
32
|
+
// Basic information
|
|
33
|
+
this.id = videoInfo.videoDetails.videoId;
|
|
34
|
+
this.title = videoInfo.videoDetails.title;
|
|
35
|
+
this.uploadDate = videoInfo.dateText.simpleText;
|
|
36
|
+
this.viewCount = +videoInfo.videoDetails.viewCount || null;
|
|
37
|
+
this.isLiveContent = videoInfo.videoDetails.isLiveContent;
|
|
38
|
+
this.thumbnails = new _1.Thumbnails().load(videoInfo.videoDetails.thumbnail.thumbnails);
|
|
39
|
+
// Channel
|
|
40
|
+
const { title, thumbnail, subscriberCountText } = videoInfo.owner.videoOwnerRenderer;
|
|
41
|
+
this.channel = new _1.ChannelCompact({
|
|
42
|
+
client: this.client,
|
|
43
|
+
id: title.runs[0].navigationEndpoint.browseEndpoint.browseId,
|
|
44
|
+
name: title.runs[0].text,
|
|
45
|
+
subscriberCount: subscriberCountText === null || subscriberCountText === void 0 ? void 0 : subscriberCountText.simpleText,
|
|
46
|
+
thumbnails: new _1.Thumbnails().load(thumbnail.thumbnails),
|
|
47
|
+
});
|
|
48
|
+
// Like Count and Dislike Count
|
|
49
|
+
const topLevelButtons = videoInfo.videoActions.menuRenderer.topLevelButtons;
|
|
50
|
+
this.likeCount = common_1.stripToInt(BaseVideo.parseButtonRenderer(topLevelButtons[0]));
|
|
51
|
+
// Tags and description
|
|
52
|
+
this.tags =
|
|
53
|
+
((_b = (_a = videoInfo.superTitleLink) === null || _a === void 0 ? void 0 : _a.runs) === null || _b === void 0 ? void 0 : _b.map((r) => r.text.trim()).filter((t) => t)) || [];
|
|
54
|
+
this.description =
|
|
55
|
+
((_c = videoInfo.description) === null || _c === void 0 ? void 0 : _c.runs.map((d) => d.text).join("")) || "";
|
|
56
|
+
// Up Next and related videos
|
|
57
|
+
this.related = [];
|
|
58
|
+
const secondaryContents = data[3].response.contents.twoColumnWatchNextResults.secondaryResults.secondaryResults
|
|
59
|
+
.results;
|
|
60
|
+
if (secondaryContents) {
|
|
61
|
+
const upNext = ((_d = secondaryContents.find((s) => "compactAutoplayRenderer" in s)) === null || _d === void 0 ? void 0 : _d.compactAutoplayRenderer.contents[0]) || null;
|
|
62
|
+
this.upNext = upNext ? BaseVideo.parseCompactRenderer(upNext, this.client) : upNext;
|
|
63
|
+
this.related.push(...BaseVideo.parseRelated(secondaryContents, this.client));
|
|
64
|
+
// Related continuation
|
|
65
|
+
this.relatedContinuation = common_1.getContinuationFromItems(secondaryContents);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
this.upNext = null;
|
|
69
|
+
this.related = [];
|
|
70
|
+
}
|
|
71
|
+
return this;
|
|
72
|
+
}
|
|
73
|
+
/** Load next related videos / playlists */
|
|
74
|
+
nextRelated(count = 1) {
|
|
75
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
76
|
+
const newRelated = [];
|
|
77
|
+
for (let i = 0; i < count || count == 0; i++) {
|
|
78
|
+
if (this.relatedContinuation === undefined)
|
|
79
|
+
break;
|
|
80
|
+
const response = yield this.client.http.post(`${constants_1.I_END_POINT}/next`, {
|
|
81
|
+
data: { continuation: this.relatedContinuation },
|
|
82
|
+
});
|
|
83
|
+
const secondaryContents = response.data.onResponseReceivedEndpoints[0].appendContinuationItemsAction
|
|
84
|
+
.continuationItems;
|
|
85
|
+
newRelated.push(...BaseVideo.parseRelated(secondaryContents, this.client));
|
|
86
|
+
this.relatedContinuation = common_1.getContinuationFromItems(secondaryContents);
|
|
87
|
+
}
|
|
88
|
+
this.related.push(...newRelated);
|
|
89
|
+
return newRelated;
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
/** @hidden */
|
|
93
|
+
static parseRawData(data) {
|
|
94
|
+
const contents = data[3].response.contents.twoColumnWatchNextResults.results.results.contents;
|
|
95
|
+
const primaryInfo = contents.find((c) => "videoPrimaryInfoRenderer" in c)
|
|
96
|
+
.videoPrimaryInfoRenderer;
|
|
97
|
+
const secondaryInfo = contents.find((c) => "videoSecondaryInfoRenderer" in c).videoSecondaryInfoRenderer;
|
|
98
|
+
const videoDetails = data[2].playerResponse.videoDetails;
|
|
99
|
+
return Object.assign(Object.assign(Object.assign({}, secondaryInfo), primaryInfo), { videoDetails });
|
|
100
|
+
}
|
|
101
|
+
static parseRelated(secondaryContents, client) {
|
|
102
|
+
return secondaryContents
|
|
103
|
+
.map((c) => BaseVideo.parseCompactRenderer(c, client))
|
|
104
|
+
.filter((c) => c !== undefined);
|
|
105
|
+
}
|
|
106
|
+
static parseCompactRenderer(data, client) {
|
|
107
|
+
if ("compactVideoRenderer" in data) {
|
|
108
|
+
return new _1.VideoCompact({ client }).load(data.compactVideoRenderer);
|
|
109
|
+
}
|
|
110
|
+
else if ("compactRadioRenderer" in data) {
|
|
111
|
+
return new _1.PlaylistCompact({ client }).load(data.compactRadioRenderer);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
static parseButtonRenderer(data) {
|
|
115
|
+
var _a;
|
|
116
|
+
const buttonRenderer = data.toggleButtonRenderer || data.buttonRenderer;
|
|
117
|
+
const accessibilityData = (((_a = buttonRenderer.defaultText) === null || _a === void 0 ? void 0 : _a.accessibility) || buttonRenderer.accessibilityData).accessibilityData;
|
|
118
|
+
return accessibilityData.label;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
exports.default = BaseVideo;
|
|
@@ -1,37 +1,33 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
import { YoutubeRawData } from "../common";
|
|
2
|
+
import ChannelCompact, { ChannelCompactAttributes } from "./ChannelCompact";
|
|
3
|
+
import PlaylistCompact from "./PlaylistCompact";
|
|
4
|
+
import Thumbnails from "./Thumbnails";
|
|
5
|
+
import VideoCompact from "./VideoCompact";
|
|
6
|
+
interface Shelf {
|
|
7
|
+
title: string;
|
|
8
|
+
subtitle?: string;
|
|
9
|
+
items: ChannelCompact[] | VideoCompact[] | PlaylistCompact[];
|
|
9
10
|
}
|
|
10
|
-
/**
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
* Get playlists from current channel
|
|
26
|
-
*
|
|
27
|
-
* TODO: Add continuation support
|
|
28
|
-
*/
|
|
29
|
-
getPlaylists(): Promise<PlaylistCompact[]>;
|
|
11
|
+
/** @hidden */
|
|
12
|
+
interface ChannelAttributes extends ChannelCompactAttributes {
|
|
13
|
+
banner: Thumbnails;
|
|
14
|
+
tvBanner: Thumbnails;
|
|
15
|
+
mobileBanner: Thumbnails;
|
|
16
|
+
shelves: Shelf[];
|
|
17
|
+
}
|
|
18
|
+
/** Represents a Youtube Channel */
|
|
19
|
+
export default class Channel extends ChannelCompact implements ChannelAttributes {
|
|
20
|
+
banner: Thumbnails;
|
|
21
|
+
mobileBanner: Thumbnails;
|
|
22
|
+
tvBanner: Thumbnails;
|
|
23
|
+
shelves: Shelf[];
|
|
24
|
+
/** @hidden */
|
|
25
|
+
constructor(channel?: Partial<ChannelAttributes>);
|
|
30
26
|
/**
|
|
31
|
-
* Load instance
|
|
27
|
+
* Load this instance with raw data from Youtube
|
|
32
28
|
*
|
|
33
|
-
* @
|
|
29
|
+
* @hidden
|
|
34
30
|
*/
|
|
35
|
-
load(
|
|
31
|
+
load(data: YoutubeRawData): Channel;
|
|
36
32
|
}
|
|
37
33
|
export {};
|
package/dist/classes/Channel.js
CHANGED
|
@@ -1,145 +1,69 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var
|
|
3
|
-
|
|
4
|
-
extendStatics = Object.setPrototypeOf ||
|
|
5
|
-
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
|
6
|
-
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
|
7
|
-
return extendStatics(d, b);
|
|
8
|
-
};
|
|
9
|
-
return function (d, b) {
|
|
10
|
-
extendStatics(d, b);
|
|
11
|
-
function __() { this.constructor = d; }
|
|
12
|
-
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
13
|
-
};
|
|
14
|
-
})();
|
|
15
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
16
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
17
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
18
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
19
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
20
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
21
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
22
|
-
});
|
|
23
|
-
};
|
|
24
|
-
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
25
|
-
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
26
|
-
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
27
|
-
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
28
|
-
function step(op) {
|
|
29
|
-
if (f) throw new TypeError("Generator is already executing.");
|
|
30
|
-
while (_) try {
|
|
31
|
-
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
32
|
-
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
33
|
-
switch (op[0]) {
|
|
34
|
-
case 0: case 1: t = op; break;
|
|
35
|
-
case 4: _.label++; return { value: op[1], done: false };
|
|
36
|
-
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
37
|
-
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
38
|
-
default:
|
|
39
|
-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
40
|
-
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
41
|
-
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
42
|
-
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
43
|
-
if (t[2]) _.ops.pop();
|
|
44
|
-
_.trys.pop(); continue;
|
|
45
|
-
}
|
|
46
|
-
op = body.call(thisArg, _);
|
|
47
|
-
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
48
|
-
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
49
|
-
}
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
50
4
|
};
|
|
51
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
6
|
+
const ChannelCompact_1 = __importDefault(require("./ChannelCompact"));
|
|
7
|
+
const PlaylistCompact_1 = __importDefault(require("./PlaylistCompact"));
|
|
8
|
+
const Thumbnails_1 = __importDefault(require("./Thumbnails"));
|
|
9
|
+
const VideoCompact_1 = __importDefault(require("./VideoCompact"));
|
|
10
|
+
/** Represents a Youtube Channel */
|
|
11
|
+
class Channel extends ChannelCompact_1.default {
|
|
12
|
+
/** @hidden */
|
|
13
|
+
constructor(channel = {}) {
|
|
14
|
+
super();
|
|
15
|
+
this.shelves = [];
|
|
16
|
+
this.videos = [];
|
|
17
|
+
this.playlists = [];
|
|
18
|
+
Object.assign(this, channel);
|
|
65
19
|
}
|
|
66
20
|
/**
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
* TODO: Add continuation support
|
|
70
|
-
*/
|
|
71
|
-
Channel.prototype.getVideos = function () {
|
|
72
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
73
|
-
var response;
|
|
74
|
-
return __generator(this, function (_a) {
|
|
75
|
-
switch (_a.label) {
|
|
76
|
-
case 0: return [4 /*yield*/, common_1.axios.post(constants_1.I_END_POINT + "/browse", {
|
|
77
|
-
browseId: this.id,
|
|
78
|
-
params: "EgZ2aWRlb3M%3D",
|
|
79
|
-
})];
|
|
80
|
-
case 1:
|
|
81
|
-
response = _a.sent();
|
|
82
|
-
return [2 /*return*/, response.data.contents.twoColumnBrowseResultsRenderer.tabs[1].tabRenderer.content.sectionListRenderer.contents[0].itemSectionRenderer.contents[0].gridRenderer.items
|
|
83
|
-
.filter(function (i) { return i.gridVideoRenderer; })
|
|
84
|
-
.map(function (i) { return new _1.VideoCompact().load(i.gridVideoRenderer); })];
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
});
|
|
88
|
-
};
|
|
89
|
-
/**
|
|
90
|
-
* Get playlists from current channel
|
|
21
|
+
* Load this instance with raw data from Youtube
|
|
91
22
|
*
|
|
92
|
-
*
|
|
23
|
+
* @hidden
|
|
93
24
|
*/
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
var response, section, gridPlaylistRenderer;
|
|
97
|
-
return __generator(this, function (_a) {
|
|
98
|
-
switch (_a.label) {
|
|
99
|
-
case 0: return [4 /*yield*/, common_1.axios.post(constants_1.I_END_POINT + "/browse", {
|
|
100
|
-
browseId: this.id,
|
|
101
|
-
params: "EglwbGF5bGlzdHM%3D",
|
|
102
|
-
})];
|
|
103
|
-
case 1:
|
|
104
|
-
response = _a.sent();
|
|
105
|
-
section = response.data.contents.twoColumnBrowseResultsRenderer.tabs[2].tabRenderer.content
|
|
106
|
-
.sectionListRenderer;
|
|
107
|
-
// Has category
|
|
108
|
-
if ("shelfRenderer" in section.contents[0].itemSectionRenderer.contents[0]) {
|
|
109
|
-
gridPlaylistRenderer = section.contents
|
|
110
|
-
.map(function (c) {
|
|
111
|
-
return c.itemSectionRenderer.contents[0].shelfRenderer.content
|
|
112
|
-
.horizontalListRenderer.items;
|
|
113
|
-
})
|
|
114
|
-
.flat();
|
|
115
|
-
}
|
|
116
|
-
else {
|
|
117
|
-
gridPlaylistRenderer =
|
|
118
|
-
section.contents[0].itemSectionRenderer.contents[0].gridRenderer.items;
|
|
119
|
-
}
|
|
120
|
-
return [2 /*return*/, gridPlaylistRenderer.map(function (i) {
|
|
121
|
-
return new _1.PlaylistCompact().load(i.gridPlaylistRenderer);
|
|
122
|
-
})];
|
|
123
|
-
}
|
|
124
|
-
});
|
|
125
|
-
});
|
|
126
|
-
};
|
|
127
|
-
/**
|
|
128
|
-
* Load instance attributes from youtube raw data
|
|
129
|
-
*
|
|
130
|
-
* @param youtubeRawData raw object from youtubei
|
|
131
|
-
*/
|
|
132
|
-
Channel.prototype.load = function (youtubeRawData) {
|
|
133
|
-
var _a;
|
|
134
|
-
var channelId = youtubeRawData.channelId, title = youtubeRawData.title, thumbnail = youtubeRawData.thumbnail, videoCountText = youtubeRawData.videoCountText, navigationEndpoint = youtubeRawData.navigationEndpoint;
|
|
135
|
-
var _b = navigationEndpoint.browseEndpoint, browseId = _b.browseId, canonicalBaseUrl = _b.canonicalBaseUrl;
|
|
25
|
+
load(data) {
|
|
26
|
+
const { channelId, title, avatar, subscriberCountText, } = data.header.c4TabbedHeaderRenderer;
|
|
136
27
|
this.id = channelId;
|
|
137
|
-
this.name = title
|
|
138
|
-
this.thumbnails =
|
|
139
|
-
this.
|
|
140
|
-
this.
|
|
28
|
+
this.name = title;
|
|
29
|
+
this.thumbnails = new Thumbnails_1.default().load(avatar.thumbnails);
|
|
30
|
+
this.videoCount = 0; // data not available
|
|
31
|
+
this.subscriberCount = subscriberCountText.simpleText;
|
|
32
|
+
this.videos = [];
|
|
33
|
+
this.playlists = [];
|
|
34
|
+
const { tvBanner, mobileBanner, banner } = data.header.c4TabbedHeaderRenderer;
|
|
35
|
+
this.banner = new Thumbnails_1.default().load(banner.thumbnails);
|
|
36
|
+
this.tvBanner = new Thumbnails_1.default().load(tvBanner.thumbnails);
|
|
37
|
+
this.mobileBanner = new Thumbnails_1.default().load(mobileBanner.thumbnails);
|
|
38
|
+
// shelves
|
|
39
|
+
const rawShelves = data.contents.twoColumnBrowseResultsRenderer.tabs[0].tabRenderer.content
|
|
40
|
+
.sectionListRenderer.contents;
|
|
41
|
+
for (const rawShelf of rawShelves) {
|
|
42
|
+
const shelfRenderer = rawShelf.itemSectionRenderer.contents[0].shelfRenderer;
|
|
43
|
+
if (!shelfRenderer)
|
|
44
|
+
continue;
|
|
45
|
+
const { title, content, subtitle } = shelfRenderer;
|
|
46
|
+
if (!content.horizontalListRenderer)
|
|
47
|
+
continue;
|
|
48
|
+
const items = content.horizontalListRenderer.items
|
|
49
|
+
.map((i) => {
|
|
50
|
+
if (i.gridVideoRenderer)
|
|
51
|
+
return new VideoCompact_1.default({ client: this.client }).load(i.gridVideoRenderer);
|
|
52
|
+
if (i.gridPlaylistRenderer)
|
|
53
|
+
return new PlaylistCompact_1.default({ client: this.client }).load(i.gridPlaylistRenderer);
|
|
54
|
+
if (i.gridChannelRenderer)
|
|
55
|
+
return new ChannelCompact_1.default({ client: this.client }).load(i.gridChannelRenderer);
|
|
56
|
+
return undefined;
|
|
57
|
+
})
|
|
58
|
+
.filter((i) => i !== undefined);
|
|
59
|
+
const shelf = {
|
|
60
|
+
title: title.runs[0].text,
|
|
61
|
+
subtitle: subtitle === null || subtitle === void 0 ? void 0 : subtitle.simpleText,
|
|
62
|
+
items,
|
|
63
|
+
};
|
|
64
|
+
this.shelves.push(shelf);
|
|
65
|
+
}
|
|
141
66
|
return this;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
}(_1.BaseCompact));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
145
69
|
exports.default = Channel;
|
|
@@ -1,39 +1,90 @@
|
|
|
1
1
|
import { YoutubeRawData } from "../common";
|
|
2
|
-
import { PlaylistCompact, VideoCompact } from ".";
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import { Base, PlaylistCompact, Thumbnails, VideoCompact, BaseAttributes } from ".";
|
|
3
|
+
/** @hidden */
|
|
4
|
+
export interface ChannelCompactAttributes extends BaseAttributes {
|
|
5
5
|
name: string;
|
|
6
|
-
|
|
7
|
-
thumbnail?: string;
|
|
6
|
+
thumbnails?: Thumbnails;
|
|
8
7
|
videoCount?: number;
|
|
8
|
+
subscriberCount?: string;
|
|
9
|
+
videoContinuation?: string | null;
|
|
10
|
+
playlistContinuation?: string | null;
|
|
9
11
|
}
|
|
10
|
-
/**
|
|
11
|
-
|
|
12
|
-
*/
|
|
13
|
-
export default class ChannelCompact implements ChannelCompactProperties {
|
|
14
|
-
id: string;
|
|
12
|
+
/** Represents a Youtube Channel */
|
|
13
|
+
export default class ChannelCompact extends Base implements ChannelCompactAttributes {
|
|
14
|
+
/** The channel's name */
|
|
15
15
|
name: string;
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
/** Thumbnails of the Channel with different sizes */
|
|
17
|
+
thumbnails?: Thumbnails;
|
|
18
|
+
/** How many video does this channel have */
|
|
18
19
|
videoCount?: number;
|
|
19
|
-
constructor(channel?: Partial<ChannelCompactProperties>);
|
|
20
20
|
/**
|
|
21
|
-
*
|
|
21
|
+
* How many subscriber does this channel have,
|
|
22
22
|
*
|
|
23
|
-
*
|
|
23
|
+
* This is not the exact amount, but a literal string like `"1.95M subscribers"`
|
|
24
24
|
*/
|
|
25
|
-
|
|
25
|
+
subscriberCount?: string;
|
|
26
|
+
/** Loaded videos on the channel, fetched from `channel.nextVideos()` */
|
|
27
|
+
videos: VideoCompact[];
|
|
28
|
+
/** Loaded playlists on the channel, fetched from `channel.nextPlaylists()` */
|
|
29
|
+
playlists: PlaylistCompact[];
|
|
30
|
+
/** Current continuation token to load next videos */
|
|
31
|
+
videoContinuation?: string | null;
|
|
32
|
+
/** Current continuation token to load next playlists */
|
|
33
|
+
playlistContinuation?: string | null;
|
|
34
|
+
/** @hidden */
|
|
35
|
+
constructor(channel?: Partial<ChannelCompactAttributes>);
|
|
36
|
+
/** The URL of the channel page */
|
|
37
|
+
get url(): string;
|
|
26
38
|
/**
|
|
27
|
-
*
|
|
39
|
+
* Load this instance with raw data from Youtube
|
|
28
40
|
*
|
|
29
|
-
*
|
|
41
|
+
* @hidden
|
|
30
42
|
*/
|
|
31
|
-
|
|
43
|
+
load(data: YoutubeRawData): ChannelCompact;
|
|
32
44
|
/**
|
|
33
|
-
* Load
|
|
45
|
+
* Load next 30 videos made by the channel, and push the loaded videos to {@link Channel.videos}
|
|
34
46
|
*
|
|
35
|
-
* @
|
|
47
|
+
* @example
|
|
48
|
+
* ```js
|
|
49
|
+
* const channel = await youtube.findOne(CHANNEL_NAME, {type: "channel"});
|
|
50
|
+
* await channel.nextVideos();
|
|
51
|
+
* console.log(channel.videos) // first 30 videos
|
|
52
|
+
*
|
|
53
|
+
* let newVideos = await channel.nextVideos();
|
|
54
|
+
* console.log(newVideos) // 30 loaded videos
|
|
55
|
+
* console.log(channel.videos) // first 60 videos
|
|
56
|
+
*
|
|
57
|
+
* await channel.nextVideos(0); // load the rest of the videos in the channel
|
|
58
|
+
* ```
|
|
59
|
+
*
|
|
60
|
+
* @param count How many time to load the next videos, pass `0` to load all
|
|
61
|
+
*
|
|
62
|
+
* @return New loaded videos
|
|
63
|
+
*/
|
|
64
|
+
nextVideos(count?: number): Promise<VideoCompact[]>;
|
|
65
|
+
/**
|
|
66
|
+
* Load next 30 playlists made by the channel, and push the loaded playlists to {@link Channel.playlists}
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```js
|
|
70
|
+
* const channel = await youtube.findOne(CHANNEL_NAME, {type: "channel"});
|
|
71
|
+
* await channel.nextPlaylists();
|
|
72
|
+
* console.log(channel.playlists) // first 30 playlists
|
|
73
|
+
*
|
|
74
|
+
* let newPlaylists = await channel.nextPlaylists();
|
|
75
|
+
* console.log(newPlaylists) // 30 loaded playlists
|
|
76
|
+
* console.log(channel.playlists) // first 60 playlists
|
|
77
|
+
*
|
|
78
|
+
* await channel.nextPlaylists(0); // load the rest of the playlists in the channel
|
|
79
|
+
* ```
|
|
80
|
+
*
|
|
81
|
+
* @param count How many time to load the next playlists, pass `0` to load all
|
|
82
|
+
*
|
|
83
|
+
* @return New loaded playlists
|
|
36
84
|
*/
|
|
37
|
-
|
|
85
|
+
nextPlaylists(count?: number): Promise<PlaylistCompact[]>;
|
|
86
|
+
/** Get tab data from youtube */
|
|
87
|
+
private getTabData;
|
|
88
|
+
/** Parse tab data from request, tab name is ignored if it's a continuation data */
|
|
89
|
+
private static parseTabData;
|
|
38
90
|
}
|
|
39
|
-
export {};
|