The bug
Since #717, AudiobookFileService reads the registered file's size from the lease's metadata path (new FileInfo(metadataPath).Length). On Linux that path is a /proc/self/fd/<n> magic link, and FileSystemInfo stats the link inode itself — proc magic symlinks report a constant st_size of 64. Result: every file registered through a pinned lease persists Size = 64, regardless of the real file.
Live evidence from my instance (v1.3.0): 361 of 27,912 AudiobookFiles rows have Size = 64 exactly — every one created after the deploy that brought in #717 (sources: download, LibraryScan, MetadataRescan). The UI shows "0 KB" wherever sizes render (the duplicate-ASIN comparison table is where I caught it).
The fix
Open the metadata path and take the stream length. Opening follows the magic link to the exact pinned object the lease holds open, so the size is read from the real file without reintroducing the rename race the metadata path exists to avoid. Applied at both write sites:
AudiobookFileService.cs— initial registration (Size = nullwhen the open fails, as before)AudiobookFileService.PhysicalGeneration.cs— generation-replacement snapshots (falls back to the previously recorded size)
Notes
ExtractMetadataAsync's cache key usesLastWriteTimeUtcof the same metadata path, which has the same lstat behavior on Linux (the link's mtime, not the file's). It only weakens cache invalidation — the key still includes the physical object identity — so I left it out of scope here; happy to fold it in if you'd like.- Existing rows. Nothing in the tree repaired a row already at
Size = 64: a library scan only refreshes a file whose physical generation changed, and the metadata rescan only picked files missing duration/format/sample rate. A missing size, or one equal to the 64-byte sentinel, now counts as missing metadata, so the existing rescan job picks...
Automated Canary build