One playlist. tracks are opaque refs — a filesystem path string for local
playlists, an item/video id for a server. Which source these belong to is
context (the active source the store was loaded for), not per-row state, so
there’s no source field and no local/server type split. The path↔file
conversion happens only at the player’s resolve boundary, not here.
The in-memory playlist read model for the active source (built by the DB
layer, never serialized). One uniform list — local vs server is the active
source context, not a per-row split.
User-supplied edits to a track’s tags. Empty strings / None mean
“remove this tag from the file”. Produced by the metadata editor UI and
consumed by crate::metadata::write_tags.
A source-agnostic artist photo reference: a local file path or a remote URL.
Resolved to a CoverUrl by the cover seam (server::cover::artist), so the
UI never branches on where the image lives. A custom user override is handled
separately (it’s a priority concern, not a source one).
Typed track identity — replaces the old Track.path synthetic-string hack.
Local tracks are a filesystem path; server tracks are a service + item id.
The cover reference is a separate Track.cover field, NOT part of identity.