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.
Files changed (62) hide show
  1. package/README.md +6 -5
  2. package/dist/classes/Base.d.ts +10 -0
  3. package/dist/classes/{client/types.js → Base.js} +3 -0
  4. package/dist/classes/BaseVideo.d.ts +59 -0
  5. package/dist/classes/BaseVideo.js +121 -0
  6. package/dist/classes/Channel.d.ts +27 -31
  7. package/dist/classes/Channel.js +59 -135
  8. package/dist/classes/ChannelCompact.d.ts +74 -23
  9. package/dist/classes/ChannelCompact.js +114 -103
  10. package/dist/classes/Chat.d.ts +29 -0
  11. package/dist/classes/Chat.js +31 -0
  12. package/dist/classes/Client.d.ts +49 -0
  13. package/dist/classes/Client.js +97 -0
  14. package/dist/classes/Comment.d.ts +50 -0
  15. package/dist/classes/Comment.js +84 -0
  16. package/dist/classes/LiveVideo.d.ts +47 -0
  17. package/dist/classes/LiveVideo.js +94 -0
  18. package/dist/classes/MixPlaylist.d.ts +32 -0
  19. package/dist/classes/MixPlaylist.js +44 -0
  20. package/dist/classes/Playlist.d.ts +35 -18
  21. package/dist/classes/Playlist.js +80 -116
  22. package/dist/classes/PlaylistCompact.d.ts +27 -15
  23. package/dist/classes/PlaylistCompact.js +42 -46
  24. package/dist/classes/Reply.d.ts +38 -0
  25. package/dist/classes/Reply.js +35 -0
  26. package/dist/classes/SearchResult.d.ts +52 -8
  27. package/dist/classes/SearchResult.js +101 -122
  28. package/dist/classes/Thumbnails.d.ts +42 -0
  29. package/dist/classes/Thumbnails.js +66 -0
  30. package/dist/classes/Video.d.ts +44 -37
  31. package/dist/classes/Video.js +74 -83
  32. package/dist/classes/VideoCompact.d.ts +38 -19
  33. package/dist/classes/VideoCompact.js +53 -53
  34. package/dist/classes/index.d.ts +10 -2
  35. package/dist/classes/index.js +20 -4
  36. package/dist/common/HTTP.d.ts +26 -0
  37. package/dist/common/HTTP.js +85 -0
  38. package/dist/common/decorators.js +6 -26
  39. package/dist/common/helper.d.ts +4 -0
  40. package/dist/common/helper.js +37 -18
  41. package/dist/common/index.d.ts +2 -1
  42. package/dist/common/index.js +5 -3
  43. package/dist/common/mixins.d.ts +2 -0
  44. package/dist/common/mixins.js +12 -0
  45. package/dist/common/types.d.ts +0 -5
  46. package/dist/constants.d.ts +5 -2
  47. package/dist/constants.js +6 -3
  48. package/package.json +23 -20
  49. package/.prettierrc +0 -8
  50. package/.vscode/settings.json +0 -4
  51. package/CHANGELOG.md +0 -6
  52. package/debug.log +0 -47
  53. package/dist/classes/BaseCompact.d.ts +0 -10
  54. package/dist/classes/BaseCompact.js +0 -27
  55. package/dist/classes/client/Client.d.ts +0 -23
  56. package/dist/classes/client/Client.js +0 -128
  57. package/dist/classes/client/index.d.ts +0 -2
  58. package/dist/classes/client/index.js +0 -19
  59. package/dist/classes/client/types.d.ts +0 -12
  60. package/dist/common/axios.d.ts +0 -4
  61. package/dist/common/axios.js +0 -44
  62. package/jest.config.js +0 -5
@@ -8,121 +8,132 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
9
  });
10
10
  };
