package api // Wire shapes for /api/library/sync upserts. The sqlc-generated row // structs (dbq.Artist, dbq.Album, dbq.Track, dbq.Playlist) have no // JSON tags (sqlc.yaml: emit_json_tags=false), so a raw json.Marshal // of them produces PascalCase field names that the Flutter sync // controller (which reads snake_case keys) can't parse. // // These view structs add the JSON tag layer + flatten pgtype.UUID and // pgtype.Timestamptz / Date into strings, mirroring the pattern // playlistRowView already established for /api/playlists. // // Field names match flutter_client/lib/cache/sync_controller.dart's // _*FromJson helpers exactly. Adding a field server-side requires a // matching read in the Flutter helper or it'll be silently dropped. import ( "git.fabledsword.com/bvandeusen/minstrel/internal/db/dbq" syncpkg "git.fabledsword.com/bvandeusen/minstrel/internal/sync" ) type artistSyncView struct { ID string `json:"id"` Name string `json:"name"` SortName string `json:"sort_name"` Mbid *string `json:"mbid"` ArtistThumbPath *string `json:"artist_thumb_path"` ArtistFanartPath *string `json:"artist_fanart_path"` } func toArtistSyncView(a dbq.Artist) artistSyncView { return artistSyncView{ ID: syncpkg.FormatUUID(a.ID), Name: a.Name, SortName: a.SortName, Mbid: a.Mbid, ArtistThumbPath: a.ArtistThumbPath, ArtistFanartPath: a.ArtistFanartPath, } } type albumSyncView struct { ID string `json:"id"` ArtistID string `json:"artist_id"` Title string `json:"title"` SortTitle string `json:"sort_title"` ReleaseDate *string `json:"release_date"` CoverArtPath *string `json:"cover_art_path"` Mbid *string `json:"mbid"` } func toAlbumSyncView(a dbq.Album) albumSyncView { var releaseDate *string if a.ReleaseDate.Valid { s := a.ReleaseDate.Time.Format("2006-01-02") releaseDate = &s } return albumSyncView{ ID: syncpkg.FormatUUID(a.ID), ArtistID: syncpkg.FormatUUID(a.ArtistID), Title: a.Title, SortTitle: a.SortTitle, ReleaseDate: releaseDate, CoverArtPath: a.CoverArtPath, Mbid: a.Mbid, } } type trackSyncView struct { ID string `json:"id"` AlbumID string `json:"album_id"` ArtistID string `json:"artist_id"` Title string `json:"title"` DurationMs int32 `json:"duration_ms"` TrackNumber *int32 `json:"track_number"` DiscNumber *int32 `json:"disc_number"` FilePath string `json:"file_path"` FileFormat string `json:"file_format"` Genre *string `json:"genre"` } func toTrackSyncView(t dbq.Track) trackSyncView { return trackSyncView{ ID: syncpkg.FormatUUID(t.ID), AlbumID: syncpkg.FormatUUID(t.AlbumID), ArtistID: syncpkg.FormatUUID(t.ArtistID), Title: t.Title, DurationMs: t.DurationMs, TrackNumber: t.TrackNumber, DiscNumber: t.DiscNumber, FilePath: t.FilePath, FileFormat: t.FileFormat, Genre: t.Genre, } } type playlistSyncView struct { ID string `json:"id"` UserID string `json:"user_id"` Name string `json:"name"` Description string `json:"description"` IsPublic bool `json:"is_public"` CoverPath *string `json:"cover_path"` TrackCount int32 `json:"track_count"` DurationSec int32 `json:"duration_sec"` SystemVariant *string `json:"system_variant"` } func toPlaylistSyncView(p dbq.Playlist) playlistSyncView { return playlistSyncView{ ID: syncpkg.FormatUUID(p.ID), UserID: syncpkg.FormatUUID(p.UserID), Name: p.Name, Description: p.Description, IsPublic: p.IsPublic, CoverPath: p.CoverPath, TrackCount: p.TrackCount, DurationSec: p.DurationSec, SystemVariant: p.SystemVariant, } }