youtubei 1.6.7 → 1.8.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 (58) hide show
  1. package/dist/cjs/common/shared/HTTP/HTTP.js +1 -3
  2. package/dist/cjs/common/utils/helper.js +5 -1
  3. package/dist/cjs/music/MusicClient/MusicClient.js +13 -18
  4. package/dist/cjs/music/MusicSearchResult/MusicSearchResult.js +6 -4
  5. package/dist/cjs/music/MusicSearchResult/MusicSearchResultParser.js +205 -14
  6. package/dist/cjs/music/MusicSearchResult/index.js +0 -1
  7. package/dist/cjs/youtube/BaseChannel/BaseChannel.js +2 -0
  8. package/dist/cjs/youtube/BaseChannel/BaseChannelParser.js +3 -2
  9. package/dist/cjs/youtube/BaseChannel/ChannelPlaylists.js +12 -2
  10. package/dist/cjs/youtube/BaseChannel/ChannelPosts.js +59 -0
  11. package/dist/cjs/youtube/BaseVideo/BaseVideoParser.js +14 -2
  12. package/dist/cjs/youtube/Channel/ChannelParser.js +10 -6
  13. package/dist/cjs/youtube/PlaylistCompact/PlaylistCompact.js +9 -0
  14. package/dist/cjs/youtube/PlaylistCompact/PlaylistCompactParser.js +23 -0
  15. package/dist/cjs/youtube/Post/Post.js +23 -0
  16. package/dist/cjs/youtube/Post/PostParser.js +23 -0
  17. package/dist/cjs/youtube/Post/index.js +14 -0
  18. package/dist/cjs/youtube/SearchResult/SearchResultParser.js +10 -0
  19. package/dist/cjs/youtube/VideoCompact/VideoCompact.js +9 -0
  20. package/dist/cjs/youtube/VideoCompact/VideoCompactParser.js +27 -0
  21. package/dist/esm/common/shared/HTTP/HTTP.js +1 -3
  22. package/dist/esm/common/utils/helper.js +5 -1
  23. package/dist/esm/music/MusicClient/MusicClient.js +16 -20
  24. package/dist/esm/music/MusicSearchResult/MusicSearchResult.js +5 -4
  25. package/dist/esm/music/MusicSearchResult/MusicSearchResultParser.js +221 -14
  26. package/dist/esm/music/MusicSearchResult/index.js +0 -1
  27. package/dist/esm/youtube/BaseChannel/BaseChannel.js +2 -0
  28. package/dist/esm/youtube/BaseChannel/BaseChannelParser.js +3 -2
  29. package/dist/esm/youtube/BaseChannel/ChannelPlaylists.js +11 -3
  30. package/dist/esm/youtube/BaseChannel/ChannelPosts.js +111 -0
  31. package/dist/esm/youtube/BaseVideo/BaseVideoParser.js +14 -2
  32. package/dist/esm/youtube/Channel/ChannelParser.js +12 -8
  33. package/dist/esm/youtube/PlaylistCompact/PlaylistCompact.js +9 -0
  34. package/dist/esm/youtube/PlaylistCompact/PlaylistCompactParser.js +23 -0
  35. package/dist/esm/youtube/Post/Post.js +36 -0
  36. package/dist/esm/youtube/Post/PostParser.js +23 -0
  37. package/dist/esm/youtube/Post/index.js +2 -0
  38. package/dist/esm/youtube/SearchResult/SearchResultParser.js +10 -0
  39. package/dist/esm/youtube/VideoCompact/VideoCompact.js +9 -0
  40. package/dist/esm/youtube/VideoCompact/VideoCompactParser.js +27 -0
  41. package/dist/typings/music/MusicClient/MusicClient.d.ts +3 -7
  42. package/dist/typings/music/MusicSearchResult/MusicSearchResult.d.ts +7 -4
  43. package/dist/typings/music/MusicSearchResult/MusicSearchResultParser.d.ts +15 -3
  44. package/dist/typings/music/MusicSearchResult/index.d.ts +0 -1
  45. package/dist/typings/youtube/BaseChannel/BaseChannel.d.ts +4 -1
  46. package/dist/typings/youtube/BaseChannel/BaseChannelParser.d.ts +1 -0
  47. package/dist/typings/youtube/BaseChannel/ChannelPosts.d.ts +30 -0
  48. package/dist/typings/youtube/PlaylistCompact/PlaylistCompact.d.ts +6 -0
  49. package/dist/typings/youtube/PlaylistCompact/PlaylistCompactParser.d.ts +1 -0
  50. package/dist/typings/youtube/Post/Post.d.ts +32 -0
  51. package/dist/typings/youtube/Post/PostParser.d.ts +5 -0
  52. package/dist/typings/youtube/Post/index.d.ts +2 -0
  53. package/dist/typings/youtube/VideoCompact/VideoCompact.d.ts +6 -0
  54. package/dist/typings/youtube/VideoCompact/VideoCompactParser.d.ts +1 -0
  55. package/package.json +1 -1
  56. package/dist/cjs/music/MusicSearchResult/MusicAllSearchResultParser.js +0 -218
  57. package/dist/esm/music/MusicSearchResult/MusicAllSearchResultParser.js +0 -234
  58. package/dist/typings/music/MusicSearchResult/MusicAllSearchResultParser.d.ts +0 -19
