youtubei 1.2.1 → 1.3.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 (34) hide show
  1. package/dist/cjs/music/MusicClient/MusicClient.js +13 -13
  2. package/dist/cjs/music/MusicClient/index.js +0 -1
  3. package/dist/cjs/music/MusicContinuable/MusicContinuable.js +43 -0
  4. package/dist/cjs/music/MusicContinuable/index.js +13 -0
  5. package/dist/cjs/music/{MusicClient/MusicSearchResultParser.js → MusicSearchResult/MusicAllSearchResultParser.js} +29 -22
  6. package/dist/cjs/music/MusicSearchResult/MusicSearchResult.js +92 -0
  7. package/dist/cjs/music/MusicSearchResult/MusicSearchResultParser.js +34 -0
  8. package/dist/cjs/music/MusicSearchResult/index.js +15 -0
  9. package/dist/cjs/music/MusicSearchResult/proto/MusicSearchProto.js +2 -0
  10. package/dist/cjs/music/MusicSearchResult/proto/index.js +35 -0
  11. package/dist/cjs/youtube/Playlist/PlaylistParser.js +2 -1
  12. package/dist/esm/music/MusicClient/MusicClient.js +15 -14
  13. package/dist/esm/music/MusicClient/index.js +0 -1
  14. package/dist/esm/music/MusicContinuable/MusicContinuable.js +125 -0
  15. package/dist/esm/music/MusicContinuable/index.js +1 -0
  16. package/dist/esm/music/{MusicClient/MusicSearchResultParser.js → MusicSearchResult/MusicAllSearchResultParser.js} +27 -24
  17. package/dist/esm/music/MusicSearchResult/MusicSearchResult.js +169 -0
  18. package/dist/esm/music/MusicSearchResult/MusicSearchResultParser.js +56 -0
  19. package/dist/esm/music/MusicSearchResult/index.js +3 -0
  20. package/dist/esm/music/MusicSearchResult/proto/MusicSearchProto.js +1 -0
  21. package/dist/esm/music/MusicSearchResult/proto/index.js +15 -0
  22. package/dist/esm/youtube/Playlist/PlaylistParser.js +3 -2
  23. package/dist/typings/music/MusicBaseArtist/MusicBaseArtist.d.ts +1 -1
  24. package/dist/typings/music/MusicClient/MusicClient.d.ts +2 -0
  25. package/dist/typings/music/MusicClient/index.d.ts +0 -1
  26. package/dist/typings/music/MusicContinuable/MusicContinuable.d.ts +24 -0
  27. package/dist/typings/music/MusicContinuable/index.d.ts +1 -0
  28. package/dist/typings/music/{MusicClient/MusicSearchResultParser.d.ts → MusicSearchResult/MusicAllSearchResultParser.d.ts} +4 -3
  29. package/dist/typings/music/MusicSearchResult/MusicSearchResult.d.ts +46 -0
  30. package/dist/typings/music/MusicSearchResult/MusicSearchResultParser.d.ts +13 -0
  31. package/dist/typings/music/MusicSearchResult/index.d.ts +3 -0
  32. package/dist/typings/music/MusicSearchResult/proto/MusicSearchProto.d.ts +14 -0
  33. package/dist/typings/music/MusicSearchResult/proto/index.d.ts +5 -0
  34. package/package.json +1 -1
@@ -12,27 +12,27 @@ Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.MusicClient = void 0;
13
13
  const common_1 = require("../../common");
14
14
  const MusicLyrics_1 = require("../MusicLyrics");
15
+ const MusicSearchResult_1 = require("../MusicSearchResult");
15
16
  const constants_1 = require("../constants");
16
- const MusicSearchResultParser_1 = require("./MusicSearchResultParser");
17
17
  /** Youtube Music Client */
18
18
  class MusicClient {
19
19
  constructor(options = {}) {
20
20
  const fullOptions = Object.assign(Object.assign({ initialCookie: "", fetchOptions: {} }, options), { youtubeClientOptions: Object.assign({ hl: "en", gl: "US" }, options.youtubeClientOptions) });
21
21
  this.http = new common_1.HTTP(Object.assign({ apiKey: constants_1.INNERTUBE_API_KEY, baseUrl: constants_1.BASE_URL, clientName: "WEB_REMIX", clientVersion: constants_1.INNERTUBE_CLIENT_VERSION }, fullOptions));
22
22
  }
23
- /**
24
- * Searches for video, song, album, playlist, or artist
25
- *
26
- * @param query The search query
27
- * @param options Search options
28
- *
29
- */
30
- search(query) {
23
+ search(query, type) {
31
24
  return __awaiter(this, void 0, void 0, function* () {
32
- const response = yield this.http.post(`${constants_1.I_END_POINT}/search`, {
33
- data: { query },
34
- });
35
- return MusicSearchResultParser_1.MusicSearchResultParser.parseSearchResult(response.data, this);
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
+ }
36
36
  });
37
37
  }
38
38
  /**
@@ -11,4 +11,3 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
11
11
  };
12
12
  Object.defineProperty(exports, "__esModule", { value: true });
13
13
  __exportStar(require("./MusicClient"), exports);
14
- __exportStar(require("./MusicSearchResultParser"), exports);
@@ -0,0 +1,43 @@
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.MusicContinuable = void 0;
13
+ const MusicBase_1 = require("../MusicBase");
14
+ /** Represents a continuable list of items `T` (like pagination) */
15
+ class MusicContinuable extends MusicBase_1.MusicBase {
16
+ /** @hidden */
17
+ constructor({ client, strictContinuationCheck }) {
18
+ super(client);
19
+ this.items = [];
20
+ this.strictContinuationCheck = !!strictContinuationCheck;
21
+ if (this.strictContinuationCheck)
22
+ this.continuation = null;
23
+ }
24
+ /** Fetch next items using continuation token */
25
+ next(count = 1) {
26
+ return __awaiter(this, void 0, void 0, function* () {
27
+ const newItems = [];
28
+ for (let i = 0; i < count || count == 0; i++) {
29
+ if (!this.hasContinuation)
30
+ break;
31
+ const { items, continuation } = yield this.fetch();
32
+ this.continuation = continuation;
33
+ newItems.push(...items);
34
+ }
35
+ this.items.push(...newItems);
36
+ return newItems;
37
+ });
38
+ }
39
+ get hasContinuation() {
40
+ return this.strictContinuationCheck ? this.continuation !== undefined : !!this.continuation;
41
+ }
42
+ }
43
+ exports.MusicContinuable = MusicContinuable;
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5
+ }) : (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ o[k2] = m[k];
8
+ }));
9
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
10
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
11
+ };
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ __exportStar(require("./MusicContinuable"), exports);
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.MusicSearchResultParser = void 0;
3
+ exports.MusicAllSearchResultParser = void 0;
4
4
  const common_1 = require("../../common");
5
5
  const MusicAlbumCompact_1 = require("../MusicAlbumCompact");
6
6
  const MusicArtistCompact_1 = require("../MusicArtistCompact");
@@ -9,7 +9,7 @@ const MusicBaseChannel_1 = require("../MusicBaseChannel");
9
9
  const MusicPlaylistCompact_1 = require("../MusicPlaylistCompact");
10
10
  const MusicSongCompact_1 = require("../MusicSongCompact");
11
11
  const MusicVideoCompact_1 = require("../MusicVideoCompact");
