UNPKG

spotify-api.js

Version:

A complete node js wrapper of spotify api with oauth support

230 lines (229 loc) 10.1 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Client = void 0; const axios_1 = __importDefault(require("axios")); const Error_1 = require("./Error"); const Auth_1 = require("./managers/Auth"); const User_1 = require("./managers/User"); const Artist_1 = require("./managers/Artist"); const Browse_1 = require("./managers/Browse"); const Album_1 = require("./managers/Album"); const Episode_1 = require("./managers/Episode"); const Playlist_1 = require("./managers/Playlist"); const Show_1 = require("./managers/Show"); const Track_1 = require("./managers/Track"); const UserClient_1 = require("./managers/UserClient"); const Cache_1 = require("./Cache"); const NOOP = () => { }; /** * The basic client to interact with the Spotify Web API. */ class Client { /** * The basic client to interact with the Spotify Web API. * * @param options The options necessary for the client. * @example const client = new Client({ token: "someToken" }); */ constructor(options) { var _a; /** * The version of spotify web api. For future purposes. */ this.version = 'v1'; /** * The refresh event of the client. */ this.onRefresh = NOOP; /** * Boolean stating should the client retry when the request is rate limited or not by default it is true. */ this.retryOnRateLimit = true; /** * Cache settings for the client. */ this.cacheSettings = {}; this.onRefresh = options.onRefresh || NOOP; this.retryOnRateLimit = (_a = options.retryOnRateLimit) !== null && _a !== void 0 ? _a : true; this.auth = new Auth_1.AuthManager(this.token); this.users = new User_1.UserManager(this); this.artists = new Artist_1.ArtistManager(this); this.browse = new Browse_1.BrowseManager(this); this.albums = new Album_1.AlbumManager(this); this.episodes = new Episode_1.EpisodeManager(this); this.playlists = new Playlist_1.PlaylistManager(this); this.shows = new Show_1.ShowManager(this); this.tracks = new Track_1.TrackManager(this); this._init(options); if (typeof options.cacheSettings == "object") this.cacheSettings = options.cacheSettings; else if (options.cacheSettings == true) this.cacheSettings = { users: true, artists: true, tracks: true, episodes: true, shows: true, albums: true, playlists: true }; } /** * Creates a client and returns it as promise when its ready. * * @param options The same client options provided for the constructor but "onReady" and "onFail" fields should not be provided. * @example const client = await Client.create({ token: "token" }); */ static create(options) { return new Promise((onReady, onFail) => new Client({ ...options, onReady, onFail })); } /** * Search a query in spotify through web api across various types. * * @param query The query to search. * @param options The types, limit, offset, market query paramaters. * @example const { tracks, albums } = await client.search('some query', { types: ['track', 'album'] }); */ async search(query, options) { const response = {}; const fetchedData = await this.fetch('/search', { params: { q: query, type: options.types.join(','), market: options.market, limit: options.limit, offset: options.offset, include_external: options.includeExternalAudio ? 'audio' : undefined } }); if (fetchedData.albums) response.albums = (0, Cache_1.createCacheStructArray)('albums', this, fetchedData.albums.items); if (fetchedData.tracks) response.tracks = (0, Cache_1.createCacheStructArray)('tracks', this, fetchedData.tracks.items); if (fetchedData.episodes) response.episodes = (0, Cache_1.createCacheStructArray)('episodes', this, fetchedData.episodes.items); if (fetchedData.shows) response.shows = (0, Cache_1.createCacheStructArray)('shows', this, fetchedData.shows.items); if (fetchedData.artists) response.artists = (0, Cache_1.createCacheStructArray)('artists', this, fetchedData.artists.items); return response; } /** * Used to fetch data from spotify rest api. * * @param url The path from spotify api to fetch! * @param options The additional options required to fetch from the api. * @example await client.fetch('/users/id'); */ fetch(url, options = {}) { return (0, axios_1.default)({ url: `https://api.spotify.com/${this.version}${url}`, method: options.method || 'GET', params: options.params, headers: { Authorization: "Bearer " + this.token, Accept: 'application/json', ...options.headers }, data: options.body }).then(response => response.data, async (error) => { var _a, _b, _c, _d; if (!error.response) throw new Error_1.SpotifyAPIError(error); else if (error.response.status == 404) return null; else if (error.response.status == 429 && this.retryOnRateLimit) { const retryAfter = error.response.headers['Retry-After']; if (typeof retryAfter == "number") await new Promise(r => setTimeout(r, retryAfter * 1000)); } else if ( // @ts-ignore ((_b = (_a = error.response.data) === null || _a === void 0 ? void 0 : _a.error) === null || _b === void 0 ? void 0 : _b.message) == "Invalid access token" || // @ts-ignore ((_d = (_c = error.response.data) === null || _c === void 0 ? void 0 : _c.error) === null || _d === void 0 ? void 0 : _d.message) == "The access token expired" && this.refreshMeta) await this.refreshFromMeta(); else throw new Error_1.SpotifyAPIError(error); return this.fetch(url, options); }); } /** * Refreshes the token from meta. */ async refreshFromMeta() { if (!this.refreshMeta) return; if ('refreshToken' in this.refreshMeta) { this.auth.getUserToken(this.refreshMeta) .then(context => { this.token = context.accessToken; if (context.refreshToken) this.refreshMeta.refreshToken = context.refreshToken; new UserClient_1.UserClient(this).patchInfo().then(x => { this.user = x; this.onRefresh(); }); }); } else { this.auth.getApiToken(this.refreshMeta.clientID, this.refreshMeta.clientSecret) .then(token => { this.token = token; this.onRefresh(); }); } this.auth = new Auth_1.AuthManager(this.token); } /** * A function to initate the client through options and client options. */ async _init(options) { var _a; if (!options.token) throw new Error_1.SpotifyAPIError('No token was provided in [ClientOptions]'); try { if (typeof options.token == "string") { if (options.refreshToken) console.trace("[SpotifyClientWarn]: You have provided a token and used `refreshToken` option. Try to provide clientID, clientSecret or user authenication details."); this.token = options.token; if (options.userAuthorizedToken) this.user = await new UserClient_1.UserClient(this).patchInfo(); } else if ('token' in options.token) { this.token = options.token.token; this.refreshMeta = options.token; if (options.userAuthorizedToken) this.user = await new UserClient_1.UserClient(this).patchInfo(); } else if (('redirectURL' in options.token) || ('refreshToken' in options.token)) { const context = await this.auth.getUserToken(options.token); this.refreshMeta = options.token; if (context.refreshToken) this.refreshMeta.refreshToken = context.refreshToken; this.token = context.accessToken; this.user = await new UserClient_1.UserClient(this).patchInfo(); } else if ('clientID' in options.token) { this.refreshMeta = options.token; this.token = await this.auth.getApiToken(options.token.clientID, options.token.clientSecret); } else throw new Error_1.SpotifyAPIError('Improper [ClientOptions] provided!.'); (_a = options.onReady) === null || _a === void 0 ? void 0 : _a.call(options, this); } catch (error) { // Only possible error here that could be thrown is AxiosError from getApiToken and getUserToken. error = new Error_1.SpotifyAPIError(error); if (options.onFail) options.onFail(error); else throw error; } } } exports.Client = Client;