@@ -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) => {
@@ -20,19 +20,18 @@ class MusicClient {
20
20
  const fullOptions = Object.assign(Object.assign({ initialCookie: "", oauth: { enabled: false }, fetchOptions: {} }, options), { youtubeClientOptions: Object.assign({ hl: "en", gl: "US" }, options.youtubeClientOptions), apiKey: options.apiKey || constants_1.INNERTUBE_API_KEY, baseUrl: options.baseUrl || constants_1.BASE_URL, clientName: options.clientName || constants_1.INNERTUBE_CLIENT_NAME, clientVersion: options.clientVersion || constants_1.INNERTUBE_CLIENT_VERSION });
21
21
  this.http = new common_1.HTTP(fullOptions);
22
22
  }
23
+ /**
24
+ * Searches for video, song, album, playlist, or artist
25
+ *
26
+ * @param query The search query
27
+ * @param type Search type
28
+ *
29
+ */
23
30
  search(query, type) {
24
31
  return __awaiter(this, void 0, void 0, function* () {
25
- if (!type) {
26
- const response = yield this.http.post(`${constants_1.I_END_POINT}/search`, {
27
- data: { query },
28
- });
29
- return MusicSearchResult_1.MusicAllSearchResultParser.parseSearchResult(response.data, this);
30
- }
31
- else {
32
- const result = new MusicSearchResult_1.MusicSearchResult({ client: this });
33
- yield result.search(query, type);
34
- return result;
35
- }
32
+ const result = new MusicSearchResult_1.MusicSearchResult({ client: this });
33
+ yield result.search(query, type);
34
+ return result;
36
35
  });
37
36
  }
38
37
  /**
@@ -42,13 +41,9 @@ class MusicClient {
42
41
  */
43
42
  searchAll(query) {
44
43
  return __awaiter(this, void 0, void 0, function* () {
45
- const response = yield this.http.post(`${constants_1.I_END_POINT}/search`, {
46
- data: { query },
47
- });
48
- return {
49
- top: MusicSearchResult_1.MusicAllSearchResultParser.parseTopResult(response.data, this),
50
- shelves: MusicSearchResult_1.MusicAllSearchResultParser.parseSearchResult(response.data, this),
51
- };
44
+ const result = new MusicSearchResult_1.MusicSearchResult({ client: this });
45
+ yield result.search(query);
46
+ return result;
52
47
  });
53
48
  }
54
49
  /**
@@ -60,14 +60,16 @@ class MusicSearchResult extends MusicContinuable_1.MusicContinuable {
60
60
  return __awaiter(this, void 0, void 0, function* () {
61
61
  this.items = [];
62
62
  this.type = type;
63
- const bufferParams = proto_1.MusicSearchProto.encode(proto_1.optionsToProto(type)).finish();
63
+ let bufferParams;
64
+ if (type)
65
+ bufferParams = proto_1.MusicSearchProto.encode(proto_1.optionsToProto(type)).finish();
64
66
  const response = yield this.client.http.post(`${constants_1.I_END_POINT}/search`, {
65
67
  data: {
66
68
  query,
67
- params: Buffer.from(bufferParams).toString("base64"),
69
+ params: bufferParams ? Buffer.from(bufferParams).toString("base64") : undefined,
68
70
  },
69
71
  });
70
- const { data, continuation } = MusicSearchResultParser_1.MusicSearchResultParser.parseInitialSearchResult(response.data, type, this.client);
72
+ const { data, continuation } = MusicSearchResultParser_1.MusicSearchResultParser.parseInitialSearchResult(response.data, this.client);
71
73
  this.items.push(...data);
72
74
  this.continuation = continuation;
73
75
  return this;
@@ -84,7 +86,7 @@ class MusicSearchResult extends MusicContinuable_1.MusicContinuable {
84
86
  const response = yield this.client.http.post(`${constants_1.I_END_POINT}/search`, {
85
87
  data: { continuation: this.continuation },
86
88
  });
87
- const { data, continuation } = MusicSearchResultParser_1.MusicSearchResultParser.parseContinuationSearchResult(response.data, this.type, this.client);
89
+ const { data, continuation } = MusicSearchResultParser_1.MusicSearchResultParser.parseContinuationSearchResult(response.data, this.client);
88
90
  return {
89
91
  items: data,
90
92
  continuation,
@@ -1,42 +1,233 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MusicSearchResultParser = void 0;
4
- const MusicAllSearchResultParser_1 = require("./MusicAllSearchResultParser");
4
+ const common_1 = require("../../common");
5
+ const MusicAlbumCompact_1 = require("../MusicAlbumCompact");
6
+ const MusicArtistCompact_1 = require("../MusicArtistCompact");
7
+ const MusicBaseArtist_1 = require("../MusicBaseArtist");
8
+ const MusicBaseChannel_1 = require("../MusicBaseChannel");
9
+ const MusicPlaylistCompact_1 = require("../MusicPlaylistCompact");
10
+ const MusicSongCompact_1 = require("../MusicSongCompact");
11
+ const MusicVideoCompact_1 = require("../MusicVideoCompact");
5
12
  class MusicSearchResultParser {
6
- static parseInitialSearchResult(data, type, client) {
13
+ static parseInitialSearchResult(data, client) {
7
14
  var _a, _b;
8
- const contentSection = data.contents.tabbedSearchResultsRenderer.tabs[0].tabRenderer.content.sectionListRenderer.contents.find((c) => "musicShelfRenderer" in c);
9
- if (!contentSection) {
15
+ const sectionContents = data.contents.tabbedSearchResultsRenderer.tabs[0].tabRenderer.content
16
+ .sectionListRenderer.contents;
17
+ const topContent = sectionContents.find((c) => "musicCardShelfRenderer" in c);
18
+ const resultContents = sectionContents.find((c) => "musicShelfRenderer" in c);
19
+ const topResult = this.parseTopResult(topContent, client);
20
+ if (!resultContents) {
10
21
  // no results
11
22
  return {
12
- data: [],
23
+ data: topResult ? [topResult] : [],
13
24
  continuation: undefined,
14
25
  };
15
26
  }
16
- const { contents, continuations } = contentSection.musicShelfRenderer;
27
+ const { contents, continuations } = resultContents.musicShelfRenderer;
28
+ const result = MusicSearchResultParser.parseSearchResult(contents, client);
29
+ if (topResult)
30
+ result.unshift(topResult);
17
31
  return {
18
- data: MusicSearchResultParser.parseSearchResult(contents, type, client),
32
+ data: result,
19
33
  continuation: (_b = (_a = continuations === null || continuations === void 0 ? void 0 : continuations[0]) === null || _a === void 0 ? void 0 : _a.nextContinuationData) === null || _b === void 0 ? void 0 : _b.continuation,
20
34
  };
21
35
  }
22
- static parseContinuationSearchResult(data, type, client) {
36
+ static parseContinuationSearchResult(data, client) {
23
37
  const shelf = data.continuationContents.musicShelfContinuation;
24
38
  return {
25
- data: MusicSearchResultParser.parseSearchResult(shelf.contents, type, client),
39
+ data: MusicSearchResultParser.parseSearchResult(shelf.contents, client),
26
40
  continuation: shelf.continuations[0].nextContinuationData.continuation,
27
41
  };
28
42
  }
29
- static parseSearchResult(shelfContents, type, client) {
30
- const rawContents = shelfContents
31
- .filter((c) => "musicResponsiveListItemRenderer" in c)
32
- .map((c) => c.musicResponsiveListItemRenderer);
43
+ static parseTopResult(data, client) {
44
+ const top = data === null || data === void 0 ? void 0 : data.musicCardShelfRenderer;
45
+ if (!top)
46
+ return;
47
+ const { browseEndpoint, watchEndpoint } = top.title.runs[0].navigationEndpoint;
48
+ const id = (watchEndpoint === null || watchEndpoint === void 0 ? void 0 : watchEndpoint.videoId) || (browseEndpoint === null || browseEndpoint === void 0 ? void 0 : browseEndpoint.browseId);
49
+ const type = (watchEndpoint === null || watchEndpoint === void 0 ? void 0 : watchEndpoint.watchEndpointMusicSupportedConfigs.watchEndpointMusicConfig.musicVideoType) || (browseEndpoint === null || browseEndpoint === void 0 ? void 0 : browseEndpoint.browseEndpointContextSupportedConfigs.browseEndpointContextMusicConfig.pageType);
50
+ const title = top.title.runs[0].text;
51
+ const thumbnail = top.thumbnail.musicThumbnailRenderer.thumbnail.thumbnails;
52
+ let topResult;
53
+ if (type === "MUSIC_VIDEO_TYPE_ATV") {
54
+ topResult = new MusicSongCompact_1.MusicSongCompact({
55
+ client,
56
+ id,
57
+ title,
58
+ duration: common_1.getDuration(top.subtitle.runs.at(-1).text),
59
+ artists: this.parseArtists(top.subtitle.runs, client),
60
+ album: this.parseAlbum(top.subtitle.runs, client),
61
+ thumbnails: new common_1.Thumbnails().load(thumbnail),
62
+ });
63
+ }
64
+ else if (type === "MUSIC_VIDEO_TYPE_UGC" || type === "MUSIC_VIDEO_TYPE_OMV") {
65
+ topResult = new MusicVideoCompact_1.MusicVideoCompact({
66
+ client,
67
+ id,
68
+ title,
69
+ duration: common_1.getDuration(top.subtitle.runs.at(-1).text),
70
+ artists: this.parseArtists(top.subtitle.runs, client),
71
+ thumbnails: new common_1.Thumbnails().load(thumbnail),
72
+ });
73
+ }
74
+ else if (type === "MUSIC_PAGE_TYPE_ALBUM") {
75
+ topResult = new MusicAlbumCompact_1.MusicAlbumCompact({
76
+ client,
77
+ id,
78
+ title,
79
+ artists: this.parseArtists(top.subtitle.runs, client),
80
+ thumbnails: new common_1.Thumbnails().load(thumbnail),
81
+ });
82
+ }
83
+ else if (type === "MUSIC_PAGE_TYPE_ARTIST") {
84
+ topResult = new MusicArtistCompact_1.MusicArtistCompact({
85
+ client,
86
+ id,
87
+ name: title,
88
+ thumbnails: new common_1.Thumbnails().load(thumbnail),
89
+ });
90
+ }
91
+ else if (type === "MUSIC_PAGE_TYPE_PLAYLIST") {
92
+ topResult = new MusicPlaylistCompact_1.MusicPlaylistCompact({
93
+ client,
94
+ id,
95
+ title,
96
+ channel: this.parseChannel(top.subtitle.runs, client),
97
+ thumbnails: new common_1.Thumbnails().load(thumbnail),
98
+ });
99
+ }
100
+ return topResult;
101
+ }
102
+ static parseSearchResult(shelfContents, client) {
103
+ const rawContents = shelfContents.filter((c) => "musicResponsiveListItemRenderer" in c);
33
104
  const contents = [];
34
105
  for (const c of rawContents) {
35
- const parsed = MusicAllSearchResultParser_1.MusicAllSearchResultParser.parseVideoItem(c, type === "video" ? "MUSIC_VIDEO_TYPE_UGC" : "MUSIC_VIDEO_TYPE_ATV", client);
106
+ const parsed = this.parseSearchItem(c, client);
36
107
  if (parsed)
37
108
  contents.push(parsed);
38
109
  }
39
110
  return contents;
40
111
  }
112
+ static parseSearchItem(content, client) {
113
+ var _a;
114
+ const item = content.musicResponsiveListItemRenderer;
115
+ const playEndpoint = (_a = item.overlay) === null || _a === void 0 ? void 0 : _a.musicItemThumbnailOverlayRenderer.content.musicPlayButtonRenderer.playNavigationEndpoint;
116
+ if (playEndpoint === null || playEndpoint === void 0 ? void 0 : playEndpoint.watchEndpoint) {
117
+ const pageType = playEndpoint.watchEndpoint.watchEndpointMusicSupportedConfigs
118
+ .watchEndpointMusicConfig.musicVideoType;
119
+ return this.parseVideoItem(item, pageType, client);
120
+ }
121
+ else if (playEndpoint === null || playEndpoint === void 0 ? void 0 : playEndpoint.watchPlaylistEndpoint.params) {
122
+ return this.parsePlaylistItem(item, client);
123
+ }
124
+ else if (playEndpoint === null || playEndpoint === void 0 ? void 0 : playEndpoint.watchPlaylistEndpoint) {
125
+ // TODO add podcast support, id starts with PL
126
+ if (playEndpoint.watchPlaylistEndpoint.playlistId.startsWith("OL")) {
127
+ return this.parseAlbumItem(item, client);
128
+ }
129
+ }
130
+ else {
131
+ return this.parseArtistItem(item, client);
132
+ }
133
+ }
134
+ static parseVideoItem(item, pageType, client) {
135
+ // TODO support other types
136
+ if (!["MUSIC_VIDEO_TYPE_ATV", "MUSIC_VIDEO_TYPE_UGC", "MUSIC_VIDEO_TYPE_OMV"].includes(pageType)) {
137
+ return;
138
+ }
139
+ const [topColumn, bottomColumn] = item.flexColumns.map((c) => c.musicResponsiveListItemFlexColumnRenderer.text.runs);
140
+ const id = topColumn[0].navigationEndpoint.watchEndpoint.videoId;
141
+ const title = topColumn[0].text;
142
+ const duration = common_1.getDuration(bottomColumn.at(-1).text) || undefined;
143
+ const thumbnails = new common_1.Thumbnails().load(item.thumbnail.musicThumbnailRenderer.thumbnail.thumbnails);
144
+ const artists = this.parseArtists(bottomColumn, client);
145
+ if (pageType === "MUSIC_VIDEO_TYPE_ATV") {
146
+ return new MusicSongCompact_1.MusicSongCompact({
147
+ client,
148
+ id,
149
+ album: this.parseAlbum(bottomColumn, client),
150
+ title,
151
+ artists,
152
+ thumbnails,
153
+ duration,
154
+ });
155
+ }
156
+ else if (pageType === "MUSIC_VIDEO_TYPE_UGC" || pageType === "MUSIC_VIDEO_TYPE_OMV") {
157
+ return new MusicVideoCompact_1.MusicVideoCompact({ client, id, title, artists, thumbnails, duration });
158
+ }
159
+ }
160
+ static parsePlaylistItem(item, client) {
161
+ const [topColumn, bottomColumn] = item.flexColumns.map((c) => c.musicResponsiveListItemFlexColumnRenderer.text.runs);
162
+ const id = item.overlay.musicItemThumbnailOverlayRenderer.content.musicPlayButtonRenderer
163
+ .playNavigationEndpoint.watchPlaylistEndpoint.playlistId;
164
+ const title = topColumn[0].text;
165
+ const songCount = common_1.stripToInt(bottomColumn.at(-1).text) || undefined;
166
+ const thumbnails = new common_1.Thumbnails().load(item.thumbnail.musicThumbnailRenderer.thumbnail.thumbnails);
167
+ const channel = this.parseChannel(bottomColumn, client);
168
+ return new MusicPlaylistCompact_1.MusicPlaylistCompact({ client, id, title, thumbnails, songCount, channel });
169
+ }
170
+ static parseAlbumItem(item, client) {
171
+ const [topColumn, bottomColumn] = item.flexColumns.map((c) => c.musicResponsiveListItemFlexColumnRenderer.text.runs);
172
+ const id = item.overlay.musicItemThumbnailOverlayRenderer.content.musicPlayButtonRenderer
173
+ .playNavigationEndpoint.watchPlaylistEndpoint.playlistId;
174
+ const title = topColumn[0].text;
175
+ const year = common_1.stripToInt(bottomColumn.at(-1).text) || undefined;
176
+ const thumbnails = new common_1.Thumbnails().load(item.thumbnail.musicThumbnailRenderer.thumbnail.thumbnails);
177
+ const artists = this.parseArtists(bottomColumn, client);
178
+ return new MusicAlbumCompact_1.MusicAlbumCompact({ client, id, title, thumbnails, artists, year });
179
+ }
180
+ static parseArtistItem(item, client) {
181
+ const [topColumn] = item.flexColumns.map((c) => c.musicResponsiveListItemFlexColumnRenderer.text.runs);
182
+ const id = item.navigationEndpoint.browseEndpoint.browseId;
183
+ const name = topColumn[0].text;
184
+ const thumbnails = new common_1.Thumbnails().load(item.thumbnail.musicThumbnailRenderer.thumbnail.thumbnails);
185
+ return new MusicArtistCompact_1.MusicArtistCompact({ client, id, name, thumbnails });
186
+ }
187
+ static parseAlbum(items, client) {
188
+ var _a;
189
+ const albumRaw = items.find((r) => {
190
+ var _a;
191
+ const pageType = (_a = r.navigationEndpoint) === null || _a === void 0 ? void 0 : _a.browseEndpoint.browseEndpointContextSupportedConfigs.browseEndpointContextMusicConfig.pageType;
192
+ return pageType === "MUSIC_PAGE_TYPE_ALBUM";
193
+ });
194
+ if (!albumRaw)
195
+ return;
196
+ const album = new MusicAlbumCompact_1.MusicAlbumCompact({
197
+ client,
198
+ title: albumRaw.text,
199
+ id: (_a = albumRaw.navigationEndpoint) === null || _a === void 0 ? void 0 : _a.browseEndpoint.browseId,
200
+ });
201
+ return album;
202
+ }
203
+ static parseArtists(items, client) {
204
+ return this.parseArtistsOrChannel(items).map((r) => {
205
+ var _a;
206
+ return new MusicBaseArtist_1.MusicBaseArtist({
207
+ client,
208
+ name: r.text,
209
+ id: (_a = r.navigationEndpoint) === null || _a === void 0 ? void 0 : _a.browseEndpoint.browseId,
210
+ });
211
+ });
212
+ }
213
+ static parseChannel(items, client) {
214
+ var _a;
215
+ const [channelRaw] = this.parseArtistsOrChannel(items);
216
+ if (!channelRaw)
217
+ return;
218
+ const channel = new MusicBaseChannel_1.MusicBaseChannel({
219
+ client,
220
+ name: channelRaw.text,
221
+ id: (_a = channelRaw.navigationEndpoint) === null || _a === void 0 ? void 0 : _a.browseEndpoint.browseId,
222
+ });
223
+ return channel;
224
+ }
225
+ static parseArtistsOrChannel(items) {
226
+ return items.filter((i) => {
227
+ var _a;
228
+ const pageType = (_a = i.navigationEndpoint) === null || _a === void 0 ? void 0 : _a.browseEndpoint.browseEndpointContextSupportedConfigs.browseEndpointContextMusicConfig.pageType;
229
+ return (pageType === "MUSIC_PAGE_TYPE_ARTIST" || pageType == "MUSIC_PAGE_TYPE_USER_CHANNEL");
230
+ });
231
+ }
41
232
  }
42
233
  exports.MusicSearchResultParser = MusicSearchResultParser;
@@ -10,6 +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("./MusicAllSearchResultParser"), exports);
14
13
  __exportStar(require("./MusicSearchResult"), exports);
15
14
  __exportStar(require("./MusicSearchResultParser"), exports);
@@ -5,6 +5,7 @@ const Base_1 = require("../Base");
5
5
  const BaseChannelParser_1 = require("./BaseChannelParser");
6
6
  const ChannelLive_1 = require("./ChannelLive");
7
7
  const ChannelPlaylists_1 = require("./ChannelPlaylists");
8
+ const ChannelPosts_1 = require("./ChannelPosts");
8
9
  const ChannelShorts_1 = require("./ChannelShorts");
9
10
  const ChannelVideos_1 = require("./ChannelVideos");
10
11
  /** Represents a Youtube Channel */
@@ -17,6 +18,7 @@ class BaseChannel extends Base_1.Base {
17
18
  this.shorts = new ChannelShorts_1.ChannelShorts({ channel: this, client: this.client });
18
19
  this.live = new ChannelLive_1.ChannelLive({ channel: this, client: this.client });
19
20
  this.playlists = new ChannelPlaylists_1.ChannelPlaylists({ channel: this, client: this.client });
21
+ this.posts = new ChannelPosts_1.ChannelPosts({ channel: this, client: this.client });
20
22
  }
21
23
  /** The URL of the channel page */
22
24
  get url() {
@@ -13,13 +13,13 @@ class BaseChannelParser {
13
13
  }
14
14
  /** Parse tab data from request, tab name is ignored if it's a continuation data */
15
15
  static parseTabData(name, data) {
16
- var _a, _b, _c, _d, _e;
16
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
17
17
  const tab = (_a = data.contents) === null || _a === void 0 ? void 0 : _a.twoColumnBrowseResultsRenderer.tabs.find((t) => {
18
18
  var _a;
19
19
  return (((_a = t.tabRenderer) === null || _a === void 0 ? void 0 : _a.endpoint.browseEndpoint.params) ===
20
20
  BaseChannelParser.TAB_TYPE_PARAMS[name]);
21
21
  });
22
- return (((_d = (_c = (_b = tab === null || tab === void 0 ? void 0 : tab.tabRenderer.content.sectionListRenderer) === null || _b === void 0 ? void 0 : _b.contents) === null || _c === void 0 ? void 0 : _c[0].itemSectionRenderer.contents[0].gridRenderer) === null || _d === void 0 ? void 0 : _d.items) || (tab === null || tab === void 0 ? void 0 : tab.tabRenderer.content.richGridRenderer.contents.map((c) => { var _a; return ((_a = c.richItemRenderer) === null || _a === void 0 ? void 0 : _a.content) || c; })) || ((_e = data.onResponseReceivedActions) === null || _e === void 0 ? void 0 : _e[0].appendContinuationItemsAction.continuationItems.map((c) => { var _a; return ((_a = c.richItemRenderer) === null || _a === void 0 ? void 0 : _a.content) || c; })) ||
22
+ return (((_d = (_c = (_b = tab === null || tab === void 0 ? void 0 : tab.tabRenderer.content.sectionListRenderer) === null || _b === void 0 ? void 0 : _b.contents) === null || _c === void 0 ? void 0 : _c[0].itemSectionRenderer.contents[0].gridRenderer) === null || _d === void 0 ? void 0 : _d.items) || ((_h = (_g = (_f = (_e = tab === null || tab === void 0 ? void 0 : tab.tabRenderer.content) === null || _e === void 0 ? void 0 : _e.sectionListRenderer) === null || _f === void 0 ? void 0 : _f.contents) === null || _g === void 0 ? void 0 : _g[0].itemSectionRenderer) === null || _h === void 0 ? void 0 : _h.contents) || (tab === null || tab === void 0 ? void 0 : tab.tabRenderer.content.richGridRenderer.contents.map((c) => { var _a; return ((_a = c.richItemRenderer) === null || _a === void 0 ? void 0 : _a.content) || c; })) || ((_j = data.onResponseReceivedActions) === null || _j === void 0 ? void 0 : _j[0].appendContinuationItemsAction.continuationItems.map((c) => { var _a; return ((_a = c.richItemRenderer) === null || _a === void 0 ? void 0 : _a.content) || c; })) || ((_k = data.onResponseReceivedEndpoints) === null || _k === void 0 ? void 0 : _k[0].appendContinuationItemsAction.continuationItems) ||
23
23
  []);
24
24
  }
25
25
  }
@@ -29,4 +29,5 @@ BaseChannelParser.TAB_TYPE_PARAMS = {
29
29
  shorts: "EgZzaG9ydHPyBgUKA5oBAA%3D%3D",
30
30
  live: "EgdzdHJlYW1z8gYECgJ6AA%3D%3D",
31
31
  playlists: "EglwbGF5bGlzdHPyBgQKAkIA",
32
+ posts: "EgVwb3N0c_IGBAoCSgA%3D",
32
33
  };
@@ -46,10 +46,20 @@ class ChannelPlaylists extends Continuable_1.Continuable {
46
46
  });
47
47
  const items = BaseChannelParser_1.BaseChannelParser.parseTabData("playlists", response.data);
48
48
  const continuation = common_1.getContinuationFromItems(items);
49
- const data = common_1.mapFilter(items, "gridPlaylistRenderer");
49
+ const data = items.filter((i) => "gridPlaylistRenderer" in i || "lockupViewModel" in i);
50
50
  return {
51
51
  continuation,
52
- items: data.map((i) => new PlaylistCompact_1.PlaylistCompact({ client: this.client, channel: this.channel }).load(i)),
52
+ items: data.map((i) => {
53
+ const playlist = new PlaylistCompact_1.PlaylistCompact({
54
+ client: this.client,
55
+ channel: this.channel,
56
+ });
57
+ if (i.gridPlaylistRenderer)
58
+ playlist.load(i.gridPlaylistRenderer);
59
+ else if (i.lockupViewModel)
60
+ playlist.loadLockup(i.lockupViewModel);
61
+ return playlist;
62
+ }),
53
63
  };
54
64
  });
