lastfm-api-lib
Version:
A TypeScript library for interacting with the Last.fm API
455 lines (448 loc) • 10.3 kB
JavaScript
// src/httpClient.ts
import axios from "axios";
var RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([408, 429, 500, 502, 503, 504]);
var LastFmHttpClient = class {
client;
apiKey;
baseURL = "https://ws.audioscrobbler.com/2.0/";
constructor(apiKey) {
this.apiKey = apiKey || "";
this.client = axios.create({
baseURL: this.baseURL,
timeout: 1e4,
headers: {
"User-Agent": "lastfm-api-lib/1.0.0",
"Accept": "application/json"
}
});
this.client.interceptors.response.use(
(response) => response,
(error) => this.handleResponseError(error)
);
}
/**
* Set API key for requests
*/
setApiKey(apiKey) {
this.apiKey = apiKey;
}
/**
* Clear the API key
*/
clearApiKey() {
this.apiKey = "";
}
/**
* Make a request to the Last.fm API
*/
async request(method, params = {}, options = {}) {
if (!this.apiKey) {
throw new Error("API key is required");
}
const requestParams = {
method,
api_key: this.apiKey,
format: "json",
...params
};
const config = {
method: options.method || "GET",
params: requestParams,
headers: options.headers
};
try {
const response = await this.client.request(config);
if (response.data?.error) {
const error = new Error(response.data.message || "Last.fm API error");
error.code = response.data.error;
error.status = response.status;
throw error;
}
return response.data;
} catch (error) {
if (error.isAxiosError) {
const lastFmError = new Error(
error.response?.data?.message || error.message || "Request failed"
);
lastFmError.status = error.response?.status;
lastFmError.response = error.response?.data;
throw lastFmError;
}
throw error;
}
}
/**
* Handle response errors with retries for certain status codes
*/
async handleResponseError(error) {
if (error.response?.status && RETRYABLE_STATUS_CODES.has(error.response.status) && error.config && !error.config._retryCount) {
error.config._retryCount = 1;
if (error.response.status === 429) {
await this.delay(1e3);
}
return this.client.request(error.config);
}
return Promise.reject(error);
}
/**
* Delay helper for rate limiting
*/
delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
/**
* Get the HTTP client instance
*/
getHttpClient() {
return this.client;
}
};
// src/endpoints/baseEndpoint.ts
var BaseEndpoint = class {
httpClient;
constructor(httpClient) {
this.httpClient = httpClient;
}
/**
* Make a request to the API
*/
async request(method, params = {}) {
return this.httpClient.request(method, params);
}
/**
* Clean up undefined/null parameters
*/
cleanParams(params) {
const cleaned = {};
for (const [key, value] of Object.entries(params)) {
if (value !== void 0 && value !== null && value !== "") {
cleaned[key] = value;
}
}
return cleaned;
}
};
// src/endpoints/artist.ts
var ArtistEndpoints = class extends BaseEndpoint {
/**
* Get artist correction
*/
async getCorrection(artist) {
const params = this.cleanParams({ artist });
return this.request("artist.getCorrection", params);
}
/**
* Get artist information
*/
async getInfo(artist, options = {}) {
const params = this.cleanParams({
artist,
...options
});
return this.request("artist.getInfo", params);
}
/**
* Get similar artists
*/
async getSimilar(artist, options = {}) {
const params = this.cleanParams({
artist,
...options
});
return this.request("artist.getSimilar", params);
}
/**
* Get top albums for an artist
*/
async getTopAlbums(artist, options = {}) {
const params = this.cleanParams({
artist,
...options
});
return this.request("artist.getTopAlbums", params);
}
/**
* Get top tags for an artist
*/
async getTopTags(artist, options = {}) {
const params = this.cleanParams({
artist,
...options
});
return this.request("artist.getTopTags", params);
}
/**
* Get top tracks for an artist
*/
async getTopTracks(artist, options = {}) {
const params = this.cleanParams({
artist,
...options
});
return this.request("artist.getTopTracks", params);
}
/**
* Search for an artist
*/
async search(artist, options = {}) {
const params = this.cleanParams({
artist,
...options
});
return this.request("artist.search", params);
}
};
// src/endpoints/album.ts
var AlbumEndpoints = class extends BaseEndpoint {
/**
* Get album information
*/
async getInfo(artist, album, options = {}) {
const params = this.cleanParams({
artist,
album,
...options
});
return this.request("album.getInfo", params);
}
/**
* Get top tags for an album
*/
async getTopTags(artist, album, options = {}) {
const params = this.cleanParams({
artist,
album,
...options
});
return this.request("album.getTopTags", params);
}
/**
* Search for an album
*/
async search(album, options = {}) {
const params = this.cleanParams({
album,
...options
});
return this.request("album.search", params);
}
};
// src/endpoints/track.ts
var TrackEndpoints = class extends BaseEndpoint {
/**
* Get track correction
*/
async getCorrection(artist, track) {
const params = this.cleanParams({ artist, track });
return this.request("track.getCorrection", params);
}
/**
* Get track information
*/
async getInfo(artist, track, options = {}) {
const params = this.cleanParams({
artist,
track,
...options
});
return this.request("track.getInfo", params);
}
/**
* Get similar tracks
*/
async getSimilar(artist, track, options = {}) {
const params = this.cleanParams({
artist,
track,
...options
});
return this.request("track.getSimilar", params);
}
/**
* Get top tags for a track
*/
async getTopTags(artist, track, options = {}) {
const params = this.cleanParams({
artist,
track,
...options
});
return this.request("track.getTopTags", params);
}
/**
* Search for a track
*/
async search(track, options = {}) {
const params = this.cleanParams({
track,
...options
});
return this.request("track.search", params);
}
};
// src/endpoints/tag.ts
var TagEndpoints = class extends BaseEndpoint {
/**
* Get tag information
*/
async getInfo(tag, options = {}) {
const params = this.cleanParams({
tag,
...options
});
return this.request("tag.getInfo", params);
}
/**
* Get similar tags
*/
async getSimilar(tag) {
const params = this.cleanParams({ tag });
return this.request("tag.getSimilar", params);
}
/**
* Get top albums for a tag
*/
async getTopAlbums(tag, options = {}) {
const params = this.cleanParams({
tag,
...options
});
return this.request("tag.getTopAlbums", params);
}
/**
* Get top artists for a tag
*/
async getTopArtists(tag, options = {}) {
const params = this.cleanParams({
tag,
...options
});
return this.request("tag.getTopArtists", params);
}
/**
* Get top tracks for a tag
*/
async getTopTracks(tag, options = {}) {
const params = this.cleanParams({
tag,
...options
});
return this.request("tag.getTopTracks", params);
}
/**
* Get top tags
*/
async getTopTags() {
return this.request("tag.getTopTags");
}
};
// src/endpoints/chart.ts
var ChartEndpoints = class extends BaseEndpoint {
/**
* Get top artists chart
*/
async getTopArtists(options = {}) {
const params = this.cleanParams(options);
return this.request("chart.getTopArtists", params);
}
/**
* Get top tracks chart
*/
async getTopTracks(options = {}) {
const params = this.cleanParams(options);
return this.request("chart.getTopTracks", params);
}
/**
* Get top tags chart
*/
async getTopTags(options = {}) {
const params = this.cleanParams(options);
return this.request("chart.getTopTags", params);
}
};
// src/main.ts
var ENDPOINT_CLASSES = {
artist: ArtistEndpoints,
album: AlbumEndpoints,
track: TrackEndpoints,
tag: TagEndpoints,
chart: ChartEndpoints
};
var LastFm = class {
httpClient;
// Endpoint groups
artist;
album;
track;
tag;
chart;
constructor(apiKey) {
if (apiKey && !this.isValidApiKeyFormat(apiKey)) {
console.warn("LastFm: API key format appears invalid");
}
this.httpClient = new LastFmHttpClient(apiKey);
this.initializeEndpoints();
}
/**
* Initialize all endpoint groups
*/
initializeEndpoints() {
for (const [name, EndpointClass] of Object.entries(ENDPOINT_CLASSES)) {
;
this[name] = new EndpointClass(this.httpClient);
}
}
/**
* Basic API key format validation
*/
isValidApiKeyFormat(apiKey) {
return typeof apiKey === "string" && /^[a-f0-9]{32}$/i.test(apiKey);
}
/**
* Set API key for API requests
*/
setApiKey(apiKey) {
this.httpClient.setApiKey(apiKey);
}
/**
* Clear the API key
*/
clearApiKey() {
this.httpClient.clearApiKey();
}
/**
* Make a raw API request
*/
async request(method, params = {}) {
return this.httpClient.request(method, params);
}
/**
* Get the HTTP client instance for advanced usage
*/
getHttpClient() {
return this.httpClient;
}
/**
* Clean up resources and clear API key
*/
destroy() {
this.clearApiKey();
Object.values(ENDPOINT_CLASSES).forEach((_, key) => {
const endpoint = this[Object.keys(ENDPOINT_CLASSES)[key]];
if (endpoint && typeof endpoint.cleanup === "function") {
endpoint.cleanup();
}
});
}
};
export {
AlbumEndpoints,
ArtistEndpoints,
ChartEndpoints,
LastFm,
LastFmHttpClient,
TagEndpoints,
TrackEndpoints,
LastFm as default
};
//# sourceMappingURL=index.js.map