Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,16 @@ out first, whether or not it breaks a compile.
release needs the `SIGNTOOL_PFX_BASE64` and `SIGNTOOL_PFX_PASSWORD` secrets:
without them the archives stop before they build and nothing is published,
though the image's build still runs.
- **Transcoding profiles** (#403, [RFC-027](docs/rfcs/RFC-027-transcoding-profiles.md)):
an account can say once what an app that does not ask should get, as a default
and per Subsonic client name, in Settings › Playback or through
`/api/v2/transcoding-profiles`. A request naming a format or a bit rate is
answered as before, and so is every request of an account with no profile.
Under one, a Subsonic `stream` or a native stream that names neither may now be
transcoded, a source at or under the bit rate excepted. Stream tickets, and so
the web player, downloads and public shares keep the original. An app that seeks
by byte range may fail to seek in a transcode still being encoded; a row of
Original for it puts it right.
- **The transcoding state in the web client's administration**: whether the
server can transcode (without it, listeners get the original files only),
the transcodes running against the global limit, the per-account limit and
Expand Down
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ The design decisions, one per document. A `Status` line never changes; the
| [RFC-024](rfcs/RFC-024-lyrics-stats-and-mixes.md) | LRCLIB lyrics, listening statistics, mixes and local similar songs |
| [RFC-025](rfcs/RFC-025-smart-playlists-and-podcasts.md) | Smart playlists and podcasts |
| [RFC-026](rfcs/RFC-026-no-jukebox.md) | No jukebox mode: why the server does not play on its own output |
| [RFC-027](rfcs/RFC-027-transcoding-profiles.md) | Transcoding profiles: per account and Subsonic client name |

## Plans and analyses

Expand Down
15 changes: 10 additions & 5 deletions docs/analyses/web-client-gap-analysis.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@
> decided against ([RFC-026](../rfcs/RFC-026-no-jukebox.md)). The client speaks
> nine languages since #391 and #392, all but English fetched on demand (#393).
> Every route of the "server knows, screen ignores" table is wired, the
> transcoding state last (#404). Still open, as an issue: per-account
> transcoding profiles (#403). **Amended 2026-11-01:** the web client follows
> the library event stream since #405; the user journal (`/api/v2/sync/*`) is
> still unread by it.
> transcoding state last (#404). Transcoding profiles, the gap of degree, are
> settled per account and per Subsonic client name
> ([RFC-027](../rfcs/RFC-027-transcoding-profiles.md), #403). **Amended
> 2026-11-01:** the web client follows the library event stream since #405; the
> user journal (`/api/v2/sync/*`) is still unread by it.

## What was found

Expand Down Expand Up @@ -103,7 +104,11 @@ format or bitrate profile. And **translation**: 34 languages against two.
> awaiting a native read (#406). Jukebox is #402, transcoding profiles #403.
>
> **State on 2026-11-02.** Jukebox is decided against, so the gap stays and is
> deliberate ([RFC-026](../rfcs/RFC-026-no-jukebox.md)).
> deliberate ([RFC-026](../rfcs/RFC-026-no-jukebox.md)). **Transcoding profiles
> are no longer a gap** ([RFC-027](../rfcs/RFC-027-transcoding-profiles.md),
> #403): an account sets a default and one row per Subsonic client name in
> Settings › Playback, for clients that do not ask. It stays narrower than
> Navidrome's on one point, deliberately: the web player keeps playing originals.

### What Navidrome does, the server already knows how to do, and the screen ignores

Expand Down
29 changes: 29 additions & 0 deletions docs/compatibility/subsonic-compatibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,35 @@ no podcast screen over Subsonic, so neither exercised the methods. Not
recorded: whether a channel's and an episode's `coverArt` show through
`getCoverArt`.

## Deviation for RFC-027: a silent request may be transcoded

An account may now set a transcoding profile
([RFC-027](../rfcs/RFC-027-transcoding-profiles.md)), through the native API and
Settings › Playback. The wire does not change: no parameter, method or field was
added. What changes is the answer to one request, for an account that set one.

- **A request that names neither a format nor a positive `maxBitRate`** may now be
answered with a transcode where it used to be answered with the original.
`maxBitRate=0` and an empty `format=` count as naming nothing. The
`Content-Type` says what was sent, as it always has.
- **Every other request is answered as before**: `format=raw`, any format named,
one this server cannot produce included (see RFC-022 above), any positive
`maxBitRate`, and `download`.
- **An account with no profile sees no change at all.** Neither does a source at or
under the profile's bit rate, which stays the original.
- **A profile's transcode takes the ceilings' slots**, and past them is answered
`429`. A client preloading the next track may meet that where it used to receive
the original.

**The limit.** A live transcode refuses a range past the first byte with `416`
until the encode is whole in the cache. So a client that does not ask and seeks by
byte range may fail to seek under a profile, where it worked on the original. An
override of `raw` for that client's name (`c`) puts it back on the original, and
Settings › Playback says so.

**No client has been run against this.** This section does not say which clients
are concerned, and the next campaign should find out.

## Correction: an unsynced lyrics line is an object in JSON

Found on 2026-10-27, while preparing the third client campaign, before any
Expand Down
34 changes: 34 additions & 0 deletions docs/guides/api-v2-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -699,6 +699,40 @@ two are indistinguishable from the rows alone.

See [RFC-007](../rfcs/RFC-007-library-event-stream.md) for the reasoning.

### Transcoding profiles

An account can say once what a client that does not ask should get
([RFC-027](../rfcs/RFC-027-transcoding-profiles.md)).
**A profile applies to `GET /api/v2/tracks/{id}/stream` and to Subsonic
`stream`, when the request names neither a format nor a bit rate**, and to nothing
else. Stream tickets, downloads and public shares keep the original, and a request
that names something gets what it named.

```bash
# The account's default
curl -X PUT "https://music.example.com/api/v2/transcoding-profiles" \
-H "Authorization: Bearer ACCESS_TOKEN" -H "Content-Type: application/json" \
-d '{"format": "opus", "bitrate": 128}'

# One Subsonic client on the original, under the name it sends as `c`
curl -X PUT "https://music.example.com/api/v2/transcoding-profiles" \
-H "Authorization: Bearer ACCESS_TOKEN" -H "Content-Type: application/json" \
-d '{"client": "Feishin", "format": "raw"}'
```

`PUT` replaces a whole row. `bitrate`, from 32 to 512, is a ceiling: a source at or
under it stays the original, and one of unknown bit rate is encoded. Left out, it
takes the format's default. `raw` is for a client's row only and takes no bit rate.
An account holds at most 32 rows, and a 33rd answers `409`. `GET` answers the
`profiles` and the `seen_clients`, the names this account's Subsonic clients have
streamed with without asking since the server started.
`DELETE /api/v2/transcoding-profiles?client=Feishin` removes a row, and without
`client` the default; it answers `204` whether or not the row was there.

A native client that names nothing and seeks by byte range will meet the `416` a
live transcode gives past the first byte, under an account with a default. Name a
format, `raw` included, if that is not what you want.

### When a transcode is refused

Transcoding is bounded twice: once for the whole server and once per account.
Expand Down
8 changes: 8 additions & 0 deletions docs/guides/subsonic-api-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,14 @@ Without an explicit format, `maxBitRate` is a ceiling: WaveFlow keeps the
original when its known bitrate fits and otherwise transcodes to MP3. Original
and completed cached responses support Range; live transcodes are chunked.

A request that names neither a format nor a positive `maxBitRate` is what the
account's transcoding profile applies to, if it set one
([RFC-027](../rfcs/RFC-027-transcoding-profiles.md)): a row for this client's `c`,
compared without ASCII case, or else the account's default. A source at or under
the profile's bit rate stays the original. The `Content-Type` names what was sent.
A client that seeks by byte range may fail to seek in a live transcode; a row of
`raw` for its name keeps it on the original.

## Implemented methods

Only the following methods are dispatched. Unknown methods answer HTTP `200`
Expand Down
Loading
Loading