fix(plex): tolerate malformed activity rows

This commit is contained in:
edde746
2026-07-12 08:42:25 +02:00
parent 34be331024
commit c81a1f9a97
5 changed files with 87 additions and 12 deletions
+8 -5
View File
@@ -1,20 +1,23 @@
import 'package:json_annotation/json_annotation.dart';
import '../../utils/json_utils.dart';
part 'plex_activity.g.dart';
/// Represents a running background task on a Plex Media Server (from /activities endpoint).
@JsonSerializable(createToJson: false)
class PlexActivity {
@JsonKey(defaultValue: '')
@JsonKey(fromJson: stringOrEmpty)
final String uuid;
@JsonKey(defaultValue: '')
@JsonKey(fromJson: stringOrEmpty)
final String type;
@JsonKey(defaultValue: '')
@JsonKey(fromJson: stringOrEmpty)
final String title;
@JsonKey(readValue: readStringField)
final String? subtitle;
@JsonKey(defaultValue: 0)
@JsonKey(fromJson: flexibleIntOrZero)
final int progress; // 0100
@JsonKey(defaultValue: false)
@JsonKey(fromJson: flexibleBool)
final bool cancellable;
const PlexActivity({
+6 -6
View File
@@ -7,10 +7,10 @@ part of 'plex_activity.dart';
// **************************************************************************
PlexActivity _$PlexActivityFromJson(Map<String, dynamic> json) => PlexActivity(
uuid: json['uuid'] as String? ?? '',
type: json['type'] as String? ?? '',
title: json['title'] as String? ?? '',
subtitle: json['subtitle'] as String?,
progress: (json['progress'] as num?)?.toInt() ?? 0,
cancellable: json['cancellable'] as bool? ?? false,
uuid: stringOrEmpty(json['uuid']),
type: stringOrEmpty(json['type']),
title: stringOrEmpty(json['title']),
subtitle: readStringField(json, 'subtitle') as String?,
progress: flexibleIntOrZero(json['progress']),
cancellable: flexibleBool(json['cancellable']),
);
+11 -1
View File
@@ -826,7 +826,17 @@ class PlexClient
if (container == null) return [];
final activityList = container['Activity'] as List?;
if (activityList == null) return [];
return activityList.map((json) => PlexActivity.fromJson(json as Map<String, dynamic>)).toList();
final activities = <PlexActivity>[];
for (final json in activityList) {
if (json is! Map<String, dynamic>) continue;
try {
final activity = PlexActivity.fromJson(json);
if (activity.uuid.isNotEmpty) activities.add(activity);
} catch (e) {
appLogger.d('Skipping malformed Plex activity', error: e);
}
}
return activities;
} catch (e) {
appLogger.e('Failed to get activities', error: e);
return [];
+22
View File
@@ -0,0 +1,22 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:plezy/models/plex/plex_activity.dart';
void main() {
test('PlexActivity tolerates stringified and scalar fields', () {
final activity = PlexActivity.fromJson({
'uuid': 42,
'type': 7,
'title': true,
'subtitle': 99,
'progress': '75',
'cancellable': '1',
});
expect(activity.uuid, '42');
expect(activity.type, '7');
expect(activity.title, 'true');
expect(activity.subtitle, '99');
expect(activity.progress, 75);
expect(activity.cancellable, isTrue);
});
}
@@ -85,4 +85,44 @@ void main() {
expect(queue?.playQueueVersion, 5);
expect(queue?.size, 3);
});
test('activities tolerate scalar drift and skip only malformed rows', () async {
final client = makeClient(
(_) async => http.Response(
jsonEncode({
'MediaContainer': {
'Activity': [
{
'uuid': 'activity-1',
'type': 'library.update',
'title': 'Scanning',
'subtitle': 'Movies',
'progress': 25,
'cancellable': false,
},
'not-an-activity',
{'type': 'library.update', 'title': 'Missing identity'},
{'uuid': 42, 'type': 7, 'title': true, 'subtitle': 99, 'progress': '75', 'cancellable': '1'},
],
},
}),
200,
headers: {'content-type': 'application/json'},
),
);
addTearDown(client.close);
final activities = await client.getActivities();
expect(activities, hasLength(2));
expect(activities.first.uuid, 'activity-1');
expect(activities.first.progress, 25);
expect(activities.first.cancellable, isFalse);
expect(activities.last.uuid, '42');
expect(activities.last.type, '7');
expect(activities.last.title, 'true');
expect(activities.last.subtitle, '99');
expect(activities.last.progress, 75);
expect(activities.last.cancellable, isTrue);
});
}