From 0398d92b4a8c63dfe620f42d13d9ce20f94040a0 Mon Sep 17 00:00:00 2001 From: Cody Duong Date: Thu, 17 Mar 2022 07:14:44 -0500 Subject: [PATCH] Update docs, fix issues with new private members --- .gitignore | 3 +- docs/YTMusic.html | 9 +- docs/global.html | 331 +--------------------------------- docs/index.html | 6 +- docs/index.js.html | 35 ++-- docs/mixins_browsing.js.html | 177 ++++++++++-------- docs/mixins_explore.js.html | 72 ++++---- docs/mixins_library.js.html | 78 ++++---- docs/mixins_playlists.js.html | 72 ++++---- docs/mixins_uploads.js.html | 107 ++++++----- docs/mixins_watch.js.html | 34 ++-- docs/module-Browsing.html | 26 +-- docs/module-Explore.html | 10 +- docs/module-Library.html | 30 +-- docs/module-Playlists.html | 18 +- docs/module-Uploads.html | 12 +- docs/module-Watch.html | 8 +- package.json | 2 +- src/index.ts | 9 +- src/ytmusic.ts | 30 +-- 20 files changed, 406 insertions(+), 663 deletions(-) diff --git a/.gitignore b/.gitignore index eedb115..4d4742e 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ dist yarn-error.log coverage TEST_setup.json -codyduong-ytmusicapi-*.tgz \ No newline at end of file +codyduong-ytmusicapi-*.tgz +tscdocs \ No newline at end of file diff --git a/docs/YTMusic.html b/docs/YTMusic.html index a48e524..387bcf6 100644 --- a/docs/YTMusic.html +++ b/docs/YTMusic.html @@ -83,7 +83,7 @@ @@ -220,6 +220,9 @@

string +| + +object @@ -239,7 +242,7 @@

- Provide a string or path to file. Authentication credentials are needed to manage your library. Should be an adjusted version of `headers_auth.json.example` in the project root. See `setup` for how to fill in the correct credentials. If not provided, a default header is used without authentication. + Provide a string (raw headers), object, or path (Node only!), Authentication credentials are needed to manage your library. Should be an adjusted version of `headers_auth.json.example` in the project root. See `setup` for how to fill in the correct credentials. If not provided, a default header is used without authentication. @@ -383,7 +386,7 @@

diff --git a/docs/global.html b/docs/global.html index f8c2c0f..08c988f 100644 --- a/docs/global.html +++ b/docs/global.html @@ -83,7 +83,7 @@ @@ -138,314 +138,24 @@

- - -

Methods

- - - - - - -

- # - (async) getArtistAlbums Get the full list of an artist's albums or singles(channelId, params) -

- - - - - - - - - - - - - -
- Parameters: - - - - - - - - - - - - +

Members

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
channelId - - -string - - - - channel Id of the artist
params - - -string - - - - params obtained by `getArtist`
- -
- - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - -
-
Source:
-
-
- - - - - - - -
+

+ # + (constant) YTMusic +

- - - - - - - - - - - -
- Returns: - - -
- List of albums in the format of `getLibraryAlbums`, except artists key is missing. +
+ Allows automated interactions with YouTube Music by emulating the YouTube web client's requests. Permits both authenticated and non-authenticated requests. Authentication header data must be provided on initialization.
- -
- - - - - - - - - -

- # - (async) getPlaylist Return a list of playlist items.(playlistIdopt, limitopt) -

- - - - - - - - - - - - - -
- Parameters: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeAttributesDefaultDescription
playlistId - - -string - - - - - - <optional>
- - - - - -
- - Playlist id.
limit - - -number - - - - - - <optional>
- - - - - -
- - 100 - - How many songs to return.
- -
- @@ -482,7 +192,7 @@

@@ -501,30 +211,9 @@

- - - - - - - - - -
- Example -

Each item is in the following format

- -
-
{
  "id": "PLQwVIlKxHM6qv-o99iX9R85og7IzF9YS_",
  "privacy": "PUBLIC",
  "title": "New EDM This Week 03/13/2020",
  "thumbnails": [...]
  "description": "Weekly r/EDM new release roundup. Created with github.com/sigma67/spotifyplaylist_to_gmusic",
  "author": "sigmatics",
  "year": "2020",
  "duration": "6+ hours",
  "duration_seconds": 52651,
  "trackCount": 237,
  "tracks": [
    {
      "videoId": "bjGppZKiuFE",
      "title": "Lost",
      "artists": [
        {
          "name": "Guest Who",
          "id": "UCkgCRdnnqWnUeIH7EIc3dBg"
        },
        {
          "name": "Kate Wild",
          "id": "UCwR2l3JfJbvB6aq0RnnJfWg"
        }
      ],
      "album": {
      "name": "Lost",
      "id": "MPREb_PxmzvDuqOnC"
    },
    "duration": "2:58",
    "likeStatus": "INDIFFERENT",
    "thumbnails": [...],
    "isAvailable": True,
    "isExplicit": False,
    "feedbackTokens": {
    "add": "AB9zfpJxtvrU...",
    "remove": "AB9zfpKTyZ..."
    }
  ]
}
- -
- -
- - @@ -555,7 +244,7 @@

diff --git a/docs/index.html b/docs/index.html index 8db053b..c3702df 100644 --- a/docs/index.html +++ b/docs/index.html @@ -83,7 +83,7 @@ @@ -243,7 +243,7 @@

Setup and Usage

]

Documentation

-

See the Documentation for the Python 3 API for reference.

+

The API does support usage in browsers, however you will have to use a proxy since YTmusic does not have CORS support.

Contributing

The library is intended to keep features within the same scope of the original Python 3 library. This may/may not change at my discretion.

Pull requests are welcome, esp. with regards to resolving any API differences that occured through mistakes or otherwise. However, note that I would @@ -276,7 +276,7 @@

Acknowledgements

diff --git a/docs/index.js.html b/docs/index.js.html index 91e2176..b25fb66 100644 --- a/docs/index.js.html +++ b/docs/index.js.html @@ -83,7 +83,7 @@ @@ -109,20 +109,21 @@

-
import { _YTMusic } from './ytmusic';
-import { BrowsingMixin } from './mixins/browsing';
-import { WatchMixin } from './mixins/watch';
-import { ExploreMixin } from './mixins/explore';
-import { LibraryMixin } from './mixins/library';
-import { PlaylistsMixin } from './mixins/playlists';
-import { UploadsMixin } from './mixins/uploads';
+            
"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const ytmusic_1 = require("./ytmusic");
+const browsing_1 = require("./mixins/browsing");
+const watch_1 = require("./mixins/watch");
+const explore_1 = require("./mixins/explore");
+const library_1 = require("./mixins/library");
+const playlists_1 = require("./mixins/playlists");
+const uploads_1 = require("./mixins/uploads");
 /**
  * Allows automated interactions with YouTube Music by emulating the YouTube web client's requests.
  * Permits both authenticated and non-authenticated requests.
  * Authentication header data must be provided on initialization.
- * @class
- * @param {Object} [options=] Options object.
- * @param {string} [options.auth=]  Provide a string or path to file.
+ * @param {_YTMusicConstructorOptions} [options=] Options object.
+ * @param {string | object} [options.auth=]  Provide a string (raw headers), object, or path (Node only!),
  * Authentication credentials are needed to manage your library.
  * Should be an adjusted version of `headers_auth.json.example` in the project root.
  * See `setup` for how to fill in the correct credentials.
@@ -132,14 +133,14 @@ 

* Otherwise the default account is used. You can retrieve the user ID * by going to https://myaccount.google.com/brandaccounts and selecting your brand account. * The user ID will be in the URL: https://myaccount.google.com/b/user_id/ - * @param {any} [options.proxies] Optional. No usage in current API + * @param {} [options.httpsAgent] Optional. Define an HTTP proxy for your request. + * @param {AxiosProxyConfig} [options.proxies] Optional. Define an HTTP proxy for your request. * @param {string} [options.language] Optional. Can be used to change the language of returned data. * English will be used by default. Available languages can be checked in - * the ytmusicapi/locales directory. A language that is not in the directory will still be - * attempted to be translated, but results may not be the best. + * the ytmusicapi/locales directory. */ -const YTMusic = UploadsMixin(LibraryMixin(PlaylistsMixin(ExploreMixin(WatchMixin(BrowsingMixin(_YTMusic)))))); -export default YTMusic; +const YTMusic = (0, uploads_1.UploadsMixin)((0, library_1.LibraryMixin)((0, playlists_1.PlaylistsMixin)((0, explore_1.ExploreMixin)((0, watch_1.WatchMixin)((0, browsing_1.BrowsingMixin)(ytmusic_1._YTMusic)))))); +exports.default = YTMusic;

@@ -165,7 +166,7 @@