12
- class MusicSearchResultParser {
12
+ class MusicAllSearchResultParser {
13
13
  static parseSearchResult(data, client) {
14
14
  const sectionListContents = data.contents.tabbedSearchResultsRenderer.tabs[0].tabRenderer.content
15
15
  .sectionListRenderer.contents;
@@ -18,7 +18,7 @@ class MusicSearchResultParser {
18
18
  .map((m) => m.musicShelfRenderer);
19
19
  return shelves.map((m) => ({
20
20
  title: m.title.runs.map((r) => r.text).join(),
21
- items: m.contents.map((c) => MusicSearchResultParser.parseSearchItem(c, client)),
21
+ items: m.contents.map((c) => MusicAllSearchResultParser.parseSearchItem(c, client)),
22
22
  }));
23
23
  }
24
24
  static parseSearchItem(content, client) {
@@ -30,16 +30,16 @@ class MusicSearchResultParser {
30
30
  .watchEndpointMusicConfig.musicVideoType;
31
31
  if (pageType === "MUSIC_VIDEO_TYPE_PODCAST_EPISODE")
32
32
  return;
33
- return MusicSearchResultParser.parseVideoItem(item, pageType, client);
33
+ return MusicAllSearchResultParser.parseVideoItem(item, pageType, client);
34
34
  }
35
35
  else if (playEndpoint === null || playEndpoint === void 0 ? void 0 : playEndpoint.watchPlaylistEndpoint.params) {
36
- return MusicSearchResultParser.parsePlaylistItem(item, client);
36
+ return MusicAllSearchResultParser.parsePlaylistItem(item, client);
37
37
  }
38
38
  else if (playEndpoint === null || playEndpoint === void 0 ? void 0 : playEndpoint.watchPlaylistEndpoint) {
39
- return MusicSearchResultParser.parseAlbumItem(item, client);
39
+ return MusicAllSearchResultParser.parseAlbumItem(item, client);
40
40
  }
41
41
  else {
42
- return MusicSearchResultParser.parseArtistItem(item, client);
42
+ return MusicAllSearchResultParser.parseArtistItem(item, client);
43
43
  }
44
44
  }
45
45
  static parseVideoItem(item, pageType, client) {
@@ -48,7 +48,7 @@ class MusicSearchResultParser {
48
48
  const title = topColumn[0].text;
49
49
  const duration = common_1.getDuration(bottomColumn.at(-1).text) || undefined;
50
50
  const thumbnails = new common_1.Thumbnails().load(item.thumbnail.musicThumbnailRenderer.thumbnail.thumbnails);
51
- const artists = MusicSearchResultParser.parseArtists(bottomColumn, client);
51
+ const artists = MusicAllSearchResultParser.parseArtists(bottomColumn, client);
52
52
  if (pageType === "MUSIC_VIDEO_TYPE_ATV") {
53
53
  const rawAlbum = bottomColumn.find((r) => {
54
54
  var _a;
@@ -82,7 +82,7 @@ class MusicSearchResultParser {
82
82
  const title = topColumn[0].text;
83
83
  const songCount = common_1.stripToInt(bottomColumn.at(-1).text) || undefined;
84
84
  const thumbnails = new common_1.Thumbnails().load(item.thumbnail.musicThumbnailRenderer.thumbnail.thumbnails);
85
- const channel = MusicSearchResultParser.parseChannel(bottomColumn, client);
85
+ const channel = MusicAllSearchResultParser.parseChannel(bottomColumn, client);
86
86
  return new MusicPlaylistCompact_1.MusicPlaylistCompact({ client, id, title, thumbnails, songCount, channel });
87
87
  }
88
88
  static parseAlbumItem(item, client) {
@@ -92,7 +92,7 @@ class MusicSearchResultParser {
92
92
  const title = topColumn[0].text;
93
93
  const year = common_1.stripToInt(bottomColumn.at(-1).text) || undefined;
94
94
  const thumbnails = new common_1.Thumbnails().load(item.thumbnail.musicThumbnailRenderer.thumbnail.thumbnails);
95
- const artists = MusicSearchResultParser.parseArtists(bottomColumn, client);
95
+ const artists = MusicAllSearchResultParser.parseArtists(bottomColumn, client);
96
96
  return new MusicAlbumCompact_1.MusicAlbumCompact({ client, id, title, thumbnails, artists, year });
97
97
  }
98
98
  static parseArtistItem(item, client) {
@@ -103,26 +103,33 @@ class MusicSearchResultParser {
103
103
  return new MusicArtistCompact_1.MusicArtistCompact({ client, id, name, thumbnails });
104
104
  }
105
105
  static parseArtists(items, client) {
106
- return this.parseArtistOrChannel(items).map((r) => new MusicBaseArtist_1.MusicBaseArtist({
107
- client,
108
- name: r.text,
109
- id: r.navigationEndpoint.browseEndpoint.browseId,
110
- }));
106
+ return this.parseArtistOrChannel(items).map((r) => {
107
+ var _a;
108
+ return new MusicBaseArtist_1.MusicBaseArtist({
109
+ client,
110
+ name: r.text,
111
+ id: (_a = r.navigationEndpoint) === null || _a === void 0 ? void 0 : _a.browseEndpoint.browseId,
112
+ });
113
+ });
111
114
  }
112
115
  static parseChannel(items, client) {
113
- const [channel] = this.parseArtistOrChannel(items).map((r) => new MusicBaseChannel_1.MusicBaseChannel({
114
- client,
115
- name: r.text,
116
- id: r.navigationEndpoint.browseEndpoint.browseId,
117
- }));
116
+ const [channel] = this.parseArtistOrChannel(items).map((r) => {
117
+ var _a;
118
+ return new MusicBaseChannel_1.MusicBaseChannel({
119
+ client,
120
+ name: r.text,
121
+ id: (_a = r.navigationEndpoint) === null || _a === void 0 ? void 0 : _a.browseEndpoint.browseId,
122
+ });
123
+ });
118
124
  return channel;
119
125
  }
120
126
  static parseArtistOrChannel(items) {
121
- return items.filter((r) => {
127
+ const contents = items.filter((r) => {
122
128
  var _a;
123
129
  const pageType = (_a = r.navigationEndpoint) === null || _a === void 0 ? void 0 : _a.browseEndpoint.browseEndpointContextSupportedConfigs.browseEndpointContextMusicConfig.pageType;
124
130
  return (pageType === "MUSIC_PAGE_TYPE_ARTIST" || pageType === "MUSIC_PAGE_TYPE_USER_CHANNEL");
125
131
  });
132
+ return !contents.length && items[0] ? [items[0]] : contents;
126
133
  }
127
134
  }
128
- exports.MusicSearchResultParser = MusicSearchResultParser;
135
+ exports.MusicAllSearchResultParser = MusicAllSearchResultParser;
@@ -0,0 +1,92 @@
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.MusicSearchResult = exports.MusicSearchTypeEnum = void 0;
13
+ const MusicContinuable_1 = require("../MusicContinuable");
14
+ const constants_1 = require("../constants");
15
+ const MusicSearchResultParser_1 = require("./MusicSearchResultParser");
16
+ const proto_1 = require("./proto");
17
+ var MusicSearchTypeEnum;
18
+ (function (MusicSearchTypeEnum) {
19
+ MusicSearchTypeEnum["Song"] = "song";
20
+ MusicSearchTypeEnum["Video"] = "video";
21
+ })(MusicSearchTypeEnum = exports.MusicSearchTypeEnum || (exports.MusicSearchTypeEnum = {}));
22
+ /**
23
+ * Represents search result, usually returned from `client.search();`.
24
+ *
25
+ * {@link MusicSearchResult} is a helper class to manage search result
26
+ *
27
+ * @example
28
+ * ```ts
29
+ * const result = await music.search("Keyword", "song");
30
+ *
31
+ * console.log(result.items); // search result from first page
32
+ *
33
+ * let nextSearchResult = await result.next();
34
+ * console.log(nextSearchResult); // search result from second page
35
+ *
36
+ * nextSearchResult = await result.next();
37
+ * console.log(nextSearchResult); // search result from third page
38
+ *
39
+ * console.log(result.items); // search result from first, second, and third page.
40
+ * ```
41
+ *
42
+ * @noInheritDoc
43
+ */
44
+ class MusicSearchResult extends MusicContinuable_1.MusicContinuable {
45
+ /** @hidden */
46
+ constructor({ client }) {
47
+ super({ client });
48
+ }
49
+ /**
50
+ * Initialize data from search
51
+ *
52
+ * @param query Search query
53
+ * @param options Search Options
54
+ *
55
+ * @hidden
56
+ */
57
+ search(query, type) {
58
+ return __awaiter(this, void 0, void 0, function* () {
59
+ this.items = [];
60
+ const bufferParams = proto_1.MusicSearchProto.encode(proto_1.optionsToProto(type)).finish();
61
+ const response = yield this.client.http.post(`${constants_1.I_END_POINT}/search`, {
62
+ data: {
63
+ query,
64
+ params: Buffer.from(bufferParams).toString("base64"),
65
+ },
66
+ });
67
+ const { data, continuation } = MusicSearchResultParser_1.MusicSearchResultParser.parseInitialSearchResult(response.data, type, this.client);
68
+ this.items.push(...data);
69
+ this.continuation = continuation;
70
+ return this;
71
+ });
72
+ }
73
+ fetch() {
74
+ return __awaiter(this, void 0, void 0, function* () {
75
+ if (!this.type) {
76
+ return {
77
+ items: [],
78
+ continuation: undefined,
79
+ };
80
+ }
81
+ const response = yield this.client.http.post(`${constants_1.I_END_POINT}/search`, {
82
+ data: { continuation: this.continuation },
83
+ });
84
+ const { data, continuation } = MusicSearchResultParser_1.MusicSearchResultParser.parseContinuationSearchResult(response.data, this.type, this.client);
85
+ return {
86
+ items: data,
87
+ continuation,
88
+ };
89
+ });
90
+ }
91
+ }
92
+ exports.MusicSearchResult = MusicSearchResult;
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MusicSearchResultParser = void 0;
4
+ const MusicAllSearchResultParser_1 = require("./MusicAllSearchResultParser");
5
+ class MusicSearchResultParser {
6
+ static parseInitialSearchResult(data, type, client) {
7
+ var _a, _b;
8
+ const { contents, continuations, } = data.contents.tabbedSearchResultsRenderer.tabs[0].tabRenderer.content.sectionListRenderer.contents[0].musicShelfRenderer;
9
+ return {
10
+ data: MusicSearchResultParser.parseSearchResult(contents, type, client),
11
+ continuation: (_b = (_a = continuations[0]) === null || _a === void 0 ? void 0 : _a.nextContinuationData) === null || _b === void 0 ? void 0 : _b.continuation,
12
+ };
13
+ }
14
+ static parseContinuationSearchResult(data, type, client) {
15
+ const shelf = data.continuationContents.musicShelfContinuation;
16
+ return {
17
+ data: MusicSearchResultParser.parseSearchResult(shelf.contents, type, client),
18
+ continuation: shelf.continuations[0].nextContinuationData.continuation,
19
+ };
20
+ }
21
+ static parseSearchResult(shelfContents, type, client) {
22
+ const rawContents = shelfContents
23
+ .filter((c) => "musicResponsiveListItemRenderer" in c)
24
+ .map((c) => c.musicResponsiveListItemRenderer);
25
+ const contents = [];
26
+ for (const c of rawContents) {
27
+ const parsed = MusicAllSearchResultParser_1.MusicAllSearchResultParser.parseVideoItem(c, type === "video" ? "MUSIC_VIDEO_TYPE_UGC" : "MUSIC_VIDEO_TYPE_ATV", client);
28
+ if (parsed)
29
+ contents.push(parsed);
30
+ }
31
+ return contents;
32
+ }
33
+ }
34
+ exports.MusicSearchResultParser = MusicSearchResultParser;
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5
+ }) : (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ o[k2] = m[k];
8
+ }));
9
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
10
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
11
+ };
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ __exportStar(require("./MusicAllSearchResultParser"), exports);
14
+ __exportStar(require("./MusicSearchResult"), exports);
15
+ __exportStar(require("./MusicSearchResultParser"), exports);
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.optionsToProto = exports.MusicSearchProto = void 0;
7
+ const protobufjs_1 = __importDefault(require("protobufjs"));
8
+ // TODO move this to .proto file
9
+ exports.MusicSearchProto = protobufjs_1.default
10
+ .parse(`
11
+ message MusicSearchOptions {
12
+ message Options {
13
+ optional int32 song = 1;
14
+ optional int32 video = 2;
15
+ }
16
+
17
+ message Params {
18
+ optional Options options = 17;
19
+ }
20
+
21
+ optional Params params = 2;
22
+ }
23
+ `)
24
+ .root.lookupType("MusicSearchOptions");
25
+ const optionsToProto = (type) => {
26
+ return {
27
+ params: {
28
+ options: {
29
+ song: type === "song" ? 1 : undefined,
30
+ video: type === "video" ? 1 : undefined,
31
+ },
32
+ },
33
+ };
34
+ };
35
+ exports.optionsToProto = optionsToProto;
@@ -14,7 +14,8 @@ class PlaylistParser {
14
14
  target.id = (_a = Object.values(metadata)
15
15
  .find((v) => v.includes("playlist?list="))) === null || _a === void 0 ? void 0 : _a.split("=")[1];
16
16
  target.title = metadata.title;
17
- target.thumbnails = new common_1.Thumbnails().load(primaryRenderer.thumbnailRenderer.playlistVideoThumbnailRenderer.thumbnail.thumbnails);
17
+ const { playlistVideoThumbnailRenderer, playlistCustomThumbnailRenderer, } = primaryRenderer.thumbnailRenderer;
18
+ target.thumbnails = new common_1.Thumbnails().load((playlistVideoThumbnailRenderer || playlistCustomThumbnailRenderer).thumbnail.thumbnails);
18
19
  const { stats } = primaryRenderer;
19
20
  if (primaryRenderer.stats.length === 3) {
20
21
  target.videoCount = PlaylistParser.parseSideBarInfo(stats[0], true);
@@ -47,8 +47,8 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
47
47
  };
48
48
  import { HTTP } from "../../common";
49
49
  import { MusicLyrics } from "../MusicLyrics";
50
+ import { MusicAllSearchResultParser, MusicSearchResult, } from "../MusicSearchResult";
50
51
  import { BASE_URL, INNERTUBE_API_KEY, INNERTUBE_CLIENT_VERSION, I_END_POINT } from "../constants";
51
- import { MusicSearchResultParser } from "./MusicSearchResultParser";
52
52
  /** Youtube Music Client */
53
53
  var MusicClient = /** @class */ (function () {
54
54
  function MusicClient(options) {
@@ -56,24 +56,25 @@ var MusicClient = /** @class */ (function () {
56
56
  var fullOptions = __assign(__assign({ initialCookie: "", fetchOptions: {} }, options), { youtubeClientOptions: __assign({ hl: "en", gl: "US" }, options.youtubeClientOptions) });
57
57
  this.http = new HTTP(__assign({ apiKey: INNERTUBE_API_KEY, baseUrl: BASE_URL, clientName: "WEB_REMIX", clientVersion: INNERTUBE_CLIENT_VERSION }, fullOptions));
58
58
  }
59
- /**
60
- * Searches for video, song, album, playlist, or artist
61
- *
62
- * @param query The search query
63
- * @param options Search options
64
- *
65
- */
66
- MusicClient.prototype.search = function (query) {
59
+ MusicClient.prototype.search = function (query, type) {
67
60
  return __awaiter(this, void 0, void 0, function () {
68
- var response;
61
+ var response, result;
69
62
  return __generator(this, function (_a) {
70
63
  switch (_a.label) {
71
- case 0: return [4 /*yield*/, this.http.post(I_END_POINT + "/search", {
72
- data: { query: query },
73
- })];
64
+ case 0:
65
+ if (!!type) return [3 /*break*/, 2];
66
+ return [4 /*yield*/, this.http.post(I_END_POINT + "/search", {
67
+ data: { query: query },
68
+ })];
74
69
  case 1:
75
70
  response = _a.sent();
76
- return [2 /*return*/, MusicSearchResultParser.parseSearchResult(response.data, this)];
71
+ return [2 /*return*/, MusicAllSearchResultParser.parseSearchResult(response.data, this)];
72
+ case 2:
73
+ result = new MusicSearchResult({ client: this });
74
+ return [4 /*yield*/, result.search(query, type)];
75
+ case 3:
76
+ _a.sent();
77
+ return [2 /*return*/, result];
77
78
  }
78
79
  });
79
80
  });
@@ -1,2 +1 @@
1
1
  export * from "./MusicClient";
2
- export * from "./MusicSearchResultParser";
@@ -0,0 +1,125 @@
1
+ var __extends = (this && this.__extends) || (function () {
2
+ var extendStatics = function (d, b) {
3
+ extendStatics = Object.setPrototypeOf ||
4
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
5
+ function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
6
+ return extendStatics(d, b);
7
+ };
8
+ return function (d, b) {
9
+ extendStatics(d, b);
10
+ function __() { this.constructor = d; }
11
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
12
+ };
13
+ })();
14
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
15
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
16
+ return new (P || (P = Promise))(function (resolve, reject) {
17
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
18
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
19
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
20
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
21
+ });
22
+ };
23
+ var __generator = (this && this.__generator) || function (thisArg, body) {
24
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
25
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
26
+ function verb(n) { return function (v) { return step([n, v]); }; }
27
+ function step(op) {
28
+ if (f) throw new TypeError("Generator is already executing.");
29
+ while (_) try {
30
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
31
+ if (y = 0, t) op = [op[0] & 2, t.value];
32
+ switch (op[0]) {
33
+ case 0: case 1: t = op; break;
34
+ case 4: _.label++; return { value: op[1], done: false };
35
+ case 5: _.label++; y = op[1]; op = [0]; continue;
36
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
37
+ default:
38
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
39
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
40
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
41
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
42
+ if (t[2]) _.ops.pop();
43
+ _.trys.pop(); continue;
44
+ }
45
+ op = body.call(thisArg, _);
46
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
47
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
48
+ }
49
+ };
50
+ var __read = (this && this.__read) || function (o, n) {
51
+ var m = typeof Symbol === "function" && o[Symbol.iterator];
52
+ if (!m) return o;
53
+ var i = m.call(o), r, ar = [], e;
54
+ try {
55
+ while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
56
+ }
57
+ catch (error) { e = { error: error }; }
58
+ finally {
59
+ try {
60
+ if (r && !r.done && (m = i["return"])) m.call(i);
61
+ }
62
+ finally { if (e) throw e.error; }
63
+ }
64
+ return ar;
65
+ };
66
+ var __spread = (this && this.__spread) || function () {
67
+ for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i]));
68
+ return ar;
69
+ };
70
+ import { MusicBase } from "../MusicBase";
71
+ /** Represents a continuable list of items `T` (like pagination) */
72
+ var MusicContinuable = /** @class */ (function (_super) {
73
+ __extends(MusicContinuable, _super);
74
+ /** @hidden */
75
+ function MusicContinuable(_a) {
76
+ var client = _a.client, strictContinuationCheck = _a.strictContinuationCheck;
77
+ var _this = _super.call(this, client) || this;
78
+ _this.items = [];
79
+ _this.strictContinuationCheck = !!strictContinuationCheck;
80
+ if (_this.strictContinuationCheck)
81
+ _this.continuation = null;
82
+ return _this;
83
+ }
84
+ /** Fetch next items using continuation token */
85
+ MusicContinuable.prototype.next = function (count) {
86
+ if (count === void 0) { count = 1; }
87
+ return __awaiter(this, void 0, void 0, function () {
88
+ var newItems, i, _a, items, continuation;
89
+ var _b;
90
+ return __generator(this, function (_c) {
91
+ switch (_c.label) {
92
+ case 0:
93
+ newItems = [];
94
+ i = 0;
95
+ _c.label = 1;
96
+ case 1:
97
+ if (!(i < count || count == 0)) return [3 /*break*/, 4];
98
+ if (!this.hasContinuation)
99
+ return [3 /*break*/, 4];
100
+ return [4 /*yield*/, this.fetch()];
101
+ case 2:
102
+ _a = _c.sent(), items = _a.items, continuation = _a.continuation;
103
+ this.continuation = continuation;
104
+ newItems.push.apply(newItems, __spread(items));
105
+ _c.label = 3;
106
+ case 3:
107
+ i++;
108
+ return [3 /*break*/, 1];
109
+ case 4:
110
+ (_b = this.items).push.apply(_b, __spread(newItems));
111
+ return [2 /*return*/, newItems];
112
+ }
113
+ });
114
+ });
115
+ };
116
+ Object.defineProperty(MusicContinuable.prototype, "hasContinuation", {
117
+ get: function () {
118
+ return this.strictContinuationCheck ? this.continuation !== undefined : !!this.continuation;
119
+ },
120
+ enumerable: false,
121
+ configurable: true
122
+ });
123
+ return MusicContinuable;
124
+ }(MusicBase));
125
+ export { MusicContinuable };
@@ -0,0 +1 @@
1
+ export * from "./MusicContinuable";
@@ -22,10 +22,10 @@ import { MusicBaseChannel } from "../MusicBaseChannel";
22
22
  import { MusicPlaylistCompact } from "../MusicPlaylistCompact";
23
23
  import { MusicSongCompact } from "../MusicSongCompact";
24
24
  import { MusicVideoCompact } from "../MusicVideoCompact";
25
- var MusicSearchResultParser = /** @class */ (function () {
26
- function MusicSearchResultParser() {
25
+ var MusicAllSearchResultParser = /** @class */ (function () {
26
+ function MusicAllSearchResultParser() {
27
27
  }
28
- MusicSearchResultParser.parseSearchResult = function (data, client) {
28
+ MusicAllSearchResultParser.parseSearchResult = function (data, client) {
29
29
  var sectionListContents = data.contents.tabbedSearchResultsRenderer.tabs[0].tabRenderer.content
30
30
  .sectionListRenderer.contents;
31
31
  var shelves = sectionListContents
@@ -34,11 +34,11 @@ var MusicSearchResultParser = /** @class */ (function () {
34
34
  return shelves.map(function (m) { return ({
35
35
  title: m.title.runs.map(function (r) { return r.text; }).join(),
36
36
  items: m.contents.map(function (c) {
37
- return MusicSearchResultParser.parseSearchItem(c, client);
37
+ return MusicAllSearchResultParser.parseSearchItem(c, client);
38
38
  }),
39
39
  }); });
40
40
  };
41
- MusicSearchResultParser.parseSearchItem = function (content, client) {
41
+ MusicAllSearchResultParser.parseSearchItem = function (content, client) {
42
42
  var _a;
43
43
  var item = content.musicResponsiveListItemRenderer;
44
44
  var playEndpoint = (_a = item.overlay) === null || _a === void 0 ? void 0 : _a.musicItemThumbnailOverlayRenderer.content.musicPlayButtonRenderer.playNavigationEndpoint;
@@ -47,25 +47,25 @@ var MusicSearchResultParser = /** @class */ (function () {
47
47
  .watchEndpointMusicConfig.musicVideoType;
48
48
  if (pageType === "MUSIC_VIDEO_TYPE_PODCAST_EPISODE")
49
49
  return;
50
- return MusicSearchResultParser.parseVideoItem(item, pageType, client);
50
+ return MusicAllSearchResultParser.parseVideoItem(item, pageType, client);
51
51
  }
52
52
  else if (playEndpoint === null || playEndpoint === void 0 ? void 0 : playEndpoint.watchPlaylistEndpoint.params) {
53
- return MusicSearchResultParser.parsePlaylistItem(item, client);
53
+ return MusicAllSearchResultParser.parsePlaylistItem(item, client);
54
54
  }
55
55
  else if (playEndpoint === null || playEndpoint === void 0 ? void 0 : playEndpoint.watchPlaylistEndpoint) {
56
- return MusicSearchResultParser.parseAlbumItem(item, client);
56
+ return MusicAllSearchResultParser.parseAlbumItem(item, client);
57
57
  }
58
58
  else {
59
- return MusicSearchResultParser.parseArtistItem(item, client);
59
+ return MusicAllSearchResultParser.parseArtistItem(item, client);
60
60
  }
61
61
  };
62
- MusicSearchResultParser.parseVideoItem = function (item, pageType, client) {
62
+ MusicAllSearchResultParser.parseVideoItem = function (item, pageType, client) {
63
63
  var _a = __read(item.flexColumns.map(function (c) { return c.musicResponsiveListItemFlexColumnRenderer.text.runs; }), 2), topColumn = _a[0], bottomColumn = _a[1];
64
64
  var id = topColumn[0].navigationEndpoint.watchEndpoint.videoId;
65
65
  var title = topColumn[0].text;
66
66
  var duration = getDuration(bottomColumn.at(-1).text) || undefined;
67
67
  var thumbnails = new Thumbnails().load(item.thumbnail.musicThumbnailRenderer.thumbnail.thumbnails);
68
- var artists = MusicSearchResultParser.parseArtists(bottomColumn, client);
68
+ var artists = MusicAllSearchResultParser.parseArtists(bottomColumn, client);
69
69
  if (pageType === "MUSIC_VIDEO_TYPE_ATV") {
70
70
  var rawAlbum = bottomColumn.find(function (r) {
71
71
  var _a;
@@ -92,59 +92,62 @@ var MusicSearchResultParser = /** @class */ (function () {
92
92
  return new MusicVideoCompact({ client: client, id: id, title: title, artists: artists, thumbnails: thumbnails, duration: duration });
93
93
  }
94
94
  };
95
- MusicSearchResultParser.parsePlaylistItem = function (item, client) {
95
+ MusicAllSearchResultParser.parsePlaylistItem = function (item, client) {
96
96
  var _a = __read(item.flexColumns.map(function (c) { return c.musicResponsiveListItemFlexColumnRenderer.text.runs; }), 2), topColumn = _a[0], bottomColumn = _a[1];
97
97
  var id = item.overlay.musicItemThumbnailOverlayRenderer.content.musicPlayButtonRenderer
98
98
  .playNavigationEndpoint.watchPlaylistEndpoint.playlistId;
99
99
  var title = topColumn[0].text;
100
100
  var songCount = stripToInt(bottomColumn.at(-1).text) || undefined;
101
101
  var thumbnails = new Thumbnails().load(item.thumbnail.musicThumbnailRenderer.thumbnail.thumbnails);
102
- var channel = MusicSearchResultParser.parseChannel(bottomColumn, client);
102
+ var channel = MusicAllSearchResultParser.parseChannel(bottomColumn, client);
103
103
  return new MusicPlaylistCompact({ client: client, id: id, title: title, thumbnails: thumbnails, songCount: songCount, channel: channel });
104
104
  };
105
- MusicSearchResultParser.parseAlbumItem = function (item, client) {
105
+ MusicAllSearchResultParser.parseAlbumItem = function (item, client) {
106
106
  var _a = __read(item.flexColumns.map(function (c) { return c.musicResponsiveListItemFlexColumnRenderer.text.runs; }), 2), topColumn = _a[0], bottomColumn = _a[1];
107
107
  var id = item.overlay.musicItemThumbnailOverlayRenderer.content.musicPlayButtonRenderer
108
108
  .playNavigationEndpoint.watchPlaylistEndpoint.playlistId;
109
109
  var title = topColumn[0].text;
110
110
  var year = stripToInt(bottomColumn.at(-1).text) || undefined;
111
111
  var thumbnails = new Thumbnails().load(item.thumbnail.musicThumbnailRenderer.thumbnail.thumbnails);
112
- var artists = MusicSearchResultParser.parseArtists(bottomColumn, client);
112
+ var artists = MusicAllSearchResultParser.parseArtists(bottomColumn, client);
113
113
  return new MusicAlbumCompact({ client: client, id: id, title: title, thumbnails: thumbnails, artists: artists, year: year });
114
114
  };
115
- MusicSearchResultParser.parseArtistItem = function (item, client) {
115
+ MusicAllSearchResultParser.parseArtistItem = function (item, client) {
116
116
  var _a = __read(item.flexColumns.map(function (c) { return c.musicResponsiveListItemFlexColumnRenderer.text.runs; }), 1), topColumn = _a[0];
117
117
  var id = item.navigationEndpoint.browseEndpoint.browseId;
118
118
  var name = topColumn[0].text;
119
119
  var thumbnails = new Thumbnails().load(item.thumbnail.musicThumbnailRenderer.thumbnail.thumbnails);
120
120
  return new MusicArtistCompact({ client: client, id: id, name: name, thumbnails: thumbnails });
121
121
  };
122
- MusicSearchResultParser.parseArtists = function (items, client) {
122
+ MusicAllSearchResultParser.parseArtists = function (items, client) {
123
123
  return this.parseArtistOrChannel(items).map(function (r) {
124
+ var _a;
124
125
  return new MusicBaseArtist({
125
126
  client: client,
126
127
  name: r.text,
127
- id: r.navigationEndpoint.browseEndpoint.browseId,
128
+ id: (_a = r.navigationEndpoint) === null || _a === void 0 ? void 0 : _a.browseEndpoint.browseId,
128
129
  });
129
130
  });
130
131
  };
131
- MusicSearchResultParser.parseChannel = function (items, client) {
132
+ MusicAllSearchResultParser.parseChannel = function (items, client) {
132
133
  var _a = __read(this.parseArtistOrChannel(items).map(function (r) {
134
+ var _a;
133
135
  return new MusicBaseChannel({
134
136
  client: client,
135
137
  name: r.text,
136
- id: r.navigationEndpoint.browseEndpoint.browseId,
138
+ id: (_a = r.navigationEndpoint) === null || _a === void 0 ? void 0 : _a.browseEndpoint.browseId,
137
139
  });
138
140
  }), 1), channel = _a[0];
139
141
  return channel;
140
142
  };
141
- MusicSearchResultParser.parseArtistOrChannel = function (items) {
142
- return items.filter(function (r) {
143
+ MusicAllSearchResultParser.parseArtistOrChannel = function (items) {
144
+ var contents = items.filter(function (r) {
143
145
  var _a;
144
146
  var pageType = (_a = r.navigationEndpoint) === null || _a === void 0 ? void 0 : _a.browseEndpoint.browseEndpointContextSupportedConfigs.browseEndpointContextMusicConfig.pageType;
145
147
  return (pageType === "MUSIC_PAGE_TYPE_ARTIST" || pageType === "MUSIC_PAGE_TYPE_USER_CHANNEL");
146
148
  });
149
+ return !contents.length && items[0] ? [items[0]] : contents;
147
150
  };
148
- return MusicSearchResultParser;
151
+ return MusicAllSearchResultParser;
149
152
  }());
150
- export { MusicSearchResultParser };
153
+ export { MusicAllSearchResultParser };
@@ -0,0 +1,169 @@
1
+ var __extends = (this && this.__extends) || (function () {
2
+ var extendStatics = function (d, b) {
3
+ extendStatics = Object.setPrototypeOf ||
4
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
5
+ function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
6
+ return extendStatics(d, b);
7
+ };
8
+ return function (d, b) {
9
+ extendStatics(d, b);
10
+ function __() { this.constructor = d; }
11
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
12
+ };
13
+ })();
14
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
15
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
16
+ return new (P || (P = Promise))(function (resolve, reject) {
17
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
18
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
19
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
20
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
21
+ });
22
+ };
23
+ var __generator = (this && this.__generator) || function (thisArg, body) {
24
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
25
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
26
+ function verb(n) { return function (v) { return step([n, v]); }; }
27
+ function step(op) {
28
+ if (f) throw new TypeError("Generator is already executing.");
29
+ while (_) try {
30
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
31
+ if (y = 0, t) op = [op[0] & 2, t.value];
32
+ switch (op[0]) {
33
+ case 0: case 1: t = op; break;
34
+ case 4: _.label++; return { value: op[1], done: false };
35
+ case 5: _.label++; y = op[1]; op = [0]; continue;
36
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
37
+ default:
38
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
39
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
40
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
41
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
42
+ if (t[2]) _.ops.pop();
43
+ _.trys.pop(); continue;
44
+ }
45
+ op = body.call(thisArg, _);
46
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
47
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
48
+ }
49
+ };
50
+ var __read = (this && this.__read) || function (o, n) {
51
+ var m = typeof Symbol === "function" && o[Symbol.iterator];
52
+ if (!m) return o;
53
+ var i = m.call(o), r, ar = [], e;
54
+ try {
55
+ while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
56
+ }
57
+ catch (error) { e = { error: error }; }
58
+ finally {
59
+ try {
60
+ if (r && !r.done && (m = i["return"])) m.call(i);
61
+ }
62
+ finally { if (e) throw e.error; }
63
+ }
64
+ return ar;
65
+ };
66
+ var __spread = (this && this.__spread) || function () {
67
+ for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i]));
68
+ return ar;
69
+ };
70
+ import { MusicContinuable, } from "../MusicContinuable";
71
+ import { I_END_POINT } from "../constants";
72
+ import { MusicSearchResultParser } from "./MusicSearchResultParser";
73
+ import { MusicSearchProto, optionsToProto } from "./proto";
74
+ export var MusicSearchTypeEnum;
75
+ (function (MusicSearchTypeEnum) {
76
+ MusicSearchTypeEnum["Song"] = "song";
77
+ MusicSearchTypeEnum["Video"] = "video";
78
+ })(MusicSearchTypeEnum || (MusicSearchTypeEnum = {}));
79
+ /**
80
+ * Represents search result, usually returned from `client.search();`.
81
+ *
82
+ * {@link MusicSearchResult} is a helper class to manage search result
83
+ *
84
+ * @example
85
+ * ```ts
86
+ * const result = await music.search("Keyword", "song");
87
+ *
88
+ * console.log(result.items); // search result from first page
89
+ *
90
+ * let nextSearchResult = await result.next();
91
+ * console.log(nextSearchResult); // search result from second page
92
+ *
93
+ * nextSearchResult = await result.next();
94
+ * console.log(nextSearchResult); // search result from third page
95
+ *
96
+ * console.log(result.items); // search result from first, second, and third page.
97
+ * ```
98
+ *
99
+ * @noInheritDoc
100
+ */
101
+ var MusicSearchResult = /** @class */ (function (_super) {
102
+ __extends(MusicSearchResult, _super);
103
+ /** @hidden */
104
+ function MusicSearchResult(_a) {
105
+ var client = _a.client;
106
+ return _super.call(this, { client: client }) || this;
107
+ }
108
+ /**
109
+ * Initialize data from search
110
+ *
111
+ * @param query Search query
112
+ * @param options Search Options
113
+ *
114
+ * @hidden
115
+ */
116
+ MusicSearchResult.prototype.search = function (query, type) {
117
+ return __awaiter(this, void 0, void 0, function () {
118
+ var bufferParams, response, _a, data, continuation;
119
+ var _b;
120
+ return __generator(this, function (_c) {
121
+ switch (_c.label) {
122
+ case 0:
123
+ this.items = [];
124
+ bufferParams = MusicSearchProto.encode(optionsToProto(type)).finish();
125
+ return [4 /*yield*/, this.client.http.post(I_END_POINT + "/search", {
126
+ data: {
127
+ query: query,
128
+ params: Buffer.from(bufferParams).toString("base64"),
129
+ },
130
+ })];
131
+ case 1:
132
+ response = _c.sent();
133
+ _a = MusicSearchResultParser.parseInitialSearchResult(response.data, type, this.client), data = _a.data, continuation = _a.continuation;
134
+ (_b = this.items).push.apply(_b, __spread(data));
135
+ this.continuation = continuation;
136
+ return [2 /*return*/, this];
137
+ }
138
+ });
139
+ });
140
+ };
141
+ MusicSearchResult.prototype.fetch = function () {
142
+ return __awaiter(this, void 0, void 0, function () {
143
+ var response, _a, data, continuation;
144
+ return __generator(this, function (_b) {
145
+ switch (_b.label) {
146
+ case 0:
147
+ if (!this.type) {
148
+ return [2 /*return*/, {
149
+ items: [],
150
+ continuation: undefined,
151
+ }];
152
+ }
153
+ return [4 /*yield*/, this.client.http.post(I_END_POINT + "/search", {
154
+ data: { continuation: this.continuation },
155
+ })];
156
+ case 1:
157
+ response = _b.sent();
158
+ _a = MusicSearchResultParser.parseContinuationSearchResult(response.data, this.type, this.client), data = _a.data, continuation = _a.continuation;
159
+ return [2 /*return*/, {
160
+ items: data,
161
+ continuation: continuation,
162
+ }];
163
+ }
164
+ });
165
+ });
166
+ };
167
+ return MusicSearchResult;
168
+ }(MusicContinuable));
169
+ export { MusicSearchResult };
@@ -0,0 +1,56 @@
1
+ var __values = (this && this.__values) || function(o) {
2
+ var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
3
+ if (m) return m.call(o);
4
+ if (o && typeof o.length === "number") return {
5
+ next: function () {
6
+ if (o && i >= o.length) o = void 0;
7
+ return { value: o && o[i++], done: !o };
8
+ }
9
+ };
10
+ throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
11
+ };
12
+ import { MusicAllSearchResultParser } from "./MusicAllSearchResultParser";
13
+ var MusicSearchResultParser = /** @class */ (function () {
14
+ function MusicSearchResultParser() {
15
+ }
16
+ MusicSearchResultParser.parseInitialSearchResult = function (data, type, client) {
17
+ var _a, _b;
18
+ var _c = data.contents.tabbedSearchResultsRenderer.tabs[0].tabRenderer.content.sectionListRenderer.contents[0].musicShelfRenderer, contents = _c.contents, continuations = _c.continuations;
19
+ return {
20
+ data: MusicSearchResultParser.parseSearchResult(contents, type, client),
21
+ continuation: (_b = (_a = continuations[0]) === null || _a === void 0 ? void 0 : _a.nextContinuationData) === null || _b === void 0 ? void 0 : _b.continuation,
22
+ };
23
+ };
24
+ MusicSearchResultParser.parseContinuationSearchResult = function (data, type, client) {
25
+ var shelf = data.continuationContents.musicShelfContinuation;
26
+ return {
27
+ data: MusicSearchResultParser.parseSearchResult(shelf.contents, type, client),
28
+ continuation: shelf.continuations[0].nextContinuationData.continuation,
29
+ };
30
+ };
31
+ MusicSearchResultParser.parseSearchResult = function (shelfContents, type, client) {
32
+ var e_1, _a;
33
+ var rawContents = shelfContents
34
+ .filter(function (c) { return "musicResponsiveListItemRenderer" in c; })
35
+ .map(function (c) { return c.musicResponsiveListItemRenderer; });
36
+ var contents = [];
37
+ try {
38
+ for (var rawContents_1 = __values(rawContents), rawContents_1_1 = rawContents_1.next(); !rawContents_1_1.done; rawContents_1_1 = rawContents_1.next()) {
39
+ var c = rawContents_1_1.value;
40
+ var parsed = MusicAllSearchResultParser.parseVideoItem(c, type === "video" ? "MUSIC_VIDEO_TYPE_UGC" : "MUSIC_VIDEO_TYPE_ATV", client);
41
+ if (parsed)
42
+ contents.push(parsed);
43
+ }
44
+ }
45
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
46
+ finally {
47
+ try {
48
+ if (rawContents_1_1 && !rawContents_1_1.done && (_a = rawContents_1.return)) _a.call(rawContents_1);
49
+ }
50
+ finally { if (e_1) throw e_1.error; }
51
+ }
52
+ return contents;
53
+ };
54
+ return MusicSearchResultParser;
55
+ }());
56
+ export { MusicSearchResultParser };
@@ -0,0 +1,3 @@
1
+ export * from "./MusicAllSearchResultParser";
2
+ export * from "./MusicSearchResult";
3
+ export * from "./MusicSearchResultParser";
@@ -0,0 +1,15 @@
1
+ import protobuf from "protobufjs";
2
+ // TODO move this to .proto file
3
+ export var MusicSearchProto = protobuf
4
+ .parse("\n\tmessage MusicSearchOptions {\n\t\tmessage Options {\n\t\t\toptional int32 song = 1;\n\t\t\toptional int32 video = 2;\n\t\t}\n\n\t\tmessage Params {\n\t\t\toptional Options options = 17;\n\t\t}\n\n\t\toptional Params params = 2;\n\t}\n")
5
+ .root.lookupType("MusicSearchOptions");
6
+ export var optionsToProto = function (type) {
7
+ return {
8
+ params: {
9
+ options: {
10
+ song: type === "song" ? 1 : undefined,
11
+ video: type === "video" ? 1 : undefined,
12
+ },
13
+ },
14
+ };
15
+ };
@@ -24,7 +24,8 @@ var PlaylistParser = /** @class */ (function () {
24
24
  target.id = (_a = Object.values(metadata)
25
25
  .find(function (v) { return v.includes("playlist?list="); })) === null || _a === void 0 ? void 0 : _a.split("=")[1];
26
26
  target.title = metadata.title;
27
- target.thumbnails = new Thumbnails().load(primaryRenderer.thumbnailRenderer.playlistVideoThumbnailRenderer.thumbnail.thumbnails);
27
+ var _d = primaryRenderer.thumbnailRenderer, playlistVideoThumbnailRenderer = _d.playlistVideoThumbnailRenderer, playlistCustomThumbnailRenderer = _d.playlistCustomThumbnailRenderer;
28
+ target.thumbnails = new Thumbnails().load((playlistVideoThumbnailRenderer || playlistCustomThumbnailRenderer).thumbnail.thumbnails);
28
29
  var stats = primaryRenderer.stats;
29
30
  if (primaryRenderer.stats.length === 3) {
30
31
  target.videoCount = PlaylistParser.parseSideBarInfo(stats[0], true);
@@ -41,7 +42,7 @@ var PlaylistParser = /** @class */ (function () {
41
42
  // Channel
42
43
  var videoOwner = (_c = sidebarRenderer[1]) === null || _c === void 0 ? void 0 : _c.playlistSidebarSecondaryInfoRenderer.videoOwner;
43
44
  if (videoOwner) {
44
- var _d = videoOwner.videoOwnerRenderer, title = _d.title, thumbnail = _d.thumbnail;
45
+ var _e = videoOwner.videoOwnerRenderer, title = _e.title, thumbnail = _e.thumbnail;
45
46
  target.channel = new BaseChannel({
46
47
  id: title.runs[0].navigationEndpoint.browseEndpoint.browseId,
47
48
  name: title.runs[0].text,
@@ -5,7 +5,7 @@ export interface MusicBaseArtistProperties extends MusicBaseProperties {
5
5
  name?: string;
6
6
  }
7
7
  export declare class MusicBaseArtist extends MusicBase implements MusicBaseArtistProperties {
8
- id: string;
8
+ id?: string;
9
9
  /** The name of this artist */
10
10
  name: string;
11
11
  /** @hidden */
@@ -4,6 +4,7 @@ import { MusicAlbumCompact } from "../MusicAlbumCompact";
4
4
  import { MusicArtistCompact } from "../MusicArtistCompact";
5
5
  import { MusicLyrics } from "../MusicLyrics";
6
6
  import { MusicPlaylistCompact } from "../MusicPlaylistCompact";
7
+ import { MusicSearchResult, MusicSearchType } from "../MusicSearchResult";
7
8
  import { MusicVideoCompact } from "../MusicVideoCompact";
8
9
  export declare type MusicClientOptions = {
9
10
  initialCookie: string;
@@ -25,6 +26,7 @@ export declare class MusicClient {
25
26
  *
26
27
  */
27
28
  search(query: string): Promise<Shelf<MusicVideoCompact[] | MusicAlbumCompact[] | MusicPlaylistCompact[] | MusicArtistCompact[]>[]>;
29
+ search<T extends MusicSearchType>(query: string, type: T): Promise<MusicSearchResult<T>>;
28
30
  /**
29
31
  * Get lyrics of a song
30
32
  *
@@ -1,2 +1 @@
1
1
  export * from "./MusicClient";
2
- export * from "./MusicSearchResultParser";
@@ -0,0 +1,24 @@
1
+ import { MusicBase } from "../MusicBase";
2
+ import { MusicClient } from "../MusicClient";
3
+ /** @hidden */
4
+ export declare type FetchResult<T> = {
5
+ items: T[];
6
+ continuation?: string;
7
+ };
8
+ /** @hidden */
9
+ export declare type MusicContinuableConstructorParams = {
10
+ client: MusicClient;
11
+ strictContinuationCheck?: boolean;
12
+ };
13
+ /** Represents a continuable list of items `T` (like pagination) */
14
+ export declare abstract class MusicContinuable<T> extends MusicBase {
15
+ items: T[];
16
+ continuation?: string | null;
17
+ private strictContinuationCheck;
18
+ /** @hidden */
19
+ constructor({ client, strictContinuationCheck }: MusicContinuableConstructorParams);
20
+ /** Fetch next items using continuation token */
21
+ next(count?: number): Promise<T[]>;
22
+ protected abstract fetch(): Promise<FetchResult<T>>;
23
+ private get hasContinuation();
24
+ }
@@ -0,0 +1 @@
1
+ export * from "./MusicContinuable";
@@ -1,12 +1,13 @@
1
1
  import { Shelf, YoutubeRawData } from "../../common";
2
2
  import { MusicAlbumCompact } from "../MusicAlbumCompact";
3
+ import { MusicClient } from "../MusicClient";
3
4
  import { MusicPlaylistCompact } from "../MusicPlaylistCompact";
5
+ import { MusicSongCompact } from "../MusicSongCompact";
4
6
  import { MusicVideoCompact } from "../MusicVideoCompact";
5
- import { MusicClient } from "./MusicClient";
6
- export declare class MusicSearchResultParser {
7
+ export declare class MusicAllSearchResultParser {
7
8
  static parseSearchResult(data: YoutubeRawData, client: MusicClient): Shelf<MusicVideoCompact[] | MusicAlbumCompact[] | MusicPlaylistCompact[]>[];
8
9
  private static parseSearchItem;
9
- private static parseVideoItem;
10
+ static parseVideoItem(item: YoutubeRawData, pageType: string, client: MusicClient): MusicSongCompact | MusicVideoCompact | undefined;
10
11
  private static parsePlaylistItem;
11
12
  private static parseAlbumItem;
12
13
  private static parseArtistItem;
@@ -0,0 +1,46 @@
1
+ import { FetchResult, MusicContinuable, MusicContinuableConstructorParams } from "../MusicContinuable";
2
+ import { MusicSongCompact } from "../MusicSongCompact";
3
+ import { MusicVideoCompact } from "../MusicVideoCompact";
4
+ export declare enum MusicSearchTypeEnum {
5
+ Song = "song",
6
+ Video = "video"
7
+ }
8
+ export declare type MusicSearchType = "song" | "video" | MusicSearchTypeEnum;
9
+ export declare type MusicSearchResultItem<T = "song"> = T extends "song" ? MusicSongCompact : MusicVideoCompact;
10
+ /**
11
+ * Represents search result, usually returned from `client.search();`.
12
+ *
13
+ * {@link MusicSearchResult} is a helper class to manage search result
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * const result = await music.search("Keyword", "song");
18
+ *
19
+ * console.log(result.items); // search result from first page
20
+ *
21
+ * let nextSearchResult = await result.next();
22
+ * console.log(nextSearchResult); // search result from second page
23
+ *
24
+ * nextSearchResult = await result.next();
25
+ * console.log(nextSearchResult); // search result from third page
26
+ *
27
+ * console.log(result.items); // search result from first, second, and third page.
28
+ * ```
29
+ *
30
+ * @noInheritDoc
31
+ */
32
+ export declare class MusicSearchResult<T extends MusicSearchType | undefined = "song"> extends MusicContinuable<MusicSearchResultItem<T>> {
33
+ private type;
34
+ /** @hidden */
35
+ constructor({ client }: MusicContinuableConstructorParams);
36
+ /**
37
+ * Initialize data from search
38
+ *
39
+ * @param query Search query
40
+ * @param options Search Options
41
+ *
42
+ * @hidden
43
+ */
44
+ search(query: string, type: MusicSearchType): Promise<MusicSearchResult<T>>;
45
+ protected fetch(): Promise<FetchResult<MusicSearchResultItem<T>>>;
46
+ }
@@ -0,0 +1,13 @@
1
+ import { YoutubeRawData } from "../../common";
2
+ import { MusicClient } from "../MusicClient";
3
+ import { MusicSearchResultItem, MusicSearchType } from "./MusicSearchResult";
4
+ declare type ParseReturnType = {
5
+ data: MusicSearchResultItem[];
6
+ continuation: string | undefined;
7
+ };
8
+ export declare class MusicSearchResultParser {
9
+ static parseInitialSearchResult(data: YoutubeRawData, type: MusicSearchType, client: MusicClient): ParseReturnType;
10
+ static parseContinuationSearchResult(data: YoutubeRawData, type: MusicSearchType, client: MusicClient): ParseReturnType;
11
+ private static parseSearchResult;
12
+ }
13
+ export {};
@@ -0,0 +1,3 @@
1
+ export * from "./MusicAllSearchResultParser";
2
+ export * from "./MusicSearchResult";
3
+ export * from "./MusicSearchResultParser";
@@ -0,0 +1,14 @@
1
+ declare type Options = {
2
+ song?: 1;
3
+ video?: 1;
4
+ };
5
+ declare type MusicSearchOptions = {
6
+ options?: Options;
7
+ };
8
+ declare type MusicSearchParams = {
9
+ params?: MusicSearchOptions;
10
+ };
11
+ export declare type MusicSearchProto = {
12
+ MusicSearchOptions: MusicSearchParams;
13
+ };
14
+ export {};
@@ -0,0 +1,5 @@
1
+ import protobuf from "protobufjs";
2
+ import { MusicSearchType } from "../MusicSearchResult";
3
+ import { MusicSearchProto as ProtoType } from "./MusicSearchProto";
4
+ export declare const MusicSearchProto: protobuf.Type;
5
+ export declare const optionsToProto: (type: MusicSearchType) => ProtoType["MusicSearchOptions"];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "youtubei",
3
- "version": "1.2.1",
3
+ "version": "1.3.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",