navidrome/core
Deluan 89026012ab fix(transcoding): make piped FLAC transcodes seekable
The FLAC muxer writes STREAMINFO before it knows the stream length, then
rewinds at the end to fill total_samples in. Navidrome pipes ffmpeg's stdout
(-f flac -), which is not seekable, so ffmpeg logs "unable to rewrite FLAC
header" and the field stays 0. A decoder needs total_samples to turn a
timestamp into a byte offset, so it reports an unknown duration and refuses to
seek. Online playback hides this because the client re-requests with a new
offset each time, but an offline copy is permanently unseekable, the symptom
reported against Symfonium where seeking a downloaded track jumps back to the
start.

Transcode now wraps its own output and rewrites total_samples as the first
bytes flow past. This lives in core/ffmpeg because the unseekable pipe is that
package's doing: buildDynamicArgs is what appends the trailing '-'. core/stream
only learns a target format and hands back an io.ReadCloser, so compensating
there leaked a transcoder implementation detail one layer up. TranscodeOptions
grows a Duration field alongside the existing Offset, which also puts the
duration-minus-offset arithmetic in the same function that emits -ss.

The wrapper runs on every transcode rather than only FLAC targets: the format
on a transcoding row is a declared target that nothing validates against the
command's actual -f, so a custom command can emit FLAC under any target_format.
The magic-byte check inside the wrapper is the authoritative test and costs a
26-byte peek. The output sample rate is read back out of the header ffmpeg just
wrote rather than taken from the transcode options, so a resampled (-ar) output
still gets the right count. Anything that is not a FLAC stream with an unset
total_samples passes through byte for byte.

Measured on a 177s source: before, total_samples=0 and ffprobe reported
duration N/A; after, total_samples=7807023 and duration 177.03s, with the audio
payload byte-identical. This affects every piped FLAC regardless of the source
format; only FLAC stores an authoritative "unknown", which is why mp3, opus
and aac survive the same pipe.

No SEEKTABLE is synthesised and the MD5 is left zero: both are optional, and
decoders binary-search using total_samples alone.
2026-09-07 16:47:42 -04:00
..
agents feat(plugins): surface the valid agent names in logs and the Plugins UI (#5910) 2026-08-30 11:11:35 -04:00
artwork fix(artwork): never retry absent artwork on its own (#6054) 2026-09-01 20:48:41 -04:00
auth feat(jellyfin): non-expiring, audience-scoped tokens revocable by password change (#6013) 2026-08-22 20:36:24 -04:00
external chore(deps): upgrade to Go 1.27 (#5990) 2026-08-30 12:43:15 -04:00
ffmpeg fix(transcoding): make piped FLAC transcodes seekable 2026-09-07 16:47:42 -04:00
lyrics fix(scanner): stop logging expected lyrics sniff misses as warnings (#5702) 2026-07-02 09:46:57 -04:00
matcher feat(listenbrainz): match collaboration top-songs via all credited artist MBIDs (#5670) 2026-06-26 17:06:14 -04:00
metrics fix(plugins): build public URLs on the caller's address instead of localhost (#6059) 2026-08-31 21:27:43 -04:00
playback chore(deps): upgrade to Go 1.27 (#5990) 2026-08-30 12:43:15 -04:00
playlists perf(scanner): improve playlist importing on large libraries (#6055) 2026-08-30 22:17:25 -04:00
publicurl fix(plugins): build public URLs on the caller's address instead of localhost (#6059) 2026-08-31 21:27:43 -04:00
scrobbler fix(deezer): treat an exhausted quota as a throttle, not as a missing artist (#6068) 2026-09-01 17:17:52 -04:00
sonic feat(jellyfin): AudioMuse-AI compatible sonic endpoints (#5782) 2026-07-15 20:44:56 -04:00
storage fix(scanner): read file birth time via statx on Linux (#6046) 2026-08-29 16:36:08 -04:00
stream fix(transcoding): make piped FLAC transcodes seekable 2026-09-07 16:47:42 -04:00
archiver.go feat(artist): add Share and Download actions to the Artist detail page (#5944) 2026-08-12 11:59:56 -04:00
archiver_test.go feat(artist): add Share and Download actions to the Artist detail page (#5944) 2026-08-12 11:59:56 -04:00
common.go feat(bfr): Big Refactor: new scanner, lots of new fields and tags, improvements and DB schema changes (#2709) 2025-02-19 20:35:17 -05:00
common_test.go test: unskip path-separator tests on Windows (#5381) (#5916) 2026-08-19 11:50:32 -04:00
core_suite_test.go Rename log.LevelCritical to log.LevelFatal 2022-12-21 14:53:36 -05:00
inspect.go refactor: multiple syntax updates for Go 1.26 2026-05-19 18:02:36 -03:00
library.go fix(nativeapi): stop partial PUTs from clearing untouched columns (#6058) 2026-08-31 11:21:32 -04:00
library_test.go fix(nativeapi): stop partial PUTs from clearing untouched columns (#6058) 2026-08-31 11:21:32 -04:00
maintenance.go refactor: run Go modernize (#5002) 2026-02-08 09:57:30 -05:00
maintenance_test.go feat(scanner): implement selective folder scanning and file system watcher improvements (#4674) 2025-11-14 22:15:43 -05:00
players.go feat(server): group Subsonic config options together 2025-03-05 12:29:30 -08:00
players_test.go Use userId in player, other fixes (#3182) 2024-08-03 13:37:21 -04:00
share.go refactor: simplify return statements 2026-08-30 12:44:09 -04:00
share_test.go refactor: extract TruncateRunes function for safe string truncation with suffix 2025-11-06 14:27:38 -05:00
user.go refactor: run Go modernize (#5002) 2026-02-08 09:57:30 -05:00
user_test.go feat(plugins): New Plugin System with multi-language PDK support (#4833) 2026-01-14 19:22:48 -05:00
wire_providers.go feat(artwork): new artwork pipeline with background resolution and Low Quality Image Placeholders (#5847) 2026-08-09 15:03:27 -04:00