UNPKG

ym-api

Version:

A Node.js wrapper for the Yandex.Music API (Unofficial) http://music.yandex.ru

384 lines (383 loc) 13 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const PreparedRequest_1 = require("./PreparedRequest"); const config_1 = __importDefault(require("./config")); const HttpClient_1 = __importDefault(require("./HttpClient")); const xml2js_1 = require("xml2js"); const crypto_1 = __importDefault(require("crypto")); class YMApi { constructor(httpClient = new HttpClient_1.default(), config = config_1.default) { this.httpClient = httpClient; this.config = config; this.user = { password: "", token: "", uid: 0, username: "", }; } getAuthHeader() { return { Authorization: `OAuth ${this.user.token}`, }; } /** * Authentication */ async init(config) { // Skip auth if access_token and uid are present if (config.access_token && config.uid) { this.user.token = config.access_token; this.user.uid = config.uid; return { access_token: config.access_token, uid: config.uid, }; } if (!config.username || !config.password) { throw new Error("username && password || access_token && uid must be set"); } this.user.username = config.username; this.user.password = config.password; const data = (await this.httpClient.post(PreparedRequest_1.authRequest().setPath("/1/token").setBodyData({ grant_type: "password", username: this.user.username, password: this.user.password, client_id: this.config.oauth.CLIENT_ID, client_secret: this.config.oauth.CLIENT_SECRET, }))); this.user.token = data.access_token; this.user.uid = data.uid; return data; } /** * GET: /account/status * Get account status for curren user */ getAccountStatus() { const request = PreparedRequest_1.apiRequest() .setPath("/account/status") .addHeaders(this.getAuthHeader()); return this.httpClient.get(request); } /** * GET: /feed * Get the user's feed */ getFeed() { const request = PreparedRequest_1.apiRequest() .setPath("/feed") .addHeaders(this.getAuthHeader()); return this.httpClient.get(request); } /** * GET: /genres * Get a list of music genres */ getGenres() { const request = PreparedRequest_1.apiRequest() .setPath("/genres") .addHeaders(this.getAuthHeader()); return this.httpClient.get(request); } /** * GET: /search * Search artists, tracks, albums. */ search(query, options = {}) { const type = !options.type ? "all" : options.type; const page = String(!options.page ? 0 : options.page); const nococrrect = String(options.nococrrect == null ? false : options.nococrrect); const request = PreparedRequest_1.apiRequest() .setPath("/search") .addHeaders(this.getAuthHeader()) .setQuery({ type, text: query, page, nococrrect, }); if (options.pageSize !== void 0) { request.addQuery({ pageSize: String(options.pageSize) }); } return this.httpClient.get(request); } searchArtists(query, options = {}) { return this.search(query, { ...options, type: "artist", }); } searchTracks(query, options = {}) { return this.search(query, { ...options, type: "track", }); } searchAlbums(query, options = {}) { return this.search(query, { ...options, type: "album", }); } searchAll(query, options = {}) { return this.search(query, { ...options, type: "all", }); } /** * GET: /users/[user_id]/playlists/list * Get a user's playlists. */ getUserPlaylists(user = null) { const uid = [null, 0, ""].includes(user) ? this.user.uid : user; const request = PreparedRequest_1.apiRequest() .setPath(`/users/${uid}/playlists/list`) .addHeaders(this.getAuthHeader()); return this.httpClient.get(request); } /** * GET: /users/[user_id]/playlists/[playlist_kind] * Get a playlist without tracks */ getPlaylist(playlistId, user = null) { const uid = [null, 0, ""].includes(user) ? this.user.uid : user; const request = PreparedRequest_1.apiRequest() .setPath(`/users/${uid}/playlists/${playlistId}`) .addHeaders(this.getAuthHeader()); return this.httpClient.get(request); } /** * GET: /users/[user_id]/playlists * Get an array of playlists with tracks */ getPlaylists(playlists, user = null, options = {}) { const uid = [null, 0, ""].includes(user) ? this.user.uid : user; const kinds = playlists.join(); const mixed = String(options.mixed == null ? false : options.mixed); const richTracks = String(options["rich-tracks"] == null ? false : options["rich-tracks"]); const request = PreparedRequest_1.apiRequest() .setPath(`/users/${uid}/playlists`) .addHeaders(this.getAuthHeader()) .setQuery({ kinds, mixed, "rich-tracks": richTracks, }); return this.httpClient.get(request); } /** * POST: /users/[user_id]/playlists/create * Create a new playlist */ createPlaylist(name, options = {}) { const visibility = !options.visibility ? "private" : options.visibility; const request = PreparedRequest_1.apiRequest() .setPath(`/users/${this.user.uid}/playlists/create`) .addHeaders(this.getAuthHeader()) .setBodyData({ title: name, visibility, }); return this.httpClient.post(request); } /** * POST: /users/[user_id]/playlists/[playlist_kind]/delete * Remove a playlist */ removePlaylist(playlistId) { const request = PreparedRequest_1.apiRequest() .setPath(`/users/${this.user.uid}/playlists/${playlistId}/delete`) .addHeaders(this.getAuthHeader()); return this.httpClient.post(request); } /** * POST: /users/[user_id]/playlists/[playlist_kind]/name * Change playlist name */ renamePlaylist(playlistId, name) { const request = PreparedRequest_1.apiRequest() .setPath(`/users/${this.user.uid}/playlists/${playlistId}/name`) .addHeaders(this.getAuthHeader()) .setBodyData({ value: name, }); return this.httpClient.post(request); } /** * POST: /users/[user_id]/playlists/[playlist_kind]/change-relative * Add tracks to the playlist */ addTracksToPlaylist(playlistId, tracks, revision, options = {}) { const at = !options.at ? 0 : options.at; const request = PreparedRequest_1.apiRequest() .setPath(`/users/${this.user.uid}/playlists/${playlistId}/change-relative`) .addHeaders(this.getAuthHeader()) .setBodyData({ diff: JSON.stringify([ { op: "insert", at, tracks: tracks, }, ]), revision: String(revision), }); return this.httpClient.post(request); } /** * POST: /users/[user_id]/playlists/[playlist_kind]/change-relative * Remove tracks from the playlist */ removeTracksFromPlaylist(playlistId, tracks, revision, options = {}) { const from = !options.from ? 0 : options.from; const to = !options.to ? tracks.length : options.to; const request = PreparedRequest_1.apiRequest() .setPath(`/users/${this.user.uid}/playlists/${playlistId}/change-relative`) .addHeaders(this.getAuthHeader()) .setBodyData({ diff: JSON.stringify([ { op: "delete", from, to, tracks, }, ]), revision: String(revision), }); return this.httpClient.post(request); } /** * GET: /tracks/[track_id] * Get an array of playlists with tracks */ getTrack(trackId) { const request = PreparedRequest_1.apiRequest() .setPath(`/tracks/${trackId}`) .addHeaders(this.getAuthHeader()); return this.httpClient.get(request); } /** * GET: /tracks/[track_id] * Get single track */ async getSingleTrack(trackId) { const tracks = await this.getTrack(trackId); if (tracks.length !== 1) { throw new Error(`More than one result received`); } return tracks.pop(); } /** * GET: /tracks/[track_id]/supplement * Get an array of playlists with tracks */ getTrackSupplement(trackId) { const request = PreparedRequest_1.apiRequest() .setPath(`/tracks/${trackId}/supplement`) .addHeaders(this.getAuthHeader()); return this.httpClient.get(request); } /** * GET: /tracks/[track_id]/download-info * Get track download information */ getTrackDownloadInfo(trackId) { const request = PreparedRequest_1.apiRequest() .setPath(`/tracks/${trackId}/download-info`) .addHeaders(this.getAuthHeader()); return this.httpClient.get(request); } /** * Get track direct link */ async getTrackDirectLink(trackDownloadUrl) { const request = PreparedRequest_1.directLinkRequest(trackDownloadUrl); const xml = await this.httpClient.get(request); const parsedXml = await xml2js_1.parseStringPromise(xml); const host = parsedXml["download-info"].host[0]; const path = parsedXml["download-info"].path[0]; const ts = parsedXml["download-info"].ts[0]; const s = parsedXml["download-info"].s[0]; const sign = crypto_1.default .createHash("md5") .update("XGRlBW9FXlekgbPrRHuSiA" + path.slice(1) + s) .digest("hex"); return `https://${host}/get-mp3/${sign}/${ts}${path}`; } /** * GET: /albums/[album_id] * Get an album */ getAlbum(albumId, withTracks = false) { const request = PreparedRequest_1.apiRequest() .setPath(`/albums/${albumId}${withTracks ? "/with-tracks" : ""}`) .addHeaders(this.getAuthHeader()); return this.httpClient.get(request); } getAlbumWithTracks(albumId) { return this.getAlbum(albumId, true); } /** * GET: /albums * Get an albums */ getAlbums(albumIds) { const request = PreparedRequest_1.apiRequest() .setPath(`/albums`) .setBodyData({ albumIds: albumIds.join() }) .addHeaders(this.getAuthHeader()); return this.httpClient.post(request); } /** * GET: /artists/[artist_id] * Get an artist */ getArtist(artistId) { const request = PreparedRequest_1.apiRequest() .setPath(`/artists/${artistId}`) .addHeaders(this.getAuthHeader()); return this.httpClient.get(request); } /** * GET: /artists * Get an artists */ getArtists(artistIds) { const request = PreparedRequest_1.apiRequest() .setPath(`/artists`) .setBodyData({ artistIds: artistIds.join() }) .addHeaders(this.getAuthHeader()); return this.httpClient.post(request); } /** * GET: /artists/[artist_id]/tracks * Get tracks by artist id */ getArtistTracks(artistId, options = {}) { const page = String(!options.page ? 0 : options.page); const request = PreparedRequest_1.apiRequest() .setPath(`/artists/${artistId}/tracks`) .addHeaders(this.getAuthHeader()) .setQuery({ page, }); if (options.pageSize !== void 0) { request.addQuery({ pageSize: String(options.pageSize) }); } return this.httpClient.get(request); } getLikedTracks(user = null) { const uid = [null, 0, ""].includes(user) ? this.user.uid : user; const request = PreparedRequest_1.apiRequest() .setPath(`/users/${uid}/likes/tracks`) .addHeaders(this.getAuthHeader()); return this.httpClient.get(request); } } exports.default = YMApi;