package dto // PublicSystemInfo is the unauthenticated handshake payload (GET /System/Info/Public). type PublicSystemInfo struct { LocalAddress string `json:"LocalAddress,omitempty"` ServerName string `json:"ServerName"` Version string `json:"Version"` ProductName string `json:"ProductName"` OperatingSystem string `json:"OperatingSystem,omitempty"` Id string `json:"Id"` StartupWizardCompleted bool `json:"StartupWizardCompleted"` } // SystemInfo is the authenticated variant (GET /System/Info). type SystemInfo struct { PublicSystemInfo HasPendingRestart bool `json:"HasPendingRestart"` IsShuttingDown bool `json:"IsShuttingDown"` SupportsLibraryMonitor bool `json:"SupportsLibraryMonitor"` CachePath string `json:"CachePath,omitempty"` } // EndPointInfo describes the caller's network location (GET /System/Endpoint). type EndPointInfo struct { IsLocal bool `json:"IsLocal"` IsInNetwork bool `json:"IsInNetwork"` } type NameGuidPair struct { Name string `json:"Name"` Id string `json:"Id"` } type UserItemDataDto struct { Rating *float64 `json:"Rating,omitempty"` PlaybackPositionTicks int64 `json:"PlaybackPositionTicks"` PlayCount int `json:"PlayCount"` IsFavorite bool `json:"IsFavorite"` Played bool `json:"Played"` Key string `json:"Key"` ItemId string `json:"ItemId,omitempty"` LastPlayedDate *string `json:"LastPlayedDate,omitempty"` Likes *bool `json:"Likes,omitempty"` } // UpdateUserItemDataDto is the POST .../UserData body; other Jellyfin fields are accepted and ignored. type UpdateUserItemDataDto struct { Rating *float64 `json:"Rating"` Likes *bool `json:"Likes"` IsFavorite *bool `json:"IsFavorite"` } type BaseItemDto struct { Name string `json:"Name"` ServerId string `json:"ServerId,omitempty"` Id string `json:"Id"` // PlaylistItemId identifies an entry within a playlist listing (GET /Playlists/{id}/Items), // distinct from Id so a song appearing more than once can be removed by occurrence // (DELETE .../Items?EntryIds=...) rather than by song id. PlaylistItemId string `json:"PlaylistItemId,omitempty"` Type string `json:"Type"` IsFolder bool `json:"IsFolder"` MediaType string `json:"MediaType,omitempty"` CollectionType string `json:"CollectionType,omitempty"` LocationType string `json:"LocationType,omitempty"` HasLyrics *bool `json:"HasLyrics,omitempty"` // ChannelId is always null for music, but Jellyfin emits it on every item and clients may require it. ChannelId *string `json:"ChannelId"` Tags []string `json:"Tags,omitzero"` SortName string `json:"SortName,omitempty"` Path string `json:"Path,omitempty"` ParentId string `json:"ParentId,omitempty"` RunTimeTicks int64 `json:"RunTimeTicks,omitempty"` IndexNumber *int `json:"IndexNumber,omitempty"` ParentIndexNumber *int `json:"ParentIndexNumber,omitempty"` ProductionYear *int `json:"ProductionYear,omitempty"` // PremiereDate is the ISO 8601 release date; Finamp sorts "Latest Releases" by it client-side. PremiereDate *string `json:"PremiereDate,omitempty"` // DateCreated is the ISO 8601 date the item was added to the library; clients show it as // "Date Added" and sort "Recently Added" by it. DateCreated string `json:"DateCreated,omitempty"` Album string `json:"Album,omitempty"` AlbumId string `json:"AlbumId,omitempty"` AlbumArtist string `json:"AlbumArtist,omitempty"` AlbumArtists []NameGuidPair `json:"AlbumArtists,omitempty"` AlbumPrimaryImageTag string `json:"AlbumPrimaryImageTag,omitempty"` Artists []string `json:"Artists,omitzero"` ArtistItems []NameGuidPair `json:"ArtistItems,omitempty"` Genres []string `json:"Genres,omitzero"` GenreItems []NameGuidPair `json:"GenreItems,omitzero"` Studios []NameGuidPair `json:"Studios,omitempty"` NormalizationGain *float64 `json:"NormalizationGain,omitempty"` AlbumNormalizationGain *float64 `json:"AlbumNormalizationGain,omitempty"` ChildCount *int `json:"ChildCount,omitempty"` SongCount *int `json:"SongCount,omitempty"` AlbumCount *int `json:"AlbumCount,omitempty"` ImageTags map[string]string `json:"ImageTags"` // ImageBlurHashes is keyed by image type (e.g. "Primary") then image tag. Finamp uses it as a // de-dup key for image downloads (and a placeholder); absent, it warns the server isn't // calculating blurhashes. ImageBlurHashes map[string]map[string]string `json:"ImageBlurHashes,omitempty"` // PrimaryImageAspectRatio is width/height of the Primary image, attached only when the request's // Fields asks for it; omitted rather than guessed, since a wrong ratio mis-shapes a placeholder. PrimaryImageAspectRatio *float64 `json:"PrimaryImageAspectRatio,omitempty"` BackdropImageTags []string `json:"BackdropImageTags"` UserData *UserItemDataDto `json:"UserData,omitempty"` MediaSources []MediaSourceInfo `json:"MediaSources,omitempty"` Container string `json:"Container,omitempty"` CanDownload bool `json:"CanDownload"` } // PlaylistUserPermissions is the response shape for GET /Playlists/{id}/Users(/{userId}), which // Finamp probes before allowing playlist edits. type PlaylistUserPermissions struct { UserId string `json:"UserId"` CanEdit bool `json:"CanEdit"` } // PlaylistInfo is the response shape for GET /Playlists/{id}. ItemIds are media item ids, not // playlist-entry ids (matching real Jellyfin); Finamp reads OpenAccess for the public-visibility toggle. type PlaylistInfo struct { OpenAccess bool `json:"OpenAccess"` Shares []PlaylistUserPermissions `json:"Shares"` ItemIds []string `json:"ItemIds"` } type QueryResult struct { Items []BaseItemDto `json:"Items"` TotalRecordCount int `json:"TotalRecordCount"` StartIndex int `json:"StartIndex"` } type UserDto struct { Name string `json:"Name"` ServerId string `json:"ServerId,omitempty"` ServerName string `json:"ServerName,omitempty"` Id string `json:"Id"` HasPassword bool `json:"HasPassword"` HasConfiguredPassword bool `json:"HasConfiguredPassword"` HasConfiguredEasyPassword bool `json:"HasConfiguredEasyPassword"` PrimaryImageTag string `json:"PrimaryImageTag,omitempty"` Policy *UserPolicy `json:"Policy,omitempty"` Configuration *UserConfiguration `json:"Configuration,omitempty"` } // UserPolicy mirrors real Jellyfin's User.Policy. Finamp reads it right after login and crashes if // it's absent, so every field must be present even though Navidrome lacks most of these concepts. type UserPolicy struct { IsAdministrator bool `json:"IsAdministrator"` IsHidden bool `json:"IsHidden"` EnableCollectionManagement bool `json:"EnableCollectionManagement"` EnableSubtitleManagement bool `json:"EnableSubtitleManagement"` EnableLyricManagement bool `json:"EnableLyricManagement"` IsDisabled bool `json:"IsDisabled"` BlockedTags []string `json:"BlockedTags"` AllowedTags []string `json:"AllowedTags"` EnableUserPreferenceAccess bool `json:"EnableUserPreferenceAccess"` AccessSchedules []string `json:"AccessSchedules"` BlockUnratedItems []string `json:"BlockUnratedItems"` EnableRemoteControlOfOtherUsers bool `json:"EnableRemoteControlOfOtherUsers"` EnableSharedDeviceControl bool `json:"EnableSharedDeviceControl"` EnableRemoteAccess bool `json:"EnableRemoteAccess"` EnableLiveTvManagement bool `json:"EnableLiveTvManagement"` EnableLiveTvAccess bool `json:"EnableLiveTvAccess"` EnableMediaPlayback bool `json:"EnableMediaPlayback"` EnableAudioPlaybackTranscoding bool `json:"EnableAudioPlaybackTranscoding"` EnableVideoPlaybackTranscoding bool `json:"EnableVideoPlaybackTranscoding"` EnablePlaybackRemuxing bool `json:"EnablePlaybackRemuxing"` ForceRemoteSourceTranscoding bool `json:"ForceRemoteSourceTranscoding"` EnableContentDeletion bool `json:"EnableContentDeletion"` EnableContentDeletionFromFolders []string `json:"EnableContentDeletionFromFolders"` EnableContentDownloading bool `json:"EnableContentDownloading"` EnableSyncTranscoding bool `json:"EnableSyncTranscoding"` EnableMediaConversion bool `json:"EnableMediaConversion"` EnabledDevices []string `json:"EnabledDevices"` EnableAllDevices bool `json:"EnableAllDevices"` EnabledChannels []string `json:"EnabledChannels"` EnableAllChannels bool `json:"EnableAllChannels"` EnabledFolders []string `json:"EnabledFolders"` EnableAllFolders bool `json:"EnableAllFolders"` InvalidLoginAttemptCount int `json:"InvalidLoginAttemptCount"` LoginAttemptsBeforeLockout int `json:"LoginAttemptsBeforeLockout"` MaxActiveSessions int `json:"MaxActiveSessions"` EnablePublicSharing bool `json:"EnablePublicSharing"` BlockedMediaFolders []string `json:"BlockedMediaFolders"` BlockedChannels []string `json:"BlockedChannels"` RemoteClientBitrateLimit int `json:"RemoteClientBitrateLimit"` AuthenticationProviderId string `json:"AuthenticationProviderId"` PasswordResetProviderId string `json:"PasswordResetProviderId"` SyncPlayAccess string `json:"SyncPlayAccess"` } // UserConfiguration mirrors real Jellyfin's User.Configuration. Like UserPolicy, clients expect it // always present, even though most settings don't apply to Navidrome's audio-only library. type UserConfiguration struct { PlayDefaultAudioTrack bool `json:"PlayDefaultAudioTrack"` SubtitleLanguagePreference string `json:"SubtitleLanguagePreference"` DisplayMissingEpisodes bool `json:"DisplayMissingEpisodes"` GroupedFolders []string `json:"GroupedFolders"` SubtitleMode string `json:"SubtitleMode"` DisplayCollectionsView bool `json:"DisplayCollectionsView"` EnableLocalPassword bool `json:"EnableLocalPassword"` OrderedViews []string `json:"OrderedViews"` LatestItemsExcludes []string `json:"LatestItemsExcludes"` MyMediaExcludes []string `json:"MyMediaExcludes"` HidePlayedInLatest bool `json:"HidePlayedInLatest"` RememberAudioSelections bool `json:"RememberAudioSelections"` RememberSubtitleSelections bool `json:"RememberSubtitleSelections"` EnableNextEpisodeAutoPlay bool `json:"EnableNextEpisodeAutoPlay"` CastReceiverId string `json:"CastReceiverId"` } // SessionInfo mirrors real Jellyfin's SessionInfoDto. JellyBox requires Id and PlayState, and Finamp // requires UserId, LastActivityDate and the bools, so none of those may be omitted. type SessionInfo struct { Id string `json:"Id"` UserId string `json:"UserId"` UserName string `json:"UserName"` Client string `json:"Client"` DeviceId string `json:"DeviceId"` DeviceName string `json:"DeviceName"` ApplicationVersion string `json:"ApplicationVersion"` ServerId string `json:"ServerId"` LastActivityDate string `json:"LastActivityDate"` IsActive bool `json:"IsActive"` SupportsMediaControl bool `json:"SupportsMediaControl"` SupportsRemoteControl bool `json:"SupportsRemoteControl"` HasCustomDeviceName bool `json:"HasCustomDeviceName"` PlayableMediaTypes []string `json:"PlayableMediaTypes"` SupportedCommands []string `json:"SupportedCommands"` AdditionalUsers []any `json:"AdditionalUsers"` NowPlayingQueue []any `json:"NowPlayingQueue"` PlayState PlayerStateInfo `json:"PlayState"` } type PlayerStateInfo struct { CanSeek bool `json:"CanSeek"` IsPaused bool `json:"IsPaused"` IsMuted bool `json:"IsMuted"` RepeatMode string `json:"RepeatMode"` PlaybackOrder string `json:"PlaybackOrder"` } type QuickConnectResult struct { Authenticated bool `json:"Authenticated"` Secret string `json:"Secret"` Code string `json:"Code"` DeviceId string `json:"DeviceId"` DeviceName string `json:"DeviceName"` AppName string `json:"AppName"` AppVersion string `json:"AppVersion"` DateAdded string `json:"DateAdded"` } type AuthenticationResult struct { User *UserDto `json:"User"` SessionInfo *SessionInfo `json:"SessionInfo"` AccessToken string `json:"AccessToken"` ServerId string `json:"ServerId"` } // MediaStream mirrors real Jellyfin's MediaStream. Finamp declares several bools as non-nullable, so // they must always be emitted (no omitempty). Finamp also does MediaStreams.firstWhere((s) => s.type // == 'Audio'), so MediaSourceInfo must include at least one Audio stream or that lookup throws. type MediaStream struct { Codec string `json:"Codec,omitempty"` Type string `json:"Type"` Index int `json:"Index"` BitRate int `json:"BitRate,omitempty"` Channels int `json:"Channels,omitempty"` SampleRate int `json:"SampleRate,omitempty"` ChannelLayout string `json:"ChannelLayout,omitempty"` IsInterlaced bool `json:"IsInterlaced"` IsDefault bool `json:"IsDefault"` IsForced bool `json:"IsForced"` IsExternal bool `json:"IsExternal"` IsTextSubtitleStream bool `json:"IsTextSubtitleStream"` SupportsExternalStream bool `json:"SupportsExternalStream"` } // MediaSourceInfo mirrors real Jellyfin's MediaSourceInfo. Finamp declares several bools/arrays as // non-nullable, so a missing field deserializes to null and throws a cast error that aborts parsing // of the whole item list; emit them always (no omitempty on bools). type MediaSourceInfo struct { Id string `json:"Id"` Path string `json:"Path,omitempty"` Protocol string `json:"Protocol"` Container string `json:"Container,omitempty"` TranscodingUrl string `json:"TranscodingUrl,omitempty"` TranscodingSubProtocol string `json:"TranscodingSubProtocol,omitempty"` Size int64 `json:"Size,omitempty"` Name string `json:"Name,omitempty"` IsRemote bool `json:"IsRemote"` RunTimeTicks int64 `json:"RunTimeTicks,omitempty"` Bitrate int `json:"Bitrate,omitempty"` SupportsTranscoding bool `json:"SupportsTranscoding"` SupportsDirectStream bool `json:"SupportsDirectStream"` SupportsDirectPlay bool `json:"SupportsDirectPlay"` Type string `json:"Type"` ReadAtNativeFramerate bool `json:"ReadAtNativeFramerate"` IgnoreDts bool `json:"IgnoreDts"` IgnoreIndex bool `json:"IgnoreIndex"` GenPtsInput bool `json:"GenPtsInput"` IsInfiniteStream bool `json:"IsInfiniteStream"` UseMostCompatibleTranscodingProfile bool `json:"UseMostCompatibleTranscodingProfile"` RequiresOpening bool `json:"RequiresOpening"` RequiresClosing bool `json:"RequiresClosing"` RequiresLooping bool `json:"RequiresLooping"` SupportsProbing bool `json:"SupportsProbing"` HasSegments bool `json:"HasSegments"` MediaStreams []MediaStream `json:"MediaStreams"` MediaAttachments []any `json:"MediaAttachments"` Formats []string `json:"Formats"` } type PlaybackInfoResponse struct { MediaSources []MediaSourceInfo `json:"MediaSources"` PlaySessionId string `json:"PlaySessionId"` } // LyricDto mirrors Jellyfin's GET /Audio/{itemId}/Lyrics response. Feishin and Jellify read only // Lyrics[].Text and Start (ticks); Metadata is for completeness/Finamp. type LyricDto struct { Metadata LyricMetadata `json:"Metadata"` Lyrics []LyricLine `json:"Lyrics"` } type LyricMetadata struct { Artist string `json:"Artist,omitempty"` Album string `json:"Album,omitempty"` Title string `json:"Title,omitempty"` Length int64 `json:"Length,omitempty"` Offset *int64 `json:"Offset,omitempty"` IsSynced bool `json:"IsSynced"` } type LyricLine struct { Text string `json:"Text"` Start *int64 `json:"Start,omitempty"` Cues []LyricLineCue `json:"Cues,omitempty"` } type LyricLineCue struct { Position int `json:"Position"` EndPosition int `json:"EndPosition"` Start int64 `json:"Start"` End *int64 `json:"End,omitempty"` } // QueryFiltersLegacy is the response for GET /Items/Filters. All four lists are always present; // clients (jellyfin-web) render each unconditionally. type QueryFiltersLegacy struct { Genres []string `json:"Genres"` Tags []string `json:"Tags"` OfficialRatings []string `json:"OfficialRatings"` Years []int `json:"Years"` }