m5, subsonic/jellyfin support

This commit is contained in:
Boof2015
2026-06-27 22:03:37 -04:00
parent 0e3e46293c
commit 3ec3f5ecf2
37 changed files with 3322 additions and 35 deletions
+2
View File
@@ -34,6 +34,8 @@ export interface Track {
sourceId?: number;
sourceTrackId?: string;
sourcePath?: string;
/** Server cover-art id for remote tracks (resolved to a URL via remoteUrls). */
artworkSourceId?: string;
isAvailable?: boolean;
availabilityReason?: string;
}
+14 -2
View File
@@ -5,8 +5,8 @@ export type TrackSourceType = 'local' | 'subsonic' | 'jellyfin';
export interface DbTrack {
id: number;
path: string; // SAF document content:// URI for local tracks
folder_id: number;
path: string; // SAF content:// URI (local), or subsonic://|jellyfin:// identity URI (remote)
folder_id: number | null; // NULL for remote tracks (no SAF folder)
title: string;
artist: string;
album: string;
@@ -25,6 +25,12 @@ export interface DbTrack {
channels: number | null;
codec: string | null;
source_type: TrackSourceType;
// Remote-source linkage (NULL for local tracks). source_id -> remote_sources.id;
// source_track_id is the server's track id; artwork_source_id is its cover-art id.
source_id: number | null;
source_track_id: string | null;
source_path: string | null;
artwork_source_id: string | null;
file_name: string;
size: number | null;
mtime: number;
@@ -57,6 +63,12 @@ export interface Album {
track_count: number;
/** Newest track import timestamp in this album, used by Home recently-added. */
latest_added_at: number;
// Representative remote-source linkage (absent/NULL for local albums) so the album
// grid / detail can resolve a server cover-art URL when there's no cached artwork_hash.
// Optional so locally-derived album shapes (e.g. artist-detail aggregates) still fit.
source_type?: TrackSourceType;
source_id?: number | null;
artwork_source_id?: string | null;
}
export interface Artist {
+2
View File
@@ -15,6 +15,8 @@ export interface Playlist {
track_count: number;
/** Entries whose track is gone (folder removed / file deleted). */
missing_track_count: number;
/** Set when this playlist mirrors a remote server playlist (-> remote_sources.id). */
remote_source_id: number | null;
}
export interface PlaylistTrackEntry {
+125
View File
@@ -0,0 +1,125 @@
// Shared types for remote music sources (Subsonic / Jellyfin). The catalog-track
// and connection-config shapes are ported verbatim from desktop Astra
// (src/main/services/{subsonic,jellyfin}.ts) — they were already identical across
// both providers, so they unify here.
export type RemoteSourceType = 'subsonic' | 'jellyfin';
export type RemoteSourceStatus = 'unknown' | 'ok' | 'error' | 'disabled' | 'syncing';
/** Credentials needed to talk to a server. */
export interface RemoteConnectionConfig {
baseUrl: string;
username: string;
password: string;
}
/**
* One catalog track as produced by a client sync. Maps 1:1 onto the `tracks` table
* (plus remote-only id/path fields). Mirrors desktop `SubsonicCatalogTrack` /
* `JellyfinCatalogTrack`.
*/
export interface RemoteCatalogTrack {
/** Stable identity URI: `subsonic://{sourceId}/track/{id}` (stored as tracks.path). */
path: string;
source_track_id: string;
source_path: string | null;
artwork_source_id: string | null;
title: string;
artist: string;
album: string;
album_artist: string | null;
duration: number;
track_number: number | null;
disc_number: number | null;
year: number | null;
genre: string | null;
artwork_hash: string | null;
format: string;
sample_rate: number | null;
bit_depth: number | null;
bitrate: number | null;
channels: number | null;
codec: string | null;
codec_profile: string | null;
is_atmos_joc: number | null;
replaygain_track_gain_db: number | null;
replaygain_album_gain_db: number | null;
bpm: number | null;
musical_key: string | null;
}
/** Unified sync-progress event across providers. */
export interface RemoteSyncProgress {
phase: 'connecting' | 'artists' | 'albums' | 'tracks' | 'items' | 'playlists' | 'saving';
current: number;
total: number;
detail: string | null;
}
/** A track entry within a synced remote playlist. */
export interface RemotePlaylistTrack {
/** subsonic://|jellyfin:// identity URI (matches a tracks.path). */
path: string;
source_track_id: string;
title: string | null;
artist: string | null;
album: string | null;
}
/** A playlist as defined on the remote server. */
export interface RemotePlaylist {
source_playlist_id: string;
name: string;
tracks: RemotePlaylistTrack[];
}
/** A configured server, as stored in the `remote_sources` table (no secret). */
export interface RemoteSourceRow {
id: number;
type: RemoteSourceType;
name: string;
base_url: string;
username: string;
enabled: number; // 0 | 1
last_status: RemoteSourceStatus;
last_error: string | null;
last_sync_at: number | null;
last_checked_at: number | null;
/** Jellyfin-only cached auth (Subsonic re-derives a salted token per request). */
access_token: string | null;
user_id: string | null;
device_id: string | null;
created_at: number;
updated_at: number;
}
export interface RemoteSourceCreateInput {
type: RemoteSourceType;
name: string;
baseUrl: string;
username: string;
password: string;
enabled: boolean;
}
export interface RemoteSourceUpdateInput {
name?: string;
baseUrl?: string;
username?: string;
/** Only set when the user enters a new password. */
password?: string;
enabled?: boolean;
}
export interface RemoteSourceTestInput {
type: RemoteSourceType;
baseUrl: string;
username: string;
password: string;
}
export interface RemoteSourceTestResult {
ok: boolean;
message: string;
}