# Provider

`@braccato/provider-blyrics` fetches lyrics from several sources in priority order and returns the first usable result. It includes ready-made providers for each endpoint of this API, parses each response with [`@braccato/parsers`](https://docs.betterlyrics.org/braccato/parsers), and hands back `Lyric[]` that you can give straight to the renderer.

Use it when you want fallback across providers (for example, TTML first, then QQ, then Kugou) without writing the fetch, parse and retry logic yourself.

## Install

```bash
npm i @braccato/provider-blyrics
```

## Quickstart

Create a `ProviderChain`, register providers, and call `fetchLyrics`. Each registration has a key, a display name, a sync type and the fetch function.

```javascript
import {
  ProviderChain,
  createBLyricsProvider,
  createPortatoProvider,
  createLegatoProvider,
} from "@braccato/provider-blyrics";

const chain = new ProviderChain();
chain.register({ key: "ttml", displayName: "TTML", syncType: "syllable", fetch: createBLyricsProvider() });
chain.register({ key: "qq", displayName: "QQ", syncType: "word", fetch: createPortatoProvider() });
chain.register({ key: "kugou", displayName: "Kugou", syncType: "line", fetch: createLegatoProvider() });

const controller = new AbortController();
const result = await chain.fetchLyrics({
  song: "Wanderlust",
  artist: "The Weeknd",
  album: "Kiss Land (Deluxe)",
  duration: 307,
  signal: controller.signal,
});

if (result) {
  view.lyrics = result.lyrics;
}
```

`duration` is in seconds, like the API's `d` parameter. The `signal` is required and goes in the same object. Aborting it stops the chain.

## Built-in providers

Each factory returns a fetch function. Every one accepts `apiUrl` and `timeout` options (the timeout defaults to 10 seconds).

| Factory | Source | Format |
| --- | --- | --- |
| `createBLyricsProvider` | This API, `/getLyrics` | TTML, syllable-synced |
| `createPortatoProvider` | This API, `/qq/getLyrics` | QRC, word-level. Passes song and artist to `parseQRC` to drop title lines |
| `createLegatoProvider` | This API, `/kugou/getLyrics` | LRC, line-synced |
| `createLRCLibSyncedProvider` | LRCLib | Synced LRC |
| `createLRCLibPlainProvider` | LRCLib | Plain text, unsynced |
| `createBinimumProvider` | BiniLyrics | TTML |
| `createUnisonProvider` | Unison | TTML, LRC or plain text. Matches on `videoId` when you pass one |

You can register your own provider too. A fetch function takes the context object and returns a result, or `null` when it has nothing.

## Order

Without a `priority` option, the chain tries providers by sync type: syllable, then word, then line, then unsynced. Pass `priority` to set the order yourself, and `disabled` to skip some:

```javascript
const result = await chain.fetchLyrics(context, {
  priority: ["ttml", "kugou", "qq"],
  disabled: new Set(["kugou"]),
});
```

The chain returns the first result with at least one line. A provider that throws is skipped, and the chain moves on. If no provider has lyrics, `fetchLyrics` returns `null`.

## Options

| Option | Description |
| --- | --- |
| `priority` | Provider keys in the order to try them |
| `disabled` | A `Set` of provider keys to skip |
| `validate` | Called with each result. Return `false` to reject it and try the next provider |
| `onProviderResult` | Called with the key and result (or `null`) of each provider tried |
| `onProviderError` | Called with the key and error when a provider throws |

## Result

| Field | Description |
| --- | --- |
| `lyrics` | The parsed `Lyric[]` |
| `source`, `sourceHref` | A display name and link for the source, for attribution |
| `language` | The lyrics language, when the source reports one |
| `cacheAllowed` | Whether the source allows you to cache the result |

## Validation

To avoid a wrong match, compare the result against text you already have. `createSimilarityValidator` rejects results whose text is less similar to the reference than the threshold (0 to 1, default 0.5):

```javascript
import { createSimilarityValidator } from "@braccato/provider-blyrics";

const validate = createSimilarityValidator(referenceText, 0.5);
const result = await chain.fetchLyrics(context, { validate });
```

## One chain per song

The chain caches each provider's result by provider key, not by song. Call `chain.clearCache()` before you fetch the next song, or create a new chain for each song. Otherwise the chain returns the previous song's lyrics.

Source: https://docs.betterlyrics.org/braccato/provider
