fix(trackers): preserve Plex library context

This commit is contained in:
edde746
2026-05-12 22:46:38 +02:00
parent 3a01fa25a7
commit de129fbb0a
30 changed files with 845 additions and 119 deletions
+16 -3
View File
@@ -18,8 +18,9 @@ Future<void> collectEpisodesForShow(
String showRatingKey, {
required bool unwatchedOnly,
required List<MediaItem> out,
MediaItem? fallback,
}) {
return _collectPlayable(client, showRatingKey, unwatchedOnly: unwatchedOnly, out: out);
return _collectPlayable(client, showRatingKey, unwatchedOnly: unwatchedOnly, out: out, fallback: fallback);
}
/// Collect every episode of a single season into [out] via the same
@@ -30,8 +31,9 @@ Future<void> collectEpisodesForSeason(
String seasonRatingKey, {
required bool unwatchedOnly,
required List<MediaItem> out,
MediaItem? fallback,
}) {
return _collectPlayable(client, seasonRatingKey, unwatchedOnly: unwatchedOnly, out: out);
return _collectPlayable(client, seasonRatingKey, unwatchedOnly: unwatchedOnly, out: out, fallback: fallback);
}
Future<void> _collectPlayable(
@@ -39,11 +41,22 @@ Future<void> _collectPlayable(
String parentId, {
required bool unwatchedOnly,
required List<MediaItem> out,
MediaItem? fallback,
}) async {
final leaves = await client.fetchPlayableDescendants(parentId);
for (final ep in leaves) {
if (ep.kind != MediaKind.episode) continue;
if (unwatchedOnly && ep.isWatched && !ep.hasActiveProgress) continue;
out.add(ep);
out.add(_withFallbackLibrary(ep, fallback));
}
}
MediaItem _withFallbackLibrary(MediaItem item, MediaItem? fallback) {
if (fallback == null) return item;
return item.copyWith(
serverId: item.serverId ?? fallback.serverId,
serverName: item.serverName ?? fallback.serverName,
libraryId: item.libraryId ?? fallback.libraryId,
libraryTitle: item.libraryTitle ?? fallback.libraryTitle,
);
}
+2
View File
@@ -124,6 +124,8 @@ Future<MediaNavigationResult> navigateToMediaItem(
title: mi.grandparentTitle ?? mi.parentTitle ?? mi.displayTitle,
thumbPath: mi.grandparentThumbPath ?? mi.parentThumbPath,
artPath: mi.grandparentArtPath,
libraryId: mi.libraryId,
libraryTitle: mi.libraryTitle,
serverId: mi.serverId,
serverName: mi.serverName,
);
+6 -2
View File
@@ -4,9 +4,13 @@
class PlexCacheParser {
PlexCacheParser._();
static Map<String, dynamic>? extractMediaContainer(Map<String, dynamic>? cached) {
final container = cached?['MediaContainer'];
return container is Map<String, dynamic> ? container : null;
}
static List<dynamic>? extractMetadataList(Map<String, dynamic>? cached) {
if (cached == null) return null;
return cached['MediaContainer']?['Metadata'] as List?;
return extractMediaContainer(cached)?['Metadata'] as List?;
}
static Map<String, dynamic>? extractFirstMetadata(Map<String, dynamic>? cached) {
+27
View File
@@ -0,0 +1,27 @@
import 'json_utils.dart';
final RegExp plexLibrarySectionPathPattern = RegExp(r'/(?:library|hubs)/sections/(\d+)');
int? plexLibrarySectionIdFromJson(Map<String, dynamic>? json) {
if (json == null) return null;
final direct = flexibleInt(json['librarySectionID']) ?? flexibleInt(json['targetLibrarySectionID']);
if (direct != null) return direct;
for (final key in const ['librarySectionKey', 'key', 'hubKey']) {
final parsed = plexLibrarySectionIdFromString(json[key]?.toString());
if (parsed != null) return parsed;
}
return null;
}
int? plexLibrarySectionIdFromString(String? value) {
if (value == null || value == 'shared') return null;
final direct = int.tryParse(value);
if (direct != null) return direct;
final match = plexLibrarySectionPathPattern.firstMatch(value);
return match == null ? null : int.tryParse(match.group(1)!);
}
String? plexLibrarySectionTitleFromJson(Map<String, dynamic>? json) {
return json?['librarySectionTitle']?.toString();
}
+2 -1
View File
@@ -63,7 +63,8 @@ class WatchStateEvent with HierarchicalEventMixin {
}) : globalKey = buildGlobalKey(serverId, itemId);
/// `serverId:librarySectionID`, matching [MediaLibrary.globalKey]. Null when
/// the library section is unknown.
/// the library section is unknown; tracker filters treat unknown as allowed
/// only when no filter is configured.
String? get librarySectionGlobalKey => librarySectionID != null ? buildGlobalKey(serverId, librarySectionID!) : null;
@override