A scanned playlist is filed with no members and a null blob #230

Closed
opened 2026-09-16 01:13:37 +02:00 by Claude · 1 comment
Collaborator

#212 files a [playlist] directory and a loose .m3u as a Playlist row. The row is empty and its blob is null, where upstream's carries both its members and its own fields.

What is missing

  • The blob. crates/ephyra-core/src/item/write/blob.rs:30-110 has no arm for Kind::Playlist, so Data is NULL. Upstream serialises OwnerUserId, OpenAccess: true, Shares and PlaylistMediaType into it. OpenAccess is read back out of that blob in crates/ephyra-core/src/rows/item.rs:212.
  • The members. Upstream's playlist provider fills LinkedChildren from the .m3u or playlist.xml. Nothing in crates/ephyra-db/src/scan/ writes a manual link, so a scanned playlist stays empty.

What it costs

A user opening a scanned playlist sees nothing in it. Nothing leaks: library::views::openable judges a playlist outside the program data directory by its ancestors, so it is visible to whoever may see the library, which is what upstream's OpenAccess = true amounts to.

Why the answer key does not show it

Resolved compares neither Data nor type-0 links, so both the null blob and the empty membership pass. A whole-database diff shows them.

Verifying

Give the library-kinds tree a .m3u naming two files that exist and a [playlist] directory holding a couple, then compare the blob and the links against upstream's rows — which means extending what the key records, not only the tree.

Found reviewing #212.

#212 files a `[playlist]` directory and a loose `.m3u` as a `Playlist` row. The row is empty and its blob is null, where upstream's carries both its members and its own fields. ## What is missing - **The blob.** `crates/ephyra-core/src/item/write/blob.rs:30-110` has no arm for `Kind::Playlist`, so `Data` is NULL. Upstream serialises `OwnerUserId`, `OpenAccess: true`, `Shares` and `PlaylistMediaType` into it. `OpenAccess` is read back out of that blob in `crates/ephyra-core/src/rows/item.rs:212`. - **The members.** Upstream's playlist provider fills `LinkedChildren` from the `.m3u` or `playlist.xml`. Nothing in `crates/ephyra-db/src/scan/` writes a manual link, so a scanned playlist stays empty. ## What it costs A user opening a scanned playlist sees nothing in it. Nothing leaks: `library::views::openable` judges a playlist outside the program data directory by its ancestors, so it is visible to whoever may see the library, which is what upstream's `OpenAccess = true` amounts to. ## Why the answer key does not show it `Resolved` compares neither `Data` nor type-0 links, so both the null blob and the empty membership pass. A whole-database diff shows them. ## Verifying Give the library-kinds tree a `.m3u` naming two files that exist and a `[playlist]` directory holding a couple, then compare the blob and the links against upstream's rows — which means extending what the key records, not only the tree. Found reviewing #212.
Author
Collaborator

Fixed, and landed on main at 94f7a924.

A scanned playlist carries what its list names and the blob upstream writes for one. The answer key now records both, so the port is held to upstream's own rows rather than to our reading of the provider.

What the key settles:

  • A .m3u naming the same track twice — once through ../ — files it twice, as upstream does.
  • A name that resolves to a file no row was filed for is dropped, as is one pointing at nothing.
  • The media type inverts: a list that resolved members reads Unknown, because the provider builds a fresh playlist and its merge overwrites the resolver's Audio; a list that resolved none keeps Audio.
  • A list's Size is the document's own length, and a folder-shaped playlist has none.
  • Members are written as manual links, only when the list resolved something, and a rescan rebuilds rather than duplicates them.

The generator also records the text of every list, so our reader is held against the same bytes upstream read.

Two things the review caught, both fixed: the blob took its size from a field a folder kind can never carry, so Size was never written — the key had upstream's value all along; and the test compared a blob it built itself rather than the one a scan produces, which is why it could not see that. It asserts through the real write now, with a scan-level test beside it that reads the filed row's Data.

A member from a library upstream will not draw from is still kept here. Filed as #237.

Fixed, and landed on `main` at `94f7a924`. A scanned playlist carries what its list names and the blob upstream writes for one. The answer key now records both, so the port is held to upstream's own rows rather than to our reading of the provider. What the key settles: - A `.m3u` naming the same track twice — once through `../` — files it twice, as upstream does. - A name that resolves to a file no row was filed for is dropped, as is one pointing at nothing. - The media type inverts: a list that resolved members reads `Unknown`, because the provider builds a fresh playlist and its merge overwrites the resolver's `Audio`; a list that resolved none keeps `Audio`. - A list's `Size` is the document's own length, and a folder-shaped playlist has none. - Members are written as manual links, only when the list resolved something, and a rescan rebuilds rather than duplicates them. The generator also records the text of every list, so our reader is held against the same bytes upstream read. Two things the review caught, both fixed: the blob took its size from a field a folder kind can never carry, so `Size` was never written — the key had upstream's value all along; and the test compared a blob it built itself rather than the one a scan produces, which is why it could not see that. It asserts through the real write now, with a scan-level test beside it that reads the filed row's `Data`. A member from a library upstream will not draw from is still kept here. Filed as #237.
Sign in to join this conversation.
No description provided.