55
65
  }
@@ -0,0 +1,59 @@
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.ChannelPosts = void 0;
13
+ const common_1 = require("../../common");
14
+ const Continuable_1 = require("../Continuable");
15
+ const Post_1 = require("../Post");
16
+ const constants_1 = require("../constants");
17
+ const BaseChannelParser_1 = require("./BaseChannelParser");
18
+ /**
19
+ * {@link Continuable} of posts inside a {@link BaseChannel}
20
+ *
21
+ * @example
22
+ * ```js
23
+ * const channel = await youtube.findOne(CHANNEL_NAME, {type: "channel"});
24
+ * await channel.posts.next();
25
+ * console.log(channel.posts.items) // first 30 posts
26
+ *
27
+ * let newPosts = await channel.posts.next();
28
+ * console.log(newPosts) // 30 loaded posts
29
+ * console.log(channel.posts.items) // first 60 posts
30
+ *
31
+ * await channel.posts.next(0); // load the rest of the posts in the channel
32
+ * ```
33
+ */
34
+ class ChannelPosts extends Continuable_1.Continuable {
35
+ /** @hidden */
36
+ constructor({ client, channel }) {
37
+ super({ client, strictContinuationCheck: true });
38
+ this.channel = channel;
39
+ }
40
+ fetch() {
41
+ var _a;
42
+ return __awaiter(this, void 0, void 0, function* () {
43
+ const params = BaseChannelParser_1.BaseChannelParser.TAB_TYPE_PARAMS.posts;
44
+ const response = yield this.client.http.post(`${constants_1.I_END_POINT}/browse`, {
45
+ data: { browseId: (_a = this.channel) === null || _a === void 0 ? void 0 : _a.id, params, continuation: this.continuation },
46
+ });
47
+ const items = BaseChannelParser_1.BaseChannelParser.parseTabData("posts", response.data);
48
+ const continuation = common_1.getContinuationFromItems(items);
49
+ const data = items
50
+ .map((i) => { var _a, _b; return (_b = (_a = i.backstagePostThreadRenderer) === null || _a === void 0 ? void 0 : _a.post) === null || _b === void 0 ? void 0 : _b.backstagePostRenderer; })
51
+ .filter((i) => i !== undefined);
52
+ return {
53
+ continuation,
54
+ items: data.map((i) => new Post_1.Post({ client: this.client, channel: this.channel }).load(i)),
55
+ };
56
+ });
57
+ }
58
+ }
59
+ exports.ChannelPosts = ChannelPosts;
@@ -28,14 +28,16 @@ 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)) || [];
35
37
  target.description = videoInfo.videoDetails.shortDescription || "";
