summaryrefslogtreecommitdiff
path: root/player-android/lib
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-05-20 14:05:21 +0300
committerPaul Buetow <paul@buetow.org>2026-05-20 14:05:21 +0300
commitb7b2d404d035f4d83a5f3a2dc66055551dd022d3 (patch)
treefee3ac53bab0ed6fa294bb90334022393e97e624 /player-android/lib
parent67c90f9b9a7a861e0f6ab0f199d66cd411948fab (diff)
Fix PlayerApiClient const constructor and Media.tags deserialization (d9+e9)
- Remove const from PlayerApiClient constructor (Uri is not const-constructable) - Replace .cast<String>() with .whereType<String>().toList() in Media.fromJson to silently drop non-string and null tag elements instead of throwing TypeError - Add regression test: tags [1, 'valid', null] deserialized as ['valid'] Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Diffstat (limited to 'player-android/lib')
-rw-r--r--player-android/lib/api/player_api_client.dart4
-rw-r--r--player-android/lib/models/media.dart5
2 files changed, 7 insertions, 2 deletions
diff --git a/player-android/lib/api/player_api_client.dart b/player-android/lib/api/player_api_client.dart
index 3031c73..eb438f4 100644
--- a/player-android/lib/api/player_api_client.dart
+++ b/player-android/lib/api/player_api_client.dart
@@ -6,7 +6,9 @@ class PlayerApiClient {
final Uri baseUrl;
final String bearerToken;
- const PlayerApiClient({required this.baseUrl, required this.bearerToken});
+ // Normal (non-const) constructor: Uri is not const-constructable, so the
+ // constructor must not be declared const.
+ PlayerApiClient({required this.baseUrl, required this.bearerToken});
Future<User> bootstrap({required String username, required String password}) => throw UnimplementedError();
Future<User> login({required String username, required String password}) => throw UnimplementedError();
diff --git a/player-android/lib/models/media.dart b/player-android/lib/models/media.dart
index 7330bca..19c57b8 100644
--- a/player-android/lib/models/media.dart
+++ b/player-android/lib/models/media.dart
@@ -10,7 +10,10 @@ class Media {
const Media({required this.id, required this.setId, required this.relPath, required this.fileName, required this.absPath, required this.type, required this.duration, required this.codec, required this.resolution, required this.bitrate, required this.fileSizeBytes, required this.width, required this.height, required this.thumbnailPath, required this.playCount, this.favorite = false, this.tags = const [], this.deletedAt, this.createdAt});
- factory Media.fromJson(Map<String, dynamic> json) => Media(id: json['id'] as int? ?? 0, setId: json['set_id'] as int? ?? 0, relPath: json['rel_path'] as String? ?? '', fileName: json['file_name'] as String? ?? '', absPath: json['abs_path'] as String? ?? '', type: json['type'] as String? ?? '', duration: (json['duration'] as num?)?.toDouble() ?? 0, codec: json['codec'] as String? ?? '', resolution: json['resolution'] as String? ?? '', bitrate: json['bitrate'] as int? ?? 0, fileSizeBytes: json['file_size_bytes'] as int? ?? 0, width: json['width'] as int? ?? 0, height: json['height'] as int? ?? 0, thumbnailPath: json['thumbnail_path'] as String? ?? '', playCount: json['play_count'] as int? ?? 0, favorite: json['favorite'] as bool? ?? false, tags: (json['tags'] as List<dynamic>? ?? const []).cast<String>(), deletedAt: dateTimeFromJson(json['deleted_at']), createdAt: dateTimeFromJson(json['created_at']));
+ factory Media.fromJson(Map<String, dynamic> json) => Media(id: json['id'] as int? ?? 0, setId: json['set_id'] as int? ?? 0, relPath: json['rel_path'] as String? ?? '', fileName: json['file_name'] as String? ?? '', absPath: json['abs_path'] as String? ?? '', type: json['type'] as String? ?? '', duration: (json['duration'] as num?)?.toDouble() ?? 0, codec: json['codec'] as String? ?? '', resolution: json['resolution'] as String? ?? '', bitrate: json['bitrate'] as int? ?? 0, fileSizeBytes: json['file_size_bytes'] as int? ?? 0, width: json['width'] as int? ?? 0, height: json['height'] as int? ?? 0, thumbnailPath: json['thumbnail_path'] as String? ?? '', playCount: json['play_count'] as int? ?? 0, favorite: json['favorite'] as bool? ?? false, // Safe tag deserialization: keep only elements that are already Strings,
+// silently dropping ints, nulls, or other unexpected types. This tolerates
+// malformed server responses without throwing a TypeError at runtime.
+tags: (json['tags'] as List<dynamic>? ?? []).whereType<String>().toList(), deletedAt: dateTimeFromJson(json['deleted_at']), createdAt: dateTimeFromJson(json['created_at']));
Map<String, dynamic> toJson() => {'id': id, 'set_id': setId, 'rel_path': relPath, 'file_name': fileName, 'abs_path': absPath, 'type': type, 'duration': duration, 'codec': codec, 'resolution': resolution, 'bitrate': bitrate, 'file_size_bytes': fileSizeBytes, 'width': width, 'height': height, 'thumbnail_path': thumbnailPath, 'play_count': playCount, 'favorite': favorite, 'tags': tags, 'deleted_at': dateTimeToJson(deletedAt), 'created_at': dateTimeToJson(createdAt)};
}