youtubei 1.3.7 → 1.4.0

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 (57) hide show
  1. package/dist/cjs/common/shared/HTTP/HTTP.js +18 -7
  2. package/dist/cjs/youtube/BaseVideo/BaseVideoParser.js +7 -2
  3. package/dist/cjs/youtube/BaseVideo/VideoCaptions.js +81 -0
  4. package/dist/cjs/youtube/BaseVideo/index.js +1 -0
  5. package/dist/cjs/youtube/Caption/Caption.js +17 -0
  6. package/dist/cjs/youtube/Caption/CaptionLanguage.js +17 -0
  7. package/dist/cjs/youtube/{Transcript/proto → Caption}/index.js +2 -1
  8. package/dist/cjs/youtube/Client/Client.js +9 -13
  9. package/dist/cjs/youtube/Comment/CommentParser.js +11 -12
  10. package/dist/cjs/youtube/LiveVideo/LiveVideo.js +15 -15
  11. package/dist/cjs/youtube/Video/Video.js +4 -6
  12. package/dist/cjs/youtube/Video/VideoParser.js +3 -3
  13. package/dist/cjs/youtube/VideoCompact/VideoCompact.js +2 -2
  14. package/dist/cjs/youtube/VideoCompact/VideoCompactParser.js +4 -2
  15. package/dist/cjs/youtube/index.js +0 -1
  16. package/dist/esm/common/shared/HTTP/HTTP.js +59 -12
  17. package/dist/esm/youtube/BaseVideo/BaseVideoParser.js +7 -2
  18. package/dist/esm/youtube/BaseVideo/VideoCaptions.js +133 -0
  19. package/dist/esm/youtube/BaseVideo/index.js +1 -0
  20. package/dist/esm/youtube/Caption/Caption.js +19 -0
  21. package/dist/esm/youtube/Caption/CaptionLanguage.js +15 -0
  22. package/dist/esm/youtube/Caption/index.js +2 -0
  23. package/dist/esm/youtube/Client/Client.js +13 -18
  24. package/dist/esm/youtube/Comment/CommentParser.js +11 -12
  25. package/dist/esm/youtube/LiveVideo/LiveVideo.js +15 -15
  26. package/dist/esm/youtube/Video/Video.js +5 -7
  27. package/dist/esm/youtube/Video/VideoParser.js +4 -4
  28. package/dist/esm/youtube/VideoCompact/VideoCompact.js +2 -2
  29. package/dist/esm/youtube/VideoCompact/VideoCompactParser.js +4 -2
  30. package/dist/esm/youtube/index.js +0 -1
  31. package/dist/typings/common/shared/HTTP/HTTP.d.ts +2 -2
  32. package/dist/typings/youtube/BaseVideo/BaseVideo.d.ts +3 -0
  33. package/dist/typings/youtube/BaseVideo/VideoCaptions.d.ts +40 -0
  34. package/dist/typings/youtube/BaseVideo/index.d.ts +1 -0
  35. package/dist/typings/youtube/Caption/Caption.d.ts +22 -0
  36. package/dist/typings/youtube/Caption/CaptionLanguage.d.ts +30 -0
  37. package/dist/typings/youtube/Caption/index.d.ts +2 -0
  38. package/dist/typings/youtube/Client/Client.d.ts +8 -3
  39. package/dist/typings/youtube/LiveVideo/LiveVideo.d.ts +5 -5
  40. package/dist/typings/youtube/Video/Video.d.ts +3 -6
  41. package/dist/typings/youtube/VideoCompact/VideoCompact.d.ts +2 -2
  42. package/dist/typings/youtube/index.d.ts +0 -1
  43. package/package.json +1 -1
  44. package/dist/cjs/youtube/Transcript/Transcript.js +0 -27
  45. package/dist/cjs/youtube/Transcript/TranscriptParser.js +0 -13
  46. package/dist/cjs/youtube/Transcript/index.js +0 -15
  47. package/dist/cjs/youtube/Transcript/proto/TranscriptParamsProto.js +0 -12
  48. package/dist/esm/youtube/Transcript/Transcript.js +0 -29
  49. package/dist/esm/youtube/Transcript/TranscriptParser.js +0 -13
  50. package/dist/esm/youtube/Transcript/index.js +0 -3
  51. package/dist/esm/youtube/Transcript/proto/TranscriptParamsProto.js +0 -2
  52. package/dist/esm/youtube/Transcript/proto/index.js +0 -1
  53. package/dist/typings/youtube/Transcript/Transcript.d.ts +0 -29
  54. package/dist/typings/youtube/Transcript/TranscriptParser.d.ts +0 -5
  55. package/dist/typings/youtube/Transcript/index.d.ts +0 -3
  56. package/dist/typings/youtube/Transcript/proto/TranscriptParamsProto.d.ts +0 -7
  57. package/dist/typings/youtube/Transcript/proto/index.d.ts +0 -1
@@ -34,23 +34,34 @@ class HTTP {
34
34
  this.defaultFetchOptions = options.fetchOptions || {};
35
35
  this.defaultClientOptions = options.youtubeClientOptions || {};
36
36
  }
37
- get(url, options) {
37
+ get(path, options) {
38
38
  return __awaiter(this, void 0, void 0, function* () {
39
- return yield this.request(url, Object.assign(Object.assign({}, options), { params: Object.assign({ prettyPrint: "false" }, options === null || options === void 0 ? void 0 : options.params), method: "GET" }));
39
+ return yield this.request(path, Object.assign(Object.assign({}, options), { params: Object.assign({ prettyPrint: "false" }, options === null || options === void 0 ? void 0 : options.params), method: "GET" }));
40
40
  });
41
41
  }
42
- post(url, options) {
42
+ post(path, options) {
43
43
  return __awaiter(this, void 0, void 0, function* () {
44
- return yield this.request(url, Object.assign(Object.assign({}, options), { method: "POST", params: Object.assign({ key: this.apiKey, prettyPrint: "false" }, options === null || options === void 0 ? void 0 : options.params), data: Object.assign({ context: {
44
+ return yield this.request(path, Object.assign(Object.assign({}, options), { method: "POST", params: Object.assign({ key: this.apiKey, prettyPrint: "false" }, options === null || options === void 0 ? void 0 : options.params), data: Object.assign({ context: {
45
45
  client: Object.assign({ clientName: this.clientName, clientVersion: this.clientVersion }, this.defaultClientOptions),
46
46
  } }, options === null || options === void 0 ? void 0 : options.data) }));
47
47
  });
48
48
  }
49
- request(url, partialOptions) {
49
+ request(path, partialOptions) {
50
50
  return __awaiter(this, void 0, void 0, function* () {
51
51
  const options = Object.assign(Object.assign(Object.assign({}, partialOptions), this.defaultFetchOptions), { headers: Object.assign(Object.assign(Object.assign(Object.assign({}, this.defaultHeaders), { cookie: this.cookie, referer: `https://${this.baseUrl}/` }), partialOptions.headers), this.defaultFetchOptions.headers), body: partialOptions.data ? JSON.stringify(partialOptions.data) : undefined });
52
- const finalUrl = `https://${this.baseUrl}/${url}?${new url_1.URLSearchParams(partialOptions.params)}`;
53
- const response = yield node_fetch_1.default(finalUrl, options);
52
+ // if URL is a full URL, ignore baseUrl
53
+ let urlString;
54
+ if (path.startsWith("http")) {
55
+ const url = new URL(path);
56
+ for (const [key, value] of Object.entries(partialOptions.params || {})) {
57
+ url.searchParams.set(key, value);
58
+ }
59
+ urlString = url.toString();
60
+ }
61
+ else {
62
+ urlString = `https://${this.baseUrl}/${path}?${new url_1.URLSearchParams(partialOptions.params)}`;
63
+ }
64
+ const response = yield node_fetch_1.default(urlString, options);
54
65
  const data = yield response.json();
55
66
  this.parseCookie(response);
56
67
  return { data };
@@ -5,6 +5,7 @@ const common_1 = require("../../common");
5
5
  const BaseChannel_1 = require("../BaseChannel");
6
6
  const PlaylistCompact_1 = require("../PlaylistCompact");
7
7
  const VideoCompact_1 = require("../VideoCompact");
8
+ const VideoCaptions_1 = require("./VideoCaptions");
8
9
  class BaseVideoParser {
9
10
  static loadBaseVideo(target, data) {
10
11
  var _a, _b, _c;
@@ -38,6 +39,10 @@ class BaseVideoParser {
38
39
  target.related.items = BaseVideoParser.parseRelatedFromSecondaryContent(secondaryContents, target.client);
39
40
  target.related.continuation = common_1.getContinuationFromItems(secondaryContents);
40
41
  }
42
+ // captions
43
+ if (videoInfo.captions) {
44
+ target.captions = new VideoCaptions_1.VideoCaptions({ client: target.client, video: target }).load(videoInfo.captions.playerCaptionsTracklistRenderer);
45
+ }
41
46
  return target;
42
47
  }
43
48
  static parseRelated(data, client) {
@@ -53,8 +58,8 @@ class BaseVideoParser {
53
58
  const primaryInfo = contents.find((c) => "videoPrimaryInfoRenderer" in c)
54
59
  .videoPrimaryInfoRenderer;
55
60
  const secondaryInfo = contents.find((c) => "videoSecondaryInfoRenderer" in c).videoSecondaryInfoRenderer;
56
- const videoDetails = data.playerResponse.videoDetails;
57
- return Object.assign(Object.assign(Object.assign({}, secondaryInfo), primaryInfo), { videoDetails });
61
+ const { videoDetails, captions } = data.playerResponse;
62
+ return Object.assign(Object.assign(Object.assign({}, secondaryInfo), primaryInfo), { videoDetails, captions });
58
63
  }
59
64
  static parseCompactRenderer(data, client) {
60
65
  if ("compactVideoRenderer" in data) {
@@ -0,0 +1,81 @@
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.VideoCaptions = void 0;
13
+ const Base_1 = require("../Base");
14
+ const Caption_1 = require("../Caption");
15
+ /**
16
+ * Captions of a video
17
+ *
18
+ * @example
19
+ * ```js
20
+ *
21
+ * console.log(result.captions.languages.map((l) => `${l.code} - ${l.name}`)); // printing out available languages for captions
22
+ *
23
+ * console.log(await result.captions.get("en")); // printing out captions of a specific language using language code
24
+ * ```
25
+ */
26
+ class VideoCaptions extends Base_1.Base {
27
+ /** @hidden */
28
+ constructor({ video, client }) {
29
+ super(client);
30
+ this.video = video;
31
+ this.languages = [];
32
+ }
33
+ /**
34
+ * Load this instance with raw data from Youtube
35
+ *
36
+ * @hidden
37
+ */
38
+ load(data) {
39
+ const { captionTracks } = data;
40
+ if (captionTracks) {
41
+ this.languages = captionTracks.map((track) => new Caption_1.CaptionLanguage({
42
+ captions: this,
43
+ name: track.name.simpleText,
44
+ code: track.languageCode,
45
+ isTranslatable: !!track.isTranslatable,
46
+ url: track.baseUrl,
47
+ }));
48
+ }
49
+ return this;
50
+ }
51
+ /**
52
+ * Get captions of a specific language or a translation of a specific language
53
+ */
54
+ get(languageCode, translationLanguageCode) {
55
+ var _a, _b;
56
+ return __awaiter(this, void 0, void 0, function* () {
57
+ if (!languageCode)
58
+ languageCode = this.client.options.youtubeClientOptions.hl;
59
+ const url = (_a = this.languages.find((l) => l.code.toUpperCase() === (languageCode === null || languageCode === void 0 ? void 0 : languageCode.toUpperCase()))) === null || _a === void 0 ? void 0 : _a.url;
60
+ if (!url)
61
+ return undefined;
62
+ const params = { fmt: "json3" };
63
+ if (translationLanguageCode)
64
+ params["tlang"] = translationLanguageCode;
65
+ const response = yield this.client.http.get(url, { params });
66
+ const captions = (_b = response.data.events) === null || _b === void 0 ? void 0 : _b.reduce((curr, e) => {
67
+ var _a;
68
+ if (e.segs === undefined)
69
+ return curr;
70
+ curr.push(new Caption_1.Caption({
71
+ duration: e.dDurationMs,
72
+ start: e.tStartMs,
73
+ text: (_a = e.segs) === null || _a === void 0 ? void 0 : _a.map((s) => Object.values(s).join("")).join(" "),
74
+ }));
75
+ return curr;
76
+ }, []);
77
+ return captions;
78
+ });
79
+ }
80
+ }
81
+ exports.VideoCaptions = VideoCaptions;
@@ -12,4 +12,5 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
12
12
  Object.defineProperty(exports, "__esModule", { value: true });
13
13
  __exportStar(require("./BaseVideo"), exports);
14
14
  __exportStar(require("./BaseVideoParser"), exports);
15
+ __exportStar(require("./VideoCaptions"), exports);
15
16
  __exportStar(require("./VideoRelated"), exports);
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Caption = void 0;
4
+ /**
5
+ * Represent a single video caption entry
6
+ */
7
+ class Caption {
8
+ /** @hidden */
9
+ constructor(attr) {
10
+ Object.assign(this, attr);
11
+ }
12
+ /** transcript end time in milliseconds */
13
+ get end() {
14
+ return this.start + this.duration;
15
+ }
16
+ }
17
+ exports.Caption = Caption;
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CaptionLanguage = void 0;
4
+ /**
5
+ * Represents a caption language option
6
+ */
7
+ class CaptionLanguage {
8
+ /** @hidden */
9
+ constructor(attr) {
10
+ Object.assign(this, attr);
11
+ }
12
+ /** Get the captions of this language using the url */
13
+ get(translationLanguageCode) {
14
+ return this.captions.get(this.code, translationLanguageCode);
15
+ }
16
+ }
17
+ exports.CaptionLanguage = CaptionLanguage;
@@ -10,4 +10,5 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
10
10
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
11
11
  };
12
12
  Object.defineProperty(exports, "__esModule", { value: true });
13
- __exportStar(require("./TranscriptParamsProto"), exports);
13
+ __exportStar(require("./Caption"), exports);
14
+ __exportStar(require("./CaptionLanguage"), exports);
@@ -16,14 +16,13 @@ const LiveVideo_1 = require("../LiveVideo");
16
16
  const MixPlaylist_1 = require("../MixPlaylist");
17
17
  const Playlist_1 = require("../Playlist");
18
18
  const SearchResult_1 = require("../SearchResult");
19
- const Transcript_1 = require("../Transcript");
20
19
  const Video_1 = require("../Video");
21
20
  const constants_1 = require("../constants");
22
21
  /** Youtube Client */
23
22
  class Client {
24
23
  constructor(options = {}) {
25
- const fullOptions = Object.assign(Object.assign({ initialCookie: "", fetchOptions: {} }, options), { youtubeClientOptions: Object.assign({ hl: "en", gl: "US" }, options.youtubeClientOptions) });
26
- this.http = new common_1.HTTP(Object.assign({ apiKey: constants_1.INNERTUBE_API_KEY, baseUrl: constants_1.BASE_URL, clientName: constants_1.INNERTUBE_CLIENT_NAME, clientVersion: constants_1.INNERTUBE_CLIENT_VERSION }, fullOptions));
24
+ this.options = Object.assign(Object.assign({ initialCookie: "", fetchOptions: {} }, options), { youtubeClientOptions: Object.assign({ hl: "en", gl: "US" }, options.youtubeClientOptions) });
25
+ this.http = new common_1.HTTP(Object.assign({ apiKey: constants_1.INNERTUBE_API_KEY, baseUrl: constants_1.BASE_URL, clientName: constants_1.INNERTUBE_CLIENT_NAME, clientVersion: constants_1.INNERTUBE_CLIENT_VERSION }, this.options));
27
26
  }
28
27
  /**
29
28
  * Searches for videos / playlists / channels
@@ -103,17 +102,14 @@ class Client {
103
102
  return new Channel_1.Channel({ client: this }).load(response.data);
104
103
  });
105
104
  }
106
- getVideoTranscript(videoId) {
105
+ /**
106
+ * Get video transcript / caption by video id
107
+ */
108
+ getVideoTranscript(videoId, languageCode) {
109
+ var _a;
107
110
  return __awaiter(this, void 0, void 0, function* () {
108
- const bufferParams = Transcript_1.TranscriptParamsProto.encode({ videoId }).finish();
109
- const response = yield this.http.post(`${constants_1.I_END_POINT}/get_transcript`, {
110
- data: { params: Buffer.from(bufferParams).toString("base64") },
111
- });
112
- if (!response.data.actions)
113
- return undefined;
114
- return response.data.actions[0].updateEngagementPanelAction.content.transcriptRenderer.body.transcriptBodyRenderer.cueGroups
115
- .map((t) => t.transcriptCueGroupRenderer.cues[0].transcriptCueRenderer)
116
- .map((t) => new Transcript_1.Transcript().load(t));
111
+ const video = yield this.getVideo(videoId);
112
+ return (_a = video === null || video === void 0 ? void 0 : video.captions) === null || _a === void 0 ? void 0 : _a.get(languageCode);
117
113
  });
118
114
  }
119
115
  }
@@ -6,25 +6,24 @@ const BaseChannel_1 = require("../BaseChannel");
6
6
  const Reply_1 = require("../Reply");
7
7
  class CommentParser {
8
8
  static loadComment(target, data) {
9
- const { authorText, authorThumbnail, authorEndpoint, contentText, publishedTimeText, commentId, voteCount, authorIsChannelOwner, pinnedCommentBadge, replyCount, } = data.comment.commentRenderer;
9
+ const { properties, toolbar, author, avatar } = data;
10
10
  // Basic information
11
- target.id = commentId;
12
- target.content = contentText.runs.map((r) => r.text).join("");
13
- target.publishDate = publishedTimeText.runs.shift().text;
14
- target.likeCount = +((voteCount === null || voteCount === void 0 ? void 0 : voteCount.simpleText) || 0);
15
- target.isAuthorChannelOwner = authorIsChannelOwner;
16
- target.isPinned = !!pinnedCommentBadge;
17
- target.replyCount = replyCount;
11
+ target.id = properties.commentId;
12
+ target.content = properties.content.content;
13
+ target.publishDate = properties.publishedTime;
14
+ target.likeCount = +toolbar.likeCountLiked; // probably broken
15
+ target.isAuthorChannelOwner = !!author.isCreator;
16
+ target.isPinned = false; // TODO fix this
17
+ target.replyCount = +toolbar.replyCount;
18
18
  // Reply Continuation
19
19
  target.replies.continuation = data.replies
20
20
  ? common_1.getContinuationFromItems(data.replies.commentRepliesRenderer.contents)
21
21
  : undefined;
22
22
  // Author
23
- const { browseId } = authorEndpoint.browseEndpoint;
24
23
  target.author = new BaseChannel_1.BaseChannel({
25
- id: browseId,
26
- name: authorText.simpleText,
27
- thumbnails: new common_1.Thumbnails().load(authorThumbnail.thumbnails),
24
+ id: author.id,
25
+ name: author.displayName,
26
+ thumbnails: new common_1.Thumbnails().load(avatar.image.sources),
28
27
  client: target.client,
29
28
  });
30
29
  return target;
@@ -21,10 +21,10 @@ class LiveVideo extends BaseVideo_1.BaseVideo {
21
21
  /** @hidden */
22
22
  constructor(attr) {
23
23
  super(attr);
24
- this._delay = 0;
25
- this._timeoutMs = 0;
26
- this._isChatPlaying = false;
27
- this._chatQueue = [];
24
+ this.delay = 0;
25
+ this.timeoutMs = 0;
26
+ this.isChatPlaying = false;
27
+ this.chatQueue = [];
28
28
  Object.assign(this, attr);
29
29
  }
30
30
  /**
@@ -43,18 +43,18 @@ class LiveVideo extends BaseVideo_1.BaseVideo {
43
43
  * @param delay chat delay in millisecond
44
44
  */
45
45
  playChat(delay = 0) {
46
- if (this._isChatPlaying)
46
+ if (this.isChatPlaying)
47
47
  return;
48
- this._delay = delay;
49
- this._isChatPlaying = true;
48
+ this.delay = delay;
49
+ this.isChatPlaying = true;
50
50
  this.pollChatContinuation();
51
51
  }
52
52
  /** Stop request polling for live chat */
53
53
  stopChat() {
54
- if (!this._chatRequestPoolingTimeout)
54
+ if (!this.chatRequestPoolingTimeout)
55
55
  return;
56
- this._isChatPlaying = false;
57
- clearTimeout(this._chatRequestPoolingTimeout);
56
+ this.isChatPlaying = false;
57
+ clearTimeout(this.chatRequestPoolingTimeout);
58
58
  }
59
59
  /** Start request polling */
60
60
  pollChatContinuation() {
@@ -67,16 +67,16 @@ class LiveVideo extends BaseVideo_1.BaseVideo {
67
67
  const chats = LiveVideoParser_1.LiveVideoParser.parseChats(response.data);
68
68
  for (const c of chats) {
69
69
  const chat = new Chat_1.Chat({ client: this.client }).load(c);
70
- if (this._chatQueue.find((c) => c.id === chat.id))
70
+ if (this.chatQueue.find((c) => c.id === chat.id))
71
71
  continue;
72
- this._chatQueue.push(chat);
73
- const timeout = chat.timestamp / 1000 - (new Date().getTime() - this._delay);
72
+ this.chatQueue.push(chat);
73
+ const timeout = chat.timestamp / 1000 - (new Date().getTime() - this.delay);
74
74
  setTimeout(() => this.emit("chat", chat), timeout);
75
75
  }
76
76
  const { timeout, continuation } = LiveVideoParser_1.LiveVideoParser.parseContinuation(response.data);
77
- this._timeoutMs = timeout;
77
+ this.timeoutMs = timeout;
78
78
  this.chatContinuation = continuation;
79
- this._chatRequestPoolingTimeout = setTimeout(() => this.pollChatContinuation(), this._timeoutMs);
79
+ this.chatRequestPoolingTimeout = setTimeout(() => this.pollChatContinuation(), this.timeoutMs);
80
80
  });
81
81
  }
82
82
  }
@@ -34,14 +34,12 @@ class Video extends BaseVideo_1.BaseVideo {
34
34
  /**
35
35
  * Get Video transcript (if exists)
36
36
  *
37
- * Equivalent to
38
- * ```js
39
- * client.getVideoTranscript(video.id);
40
- * ```
37
+ * @deprecated use `video.captions.get()` instead
41
38
  */
42
- getTranscript() {
39
+ getTranscript(languageCode) {
40
+ var _a;
43
41
  return __awaiter(this, void 0, void 0, function* () {
44
- return this.client.getVideoTranscript(this.id);
42
+ return (_a = this.captions) === null || _a === void 0 ? void 0 : _a.get(languageCode);
45
43
  });
46
44
  }
47
45
  }
@@ -23,9 +23,9 @@ class VideoParser {
23
23
  return target;
24
24
  }
25
25
  static parseComments(data, video) {
26
- const endpoints = data.onResponseReceivedEndpoints.at(-1);
27
- const continuationItems = (endpoints.reloadContinuationItemsCommand || endpoints.appendContinuationItemsAction).continuationItems;
28
- const comments = common_1.mapFilter(continuationItems, "commentThreadRenderer");
26
+ const comments = data.frameworkUpdates.entityBatchUpdate.mutations
27
+ .filter((m) => m.payload.commentEntityPayload)
28
+ .map((m) => m.payload.commentEntityPayload);
29
29
  return comments.map((c) => new Comment_1.Comment({ video, client: video.client }).load(c));
30
30
  }
31
31
  static parseCommentContinuation(data) {
@@ -53,9 +53,9 @@ class VideoCompact extends Base_1.Base {
53
53
  * client.getVideoTranscript(video.id);
54
54
  * ```
55
55
  */
56
- getTranscript() {
56
+ getTranscript(languageCode) {
57
57
  return __awaiter(this, void 0, void 0, function* () {
58
- return this.client.getVideoTranscript(this.id);
58
+ return this.client.getVideoTranscript(this.id, languageCode);
59
59
  });
60
60
  }
61
61
  }
@@ -5,7 +5,7 @@ const common_1 = require("../../common");
5
5
  const BaseChannel_1 = require("../BaseChannel");
6
6
  class VideoCompactParser {
7
7
  static loadVideoCompact(target, data) {
8
- var _a, _b, _c, _d;
8
+ var _a, _b, _c, _d, _e;
9
9
  const { videoId, title, headline, lengthText, thumbnail, ownerText, shortBylineText, publishedTimeText, viewCountText, badges, thumbnailOverlays, channelThumbnailSupportedRenderers, detailedMetadataSnippets, } = data;
10
10
  target.id = videoId;
11
11
  target.title = headline
@@ -18,7 +18,9 @@ class VideoCompactParser {
18
18
  target.duration =
19
19
  common_1.getDuration((lengthText === null || lengthText === void 0 ? void 0 : lengthText.simpleText) || ((_d = thumbnailOverlays === null || thumbnailOverlays === void 0 ? void 0 : thumbnailOverlays[0].thumbnailOverlayTimeStatusRenderer) === null || _d === void 0 ? void 0 : _d.text.simpleText) ||
20
20
  "") || null;
21
- target.isLive = !!((badges === null || badges === void 0 ? void 0 : badges[0].metadataBadgeRenderer.style) === "BADGE_STYLE_TYPE_LIVE_NOW");
21
+ target.isLive =
22
+ !!((badges === null || badges === void 0 ? void 0 : badges[0].metadataBadgeRenderer.style) === "BADGE_STYLE_TYPE_LIVE_NOW") ||
23
+ ((_e = thumbnailOverlays === null || thumbnailOverlays === void 0 ? void 0 : thumbnailOverlays[0].thumbnailOverlayTimeStatusRenderer) === null || _e === void 0 ? void 0 : _e.style) === "LIVE";
22
24
  // Channel
23
25
  if (ownerText || shortBylineText) {
24
26
  const browseEndpoint = (ownerText || shortBylineText).runs[0].navigationEndpoint
@@ -24,6 +24,5 @@ __exportStar(require("./Playlist"), exports);
24
24
  __exportStar(require("./PlaylistCompact"), exports);
25
25
  __exportStar(require("./Reply"), exports);
26
26
  __exportStar(require("./SearchResult"), exports);
27
- __exportStar(require("./Transcript"), exports);
28
27
  __exportStar(require("./Video"), exports);
29
28
  __exportStar(require("./VideoCompact"), exports);
@@ -45,6 +45,33 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
45
45
  if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
46
46
  }
47
47
  };
48
+ var __values = (this && this.__values) || function(o) {
49
+ var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
50
+ if (m) return m.call(o);
51
+ if (o && typeof o.length === "number") return {
52
+ next: function () {
53
+ if (o && i >= o.length) o = void 0;
54
+ return { value: o && o[i++], done: !o };
55
+ }
56
+ };
57
+ throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
58
+ };
59
+ var __read = (this && this.__read) || function (o, n) {
60
+ var m = typeof Symbol === "function" && o[Symbol.iterator];
61
+ if (!m) return o;
62
+ var i = m.call(o), r, ar = [], e;
63
+ try {
64
+ while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
65
+ }
66
+ catch (error) { e = { error: error }; }
67
+ finally {
68
+ try {
69
+ if (r && !r.done && (m = i["return"])) m.call(i);
70
+ }
71
+ finally { if (e) throw e.error; }
72
+ }
73
+ return ar;
74
+ };
48
75
  import fetch from "node-fetch";
49
76
  import { URLSearchParams } from "url";
50
77
  /**
@@ -66,21 +93,21 @@ var HTTP = /** @class */ (function () {
66
93
  this.defaultFetchOptions = options.fetchOptions || {};
67
94
  this.defaultClientOptions = options.youtubeClientOptions || {};
68
95
  }
69
- HTTP.prototype.get = function (url, options) {
96
+ HTTP.prototype.get = function (path, options) {
70
97
  return __awaiter(this, void 0, void 0, function () {
71
98
  return __generator(this, function (_a) {
72
99
  switch (_a.label) {
73
- case 0: return [4 /*yield*/, this.request(url, __assign(__assign({}, options), { params: __assign({ prettyPrint: "false" }, options === null || options === void 0 ? void 0 : options.params), method: "GET" }))];
100
+ case 0: return [4 /*yield*/, this.request(path, __assign(__assign({}, options), { params: __assign({ prettyPrint: "false" }, options === null || options === void 0 ? void 0 : options.params), method: "GET" }))];
74
101
  case 1: return [2 /*return*/, _a.sent()];
75
102
  }
76
103
  });
77
104
  });
78
105
  };
79
- HTTP.prototype.post = function (url, options) {
106
+ HTTP.prototype.post = function (path, options) {
80
107
  return __awaiter(this, void 0, void 0, function () {
81
108
  return __generator(this, function (_a) {
82
109
  switch (_a.label) {
83
- case 0: return [4 /*yield*/, this.request(url, __assign(__assign({}, options), { method: "POST", params: __assign({ key: this.apiKey, prettyPrint: "false" }, options === null || options === void 0 ? void 0 : options.params), data: __assign({ context: {
110
+ case 0: return [4 /*yield*/, this.request(path, __assign(__assign({}, options), { method: "POST", params: __assign({ key: this.apiKey, prettyPrint: "false" }, options === null || options === void 0 ? void 0 : options.params), data: __assign({ context: {
84
111
  client: __assign({ clientName: this.clientName, clientVersion: this.clientVersion }, this.defaultClientOptions),
85
112
  } }, options === null || options === void 0 ? void 0 : options.data) }))];
86
113
  case 1: return [2 /*return*/, _a.sent()];
@@ -88,20 +115,40 @@ var HTTP = /** @class */ (function () {
88
115
  });
89
116
  });
90
117
  };
91
- HTTP.prototype.request = function (url, partialOptions) {
118
+ HTTP.prototype.request = function (path, partialOptions) {
92
119
  return __awaiter(this, void 0, void 0, function () {
93
- var options, finalUrl, response, data;
94
- return __generator(this, function (_a) {
95
- switch (_a.label) {
120
+ var options, urlString, url, _a, _b, _c, key, value, response, data;
121
+ var e_1, _d;
122
+ return __generator(this, function (_e) {
123
+ switch (_e.label) {
96
124
  case 0:
97
125
  options = __assign(__assign(__assign({}, partialOptions), this.defaultFetchOptions), { headers: __assign(__assign(__assign(__assign({}, this.defaultHeaders), { cookie: this.cookie, referer: "https://" + this.baseUrl + "/" }), partialOptions.headers), this.defaultFetchOptions.headers), body: partialOptions.data ? JSON.stringify(partialOptions.data) : undefined });
98
- finalUrl = "https://" + this.baseUrl + "/" + url + "?" + new URLSearchParams(partialOptions.params);
99
- return [4 /*yield*/, fetch(finalUrl, options)];
126
+ if (path.startsWith("http")) {
127
+ url = new URL(path);
128
+ try {
129
+ for (_a = __values(Object.entries(partialOptions.params || {})), _b = _a.next(); !_b.done; _b = _a.next()) {
130
+ _c = __read(_b.value, 2), key = _c[0], value = _c[1];
131
+ url.searchParams.set(key, value);
132
+ }
133
+ }
134
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
135
+ finally {
136
+ try {
137
+ if (_b && !_b.done && (_d = _a.return)) _d.call(_a);
138
+ }
139
+ finally { if (e_1) throw e_1.error; }
140
+ }
141
+ urlString = url.toString();
142
+ }
143
+ else {
144
+ urlString = "https://" + this.baseUrl + "/" + path + "?" + new URLSearchParams(partialOptions.params);
145
+ }
146
+ return [4 /*yield*/, fetch(urlString, options)];
100
147
  case 1:
101
- response = _a.sent();
148
+ response = _e.sent();
102
149
  return [4 /*yield*/, response.json()];
103
150
  case 2:
104
- data = _a.sent();
151
+ data = _e.sent();
105
152
  this.parseCookie(response);
106
153
  return [2 /*return*/, { data: data }];
107
154
  }
@@ -13,6 +13,7 @@ import { getContinuationFromItems, stripToInt, Thumbnails } from "../../common";
13
13
  import { BaseChannel } from "../BaseChannel";
14
14
  import { PlaylistCompact } from "../PlaylistCompact";
15
15
  import { VideoCompact } from "../VideoCompact";
16
+ import { VideoCaptions } from "./VideoCaptions";
16
17
  var BaseVideoParser = /** @class */ (function () {
17
18
  function BaseVideoParser() {
18
19
  }
@@ -48,6 +49,10 @@ var BaseVideoParser = /** @class */ (function () {
48
49
  target.related.items = BaseVideoParser.parseRelatedFromSecondaryContent(secondaryContents, target.client);
49
50
  target.related.continuation = getContinuationFromItems(secondaryContents);
50
51
  }
52
+ // captions
53
+ if (videoInfo.captions) {
54
+ target.captions = new VideoCaptions({ client: target.client, video: target }).load(videoInfo.captions.playerCaptionsTracklistRenderer);
55
+ }
51
56
  return target;
52
57
  };
53
58
  BaseVideoParser.parseRelated = function (data, client) {
@@ -63,8 +68,8 @@ var BaseVideoParser = /** @class */ (function () {
63
68
  var primaryInfo = contents.find(function (c) { return "videoPrimaryInfoRenderer" in c; })
64
69
  .videoPrimaryInfoRenderer;
65
70
  var secondaryInfo = contents.find(function (c) { return "videoSecondaryInfoRenderer" in c; }).videoSecondaryInfoRenderer;
66
- var videoDetails = data.playerResponse.videoDetails;
67
- return __assign(__assign(__assign({}, secondaryInfo), primaryInfo), { videoDetails: videoDetails });
71
+ var _a = data.playerResponse, videoDetails = _a.videoDetails, captions = _a.captions;
72
+ return __assign(__assign(__assign({}, secondaryInfo), primaryInfo), { videoDetails: videoDetails, captions: captions });
68
73
  };
69
74
  BaseVideoParser.parseCompactRenderer = function (data, client) {
70
75
  if ("compactVideoRenderer" in data) {