An mp3's recording id is in a frame ffprobe does not report #29

Closed
opened 2026-08-02 20:57:27 +02:00 by Claude · 1 comment
Collaborator

MusicBrainzRecording holds 3050 rows upstream and 1637 here. Every one of
the 1413 missing is an mp3; every flac, m4a and ogg has it.

Picard writes the recording id into the ID3 UFID frame — owner
http://musicbrainz.org, the mbid as the identifier — rather than into a text
frame, and ffprobe reports no UFID. Upstream reads the file a second time with
a tag library and takes it from there
(MediaBrowser.Providers/MediaInfo/AudioFileProber.cs, the
TryGetSanitizedUFIDFields branch); this port has only the prober, so the frame
never reaches it.

The text-tag half is already read: MUSICBRAINZ_TRACKID and MusicBrainz Track Id reach the row through MediaInfo::tags (jellyfin-app/src/scan/ingest.rs).
What is missing is a reader for the frame itself — jellyfin-matroska is the
precedent for reading a container ffprobe will not answer for.


Found by scanning /mnt/media (285 movies, 289 shows, 74 music artists —
~13 000 media files on a CIFS mount) with both this server and upstream
Jellyfin at the pinned commit, remote metadata and image fetchers disabled on
upstream so both were reading files only. Upstream was given the same mount
path so MD5(UTF-16LE(path)) agrees and rows compare directly.

`MusicBrainzRecording` holds **3050** rows upstream and 1637 here. Every one of the 1413 missing is an mp3; every flac, m4a and ogg has it. Picard writes the recording id into the ID3 `UFID` frame — owner `http://musicbrainz.org`, the mbid as the identifier — rather than into a text frame, and ffprobe reports no `UFID`. Upstream reads the file a second time with a tag library and takes it from there (`MediaBrowser.Providers/MediaInfo/AudioFileProber.cs`, the `TryGetSanitizedUFIDFields` branch); this port has only the prober, so the frame never reaches it. The text-tag half is already read: `MUSICBRAINZ_TRACKID` and `MusicBrainz Track Id` reach the row through `MediaInfo::tags` (`jellyfin-app/src/scan/ingest.rs`). What is missing is a reader for the frame itself — `jellyfin-matroska` is the precedent for reading a container ffprobe will not answer for. --- Found by scanning `/mnt/media` (285 movies, 289 shows, 74 music artists — ~13 000 media files on a CIFS mount) with both this server and upstream Jellyfin at the pinned commit, remote metadata and image fetchers disabled on upstream so both were reading files only. Upstream was given the same mount path so `MD5(UTF-16LE(path))` agrees and rows compare directly.
Author
Collaborator

Fixed in the merge of fix/29-ufid-recording.

The reader this needed arrived with #27: a track is opened a second time, as
upstream opens one, and what its tags hold reaches MediaInfo::written. The
recording id is one more key read from there — the frame a tagger writes it
into, which the prober reports nothing about.

The order is upstream's: the text tag names it where a tagger wrote one there,
and the frame answers for the file that has none. A tag present and holding
nothing is a tag naming nothing, so the frame answers for that too.

Measured

/mnt/media/music, against the upstream database for the same files:

upstream before after
MusicBrainzRecording rows 3050 1637 3051

and now the comparison worth having: no row where the value differs, and none
upstream has that this does not
. The one extra is a file upstream's own
parsing refuses — its UFID payload or its owner is not the exact shape
TryGetSanitizedUFIDFields insists on — where the reader here accepts it.

The mapping is pinned by a file rather than by a stub: the reference mp3, with
a real UFID frame written into a copy of it, read through the prober. The
same test asserts the prober still reports no such frame, so if ffmpeg ever
learns to, it says the second read is unnecessary rather than passing quietly.

Left for later

  • #40 — MusicBrainzTrack is 2939 against upstream's 3041. Not this frame:
    the release track id is a text tag, and the gap is somewhere else.
Fixed in the merge of `fix/29-ufid-recording`. The reader this needed arrived with #27: a track is opened a second time, as upstream opens one, and what its tags hold reaches `MediaInfo::written`. The recording id is one more key read from there — the frame a tagger writes it into, which the prober reports nothing about. The order is upstream's: the text tag names it where a tagger wrote one there, and the frame answers for the file that has none. A tag present and holding nothing is a tag naming nothing, so the frame answers for that too. ## Measured `/mnt/media/music`, against the upstream database for the same files: | | upstream | before | after | |---|---|---|---| | `MusicBrainzRecording` rows | 3050 | 1637 | 3051 | and now the comparison worth having: **no row where the value differs, and none upstream has that this does not**. The one extra is a file upstream's own parsing refuses — its `UFID` payload or its owner is not the exact shape `TryGetSanitizedUFIDFields` insists on — where the reader here accepts it. The mapping is pinned by a file rather than by a stub: the reference mp3, with a real `UFID` frame written into a copy of it, read through the prober. The same test asserts the prober still reports no such frame, so if ffmpeg ever learns to, it says the second read is unnecessary rather than passing quietly. ## Left for later - #40 — `MusicBrainzTrack` is 2939 against upstream's 3041. Not this frame: the release track id is a text tag, and the gap is somewhere else.
Sign in to join this conversation.
No description provided.