deezer-ts
Version:
Deezer API wrapper for Node.js
1,187 lines (1,171 loc) • 34.5 kB
JavaScript
// src/exceptions.ts
var DeezerAPIException = class extends Error {
constructor(message) {
super(message);
this.name = "DeezerAPIException";
}
};
var DeezerRetryableException = class extends DeezerAPIException {
constructor(message) {
super(message);
this.name = "DeezerRetryableException";
}
};
var DeezerQuotaExceededError = class extends DeezerRetryableException {
constructor() {
super("Quota limit exceeded");
this.name = "DeezerQuotaExceededError";
}
};
var DeezerHTTPError = class _DeezerHTTPError extends DeezerAPIException {
constructor(response) {
super(`HTTP ${response.status}: ${response.statusText}`);
this.name = "DeezerHTTPError";
}
/**
* Creates the appropriate HTTP error based on the response status code.
*
* @param response - The HTTP response object
* @returns A specific HTTP error instance
* @internal
*/
static fromResponse(response) {
if ([502, 503, 504].includes(response.status)) {
return new DeezerRetryableHTTPError(response);
}
if (response.status === 403) {
return new DeezerForbiddenError(response);
}
if (response.status === 404) {
return new DeezerNotFoundError(response);
}
if (response.status === 429) {
return new DeezerQuotaExceededError();
}
return new _DeezerHTTPError(response);
}
};
var DeezerRetryableHTTPError = class extends DeezerRetryableException {
constructor(response) {
super(`Retryable HTTP ${response.status}: ${response.statusText}`);
this.name = "DeezerRetryableHTTPError";
}
};
var DeezerForbiddenError = class extends DeezerHTTPError {
constructor(response) {
super(response);
this.name = "DeezerForbiddenError";
}
};
var DeezerNotFoundError = class extends DeezerHTTPError {
constructor(response) {
super(response);
this.name = "DeezerNotFoundError";
}
};
var DeezerErrorResponse = class extends DeezerAPIException {
constructor(error) {
super(error.message || "Unknown API error");
this.name = "DeezerErrorResponse";
if (error.message === "Quota limit exceeded") {
throw new DeezerQuotaExceededError();
}
}
};
var DeezerUnknownResource = class extends DeezerAPIException {
constructor(message) {
super(message);
this.name = "DeezerUnknownResource";
}
};
// src/pagination.ts
var PaginatedList = class {
constructor(client, basePath, parent, params) {
this.client = client;
this.basePath = basePath;
this.parent = parent;
this.params = params;
this.elements = [];
this.nextParams = {};
this.totalItems = null;
this.nextPath = basePath;
this.nextParams = { ...params };
}
async *[Symbol.asyncIterator]() {
for (const element of this.elements) {
yield element;
}
while (await this.couldGrow()) {
const newElements = await this.grow();
for (const element of newElements) {
yield element;
}
}
}
/**
* The total number of items in the list, mirroring what Deezer returns.
*
* @returns {Promise<number | null>} - The total number of items in the list.
*/
async total() {
if (this.totalItems === null) {
const params = { ...this.params, limit: "1" };
const response = await this.client.request(
"GET",
this.basePath,
true,
this.parent,
void 0,
void 0,
params
);
this.totalItems = response.total;
}
return this.totalItems;
}
/**
* Returns a slice of the list.
*
* @param {number} start - The index to start the slice at.
* @param {number} end - The index to end the slice at.
*
* @returns {Promise<T[]>} - The slice of the list.
*/
async slice(start = 0, end) {
const results = [];
let count = 0;
for await (const item of this) {
if (count >= start) {
results.push(item);
}
count++;
if (end !== void 0 && count >= end) {
break;
}
}
return results;
}
/**
* Returns the item at the given index.
*
* @param {number} index - The index of the item to return.
*
* @returns {Promise<T>} - The item at the given index.
*/
async get(index) {
const items = await this.slice(index, index + 1);
if (items.length === 0 || !items[0]) {
throw new Error(`Index ${index} is out of bounds`);
}
return items[0];
}
/**
* Returns the list as an array.
*
* This method is not recommended for large lists, as it will fetch all items at once.
*
* @returns {Promise<T[]>} - The list as an array.
*/
async toArray() {
const results = [];
for await (const item of this) {
results.push(item);
}
return results;
}
async couldGrow() {
return this.nextPath !== null;
}
async grow() {
const newElements = await this.fetchNextPage();
this.elements.push(...newElements);
return newElements;
}
async fetchNextPage() {
if (!this.nextPath) {
return [];
}
const response = await this.client.request(
"GET",
this.nextPath,
true,
this.parent,
void 0,
void 0,
this.nextParams
);
this.totalItems = response.total;
if (response.next) {
const url = new URL(response.next);
this.nextPath = url.pathname.substring(1);
this.nextParams = Object.fromEntries(url.searchParams);
} else {
this.nextPath = null;
}
return response.data;
}
};
// src/date.ts
function parseDate(dateStr) {
if (!dateStr || dateStr.startsWith("0000-00-00")) {
return null;
}
const date = new Date(dateStr);
return isNaN(date.getTime()) ? null : date;
}
// src/resources/resource.ts
var Resource = class _Resource {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
constructor(client, json) {
this._fetched = false;
this.client = client;
for (const fieldName of Object.keys(json)) {
const parseFunc = this[`_parse_${fieldName}`];
if (typeof parseFunc === "function") {
json[fieldName] = parseFunc.call(this, json[fieldName]);
}
}
this._fields = Object.keys(json);
Object.assign(this, json);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
toJSON() {
const result = {};
for (const key of this._fields) {
let value = this[key];
if (Array.isArray(value)) {
value = value.map((i) => i instanceof _Resource ? i.toJSON() : i);
} else if (value instanceof _Resource) {
value = value.toJSON();
} else if (value instanceof Date) {
value = value.toISOString();
if (value.endsWith("T00:00:00.000Z")) {
value = value.slice(0, 10);
}
}
result[key] = value;
}
return result;
}
async getRelation(relation, resourceType, params, fwdParent = true) {
return this.client.request(
"GET",
`${this.type}/${this.id}/${relation}`,
false,
fwdParent ? this : void 0,
resourceType,
void 0,
params
);
}
getPaginatedList(relation, params) {
return new PaginatedList(
this.client,
`${this.type}/${this.id}/${relation}`,
this,
params
);
}
/**
* Ensures a field is loaded, fetching the full resource if necessary.
*
* @param fieldName The name of the field to ensure
* @returns The value of the field
* @throws Error if the field cannot be loaded
*/
async ensureField(fieldName) {
if (!this._fields.includes(fieldName) || // eslint-disable-next-line @typescript-eslint/no-explicit-any
this[fieldName] === void 0) {
if (!this._fetched) {
const fullResource = await this.get();
const missingFields = Object.keys(fullResource).filter(
(field) => !this._fields.includes(field) && !["client", "_fields", "_fetched"].includes(field)
);
for (const field of missingFields) {
this[field] = fullResource[field];
this._fields.push(field);
}
if (this._fields.includes(fieldName) && // eslint-disable-next-line @typescript-eslint/no-explicit-any
this[fieldName] !== void 0) {
return this[fieldName];
}
}
throw new Error(
`Field '${fieldName}' is not available on ${this.constructor.name} with id ${this.id}`
);
}
return this[fieldName];
}
/**
* Get the resource from the API.
*
* @returns {Promise<this>}
*/
async get() {
this._fetched = true;
return await this.client.request("GET", `${this.type}/${this.id}`, false);
}
};
// src/resources/artist.ts
var Artist = class extends Resource {
/**
* Get the top tracks of an artist.
*
* @returns {Promise<PaginatedList<Track>>} - a {@link PaginatedList} of {@link Track} instances.
*/
async getTop(params) {
return this.getPaginatedList("top", params);
}
/**
* Get a list of related artists.
*
* @returns {Promise<PaginatedList<Artist>>} - a {@link PaginatedList} of {@link Artist} instances.
*/
async getRelated(params) {
return this.getPaginatedList("related", params);
}
/**
* Get a list of tracks.
*
* @returns {Promise<Track[]>} - list of {@link Track} instances.
*/
async getRadio(params) {
return this.getRelation("radio", void 0, params, false);
}
/**
* Get a list of artist's albums.
*
* @returns {Promise<PaginatedList<Album>>} - a {@link PaginatedList} of {@link Album} instances.
*/
async getAlbums(params) {
return this.getPaginatedList("albums", params);
}
/**
* Get a list of artist's playlists.
*
* @returns {Promise<PaginatedList<Playlist>>} - a {@link PaginatedList} of {@link Playlist} instances.
*/
async getPlaylists(params) {
return this.getPaginatedList("playlists", params);
}
};
// src/resources/album.ts
var Album = class extends Resource {
/**
* @internal
*/
_parse_release_date(params) {
return parseDate(params);
}
/**
* @internal
*/
_parse_contributors(rawValue) {
return rawValue.map((val) => new Artist(this.client, val));
}
/**
* Get the artist of the Album.
*
* @returns {Promise<Artist>} - the class {@link Artist} of the Album.
*/
async getArtist() {
const artist = await this.ensureField("artist");
return this.client.getArtist(artist.id);
}
/**
* Get a list of album's tracks.
*
* @returns {Promise<PaginatedList<Track>>} - a {@link PaginatedList} of {@link Track}.
*/
async getTracks(params) {
return this.getPaginatedList("tracks", params);
}
};
// src/resources/chart.ts
var Chart = class extends Resource {
constructor() {
super(...arguments);
this.type = "chart";
}
/**
* Return the chart for tracks.
*
* @returns {Promise<PaginatedList<Track>>} - a {@link PaginatedList} of {@link Track} instances.
*/
async getTracks(params) {
return this.getPaginatedList("tracks", params);
}
/**
* Return the chart for albums.
*
* @returns {Promise<PaginatedList<Album>>} - a {@link PaginatedList} of {@link Album} instances.
*/
async getAlbums(params) {
return this.getPaginatedList("albums", params);
}
/**
* Return the chart for artists.
*
* @returns {Promise<PaginatedList<Artist>>} - a {@link PaginatedList} of {@link Artist} instances.
*/
async getArtists(params) {
return this.getPaginatedList("artists", params);
}
/**
* Return the chart for playlists.
*
* @returns {Promise<PaginatedList<Playlist>>} - a {@link PaginatedList} of {@link Playlist} instances.
*/
async getPlaylists(params) {
return this.getPaginatedList("playlists", params);
}
/**
* Return the chart for podcasts.
*
* @returns {Promise<PaginatedList<Podcast>>} - a {@link PaginatedList} of {@link Podcast} instances.
*/
async getPodcasts(params) {
return this.getPaginatedList("podcasts", params);
}
};
// src/resources/editorial.ts
var Editorial = class extends Resource {
/**
* Get a list of albums selected every week by the Deezer Team.
*
* @returns {Promise<Album[]>} - list of {@link Album} instances.
*/
async getSelection() {
return this.getRelation("selection");
}
/**
* Get top charts for tracks, albums, artists and playlists.
*
* @returns {Promise<Chart>} - a {@link Chart} instances.
*/
async getChart() {
return this.getRelation("charts", Chart);
}
/**
* Get the new releases per genre for the current country.
*
* @returns {Promise<PaginatedList<Album>>} - a {@link PaginatedList} of {@link Album} instances.
*/
async getReleases(params) {
return this.getPaginatedList("releases", params);
}
};
// src/resources/episode.ts
var Episode = class extends Resource {
/**
* @internal
*/
_parse_release_date(params) {
return parseDate(params);
}
};
// src/resources/genre.ts
var Genre = class extends Resource {
/**
* Get all artists for a genre.
*
* @returns {Promise<Artist[]>} - list of {@link Artist} instances.
*/
async getArtists(params) {
return this.getRelation("artists", void 0, params);
}
/**
* Get all podcasts for a genre.
*
* @returns {Promise<PaginatedList<Podcast>>} - a {@link PaginatedList} of {@link Podcast} instances.
*/
async getPodcasts(params) {
return this.getPaginatedList("podcasts", params);
}
/**
* Get all radios for a genre.
*
* @returns {Promise<Radio[]>} - list of {@link Artist} instances.
*/
async getRadios(params) {
return this.getRelation("radios", void 0, params);
}
};
// src/resources/playlist.ts
var Playlist = class extends Resource {
/**
* Get tracks from a playlist.
*
* @returns {Promise<PaginatedList<Track>>} - a {@link PaginatedList} of {@link Track} instances.
*/
async getTracks(params) {
return this.getPaginatedList("tracks", params);
}
/**
* Get fans from a playlist.
*
* @returns {Promise<PaginatedList<User>>} - a {@link PaginatedList} of {@link User} instances.
*/
async getFans(params) {
return this.getPaginatedList("fans", params);
}
};
// src/resources/podcast.ts
var Podcast = class extends Resource {
/**
* Get episodes from a podcast.
*
* @returns {Promise<PaginatedList<Episode>>} - a {@link PaginatedList} of {@link Episode} instances.
*/
async getEpisodes(params) {
return this.getPaginatedList("episodes", params);
}
};
// src/resources/radio.ts
var Radio = class extends Resource {
/**
* Get first 40 tracks in the radio.
*
* Note that this endpoint is NOT paginated.
*
* @returns {Promise<Track[]>} - list of {@link Track} instances.
*/
async getTracks() {
return this.getRelation("tracks");
}
};
// src/resources/track.ts
var Track = class extends Resource {
/**
* @internal
*/
_parse_release_date(params) {
return new Date(params);
}
/**
* @internal
*/
_parse_contributors(rawValue) {
return rawValue.map((val) => new Artist(this.client, val));
}
/**
* Get the artist of the Track.
*
* @returns {Promise<Artist>} - the class {@link Artist} of the Track.
*/
async getArtist() {
return this.client.getArtist(this.artist.id);
}
/**
* Get the album of the Track.
*
* @returns {Promise<Album>} - the class {@link Album} of the Track.
*/
async getAlbum() {
return this.client.getAlbum(this.album.id);
}
};
// src/resources/user.ts
var User = class extends Resource {
/**
* Get user's favorite albums.
*
* @returns {Promise<PaginatedList<Album>>} - a {@link PaginatedList} of {@link Album} instances.
*/
async getAlbums(params) {
return this.getPaginatedList("albums", params);
}
/**
* Get user's favorite artists.
*
* @returns {Promise<PaginatedList<Artist>>} - a {@link PaginatedList} of {@link Artist} instances.
*/
async getArtists(params) {
return this.getPaginatedList("artists", params);
}
/**
* Get user's followings.
*
* @returns {Promise<PaginatedList<User>>} - a {@link PaginatedList} of {@link User} instances.
*/
async getFollowers(params) {
return this.getPaginatedList("followers", params);
}
/**
* Get user's followers.
*
* @returns {Promise<PaginatedList<User>>} - a {@link PaginatedList} of {@link User} instances.
*/
async getFollowings(params) {
return this.getPaginatedList("followings", params);
}
/**
* Get user's public playlists.
*
* @returns {Promise<PaginatedList<Playlist>>} - a {@link PaginatedList} of {@link Playlist} instances.
*/
async getPlaylists(params) {
return this.getPaginatedList("playlists", params);
}
};
// src/client.ts
var _Client = class _Client {
// 45 requests per 5 seconds
/**
* Create a new client instance.
*
* @param {Record<string, string>} [headers] - Additional headers to pass.
*/
constructor(headers) {
this.headers = headers;
/**
* @internal
*/
this.baseUrl = "https://api.deezer.com";
/**
* @internal
*/
this.objectsTypes = {
album: Album,
artist: Artist,
chart: Chart,
editorial: Editorial,
episode: Episode,
genre: Genre,
playlist: Playlist,
podcast: Podcast,
radio: Radio,
search: null,
track: Track,
user: User
};
}
/**
* @internal
* Ensures we don't exceed Deezer's rate limit of 50 requests per 5 seconds
*/
async enforceRateLimit() {
const now = Date.now();
_Client.requestQueue = _Client.requestQueue.filter(
(req) => now - req.timestamp < _Client.QUOTA_WINDOW
);
if (_Client.requestQueue.length >= _Client.QUOTA_LIMIT) {
const oldestRequest = _Client.requestQueue[0];
if (oldestRequest) {
const waitTime = _Client.QUOTA_WINDOW - (now - oldestRequest.timestamp);
if (waitTime > 0) {
await new Promise((resolve) => setTimeout(resolve, waitTime));
}
}
}
_Client.requestQueue.push({ timestamp: Date.now() });
}
/**
* @internal
* Retries an operation with exponential backoff when encountering retryable errors.
*
* This method implements an exponential backoff strategy for retrying failed operations:
* - Initial delay is baseDelay (default 2000ms)
* - Each retry doubles the delay
* - Adds random jitter (75-125% of calculated delay) to prevent thundering herd
* - Only retries on quota exceeded or other retryable errors
*
* @param {() => Promise<T>} operation - The async operation to retry
* @param {number} maxRetries - Maximum number of retry attempts (default: 3)
* @param {number} baseDelay - Base delay in milliseconds before first retry (default: 2000)
* @returns {Promise<T>} The result of the operation if successful
* @throws {Error} The last error encountered if all retries fail
* @throws {Error} Non-retryable errors immediately without retry
* @template T - The return type of the operation
*/
async retryWithBackoff(operation, maxRetries = 3, baseDelay = 5e3) {
let lastError;
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await operation();
} catch (error) {
if (error instanceof DeezerQuotaExceededError || error instanceof DeezerRetryableException) {
lastError = error;
const delay = baseDelay * Math.pow(2, attempt) * (0.75 + Math.random() * 0.5);
await new Promise((resolve) => setTimeout(resolve, delay));
continue;
}
throw error;
}
}
throw lastError;
}
/**
* @internal
*
* Recursively convert dictionary to Resource object.
*
* @param item - the JSON response as object.
* @param parent - A reference to the parent resource, to avoid fetching again.
* @param resourceType - The resource class to use as top level.
* @param resourceId - The resource id to use as top level.
* @param paginateList - Whether to wrap list into a pagination object.
* @returns instance of Resource
* @throws DeezerUnknownResource
*/
_processJson(item, paginateList = false, parent, resourceType, resourceId) {
if (item.data) {
const parsedData = item.data.map(
(i) => this._processJson(i, false, parent)
);
if (!paginateList) {
return parsedData;
}
item.data = parsedData;
return item;
}
const result = {};
for (const key in item) {
let value = item[key];
if (value !== null && typeof value === "object" && ("type" in value || "data" in value)) {
value = this._processJson(value, false, parent);
}
result[key] = value;
}
if (parent) {
result[parent.type] = parent;
}
if (result.id === void 0 && resourceId !== null) {
result.id = resourceId;
}
let objectClass = null;
if (result.type && result.type in this.objectsTypes) {
objectClass = this.objectsTypes[result.type];
} else if (result.type || !resourceType && result.id) {
objectClass = Resource;
} else if (resourceType) {
objectClass = resourceType;
} else {
throw new DeezerUnknownResource(
`Unable to find resource type for ${JSON.stringify(result)}`
);
}
if (objectClass === null) {
throw new DeezerUnknownResource(
`Object class is null for ${JSON.stringify(result)}`
);
}
debugger;
return new objectClass(this, result);
}
async request(method, path, paginateList = false, parent, resourceType, resourceId, params) {
return this.retryWithBackoff(async () => {
await this.enforceRateLimit();
const url = new URL(`${this.baseUrl}/${path}`);
if (params) {
Object.entries(params).forEach(([key, value]) => {
url.searchParams.append(key, String(value));
});
}
const response = await fetch(url, {
method,
headers: {
...this.headers
}
});
if (!response.ok) {
throw DeezerHTTPError.fromResponse(response);
}
const data = await response.json();
if (data.error) {
throw new DeezerErrorResponse(data.error);
}
return this._processJson(
data,
paginateList,
parent,
resourceType,
resourceId
);
});
}
/**
* @internal
* @private
*/
getPaginatedList(path, params) {
return new PaginatedList(this, path, void 0, params);
}
/**
* Get the album with the given id.
*
* @param {number} albumId - The id of the album to get.
* @returns {@link Album} - An album object.
*/
async getAlbum(albumId) {
return this.request("GET", `album/${albumId}`, false);
}
/**
* Get the artist with the given id.
*
* @param {number} artistId - The id of the artist to get.
* @returns {@link Artist} - An artist object.
*/
async getArtist(artistId) {
return this.request("GET", `artist/${artistId}`, false);
}
/**
* Get overall charts for tracks, albums, artists and playlists for the given genre ID.
*
* Combine charts of several resources in one endpoint.
*
* @param {number} genreId - The genre id, default to `All` genre (genreId = 0).
* @returns {@link Chart} - A chart instance.
*/
async getChart(genreId = 0) {
return this.request(
"GET",
`chart/${genreId}`,
false,
void 0,
Chart,
genreId
);
}
/**
* Get top tracks for the given genre id.
*
* @param {number} genreId - The genre id, default to `All` genre (genreId = 0).
* @returns A list of {@link Track} instances.
*/
async getTracksChart(genreId = 0) {
return this.request("GET", `chart/${genreId}/tracks`, false);
}
/**
* Get top albums for the given genre id.
*
* @param {number} genreId - The genre id, default to `All` genre (genreId = 0).
* @returns A list of {@link Album} instances.
*/
async getAlbumsChart(genreId = 0) {
return this.request("GET", `chart/${genreId}/albums`, false);
}
/**
* Get top artists for the given genre id.
*
* @param {number} genreId - The genre id, default to `All` genre (genreId = 0).
* @returns A list of {@link Artist} instances.
*/
async getArtistsChart(genreId = 0) {
return this.request("GET", `chart/${genreId}/artists`, false);
}
/**
* Get top playlists for the given genre id.
*
* @param {number} genreId - The genre id, default to `All` genre (genreId = 0).
* @returns A list of {@link Playlist} instances.
*/
async getPlaylistsChart(genreId = 0) {
return this.request("GET", `chart/${genreId}/playlists`, false);
}
/**
* Get top podcasts for the given genre id.
*
* @param {number} genreId - The genre id, default to `All` genre (genreId = 0).
* @returns A list of {@link Podcast} instances.
*/
async getPodcastsChart(genreId = 0) {
return this.request("GET", `chart/${genreId}/podcasts`, false);
}
/**
* Get the editorial with the given id.
*
* @param {number} editorialId - The id of the editorial to get.
* @returns a {@link Editorial} object.
*/
async getEditorial(editorialId) {
return this.request("GET", `editorial/${editorialId}`, false);
}
/**
* List editorials.
*
* @returns {@link PaginatedList} of {@link Editorial} - An editorial object.
*/
async listEditorials() {
return this.getPaginatedList("editorial");
}
/**
* Get the episode with the given id.
*
* @param {number} episodeId - The id of the episode to get.
* @returns {@link Episode} - An episode object.
*/
async getEpisode(episodeId) {
return this.request("GET", `episode/${episodeId}`, false);
}
/**
* Get the genre with the given id.
*
* @param {number} genreId - The id of the genre to get.
* @returns {@link Genre} - A genre object.
*/
async getGenre(genreId) {
return this.request("GET", `genre/${genreId}`, false);
}
/**
* Get the playlist with the given id.
*
* @param {number} playlistId - The id of the playlist to get.
* @returns {@link Playlist} - A playlist object.
*/
async getPlaylist(playlistId) {
return this.request("GET", `playlist/${playlistId}`, false);
}
/**
* Get the podcast with the given id.
*
* @param {number} podcastId - The id of the podcast to get.
* @returns {@link Podcast} - A podcast object.
*/
async getPodcast(podcastId) {
return this.request("GET", `podcast/${podcastId}`, false);
}
/**
* Get the radio with the given id.
*
* @param {number} radioId - The id of the radio to get.
* @returns {@link Radio} - A radio object.
*/
async getRadio(radioId) {
return this.request("GET", `radio/${radioId}`, false);
}
/**
* List radios.
*
* @returns A list of {@link Radio} instances.
*/
async listRadios() {
return this.request("GET", "radio", false);
}
/**
* Get the top radios.
*
* @returns {@link PaginatedList} of {@link Radio} objects.
*/
async getRadioTop() {
return this.getPaginatedList("radio/top");
}
/**
* Get the track with the given id.
*
* @param {number} trackId - The id of the track to get.
* @returns {@link Track} - A track object.
*/
async getTrack(trackId) {
return this.request("GET", `track/${trackId}`, false);
}
/**
* Get the user with the given id.
*
* @param {number} userId - The id of the user to get.
* @returns {@link User} - A user object.
*/
async getUser(userId) {
return this.request("GET", `user/${userId}`, false);
}
/**
* Get the flow of the user with the given id.
*
* @param {number} userId - The id of the user to get.
* @param {Record<string, string>} [params] - Additional parameters to pass.
* @returns {@link PaginatedList} of {@link Track} - A list of tracks.
*/
async getUserFlow(userId, params) {
return this.getPaginatedList(`user/${userId}/flow`, params);
}
/**
* Get the favourites albums for the given userId.
*
* @param {number} userId - The user id to get the favourite albums.
* @returns {@link PaginatedList} of {@link Album} - A list of albums.
*/
async getUserAlbums(userId) {
return this.getPaginatedList(`user/${userId}/albums`);
}
/**
* Get the favourite artists for the given userId.
*
* @param {number} userId - The user id to get the favourite artists.
* @returns {@link PaginatedList} of {@link Artist} - A list of artists.
*/
async getUserArtists(userId) {
return this.getPaginatedList(`user/${userId}/artists`);
}
/**
* Get the followers for the given userId.
*
* @param {number} userId - The user id to get followers.
* @returns {@link PaginatedList} of {@link User} - A list of followers.
*/
async getUserFollowers(userId) {
return this.getPaginatedList(`user/${userId}/followers`);
}
/**
* Get the followings for the given userId.
*
* @param {number} userId - The user id to get followings.
* @returns {@link PaginatedList} of {@link User} - A list of followings.
*/
async getUserFollowings(userId) {
return this.getPaginatedList(`user/${userId}/followings`);
}
/**
* Get the favourites tracks for the given userId.
*
* @param {number} userId - The user id to get the favourite tracks.
* @returns {@link PaginatedList} of {@link Track} - A list of tracks.
*/
async getUserTracks(userId) {
return this.getPaginatedList(`user/${userId}/tracks`);
}
/**
* Get the playlists for the given userId.
*
* @param {number} userId - The user id to get the playlists.
* @returns {@link PaginatedList} of {@link Playlist} - A list of playlists.
*/
async getUserPlaylists(userId) {
return this.getPaginatedList(`user/${userId}/playlists`);
}
// --------------------------------------------------
/**
* Get the podcasts for the given userId.
*
* @param {number} userId - The user id to get the podcasts.
* @returns {@link PaginatedList} of {@link Podcast} - A list of podcasts.
*/
async getUserPodcasts(userId) {
return this.getPaginatedList(`user/${userId}/podcasts`);
}
/**
* Get the radios for the given userId.
*
* @param {number} userId - The user id to get the radios.
* @returns {@link PaginatedList} of {@link Radio} - A list of radios.
*/
async getUserRadios(userId) {
return this.getPaginatedList(`user/${userId}/radios`);
}
/**
* Get the charts for the given userId.
*
* @param {number} userId - The user id to get the charts.
* @returns {@link PaginatedList} of {@link Chart} - A list of charts.
*/
async getUserCharts(userId) {
return this.getPaginatedList(`user/${userId}/charts`);
}
// --------------------------------------------------
/**
* @internal
*/
async _search(path, query = "", strict, ordering, advancedParams = {}) {
const optionalParams = {};
if (strict === true) {
optionalParams["strict"] = "on";
}
if (ordering) {
optionalParams["ordering"] = ordering;
}
const queryParts = [];
if (query) {
queryParts.push(query);
}
Object.entries(advancedParams).filter(([_, value]) => value !== null).forEach(([paramName, paramValue]) => {
queryParts.push(`${paramName}:"${paramValue}"`);
});
return this.getPaginatedList(path ? `search/${path}` : "search", {
q: queryParts.join(" "),
...optionalParams
});
}
/**
* Search tracks.
*
* Advanced search is available by either formatting the query yourself or
* by using the dedicated keywords arguments.
*
* @param {string} query - the query to search for, this is directly passed as q query.
* @param {boolean} strict - whether to disable fuzzy search and enable strict mode.
* @param {string} ordering - see Deezer API docs for possible values..
* @param {Object} advancedParams - Additional parameters to pass.
* @param {string} advancedParams.artist - The artist to search for
* @param {string} advancedParams.album - The album to search for
* @param {string} advancedParams.track - The track to search for
* @param {string} advancedParams.label - The label to search for
* @param {number} advancedParams.dur_min - The minimum duration of the track
* @param {number} advancedParams.dur_max - The maximum duration of the track
* @param {number} advancedParams.bpm_min - The minimum BPM of the track
* @param {number} advancedParams.bpm_max - The maximum BPM of the track
*
* @returns {@link PaginatedList} of {@link Track} - A list of tracks.
*/
async search(query = "", strict, ordering, advancedParams = {}) {
return this._search("", query, strict, ordering, advancedParams);
}
/**
* Search albums matching the given query.
*
* @param query - the query to search for, this is directly passed as q query.
* @param strict - whether to disable fuzzy search and enable strict mode.
* @param ordering - see Deezer API docs for possible values.
*/
async searchAlbums(query = "", strict, ordering) {
return this._search("album", query, strict, ordering);
}
/**
* Search artists matching the given query.
*
* @param query - the query to search for, this is directly passed as q query.
* @param strict - whether to disable fuzzy search and enable strict mode.
* @param ordering - see Deezer API docs for possible values.
*/
async searchArtists(query = "", strict, ordering) {
return this._search("artist", query, strict, ordering);
}
/**
* Search playlists matching the given query.
*
* @param query - the query to search for, this is directly passed as q query.
* @param strict - whether to disable fuzzy search and enable strict mode.
* @param ordering - see Deezer API docs for possible values.
*/
async searchPlaylists(query = "", strict, ordering) {
return this._search("playlist", query, strict, ordering);
}
};
_Client.requestQueue = [];
_Client.QUOTA_WINDOW = 5e3;
// 5 seconds in ms
_Client.QUOTA_LIMIT = 45;
var Client = _Client;
export {
Album,
Artist,
Chart,
Client,
DeezerAPIException,
DeezerErrorResponse,
DeezerForbiddenError,
DeezerHTTPError,
DeezerNotFoundError,
DeezerQuotaExceededError,
DeezerRetryableException,
DeezerRetryableHTTPError,
DeezerUnknownResource,
Editorial,
Episode,
Genre,
PaginatedList,
Playlist,
Podcast,
Radio,
Resource,
Track,
User
};