Skip to content

Give OpenSubsonic one canonical song projection and renderer #744

Description

@forkwright

Finding

The OpenSubsonic implementation defines the same database projection type and response conversion repeatedly across browsing, list, search, and playlist endpoints. Four private SongRow structs are field-for-field identical, their SQL repeats the same song/album/artist projection and joins with endpoint-specific predicates layered around it, and two endpoint modules contain byte-identical song-to-XML/JSON render loops.

These are all one OpenSubsonic representation of one Harmonia song; there is no trust boundary or protocol distinction between the copies.

Verified against main 65644113e47814c87eaba14a6f322820974b8780.

Evidence

  • crates/paroche/src/subsonic/browsing.rs:35-46, lists.rs:63-74, search.rs:52-63, and playlists.rs:81-92 each define the same SongRow { id, title, position, duration_ms, codec, album_id, album_title, year, artist_id, artist_name }.
  • The associated queries repeatedly project those exact columns from songs/tracks joined to albums and artists; only the endpoint filter/order/limit changes.
  • crates/paroche/src/subsonic/lists.rs:336-378 (build_song_lists) and playlists.rs:559-601 (build_songs) are byte-equivalent conversions: UUID conversion, optional artist handling, milliseconds→seconds, codec content type/suffix, song_xml_elem, and song_json.
  • AlbumRow is likewise repeated across browsing/list/search with the same OpenSubsonic-facing role.

No open issue currently owns this OpenSubsonic projection/serialization duplication.

Why this matters

The duplicated structs encode the expected SQL row shape, while the duplicated renderer encodes the public wire representation. Adding or correcting one projected field—disc number, bitrate, content type, cover art, year, artist handling, duration semantics, or a future OpenSubsonic field—must currently be discovered and updated in several modules.

That creates endpoint-dependent protocol behavior from one database record. The exact duplicate renderer already demonstrates that there is no endpoint-specific function being preserved by the copies.

Desired correction

Create one internal OpenSubsonic song projection with one canonical SQL select fragment/query builder or repository function for the shared joins/columns, while allowing each endpoint to supply its own predicate/order/pagination. Give that projection one conversion into the existing XML/JSON song representation.

Apply the same treatment to AlbumRow where the row really is identical, without turning all Subsonic queries into a generic query framework.

Done when:

  • SongRow has one definition under the OpenSubsonic surface;
  • shared song/album/artist column selection and joins have one owner;
  • list, playlist, browse, and search endpoints express only their true filtering/order differences;
  • one renderer converts the canonical song projection to XML/JSON; and
  • a shared fixture proves the same song produces identical wire fields regardless of which endpoint returned it.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    refactorCode quality and architecture improvementstech-debtTechnical debt and cleanup

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions