youtubei 1.3.6 → 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.
- package/dist/cjs/common/shared/HTTP/HTTP.js +18 -7
- package/dist/cjs/youtube/BaseVideo/BaseVideoParser.js +9 -4
- package/dist/cjs/youtube/BaseVideo/VideoCaptions.js +81 -0
- package/dist/cjs/youtube/BaseVideo/index.js +1 -0
- package/dist/cjs/youtube/Caption/Caption.js +17 -0
- package/dist/cjs/youtube/Caption/CaptionLanguage.js +17 -0
- package/dist/cjs/youtube/{Transcript/proto → Caption}/index.js +2 -1
- package/dist/cjs/youtube/Client/Client.js +16 -18
- package/dist/cjs/youtube/Comment/CommentParser.js +11 -12
- package/dist/cjs/youtube/LiveVideo/LiveVideo.js +15 -15
- package/dist/cjs/youtube/LiveVideo/LiveVideoParser.js +1 -1
- package/dist/cjs/youtube/Video/Video.js +4 -6
- package/dist/cjs/youtube/Video/VideoParser.js +5 -5
- package/dist/cjs/youtube/VideoCompact/VideoCompact.js +2 -2
- package/dist/cjs/youtube/VideoCompact/VideoCompactParser.js +9 -5
- package/dist/cjs/youtube/index.js +0 -1
- package/dist/esm/common/shared/HTTP/HTTP.js +59 -12
- package/dist/esm/youtube/BaseVideo/BaseVideoParser.js +9 -4
- package/dist/esm/youtube/BaseVideo/VideoCaptions.js +133 -0
- package/dist/esm/youtube/BaseVideo/index.js +1 -0
- package/dist/esm/youtube/Caption/Caption.js +19 -0
- package/dist/esm/youtube/Caption/CaptionLanguage.js +15 -0
- package/dist/esm/youtube/Caption/index.js +2 -0
- package/dist/esm/youtube/Client/Client.js +21 -24
- package/dist/esm/youtube/Comment/CommentParser.js +11 -12
- package/dist/esm/youtube/LiveVideo/LiveVideo.js +15 -15
- package/dist/esm/youtube/LiveVideo/LiveVideoParser.js +1 -1
- package/dist/esm/youtube/Video/Video.js +5 -7
- package/dist/esm/youtube/Video/VideoParser.js +6 -6
- package/dist/esm/youtube/VideoCompact/VideoCompact.js +2 -2
- package/dist/esm/youtube/VideoCompact/VideoCompactParser.js +9 -5
- package/dist/esm/youtube/index.js +0 -1
- package/dist/typings/common/shared/HTTP/HTTP.d.ts +2 -2
- package/dist/typings/youtube/BaseVideo/BaseVideo.d.ts +3 -0
- package/dist/typings/youtube/BaseVideo/VideoCaptions.d.ts +40 -0
- package/dist/typings/youtube/BaseVideo/index.d.ts +1 -0
- package/dist/typings/youtube/Caption/Caption.d.ts +22 -0
- package/dist/typings/youtube/Caption/CaptionLanguage.d.ts +30 -0
- package/dist/typings/youtube/Caption/index.d.ts +2 -0
- package/dist/typings/youtube/Client/Client.d.ts +8 -3
- package/dist/typings/youtube/LiveVideo/LiveVideo.d.ts +5 -5
- package/dist/typings/youtube/Video/Video.d.ts +3 -6
- package/dist/typings/youtube/VideoCompact/VideoCompact.d.ts +2 -2
- package/dist/typings/youtube/index.d.ts +0 -1
- package/package.json +1 -1
- package/dist/cjs/youtube/Transcript/Transcript.js +0 -27
- package/dist/cjs/youtube/Transcript/TranscriptParser.js +0 -13
- package/dist/cjs/youtube/Transcript/index.js +0 -15
- package/dist/cjs/youtube/Transcript/proto/TranscriptParamsProto.js +0 -12
- package/dist/esm/youtube/Transcript/Transcript.js +0 -29
- package/dist/esm/youtube/Transcript/TranscriptParser.js +0 -13
- package/dist/esm/youtube/Transcript/index.js +0 -3
- package/dist/esm/youtube/Transcript/proto/TranscriptParamsProto.js +0 -2
- package/dist/esm/youtube/Transcript/proto/index.js +0 -1
- package/dist/typings/youtube/Transcript/Transcript.d.ts +0 -29
- package/dist/typings/youtube/Transcript/TranscriptParser.d.ts +0 -5
- package/dist/typings/youtube/Transcript/index.d.ts +0 -3
- package/dist/typings/youtube/Transcript/proto/TranscriptParamsProto.d.ts +0 -7
- 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(
|
|
37
|
+
get(path, options) {
|
|
38
38
|
return __awaiter(this, void 0, void 0, function* () {
|
|
39
|
-
return yield this.request(
|
|
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(
|
|
42
|
+
post(path, options) {
|
|
43
43
|
return __awaiter(this, void 0, void 0, function* () {
|
|
44
|
-
return yield this.request(
|
|
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(
|
|
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
|
-
|
|
53
|
-
|
|
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;
|
|
@@ -33,11 +34,15 @@ class BaseVideoParser {
|
|
|
33
34
|
((_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)) || [];
|
|
34
35
|
target.description = videoInfo.videoDetails.shortDescription || "";
|
|
35
36
|
// related videos
|
|
36
|
-
const secondaryContents = (_c = data
|
|
37
|
+
const secondaryContents = (_c = data.response.contents.twoColumnWatchNextResults.secondaryResults) === null || _c === void 0 ? void 0 : _c.secondaryResults.results;
|
|
37
38
|
if (secondaryContents) {
|
|
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) {
|
|
@@ -49,12 +54,12 @@ class BaseVideoParser {
|
|
|
49
54
|
return common_1.getContinuationFromItems(secondaryContents);
|
|
50
55
|
}
|
|
51
56
|
static parseRawData(data) {
|
|
52
|
-
const contents = data
|
|
57
|
+
const contents = data.response.contents.twoColumnWatchNextResults.results.results.contents;
|
|
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
|
|
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("./
|
|
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
|
-
|
|
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 },
|
|
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
|
|
@@ -79,13 +78,15 @@ class Client {
|
|
|
79
78
|
const response = yield this.http.get(`${constants_1.WATCH_END_POINT}`, {
|
|
80
79
|
params: { v: videoId, pbj: "1" },
|
|
81
80
|
});
|
|
82
|
-
|
|
83
|
-
response.data
|
|
81
|
+
const data = Array.isArray(response.data)
|
|
82
|
+
? response.data.reduce((prev, curr) => (Object.assign(Object.assign({}, prev), curr)), {})
|
|
83
|
+
: response.data;
|
|
84
|
+
if (!((_a = data.response) === null || _a === void 0 ? void 0 : _a.contents) || data.playerResponse.playabilityStatus.status === "ERROR") {
|
|
84
85
|
return undefined;
|
|
85
86
|
}
|
|
86
|
-
return (!
|
|
87
|
-
? new Video_1.Video({ client: this }).load(
|
|
88
|
-
: new LiveVideo_1.LiveVideo({ client: this }).load(
|
|
87
|
+
return (!data.playerResponse.playabilityStatus.liveStreamability
|
|
88
|
+
? new Video_1.Video({ client: this }).load(data)
|
|
89
|
+
: new LiveVideo_1.LiveVideo({ client: this }).load(data));
|
|
89
90
|
});
|
|
90
91
|
}
|
|
91
92
|
/** Get channel information by channel id+ */
|
|
@@ -101,17 +102,14 @@ class Client {
|
|
|
101
102
|
return new Channel_1.Channel({ client: this }).load(response.data);
|
|
102
103
|
});
|
|
103
104
|
}
|
|
104
|
-
|
|
105
|
+
/**
|
|
106
|
+
* Get video transcript / caption by video id
|
|
107
|
+
*/
|
|
108
|
+
getVideoTranscript(videoId, languageCode) {
|
|
109
|
+
var _a;
|
|
105
110
|
return __awaiter(this, void 0, void 0, function* () {
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
data: { params: Buffer.from(bufferParams).toString("base64") },
|
|
109
|
-
});
|
|
110
|
-
if (!response.data.actions)
|
|
111
|
-
return undefined;
|
|
112
|
-
return response.data.actions[0].updateEngagementPanelAction.content.transcriptRenderer.body.transcriptBodyRenderer.cueGroups
|
|
113
|
-
.map((t) => t.transcriptCueGroupRenderer.cues[0].transcriptCueRenderer)
|
|
114
|
-
.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);
|
|
115
113
|
});
|
|
116
114
|
}
|
|
117
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 {
|
|
9
|
+
const { properties, toolbar, author, avatar } = data;
|
|
10
10
|
// Basic information
|
|
11
|
-
target.id = commentId;
|
|
12
|
-
target.content =
|
|
13
|
-
target.publishDate =
|
|
14
|
-
target.likeCount = +
|
|
15
|
-
target.isAuthorChannelOwner =
|
|
16
|
-
target.isPinned =
|
|
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:
|
|
26
|
-
name:
|
|
27
|
-
thumbnails: new common_1.Thumbnails().load(
|
|
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.
|
|
25
|
-
this.
|
|
26
|
-
this.
|
|
27
|
-
this.
|
|
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.
|
|
46
|
+
if (this.isChatPlaying)
|
|
47
47
|
return;
|
|
48
|
-
this.
|
|
49
|
-
this.
|
|
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.
|
|
54
|
+
if (!this.chatRequestPoolingTimeout)
|
|
55
55
|
return;
|
|
56
|
-
this.
|
|
57
|
-
clearTimeout(this.
|
|
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.
|
|
70
|
+
if (this.chatQueue.find((c) => c.id === chat.id))
|
|
71
71
|
continue;
|
|
72
|
-
this.
|
|
73
|
-
const timeout = chat.timestamp / 1000 - (new Date().getTime() - this.
|
|
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.
|
|
77
|
+
this.timeoutMs = timeout;
|
|
78
78
|
this.chatContinuation = continuation;
|
|
79
|
-
this.
|
|
79
|
+
this.chatRequestPoolingTimeout = setTimeout(() => this.pollChatContinuation(), this.timeoutMs);
|
|
80
80
|
});
|
|
81
81
|
}
|
|
82
82
|
}
|
|
@@ -10,7 +10,7 @@ class LiveVideoParser {
|
|
|
10
10
|
.map((r) => r.text)
|
|
11
11
|
.join(" ")
|
|
12
12
|
.replace(/[^0-9]/g, "");
|
|
13
|
-
target.chatContinuation = (_a = data
|
|
13
|
+
target.chatContinuation = (_a = data.response.contents.twoColumnWatchNextResults.conversationBar.liveChatRenderer) === null || _a === void 0 ? void 0 : _a.continuations[0].reloadContinuationData.continuation;
|
|
14
14
|
return target;
|
|
15
15
|
}
|
|
16
16
|
static parseChats(data) {
|
|
@@ -34,14 +34,12 @@ class Video extends BaseVideo_1.BaseVideo {
|
|
|
34
34
|
/**
|
|
35
35
|
* Get Video transcript (if exists)
|
|
36
36
|
*
|
|
37
|
-
*
|
|
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.
|
|
42
|
+
return (_a = this.captions) === null || _a === void 0 ? void 0 : _a.get(languageCode);
|
|
45
43
|
});
|
|
46
44
|
}
|
|
47
45
|
}
|
|
@@ -9,11 +9,11 @@ class VideoParser {
|
|
|
9
9
|
var _a, _b, _c;
|
|
10
10
|
const videoInfo = BaseVideo_1.BaseVideoParser.parseRawData(data);
|
|
11
11
|
target.duration = +videoInfo.videoDetails.lengthSeconds;
|
|
12
|
-
const itemSectionRenderer = (_a = data
|
|
12
|
+
const itemSectionRenderer = (_a = data.response.contents.twoColumnWatchNextResults.results.results.contents
|
|
13
13
|
.reverse()
|
|
14
14
|
.find((c) => c.itemSectionRenderer)) === null || _a === void 0 ? void 0 : _a.itemSectionRenderer;
|
|
15
15
|
target.comments.continuation = common_1.getContinuationFromItems((itemSectionRenderer === null || itemSectionRenderer === void 0 ? void 0 : itemSectionRenderer.contents) || []);
|
|
16
|
-
const chapters = (_c = (_b = data
|
|
16
|
+
const chapters = (_c = (_b = data.response.playerOverlays.playerOverlayRenderer.decoratedPlayerBarRenderer) === null || _b === void 0 ? void 0 : _b.decoratedPlayerBarRenderer.playerBar.multiMarkersPlayerBarRenderer.markersMap) === null || _c === void 0 ? void 0 : _c[0].value.chapters;
|
|
17
17
|
target.chapters =
|
|
18
18
|
(chapters === null || chapters === void 0 ? void 0 : chapters.map(({ chapterRenderer: c }) => ({
|
|
19
19
|
title: c.title.simpleText,
|
|
@@ -23,9 +23,9 @@ class VideoParser {
|
|
|
23
23
|
return target;
|
|
24
24
|
}
|
|
25
25
|
static parseComments(data, video) {
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
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,18 +5,22 @@ 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;
|
|
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
|
-
target.title = headline
|
|
11
|
+
target.title = headline
|
|
12
|
+
? headline.simpleText
|
|
13
|
+
: title.simpleText || ((_b = (_a = title.runs) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.text) || "";
|
|
12
14
|
target.thumbnails = new common_1.Thumbnails().load(thumbnail.thumbnails);
|
|
13
15
|
target.uploadDate = publishedTimeText === null || publishedTimeText === void 0 ? void 0 : publishedTimeText.simpleText;
|
|
14
16
|
target.description =
|
|
15
|
-
((
|
|
17
|
+
((_c = detailedMetadataSnippets === null || detailedMetadataSnippets === void 0 ? void 0 : detailedMetadataSnippets[0].snippetText.runs) === null || _c === void 0 ? void 0 : _c.map((r) => r.text).join("")) || "";
|
|
16
18
|
target.duration =
|
|
17
|
-
common_1.getDuration((lengthText === null || lengthText === void 0 ? void 0 : lengthText.simpleText) || ((
|
|
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) ||
|
|
18
20
|
"") || null;
|
|
19
|
-
target.isLive =
|
|
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";
|
|
20
24
|
// Channel
|
|
21
25
|
if (ownerText || shortBylineText) {
|
|
22
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 (
|
|
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(
|
|
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 (
|
|
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(
|
|
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 (
|
|
118
|
+
HTTP.prototype.request = function (path, partialOptions) {
|
|
92
119
|
return __awaiter(this, void 0, void 0, function () {
|
|
93
|
-
var options,
|
|
94
|
-
|
|
95
|
-
|
|
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
|
-
|
|
99
|
-
|
|
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 =
|
|
148
|
+
response = _e.sent();
|
|
102
149
|
return [4 /*yield*/, response.json()];
|
|
103
150
|
case 2:
|
|
104
|
-
data =
|
|
151
|
+
data = _e.sent();
|
|
105
152
|
this.parseCookie(response);
|
|
106
153
|
return [2 /*return*/, { data: data }];
|
|
107
154
|
}
|