|
| 1 | +# Nodelink-Only Features |
| 2 | + |
| 3 | +[Nodelink](https://nodelink.js.org) is a Lavalink-compatible server that Riffy treats as a superset of Lavalink. Riffy auto-detects it from `node.info.isNodelink` (populated from the `/info` response on WS `open`). Almost every Nodelink API is guarded so calling it on a plain Lavalink node throws a clear error. |
| 4 | + |
| 5 | +Detection helpers: `node.info?.isNodelink` (boolean), `node.mixer.check()`, `node.sponserBlock.check()`, `node.lyrics.checkAvailable(...)`. |
| 6 | + |
| 7 | +## Lyrics (`Node.lyrics`) |
| 8 | +- `lyrics.checkAvailable(eitherOne=true, ...plugins)` — verifies required plugins are present on the node (`lavalyrics-plugin`, `java-lyrics-plugin`, `lyrics`). Throws `RangeError` if missing and node not connected. |
| 9 | +- `lyrics.get(trackOrEncoded, skipTrackSource=false)` — v4 `/v4/lyrics?...`. |
| 10 | +- `lyrics.getCurrentTrack(guildId, skipTrackSource, plugin)` — fetches lyrics for the active track, with fallback URL logic depending on which lyric plugins the node exposes. |
| 11 | + |
| 12 | +These surface as `lyricsFound` / `lyricsNotFound` / `lyricsLine` events. |
| 13 | + |
| 14 | +## Audio Mixer (`Node.mixer`) |
| 15 | +Adds/removes/updates mix layers (overlapping audio) on a player — Nodelink's "Audio Mixer". |
| 16 | +- `mixer.addMixLayer(guildId, { track, volume })` — `POST /v4/.../mix`. |
| 17 | +- `mixer.getActiveMixLayers(guildId)` — `GET`. |
| 18 | +- `mixer.updateMixLayerVolume(guildId, mixId, volume)` — `PATCH` (volume 0–1). |
| 19 | +- `mixer.removeMixLayer(guildId, mixId)` — `DELETE`. |
| 20 | +- All throw if `!isNodelink`. Surfaced via `mixStarted` / `mixEnded` events. |
| 21 | + |
| 22 | +## SponsorBlock (`Node.sponsorBlock`) |
| 23 | +Nodelink's segment-skip feature. |
| 24 | +- `sponsorBlock.getCurrentBlock(guildId)` — `GET`. |
| 25 | +- `sponsorBlock.updateSettings(guildId, options)` — `PATCH` with strict validation (categories array of strings, optional actionTypes, non-negative `skipMarginMs`). |
| 26 | +- `sponsorBlock.setBlockSegments(guildId, segments)` — `PUT` (validates each segment: uuid, start/end, category, actionType, votes, locked, videoDuration, description). |
| 27 | +- `sponsorBlock.clearSponsorBlock(guildId)` — `DELETE`. |
| 28 | +- Surfaced via `sponsorBlockSegmentsLoaded` / `sponsorBlockSegmentSkipped` events. |
| 29 | + |
| 30 | +## Direct streaming & PCM (`Node`) |
| 31 | +Require corresponding server-side config flags on the Nodelink server. |
| 32 | +- `fetchTrackStream(encodedTrackStr, itag=null)` — `GET /v4/trackstream?...` returns the source audio URL (not the bytes). Needs `enableTrackStreamEndpoint`. |
| 33 | +- `fetchPCMStream(encodedTrackStr, volume, position, filters)` — `POST /v4/loadstream` returns a raw PCM `ReadableStream` for custom processing/recording. Needs `enableLoadStreamEndpoint`. Validates `volume` (0–1000), `position` (≥0). |
| 34 | +- `loadChapters(encodedTrackStr)` — `GET /v4/loadchapters?...` returns YouTube chapter markers. |
| 35 | + |
| 36 | +## Other Nodelink touches |
| 37 | +- **Player `sendNextTrack(track)`** — preloads next track for gapless playback (`{ nextTrack: { encoded } }`). |
| 38 | +- **Player `setFadings(enabled, fading)`** — high-fidelity volume fades (trackStart/trackEnd/trackStop/seek/ducking) with duration + curve. |
| 39 | +- **v4 voice payload `channelId`** — added in v1.0.12 (`Connection.checkAndSend`) for upcoming Nodelink v4.2.0. |
| 40 | + |
| 41 | +## Change guidance |
| 42 | +- Any new Nodelink method must: (1) live on `Node` (or `Player` for player-scoped ones), (2) guard with `this.info?.isNodelink` / `*.check()`, (3) throw `TypeError` for invalid args matching the existing strict validation style, and (4) map emitted events in `Player.handleEvent` and `RiffyEvents` (`index.d.ts`). |
| 43 | +- Nodelink search types use `searchType` (`track|album|playlist|artist`) in `Riffy.resolve`; the node filter `n.info.isNodelink` is applied when a `searchType` is requested. |
0 commit comments