lrclib-api
Version: 
The unofficial lrclib.net library for JS and TS
253 lines (249 loc) • 8.63 kB
JavaScript
var __async = (__this, __arguments, generator) => {
  return new Promise((resolve, reject) => {
    var fulfilled = (value) => {
      try {
        step(generator.next(value));
      } catch (e) {
        reject(e);
      }
    };
    var rejected = (value) => {
      try {
        step(generator.throw(value));
      } catch (e) {
        reject(e);
      }
    };
    var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
    step((generator = generator.apply(__this, __arguments)).next());
  });
};
// src/errors/NotFound.ts
var NotFoundError = class extends Error {
  constructor() {
    super("Track was not found");
  }
};
var NoResultError = class extends Error {
  constructor() {
    super("No result was found");
  }
};
// src/errors/RequestError.ts
var RequestError = class extends Error {
  constructor(error) {
    super("Request error " + error);
  }
};
// src/utils.ts
function parseLocalLyrics(lyrics) {
  const lines = lyrics.replace(/\[[a-zA-Z]+:.+\]/g, "").trim().split("\n");
  const syncedTimestamp = /\[([0-9:.]+)\]/;
  const unsynced = [];
  const synced = [];
  lines.forEach((line) => {
    const syncMatch = line.match(syncedTimestamp);
    if (syncMatch) {
      const startTime = parseTime(syncMatch[1]);
      const text = line.replace(syncedTimestamp, "").trim();
      if (text) {
        synced.push({ text, startTime });
      }
    } else {
      const text = line.trim();
      if (text) {
        unsynced.push({ text });
      }
    }
  });
  return {
    synced: synced.length > 0 ? synced : null,
    unsynced
  };
}
function parseTime(time) {
  const [minutes, seconds] = time.split(":").map(Number);
  return minutes * 60 + seconds;
}
// src/lyrics.ts
var Client = class {
  /**
   * Creates a request client to api
   *
   * Example Usage;
   * ```typescript
   * const client = new Client();
   *
   * client.findLyrics({ track_name: "The Chain", artist_name: "Fleetwood Mac" }).then(console.log);
   * ```
   *
   * @notigorwastaken: I'm still working on it.
   *
   * @param options - An optional object containing Client Options
   *  - `url`: The base URL, e.g. you can set up a custom url that uses another lrclib.net instance
   *  - `key`: The token used to publish lyrics to the api. [click here for more info](https://lrclib.net/docs)
   */
  constructor(options) {
    this._url = "https://lrclib.net/api";
    this._url = (options == null ? void 0 : options.url) || this._url;
    this._key = options == null ? void 0 : options.key;
  }
  request(path, options) {
    return __async(this, null, function* () {
      return yield fetch(this._url + path, options);
    });
  }
  /**
   * Sends a request to the lyrics search API at https://lrclib.net/api/search.
   *
   * Example Usage:
   * ```typescript
   * const search = await searchLyrics({ query: "The Chain" });
   * ```
   *
   * @param info - An object containing search parameters:
   *  - `query`: The search term (conditional | e.g., song title or lyrics fragment).
   *  - `track_name`: The name of the track (conditional).
   *  - `artist_name`: The artist's name (optional).
   *  - `duration`: The song duration in milliseconds (optional).
   *
   * @returns A promise that resolves to an array of {@link FindLyricsResponse | FindLyricsResponse[]}.
   */
  searchLyrics(info, options) {
    return __async(this, null, function* () {
      const baseURL = "/search";
      const params = {
        q: info.query || "",
        track_name: info.track_name || "",
        artist_name: info.artist_name || "",
        duration: info.duration ? info.duration / 1e3 : ""
      };
      const finalURL = `${baseURL}?${Object.entries(params).filter(([_, value]) => value !== void 0 && value !== "").map(([key, value]) => `${key}=${encodeURIComponent(value)}`).join("&")}`;
      const response = yield this.request(finalURL, options);
      if (!response.ok) {
        throw new RequestError();
      }
      const body = yield response.json();
      if (!body) {
        throw new NoResultError();
      }
      return body;
    });
  }
  /**
   * Finds lyrics for a given track using the API at https://lrclib.net/api/get.
   *
   * Example Usage:
   * ```typescript
   * const lyrics = await findLyrics({ track_name: "The Chain", artist_name: "Fleetwood Mac" });
   * ```
   *
   * @param info - An object containing query parameters:
   *  - `id`: The unique identifier of the track (conditional).
   *  - `track_name`: The name of the track (conditional).
   *  - `artist_name`: The artist's name (conditional).
   *  - `album_name`: The album's name (optional).
   *  - `duration`: The song duration in milliseconds (optional).
   *
   * @returns A promise that resolves to a {@link FindLyricsResponse | FindLyricsResponse} object containing the track's lyrics.
   * @throws Will throw an error if the request fails or the track is not found.
   */
  findLyrics(info, options) {
    return __async(this, null, function* () {
      const parseID = info.id ? `/${info.id}` : "?";
      const baseURL = "/get" + parseID;
      const durr = (info == null ? void 0 : info.duration) ? info.duration / 1e3 : void 0;
      const params = {
        track_name: info.track_name || "",
        artist_name: info.artist_name || "",
        album_name: info.album_name || "",
        duration: durr || ""
      };
      const finalURL = `${baseURL}${Object.entries(params).filter(([_, value]) => value !== void 0 && value !== "").map(([key, value]) => `${key}=${encodeURIComponent(value)}`).join("&")}`;
      const response = yield this.request(finalURL, options);
      if (!response.ok && response.status === 404) {
        throw new NotFoundError();
      } else if (!response.ok && response.status !== 200) {
        throw new RequestError(response.statusText);
      }
      const body = yield response.json();
      return body;
    });
  }
  /**
   * Retrieves unsynchronized (plain) lyrics for a given track.
   *
   * Example Usage:
   * ```typescript
   * const unsyncedLyrics = await getUnsynced({ track_name: "The Chain", artist_name: "Fleetwood Mac" });
   * ```
   *
   * @param info - An object containing query parameters:
   *  - `id`: The unique identifier of the track (conditional).
   *  - `track_name`: The name of the track (conditional).
   *  - `artist_name`: The artist's name (conditional).
   *  - `album_name`: The album's name (optional).
   *  - `duration`: The song duration in milliseconds (optional).
   *
   * @returns A promise that resolves to an array of {@link LyricLine | LyricLine[]} objects
   *          containing unsynchronized lyrics or `null` if no lyrics are found.
   */
  getUnsynced(info) {
    return __async(this, null, function* () {
      try {
        const body = yield this.findLyrics(info);
        if ("error" in body) return null;
        const unsyncedLyrics = body == null ? void 0 : body.plainLyrics;
        const isInstrumental = body.instrumental;
        if (isInstrumental) return [{ text: "[Instrumental]" }];
        if (!unsyncedLyrics) return null;
        return parseLocalLyrics(unsyncedLyrics).unsynced;
      } catch (e) {
        console.error(e);
        return null;
      }
    });
  }
  /**
   * Retrieves synchronized (timed) lyrics for a given track.
   *
   * Example Usage:
   * ```typescript
   * const syncedLyrics = await getSynced({ track_name: "The Chain", artist_name: "Fleetwood Mac" });
   * ```
   *
   * @param info - An object containing query parameters:
   *  - `id`: The unique identifier of the track (conditional).
   *  - `track_name`: The name of the track (conditional).
   *  - `artist_name`: The artist's name (conditional).
   *  - `album_name`: The album's name (optional).
   *  - `duration`: The song duration in milliseconds (optional).
   *
   * @returns A promise that resolves to an array of {@link LyricLine | LyricLine[]} objects
   *          containing synchronized lyrics or `null` if no lyrics are found.
   */
  getSynced(info) {
    return __async(this, null, function* () {
      try {
        const body = yield this.findLyrics(info);
        const syncedLyrics = body == null ? void 0 : body.syncedLyrics;
        const isInstrumental = body.instrumental;
        if (isInstrumental) return [{ text: "[Instrumental]" }];
        if (!syncedLyrics) return null;
        return parseLocalLyrics(syncedLyrics).synced;
      } catch (e) {
        console.error(e);
        return null;
      }
    });
  }
};
export {
  Client,
  NoResultError,
  NotFoundError,
  RequestError,
  parseLocalLyrics,
  parseTime
};
//# sourceMappingURL=index.mjs.map