36
38
  // related videos
37
39
  let secondaryContents = (_c = data.response.contents.twoColumnWatchNextResults.secondaryResults) === null || _c === void 0 ? void 0 : _c.secondaryResults.results;
38
- const itemSectionRenderer = (_d = secondaryContents.find((c) => {
40
+ const itemSectionRenderer = (_d = secondaryContents === null || secondaryContents === void 0 ? void 0 : secondaryContents.find((c) => {
39
41
  return c.itemSectionRenderer;
40
42
  })) === null || _d === void 0 ? void 0 : _d.itemSectionRenderer;
41
43
  if (itemSectionRenderer)
@@ -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
@@ -7,7 +7,7 @@ const PlaylistCompact_1 = require("../PlaylistCompact");
7
7
  const VideoCompact_1 = require("../VideoCompact");
8
8
  class ChannelParser {
9
9
  static loadChannel(target, data) {
10
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
10
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
11
11
  let channelId, title, handle, description, avatar, subscriberCountText, videoCountText, tvBanner, mobileBanner, banner;
12
12
  const { c4TabbedHeaderRenderer, pageHeaderRenderer } = data.header;
13
13
  if (c4TabbedHeaderRenderer) {
@@ -26,11 +26,15 @@ class ChannelParser {
26
26
  .browseEndpoint.browseId;
27
27
  title = pageHeaderRenderer.pageTitle;
28
28
  const { metadata, image: imageModel, banner: bannerModel, description: descriptionModel, } = pageHeaderRenderer.content.pageHeaderViewModel;
29
- const metadataRow = metadata.contentMetadataViewModel.metadataRows.find((m) => m.metadataParts && m.metadataParts.length == 2);
30
- const handleRow = metadata.contentMetadataViewModel.metadataRows.find((m) => m.metadataParts && m.metadataParts.length == 1);
31
- handle = (_j = handleRow === null || handleRow === void 0 ? void 0 : handleRow.metadataParts[0].text) === null || _j === void 0 ? void 0 : _j.content;
32
- videoCountText = (_k = metadataRow.metadataParts.find((m) => m.text.styleRuns)) === null || _k === void 0 ? void 0 : _k.text.content;
33
- subscriberCountText = (_l = metadataRow.metadataParts.find((m) => !m.text.styleRuns)) === null || _l === void 0 ? void 0 : _l.text.content;
29
+ const metadataParts = metadata.contentMetadataViewModel.metadataRows
30
+ .map((m) => m.metadataParts)
31
+ .flat();
32
+ const handlePart = metadataParts.find((m) => { var _a; return (_a = m.text.styleRuns) === null || _a === void 0 ? void 0 : _a.some((s) => "weightLabel" in s); });
33
+ const subscriberCountPart = metadataParts.find((m) => m.accessibilityLabel);
34
+ const videoCountPart = metadataParts.find((m) => { var _a; return (_a = m.text.styleRuns) === null || _a === void 0 ? void 0 : _a.some((s) => "startIndex" in s); });
35
+ handle = (_j = handlePart === null || handlePart === void 0 ? void 0 : handlePart.text) === null || _j === void 0 ? void 0 : _j.content;
36
+ videoCountText = videoCountPart === null || videoCountPart === void 0 ? void 0 : videoCountPart.text.content;
37
+ subscriberCountText = subscriberCountPart === null || subscriberCountPart === void 0 ? void 0 : subscriberCountPart.text.content;
34
38
  avatar = imageModel.decoratedAvatarViewModel.avatar.avatarViewModel.image.sources;
35
39
  banner = bannerModel === null || bannerModel === void 0 ? void 0 : bannerModel.imageBannerViewModel.image.sources;
36
40
  description = descriptionModel === null || descriptionModel === void 0 ? void 0 : descriptionModel.descriptionPreviewViewModel.description.content;
@@ -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
+ var _a;
28
+ const lockupMetadataViewModel = data.metadata.lockupMetadataViewModel;
29
+ const channelMetadata = (_a = lockupMetadataViewModel.metadata.contentMetadataViewModel.metadataRows) === null || _a === void 0 ? void 0 : _a[0].metadataParts[0];
30
+ const thumbnailViewModel = data.contentImage.collectionThumbnailViewModel.primaryThumbnail.thumbnailViewModel;
31
+ if (channelMetadata === null || channelMetadata === void 0 ? void 0 : 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;
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Post = void 0;
4
+ const Base_1 = require("../Base");
5
+ const PostParser_1 = require("./PostParser");
6
+ /** Represents a chat in a live stream */
7
+ class Post extends Base_1.Base {
8
+ /** @hidden */
9
+ constructor(attr) {
10
+ super(attr.client);
11
+ Object.assign(this, attr);
12
+ }
13
+ /**
14
+ * Load this instance with raw data from Youtube
15
+ *
16
+ * @hidden
17
+ */
18
+ load(data) {
19
+ PostParser_1.PostParser.loadPost(this, data);
20
+ return this;
21
+ }
22
+ }
23
+ exports.Post = Post;