npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

lyrics-lib

v2.0.0

Published

Async Node.js client for the public LRCLIB lyrics API. Fetches plain or synced lyrics by title (and optional artist).

Downloads

183

Readme

lyrics-lib

npm version Node Downloads License: MIT

Async Node.js client for the public LRCLIB lyrics API. Returns plain or synced lyrics for a track by title (and optional artist). Written in TypeScript with full .d.ts declarations.

Scope: Today, only the LRCLIB provider returns lyrics. Genius and Musixmatch ship as placeholder providers — registered and importable, but their fetchLyrics calls throw NotImplementedError until the real integrations land. See Providers.

Requirements

  • Node.js >= 18 (uses the global fetch)

Install

npm install lyrics-lib

Quick start

import { getLyrics, parseLyrics } from 'lyrics-lib';

const lyrics = await getLyrics({ title: 'Shape of You', artist: 'Ed Sheeran' });

if (lyrics) {
  console.log(lyrics);

  // Optional: split synced ([mm:ss.xx]) lines from plain lines
  const { synced, unsynced } = parseLyrics(lyrics);
  console.log(synced ?? unsynced);
} else {
  console.log('No lyrics found.');
}

API

getLyrics(options): Promise<string | null>

interface GetLyricsOptions {
  title: string;        // required
  artist?: string;      // recommended for accuracy
}
  • With artist: hits GET /api/get?track_name=&artist_name= first.
  • Without artist (or if the artist+title lookup misses): falls back to GET /api/search and uses the first result with no ranking — pass an artist when match precision matters.
  • Returns plainLyrics if available, else syncedLyrics, else null.
  • Throws LyricsLibError("Title is required") when title is empty.
  • Throws RequestError on transport failure or non-2xx HTTP responses (other than 404, which is treated as "no result" and returns null).

parseLyrics(raw): ParsedLyrics

Splits a raw LRC string into synced and unsynced lines.

interface ParsedLyrics {
  synced: { text: string; startTime: number }[] | null; // ms from start
  unsynced: { text: string }[];
}

synced is null when the input has no [mm:ss.xx] timestamps.

Errors

All library-thrown errors extend LyricsLibError (which extends Error).

| Class | When | | ---------------- | -------------------------------------------------- | | RequestError | Transport failure or non-2xx HTTP response | | NotFoundError | Reserved for callers preferring exception flow | | LyricsLibError | Base class — catch this to handle all of the above |

RequestError exposes .status (HTTP status, when applicable) and .cause (the underlying error, when applicable).

Providers

The library exposes a small provider abstraction so additional sources can be wired in without touching the public getLyrics surface.

import { providers, lrclibProvider, geniusProvider, musixmatchProvider, type LyricsProvider } from 'lyrics-lib';

// All registered providers, keyed by name
console.log(Object.keys(providers)); // ['lrclib', 'genius', 'musixmatch']

// Use a provider directly
const lyrics = await lrclibProvider.fetchLyrics({ title: 'Imagine', artist: 'John Lennon' });

| Provider | Status | Notes | | ---------------------- | -------------- | ---------------------------------------------------- | | lrclibProvider | ✅ implemented | Backs getLyrics. No API key. | | geniusProvider | 🟡 placeholder | Throws NotImplementedError until the API/scrape lands. | | musixmatchProvider | 🟡 placeholder | Throws NotImplementedError; will require an API key. |

The LyricsProvider interface is the contract — any future implementation just needs name: string and fetchLyrics(opts): Promise<string | null>. A NotImplementedError is thrown by placeholder providers and exposes .provider and .feature so a fallback chain can skip an unavailable source without losing diagnostic context.

getLyrics itself only consults LRCLIB today. A future minor release will add an opt-in multi-provider strategy without breaking existing callers.

How it picks lyrics

LRCLIB returns both plainLyrics and syncedLyrics per track when available. This library prefers plainLyrics — if you specifically want timestamped output, parse the result and check whether synced is non-null:

const raw = await getLyrics({ title, artist });
if (raw) {
  const { synced } = parseLyrics(raw);
  if (synced) {
    /* use timestamps */
  }
}

Development

npm install
npm run build       # dual build → dist/esm + dist/cjs (types + source maps)
npm run typecheck   # tsc --noEmit
npm run docs        # typedoc → docs/
npm test            # vitest

tsconfig.json emits the ESM build into dist/esm; tsconfig.cjs.json emits the CJS build into dist/cjs. Each includes its own .d.ts and source maps. prepublishOnly cleans and rebuilds before any npm publish.

Contributing

See CONTRIBUTING.md. Conventional Commits (feat:, fix:, docs:, refactor:, chore:).

License

MIT