lrclib-api
Version:
The unofficial lrclib.net library for JS and TS
1 lines • 13.6 kB
Source Map (JSON)
{"version":3,"sources":["../src/errors/NotFound.ts","../src/errors/RequestError.ts","../src/utils.ts","../src/lyrics.ts"],"sourcesContent":["export class NotFoundError extends Error {\n constructor() {\n super(\"Track was not found\");\n }\n}\nexport class NoResultError extends Error {\n constructor() {\n super(\"No result was found\");\n }\n}\n","export class RequestError extends Error {\n constructor(error?: string) {\n super(\"Request error \" + error);\n }\n}\n","import { LyricLine, ParsedLyrics } from \"./types/Utils\";\n\n/**\n * Parses song lyrics into a structured format by removing metadata tags and separating lines.\n *\n * Example Input:\n * ```\n * [00:27.93] Listen to the wind blow\n * [00:30.88] Watch the sun rise\n * ```\n *\n * Example Output:\n * {\n * synced: [{ text: \"Listen to the wind blow\", startTime: 27930 }, { text: \"Watch the sun rise\", startTime: 30880 }],\n * unsynced: []\n * }\n *\n * @param lyrics - The raw lyrics string with optional tags and timestamps\n * @returns A ParsedLyrics object containing structured lyric data\n */\nfunction parseLocalLyrics(lyrics: string): ParsedLyrics {\n // Preprocess lyrics by removing [tags] (e.g., [artist:Name]) and trimming extra whitespace\n const lines = lyrics\n .replace(/\\[[a-zA-Z]+:.+\\]/g, \"\") // Removes metadata tags like [artist:Name]\n .trim()\n .split(\"\\n\"); // Splits the lyrics into an array of lines\n\n // Regular expressions for matching synced and karaoke timestamps\n const syncedTimestamp = /\\[([0-9:.]+)\\]/; // Matches [00:12.34]\n\n const unsynced: LyricLine[] = []; // Array to store unsynchronized lyrics\n const synced: LyricLine[] = []; // Array to store synchronized lyrics\n\n // Process each line to extract lyrics and timing information\n lines.forEach((line) => {\n // Match synchronized lyrics\n const syncMatch = line.match(syncedTimestamp);\n if (syncMatch) {\n const startTime = parseTime(syncMatch[1]);\n const text = line.replace(syncedTimestamp, \"\").trim();\n if (text) {\n synced.push({ text, startTime });\n }\n }\n // Add to unsynchronized lyrics if no timestamps are found\n else {\n const text = line.trim();\n if (text) {\n unsynced.push({ text });\n }\n }\n });\n\n return {\n synced: synced.length > 0 ? synced : null,\n unsynced,\n };\n}\n\n/**\n * Converts a timestamp string in the format \"mm:ss\" or \"mm:ss.SSS\" to a number in seconds.\n *\n * @param time - The timestamp string to parse\n * @returns The time in seconds as a number\n */\nfunction parseTime(time: string): number {\n const [minutes, seconds] = time.split(\":\").map(Number);\n return minutes * 60 + seconds;\n}\n\nexport { parseLocalLyrics, parseTime };\n","import { NoResultError, NotFoundError, RequestError } from \"./errors\";\nimport { ClientOptions } from \"./types/Client\";\nimport { FindLyricsResponse, Query, Search } from \"./types/Lyrics\";\nimport { LyricLine } from \"./types/Utils\";\nimport { parseLocalLyrics } from \"./utils\";\n\nexport class Client {\n private _url: string = \"https://lrclib.net/api\";\n private _key: string | undefined;\n /**\n * Creates a request client to api\n *\n * Example Usage;\n * ```typescript\n * const client = new Client();\n *\n * client.findLyrics({ track_name: \"The Chain\", artist_name: \"Fleetwood Mac\" }).then(console.log);\n * ```\n *\n * @notigorwastaken: I'm still working on it.\n *\n * @param options - An optional object containing Client Options\n * - `url`: The base URL, e.g. you can set up a custom url that uses another lrclib.net instance\n * - `key`: The token used to publish lyrics to the api. [click here for more info](https://lrclib.net/docs)\n */\n constructor(options?: ClientOptions) {\n this._url = options?.url || this._url;\n this._key = options?.key;\n }\n private async request(\n path: string,\n options?: RequestInit,\n ): Promise<Response> {\n return await fetch(this._url + path, options);\n }\n /**\n * Sends a request to the lyrics search API at https://lrclib.net/api/search.\n *\n * Example Usage:\n * ```typescript\n * const search = await searchLyrics({ query: \"The Chain\" });\n * ```\n *\n * @param info - An object containing search parameters:\n * - `query`: The search term (conditional | e.g., song title or lyrics fragment).\n * - `track_name`: The name of the track (conditional).\n * - `artist_name`: The artist's name (optional).\n * - `duration`: The song duration in milliseconds (optional).\n *\n * @returns A promise that resolves to an array of {@link FindLyricsResponse | FindLyricsResponse[]}.\n */\n public async searchLyrics(\n info: Search,\n options?: RequestInit,\n ): Promise<FindLyricsResponse[]> {\n const baseURL = \"/search\";\n const params = {\n q: info.query || \"\",\n track_name: info.track_name || \"\",\n artist_name: info.artist_name || \"\",\n duration: info.duration ? info.duration / 1000 : \"\",\n };\n const finalURL = `${baseURL}?${Object.entries(params)\n .filter(([_, value]) => value !== undefined && value !== \"\")\n .map(([key, value]) => `${key}=${encodeURIComponent(value as string)}`)\n .join(\"&\")}`;\n const response = await this.request(finalURL, options);\n\n if (!response.ok) {\n throw new RequestError();\n }\n const body = await response.json();\n\n if (!body) {\n throw new NoResultError();\n }\n\n return body;\n }\n\n /**\n * Finds lyrics for a given track using the API at https://lrclib.net/api/get.\n *\n * Example Usage:\n * ```typescript\n * const lyrics = await findLyrics({ track_name: \"The Chain\", artist_name: \"Fleetwood Mac\" });\n * ```\n *\n * @param info - An object containing query parameters:\n * - `id`: The unique identifier of the track (conditional).\n * - `track_name`: The name of the track (conditional).\n * - `artist_name`: The artist's name (conditional).\n * - `album_name`: The album's name (optional).\n * - `duration`: The song duration in milliseconds (optional).\n *\n * @returns A promise that resolves to a {@link FindLyricsResponse | FindLyricsResponse} object containing the track's lyrics.\n * @throws Will throw an error if the request fails or the track is not found.\n */\n public async findLyrics(\n info: Query,\n options?: RequestInit,\n ): Promise<FindLyricsResponse> {\n const parseID = info.id ? `/${info.id}` : \"?\";\n const baseURL = \"/get\" + parseID;\n const durr = info?.duration ? info.duration / 1000 : undefined;\n const params = {\n track_name: info.track_name || \"\",\n artist_name: info.artist_name || \"\",\n album_name: info.album_name || \"\",\n duration: durr || \"\",\n };\n\n const finalURL = `${baseURL}${Object.entries(params)\n .filter(([_, value]) => value !== undefined && value !== \"\")\n .map(([key, value]) => `${key}=${encodeURIComponent(value as string)}`)\n .join(\"&\")}`;\n\n const response = await this.request(finalURL, options);\n if (!response.ok && response.status === 404) {\n throw new NotFoundError();\n } else if (!response.ok && response.status !== 200) {\n throw new RequestError(response.statusText);\n }\n\n const body = await response.json();\n\n return body;\n }\n\n /**\n * Retrieves unsynchronized (plain) lyrics for a given track.\n *\n * Example Usage:\n * ```typescript\n * const unsyncedLyrics = await getUnsynced({ track_name: \"The Chain\", artist_name: \"Fleetwood Mac\" });\n * ```\n *\n * @param info - An object containing query parameters:\n * - `id`: The unique identifier of the track (conditional).\n * - `track_name`: The name of the track (conditional).\n * - `artist_name`: The artist's name (conditional).\n * - `album_name`: The album's name (optional).\n * - `duration`: The song duration in milliseconds (optional).\n *\n * @returns A promise that resolves to an array of {@link LyricLine | LyricLine[]} objects\n * containing unsynchronized lyrics or `null` if no lyrics are found.\n */\n public async getUnsynced(info: Query): Promise<LyricLine[] | null> {\n try {\n const body = await this.findLyrics(info);\n if (\"error\" in body) return null;\n\n const unsyncedLyrics = body?.plainLyrics;\n const isInstrumental = body.instrumental;\n if (isInstrumental) return [{ text: \"[Instrumental]\" }];\n\n if (!unsyncedLyrics) return null;\n\n return parseLocalLyrics(unsyncedLyrics).unsynced;\n } catch (e) {\n console.error(e);\n return null;\n }\n }\n\n /**\n * Retrieves synchronized (timed) lyrics for a given track.\n *\n * Example Usage:\n * ```typescript\n * const syncedLyrics = await getSynced({ track_name: \"The Chain\", artist_name: \"Fleetwood Mac\" });\n * ```\n *\n * @param info - An object containing query parameters:\n * - `id`: The unique identifier of the track (conditional).\n * - `track_name`: The name of the track (conditional).\n * - `artist_name`: The artist's name (conditional).\n * - `album_name`: The album's name (optional).\n * - `duration`: The song duration in milliseconds (optional).\n *\n * @returns A promise that resolves to an array of {@link LyricLine | LyricLine[]} objects\n * containing synchronized lyrics or `null` if no lyrics are found.\n */\n public async getSynced(info: Query): Promise<LyricLine[] | null> {\n try {\n const body = await this.findLyrics(info);\n const syncedLyrics = body?.syncedLyrics;\n const isInstrumental = body.instrumental;\n if (isInstrumental) return [{ text: \"[Instrumental]\" }];\n\n if (!syncedLyrics) return null;\n\n return parseLocalLyrics(syncedLyrics).synced;\n } catch (e) {\n console.error(e);\n return null;\n }\n }\n}\n"],"mappings":"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","names":[]}