youtubei 1.6.7 → 1.7.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.
@@ -48,9 +48,7 @@ class HTTP {
48
48
  return __awaiter(this, void 0, void 0, function* () {
49
49
  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: {
50
50
  client: Object.assign({ clientName: this.clientName, clientVersion: this.clientVersion, visitorData: (_a = this.pot) === null || _a === void 0 ? void 0 : _a.visitorData }, this.defaultClientOptions),
51
- }, serviceIntegrityDimensions: this.pot
52
- ? { poToken: this.pot.token, }
53
- : undefined }, options === null || options === void 0 ? void 0 : options.data) }));
51
+ }, serviceIntegrityDimensions: this.pot ? { poToken: this.pot.token } : undefined }, options === null || options === void 0 ? void 0 : options.data) }));
54
52
  });
55
53
  }
56
54
  request(path, partialOptions) {
@@ -26,6 +26,7 @@ const stripToInt = (string) => {
26
26
  };
27
27
  exports.stripToInt = stripToInt;
28
28
  const getContinuationFromItems = (items, accessors = ["continuationEndpoint"]) => {
29
+ var _a, _b, _c;
29
30
  const continuation = items[items.length - 1];
30
31
  const renderer = continuation === null || continuation === void 0 ? void 0 : continuation.continuationItemRenderer;
31
32
  if (!renderer)
@@ -34,7 +35,10 @@ const getContinuationFromItems = (items, accessors = ["continuationEndpoint"]) =
34
35
  for (const accessor of accessors) {
35
36
  current = current[accessor];
36
37
  }
37
- return current.continuationCommand.token;
38
+ if ((_b = (_a = current === null || current === void 0 ? void 0 : current.commandExecutorCommand) === null || _a === void 0 ? void 0 : _a.commands) === null || _b === void 0 ? void 0 : _b.length) {
39
+ current = current.commandExecutorCommand.commands.find((cmd) => "continuationCommand" in cmd);
40
+ }
41
+ return (_c = current === null || current === void 0 ? void 0 : current.continuationCommand) === null || _c === void 0 ? void 0 : _c.token;
38
42
  };
39
43
  exports.getContinuationFromItems = getContinuationFromItems;
40
44
  const mapFilter = (items, key) => {
@@ -28,7 +28,9 @@ class BaseVideoParser {
28
28
  });
29
29
  // Like Count and Dislike Count
30
30
  const topLevelButtons = videoInfo.videoActions.menuRenderer.topLevelButtons;
31
- target.likeCount = common_1.stripToInt(BaseVideoParser.parseButtonRenderer(topLevelButtons[0]));
31
+ target.likeCount = topLevelButtons
32
+ ? common_1.stripToInt(BaseVideoParser.parseButtonRenderer(topLevelButtons[0]))
33
+ : null;
32
34
  // Tags and description
33
35
  target.tags =
34
36
  ((_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)) || [];
@@ -73,6 +75,16 @@ class BaseVideoParser {
73
75
  else if ("compactRadioRenderer" in data) {
74
76
  return new PlaylistCompact_1.PlaylistCompact({ client }).load(data.compactRadioRenderer);
75
77
  }
78
+ else if ("lockupViewModel" in data) {
79
+ // new data structure for related contents
80
+ const type = data.lockupViewModel.contentType;
81
+ if (type === "LOCKUP_CONTENT_TYPE_VIDEO") {
82
+ return new VideoCompact_1.VideoCompact({ client }).loadLockup(data.lockupViewModel);
83
+ }
84
+ else if (type === "LOCKUP_CONTENT_TYPE_PLAYLIST") {
85
+ return new PlaylistCompact_1.PlaylistCompact({ client }).loadLockup(data.lockupViewModel);
86
+ }
87
+ }
76
88
  }
77
89
  static parseRelatedFromSecondaryContent(secondaryContents, client) {
78
90
  return secondaryContents
@@ -28,6 +28,15 @@ class PlaylistCompact extends Base_1.Base {
28
28
  PlaylistCompactParser_1.PlaylistCompactParser.loadPlaylistCompact(this, data);
29
29
  return this;
30
30
  }
31
+ /**
32
+ * Load this instance with raw lockup data from Youtube
33
+ *
34
+ * @hidden
35
+ */
36
+ loadLockup(data) {
37
+ PlaylistCompactParser_1.PlaylistCompactParser.loadLockupPlaylistCompact(this, data);
38
+ return this;
39
+ }
31
40
  /**
32
41
  * Get {@link Playlist} object based on current playlist id
33
42
  *
@@ -23,5 +23,28 @@ class PlaylistCompactParser {
23
23
  }
24
24
  return target;
25
25
  }
26
+ static loadLockupPlaylistCompact(target, data) {
27
+ const lockupMetadataViewModel = data.metadata.lockupMetadataViewModel;
28
+ const channelMetadata = lockupMetadataViewModel.metadata.contentMetadataViewModel.metadataRows[0]
29
+ .metadataParts[0];
30
+ const thumbnailViewModel = data.contentImage.collectionThumbnailViewModel.primaryThumbnail.thumbnailViewModel;
31
+ if (channelMetadata.text.commandRuns) {
32
+ // not a mix
33
+ const channel = new BaseChannel_1.BaseChannel({
34
+ client: target.client,
35
+ name: channelMetadata.text.content,
36
+ id: channelMetadata.text.commandRuns[0].onTap.innertubeCommand.browseEndpoint
37
+ .browseId,
38
+ });
39
+ target.channel = channel;
40
+ }
41
+ target.id = data.contentId;
42
+ target.title = lockupMetadataViewModel.title.content;
43
+ target.videoCount =
44
+ common_1.stripToInt(thumbnailViewModel.overlays[0].thumbnailOverlayBadgeViewModel.thumbnailBadges[0]
45
+ .thumbnailBadgeViewModel.text) || 0;
46
+ target.thumbnails = new common_1.Thumbnails().load(thumbnailViewModel.image.sources);
47
+ return target;
48
+ }
26
49
  }
27
50
  exports.PlaylistCompactParser = PlaylistCompactParser;
@@ -33,6 +33,16 @@ class SearchResultParser {
33
33
  contents.push(new VideoCompact_1.VideoCompact({ client }).load(c.videoRenderer));
34
34
  else if ("channelRenderer" in c)
35
35
  contents.push(new BaseChannel_1.BaseChannel({ client }).load(c.channelRenderer));
36
+ else if ("lockupViewModel" in c) {
37
+ // new data structure for search result
38
+ const type = c.lockupViewModel.contentType;
39
+ if (type === "LOCKUP_CONTENT_TYPE_VIDEO") {
40
+ contents.push(new VideoCompact_1.VideoCompact({ client }).loadLockup(c.lockupViewModel));
41
+ }
42
+ else if (type === "LOCKUP_CONTENT_TYPE_PLAYLIST") {
43
+ contents.push(new PlaylistCompact_1.PlaylistCompact({ client }).loadLockup(c.lockupViewModel));
44
+ }
45
+ }
36
46
  }
37
47
  return contents;
38
48
  }
@@ -32,6 +32,15 @@ class VideoCompact extends Base_1.Base {
32
32
  VideoCompactParser_1.VideoCompactParser.loadVideoCompact(this, data);
33
33
  return this;
34
34
  }
35
+ /**
36
+ * Load this instance with raw lockup data from Youtube
37
+ *
38
+ * @hidden
39
+ */
40
+ loadLockup(data) {
41
+ VideoCompactParser_1.VideoCompactParser.loadLockupVideoCompact(this, data);
42
+ return this;
43
+ }
35
44
  /**
36
45
  * Get {@link Video} object based on current video id
37
46
  *
@@ -38,5 +38,32 @@ class VideoCompactParser {
38
38
  target.viewCount = common_1.stripToInt((viewCountText === null || viewCountText === void 0 ? void 0 : viewCountText.simpleText) || (viewCountText === null || viewCountText === void 0 ? void 0 : viewCountText.runs[0].text));
39
39
  return target;
40
40
  }
41
+ static loadLockupVideoCompact(target, data) {
42
+ var _a, _b;
43
+ const lockupMetadataViewModel = data.metadata.lockupMetadataViewModel;
44
+ const decoratedAvatarViewModel = lockupMetadataViewModel.image.decoratedAvatarViewModel;
45
+ const thumbnailBadge = data.contentImage.thumbnailViewModel.overlays[0].thumbnailOverlayBadgeViewModel
46
+ .thumbnailBadges[0].thumbnailBadgeViewModel;
47
+ const metadataRows = lockupMetadataViewModel.metadata.contentMetadataViewModel.metadataRows;
48
+ const channel = new BaseChannel_1.BaseChannel({
49
+ client: target.client,
50
+ name: metadataRows[0].metadataParts[0].text.content,
51
+ id: decoratedAvatarViewModel.rendererContext.commandContext.onTap.innertubeCommand
52
+ .browseEndpoint.browseId,
53
+ thumbnails: new common_1.Thumbnails().load(decoratedAvatarViewModel.avatar.avatarViewModel.image.sources),
54
+ });
55
+ const isLive = ((_a = thumbnailBadge.icon) === null || _a === void 0 ? void 0 : _a.sources[0].clientResource.imageName) === "LIVE";
56
+ target.channel = channel;
57
+ target.id = data.contentId;
58
+ target.title = lockupMetadataViewModel.title.content;
59
+ target.isLive = ((_b = thumbnailBadge.icon) === null || _b === void 0 ? void 0 : _b.sources[0].clientResource.imageName) === "LIVE";
60
+ target.duration = !isLive ? common_1.getDuration(thumbnailBadge.text) : null;
61
+ target.thumbnails = new common_1.Thumbnails().load(data.contentImage.thumbnailViewModel.image.sources);
62
+ target.viewCount = common_1.stripToInt(metadataRows[1].metadataParts[0].text.content);
63
+ target.uploadDate = !isLive
64
+ ? metadataRows[1].metadataParts[metadataRows[1].metadataParts.length - 1].text.content
65
+ : undefined;
66
+ return target;
67
+ }
41
68
  }
42
69
  exports.VideoCompactParser = VideoCompactParser;
@@ -114,9 +114,7 @@ var HTTP = /** @class */ (function () {
114
114
  switch (_b.label) {
115
115
  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: {
116
116
  client: __assign({ clientName: this.clientName, clientVersion: this.clientVersion, visitorData: (_a = this.pot) === null || _a === void 0 ? void 0 : _a.visitorData }, this.defaultClientOptions),
117
- }, serviceIntegrityDimensions: this.pot
118
- ? { poToken: this.pot.token, }
119
- : undefined }, options === null || options === void 0 ? void 0 : options.data) }))];
117
+ }, serviceIntegrityDimensions: this.pot ? { poToken: this.pot.token } : undefined }, options === null || options === void 0 ? void 0 : options.data) }))];
120
118
  case 1: return [2 /*return*/, _b.sent()];
