# Option to revert to v14.x.x transcoding settings

**URL:** <http://support.symfonium.app/t/option-to-revert-to-v14-x-x-transcoding-settings/14944>\
**Category:** Feature requests\
**Tags:** handled\
**Created:** [September 5, 2026, 11:51pm UTC](http://support.symfonium.app/t/option-to-revert-to-v14-x-x-transcoding-settings/14944 "2026-09-05T23:51:51Z")\
**Posts on this page:** 1\
**Showing post:** 7

<div class="post-metadata">

**Author:** ![glennza](http://support.symfonium.app/user_avatar/support.symfonium.app/glennza/32/12235_2.png) [@glennza](http://support.symfonium.app/u/glennza)\
**Post date:** [September 9, 2026, 11:09am UTC](http://support.symfonium.app/t/option-to-revert-to-v14-x-x-transcoding-settings/14944/7 "2026-09-09T11:09:24Z")

</div>

It was on Github. Deluan had already fixed it before I asked. I guess he must have seen it here

Quote:

“Two fixes for the same report from the Symfonium author ([14877](https://support.symfonium.app/t/offline-files-does-not-skip-forward-or-back/14877/2), [14944](https://support.symfonium.app/t/option-to-revert-to-v14-x-x-transcoding-settings/14944)).”

> <https://github.com/navidrome/navidrome/pull/6105>
>
> \### Description
> 
> Two fixes for the same report from the Symfonium author (\[14877…\](https://support.symfonium.app/t/offline-files-does-not-skip-forward-or-back/14877/2), \[14944\](https://support.symfonium.app/t/option-to-revert-to-v14-x-x-transcoding-settings/14944)).
> 
> \*\*A source already in the forced format was re-encoded to itself.\*\* \`ClientInfo.ForceFormat\` cleared \`DirectPlayProfiles\` unconditionally, so a player set to "transcode to FLAC" made a FLAC source transcode flac to flac. Only the new \`getTranscodeDecision\` path is affected; the legacy \`/rest/stream\` path already synthesises a direct-play profile in \`applyServerOverride\`, so the same setup direct-plays there. \`ForceFormat\` now rebuilds direct play from the matching transcoding profiles instead of dropping them, on the grounds that a client declaring a transcoding profile for a format can play that format.
> 
> Restoring direct play needs a bitrate ceiling, since \`DirectPlayProfile\` has no bitrate field and \`MaxAudioBitrate\` is the only thing that keeps an over-bitrate source out of the shortcut. \`GetTranscodeDecision\` now takes that ceiling from the transcoding row's \`DefaultBitRate\` when a format was forced, with the player's \`MaxBitRate\` still winning. That also closes a gap where the new endpoint ignored \`DefaultBitRate\` entirely, so an mp3 320 on a player forced to mp3@192 was served at 320 while the legacy path gave 192.
> 
> \*\*Piped FLAC transcodes were unseekable.\*\* The FLAC muxer writes STREAMINFO before it knows the length, then rewinds at the end to fill in \`total\_samples\`. We pipe ffmpeg's stdout, which is not seekable, so ffmpeg logs \`unable to rewrite FLAC header\` and the field stays 0. Decoders need it to turn a timestamp into a byte offset. Streaming hides this because the client re-requests with a new offset each time, but a downloaded copy can never be seeked, which is what was reported.
> 
> \`Transcode\` now wraps its own output and rewrites \`total\_samples\` as the first 26 bytes flow past. This lives in \`core/ffmpeg\` because the unseekable pipe is that package's doing, since \`buildDynamicArgs\` is what appends the trailing \`-\`. \`core/stream\` only picks a target format and hands back an \`io.ReadCloser\`, so compensating there leaked a transcoder detail one layer up. \`TranscodeOptions\` grows a \`Duration\` field next to the existing \`Offset\`, which also puts the duration-minus-offset arithmetic in the same function that emits \`-ss\`. The sample rate comes from the header ffmpeg just wrote rather than from the transcode options, so a resampled (\`-ar\`) output still gets the right count. It runs on every transcode instead of only FLAC targets, because a transcoding row's target format is a declared value that nothing checks against the command's actual \`-f\`. The magic-byte test inside the wrapper is the real gate and costs a 26-byte peek.
> 
> Both are in one PR because they are one report. The first stops the pointless re-encode; only the second makes a legitimate FLAC transcode seekable offline.
> 
> \### Type of Change
> \- \[x\] Bug fix
> \- \[\] New feature
> \- \[\] Documentation update
> \- \[\] Refactor
> \- \[\] Other (please describe):
> 
> \### Checklist
> Please review and check all that apply:
> 
> \- \[x\] My code follows the project’s coding style
> \- \[x\] I have tested the changes locally
> \- \[\] I have added or updated documentation as needed
> \- \[x\] I have added tests that prove my fix/feature works (or explain why not)
> \- \[x\] All existing and new tests pass
> 
> \### How to Test
> 
> Set a player's Transcoding to \`flac\`, then call \`getTranscodeDecision\` for a FLAC source. Before, you get \`canDirectPlay: false\` and a \`flac/flac\` transcode stream; after, \`canDirectPlay: true\`. Switch the player to \`mp3\` (192) and check that an mp3 320 still transcodes to 192 while an mp3 128 direct-plays.
> 
> FLAC seekability needs a lossless source, since lossy to lossless is rejected during negotiation. Drop a WAV in the library, then:
> 
> \`\`\`
> curl -o out.flac 'http://localhost:4533/rest/stream.view?...&id=\<wav-id\>&format=flac'
> ffprobe -v error -show\_entries format=duration -of default=nw=1:nk=1 out.flac
> \`\`\`
> 
> \`N/A\` before, the real duration after. Clear \`data/cache/transcoding\` between runs or the second request is a cache hit.
> 
> I tested both on a dev server, building a separate binary per arm. On a 177s source, \`total\_samples\` was 0 and the duration \`N/A\` before; after, 7807023 and 177.03s, with the audio bytes identical and \`timeOffset=60\` giving 117.03s.
> 
> \### Additional Notes
> 
> \`total\_samples\` comes from the stored track duration, which is held at limited precision, so it can be off by a couple of hundred samples on a long track (0.003s on that 177s file). The displayed duration shifts slightly. Seeking is unaffected.
> 
> The first fix leaves \`/rest/stream\` and \`/rest/download\` alone, since \`ForceFormat\` has one caller. The second does reach them, because every transcoded stream goes through the same \`MediaStreamer\` cache loader, including the zip download in \`core/archiver.go\`, which is the offline case in the report.
> 
> The wiring is covered by two specs in \`core/ffmpeg/ffmpeg\_test.go\` that run a real ffmpeg. Those skip when ffmpeg is missing, and the CI runner does not have it, so in CI the wiring line itself is unverified while the 15 unit tests on the patcher still run. I checked both specs bite: removing the wrapper fails both, and dropping the offset subtraction fails one.
> 
> One judgement call worth a second opinion. On the new endpoint, forcing a format now makes \`DefaultBitRate\` a cap rather than a default. That matches \`applyServerOverride\` on the legacy path, and it is what keeps an over-bitrate source out of the restored direct-play shortcut, but a client advertising a higher limit no longer overrides the admin's per-player setting.

---

_[View the full topic](http://support.symfonium.app/t/option-to-revert-to-v14-x-x-transcoding-settings/14944)._
