This commit is contained in:
Forrest 2026-07-29 22:41:38 -04:00
parent 981b4836f9
commit ed910748cb
34 changed files with 2054 additions and 153 deletions

View file

@ -1,6 +1,6 @@
import 'json_helpers.dart';
/// Subsonic domain models, ported from `ratune-subsonic/src/models.rs`.
/// Subsonic domain models, ported from `timbre-subsonic/src/models.rs`.
/// Nearly every field is nullable — servers omit fields freely. All parsing
/// goes through the defensive helpers in `json_helpers.dart`.
@ -242,7 +242,7 @@ class SearchResult3 {
}
/// A playlist summary from `getPlaylists` (no tracks), ported from
/// `ratune-subsonic/src/models.rs`. [toJson] backs the offline playlist mirror
/// `timbre-subsonic/src/models.rs`. [toJson] backs the offline playlist mirror
/// (`playlists/playlists.dart`).
class Playlist {
Playlist({
@ -253,6 +253,7 @@ class Playlist {
this.owner,
this.public,
this.coverArt,
this.comment,
});
final String id;
@ -263,6 +264,10 @@ class Playlist {
final bool? public;
final String? coverArt;
/// Free-text playlist comment. Timbre stores a marker here to flag a playlist
/// as a "tag" (see `playlists.dart`'s `kTagMarker`); otherwise usually null.
final String? comment;
factory Playlist.fromJson(Map<String, dynamic> j) => Playlist(
id: asString(j['id']) ?? '',
name: asString(j['name']) ?? 'Untitled',
@ -271,6 +276,7 @@ class Playlist {
owner: asString(j['owner']),
public: j['public'] is bool ? j['public'] as bool : null,
coverArt: asString(j['coverArt']),
comment: asString(j['comment']),
);
Map<String, dynamic> toJson() => {
@ -281,6 +287,7 @@ class Playlist {
if (owner != null) 'owner': owner,
if (public != null) 'public': public,
if (coverArt != null) 'coverArt': coverArt,
if (comment != null) 'comment': comment,
};
}
@ -293,6 +300,7 @@ class PlaylistDetail {
this.songCount,
this.duration,
this.coverArt,
this.comment,
this.songs = const [],
});
@ -301,6 +309,9 @@ class PlaylistDetail {
final int? songCount;
final int? duration;
final String? coverArt;
/// See [Playlist.comment] — carries the Timbre tag marker when present.
final String? comment;
final List<Song> songs;
factory PlaylistDetail.fromJson(Map<String, dynamic> j) => PlaylistDetail(
@ -309,6 +320,7 @@ class PlaylistDetail {
songCount: asInt(j['songCount']),
duration: asInt(j['duration']),
coverArt: asString(j['coverArt']),
comment: asString(j['comment']),
songs: oneOrMany(j['entry'], Song.fromJson),
);
@ -318,6 +330,7 @@ class PlaylistDetail {
if (songCount != null) 'songCount': songCount,
if (duration != null) 'duration': duration,
if (coverArt != null) 'coverArt': coverArt,
if (comment != null) 'comment': comment,
'entry': songs.map((s) => s.toJson()).toList(),
};
@ -327,5 +340,6 @@ class PlaylistDetail {
songCount: songCount ?? songs.length,
duration: duration,
coverArt: coverArt,
comment: comment,
);
}