Merge branch 'development' into 'main'

reverted to spotify-web-api-node (local) and applied Buffer fix.

See merge request deeplydrumming/DeemixFix!7
This commit is contained in:
DeeplyDrumming
2024-07-31 08:18:23 +00:00
32 changed files with 893 additions and 391 deletions

View File

@@ -10,7 +10,7 @@ const {
const { Convertable, Collection } = require('../types/DownloadObjects.js')
const { sep } = require('path')
const fs = require('fs')
const SpotifyWebApi = require('spotify-web-api-node-plus')
const SpotifyWebApi = require('spotify-web-api-node')
const got = require('got')
const { queue } = require('async')

View File

@@ -20,7 +20,7 @@
"got": "^11.8.2",
"html-entities": "^2.3.3",
"metaflac-js2": "^1.0.8",
"spotify-web-api-node-plus": "../spotify-web-api-node-plus",
"spotify-web-api-node": "../spotify-web-api-node",
"tough-cookie": "^4.0.0"
},
"devDependencies": {

View File

@@ -1 +1 @@
2024.7.26-r256.c452e2e47d
2024.7.31-r258.711fcec286

View File

@@ -1,175 +0,0 @@
'use strict'
const {
TimeoutError,
WebapiError,
WebapiRegularError,
WebapiAuthenticationError,
WebapiPlayerError
} = require('./response-error')
const HttpManager = {}
/* Create superagent options from the base request */
const _getParametersFromRequest = function (request) {
const options = {}
if (request.getQueryParameters()) {
options.query = new URLSearchParams(
request.getQueryParameters()
).toString()
}
if (
request.getHeaders() &&
request.getHeaders()['Content-Type'] === 'application/json'
) {
options.data = JSON.stringify(request.getBodyParameters())
} else if (request.getBodyParameters()) {
options.data = request.getBodyParameters()
}
if (request.getHeaders()) {
options.headers = request.getHeaders()
}
if (request.getTimeout()) {
options.timeout = request.getTimeout()
}
return options
}
/**
* @param {Response} response
*/
const _toError = async function (response) {
const body = await response.json()
if (
typeof body === 'object' &&
typeof body.error === 'object' &&
typeof body.error.reason === 'string'
) {
return new WebapiPlayerError(body, response.headers, response.status)
}
if (typeof body === 'object' && typeof body.error === 'object') {
return new WebapiRegularError(body, response.headers, response.status)
}
if (typeof body === 'object' && typeof body.error === 'string') {
return new WebapiAuthenticationError(
body,
response.headers,
response.status
)
}
/* Other type of error, or unhandled Web API error format */
return new WebapiError(body, response.headers, response.status, body)
}
/* Make the request to the Web API */
HttpManager._makeRequest = function (method, options, uri, callback) {
const headers = new Headers(options.headers || {})
let serializationMethod = JSON.stringify
if (headers.get('Content-Type') === 'application/x-www-form-urlencoded') {
serializationMethod = d => new URLSearchParams(d)
}
const controller = new AbortController()
let timeoutId
if (options.timeout) {
setTimeout(() => {
controller.abort()
}, options.timeout)
}
let body = options.data
if (body && typeof body !== 'string') {
body = serializationMethod(body)
}
fetch(uri + (options.query ? '?' + options.query : ''), {
method,
headers,
body,
signal: controller.signal
})
.then(async resp => {
clearTimeout(timeoutId)
if (!resp.ok) {
return callback(await _toError(resp))
}
return callback(null, {
body: await resp.json().catch(() => null),
headers: resp.headers,
statusCode: resp.status
})
})
.catch(err => {
if (controller.signal.aborted) {
return callback(
new TimeoutError(`request took longer than ${options.timeout}ms`)
)
}
return callback(err)
})
}
/**
* Make a HTTP GET request.
* @param {BaseRequest} The request.
* @param {Function} The callback function.
*/
HttpManager.get = function (request, callback) {
const options = _getParametersFromRequest(request)
const method = 'GET'
HttpManager._makeRequest(method, options, request.getURI(), callback)
}
/**
* Make a HTTP POST request.
* @param {BaseRequest} The request.
* @param {Function} The callback function.
*/
HttpManager.post = function (request, callback) {
const options = _getParametersFromRequest(request)
const method = 'POST'
HttpManager._makeRequest(method, options, request.getURI(), callback)
}
/**
* Make a HTTP DELETE request.
* @param {BaseRequest} The request.
* @param {Function} The callback function.
*/
HttpManager.del = function (request, callback) {
const options = _getParametersFromRequest(request)
const method = 'DELETE'
HttpManager._makeRequest(method, options, request.getURI(), callback)
}
/**
* Make a HTTP PUT request.
* @param {BaseRequest} The request.
* @param {Function} The callback function.
*/
HttpManager.put = function (request, callback) {
const options = _getParametersFromRequest(request)
const method = 'PUT'
HttpManager._makeRequest(method, options, request.getURI(), callback)
}
module.exports = HttpManager

View File

@@ -0,0 +1,5 @@
language: node_js
node_js:
- 14
after_success:
- npm run travis

View File

@@ -1,62 +1,47 @@
## Change log
#### 7.0.0 (Jan 2024)
* Added [getQueue](https://developer.spotify.com/documentation/web-api/reference/get-queue) endpoint
* Added [getPlaylistCoverImage](https://developer.spotify.com/documentation/web-api/reference/get-playlist-cover) endpoint
* Added [addToQueue](https://developer.spotify.com/documentation/web-api/reference/add-to-queue) endpoint
* Added [getAvailableMarkets](https://developer.spotify.com/documentation/web-api/reference/get-available-markets) endpoint
* Updated Readme with new endpoints
#### 6.0.0
* **BREAKING CHANGES**
* This release replaces superagent with `fetch` when making HTTP calls. This should result in no changes for consumers
of this library except that the `headers` returned in the responses are now `Headers` objects instead of plain objects.
Headers are case insensitive and must be treated as such.
#### 5.0.2 (Jan 2021)
* Fix: Make `transferMyPlayback` not require the `options` object, since it should be optional. Thanks for the heads-up [@Simber1](https://github.com/Simber1)!
* Fix: Make `transferMyPlayback` not require the `options` object, since it should be optional. Thanks for the heads-up [@Simber1](https://github.com/Simber1)!
#### 5.0.1 (Jan 2021)
* Fix error handling in the HTTP client. Thanks [@yamadapc](https://github.com/yamadapc)!
* This package can currently not be built on **Node 15 on Linux**, due to a dependency not being available yet. Issue can be followed on the [node-canvas](https://github.com/Automattic/node-canvas/issues/1688) issue tracker. In the mean time, Travis CI will run on earlier versions of Node.
* This package can currently not be built on **Node 15 on Linux**, due to a dependency not being available yet. Issue can be followed on the [node-canvas](https://github.com/Automattic/node-canvas/issues/1688) issue tracker. In the mean time, Travis CI will run on earlier versions of Node.
#### 5.0.0 (Oct 2020)
* **BREAKING CHANGES**.
* **BREAKING CHANGES**.
* Arguments for some API methods have changed, causing incorrect behaviour using argument order from version 4.x. See the `README.md` for examples of how the methods can be used.
* Create Playlist (`createPlaylist`) method no longer accepts a `userId` string as its first argument.
* Transfer A User's Playback (`transferMyPlayback`) method takes a `deviceIds` array as its first argument.
* Create Playlist (`createPlaylist`) method no longer accepts a `userId` string as its first argument.
* Transfer A User's Playback (`transferMyPlayback`) method takes a `deviceIds` array as its first argument.
* Skip to Previous (`skipToPrevious`) method takes an `options` object as its first argument.
* Skip to Next (`skipToNext`) method takes an `options` object as its first argument.
* Set Repeat Mode on the Current User's Playback (`setRepeat`) method takes a `state` string as its first argument.
* Set Shuffle Mode on the Current User's Playback (`setShuffle`) method takes a `state` string as its first argument.
Cheers [@marinacrachi](https://github.com/marinacrachi) for the createPlaylist update.
* Removed legacy support for not passing an `options` object while providing a callback method. This was only supported on a few of the older endpoints, and could lead to tricky bugs. The affected endpoints are `getTrack`, `getTracks`, `getAlbum`, `getAlbums`, and `createPlaylist`. Again, check the `README.md` for examples on how these methods can be used if needed.
* Removed `options` argument for retrieving an access token using the Client Credentials flow, `clientCredentialsGrant`.
* API errors come in five different flavours.
* WebapiRegularError - For errors returned by most API endpoints.
* WebapiPlayerError - For errors returned by the Player API. These contain a bit more information.
* WebapiPlayerError - For errors returned by the Player API. These contain a bit more information.
* WebapiAuthenticationError - For errors related to authentication.
* WebapiError - For errors that come from the Web API that didn't fit into one of the above.
* TimeoutError - For network timeout errors.
More importantly, errors now contain the response body, headers, and status code. One side-effect of this is that rate limited requests can be handled by checking the `Retry-After` header. Thanks for the PRs [@kauffecup](https://github.com/kauffecup), [@lantelyes](https://github.com/lantelyes), [@dkliemsch](https://github.com/dkliemsch), and [@erezny](https://github.com/erezny).
Much appreciated [@konstantinjdobler](https://github.com/konstantinjdobler) for updates to the Player API errors.
* Added support for [Implicit Grant flow](https://developer.spotify.com/documentation/general/guides/authorization-guide/#implicit-grant-flow) - Thanks [@gaganza](https://github.com/gaganza), [@reblws](https://github.com/reblws) and [@noahp78](https://github.com/noahp78)!
* Starts or Resumes the Current User's Playback (`play`) method now supports the `position_ms` option. Thanks [@alqubo](https://github.com/alqubo), [@koflin](https://github.com/koflin), [@DoctorFishy](https://github.com/DoctorFishy). Thanks [@carmilso](https://github.com/carmilso) for general improvements to the Player API methods.
* Binding for [Add an Item to the User's Playback Queue](https://developer.spotify.com/documentation/web-api/reference/player/add-to-queue/) endpoint added. Thanks [@thattomperson](https://github.com/thattomperson) and [@AriciducaZagaria](https://github.com/AriciducaZagaria)!
* Binding for all [Shows and Episodes endpoints](https://developer.spotify.com/console/shows/). Thanks a _lot_ [@andyruwruw](https://github.com/andyruwruw)!
* Binding for all [Shows and Episodes endpoints](https://developer.spotify.com/console/shows/). Thanks a _lot_ [@andyruwruw](https://github.com/andyruwruw)!
* Documentation updates to keep up to date with ES6, thanks [@dandv](https://github.com/dandv)! Other documentation improvements by [@terensu-desu](https://github.com/terensu-desu), and examples by [@dersimn](https://github.com/dersimn). Thanks!
* Bumped dependencies to resolve critical security issues.
* Finally, hat off to [@dersimn](https://github.com/dersimn). Thanks for collecting all of the lingering PRs and merging them into a working and up-to-date fork. You really stepped up.
* Bumped dependencies to resolve critical security issues.
* Finally, hat off to [@dersimn](https://github.com/dersimn). Thanks for collecting all of the lingering PRs and merging them into a working and up-to-date fork. You really stepped up.
Likely more changes coming before release to npm, which will happen shortly.
Likely more changes coming before release to npm, which will happen shortly.
#### 4.0.0 (14 Sep 2018)
@@ -91,11 +76,11 @@ Likely more changes coming before release to npm, which will happen shortly.
#### 2.4.0 (2 May 2017)
* Change `addTracksToPlaylist` to pass the data in the body, preventing an issue with a long URL when passing many tracks. Thanks [@dolcalmi](https://github.com/dolcalmi) for [the PR](https://github.com/thelinmichael/spotify-web-api-node/pull/117)
* Add support for fetching [recently played tracks](https://developer.spotify.com/documentation/web-api/reference/get-recently-played/). Thanks [@jeremyboles](https://github.com/jeremyboles) for [the PR](https://github.com/thelinmichael/spotify-web-api-node/pull/111).
* Add support for fetching [recently played tracks](https://developer.spotify.com/web-api/console/get-recently-played/). Thanks [@jeremyboles](https://github.com/jeremyboles) for [the PR](https://github.com/thelinmichael/spotify-web-api-node/pull/111).
#### 2.3.6 (15 October 2016)
* Add language bindings for the **[Get Audio Analysis for a Track](https://developer.spotify.com/documentation/web-api/reference/get-audio-analysis/)** endpoint.
* Add language bindings for the **[Get Audio Analysis for a Track](https://developer.spotify.com/web-api/get-audio-analysis/)** endpoint.
#### 2.3.5 (20 July 2016)
@@ -111,7 +96,7 @@ Likely more changes coming before release to npm, which will happen shortly.
#### 2.3.2 (10 July 2016)
* Add language bindings for **[Get a List of Current User's Playlists](https://developer.spotify.com/documentation/web-api/reference/get-a-list-of-current-users-playlists/)**. Thanks [@JMPerez](https://github.com/JMPerez) and [@vinialbano](https://github.com/vinialbano).
* Add language bindings for **[Get a List of Current User's Playlists](https://developer.spotify.com/web-api/get-a-list-of-current-users-playlists/)**. Thanks [@JMPerez](https://github.com/JMPerez) and [@vinialbano](https://github.com/vinialbano).
#### 2.3.1 (3 July 2016)
@@ -119,12 +104,12 @@ Likely more changes coming before release to npm, which will happen shortly.
#### 2.3.0 (2 April 2016)
* Add language bindings for **[Get Recommendations Based on Seeds](https://developer.spotify.com/documentation/web-api/reference/get-recommendations/)**, **[Get a User's Top Artists and Tracks](https://developer.spotify.com/documentation/web-api/reference/get-users-top-artists-and-tracks/)**, **[Get Audio Features for a Track](https://developer.spotify.com/documentation/web-api/reference/get-audio-features/)**, and **[Get Audio Features for Several Tracks](https://developer.spotify.com/documentation/web-api/reference/get-several-audio-features/)**. Read more about the endpoints in the links above or in this [blog post](https://developer.spotify.com/news-stories/2016/03/29/api-improvements-update/).
* Add language bindings for **[Get Recommendations Based on Seeds](https://developer.spotify.com/web-api/get-recommendations/)**, **[Get a User's Top Artists and Tracks](https://developer.spotify.com/web-api/get-users-top-artists-and-tracks/)**, **[Get Audio Features for a Track](https://developer.spotify.com/web-api/get-audio-features/)**, and **[Get Audio Features for Several Tracks](https://developer.spotify.com/web-api/get-several-audio-features/)**. Read more about the endpoints in the links above or in this [blog post](https://developer.spotify.com/news-stories/2016/03/29/api-improvements-update/).
* Add generic search method enabling searches for several types at once, e.g. search for both tracks and albums in a single request, instead of one request for track results and one request for album results.
#### 2.2.0 (23 November 2015)
* Add language bindings for **[Get User's Saved Albums](https://developer.spotify.com/documentation/web-api/reference/get-users-saved-albums/)** and other endpoints related to the user's saved albums.
* Add language bindings for **[Get User's Saved Albums](https://developer.spotify.com/web-api/get-users-saved-albums/)** and other endpoints related to the user's saved albums.
#### 2.1.1 (23 November 2015)
@@ -132,7 +117,7 @@ Likely more changes coming before release to npm, which will happen shortly.
#### 2.1.0 (16 July 2015)
* Add language binding for **[Get Followed Artists](https://developer.spotify.com/documentation/web-api/reference/get-followed-artists/)**
* Add language binding for **[Get Followed Artists](https://developer.spotify.com/web-api/get-followed-artists/)**
#### 2.0.2 (11 May 2015)
@@ -149,28 +134,28 @@ Likely more changes coming before release to npm, which will happen shortly.
#### 1.3.13 (26 Feb 2015)
* Add language binding for **[Reorder tracks in a Playlist](https://developer.spotify.com/documentation/web-api/reference/reorder-playlists-tracks/)**
* Add language binding for **[Reorder tracks in a Playlist](https://developer.spotify.com/web-api/reorder-playlists-tracks/)**
#### 1.3.12 (22 Feb 2015)
* Add language binding for **[Remove tracks in a Playlist by Position](https://developer.spotify.com/documentation/web-api/reference/remove-tracks-playlist/)**
* Add language binding for **[Remove tracks in a Playlist by Position](https://developer.spotify.com/web-api/remove-tracks-playlist/)**
#### 1.3.11
* Add **[Search for Playlists](https://developer.spotify.com/documentation/web-api/reference/search-item/)** endpoint.
* Add **[Search for Playlists](https://developer.spotify.com/web-api/search-item/)** endpoint.
#### 1.3.10
* Add market parameter to endpoints supporting **[Track Relinking](https://developer.spotify.com/documentation/web-api/reference/track-relinking-guide/)**.
* Add market parameter to endpoints supporting **[Track Relinking](https://developer.spotify.com/web-api/track-relinking-guide/)**.
* Improve SEO by adding keywords to the package.json file. ;-)
#### 1.3.8
* Add **[Get a List of Categories](https://developer.spotify.com/documentation/web-api/reference/get-list-categories/)**, **[Get a Category](https://developer.spotify.com/documentation/web-api/reference/get-category/)**, and **[Get A Category's Playlists](https://developer.spotify.com/documentation/web-api/reference/get-categorys-playlists/)** endpoints.
* Add **[Get a List of Categories](https://developer.spotify.com/web-api/get-list-categories/)**, **[Get a Category](https://developer.spotify.com/web-api/get-category/)**, and **[Get A Category's Playlists](https://developer.spotify.com/web-api/get-categorys-playlists/)** endpoints.
#### 1.3.7
* Add **[Check if Users are Following Playlist](https://developer.spotify.com/documentation/web-api/reference/check-user-following-playlist/)** endpoint.
* Add **[Check if Users are Following Playlist](https://developer.spotify.com/web-api/check-user-following-playlist/)** endpoint.
#### 1.3.5
@@ -178,7 +163,7 @@ Likely more changes coming before release to npm, which will happen shortly.
#### 1.3.4
* Add **[Follow Playlist](https://developer.spotify.com/documentation/web-api/reference/follow-playlist/)** and **[Unfollow Playlist](https://developer.spotify.com/documentation/web-api/reference/unfollow-playlist/)** endpoints.
* Add **[Follow Playlist](https://developer.spotify.com/web-api/follow-playlist/)** and **[Unfollow Playlist](https://developer.spotify.com/web-api/unfollow-playlist/)** endpoints.
#### 1.3.3
@@ -194,11 +179,11 @@ Likely more changes coming before release to npm, which will happen shortly.
#### 1.2.1
* Add **[Follow endpoints](https://developer.spotify.com/documentation/web-api/reference/web-api-follow-endpoints/)**. Great work [JMPerez](https://github.com/JMPerez).
* Add **[Follow endpoints](https://developer.spotify.com/web-api/web-api-follow-endpoints/)**. Great work [JMPerez](https://github.com/JMPerez).
#### 1.1.0
* Add **[Browse endpoints](https://developer.spotify.com/documentation/web-api/reference/browse-endpoints/)**. Thanks [fsahin](https://github.com/fsahin).
* Add **[Browse endpoints](https://developer.spotify.com/web-api/browse-endpoints/)**. Thanks [fsahin](https://github.com/fsahin).
#### 1.0.2
@@ -210,19 +195,19 @@ Likely more changes coming before release to npm, which will happen shortly.
#### 1.0.0
* Add **[Replace tracks in a Playlist](https://developer.spotify.com/documentation/web-api/reference/replace-playlists-tracks/)** endpoint
* Add **[Remove tracks in a Playlist](https://developer.spotify.com/documentation/web-api/reference/remove-tracks-playlist/)** endpoint
* Add **[Replace tracks in a Playlist](https://developer.spotify.com/web-api/replace-playlists-tracks/)** endpoint
* Add **[Remove tracks in a Playlist](https://developer.spotify.com/web-api/remove-tracks-playlist/)** endpoint
* Return errors as Error objects instead of unparsed JSON. Thanks [niftylettuce](https://github.com/niftylettuce).
#### 0.0.11
* Add **[Change Playlist details](https://developer.spotify.com/documentation/web-api/reference/change-playlist-details/)** endpoint (change published status and name). Gracias [JMPerez](https://github.com/JMPerez).
* Add **[Change Playlist details](https://developer.spotify.com/web-api/change-playlist-details/)** endpoint (change published status and name). Gracias [JMPerez](https://github.com/JMPerez).
#### 0.0.10
* Add Your Music Endpoints (**[Add tracks](https://developer.spotify.com/documentation/web-api/reference/save-tracks-user/)**, **[Remove tracks](https://developer.spotify.com/documentation/web-api/reference/remove-tracks-user/)**, **[Contains tracks](https://developer.spotify.com/documentation/web-api/reference/check-users-saved-tracks/)**, **[Get tracks](https://developer.spotify.com/documentation/web-api/reference/get-users-saved-tracks/)**).
* Add Your Music Endpoints (**[Add tracks](https://developer.spotify.com/web-api/save-tracks-user/)**, **[Remove tracks](https://developer.spotify.com/web-api/remove-tracks-user/)**, **[Contains tracks](https://developer.spotify.com/web-api/check-users-saved-tracks/)**, **[Get tracks](https://developer.spotify.com/web-api/get-users-saved-tracks/)**).
* Documentation updates (change scope name of playlist-modify to playlist-modify-public, and a fix to a parameter type). Thanks [JMPerez](https://github.com/JMPerez) and [matiassingers](https://github.com/matiassingers).
#### 0.0.9
* Add **[Related artists](https://developer.spotify.com/documentation/web-api/reference/get-related-artists/)** endpoint
* Add **[Related artists](https://developer.spotify.com/web-api/get-related-artists/)** endpoint

View File

@@ -1,9 +1,16 @@
# Spotify Web API Node
[![Tests](https://travis-ci.org/thelinmichael/spotify-web-api-node.svg?branch=master)](https://travis-ci.org/thelinmichael/spotify-web-api-node)
[![Coverage Status](https://coveralls.io/repos/thelinmichael/spotify-web-api-node/badge.svg)](https://coveralls.io/r/thelinmichael/spotify-web-api-node)
[![npm bundle size (minified + gzip)](https://img.shields.io/bundlephobia/minzip/spotify-web-api-node.svg)](https://bundlephobia.com/result?p=spotify-web-api-node)
This is a universal wrapper/client for the [Spotify Web API](https://developer.spotify.com/web-api/) that runs on Node.JS and the browser, using [browserify](http://browserify.org/)/[webpack](https://webpack.github.io/)/[rollup](http://rollupjs.org/). A list of selected wrappers for different languages and environments is available at the Developer site's [Libraries page](https://developer.spotify.com/documentation/web-api/libraries/).
This library is a fork of [spotify-web-api-node](https://github.com/thelinmichael/spotify-web-api-node) with the goal of
the library becoming dependency free and actively maintained.
Project owners are [thelinmichael](https://github.com/thelinmichael) and [JMPerez](https://github.com/JMPerez), with help from [a lot of awesome contributors](https://github.com/thelinmichael/spotify-web-api-node/network/members).
## Version 5
> :warning: Since the last release (4.0.0, released over year ago) a lot of new functionality has been added by a lot of different contributors. **Thank you.** In order to implement some of the feature requests, some **breaking changes** had to be made. A list of them, along with a list of the new functionality, can be found in the [CHANGELOG](https://github.com/thelinmichael/spotify-web-api-node/blob/master/CHANGELOG.md).
## Table of contents
@@ -45,7 +52,7 @@ The library includes helper functions to do the following:
#### Your Music library
* Add, remove, and get tracks and albums that are in the signed in user's Your Music library
* Check if a track or album is in the signed in user's Your Music library
* Check if a track or album is in the signed in user's Your Music library
#### Personalization
@@ -66,14 +73,14 @@ The library includes helper functions to do the following:
* Get a User's Available Devices
* Get Information About The User's Current Playback State
* Get Current User's Recently Played Tracks
* Get the User's Currently Playing Track
* Get the User's Currently Playing Track
* Pause a User's Playback
* Seek To Position In Currently Playing Track
* Set Repeat Mode On Users Playback
* Set Volume For User's Playback
* Skip Users Playback To Next Track
* Skip Users Playback To Previous Track
* Start/Resume a User's Playback
* Skip Users Playback To Previous Track
* Start/Resume a User's Playback
* Toggle Shuffle For Users Playback
* Transfer a User's Playback
@@ -120,12 +127,7 @@ This project depends on [superagent](https://github.com/visionmedia/superagent)
## Installation
```bash
$ npm install spotify-web-api-node --save
# If you are using this library in a Node version that does not have the fetch function (Node < v17), you will need
# to install and configure a fetch polyfill
$ npm install node-fetch --save
```
$ npm install spotify-web-api-node --save
## Usage
@@ -199,7 +201,7 @@ spotifyApi
### Responses and errors
This exposes the response headers, status code and body.
This exposes the response headers, status code and body.
```json
{
@@ -213,7 +215,7 @@ This exposes the response headers, status code and body.
}
```
Errors have same fields, as well as a human readable `message`. This is especially useful since
Errors have same fields, as well as a human readable `message`. This is especially useful since
Spotify's Web API returns different types of error objects depending on the endpoint being called.
#### Example of a response
@@ -242,7 +244,7 @@ Retrieving a track's metadata in `spotify-web-api-node` version 1.4.0 and later:
### More examples
Below are examples for all helper functions. Longer examples of some requests can be found in the [examples folder](examples/).
Below are examples for all helper functions. Longer examples of some requests can be found in the [examples folder](examples/).
```javascript
var SpotifyWebApi = require('spotify-web-api-node');
@@ -253,22 +255,6 @@ var spotifyApi = new SpotifyWebApi();
* Get metadata of tracks, albums, artists, shows, and episodes
*/
// Get track
spotifyApi.getTrack("28s5iRbX62GZG2ReZTLKDa")
.then(function (data) {
console.log("Track information", data.body);
}, function (err) {
console.error(err);
});
// Get multiple tracks
spotifyApi.getTracks(["6Rb4xFCy3Hh5MJ88ehLXdc", "5oOoQn1bhg6nbuvd54URPF"])
.then(function (data) {
console.log("Tracks information", track);
},function (err) {
console.error(err);
});
// Get album
spotifyApi.getAlbum('5U4W9E5WsYb2jUQWePT8Xm')
.then(function(data) {
@@ -834,7 +820,7 @@ spotifyApi.getMyRecentlyPlayedTracks({
console.log('Something went wrong!', err);
});
// Get the User's Currently Playing Track
// Get the User's Currently Playing Track
spotifyApi.getMyCurrentPlayingTrack()
.then(function(data) {
console.log('Now playing: ' + data.body.item.name);
@@ -887,7 +873,7 @@ spotifyApi.skipToNext()
console.log('Something went wrong!', err);
});
// Skip Users Playback To Previous Track
// Skip Users Playback To Previous Track
spotifyApi.skipToPrevious()
.then(function() {
console.log('Skip to previous');
@@ -896,7 +882,7 @@ spotifyApi.skipToPrevious()
console.log('Something went wrong!', err);
});
// Start/Resume a User's Playback
// Start/Resume a User's Playback
spotifyApi.play()
.then(function() {
console.log('Playback started');
@@ -1252,7 +1238,7 @@ api
## Development
See something you think can be improved? [Open an issue](https://github.com/eligundry/spotify-web-api-fetch/issues/new) or clone the project and send a pull request with your changes.
See something you think can be improved? [Open an issue](https://github.com/thelinmichael/spotify-web-api-node/issues/new) or clone the project and send a pull request with your changes.
### Running tests

View File

@@ -0,0 +1,86 @@
const SpotifyWebApi = require('../');
/**
* This example refreshes an access token. Refreshing access tokens is only possible access tokens received using the
* Authorization Code flow, documented here: https://developer.spotify.com/spotify-web-api/authorization-guide/#authorization_code_flow
*/
/* Retrieve an authorization code as documented here:
* https://developer.spotify.com/documentation/general/guides/authorization-guide/#authorization-code-flow
* or in the Authorization section of the README.
*
* Codes are given for a set of scopes. For this example, the scopes are user-read-private and user-read-email.
* Scopes are documented here:
* https://developer.spotify.com/documentation/general/guides/scopes/
*/
const authorizationCode =
'<insert authorization code with user-read-private and user-read-email scopes>';
/**
* Get the credentials from Spotify's Dashboard page.
* https://developer.spotify.com/dashboard/applications
*/
const spotifyApi = new SpotifyWebApi({
clientId: '<insert client id>',
clientSecret: '<insert client secret>',
redirectUri: '<insert redirect URI>'
});
// When our access token will expire
let tokenExpirationEpoch;
// First retrieve an access token
spotifyApi.authorizationCodeGrant(authorizationCode).then(
function(data) {
// Set the access token and refresh token
spotifyApi.setAccessToken(data.body['access_token']);
spotifyApi.setRefreshToken(data.body['refresh_token']);
// Save the amount of seconds until the access token expired
tokenExpirationEpoch =
new Date().getTime() / 1000 + data.body['expires_in'];
console.log(
'Retrieved token. It expires in ' +
Math.floor(tokenExpirationEpoch - new Date().getTime() / 1000) +
' seconds!'
);
},
function(err) {
console.log(
'Something went wrong when retrieving the access token!',
err.message
);
}
);
// Continually print out the time left until the token expires..
let numberOfTimesUpdated = 0;
setInterval(function() {
console.log(
'Time left: ' +
Math.floor(tokenExpirationEpoch - new Date().getTime() / 1000) +
' seconds left!'
);
// OK, we need to refresh the token. Stop printing and refresh.
if (++numberOfTimesUpdated > 5) {
clearInterval(this);
// Refresh token and print the new time to expiration.
spotifyApi.refreshAccessToken().then(
function(data) {
tokenExpirationEpoch =
new Date().getTime() / 1000 + data.body['expires_in'];
console.log(
'Refreshed token. It now expires in ' +
Math.floor(tokenExpirationEpoch - new Date().getTime() / 1000) +
' seconds!'
);
},
function(err) {
console.log('Could not refresh the token!', err.message);
}
);
}
}, 1000);

View File

@@ -0,0 +1,32 @@
const SpotifyWebApi = require('../');
/**
* This example retrieves an access token using the Client Credentials Flow, documented at:
* https://developer.spotify.com/documentation/general/guides/authorization-guide/#client-credentials-flow
*/
/**
* Get the credentials from Spotify's Dashboard page.
* https://developer.spotify.com/dashboard/applications
*/
const spotifyApi = new SpotifyWebApi({
clientId: '<insert client id>',
clientSecret: '<insert client secret>'
});
// Retrieve an access token
spotifyApi.clientCredentialsGrant().then(
function(data) {
console.log('The access token expires in ' + data.body['expires_in']);
console.log('The access token is ' + data.body['access_token']);
// Save the access token so that it's used in future calls
spotifyApi.setAccessToken(data.body['access_token']);
},
function(err) {
console.log(
'Something went wrong when retrieving an access token',
err.message
);
}
);

View File

@@ -0,0 +1,84 @@
const SpotifyWebApi = require('../');
/**
* This example demonstrates adding tracks, removing tracks, and replacing tracks in a playlist. At the time of writing this
* documentation, this is the available playlist track modification feature in the Spotify Web API.
*
* Since authorization is required, this example retrieves an access token using the Authorization Code Grant flow,
* documented here: https://developer.spotify.com/documentation/general/guides/authorization-guide/#authorization-code-flow
*
* Codes are given for a set of scopes. For this example, the scopes are playlist-modify-public.
* Scopes are documented here:
* https://developer.spotify.com/documentation/general/guides/scopes/
*/
/* Obtain the `authorizationCode` below as described in the Authorization section of the README.
*/
const authorizationCode =
'<insert authorization code with playlist-modify-public scope>';
/**
* Get the credentials from Spotify's Dashboard page.
* https://developer.spotify.com/dashboard/applications
*/
const spotifyApi = new SpotifyWebApi({
clientId: '<insert client id>',
clientSecret: '<insert client secret>',
redirectUri: '<insert redirect URI>'
});
let playlistId;
// First retrieve an access token
spotifyApi
.authorizationCodeGrant(authorizationCode)
.then(function(data) {
// Save the access token so that it's used in future requests
spotifyApi.setAccessToken(data['access_token']);
// Create a playlist
return spotifyApi.createPlaylist(
'thelinmichael',
'My New Awesome Playlist'
);
})
.then(function(data) {
console.log('Ok. Playlist created!');
playlistId = data.body['id'];
// Add tracks to the playlist
return spotifyApi.addTracksToPlaylist(playlistId, [
'spotify:track:4iV5W9uYEdYUVa79Axb7Rh',
'spotify:track:6tcfwoGcDjxnSc6etAkDRR',
'spotify:track:4iV5W9uYEdYUVa79Axb7Rh'
]);
})
.then(function(data) {
console.log('Ok. Tracks added!');
// Woops! Made a duplicate. Remove one of the duplicates from the playlist
return spotifyApi.removeTracksFromPlaylist('thelinmichael', playlistId, [
{
uri: 'spotify:track:4iV5W9uYEdYUVa79Axb7Rh',
positions: [0]
}
]);
})
.then(function(data) {
console.log('Ok. Tracks removed!');
// Actually, lets just replace all tracks in the playlist with something completely different
return spotifyApi.replaceTracksInPlaylist('thelinmichael', playlistId, [
'spotify:track:5Wd2bfQ7wc6GgSa32OmQU3',
'spotify:track:4r8lRYnoOGdEi6YyI5OC1o',
'spotify:track:4TZZvblv2yzLIBk2JwJ6Un',
'spotify:track:2IA4WEsWAYpV9eKkwR2UYv',
'spotify:track:6hDH3YWFdcUNQjubYztIsG'
]);
})
.then(function(data) {
console.log('Ok. Tracks replaced!');
})
.catch(function(err) {
console.log('Something went wrong:', err.message);
});

View File

@@ -0,0 +1,49 @@
const SpotifyWebApi = require('../');
/**
* This example demonstrates adding tracks to a specified position in a playlist.
*
* Since authorization is required, this example retrieves an access token using the Authorization Code Grant flow,
* documented here: https://developer.spotify.com/documentation/general/guides/authorization-guide/#authorization-code-flow
*
* Codes are given for a set of scopes. For this example, the scopes are playlist-modify-public.
* Scopes are documented here:
* https://developer.spotify.com/documentation/general/guides/scopes/
*/
/* Obtain the `authorizationCode` below as described in the Authorization section of the README.
*/
const authorizationCode = '<insert authorization code>';
/**
* Get the credentials from Spotify's Dashboard page.
* https://developer.spotify.com/dashboard/applications
*/
const spotifyApi = new SpotifyWebApi({
clientId: '<insert client id>',
clientSecret: '<insert client secret>',
redirectUri: '<insert redirect URI>'
});
// First retrieve an access token
spotifyApi
.authorizationCodeGrant(authorizationCode)
.then(function(data) {
spotifyApi.setAccessToken(data.body['access_token']);
return spotifyApi.addTracksToPlaylist(
'5ieJqeLJjjI8iJWaxeBLuK',
[
'spotify:track:4iV5W9uYEdYUVa79Axb7Rh',
'spotify:track:1301WleyT98MSxVHPZCA6M'
],
{
position: 10
}
);
})
.then(function(data) {
console.log('Added tracks to the playlist!');
})
.catch(function(err) {
console.log('Something went wrong:', err.message);
});

View File

@@ -0,0 +1,26 @@
const { util } = require('prettier');
var SpotifyWebApi = require('../');
/**
* This example uses the Client Credentials authorization flow.
*/
/**
* Get the credentials from Spotify's Dashboard page.
* https://developer.spotify.com/dashboard/applications
*/
const spotifyApi = new SpotifyWebApi({
clientId: '<Client ID>',
clientSecret: '<Client Secret>'
});
// Retrieve an access token using your credentials
spotifyApi.clientCredentialsGrant().
then(function(result) {
console.log('It worked! Your access token is: ' + result.body.access_token);
}).catch(function(err) {
console.log('If this is printed, it probably means that you used invalid ' +
'clientId and clientSecret values. Please check!');
console.log('Hint: ');
console.log(err);
});

View File

@@ -0,0 +1,54 @@
const SpotifyWebApi = require('../');
/**
* This example retrieves information about the 'current' user. The current user is the user that has
* authorized the application to access its data.
*/
/* Retrieve a code as documented here:
* https://developer.spotify.com/documentation/general/guides/authorization-guide/#authorization-code-flow
*
* Codes are given for a set of scopes. For this example, the scopes are user-read-private and user-read-email.
* Scopes are documented here:
* https://developer.spotify.com/documentation/general/guides/scopes/
*/
const authorizationCode =
'<insert authorization code with user-read-private and user-read-email scopes>';
/* Get the credentials from Spotify's Dashboard page.
* https://developer.spotify.com/dashboard/applications
*/
const spotifyApi = new SpotifyWebApi({
clientId: '<insert client id>',
clientSecret: '<insert client secret>',
redirectUri: '<insert redirect URI>'
});
// First retrieve an access token
spotifyApi
.authorizationCodeGrant(authorizationCode)
.then(function(data) {
console.log('Retrieved access token', data.body['access_token']);
// Set the access token
spotifyApi.setAccessToken(data.body['access_token']);
// Use the access token to retrieve information about the user connected to it
return spotifyApi.getMe();
})
.then(function(data) {
// "Retrieved data for Faruk Sahin"
console.log('Retrieved data for ' + data.body['display_name']);
// "Email is farukemresahin@gmail.com"
console.log('Email is ' + data.body.email);
// "Image URL is http://media.giphy.com/media/Aab07O5PYOmQ/giphy.gif"
console.log('Image URL is ' + data.body.images[0].url);
// "This user has a premium account"
console.log('This user has a ' + data.body.product + ' account');
})
.catch(function(err) {
console.log('Something went wrong:', err.message);
});

View File

@@ -0,0 +1,49 @@
var SpotifyWebApi = require('../');
/*
* This example shows how to get artists related to another artists. The endpoint is documented here:
* https://developer.spotify.com/web-api/get-related-artists/
* Please note that authorization is now required and so this example retrieves an access token using the Authorization Code Flow,
* documented here: https://developer.spotify.com/documentation/general/guides/authorization-guide/#authorization-code-flow
*/
var authorizationCode =
'AQAgjS78s64u1axMCBCRA0cViW_ZDDU0pbgENJ_-WpZr3cEO7V5O-JELcEPU6pGLPp08SfO3dnHmu6XJikKqrU8LX9W6J11NyoaetrXtZFW-Y58UGeV69tuyybcNUS2u6eyup1EgzbTEx4LqrP_eCHsc9xHJ0JUzEhi7xcqzQG70roE4WKM_YrlDZO-e7GDRMqunS9RMoSwF_ov-gOMpvy9OMb7O58nZoc3LSEdEwoZPCLU4N4TTJ-IF6YsQRhQkEOJK';
/* Set the credentials given on Spotify's My Applications page.
* https://developer.spotify.com/my-applications
*/
var spotifyApi = new SpotifyWebApi({
clientId: '<insert client id>',
clientSecret: '<insert client secret>',
redirectUri: '<insert redirect URI>'
});
var artistId = '0qeei9KQnptjwb8MgkqEoy';
spotifyApi
.authorizationCodeGrant(authorizationCode)
.then(function(data) {
console.log('Retrieved access token', data.body['access_token']);
// Set the access token
spotifyApi.setAccessToken(data.body['access_token']);
// Use the access token to retrieve information about the user connected to it
return spotifyApi.getArtistRelatedArtists(artistId);
})
.then(function(data) {
if (data.body.artists.length) {
// Print the number of similar artists
console.log('I got ' + data.body.artists.length + ' similar artists!');
console.log('The most similar one is ' + data.body.artists[0].name);
} else {
console.log("I didn't find any similar artists.. Sorry.");
}
},
function(err) {
console.log('Something went wrong:', err.message);
}
);

View File

@@ -0,0 +1,56 @@
const SpotifyWebApi = require('../');
/**
* This example retrieves the top tracks for an artist.
* https://developer.spotify.com/documentation/web-api/reference/artists/get-artists-top-tracks/
*/
/**
* This endpoint doesn't require an access token, but it's beneficial to use one as it
* gives the application a higher rate limit.
*
* Since it's not necessary to get an access token connected to a specific user, this example
* uses the Client Credentials flow. This flow uses only the client ID and the client secret.
* https://developer.spotify.com/documentation/general/guides/authorization-guide/#client-credentials-flow
*/
const spotifyApi = new SpotifyWebApi({
clientId: '<insert client id>',
clientSecret: '<insert client secret>'
});
// Retrieve an access token
spotifyApi
.clientCredentialsGrant()
.then(function(data) {
// Set the access token on the API object so that it's used in all future requests
spotifyApi.setAccessToken(data.body['access_token']);
// Get the most popular tracks by David Bowie in Great Britain
return spotifyApi.getArtistTopTracks('0oSGxfWSnnOXhD2fKuz2Gy', 'GB');
})
.then(function(data) {
console.log('The most popular tracks for David Bowie is..');
console.log('Drum roll..');
console.log('...');
/*
* 1. Space Oddity - 2009 Digital Remaster (popularity is 51)
* 2. Heroes - 1999 Digital Remaster (popularity is 33)
* 3. Let's Dance - 1999 Digital Remaster (popularity is 20)
* 4. ...
*/
data.body.tracks.forEach(function(track, index) {
console.log(
index +
1 +
'. ' +
track.name +
' (popularity is ' +
track.popularity +
')'
);
});
})
.catch(function(err) {
console.log('Unfortunately, something has gone wrong.', err.message);
});

View File

@@ -0,0 +1,55 @@
const SpotifyWebApi = require('../');
/*
* This example shows how to search for a track. The endpoint is documented here:
* https://developer.spotify.com/documentation/web-api/reference/search/
* Since authorization is now required, this example retrieves an access token using the Authorization Code Grant flow,
* documented here: https://developer.spotify.com/documentation/general/guides/authorization-guide/#authorization-code-flow
*
* Obtain the `authorizationCode` below as described in the Authorization section of the README.
*/
const authorizationCode = '<insert authorization code>';
/**
* Get the credentials from Spotify's Dashboard page.
* https://developer.spotify.com/dashboard/applications
*/
const spotifyApi = new SpotifyWebApi({
clientId: '<insert client id>',
clientSecret: '<insert client secret>',
redirectUri: '<insert redirect URI>'
});
spotifyApi
.authorizationCodeGrant(authorizationCode)
.then(function(data) {
console.log('Retrieved access token', data.body['access_token']);
// Set the access token
spotifyApi.setAccessToken(data.body['access_token']);
// Use the access token to retrieve information about the user connected to it
return spotifyApi.searchTracks('Love');
})
.then(function(data) {
// Print some information about the results
console.log('I got ' + data.body.tracks.total + ' results!');
// Go through the first page of results
var firstPage = data.body.tracks.items;
console.log('The tracks in the first page are (popularity in parentheses):');
/*
* 0: All of Me (97)
* 1: My Love (91)
* 2: I Love This Life (78)
* ...
*/
firstPage.forEach(function(track, index) {
console.log(index + ': ' + track.name + ' (' + track.popularity + ')');
});
}).catch(function(err) {
console.log('Something went wrong:', err.message);
});

View File

@@ -0,0 +1,101 @@
/**
* This example is using the Authorization Code flow.
*
* In root directory run
*
* npm install express
*
* then run with the followinng command. If you don't have a client_id and client_secret yet,
* create an application on Create an application here: https://developer.spotify.com/my-applications to get them.
* Make sure you whitelist the correct redirectUri in line 26.
*
* node access-token-server.js "<Client ID>" "<Client Secret>"
*
* and visit <http://localhost:8888/login> in your Browser.
*/
const SpotifyWebApi = require('../../');
const express = require('../../node_modules/express');
const scopes = [
'ugc-image-upload',
'user-read-playback-state',
'user-modify-playback-state',
'user-read-currently-playing',
'streaming',
'app-remote-control',
'user-read-email',
'user-read-private',
'playlist-read-collaborative',
'playlist-modify-public',
'playlist-read-private',
'playlist-modify-private',
'user-library-modify',
'user-library-read',
'user-top-read',
'user-read-playback-position',
'user-read-recently-played',
'user-follow-read',
'user-follow-modify'
];
const spotifyApi = new SpotifyWebApi({
redirectUri: 'http://localhost:8888/callback',
clientId: process.argv.slice(2)[0],
clientSecret: process.argv.slice(2)[1]
});
const app = express();
app.get('/login', (req, res) => {
res.redirect(spotifyApi.createAuthorizeURL(scopes));
});
app.get('/callback', (req, res) => {
const error = req.query.error;
const code = req.query.code;
const state = req.query.state;
if (error) {
console.error('Callback Error:', error);
res.send(`Callback Error: ${error}`);
return;
}
spotifyApi
.authorizationCodeGrant(code)
.then(data => {
const access_token = data.body['access_token'];
const refresh_token = data.body['refresh_token'];
const expires_in = data.body['expires_in'];
spotifyApi.setAccessToken(access_token);
spotifyApi.setRefreshToken(refresh_token);
console.log('access_token:', access_token);
console.log('refresh_token:', refresh_token);
console.log(
`Sucessfully retreived access token. Expires in ${expires_in} s.`
);
res.send('Success! You can now close the window.');
setInterval(async () => {
const data = await spotifyApi.refreshAccessToken();
const access_token = data.body['access_token'];
console.log('The access token has been refreshed!');
console.log('access_token:', access_token);
spotifyApi.setAccessToken(access_token);
}, expires_in / 2 * 1000);
})
.catch(error => {
console.error('Error getting Tokens:', error);
res.send(`Error getting Tokens: ${error}`);
});
});
app.listen(8888, () =>
console.log(
'HTTP Server up. Now go to http://localhost:8888/login in your browser.'
)
);

View File

@@ -0,0 +1,11 @@
const SpotifyWebApi = require('../../../');
const spotifyApi = new SpotifyWebApi();
spotifyApi.setAccessToken(process.env.SPOTIFY_ACCESS_TOKEN);
(async () => {
const me = await spotifyApi.getMe();
console.log(me);
})().catch(e => {
console.error(e);
});

View File

@@ -0,0 +1,17 @@
Execute all commands from the root folder of this repository.
Start with
git clone <this repo url>
cd spotify-web-api-node
npm install
npm install express
node examples/tutorial/00-get-access-token.js "<Client ID>" "<Client Secret>"
and visit <http://localhost:8888/login> in your browser to get an `access_token`.
If you don't have a `client_id` and `client_secret` yet, create an application here: <https://developer.spotify.com/my-applications> to get them. Make sure you whitelist the correct redirectUri when creating your application, which is `http://localhost:8888/callback`.
After you got the `access_token`, call all other examples with this token in ENV variable `SPOTIFY_ACCESS_TOKEN`. The easiest way is to call:
export SPOTIFY_ACCESS_TOKEN="<Token content here>"
node examples/tutorial/01-basics/01-get-info-about-current-user.js

View File

@@ -1,14 +1,14 @@
{
"name": "spotify-web-api-node-plus",
"version": "7.0.1",
"homepage": "https://github.com/manhbi18112005/spotify-web-api-node-plus",
"name": "spotify-web-api-node",
"version": "5.0.3",
"homepage": "https://github.com/thelinmichael/spotify-web-api-node",
"description": "A Node.js wrapper for Spotify's Web API",
"main": "./src/server.js",
"author": "Michael Thelin",
"contributors": [
{
"name": "Le Ngo Duc Manh",
"url": "https://github.com/manhbi18112005"
"name": "José M. Perez",
"url": "https://github.com/JMPerez"
},
{
"name": "Deeplydrumming",
@@ -18,20 +18,16 @@
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/manhbi18112005/spotify-web-api-node-plus.git"
"url": "https://github.com/thelinmichael/spotify-web-api-node.git"
},
"scripts": {
"test": "jest",
"travis": "npm test -- --coverage && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js",
"precommit": "lint-staged",
"prettier": "prettier . --write"
"precommit": "lint-staged"
},
"jest": {
"verbose": true,
"testURL": "http://localhost/",
"setupFiles": [
"./setupJest.js"
]
"testURL": "http://localhost/"
},
"lint-staged": {
"*.{js,json,css,md}": [
@@ -39,15 +35,24 @@
"git add"
]
},
"dependencies": {
"superagent": "^6.1.0"
},
"devDependencies": {
"coveralls": "^3.1.0",
"husky": "^4.3.0",
"jest": "^26.6.3",
"jest-fetch-mock": "^3.0.3",
"jest-resolve": "^26.6.2",
"lint-staged": "^10.4.0",
"node-fetch": "^2.6.12",
"prettier": "^2.1.2"
"prettier": "^2.1.2",
"sinon": "^9.0.3",
"canvas": "^2.6.1",
"bufferutil": "^4.0.1",
"utf-8-validate": "^5.0.2",
"jest-resolve": "^26.6.2",
"minimist": "^1.2.5",
"set-value": ">=2.0.1",
"mixin-deep": ">=1.3.2",
"ini": ">=1.3.6"
},
"keywords": [
"spotify",
@@ -60,20 +65,5 @@
],
"browser": {
"./src/server.js": "./src/client.js"
},
"files": [
"./src",
"./LICENSE",
"./package.json",
"./README.md",
"./CHANGELOG.md"
],
"prettier": {
"trailingComma": "none",
"tabWidth": 2,
"semi": true,
"singleQuote": true,
"bracketSpacing": true,
"arrowParens": "avoid"
}
}

View File

@@ -12,7 +12,6 @@ const Request = function (builder) {
this.bodyParameters = builder.bodyParameters
this.headers = builder.headers
this.path = builder.path
this.timeout = builder.timeout
}
Request.prototype._getter = function (key) {
@@ -39,8 +38,6 @@ Request.prototype.getBodyParameters = Request.prototype._getter(
Request.prototype.getHeaders = Request.prototype._getter('headers')
Request.prototype.getTimeout = Request.prototype._getter('timeout')
Request.prototype.getURI = function () {
if (!this.scheme || !this.host || !this.port) {
throw new Error('Missing components necessary to construct URI')
@@ -139,8 +136,6 @@ Builder.prototype.withBodyParameters = Builder.prototype._assigner(
Builder.prototype.withHeaders = Builder.prototype._assigner('headers')
Builder.prototype.withTimeout = Builder.prototype._setter('timeout')
Builder.prototype.withAuth = function (accessToken) {
if (accessToken) {
this.withHeaders({ Authorization: 'Bearer ' + accessToken })

View File

@@ -0,0 +1,134 @@
'use strict'
const superagent = require('superagent')
const {
TimeoutError,
WebapiError,
WebapiRegularError,
WebapiAuthenticationError,
WebapiPlayerError
} = require('./response-error')
const HttpManager = {}
/* Create superagent options from the base request */
const _getParametersFromRequest = function (request) {
const options = {}
if (request.getQueryParameters()) {
options.query = request.getQueryParameters()
}
if (request.getHeaders() && request.getHeaders()['Content-Type'] === 'application/json') {
options.data = JSON.stringify(request.getBodyParameters())
} else if (request.getBodyParameters()) {
options.data = request.getBodyParameters()
}
if (request.getHeaders()) {
options.headers = request.getHeaders()
}
return options
}
const _toError = function (response) {
if (typeof response.body === 'object' && response.body.error && typeof response.body.error === 'object' && response.body.error.reason) {
return new WebapiPlayerError(response.body, response.headers, response.statusCode)
}
if (typeof response.body === 'object' && response.body.error && typeof response.body.error === 'object') {
return new WebapiRegularError(response.body, response.headers, response.statusCode)
}
if (typeof response.body === 'object' && response.body.error && typeof response.body.error === 'string') {
return new WebapiAuthenticationError(response.body, response.headers, response.statusCode)
}
/* Other type of error, or unhandled Web API error format */
return new WebapiError(response.body, response.headers, response.statusCode, response.body)
}
/* Make the request to the Web API */
HttpManager._makeRequest = function (method, options, uri, callback) {
const req = method.bind(superagent)(uri)
if (options.query) {
req.query(options.query)
}
if (options.headers) {
req.set(options.headers)
}
if (options.data) {
req.send(options.data)
}
req.end(function (err, response) {
if (err) {
if (err.timeout) {
return callback(new TimeoutError())
} else if (err.response) {
return callback(_toError(err.response))
} else {
return callback(err)
}
}
return callback(null, {
body: response.body,
headers: response.headers,
statusCode: response.statusCode
})
})
}
/**
* Make a HTTP GET request.
* @param {BaseRequest} The request.
* @param {Function} The callback function.
*/
HttpManager.get = function (request, callback) {
const options = _getParametersFromRequest(request)
const method = superagent.get
HttpManager._makeRequest(method, options, request.getURI(), callback)
}
/**
* Make a HTTP POST request.
* @param {BaseRequest} The request.
* @param {Function} The callback function.
*/
HttpManager.post = function (request, callback) {
const options = _getParametersFromRequest(request)
const method = superagent.post
HttpManager._makeRequest(method, options, request.getURI(), callback)
}
/**
* Make a HTTP DELETE request.
* @param {BaseRequest} The request.
* @param {Function} The callback function.
*/
HttpManager.del = function (request, callback) {
const options = _getParametersFromRequest(request)
const method = superagent.del
HttpManager._makeRequest(method, options, request.getURI(), callback)
}
/**
* Make a HTTP PUT request.
* @param {BaseRequest} The request.
* @param {Function} The callback function.
*/
HttpManager.put = function (request, callback) {
const options = _getParametersFromRequest(request)
const method = superagent.put
HttpManager._makeRequest(method, options, request.getURI(), callback)
}
module.exports = HttpManager

View File

@@ -7,8 +7,7 @@ class NamedError extends Error {
class TimeoutError extends NamedError {
constructor () {
const message =
"A timeout occurred while communicating with Spotify's Web API."
const message = 'A timeout occurred while communicating with Spotify\'s Web API.'
super(message)
}
}
@@ -29,11 +28,8 @@ class WebapiError extends NamedError {
*/
class WebapiRegularError extends WebapiError {
constructor (body, headers, statusCode) {
const message =
"An error occurred while communicating with Spotify's Web API.\n" +
'Details: ' +
body.error.message +
'.'
const message = 'An error occurred while communicating with Spotify\'s Web API.\n' +
'Details: ' + body.error.message + '.'
super(body, headers, statusCode, message)
}
@@ -45,11 +41,8 @@ class WebapiRegularError extends WebapiError {
*/
class WebapiAuthenticationError extends WebapiError {
constructor (body, headers, statusCode) {
const message =
"An authentication error occurred while communicating with Spotify's Web API.\n" +
'Details: ' +
body.error +
(body.error_description ? ' ' + body.error_description + '.' : '.')
const message = 'An authentication error occurred while communicating with Spotify\'s Web API.\n' +
'Details: ' + body.error + (body.error_description ? ' ' + body.error_description + '.' : '.')
super(body, headers, statusCode, message)
}
@@ -61,20 +54,11 @@ class WebapiAuthenticationError extends WebapiError {
*/
class WebapiPlayerError extends WebapiError {
constructor (body, headers, statusCode) {
const message =
"An error occurred while communicating with Spotify's Web API.\n" +
'Details: ' +
body.error.message +
(body.error.reason ? ' ' + body.error.reason + '.' : '.')
const message = 'An error occurred while communicating with Spotify\'s Web API.\n' +
'Details: ' + body.error.message + (body.error.reason ? ' ' + body.error.reason + '.' : '.')
super(body, headers, statusCode, message)
}
}
module.exports = {
WebapiError,
TimeoutError,
WebapiRegularError,
WebapiAuthenticationError,
WebapiPlayerError
}
module.exports = { WebapiError, TimeoutError, WebapiRegularError, WebapiAuthenticationError, WebapiPlayerError }

View File

@@ -4,6 +4,7 @@ const AuthenticationRequest = require('./authentication-request')
const HttpManager = require('./http-manager')
module.exports = {
/**
* Retrieve a URL where the user can give the application permissions.
* @param {string[]} scopes The scopes corresponding to the permissions the application needs.
@@ -12,12 +13,7 @@ module.exports = {
* @param {string} responseType An optional parameter that you can use to specify the code response based on the authentication type - can be set to 'code' or 'token'. Default 'code' to ensure backwards compatability.
* @returns {string} The URL where the user can give application permissions.
*/
createAuthorizeURL: function (
scopes,
state,
showDialog,
responseType = 'code'
) {
createAuthorizeURL: function (scopes, state, showDialog, responseType = 'code') {
return AuthenticationRequest.builder()
.withPath('/authorize')
.withQueryParameters({
@@ -46,7 +42,11 @@ module.exports = {
grant_type: 'client_credentials'
})
.withHeaders({
Authorization: 'Basic ' + Buffer.from(this.getClientId() + ':' + this.getClientSecret()).toString('base64'),
Authorization:
'Basic ' +
Buffer.from(
this.getClientId() + ':' + this.getClientSecret()
).toString('base64'),
'Content-Type': 'application/x-www-form-urlencoded'
})
.build()
@@ -94,7 +94,10 @@ module.exports = {
})
.withHeaders({
Authorization:
'Basic ' + Buffer.from(this.getClientId() + ':' + this.getClientSecret()).toString('base64'),
'Basic ' +
Buffer.from(
this.getClientId() + ':' + this.getClientSecret()
).toString('base64'),
'Content-Type': 'application/x-www-form-urlencoded'
})
.build()

View File

@@ -1,6 +1,6 @@
'use strict'
/* const AuthenticationRequest = require('./authentication-request') */
const AuthenticationRequest = require('./authentication-request')
const WebApiRequest = require('./webapi-request')
const HttpManager = require('./http-manager')
@@ -91,13 +91,17 @@ SpotifyWebApi.prototype = {
},
_getCredential: function (credentialKey) {
if (this._credentials) {
if (!this._credentials) {
} else {
return this._credentials[credentialKey]
}
},
_resetCredential: function (credentialKey) {
if (this._credentials) {
if (!this._credentials) {
} else {
this._credentials[credentialKey] = null
}
},
@@ -180,27 +184,6 @@ SpotifyWebApi.prototype = {
.execute(HttpManager.get, callback)
},
getPlaylistCoverImage: function (playlistId, callback) {
return WebApiRequest.builder(this.getAccessToken())
.withPath('/v1/playlists/' + playlistId + '/images')
.withHeaders({ 'Content-Type': 'application/json' })
.build()
.execute(HttpManager.get, callback)
},
/** Get available markets that Spotify support
* @param {requestCallback} callback
* @returns {Promise|undefined} A promise that if successful, returns an object containing the user's devices. Not returned if a callback is given.
*/
getAvailableMarkets: function (callback) {
const path = '/v1/markets'
return WebApiRequest.builder(this.getAccessToken())
.withPath(path)
.withHeaders({ 'Content-Type': 'application/json' })
.build()
.execute(HttpManager.get, callback)
},
/**
* Look up an artist.
* @param {string} artistId The artist's ID.
@@ -496,12 +479,9 @@ SpotifyWebApi.prototype = {
return WebApiRequest.builder(this.getAccessToken())
.withPath('/v1/me/playlists')
.withHeaders({ 'Content-Type': 'application/json' })
.withBodyParameters(
{
name
},
options
)
.withBodyParameters({
name
}, options)
.build()
.execute(HttpManager.post, callback)
},
@@ -574,16 +554,16 @@ SpotifyWebApi.prototype = {
},
/**
* Add tracks to a playlist.
* @param {string} playlistId The playlist's ID
* @param {string[]} tracks URIs of the tracks to add to the playlist.
* @param {Object} [options] Options, position being the only one.
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @example addTracksToPlaylist('3EsfV6XzCHU8SPNdbnFogK',
'["spotify:track:4iV5W9uYEdYUVa79Axb7Rh", "spotify:track:1301WleyT98MSxVHPZCA6M"]').then(...)
* @returns {Promise|undefined} A promise that if successful returns an object containing a snapshot_id. If rejected,
* it contains an error object. Not returned if a callback is given.
*/
* Add tracks to a playlist.
* @param {string} playlistId The playlist's ID
* @param {string[]} tracks URIs of the tracks to add to the playlist.
* @param {Object} [options] Options, position being the only one.
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @example addTracksToPlaylist('3EsfV6XzCHU8SPNdbnFogK',
'["spotify:track:4iV5W9uYEdYUVa79Axb7Rh", "spotify:track:1301WleyT98MSxVHPZCA6M"]').then(...)
* @returns {Promise|undefined} A promise that if successful returns an object containing a snapshot_id. If rejected,
* it contains an error object. Not returned if a callback is given.
*/
addTracksToPlaylist: function (playlistId, tracks, options, callback) {
return WebApiRequest.builder(this.getAccessToken())
.withPath('/v1/playlists/' + playlistId + '/tracks')
@@ -1359,15 +1339,22 @@ SpotifyWebApi.prototype = {
/**
* Check if users are following a playlist.
* @param {String[]} followerIds IDs of the following users
* @param {string} userId The playlist's owner's user ID
* @param {string} playlistId The playlist's ID
* @param {String[]} User IDs of the following users
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @returns {Promise|undefined} A promise that if successful returns an array of booleans. If rejected,
* it contains an error object. Not returned if a callback is given.
*/
areFollowingPlaylist: function (playlistId, followerIds, callback) {
areFollowingPlaylist: function (userId, playlistId, followerIds, callback) {
return WebApiRequest.builder(this.getAccessToken())
.withPath('/v1/playlists/' + playlistId + '/followers/contains')
.withPath(
'/v1/users/' +
encodeURIComponent(userId) +
'/playlists/' +
playlistId +
'/followers/contains'
)
.withQueryParameters({
ids: followerIds.join(',')
})
@@ -1542,9 +1529,7 @@ SpotifyWebApi.prototype = {
return WebApiRequest.builder(this.getAccessToken())
.withPath('/v1/me/shows')
.withHeaders({ 'Content-Type': 'application/json' })
.withQueryParameters({
ids: showIds.join(',')
})
.withBodyParameters(showIds)
.build()
.execute(HttpManager.del, callback)
},
@@ -1662,21 +1647,7 @@ SpotifyWebApi.prototype = {
)
.build()
.execute(HttpManager.get, callback)
},
/** Get current Queue
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @returns {Promise|undefined} A promise that if successful, returns an object containing information
* about the episodes. Not returned if a callback is given.
*/
getQueue: function (callback) {
return WebApiRequest.builder(this.getAccessToken())
.withPath('/v1/me/player/queue')
.build()
.execute(HttpManager.get, callback)
}
}
SpotifyWebApi._addMethods = function (methods) {

View File

@@ -37,6 +37,10 @@
<h2>Changelog</h2>
<ul class="no-dots" style="font-size: 15px;">
<h3>31/07/2024</h3>
- Reverted to spotify-web-api-node<br>
- Fixed (again) the Buffer method in spotify-web-api-node<br>
<br>
<h3>26/07/2024</h3>
- Moved all scripts to pnpm<br>
- Fixed a few dependencies<br>
@@ -128,7 +132,7 @@
</span>
</h2>
<ul>
<!-- <li>
<!-- <li>
{{ $t('about.acceptFeatures') }}
</li> -->
<i18n path="about.contributeWebUI.text" tag="li">

View File

@@ -1,7 +1,7 @@
<template>
<div id="home_tab">
<h1 class="mb-8 text-5xl">{{ $t('globals.welcome') }}</h1>
Version published 26/07/2024, see About section for changelog.
Version published 31/07/2024, see About section for changelog.
<section v-if="!isLoggedIn" ref="notLogged" class="py-6 border-0 border-t border-solid border-grayscale-500">
<p id="home_not_logged_text" class="mb-4">{{ $t('home.needTologin') }}</p>
<router-link custom v-slot="{ navigate }" class="btn btn-primary" name="button" :to="{ name: 'Settings' }">