diff --git a/docs/mixins_browsing.js.html b/docs/mixins_browsing.js.html index e237f2a..b72d443 100644 --- a/docs/mixins_browsing.js.html +++ b/docs/mixins_browsing.js.html @@ -83,7 +83,7 @@ @@ -109,22 +109,48 @@

-
import * as utf8 from 'utf8';
-import { YTM_DOMAIN } from '../constants';
-import { DESCRIPTION, GRID_ITEMS, MUSIC_SHELF, NAVIGATION_BROWSE_ID, NAVIGATION_WATCH_PLAYLIST_ID, RUN_TEXT, SECTION_LIST, SECTION_LIST_ITEM, SINGLE_COLUMN_TAB, THUMBNAILS, TITLE, TITLE_TEXT, } from '../parsers/index';
-import * as helpers from '../helpers';
-import { re } from '../pyLibraryMock';
-import { parseAlbumHeader } from '../parsers/albums';
-import { parseContentList, parsePlaylist } from '../parsers/browsing';
-import { parseAlbums } from '../parsers/library';
-import { parsePlaylistItems } from '../parsers/playlists';
-import { getSearchParams } from '../parsers/searchParams';
-import { findObjectByKey, getContinuations, nav } from '../parsers/utils';
+            
"use strict";
+var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
+    if (k2 === undefined) k2 = k;
+    var desc = Object.getOwnPropertyDescriptor(m, k);
+    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
+      desc = { enumerable: true, get: function() { return m[k]; } };
+    }
+    Object.defineProperty(o, k2, desc);
+}) : (function(o, m, k, k2) {
+    if (k2 === undefined) k2 = k;
+    o[k2] = m[k];
+}));
+var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
+    Object.defineProperty(o, "default", { enumerable: true, value: v });
+}) : function(o, v) {
+    o["default"] = v;
+});
+var __importStar = (this && this.__importStar) || function (mod) {
+    if (mod && mod.__esModule) return mod;
+    var result = {};
+    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
+    __setModuleDefault(result, mod);
+    return result;
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.BrowsingMixin = void 0;
+const utf8 = __importStar(require("utf8"));
+const constants_1 = require("../constants");
+const index_1 = require("../parsers/index");
+const helpers = __importStar(require("../helpers"));
+const pyLibraryMock_1 = require("../pyLibraryMock");
+const albums_1 = require("../parsers/albums");
+const browsing_1 = require("../parsers/browsing");
+const library_1 = require("../parsers/library");
+const playlists_1 = require("../parsers/playlists");
+const searchParams_1 = require("../parsers/searchParams");
+const utils_1 = require("../parsers/utils");
 /**
  * @module Browsing
  */
 // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
-export const BrowsingMixin = (Base) => {
+const BrowsingMixin = (Base) => {
     return class Browsing extends Base {
         /**
          * Get the home page.
@@ -212,18 +238,18 @@ 

const endpoint = 'browse'; const body = { browseId: 'FEmusic_home' }; const response = await this._sendRequest(endpoint, body); - const results = nav(response, [...SINGLE_COLUMN_TAB, ...SECTION_LIST]); - let home = [...this.parser.parseHome(results)]; - const sectionList = nav(response, [ - ...SINGLE_COLUMN_TAB, + const results = (0, utils_1.nav)(response, [...index_1.SINGLE_COLUMN_TAB, ...index_1.SECTION_LIST]); + let home = [...this._parser.parseHome(results)]; + const sectionList = (0, utils_1.nav)(response, [ + ...index_1.SINGLE_COLUMN_TAB, 'sectionListRenderer', ]); if ('continuations' in sectionList) { const requestFunc = async (additionalParams) => await this._sendRequest(endpoint, body, additionalParams); - const parseFunc = (contents) => this.parser.parseHome(contents); + const parseFunc = (contents) => this._parser.parseHome(contents); home = [ ...home, - ...(await getContinuations(sectionList, 'sectionListContinuation', limit - home.length, requestFunc, parseFunc)), + ...(await (0, utils_1.getContinuations)(sectionList, 'sectionListContinuation', limit - home.length, requestFunc, parseFunc)), ]; } return home; @@ -251,7 +277,7 @@

if (scope && !scopes.includes(scope)) { throw new Error(`Invalid scope provided. Please use one of the following scopes or leave out the parameter: ${scopes.join(', ')}`); } - const params = getSearchParams(filter, scope, ignoreSpelling); + const params = (0, searchParams_1.getSearchParams)(filter, scope, ignoreSpelling); if (params) { body['params'] = params; } @@ -270,7 +296,7 @@

else { results = response['contents']; } - const resultsNav = nav(results, SECTION_LIST); + const resultsNav = (0, utils_1.nav)(results, index_1.SECTION_LIST); // no results if (!resultsNav || (resultsNav.length == 1 && 'itemSectionRenderer' in resultsNav)) { @@ -287,7 +313,7 @@

if ('musicShelfRenderer' in res) { const resultsMusicShelfContents = res['musicShelfRenderer']['contents']; const original_filter = filter; - const category = nav(res, [...MUSIC_SHELF, ...TITLE_TEXT], true); + const category = (0, utils_1.nav)(res, [...index_1.MUSIC_SHELF, ...index_1.TITLE_TEXT], true); if (!filter && scope == scopes[0]) { filter = category; } @@ -296,15 +322,15 @@

: null; searchResults = [ ...searchResults, - ...this.parser.parseSearchResults(resultsMusicShelfContents, type, category), + ...this._parser.parseSearchResults(resultsMusicShelfContents, type, category), ]; filter = original_filter; if ('continuations' in res['musicShelfRenderer']) { const requestFunc = async (additionalParams) => await this._sendRequest(endpoint, body, additionalParams); - const parseFunc = (contents) => this.parser.parseSearchResults(contents, type, category); + const parseFunc = (contents) => this._parser.parseSearchResults(contents, type, category); searchResults = [ ...searchResults, - ...(await getContinuations(res['musicShelfRenderer'], 'musicShelfContinuation', limit - searchResults.length, requestFunc, parseFunc)), + ...(await (0, utils_1.getContinuations)(res['musicShelfRenderer'], 'musicShelfContinuation', limit - searchResults.length, requestFunc, parseFunc)), ]; } } @@ -401,9 +427,9 @@

const body = { browseId: channelId }; const endpoint = 'browse'; const response = await this._sendRequest(endpoint, body); - const results = nav(response, [ - ...SINGLE_COLUMN_TAB, - ...SECTION_LIST, + const results = (0, utils_1.nav)(response, [ + ...index_1.SINGLE_COLUMN_TAB, + ...index_1.SECTION_LIST, ]); if (results.length == 1) { // not a YouTube Music Channel, a standard YouTube Channel ID with no music content was given @@ -414,36 +440,36 @@

views: null, }; const header = response['header']['musicImmersiveHeaderRenderer']; - artist['name'] = nav(header, TITLE_TEXT); - const descriptionShelf = findObjectByKey(results, 'musicDescriptionShelfRenderer', undefined, true); + artist['name'] = (0, utils_1.nav)(header, index_1.TITLE_TEXT); + const descriptionShelf = (0, utils_1.findObjectByKey)(results, 'musicDescriptionShelfRenderer', undefined, true); if (descriptionShelf) { - artist['description'] = nav(descriptionShelf, DESCRIPTION); + artist['description'] = (0, utils_1.nav)(descriptionShelf, index_1.DESCRIPTION); artist['views'] = !('subheader' in descriptionShelf) ? null : descriptionShelf['subheader']['runs'][0]['text']; } const subscriptionButton = header['subscriptionButton']['subscribeButtonRenderer']; artist['channelId'] = subscriptionButton['channelId']; - artist['shuffleId'] = nav(header, ['playButton', 'buttonRenderer', ...NAVIGATION_WATCH_PLAYLIST_ID], true); - artist['radioId'] = nav(header, ['startRadioButton', 'buttonRenderer', ...NAVIGATION_WATCH_PLAYLIST_ID], true); - artist['subscribers'] = nav(subscriptionButton, ['subscriberCountText', 'runs', 0, 'text'], true); + artist['shuffleId'] = (0, utils_1.nav)(header, ['playButton', 'buttonRenderer', ...index_1.NAVIGATION_WATCH_PLAYLIST_ID], true); + artist['radioId'] = (0, utils_1.nav)(header, ['startRadioButton', 'buttonRenderer', ...index_1.NAVIGATION_WATCH_PLAYLIST_ID], true); + artist['subscribers'] = (0, utils_1.nav)(subscriptionButton, ['subscriberCountText', 'runs', 0, 'text'], true); artist['subscribed'] = subscriptionButton['subscribed']; - artist['thumbnails'] = nav(header, THUMBNAILS, true); + artist['thumbnails'] = (0, utils_1.nav)(header, index_1.THUMBNAILS, true); artist['songs'] = { browseId: null }; if ('musicShelfRenderer' in results[0]) { // API sometimes does not return songs - const musicShelf = nav(results[0], MUSIC_SHELF); + const musicShelf = (0, utils_1.nav)(results[0], index_1.MUSIC_SHELF); if ('navigationEndpoint' in - nav(musicShelf, TITLE)) { - artist['songs']['browseId'] = nav(musicShelf, [ - ...TITLE, - ...NAVIGATION_BROWSE_ID, + (0, utils_1.nav)(musicShelf, index_1.TITLE)) { + artist['songs']['browseId'] = (0, utils_1.nav)(musicShelf, [ + ...index_1.TITLE, + ...index_1.NAVIGATION_BROWSE_ID, ]); } //@ts-expect-error: We're overriding the shape here - artist['songs']['results'] = parsePlaylistItems(musicShelf['contents']); + artist['songs']['results'] = (0, playlists_1.parsePlaylistItems)(musicShelf['contents']); } - artist = { ...artist, ...this.parser.parseArtistContents(results) }; + artist = { ...artist, ...this._parser.parseArtistContents(results) }; return artist; } /** @@ -456,12 +482,12 @@

const body = { browseId: channelId, params: params }; const endpoint = 'browse'; const response = await this._sendRequest(endpoint, body); - const results = nav(response, [ - ...SINGLE_COLUMN_TAB, - ...SECTION_LIST_ITEM, - ...GRID_ITEMS, + const results = (0, utils_1.nav)(response, [ + ...index_1.SINGLE_COLUMN_TAB, + ...index_1.SECTION_LIST_ITEM, + ...index_1.GRID_ITEMS, ]); - const albums = parseAlbums(results); + const albums = (0, library_1.parseAlbums)(results); return albums; } /** @@ -512,14 +538,14 @@

const endpoint = 'browse'; const body = { browseId: channelId }; const response = await this._sendRequest(endpoint, body); - const results = nav(response, [...SINGLE_COLUMN_TAB, ...SECTION_LIST]); + const results = (0, utils_1.nav)(response, [...index_1.SINGLE_COLUMN_TAB, ...index_1.SECTION_LIST]); const user = { - name: nav(response, [ + name: (0, utils_1.nav)(response, [ 'header', 'musicVisualHeaderRenderer', - ...TITLE_TEXT, + ...index_1.TITLE_TEXT, ]), - ...this.parser.parseArtistContents(results), + ...this._parser.parseArtistContents(results), }; return user; } @@ -534,12 +560,12 @@

const endpoint = 'browse'; const body = { browseId: channelId, params: params }; const response = await this._sendRequest(endpoint, body); - const results = nav(response, [ - ...SINGLE_COLUMN_TAB, - ...SECTION_LIST_ITEM, - ...GRID_ITEMS, + const results = (0, utils_1.nav)(response, [ + ...index_1.SINGLE_COLUMN_TAB, + ...index_1.SECTION_LIST_ITEM, + ...index_1.GRID_ITEMS, ]); - const userPlaylists = parseContentList(results, parsePlaylist); + const userPlaylists = (0, browsing_1.parseContentList)(results, browsing_1.parsePlaylist); return userPlaylists; } /** @@ -549,8 +575,8 @@

*/ async getAlbumBrowseId(audioPlaylistId) { const params = { list: audioPlaylistId }; - const response = await this._sendGetRequest(YTM_DOMAIN + '/playlist', params); - const matches = re.findall(/"MPRE.+?"/, response); + const response = await this._sendGetRequest(constants_1.YTM_DOMAIN + '/playlist', params); + const matches = pyLibraryMock_1.re.findall(/"MPRE.+?"/, response); let browse_id = null; if (matches.length > 0) { browse_id = utf8 @@ -610,15 +636,15 @@

const body = { browseId: browseId }; const endpoint = 'browse'; const response = await this._sendRequest(endpoint, body); - const results = nav(response, [ - ...SINGLE_COLUMN_TAB, - ...SECTION_LIST_ITEM, - ...MUSIC_SHELF, + const results = (0, utils_1.nav)(response, [ + ...index_1.SINGLE_COLUMN_TAB, + ...index_1.SECTION_LIST_ITEM, + ...index_1.MUSIC_SHELF, ]); const album = { - ...parseAlbumHeader(response), + ...(0, albums_1.parseAlbumHeader)(response), //@ts-expect-error: We'll swap this out proper later. - tracks: parsePlaylistItems(results['contents']), + tracks: (0, playlists_1.parsePlaylistItems)(results['contents']), duration_seconds: undefined, }; album['duration_seconds'] = helpers.sumTotalDuration(album); @@ -813,18 +839,18 @@

const response = await this._sendRequest('browse', { browseId: browseId, }); - lyrics['lyrics'] = nav(response, [ + lyrics['lyrics'] = (0, utils_1.nav)(response, [ 'contents', - ...SECTION_LIST_ITEM, + ...index_1.SECTION_LIST_ITEM, 'musicDescriptionShelfRenderer', - ...DESCRIPTION, + ...index_1.DESCRIPTION, ], true); - lyrics['source'] = nav(response, [ + lyrics['source'] = (0, utils_1.nav)(response, [ 'contents', - ...SECTION_LIST_ITEM, + ...index_1.SECTION_LIST_ITEM, 'musicDescriptionShelfRenderer', 'footer', - ...RUN_TEXT, + ...index_1.RUN_TEXT, ], true); return lyrics; } @@ -833,12 +859,12 @@

* @return {string} URL to `base.js` */ async getBaseJSUrl() { - const response = await this._sendGetRequest(YTM_DOMAIN); - const match = re.search(/jsUrl"\s*:\s*"([^"]+)"/, response); + const response = await this._sendGetRequest(constants_1.YTM_DOMAIN); + const match = pyLibraryMock_1.re.search(/jsUrl"\s*:\s*"([^"]+)"/, response); if (!match) { throw new Error('Could not identify the URL for base.js player.'); } - return YTM_DOMAIN + match[0].slice(8, -1); + return constants_1.YTM_DOMAIN + match[0].slice(8, -1); } /** * Fetch the `base.js` script from YouTube Music and parse out the `signatureTimestamp` for use with {@link https://codyduong.github.io/ytmusicapiJS/module-Browsing.html#getSong | getSong}. @@ -850,7 +876,7 @@

url = await this.getBaseJSUrl(); } const response = await this._sendGetRequest(url); - const match = re.search(/signatureTimestamp[:=](\d+)/, response); + const match = pyLibraryMock_1.re.search(/signatureTimestamp[:=](\d+)/, response); if (!match) { throw new Error('Unable to identify the signatureTimestamp.'); } @@ -861,6 +887,7 @@

} }; }; +exports.BrowsingMixin = BrowsingMixin;

@@ -886,7 +913,7 @@

diff --git a/docs/mixins_explore.js.html b/docs/mixins_explore.js.html index 76ab9a9..d5d6a51 100644 --- a/docs/mixins_explore.js.html +++ b/docs/mixins_explore.js.html @@ -83,7 +83,7 @@ @@ -109,15 +109,18 @@

-
import { CAROUSEL, CAROUSEL_CONTENTS, CAROUSEL_TITLE, CATEGORY_PARAMS, CATEGORY_TITLE, FRAMEWORK_MUTATIONS, GRID, GRID_ITEMS, MRLIR, MTRIR, MUSIC_SHELF, NAVIGATION_BROWSE_ID, SECTION_LIST, SINGLE_COLUMN_TAB, TITLE, TITLE_TEXT, } from '../parsers';
-import { parseContentList, parsePlaylist, parseVideo, } from '../parsers/browsing';
-import { parseChartArtist, parseChartSong, parseChartTrending, } from '../parsers/explore';
-import { nav } from '../parsers/utils';
+            
"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.ExploreMixin = void 0;
+const parsers_1 = require("../parsers");
+const browsing_1 = require("../parsers/browsing");
+const explore_1 = require("../parsers/explore");
+const utils_1 = require("../parsers/utils");
 /**
  * @module Explore
  */
 // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
-export const ExploreMixin = (Base) => {
+const ExploreMixin = (Base) => {
     return class ExploreMixin extends Base {
         /**
          * Fetch "Moods & Genres" categories from YouTube Music.
@@ -163,22 +166,22 @@ 

async getMoodCategories() { const sections = {}; const response = await this._sendRequest('browse', { browseId: 'FEmusic_moods_and_genres' }); - const naved = nav(response, [ - ...SINGLE_COLUMN_TAB, - ...SECTION_LIST, + const naved = (0, utils_1.nav)(response, [ + ...parsers_1.SINGLE_COLUMN_TAB, + ...parsers_1.SECTION_LIST, ]); for (const section of naved) { - const title = nav(section, [ - ...GRID, + const title = (0, utils_1.nav)(section, [ + ...parsers_1.GRID, 'header', 'gridHeaderRenderer', - ...TITLE_TEXT, + ...parsers_1.TITLE_TEXT, ]); sections[title] = []; - for (const category of nav(section, GRID_ITEMS)) { + for (const category of (0, utils_1.nav)(section, parsers_1.GRID_ITEMS)) { sections[title].push({ - title: nav(category, CATEGORY_TITLE), - params: nav(category, CATEGORY_PARAMS), + title: (0, utils_1.nav)(category, parsers_1.CATEGORY_TITLE), + params: (0, utils_1.nav)(category, parsers_1.CATEGORY_PARAMS), }); } } @@ -195,22 +198,22 @@

browseId: 'FEmusic_moods_and_genres_category', params: params, }); - for (const section of nav(response, [...SINGLE_COLUMN_TAB, ...SECTION_LIST])) { + for (const section of (0, utils_1.nav)(response, [...parsers_1.SINGLE_COLUMN_TAB, ...parsers_1.SECTION_LIST])) { let path = []; if ('gridRenderer' in section) { - path = GRID_ITEMS; + path = parsers_1.GRID_ITEMS; } else if ('musicCarouselShelfRenderer' in section) { - path = CAROUSEL_CONTENTS; + path = parsers_1.CAROUSEL_CONTENTS; } else if ('musicImmersiveCarouselShelfRenderer' in section) { path = ['musicImmersiveCarouselShelfRenderer', 'contents']; } if (path.length) { - const results = nav(section, path); + const results = (0, utils_1.nav)(section, path); playlists = [ ...playlists, - ...parseContentList(results, parsePlaylist), + ...(0, browsing_1.parseContentList)(results, browsing_1.parsePlaylist), ]; } } @@ -317,12 +320,12 @@

} const endpoint = 'browse'; const response = await this._sendRequest(endpoint, body); - const results = nav(response, [...SINGLE_COLUMN_TAB, ...SECTION_LIST]); + const results = (0, utils_1.nav)(response, [...parsers_1.SINGLE_COLUMN_TAB, ...parsers_1.SECTION_LIST]); const charts = { countries: {}, }; - const menu = nav(results[0], [ - ...MUSIC_SHELF, + const menu = (0, utils_1.nav)(results[0], [ + ...parsers_1.MUSIC_SHELF, 'subheaders', 0, 'musicSideAlignedItemRenderer', @@ -330,9 +333,9 @@

0, 'musicSortFilterButtonRenderer', ]); - charts['countries']['selected'] = nav(menu, TITLE); - charts['countries']['options'] = nav(response, FRAMEWORK_MUTATIONS) - .map((m) => nav(m, ['payload', 'musicFormBooleanChoice', 'opaqueToken'], true)) + charts['countries']['selected'] = (0, utils_1.nav)(menu, parsers_1.TITLE); + charts['countries']['options'] = (0, utils_1.nav)(response, parsers_1.FRAMEWORK_MUTATIONS) + .map((m) => (0, utils_1.nav)(m, ['payload', 'musicFormBooleanChoice', 'opaqueToken'], true)) .filter((x) => x); const chartsCategories = ['videos', 'artists']; const hasSongs = !!this.getAuth(); @@ -349,35 +352,36 @@

} // eslint-disable-next-line @typescript-eslint/explicit-function-return-type const parseChart = (i, parseFunc, key) => { - return parseContentList(nav(results[i + (hasSongs ? 1 : 0)], CAROUSEL_CONTENTS, true), parseFunc, key).filter((x) => x); + return (0, browsing_1.parseContentList)((0, utils_1.nav)(results[i + (hasSongs ? 1 : 0)], parsers_1.CAROUSEL_CONTENTS, true), parseFunc, key).filter((x) => x); }; for (const [i, c] of chartsCategories.entries()) { //@ts-expect-error, we'll set the items later... charts[c] = { - playlist: nav(results[1 + i], [...CAROUSEL, ...CAROUSEL_TITLE, ...NAVIGATION_BROWSE_ID], true), + playlist: (0, utils_1.nav)(results[1 + i], [...parsers_1.CAROUSEL, ...parsers_1.CAROUSEL_TITLE, ...parsers_1.NAVIGATION_BROWSE_ID], true), }; } if (hasSongs) { charts['songs'] = { ...charts['songs'], ...{ - items: parseChart(0, parseChartSong, MRLIR), + items: parseChart(0, explore_1.parseChartSong, parsers_1.MRLIR), }, }; } - charts['videos']['items'] = parseChart(1, parseVideo, MTRIR); - charts['artists']['items'] = parseChart(2, parseChartArtist, MRLIR); + charts['videos']['items'] = parseChart(1, browsing_1.parseVideo, parsers_1.MTRIR); + charts['artists']['items'] = parseChart(2, explore_1.parseChartArtist, parsers_1.MRLIR); if (hasGenres) { //@ts-expect-error: TS didn't detect this control flow discrimination... - charts['genres'] = parseChart(3, parsePlaylist, MTRIR); + charts['genres'] = parseChart(3, browsing_1.parsePlaylist, parsers_1.MTRIR); } if (hasTrending) { - charts['trending']['items'] = parseChart(3 + (hasGenres ? 1 : 0), parseChartTrending, MRLIR); + charts['trending']['items'] = parseChart(3 + (hasGenres ? 1 : 0), explore_1.parseChartTrending, parsers_1.MRLIR); } return charts; } }; }; +exports.ExploreMixin = ExploreMixin;

@@ -403,7 +407,7 @@

diff --git a/docs/mixins_library.js.html b/docs/mixins_library.js.html index c431347..54b30a3 100644 --- a/docs/mixins_library.js.html +++ b/docs/mixins_library.js.html @@ -83,7 +83,7 @@ @@ -109,17 +109,20 @@

-
import { prepareLikeEndpoint, prepareOrderParams, validateOrderParameters, } from '../helpers';
-import { SINGLE_COLUMN_TAB, SECTION_LIST, ITEM_SECTION, GRID, MUSIC_SHELF, TITLE, MENU_SERVICE, FEEDBACK_TOKEN, TITLE_TEXT, } from '../parsers';
-import { parseContentList, parsePlaylist } from '../parsers/browsing';
-import { parseLibraryAlbums, parseLibraryArtists, parseLibrarySongs, } from '../parsers/library';
-import { parsePlaylistItems } from '../parsers/playlists';
-import { validateResponse as validateResponseFunc, findObjectByKey, getContinuations, nav, resendRequestUntilParsedResponseIsValid, getValidatedContinuations, } from '../parsers/utils';
+            
"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.LibraryMixin = void 0;
+const helpers_1 = require("../helpers");
+const parsers_1 = require("../parsers");
+const browsing_1 = require("../parsers/browsing");
+const library_1 = require("../parsers/library");
+const playlists_1 = require("../parsers/playlists");
+const utils_1 = require("../parsers/utils");
 /**
  * @module Library
  */
 // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
-export const LibraryMixin = (Base) => {
+const LibraryMixin = (Base) => {
     return class LibraryMixin extends Base {
         /**
          * Retrieves the playlists in the user's library.
@@ -138,16 +141,16 @@ 

const body = { browseId: 'FEmusic_liked_playlists' }; const endpoint = 'browse'; const response = await this._sendRequest(endpoint, body); - let results = findObjectByKey(nav(response, [...SINGLE_COLUMN_TAB, ...SECTION_LIST]), 'itemSectionRenderer'); - results = nav(results, [...ITEM_SECTION, ...GRID]); - let playlists = parseContentList(results['items'].slice(1), parsePlaylist); + let results = (0, utils_1.findObjectByKey)((0, utils_1.nav)(response, [...parsers_1.SINGLE_COLUMN_TAB, ...parsers_1.SECTION_LIST]), 'itemSectionRenderer'); + results = (0, utils_1.nav)(results, [...parsers_1.ITEM_SECTION, ...parsers_1.GRID]); + let playlists = (0, browsing_1.parseContentList)(results['items'].slice(1), browsing_1.parsePlaylist); if ('continuations' in results) { const requestFunc = async (additionalParams) => await this._sendRequest(endpoint, body, additionalParams); // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - const parseFunc = (contents) => parseContentList(contents, parsePlaylist); + const parseFunc = (contents) => (0, browsing_1.parseContentList)(contents, browsing_1.parsePlaylist); playlists = [ ...playlists, - ...(await getContinuations(results, 'gridContinuation', limit - playlists.length, requestFunc, parseFunc)), + ...(await (0, utils_1.getContinuations)(results, 'gridContinuation', limit - playlists.length, requestFunc, parseFunc)), ]; } return playlists; @@ -165,18 +168,18 @@

this._checkAuth(); const { limit = 25, validateResponse = false, order } = options ?? {}; const body = { browseId: 'FEmusic_liked_videos' }; - validateOrderParameters(order); + (0, helpers_1.validateOrderParameters)(order); if (order) { - body['params'] = prepareOrderParams(order); + body['params'] = (0, helpers_1.prepareOrderParams)(order); } const endpoint = 'browse'; const perPage = 25; const requestFunc = async (_additionalParams) => await this._sendRequest(endpoint, body); //additionalParams doesnt do anything? @codyduong PR this. - const parseFunc = (rawResponse) => parseLibrarySongs(rawResponse); + const parseFunc = (rawResponse) => (0, library_1.parseLibrarySongs)(rawResponse); let response; if (validateResponse) { - const validateFunc = (parsed) => validateResponseFunc(parsed, perPage, limit, 0); - response = await resendRequestUntilParsedResponseIsValid(requestFunc, null, parseFunc, validateFunc, 3); + const validateFunc = (parsed) => (0, utils_1.validateResponse)(parsed, perPage, limit, 0); + response = await (0, utils_1.resendRequestUntilParsedResponseIsValid)(requestFunc, null, parseFunc, validateFunc, 3); } else { response = parseFunc(await requestFunc(null)); @@ -185,17 +188,17 @@

let songs = response['parsed']; if ('continuations' in results) { const requestContinuationsFunc = async (additionalParams) => await this._sendRequest(endpoint, body, additionalParams); - const parseContinuationsFunc = (contents) => parsePlaylistItems(contents); + const parseContinuationsFunc = (contents) => (0, playlists_1.parsePlaylistItems)(contents); if (validateResponse) { songs = [ ...songs, - ...(await getValidatedContinuations(results, 'musicShelfContinuation', limit - songs.length, perPage, requestContinuationsFunc, parseContinuationsFunc)), + ...(await (0, utils_1.getValidatedContinuations)(results, 'musicShelfContinuation', limit - songs.length, perPage, requestContinuationsFunc, parseContinuationsFunc)), ]; } else { songs = [ ...songs, - ...(await getContinuations(results, 'musicShelfContinuation', limit - songs.length, requestContinuationsFunc, parseContinuationsFunc)), + ...(await (0, utils_1.getContinuations)(results, 'musicShelfContinuation', limit - songs.length, requestContinuationsFunc, parseContinuationsFunc)), ]; } } @@ -225,11 +228,11 @@

const { limit = 25, order } = options ?? {}; const body = { browseId: 'FEmusic_liked_albums' }; if (order) { - body['params'] = prepareOrderParams(order); + body['params'] = (0, helpers_1.prepareOrderParams)(order); } const endpoint = 'browse'; const response = await this._sendRequest(endpoint, body); - return await parseLibraryAlbums(response, async (additionalParams) => await this._sendRequest(endpoint, body, additionalParams), limit); + return await (0, library_1.parseLibraryAlbums)(response, async (additionalParams) => await this._sendRequest(endpoint, body, additionalParams), limit); } /** * Gets the artists of the songs in the user's library. @@ -249,10 +252,10 @@

this._checkAuth(); const body = { browseId: 'FEmusic_library_corpus_track_artists' }; const { limit = 25, order } = options ?? {}; - validateOrderParameters(order); + (0, helpers_1.validateOrderParameters)(order); const endpoint = 'browse'; const response = await this._sendRequest(endpoint, body); - return await parseLibraryArtists(response, async (additionalParams) => await this._sendRequest(endpoint, body, additionalParams), limit); + return await (0, library_1.parseLibraryArtists)(response, async (additionalParams) => await this._sendRequest(endpoint, body, additionalParams), limit); } /** * Gets the artists the user has subscribed to. @@ -267,13 +270,13 @@

const body = { browseId: 'FEmusic_library_corpus_artists', }; - validateOrderParameters(order); + (0, helpers_1.validateOrderParameters)(order); if (order) { - body['params'] = prepareOrderParams(order); + body['params'] = (0, helpers_1.prepareOrderParams)(order); } const endpoint = 'browse'; const response = await this._sendRequest(endpoint, body); - return parseLibraryArtists(response, async (additionalParams) => await this._sendRequest(endpoint, body, additionalParams), limit); + return (0, library_1.parseLibraryArtists)(response, async (additionalParams) => await this._sendRequest(endpoint, body, additionalParams), limit); } /** * Gets playlist items for the 'Liked Songs' playlist. @@ -297,18 +300,18 @@

const body = { browseId: 'FEmusic_history' }; const endpoint = 'browse'; const response = await this._sendRequest(endpoint, body); - const results = nav(response, [...SINGLE_COLUMN_TAB, ...SECTION_LIST]); + const results = (0, utils_1.nav)(response, [...parsers_1.SINGLE_COLUMN_TAB, ...parsers_1.SECTION_LIST]); let songs = []; for (const content of results) { - const data = nav(content, [...MUSIC_SHELF, 'contents'], true); + const data = (0, utils_1.nav)(content, [...parsers_1.MUSIC_SHELF, 'contents'], true); if (!data) { - const error = nav(content, ['musicNotifierShelfRenderer', ...TITLE], true); + const error = (0, utils_1.nav)(content, ['musicNotifierShelfRenderer', ...parsers_1.TITLE], true); throw new Error(error); } - const menuEntries = [[-1, ...MENU_SERVICE, ...FEEDBACK_TOKEN]]; - const songlist = parsePlaylistItems(data, menuEntries); + const menuEntries = [[-1, ...parsers_1.MENU_SERVICE, ...parsers_1.FEEDBACK_TOKEN]]; + const songlist = (0, playlists_1.parsePlaylistItems)(data, menuEntries); for (const song of songlist) { - song['played'] = nav(content['musicShelfRenderer'], TITLE_TEXT); + song['played'] = (0, utils_1.nav)(content['musicShelfRenderer'], parsers_1.TITLE_TEXT); } songs = [...songs, ...songlist]; } @@ -336,7 +339,7 @@

async rateSong(videoId, rating = 'INDIFFERENT') { this._checkAuth(); const body = { target: { videoId } }; - const endpoint = prepareLikeEndpoint(rating); + const endpoint = (0, helpers_1.prepareLikeEndpoint)(rating); if (!endpoint) { throw new Error('Invalid rating provided'); } @@ -365,7 +368,7 @@

async ratePlaylist(playlistId, rating = 'INDIFFERENT') { this._checkAuth(); const body = { target: { playlistId } }; - const endpoint = prepareLikeEndpoint(rating); + const endpoint = (0, helpers_1.prepareLikeEndpoint)(rating); if (!endpoint) { throw new Error('Invalid rating provided'); } @@ -395,6 +398,7 @@

} }; }; +exports.LibraryMixin = LibraryMixin;

@@ -420,7 +424,7 @@

diff --git a/docs/mixins_playlists.js.html b/docs/mixins_playlists.js.html index d2e45ed..d7f9cca 100644 --- a/docs/mixins_playlists.js.html +++ b/docs/mixins_playlists.js.html @@ -83,7 +83,7 @@ @@ -109,15 +109,18 @@

-
import { htmlToText, sumTotalDuration, toInt } from '../helpers';
-import { DESCRIPTION, MUSIC_SHELF, NAVIGATION_BROWSE_ID, RELOAD_CONTINUATION, SECTION_LIST_ITEM, SINGLE_COLUMN_TAB, SUBTITLE2, SUBTITLE3, THUMBNAIL_CROPPED, TITLE_TEXT, } from '../parsers';
-import { parsePlaylistItems } from '../parsers/playlists';
-import { getContinuations, getContinuationString, nav, validatePlaylistId, } from '../parsers/utils';
+            
"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.PlaylistsMixin = void 0;
+const helpers_1 = require("../helpers");
+const parsers_1 = require("../parsers");
+const playlists_1 = require("../parsers/playlists");
+const utils_1 = require("../parsers/utils");
 /**
  * @module Playlists
  */
 // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
-export const PlaylistsMixin = (Base) => {
+const PlaylistsMixin = (Base) => {
     return class PlaylistsMixin extends Base {
         /**
          * Return a list of playlist items.
@@ -172,9 +175,9 @@ 

const body = { browseId: browseId }; const endpoint = 'browse'; const response = await this._sendRequest(endpoint, body); - const results = nav(response, [ - ...SINGLE_COLUMN_TAB, - ...SECTION_LIST_ITEM, + const results = (0, utils_1.nav)(response, [ + ...parsers_1.SINGLE_COLUMN_TAB, + ...parsers_1.SECTION_LIST_ITEM, 'musicPlaylistShelfRenderer', ]); const playlist = { @@ -193,51 +196,51 @@

header['editHeader']['musicPlaylistEditHeaderRenderer']['privacy']; header = header['header']['musicDetailHeaderRenderer']; } - playlist['title'] = nav(header, TITLE_TEXT); - playlist['thumbnails'] = nav(header, THUMBNAIL_CROPPED); - playlist['description'] = nav(header, DESCRIPTION, true); + playlist['title'] = (0, utils_1.nav)(header, parsers_1.TITLE_TEXT); + playlist['thumbnails'] = (0, utils_1.nav)(header, parsers_1.THUMBNAIL_CROPPED); + playlist['description'] = (0, utils_1.nav)(header, parsers_1.DESCRIPTION, true); const runCount = header['subtitle']['runs'].length; if (runCount > 1) { playlist['author'] = { - name: nav(header, SUBTITLE2), - id: nav(header, ['subtitle', 'runs', 2, ...NAVIGATION_BROWSE_ID], true), + name: (0, utils_1.nav)(header, parsers_1.SUBTITLE2), + id: (0, utils_1.nav)(header, ['subtitle', 'runs', 2, ...parsers_1.NAVIGATION_BROWSE_ID], true), }; if (runCount == 5) { - playlist['year'] = nav(header, SUBTITLE3); + playlist['year'] = (0, utils_1.nav)(header, parsers_1.SUBTITLE3); } } - const songCount = toInt(header['secondSubtitle']['runs'][0]['text'].normalize('NFKD')); + const songCount = (0, helpers_1.toInt)(header['secondSubtitle']['runs'][0]['text'].normalize('NFKD')); if (header['secondSubtitle']['runs'].length > 1) { playlist['duration'] = header['secondSubtitle']['runs'][2]['text']; } playlist['trackCount'] = songCount; - playlist['suggestions_token'] = nav(response, [ - ...SINGLE_COLUMN_TAB, + playlist['suggestions_token'] = (0, utils_1.nav)(response, [ + ...parsers_1.SINGLE_COLUMN_TAB, 'sectionListRenderer', 'contents', 1, - ...MUSIC_SHELF, - ...RELOAD_CONTINUATION, + ...parsers_1.MUSIC_SHELF, + ...parsers_1.RELOAD_CONTINUATION, ], true); playlist['tracks'] = []; if (songCount > 0) { playlist['tracks'] = [ ...playlist['tracks'], - ...parsePlaylistItems(results['contents']), + ...(0, playlists_1.parsePlaylistItems)(results['contents']), ]; const songsToGet = Math.min(limit, songCount); if ('continuations' in results) { const requestFunc = async (additionalParams) => await this._sendRequest(endpoint, body, additionalParams); - const parseFunc = (contents) => parsePlaylistItems(contents); + const parseFunc = (contents) => (0, playlists_1.parsePlaylistItems)(contents); playlist['tracks'] = [ ...playlist['tracks'], - ...(await getContinuations(results, 'musicPlaylistShelfContinuation', songsToGet - playlist['tracks'].length, requestFunc, parseFunc)), + ...(await (0, utils_1.getContinuations)(results, 'musicPlaylistShelfContinuation', songsToGet - playlist['tracks'].length, requestFunc, parseFunc)), ]; } } //For some reason we are able to go over limit, so manually truncate at the end @codyduong TODO playlist['tracks'] = playlist['tracks'].slice(0, limit); - playlist['duration_seconds'] = sumTotalDuration(playlist); + playlist['duration_seconds'] = (0, helpers_1.sumTotalDuration)(playlist); return playlist; } /** @@ -251,14 +254,14 @@

throw new Error('Suggestions token is undefined.\nPlease ensure the playlist is small enough to receive suggestions.'); } const endpoint = 'browse'; - const additionalParams = getContinuationString(suggestionsToken); + const additionalParams = (0, utils_1.getContinuationString)(suggestionsToken); const response = this._sendRequest(endpoint, {}, additionalParams); - const results = nav(response, [ + const results = (0, utils_1.nav)(response, [ 'continuationContents', 'musicShelfContinuation', ]); - const refreshToken = nav(results, RELOAD_CONTINUATION); - const suggestions = parsePlaylistItems(results['contents']); + const refreshToken = (0, utils_1.nav)(results, parsers_1.RELOAD_CONTINUATION); + const suggestions = (0, playlists_1.parsePlaylistItems)(results['contents']); return { tracks: suggestions, refresh_token: refreshToken }; } /** @@ -294,7 +297,7 @@

} const body = { title: title, - description: htmlToText(actualDescription ?? ''), + description: (0, helpers_1.htmlToText)(actualDescription ?? ''), privacyStatus: actualPrivacyStatus, }; if (actualVideoIds) { @@ -325,7 +328,7 @@

this._checkAuth(); const { title, description, privacyStatus, moveItem, addPlaylistId } = options; const body = { - playlistId: validatePlaylistId(playlistId), + playlistId: (0, utils_1.validatePlaylistId)(playlistId), }; const actions = []; if (title) { @@ -373,7 +376,7 @@

*/ async deletePlaylist(playlistId) { this._checkAuth(); - const body = { playlistId: validatePlaylistId(playlistId) }; + const body = { playlistId: (0, utils_1.validatePlaylistId)(playlistId) }; const endpoint = 'playlist/delete'; const response = await this._sendRequest(endpoint, body); return 'status' in response ? response['status'] : response; @@ -390,7 +393,7 @@

this._checkAuth(); const { videoIds, sourcePlaylist, duplicates } = options; const body = { - playlistId: validatePlaylistId(playlistId), + playlistId: (0, utils_1.validatePlaylistId)(playlistId), actions: [], }; if (!videoIds && !sourcePlaylist) { @@ -447,7 +450,7 @@

throw new Error('Cannot remove songs, because setVideoId is missing. Do you own this playlist?'); } const body = { - playlistId: validatePlaylistId(playlistId), + playlistId: (0, utils_1.validatePlaylistId)(playlistId), actions: [], }; for (const video of videos) { @@ -463,6 +466,7 @@

} }; }; +exports.PlaylistsMixin = PlaylistsMixin;

@@ -488,7 +492,7 @@

diff --git a/docs/mixins_uploads.js.html b/docs/mixins_uploads.js.html index e24db96..8331ad0 100644 --- a/docs/mixins_uploads.js.html +++ b/docs/mixins_uploads.js.html @@ -83,7 +83,7 @@ @@ -109,21 +109,27 @@

-
/**
+            
"use strict";
+/**
  * @module Uploads
  */
-import { prepareOrderParams, sumTotalDuration, validateOrderParameters, } from '../helpers';
-import { SINGLE_COLUMN_TAB, SECTION_LIST, ITEM_SECTION, MUSIC_SHELF, SECTION_LIST_ITEM, } from '../parsers';
-import { parseAlbumHeader } from '../parsers/albums';
-import { parseLibraryArtists, parseLibraryAlbums } from '../parsers/library';
-import { parseUploadedItems } from '../parsers/uploads';
-import { findObjectByKey, getContinuations, nav } from '../parsers/utils';
-import { existsSync, readFileSync, statSync } from 'fs';
-import { extname, basename } from 'path';
-import utf8 from 'utf8';
-import axios from 'axios';
+var __importDefault = (this && this.__importDefault) || function (mod) {
+    return (mod && mod.__esModule) ? mod : { "default": mod };
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.UploadsMixin = void 0;
+const helpers_1 = require("../helpers");
+const parsers_1 = require("../parsers");
+const albums_1 = require("../parsers/albums");
+const library_1 = require("../parsers/library");
+const uploads_1 = require("../parsers/uploads");
+const utils_1 = require("../parsers/utils");
+const fs_1 = require("fs");
+const path_1 = require("path");
+const utf8_1 = __importDefault(require("utf8"));
+const axios_1 = __importDefault(require("axios"));
 // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
-export const UploadsMixin = (Base) => {
+const UploadsMixin = (Base) => {
     return class UploadsMixin extends Base {
         async getLibraryUploadSongs(options, order) {
             this._checkAuth();
@@ -132,13 +138,13 @@ 

const body = { browseId: 'FEmusic_library_privately_owned_tracks', }; - validateOrderParameters(_order); + (0, helpers_1.validateOrderParameters)(_order); if (_order) { - body['params'] = prepareOrderParams(_order); + body['params'] = (0, helpers_1.prepareOrderParams)(_order); } const response = await this._sendRequest(endpoint, body); - let results = findObjectByKey(nav(response, [...SINGLE_COLUMN_TAB, ...SECTION_LIST]), 'itemSectionRenderer'); - results = nav(results, ITEM_SECTION); + let results = (0, utils_1.findObjectByKey)((0, utils_1.nav)(response, [...parsers_1.SINGLE_COLUMN_TAB, ...parsers_1.SECTION_LIST]), 'itemSectionRenderer'); + results = (0, utils_1.nav)(results, parsers_1.ITEM_SECTION); if (!results['musicShelfRenderer']) { return []; } @@ -146,12 +152,12 @@

results = results['musicShelfRenderer']; } let songs = []; - songs = [...parseUploadedItems(results['contents'].slice(1))]; + songs = [...(0, uploads_1.parseUploadedItems)(results['contents'].slice(1))]; if ('continuations' in results) { const requestFunc = async (additionalParams) => await this._sendRequest(endpoint, body, additionalParams); songs = [ ...songs, - ...(await getContinuations(results, 'musicShelfContinuation', limit - songs.length, requestFunc, parseUploadedItems)), + ...(await (0, utils_1.getContinuations)(results, 'musicShelfContinuation', limit - songs.length, requestFunc, uploads_1.parseUploadedItems)), ]; } return songs; @@ -162,13 +168,13 @@

const body = { browseId: 'FEmusic_library_privately_owned_releases', }; - validateOrderParameters(_order); + (0, helpers_1.validateOrderParameters)(_order); if (_order) { - body['params'] = prepareOrderParams(_order); + body['params'] = (0, helpers_1.prepareOrderParams)(_order); } const endpoint = 'browse'; const response = await this._sendRequest(endpoint, body); - return await parseLibraryAlbums(response, async (additionalParams) => await this._sendRequest(endpoint, body, additionalParams), limit); + return await (0, library_1.parseLibraryAlbums)(response, async (additionalParams) => await this._sendRequest(endpoint, body, additionalParams), limit); } async getLibraryUploadArtists(options, order) { this._checkAuth(); @@ -176,13 +182,13 @@

const body = { browseId: 'FEmusic_library_privately_owned_artists', }; - validateOrderParameters(_order); + (0, helpers_1.validateOrderParameters)(_order); if (_order) { - body['params'] = prepareOrderParams(_order); + body['params'] = (0, helpers_1.prepareOrderParams)(_order); } const endpoint = 'browse'; const response = await this._sendRequest(endpoint, body); - return parseLibraryArtists(response, async (additionalParams) => await this._sendRequest(endpoint, body, additionalParams), limit); + return (0, library_1.parseLibraryArtists)(response, async (additionalParams) => await this._sendRequest(endpoint, body, additionalParams), limit); } /** * Returns a list of uploaded tracks for the artist. @@ -211,21 +217,21 @@

const body = { browseId: browseId }; const endpoint = 'browse'; const response = await this._sendRequest(endpoint, body); - const results = nav(response, [ - ...SINGLE_COLUMN_TAB, - ...SECTION_LIST_ITEM, - ...MUSIC_SHELF, + const results = (0, utils_1.nav)(response, [ + ...parsers_1.SINGLE_COLUMN_TAB, + ...parsers_1.SECTION_LIST_ITEM, + ...parsers_1.MUSIC_SHELF, ]); if (results['contents'].results > 1) { results['contents'].pop(0); } - let items = parseUploadedItems(results['contents']); + let items = (0, uploads_1.parseUploadedItems)(results['contents']); if ('continuations' in results) { const requestFunc = async (additionalParams) => await this._sendRequest(endpoint, body, additionalParams); - const parseFunc = (contents) => parseUploadedItems(contents); + const parseFunc = (contents) => (0, uploads_1.parseUploadedItems)(contents); items = [ ...items, - ...(await getContinuations(results, 'musicShelfContinuation', limit, requestFunc, parseFunc)), + ...(await (0, utils_1.getContinuations)(results, 'musicShelfContinuation', limit, requestFunc, parseFunc)), ]; } return items; @@ -265,14 +271,14 @@

const body = { browseId: browseId }; const endpoint = 'browse'; const response = await this._sendRequest(endpoint, body); - const album = parseAlbumHeader(response); - const results = nav(response, [ - ...SINGLE_COLUMN_TAB, - ...SECTION_LIST_ITEM, - ...MUSIC_SHELF, + const album = (0, albums_1.parseAlbumHeader)(response); + const results = (0, utils_1.nav)(response, [ + ...parsers_1.SINGLE_COLUMN_TAB, + ...parsers_1.SECTION_LIST_ITEM, + ...parsers_1.MUSIC_SHELF, ]); - album['tracks'] = parseUploadedItems(results['contents']); - album['duration_seconds'] = sumTotalDuration(album); + album['tracks'] = (0, uploads_1.parseUploadedItems)(results['contents']); + album['duration_seconds'] = (0, helpers_1.sumTotalDuration)(album); return album; } /** @@ -282,37 +288,37 @@

*/ async uploadSong(filepath) { this._checkAuth(); - if (!existsSync(filepath)) { + if (!(0, fs_1.existsSync)(filepath)) { throw new Error('The provided file does not exist.'); } const supportedFiletypes = ['mp3', 'm4a', 'wma', 'flac', 'ogg']; - if (supportedFiletypes.includes(extname(filepath))) { + if (supportedFiletypes.includes((0, path_1.extname)(filepath))) { throw new Error('The provided file type is not supported by YouTube Music. Supported file types are ' + supportedFiletypes.join(', ')); } - const headers = this.headers; + const headers = this._headers; let uploadUrl = `https://upload.youtube.com/upload/usermusic/http?authuser=${headers['x-goog-authuser']}`; - const filesize = statSync(filepath).size; - const body = 'filename=' + utf8.encode(basename(filepath)); + const filesize = (0, fs_1.statSync)(filepath).size; + const body = 'filename=' + utf8_1.default.encode((0, path_1.basename)(filepath)); delete headers['content-encoding']; headers['content-type'] = 'application/x-www-form-urlencoded;charset=utf-8'; headers['X-Goog-Upload-Command'] = 'start'; headers['X-Goog-Upload-Header-Content-Length'] = filesize; headers['X-Goog-Upload-Protocol'] = 'resumable'; - const response = await axios.post(uploadUrl, body, { + const response = await axios_1.default.post(uploadUrl, body, { headers: headers, - proxy: this.proxies, + proxy: this._proxies, }); headers['X-Goog-Upload-Command'] = 'upload, finalize'; headers['X-Goog-Upload-Offset'] = '0'; uploadUrl = response.headers['X-Goog-Upload-URL'] ?? response.headers['x-goog-upload-url']; - const data = readFileSync(filepath); - const response2 = await axios.post(uploadUrl, data, { + const data = (0, fs_1.readFileSync)(filepath); + const response2 = await axios_1.default.post(uploadUrl, data, { headers: headers, - proxy: this.proxies, + proxy: this._proxies, }); if (response2.status == 200) { return 'STATUS_SUCCEEDED'; @@ -344,6 +350,7 @@

} }; }; +exports.UploadsMixin = UploadsMixin;

@@ -369,7 +376,7 @@

diff --git a/docs/mixins_watch.js.html b/docs/mixins_watch.js.html index c821ea2..ba5f0a6 100644 --- a/docs/mixins_watch.js.html +++ b/docs/mixins_watch.js.html @@ -83,7 +83,7 @@ @@ -109,14 +109,17 @@

-
/**
+            
"use strict";
+/**
  * @module Watch
  */
-import { NAVIGATION_PLAYLIST_ID, TAB_CONTENT } from '../parsers';
-import { getContinuations, nav, validatePlaylistId } from '../parsers/utils';
-import { parseWatchPlaylist } from '../parsers/watch';
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.WatchMixin = void 0;
+const parsers_1 = require("../parsers");
+const utils_1 = require("../parsers/utils");
+const watch_1 = require("../parsers/watch");
 // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
-export const WatchMixin = (Base) => {
+const WatchMixin = (Base) => {
     return class WatchMixin extends Base {
         /**
          * Get a watch list of tracks. This watch playlist appears when you press
@@ -222,7 +225,7 @@ 

}; } } - body['playlistId'] = validatePlaylistId(playlistId); + body['playlistId'] = (0, utils_1.validatePlaylistId)(playlistId); const isPlaylist = body['playlistId'].startsWith('PL') || body['playlistId'].startsWith('OLA'); if (params) { @@ -230,7 +233,7 @@

} const endpoint = 'next'; const response = await this._sendRequest(endpoint, body); - const watchNextRenderer = nav(response, [ + const watchNextRenderer = (0, utils_1.nav)(response, [ 'contents', 'singleColumnMusicWatchNextResultsRenderer', 'tabbedRenderer', @@ -241,22 +244,22 @@

lyrics_browse_id = watchNextRenderer['tabs'][1]['tabRenderer']['endpoint']['browseEndpoint']['browseId']; } - const results = nav(watchNextRenderer, [ - ...TAB_CONTENT, + const results = (0, utils_1.nav)(watchNextRenderer, [ + ...parsers_1.TAB_CONTENT, 'musicQueueRenderer', 'content', 'playlistPanelRenderer', ]); const playlist = results['contents'] - .map((x) => nav(x, ['playlistPanelVideoRenderer', ...NAVIGATION_PLAYLIST_ID], true)) + .map((x) => (0, utils_1.nav)(x, ['playlistPanelVideoRenderer', ...parsers_1.NAVIGATION_PLAYLIST_ID], true)) .filter((x) => !!x)[0]; - let tracks = parseWatchPlaylist(results['contents']); + let tracks = (0, watch_1.parseWatchPlaylist)(results['contents']); if ('continuations' in results) { const request_func = (additionalParams) => this._sendRequest(endpoint, body, additionalParams); - const parse_func = (contents) => parseWatchPlaylist(contents); + const parse_func = (contents) => (0, watch_1.parseWatchPlaylist)(contents); tracks = [ ...tracks, - ...(await getContinuations(results, 'playlistPanelContinuation', limit - tracks.length, request_func, parse_func, isPlaylist ? '' : 'Radio')), + ...(await (0, utils_1.getContinuations)(results, 'playlistPanelContinuation', limit - tracks.length, request_func, parse_func, isPlaylist ? '' : 'Radio')), ]; } return { tracks: tracks, playlistId: playlist, lyrics: lyrics_browse_id }; @@ -276,6 +279,7 @@

} }; }; +exports.WatchMixin = WatchMixin;

@@ -301,7 +305,7 @@

diff --git a/docs/module-Browsing.html b/docs/module-Browsing.html index be21281..b7746c3 100644 --- a/docs/module-Browsing.html +++ b/docs/module-Browsing.html @@ -83,7 +83,7 @@ @@ -254,7 +254,7 @@

@@ -432,7 +432,7 @@

@@ -587,7 +587,7 @@

@@ -776,7 +776,7 @@

@@ -880,7 +880,7 @@

@@ -1067,7 +1067,7 @@

@@ -1228,7 +1228,7 @@

@@ -1389,7 +1389,7 @@

@@ -1589,7 +1589,7 @@

@@ -1755,7 +1755,7 @@

@@ -1966,7 +1966,7 @@

@@ -2039,7 +2039,7 @@

diff --git a/docs/module-Explore.html b/docs/module-Explore.html index f97c001..15b37a1 100644 --- a/docs/module-Explore.html +++ b/docs/module-Explore.html @@ -83,7 +83,7 @@ @@ -274,7 +274,7 @@

@@ -389,7 +389,7 @@

@@ -550,7 +550,7 @@

@@ -623,7 +623,7 @@

diff --git a/docs/module-Library.html b/docs/module-Library.html index cd0bab0..cea556f 100644 --- a/docs/module-Library.html +++ b/docs/module-Library.html @@ -83,7 +83,7 @@ @@ -266,7 +266,7 @@

@@ -370,7 +370,7 @@

@@ -619,7 +619,7 @@

@@ -881,7 +881,7 @@

@@ -1069,7 +1069,7 @@

@@ -1370,7 +1370,7 @@

@@ -1626,7 +1626,7 @@

Properties
@@ -1801,7 +1801,7 @@

@@ -2013,7 +2013,7 @@

@@ -2225,7 +2225,7 @@

@@ -2375,7 +2375,7 @@

@@ -2525,7 +2525,7 @@

@@ -2675,7 +2675,7 @@

@@ -2748,7 +2748,7 @@

diff --git a/docs/module-Playlists.html b/docs/module-Playlists.html index 7289672..b07f6e7 100644 --- a/docs/module-Playlists.html +++ b/docs/module-Playlists.html @@ -83,7 +83,7 @@ @@ -378,7 +378,7 @@

@@ -737,7 +737,7 @@

Properties
@@ -904,7 +904,7 @@

@@ -1265,7 +1265,7 @@

Properties
@@ -1477,7 +1477,7 @@

@@ -1627,7 +1627,7 @@

@@ -1795,7 +1795,7 @@

@@ -1855,7 +1855,7 @@

diff --git a/docs/module-Uploads.html b/docs/module-Uploads.html index 443ca98..e1a8261 100644 --- a/docs/module-Uploads.html +++ b/docs/module-Uploads.html @@ -83,7 +83,7 @@ @@ -249,7 +249,7 @@

@@ -416,7 +416,7 @@

@@ -639,7 +639,7 @@

@@ -787,7 +787,7 @@

@@ -860,7 +860,7 @@

diff --git a/docs/module-Watch.html b/docs/module-Watch.html index e1e2530..bcd759b 100644 --- a/docs/module-Watch.html +++ b/docs/module-Watch.html @@ -83,7 +83,7 @@ @@ -318,7 +318,7 @@

@@ -530,7 +530,7 @@

@@ -603,7 +603,7 @@

diff --git a/package.json b/package.json index 8a994a2..4772ad5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@codyduong/ytmusicapi", - "version": "0.3.0", + "version": "0.3.1", "description": "Unofficial API for YouTube Music", "main": "dist/index.js", "browser": "dist/index.browser.js", diff --git a/src/index.ts b/src/index.ts index 950605c..de044ad 100644 --- a/src/index.ts +++ b/src/index.ts @@ -10,8 +10,7 @@ import { UploadsMixin } from './mixins/uploads'; * Allows automated interactions with YouTube Music by emulating the YouTube web client's requests. * Permits both authenticated and non-authenticated requests. * Authentication header data must be provided on initialization. - * @class - * @param {Object} [options=] Options object. + * @param {_YTMusicConstructorOptions} [options=] Options object. * @param {string | object} [options.auth=] Provide a string (raw headers), object, or path (Node only!), * Authentication credentials are needed to manage your library. * Should be an adjusted version of `headers_auth.json.example` in the project root. @@ -22,11 +21,11 @@ import { UploadsMixin } from './mixins/uploads'; * Otherwise the default account is used. You can retrieve the user ID * by going to https://myaccount.google.com/brandaccounts and selecting your brand account. * The user ID will be in the URL: https://myaccount.google.com/b/user_id/ - * @param {any} [options.proxies] Optional. No usage in current API + * @param {} [options.httpsAgent] Optional. Define an HTTP proxy for your request. + * @param {AxiosProxyConfig} [options.proxies] Optional. Define an HTTP proxy for your request. * @param {string} [options.language] Optional. Can be used to change the language of returned data. * English will be used by default. Available languages can be checked in - * the ytmusicapi/locales directory. A language that is not in the directory will still be - * attempted to be translated, but results may not be the best. + * the ytmusicapi/locales directory. */ const YTMusic = UploadsMixin( LibraryMixin( diff --git a/src/ytmusic.ts b/src/ytmusic.ts index 27de98b..ab925db 100644 --- a/src/ytmusic.ts +++ b/src/ytmusic.ts @@ -28,13 +28,13 @@ if (typeof process != 'undefined') { export class _YTMusic { #auth: string | null; - #httpsAgent: https.Agent | undefined; + _httpsAgent: https.Agent | undefined; _proxies?: AxiosProxyConfig | false; _headers: Headers; - #context: any; + _context: any; #language: string | undefined; _parser: Parser; - #sapisid: any; + _sapisid: any; /** * This is an internal class, please use {@link YTMusic} @@ -69,14 +69,14 @@ export class _YTMusic { if (typeof httpsAgent === 'boolean') { if (httpsAgent) { - this.#httpsAgent = new https.Agent({ + this._httpsAgent = new https.Agent({ timeout: 30000, }); } else { - this.#httpsAgent = undefined; + this._httpsAgent = undefined; } } else { - this.#httpsAgent = httpsAgent; + this._httpsAgent = httpsAgent; } this._proxies = proxies; @@ -108,8 +108,8 @@ export class _YTMusic { } // prepare context - this.#context = helpers.initializeContext(); - this.#context['context']['client']['hl'] = language; + this._context = helpers.initializeContext(); + this._context['context']['client']['hl'] = language; this.#language = language; const supportedLanguages = ['en', 'de', 'es', 'fr', 'it', 'ja']; @@ -146,14 +146,14 @@ export class _YTMusic { this._parser = new Parser(); if (user) { - this.#context['context']['user']['onBehalfOfUser'] = user; + this._context['context']['user']['onBehalfOfUser'] = user; } // verify authentication credentials work if (auth) { const cookie = this._headers.cookie; if (cookie) { - this.#sapisid = helpers.sapisidFromCookie(cookie); + this._sapisid = helpers.sapisidFromCookie(cookie); } else { throw new Error( 'Your cookie is missing the required value __Secure-3PAPISID' @@ -167,12 +167,12 @@ export class _YTMusic { body: Record, additionalParams = '' ): Promise { - body = { ...body, ...this.#context }; + body = { ...body, ...this._context }; if (this.#auth) { const origin = this._headers['origin'] ?? this._headers['x-origin']; this._headers['authorization'] = helpers.getAuthorization( - this.#sapisid + ' ' + origin + this._sapisid + ' ' + origin ); } @@ -186,7 +186,7 @@ export class _YTMusic { { headers: this._headers, proxy: this._proxies, - httpsAgent: this.#httpsAgent, + httpsAgent: this?._httpsAgent, } ); //console.log(response); @@ -203,7 +203,7 @@ export class _YTMusic { params: params, headers: this?._headers, proxy: this?._proxies, - httpsAgent: this.#httpsAgent, + httpsAgent: this?._httpsAgent, }); return response.data; } @@ -242,7 +242,7 @@ export class _YTMusic { async changeLanguage(language: string): Promise { this.#language = language; - this.#context['context']['client']['hl'] = language; + this._context['context']['client']['hl'] = language; const changeLanguage = new Promise((resolve, reject) => { i18next.changeLanguage(language, (error) => { if (error) {