11
- var __generator = (this && this.__generator) || function (thisArg, body) {
12
- var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
13
- return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
- function verb(n) { return function (v) { return step([n, v]); }; }
15
- function step(op) {
16
- if (f) throw new TypeError("Generator is already executing.");
17
- while (_) try {
18
- 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;
19
- if (y = 0, t) op = [op[0] & 2, t.value];
20
- switch (op[0]) {
21
- case 0: case 1: t = op; break;
22
- case 4: _.label++; return { value: op[1], done: false };
23
- case 5: _.label++; y = op[1]; op = [0]; continue;
24
- case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
- default:
26
- if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
- if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
- if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
- if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
- if (t[2]) _.ops.pop();
31
- _.trys.pop(); continue;
32
- }
33
- op = body.call(thisArg, _);
34
- } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
- if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
- }
37
- };
38
11
  Object.defineProperty(exports, "__esModule", { value: true });
39
- var common_1 = require("../common");
40
- var _1 = require(".");
41
- var constants_1 = require("../constants");
42
- /**
43
- * Represent a Youtube Channel
44
- */
45
- var ChannelCompact = /** @class */ (function () {
46
- function ChannelCompact(channel) {
47
- if (channel === void 0) { channel = {}; }
12
+ const common_1 = require("../common");
13
+ const _1 = require(".");
14
+ const constants_1 = require("../constants");
15
+ /** Represents a Youtube Channel */
16
+ class ChannelCompact extends _1.Base {
17
+ /** @hidden */
18
+ constructor(channel = {}) {
19
+ super();
20
+ /** Loaded videos on the channel, fetched from `channel.nextVideos()` */
21
+ this.videos = [];
22
+ /** Loaded playlists on the channel, fetched from `channel.nextPlaylists()` */
23
+ this.playlists = [];
24
+ /** Current continuation token to load next videos */
25
+ this.videoContinuation = null;
26
+ /** Current continuation token to load next playlists */
27
+ this.playlistContinuation = null;
48
28
  Object.assign(this, channel);
49
29
  }
30
+ /** The URL of the channel page */
31
+ get url() {
32
+ return `https://www.youtube.com/channel/${this.id}`;
33
+ }
50
34
  /**
51
- * Get videos from current Channel
35
+ * Load this instance with raw data from Youtube
52
36
  *
53
- * TODO: Add continuation support
37
+ * @hidden
54
38
  */
55
- ChannelCompact.prototype.getVideos = function () {
56
- return __awaiter(this, void 0, void 0, function () {
57
- var response;
58
- return __generator(this, function (_a) {
59
- switch (_a.label) {
60
- case 0: return [4 /*yield*/, common_1.axios.post(constants_1.I_END_POINT + "/browse", {
61
- browseId: this.id,
62
- params: "EgZ2aWRlb3M%3D",
63
- })];
64
- case 1:
65
- response = _a.sent();
66
- return [2 /*return*/, response.data.contents.twoColumnBrowseResultsRenderer.tabs[1].tabRenderer.content.sectionListRenderer.contents[0].itemSectionRenderer.contents[0].gridRenderer.items
67
- .filter(function (i) { return i.gridVideoRenderer; })
68
- .map(function (i) { return new _1.VideoCompact().load(i.gridVideoRenderer); })];
69
- }
70
- });
71
- });
72
- };
39
+ load(data) {
40
+ const { channelId, title, thumbnail, videoCountText, subscriberCountText } = data;
41
+ this.id = channelId;
42
+ this.name = title.simpleText;
43
+ this.thumbnails = new _1.Thumbnails().load(thumbnail.thumbnails);
44
+ this.videoCount = common_1.stripToInt(videoCountText === null || videoCountText === void 0 ? void 0 : videoCountText.runs[0].text) || 0;
45
+ this.subscriberCount = subscriberCountText === null || subscriberCountText === void 0 ? void 0 : subscriberCountText.simpleText;
46
+ this.videos = [];
47
+ this.playlists = [];
48
+ return this;
49
+ }
73
50
  /**
74
- * Get playlists from current channel
51
+ * Load next 30 videos made by the channel, and push the loaded videos to {@link Channel.videos}
52
+ *
53
+ * @example
54
+ * ```js
55
+ * const channel = await youtube.findOne(CHANNEL_NAME, {type: "channel"});
56
+ * await channel.nextVideos();
57
+ * console.log(channel.videos) // first 30 videos
58
+ *
59
+ * let newVideos = await channel.nextVideos();
60
+ * console.log(newVideos) // 30 loaded videos
61
+ * console.log(channel.videos) // first 60 videos
75
62
  *
76
- * TODO: Add continuation support
63
+ * await channel.nextVideos(0); // load the rest of the videos in the channel
64
+ * ```
65
+ *
66
+ * @param count How many time to load the next videos, pass `0` to load all
67
+ *
68
+ * @return New loaded videos
77
69
  */
78
- ChannelCompact.prototype.getPlaylists = function () {
79
- return __awaiter(this, void 0, void 0, function () {
80
- var response, section, gridPlaylistRenderer;
81
- return __generator(this, function (_a) {
82
- switch (_a.label) {
83
- case 0: return [4 /*yield*/, common_1.axios.post(constants_1.I_END_POINT + "/browse", {
84
- browseId: this.id,
85
- params: "EglwbGF5bGlzdHM%3D",
86
- })];
87
- case 1:
88
- response = _a.sent();
89
- section = response.data.contents.twoColumnBrowseResultsRenderer.tabs[2].tabRenderer.content
90
- .sectionListRenderer;
91
- // Has category
92
- if ("shelfRenderer" in section.contents[0].itemSectionRenderer.contents[0]) {
93
- gridPlaylistRenderer = section.contents
94
- .map(function (c) {
95
- return c.itemSectionRenderer.contents[0].shelfRenderer.content
96
- .horizontalListRenderer.items;
97
- })
98
- .flat();
99
- }
100
- else {
101
- gridPlaylistRenderer =
102
- section.contents[0].itemSectionRenderer.contents[0].gridRenderer.items;
103
- }
104
- return [2 /*return*/, gridPlaylistRenderer.map(function (i) {
105
- return new _1.PlaylistCompact().load(i.gridPlaylistRenderer);
106
- })];
107
- }
108
- });
70
+ nextVideos(count = 1) {
71
+ return __awaiter(this, void 0, void 0, function* () {
72
+ const newVideos = [];
73
+ for (let i = 0; i < count || count == 0; i++) {
74
+ if (this.videoContinuation === undefined)
75
+ break;
76
+ const items = yield this.getTabData("videos");
77
+ this.videoContinuation = common_1.getContinuationFromItems(items);
78
+ const videos = common_1.mapFilter(items, "gridVideoRenderer");
79
+ newVideos.push(...videos.map((i) => new _1.VideoCompact({ client: this.client }).load(i)));
80
+ }
81
+ this.videos.push(...newVideos);
82
+ return newVideos;
109
83
  });
110
- };
84
+ }
111
85
  /**
112
- * Load instance attributes from youtube raw data
86
+ * Load next 30 playlists made by the channel, and push the loaded playlists to {@link Channel.playlists}
87
+ *
88
+ * @example
89
+ * ```js
90
+ * const channel = await youtube.findOne(CHANNEL_NAME, {type: "channel"});
91
+ * await channel.nextPlaylists();
92
+ * console.log(channel.playlists) // first 30 playlists
93
+ *
94
+ * let newPlaylists = await channel.nextPlaylists();
95
+ * console.log(newPlaylists) // 30 loaded playlists
96
+ * console.log(channel.playlists) // first 60 playlists
97
+ *
98
+ * await channel.nextPlaylists(0); // load the rest of the playlists in the channel
99
+ * ```
113
100
  *
114
- * @param youtubeRawData raw object from youtubei
101
+ * @param count How many time to load the next playlists, pass `0` to load all
102
+ *
103
+ * @return New loaded playlists
115
104
  */
116
- ChannelCompact.prototype.load = function (youtubeRawData) {
105
+ nextPlaylists(count = 1) {
106
+ return __awaiter(this, void 0, void 0, function* () {
107
+ const newPlaylists = [];
108
+ for (let i = 0; i < count || count == 0; i++) {
109
+ if (this.playlistContinuation === undefined)
110
+ break;
111
+ const items = yield this.getTabData("playlists");
112
+ this.playlistContinuation = common_1.getContinuationFromItems(items);
113
+ const playlists = common_1.mapFilter(items, "gridPlaylistRenderer");
114
+ newPlaylists.push(...playlists.map((i) => new _1.PlaylistCompact({ client: this.client }).load(i)));
115
+ }
116
+ this.playlists.push(...newPlaylists);
117
+ return newPlaylists;
118
+ });
119
+ }
120
+ /** Get tab data from youtube */
121
+ getTabData(name) {
122
+ return __awaiter(this, void 0, void 0, function* () {
123
+ const params = name === "videos" ? "EgZ2aWRlb3M%3D" : "EglwbGF5bGlzdHMgAQ%3D%3D";
124
+ const continuation = name === "videos" ? this.videoContinuation : this.playlistContinuation;
125
+ const response = yield this.client.http.post(`${constants_1.I_END_POINT}/browse`, {
126
+ data: { browseId: this.id, params, continuation },
127
+ });
128
+ return ChannelCompact.parseTabData(name, response.data);
129
+ });
130
+ }
131
+ /** Parse tab data from request, tab name is ignored if it's a continuation data */
132
+ static parseTabData(name, data) {
117
133
  var _a;
118
- var channelId = youtubeRawData.channelId, title = youtubeRawData.title, thumbnail = youtubeRawData.thumbnail, videoCountText = youtubeRawData.videoCountText, navigationEndpoint = youtubeRawData.navigationEndpoint;
119
- this.id = channelId;
120
- this.name = title.simpleText;
121
- this.thumbnail = "https:" + thumbnail.thumbnails[thumbnail.thumbnails.length - 1].url;
122
- this.url = "https://www.youtube.com" + navigationEndpoint.browseEndpoint.canonicalBaseUrl;
123
- this.videoCount = (_a = +(videoCountText === null || videoCountText === void 0 ? void 0 : videoCountText.runs[0].text.replace(/[^0-9]/g, ""))) !== null && _a !== void 0 ? _a : 0;
124
- return this;
125
- };
126
- return ChannelCompact;
127
- }());
134
+ const index = name === "videos" ? 1 : 2;
135
+ return (((_a = data.contents) === null || _a === void 0 ? void 0 : _a.twoColumnBrowseResultsRenderer.tabs[index].tabRenderer.content.sectionListRenderer.contents[0].itemSectionRenderer.contents[0].gridRenderer.items) ||
136
+ data.onResponseReceivedActions[0].appendContinuationItemsAction.continuationItems);
137
+ }
138
+ }
128
139
  exports.default = ChannelCompact;
@@ -0,0 +1,29 @@
1
+ import { Base, ChannelCompact, Video, BaseAttributes } from ".";
2
+ import { YoutubeRawData } from "../common";
3
+ /** @hidden */
4
+ interface ChatAttributes extends BaseAttributes {
5
+ video: Video;
6
+ author: ChannelCompact;
7
+ message: string;
8
+ timestamp: number;
9
+ }
10
+ /** Represents a chat in a live stream */
11
+ export default class Chat extends Base implements ChatAttributes {
12
+ /** The video this chat belongs to */
13
+ video: Video;
14
+ /** The chat's author */
15
+ author: ChannelCompact;
16
+ /** The message of this chat */
17
+ message: string;
18
+ /** Timestamp in usec / microsecond */
19
+ timestamp: number;
20
+ /** @hidden */
21
+ constructor(chat?: Partial<ChatAttributes>);
22
+ /**
23
+ * Load this instance with raw data from Youtube
24
+ *
25
+ * @hidden
26
+ */
27
+ load(data: YoutubeRawData): Chat;
28
+ }
29
+ export {};
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const _1 = require(".");
4
+ /** Represents a chat in a live stream */
5
+ class Chat extends _1.Base {
6
+ /** @hidden */
7
+ constructor(chat = {}) {
8
+ super();
9
+ Object.assign(this, chat);
10
+ }
11
+ /**
12
+ * Load this instance with raw data from Youtube
13
+ *
14
+ * @hidden
15
+ */
16
+ load(data) {
17
+ const { id, message, authorName, authorPhoto, timestampUsec, authorExternalChannelId, } = data;
18
+ // Basic information
19
+ this.id = id;
20
+ this.message = message.runs.map((r) => r.text).join("");
21
+ this.author = new _1.ChannelCompact({
22
+ id: authorExternalChannelId,
23
+ name: authorName.simpleText,
24
+ thumbnails: authorPhoto.thumbnails,
25
+ client: this.client,
26
+ });
27
+ this.timestamp = +timestampUsec;
28
+ return this;
29
+ }
30
+ }
31
+ exports.default = Chat;
@@ -0,0 +1,49 @@
1
+ /// <reference types="node" />
2
+ import { HTTP } from "../common";
3
+ import { Playlist, Video, SearchResult, LiveVideo, Channel, MixPlaylist } from ".";
4
+ import { SearchResultType } from "./SearchResult";
5
+ import { RequestOptions } from "https";
6
+ export declare namespace Client {
7
+ type SearchType = "video" | "channel" | "playlist" | "all";
8
+ type SearchOptions = {
9
+ /** Search type, can be `"video"`, `"channel"`, `"playlist"`, or `"all"` */
10
+ type?: SearchType;
11
+ /** Raw search params to be passed on the request, ignores `type` value if this is provided */
12
+ params?: string;
13
+ };
14
+ type ClientOptions = {
15
+ cookie: string;
16
+ /** Optional options for http client */
17
+ requestOptions: Partial<RequestOptions>;
18
+ /** Optional options passed when sending a request to youtube (context.client) */
19
+ youtubeClientOptions: Record<string, unknown>;
20
+ /** Use Node `https` module, set false to use `http` */
21
+ https: boolean;
22
+ };
23
+ }
24
+ /** Youtube Client */
25
+ export default class Client {
26
+ /** @hidden */
27
+ http: HTTP;
28
+ constructor(options?: Partial<Client.ClientOptions>);
29
+ /**
30
+ * Searches for videos / playlists / channels
31
+ *
32
+ * @param query The search query
33
+ * @param searchOptions Search options
34
+ *
35
+ */
36
+ search<T extends Client.SearchOptions>(query: string, searchOptions?: T): Promise<SearchResult<T["type"]>>;
37
+ /**
38
+ * Search for videos / playlists / channels and returns the first result
39
+ *
40
+ * @return Can be {@link VideoCompact} | {@link PlaylistCompact} | {@link Channel} | `undefined`
41
+ */
42
+ findOne<T extends Client.SearchOptions>(query: string, searchOptions?: Partial<T>): Promise<SearchResultType<T["type"]> | undefined>;
43
+ /** Get playlist information and its videos by playlist id or URL */
44
+ getPlaylist<T extends Playlist | MixPlaylist | undefined>(playlistIdOrUrl: string): Promise<T>;
45
+ /** Get video information by video id or URL */
46
+ getVideo<T extends Video | LiveVideo | undefined>(videoIdOrUrl: string): Promise<T>;
47
+ /** Get channel information by channel id+ */
48
+ getChannel(channelId: string): Promise<Channel | undefined>;
49
+ }
@@ -0,0 +1,97 @@
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 constants_1 = require("../constants");
13
+ const common_1 = require("../common");
14
+ const _1 = require(".");
15
+ /** Youtube Client */
16
+ class Client {
17
+ constructor(options = {}) {
18
+ const fullOptions = Object.assign(Object.assign({ cookie: "", https: true, requestOptions: {} }, options), { youtubeClientOptions: Object.assign({ hl: "en", gl: "US" }, options.youtubeClientOptions) });
19
+ this.http = new common_1.HTTP(fullOptions);
20
+ }
21
+ /**
22
+ * Searches for videos / playlists / channels
23
+ *
24
+ * @param query The search query
25
+ * @param searchOptions Search options
26
+ *
27
+ */
28
+ search(query, searchOptions) {
29
+ return __awaiter(this, void 0, void 0, function* () {
30
+ const options = Object.assign({ type: "all", params: "" }, searchOptions);
31
+ const result = new _1.SearchResult().load(this);
32
+ yield result.init(query, options);
33
+ return result;
34
+ });
35
+ }
36
+ /**
37
+ * Search for videos / playlists / channels and returns the first result
38
+ *
39
+ * @return Can be {@link VideoCompact} | {@link PlaylistCompact} | {@link Channel} | `undefined`
40
+ */
41
+ findOne(query, searchOptions) {
42
+ return __awaiter(this, void 0, void 0, function* () {
43
+ return (yield this.search(query, searchOptions)).shift();
44
+ });
45
+ }
46
+ /** Get playlist information and its videos by playlist id or URL */
47
+ getPlaylist(playlistIdOrUrl) {
48
+ var _a, _b, _c;
49
+ return __awaiter(this, void 0, void 0, function* () {
50
+ const playlistId = common_1.getQueryParameter(playlistIdOrUrl, "list");
51
+ if (playlistId.startsWith("RD")) {
52
+ const response = yield this.http.post(`${constants_1.I_END_POINT}/next`, {
53
+ data: { playlistId },
54
+ });
55
+ if (response.data.error) {
56
+ return undefined;
57
+ }
58
+ return new _1.MixPlaylist({ client: this }).load(response.data);
59
+ }
60
+ const response = yield this.http.post(`${constants_1.I_END_POINT}/browse`, {
61
+ data: { browseId: `VL${playlistId}` },
62
+ });
63
+ if (response.data.error || ((_c = (_b = (_a = response.data.alerts) === null || _a === void 0 ? void 0 : _a.shift()) === null || _b === void 0 ? void 0 : _b.alertRenderer) === null || _c === void 0 ? void 0 : _c.type) === "ERROR") {
64
+ return undefined;
65
+ }
66
+ return new _1.Playlist({ client: this }).load(response.data);
67
+ });
68
+ }
69
+ /** Get video information by video id or URL */
70
+ getVideo(videoIdOrUrl) {
71
+ return __awaiter(this, void 0, void 0, function* () {
72
+ const videoId = common_1.getQueryParameter(videoIdOrUrl, "v");
73
+ const response = yield this.http.get(`${constants_1.WATCH_END_POINT}`, {
74
+ params: { v: videoId, pbj: "1" },
75
+ });
76
+ if (!response.data[3].response.contents)
77
+ return undefined;
78
+ return (!response.data[2].playerResponse.playabilityStatus.liveStreamability
79
+ ? new _1.Video({ client: this }).load(response.data)
80
+ : new _1.LiveVideo({ client: this }).load(response.data));
81
+ });
82
+ }
83
+ /** Get channel information by channel id+ */
84
+ getChannel(channelId) {
85
+ var _a, _b, _c;
86
+ return __awaiter(this, void 0, void 0, function* () {
87
+ const response = yield this.http.post(`${constants_1.I_END_POINT}/browse`, {
88
+ data: { browseId: channelId },
89
+ });
90
+ if (response.data.error || ((_c = (_b = (_a = response.data.alerts) === null || _a === void 0 ? void 0 : _a.shift()) === null || _b === void 0 ? void 0 : _b.alertRenderer) === null || _c === void 0 ? void 0 : _c.type) === "ERROR") {
91
+ return undefined;
92
+ }
93
+ return new _1.Channel({ client: this }).load(response.data);
94
+ });
95
+ }
96
+ }
97
+ exports.default = Client;
@@ -0,0 +1,50 @@
1
+ import { Base, ChannelCompact, Video, BaseAttributes, Reply } from ".";
2
+ import { YoutubeRawData } from "../common";
3
+ /** @hidden */
4
+ interface CommentAttributes extends BaseAttributes {
5
+ video: Video;
6
+ author: ChannelCompact;
7
+ content: string;
8
+ publishDate: string;
9
+ likeCount: number;
10
+ isAuthorChannelOwner: boolean;
11
+ isPinnedComment: boolean;
12
+ replyCount: number;
13
+ replyContinuation?: string;
14
+ }
15
+ /** Represents a Comment / Reply */
16
+ export default class Comment extends Base implements CommentAttributes {
17
+ /** The video this comment belongs to */
18
+ video: Video;
19
+ /** The comment's author */
20
+ author: ChannelCompact;
21
+ /** The content of this comment */
22
+ content: string;
23
+ /** The publish date of the comment */
24
+ publishDate: string;
25
+ /** How many likes does this comment have */
26
+ likeCount: number;
27
+ /** Whether the comment is posted by the video uploader / owner */
28
+ isAuthorChannelOwner: boolean;
29
+ /** Whether the comment is pinned */
30
+ isPinnedComment: boolean;
31
+ /** Comment's reply count */
32
+ replyCount: number;
33
+ /** Comment's loaded replies */
34
+ replies: Reply[];
35
+ /** Current continuation token to load next replies */
36
+ replyContinuation?: string;
37
+ /** @hidden */
38
+ constructor(comment?: Partial<CommentAttributes>);
39
+ /**
40
+ * Load this instance with raw data from Youtube
41
+ *
42
+ * @hidden
43
+ */
44
+ load(data: YoutubeRawData): Comment;
45
+ /** URL to the video with this comment being highlighted (appears on top of the comment section) */
46
+ get url(): string;
47
+ /** Load next replies of the comment */
48
+ nextReplies(count?: number): Promise<Reply[]>;
49
+ }
50
+ export {};
@@ -0,0 +1,84 @@
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 Comment / Reply */
16
+ class Comment extends _1.Base {
17
+ /** @hidden */
18
+ constructor(comment = {}) {
19
+ super();
20
+ /** Comment's loaded replies */
21
+ this.replies = [];
22
+ Object.assign(this, comment);
23
+ }
24
+ /**
25
+ * Load this instance with raw data from Youtube
26
+ *
27
+ * @hidden
28
+ */
29
+ load(data) {
30
+ const { authorText, authorThumbnail, authorEndpoint, contentText, publishedTimeText, commentId, voteCount, authorIsChannelOwner, pinnedCommentBadge, replyCount, } = data.comment.commentRenderer;
31
+ // Basic information
32
+ this.id = commentId;
33
+ this.content = contentText.runs.map((r) => r.text).join("");
34
+ this.publishDate = publishedTimeText.runs.shift().text;
35
+ this.likeCount = +((voteCount === null || voteCount === void 0 ? void 0 : voteCount.simpleText) || 0);
36
+ this.isAuthorChannelOwner = authorIsChannelOwner;
37
+ this.isPinnedComment = !!pinnedCommentBadge;
38
+ this.replyCount = replyCount;
39
+ // Reply Continuation
40
+ this.replies = [];
41
+ this.replyContinuation = data.replies
42
+ ? common_1.getContinuationFromItems(data.replies.commentRepliesRenderer.contents)
43
+ : undefined;
44
+ // Author
45
+ const { browseId } = authorEndpoint.browseEndpoint;
46
+ this.author = new _1.ChannelCompact({
47
+ id: browseId,
48
+ name: authorText.simpleText,
49
+ thumbnails: new _1.Thumbnails().load(authorThumbnail.thumbnails),
50
+ client: this.client,
51
+ });
52
+ return this;
53
+ }
54
+ /** URL to the video with this comment being highlighted (appears on top of the comment section) */
55
+ get url() {
56
+ return `https://www.youtube.com/watch?v=${this.video.id}&lc=${this.id}`;
57
+ }
58
+ /** Load next replies of the comment */
59
+ nextReplies(count = 1) {
60
+ return __awaiter(this, void 0, void 0, function* () {
61
+ const newReplies = [];
62
+ for (let i = 0; i < count || count == 0; i++) {
63
+ if (!this.replyContinuation)
64
+ break;
65
+ // Send request
66
+ const response = yield this.client.http.post(`${constants_1.I_END_POINT}/next`, {
67
+ data: { continuation: this.replyContinuation },
68
+ });
69
+ const continuationItems = response.data.onResponseReceivedEndpoints[0].appendContinuationItemsAction
70
+ .continuationItems;
71
+ this.replyContinuation = common_1.getContinuationFromItems(continuationItems, [
72
+ "button",
73
+ "buttonRenderer",
74
+ "command",
75
+ ]);
76
+ const replies = common_1.mapFilter(continuationItems, "commentRenderer");
77
+ newReplies.push(...replies.map((i) => new _1.Reply({ video: this.video, comment: this, client: this.client }).load(i)));
78
+ }
79
+ this.replies.push(...newReplies);
80
+ return newReplies;
81
+ });
82
+ }
83
+ }
84
+ exports.default = Comment;
@@ -0,0 +1,47 @@
1
+ import { YoutubeRawData } from "../common";
2
+ import { Chat, BaseVideo, BaseVideoAttributes } from ".";
3
+ /** @hidden */
4
+ interface LiveVideoAttributes extends BaseVideoAttributes {
5
+ watchingCount: number;
6
+ chatContinuation?: string;
7
+ }
8
+ interface LiveVideoEvents {
9
+ chat: (chat: Chat) => void;
10
+ }
11
+ declare interface LiveVideo {
12
+ on<T extends keyof LiveVideoEvents>(event: T, listener: LiveVideoEvents[T]): AsyncIterableIterator<any>;
13
+ emit<T extends keyof LiveVideoEvents>(event: T, ...args: Parameters<LiveVideoEvents[T]>): boolean;
14
+ }
15
+ /** Represents a video that's currently live, usually returned from `client.getVideo()` */
16
+ declare class LiveVideo extends BaseVideo implements LiveVideoAttributes {
17
+ /** Number of people who's watching the live stream right now */
18
+ watchingCount: number;
19
+ /** Current continuation token to load next chat */
20
+ chatContinuation: string;
21
+ private _delay;
22
+ private _chatRequestPoolingTimeout;
23
+ private _timeoutMs;
24
+ private _isChatPlaying;
25
+ private _chatQueue;
26
+ /** @hidden */
27
+ constructor(video?: Partial<LiveVideoAttributes>);
28
+ /**
29
+ * Load this instance with raw data from Youtube
30
+ *
31
+ * @hidden
32
+ */
33
+ load(data: YoutubeRawData): LiveVideo;
34
+ /**
35
+ * Start polling for get live chat request
36
+ *
37
+ * @param delay chat delay in millisecond
38
+ */
39
+ playChat(delay?: number): void;
40
+ /** Stop request polling for live chat */
41
+ stopChat(): void;
42
+ /** Start request polling */
43
+ private pollChatContinuation;
44
+ /** Parse chat data from Youtube and add to chatQueue */
45
+ private parseChat;
46
+ }
47
+ export default LiveVideo;