121
119
  }
122
120
  });
@@ -33,6 +33,7 @@ export var stripToInt = function (string) {
33
33
  };
34
34
  export var getContinuationFromItems = function (items, accessors) {
35
35
  var e_1, _a;
36
+ var _b, _c, _d;
36
37
  if (accessors === void 0) { accessors = ["continuationEndpoint"]; }
37
38
  var continuation = items[items.length - 1];
38
39
  var renderer = continuation === null || continuation === void 0 ? void 0 : continuation.continuationItemRenderer;
@@ -52,7 +53,10 @@ export var getContinuationFromItems = function (items, accessors) {
52
53
  }
53
54
  finally { if (e_1) throw e_1.error; }
54
55
  }
55
- return current.continuationCommand.token;
56
+ if ((_c = (_b = current === null || current === void 0 ? void 0 : current.commandExecutorCommand) === null || _b === void 0 ? void 0 : _b.commands) === null || _c === void 0 ? void 0 : _c.length) {
57
+ current = current.commandExecutorCommand.commands.find(function (cmd) { return "continuationCommand" in cmd; });
58
+ }
59
+ return (_d = current === null || current === void 0 ? void 0 : current.continuationCommand) === null || _d === void 0 ? void 0 : _d.token;
56
60
  };
57
61
  export var mapFilter = function (items, key) {
58
62
  return items
@@ -38,7 +38,9 @@ var BaseVideoParser = /** @class */ (function () {
38
38
  });
39
39
  // Like Count and Dislike Count
40
40
  var topLevelButtons = videoInfo.videoActions.menuRenderer.topLevelButtons;
41
- target.likeCount = stripToInt(BaseVideoParser.parseButtonRenderer(topLevelButtons[0]));
41
+ target.likeCount = topLevelButtons
42
+ ? stripToInt(BaseVideoParser.parseButtonRenderer(topLevelButtons[0]))
43
+ : null;
42
44
  // Tags and description
43
45
  target.tags =
44
46
  ((_b = (_a = videoInfo.superTitleLink) === null || _a === void 0 ? void 0 : _a.runs) === null || _b === void 0 ? void 0 : _b.map(function (r) { return r.text.trim(); }).filter(function (t) { return t; })) || [];
@@ -83,6 +85,16 @@ var BaseVideoParser = /** @class */ (function () {
83
85
  else if ("compactRadioRenderer" in data) {
84
86
  return new PlaylistCompact({ client: client }).load(data.compactRadioRenderer);
85
87
  }
88
+ else if ("lockupViewModel" in data) {
89
+ // new data structure for related contents
90
+ var type = data.lockupViewModel.contentType;
91
+ if (type === "LOCKUP_CONTENT_TYPE_VIDEO") {
92
+ return new VideoCompact({ client: client }).loadLockup(data.lockupViewModel);
93
+ }
94
+ else if (type === "LOCKUP_CONTENT_TYPE_PLAYLIST") {
95
+ return new PlaylistCompact({ client: client }).loadLockup(data.lockupViewModel);
96
+ }
97
+ }
86
98
  };
87
99
  BaseVideoParser.parseRelatedFromSecondaryContent = function (secondaryContents, client) {
88
100
  return secondaryContents
@@ -67,6 +67,15 @@ var PlaylistCompact = /** @class */ (function (_super) {
67
67
  PlaylistCompactParser.loadPlaylistCompact(this, data);
68
68
  return this;
69
69
  };
70
+ /**
71
+ * Load this instance with raw lockup data from Youtube
72
+ *
73
+ * @hidden
74
+ */
75
+ PlaylistCompact.prototype.loadLockup = function (data) {
76
+ PlaylistCompactParser.loadLockupPlaylistCompact(this, data);
77
+ return this;
78
+ };
70
79
  /**
71
80
  * Get {@link Playlist} object based on current playlist id
72
81
  *
@@ -22,6 +22,29 @@ var PlaylistCompactParser = /** @class */ (function () {
22
22
  }
23
23
  return target;
24
24
  };
25
+ PlaylistCompactParser.loadLockupPlaylistCompact = function (target, data) {
26
+ var lockupMetadataViewModel = data.metadata.lockupMetadataViewModel;
27
+ var channelMetadata = lockupMetadataViewModel.metadata.contentMetadataViewModel.metadataRows[0]
28
+ .metadataParts[0];
29
+ var thumbnailViewModel = data.contentImage.collectionThumbnailViewModel.primaryThumbnail.thumbnailViewModel;
30
+ if (channelMetadata.text.commandRuns) {
31
+ // not a mix
32
+ var channel = new BaseChannel({
33
+ client: target.client,
34
+ name: channelMetadata.text.content,
35
+ id: channelMetadata.text.commandRuns[0].onTap.innertubeCommand.browseEndpoint
36
+ .browseId,
37
+ });
38
+ target.channel = channel;
39
+ }
40
+ target.id = data.contentId;
41
+ target.title = lockupMetadataViewModel.title.content;
42
+ target.videoCount =
43
+ stripToInt(thumbnailViewModel.overlays[0].thumbnailOverlayBadgeViewModel.thumbnailBadges[0]
44
+ .thumbnailBadgeViewModel.text) || 0;
45
+ target.thumbnails = new Thumbnails().load(thumbnailViewModel.image.sources);
46
+ return target;
47
+ };
25
48
  return PlaylistCompactParser;
26
49
  }());
27
50
  export { PlaylistCompactParser };
@@ -46,6 +46,16 @@ var SearchResultParser = /** @class */ (function () {
46
46
  contents.push(new VideoCompact({ client: client }).load(c.videoRenderer));
47
47
  else if ("channelRenderer" in c)
48
48
  contents.push(new BaseChannel({ client: client }).load(c.channelRenderer));
49
+ else if ("lockupViewModel" in c) {
50
+ // new data structure for search result
51
+ var type = c.lockupViewModel.contentType;
52
+ if (type === "LOCKUP_CONTENT_TYPE_VIDEO") {
53
+ contents.push(new VideoCompact({ client: client }).loadLockup(c.lockupViewModel));
54
+ }
55
+ else if (type === "LOCKUP_CONTENT_TYPE_PLAYLIST") {
56
+ contents.push(new PlaylistCompact({ client: client }).loadLockup(c.lockupViewModel));
57
+ }
58
+ }
49
59
  }
50
60
  }
51
61
  catch (e_1_1) { e_1 = { error: e_1_1 }; }
@@ -75,6 +75,15 @@ var VideoCompact = /** @class */ (function (_super) {
75
75
  VideoCompactParser.loadVideoCompact(this, data);
76
76
  return this;
77
77
  };
78
+ /**
79
+ * Load this instance with raw lockup data from Youtube
80
+ *
81
+ * @hidden
82
+ */
83
+ VideoCompact.prototype.loadLockup = function (data) {
84
+ VideoCompactParser.loadLockupVideoCompact(this, data);
85
+ return this;
86
+ };
78
87
  /**
79
88
  * Get {@link Video} object based on current video id
80
89
  *
@@ -37,6 +37,33 @@ var VideoCompactParser = /** @class */ (function () {
37
37
  target.viewCount = stripToInt((viewCountText === null || viewCountText === void 0 ? void 0 : viewCountText.simpleText) || (viewCountText === null || viewCountText === void 0 ? void 0 : viewCountText.runs[0].text));
38
38
  return target;
39
39
  };
40
+ VideoCompactParser.loadLockupVideoCompact = function (target, data) {
41
+ var _a, _b;
42
+ var lockupMetadataViewModel = data.metadata.lockupMetadataViewModel;
43
+ var decoratedAvatarViewModel = lockupMetadataViewModel.image.decoratedAvatarViewModel;
44
+ var thumbnailBadge = data.contentImage.thumbnailViewModel.overlays[0].thumbnailOverlayBadgeViewModel
45
+ .thumbnailBadges[0].thumbnailBadgeViewModel;
46
+ var metadataRows = lockupMetadataViewModel.metadata.contentMetadataViewModel.metadataRows;
47
+ var channel = new BaseChannel({
48
+ client: target.client,
49
+ name: metadataRows[0].metadataParts[0].text.content,
50
+ id: decoratedAvatarViewModel.rendererContext.commandContext.onTap.innertubeCommand
51
+ .browseEndpoint.browseId,
52
+ thumbnails: new Thumbnails().load(decoratedAvatarViewModel.avatar.avatarViewModel.image.sources),
53
+ });
54
+ var isLive = ((_a = thumbnailBadge.icon) === null || _a === void 0 ? void 0 : _a.sources[0].clientResource.imageName) === "LIVE";
55
+ target.channel = channel;
56
+ target.id = data.contentId;
57
+ target.title = lockupMetadataViewModel.title.content;
58
+ target.isLive = ((_b = thumbnailBadge.icon) === null || _b === void 0 ? void 0 : _b.sources[0].clientResource.imageName) === "LIVE";
59
+ target.duration = !isLive ? getDuration(thumbnailBadge.text) : null;
60
+ target.thumbnails = new Thumbnails().load(data.contentImage.thumbnailViewModel.image.sources);
61
+ target.viewCount = stripToInt(metadataRows[1].metadataParts[0].text.content);
62
+ target.uploadDate = !isLive
63
+ ? metadataRows[1].metadataParts[metadataRows[1].metadataParts.length - 1].text.content
64
+ : undefined;
65
+ return target;
66
+ };
40
67
  return VideoCompactParser;
41
68
  }());
42
69
  export { VideoCompactParser };
@@ -29,6 +29,12 @@ export declare class PlaylistCompact extends Base implements PlaylistCompactProp
29
29
  * @hidden
30
30
  */
31
31
  load(data: YoutubeRawData): PlaylistCompact;
32
+ /**
33
+ * Load this instance with raw lockup data from Youtube
34
+ *
35
+ * @hidden
36
+ */
37
+ loadLockup(data: YoutubeRawData): PlaylistCompact;
32
38
  /**
33
39
  * Get {@link Playlist} object based on current playlist id
34
40
  *
@@ -2,4 +2,5 @@ import { YoutubeRawData } from "../../common";
2
2
  import { PlaylistCompact } from "./PlaylistCompact";
3
3
  export declare class PlaylistCompactParser {
4
4
  static loadPlaylistCompact(target: PlaylistCompact, data: YoutubeRawData): PlaylistCompact;
5
+ static loadLockupPlaylistCompact(target: PlaylistCompact, data: YoutubeRawData): PlaylistCompact;
5
6
  }
@@ -46,6 +46,12 @@ export declare class VideoCompact extends Base implements VideoCompactProperties
46
46
  * @hidden
47
47
  */
48
48
  load(data: YoutubeRawData): VideoCompact;
49
+ /**
50
+ * Load this instance with raw lockup data from Youtube
51
+ *
52
+ * @hidden
53
+ */
54
+ loadLockup(data: YoutubeRawData): VideoCompact;
49
55
  /**
50
56
  * Get {@link Video} object based on current video id
51
57
  *
@@ -2,4 +2,5 @@ import { YoutubeRawData } from "../../common";
2
2
  import { VideoCompact } from "./VideoCompact";
3
3
  export declare class VideoCompactParser {
4
4
  static loadVideoCompact(target: VideoCompact, data: YoutubeRawData): VideoCompact;
5
+ static loadLockupVideoCompact(target: VideoCompact, data: YoutubeRawData): VideoCompact;
5
6
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "youtubei",
3
- "version": "1.6.7",
3
+ "version": "1.7.0",
4
4
  "description": "Simple package to get information from youtube such as videos, playlists, channels, video information & comments, related videos, up next video, and more!",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",