fix(trackers): preserve Plex library context
This commit is contained in:
@@ -17,6 +17,7 @@ import '../i18n/strings.g.dart';
|
||||
import 'base_media_list_detail_screen.dart';
|
||||
import 'focusable_detail_screen_mixin.dart';
|
||||
import '../mixins/grid_focus_node_mixin.dart';
|
||||
import '../services/playlist_items_loader.dart';
|
||||
|
||||
/// Screen to display the contents of a collection
|
||||
class CollectionDetailScreen extends StatefulWidget {
|
||||
@@ -59,7 +60,14 @@ class _CollectionDetailScreenState extends BaseMediaListDetailScreen<CollectionD
|
||||
|
||||
@override
|
||||
Future<LibraryPage<MediaItem>> fetchPage(int start, int size, AbortController? abort) {
|
||||
return mediaClient.fetchCollectionPage(widget.collection.id, start: start, size: size, abort: abort);
|
||||
return mediaClient.fetchCollectionPage(
|
||||
widget.collection.id,
|
||||
start: start,
|
||||
size: size,
|
||||
abort: abort,
|
||||
libraryId: widget.collection.libraryId,
|
||||
libraryTitle: widget.collection.libraryTitle,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -147,10 +155,12 @@ class _CollectionDetailScreenState extends BaseMediaListDetailScreen<CollectionD
|
||||
|
||||
final downloadProvider = context.read<DownloadProvider>();
|
||||
try {
|
||||
// [fetchChildren] is the neutral equivalent of the Plex-only
|
||||
// `fetchAllCollectionItemsAsMediaItems` — both backends return the
|
||||
// collection's full contents.
|
||||
final allItems = await mediaClient.fetchChildren(widget.collection.id);
|
||||
final allItems = await fetchAllCollectionItemsPaged(
|
||||
mediaClient,
|
||||
widget.collection.id,
|
||||
libraryId: widget.collection.libraryId,
|
||||
libraryTitle: widget.collection.libraryTitle,
|
||||
);
|
||||
if (!mounted) return;
|
||||
final result = await showCollectionDownloadOptionsAndQueue(
|
||||
context,
|
||||
|
||||
@@ -116,7 +116,11 @@ class FolderTreeViewState extends State<FolderTreeView> {
|
||||
final client = context.getPlexClientForServer(widget.serverId!);
|
||||
|
||||
// Items are automatically tagged with server info by PlexClient.
|
||||
final children = await client.fetchFolderChildren(folderKey);
|
||||
final children = await client.fetchFolderChildren(
|
||||
folderKey,
|
||||
libraryId: folder.libraryId,
|
||||
libraryTitle: folder.libraryTitle,
|
||||
);
|
||||
|
||||
if (!mounted) return;
|
||||
|
||||
@@ -162,7 +166,12 @@ class FolderTreeViewState extends State<FolderTreeView> {
|
||||
if (folderKey == null) return;
|
||||
final client = context.getPlexClientForServer(widget.serverId!);
|
||||
final launcher = PlexPlayQueueLauncher(context: context, client: client, serverId: widget.serverId);
|
||||
await launcher.launchFromFolder(folderKey: folderKey, shuffle: false);
|
||||
await launcher.launchFromFolder(
|
||||
folderKey: folderKey,
|
||||
shuffle: false,
|
||||
libraryId: folder.libraryId,
|
||||
libraryTitle: folder.libraryTitle,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _handleFolderShuffle(MediaItem folder) async {
|
||||
@@ -170,7 +179,12 @@ class FolderTreeViewState extends State<FolderTreeView> {
|
||||
if (folderKey == null) return;
|
||||
final client = context.getPlexClientForServer(widget.serverId!);
|
||||
final launcher = PlexPlayQueueLauncher(context: context, client: client, serverId: widget.serverId);
|
||||
await launcher.launchFromFolder(folderKey: folderKey, shuffle: true);
|
||||
await launcher.launchFromFolder(
|
||||
folderKey: folderKey,
|
||||
shuffle: true,
|
||||
libraryId: folder.libraryId,
|
||||
libraryTitle: folder.libraryTitle,
|
||||
);
|
||||
}
|
||||
|
||||
bool _isFolder(MediaItem item) {
|
||||
|
||||
@@ -117,6 +117,14 @@ class _MediaDetailScreenState extends State<MediaDetailScreen>
|
||||
final Map<String, List<MediaItem>> _episodeCache = {};
|
||||
bool _isLoadingSeasonEpisodes = false;
|
||||
List<FocusNode> _seasonTabFocusNodes = [];
|
||||
|
||||
MediaItem _withFallbackLibrary(MediaItem item, MediaItem fallback) {
|
||||
return item.copyWith(
|
||||
libraryId: item.libraryId ?? fallback.libraryId,
|
||||
libraryTitle: item.libraryTitle ?? fallback.libraryTitle,
|
||||
);
|
||||
}
|
||||
|
||||
final Map<int, GlobalKey<MediaContextMenuState>> _seasonContextMenuKeys = {};
|
||||
final ScrollController _seasonTabsScrollController = ScrollController();
|
||||
final FocusNode _firstEpisodeFocusNode = FocusNode(debugLabel: 'first_episode');
|
||||
@@ -400,15 +408,23 @@ class _MediaDetailScreenState extends State<MediaDetailScreen>
|
||||
final metadata = result.item;
|
||||
final onDeckEpisode = result.onDeckEpisode;
|
||||
if (metadata != null) {
|
||||
setStateIfMounted(() {
|
||||
_fullMetadata = _applyLocalProgress(
|
||||
final refreshedMetadata = _applyLocalProgress(
|
||||
_withFallbackLibrary(
|
||||
metadata.copyWith(serverId: serverId, serverName: serverName ?? metadata.serverName),
|
||||
);
|
||||
_onDeckEpisode = onDeckEpisode == null
|
||||
? null
|
||||
: _applyLocalProgress(
|
||||
_metadata,
|
||||
),
|
||||
);
|
||||
final refreshedOnDeck = onDeckEpisode == null
|
||||
? null
|
||||
: _applyLocalProgress(
|
||||
_withFallbackLibrary(
|
||||
onDeckEpisode.copyWith(serverId: serverId, serverName: serverName ?? onDeckEpisode.serverName),
|
||||
);
|
||||
refreshedMetadata,
|
||||
),
|
||||
);
|
||||
setStateIfMounted(() {
|
||||
_fullMetadata = refreshedMetadata;
|
||||
_onDeckEpisode = refreshedOnDeck;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -417,7 +433,12 @@ class _MediaDetailScreenState extends State<MediaDetailScreen>
|
||||
_episodeCache.clear();
|
||||
setStateIfMounted(() {
|
||||
_seasons = seasons
|
||||
.map((s) => s.copyWith(serverId: serverId, serverName: serverName ?? s.serverName))
|
||||
.map(
|
||||
(s) => _withFallbackLibrary(
|
||||
s.copyWith(serverId: serverId, serverName: serverName ?? s.serverName),
|
||||
_metadata,
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
});
|
||||
if (_showEpisodesDirectly) {
|
||||
@@ -1002,14 +1023,20 @@ class _MediaDetailScreenState extends State<MediaDetailScreen>
|
||||
final serverName = _metadata.serverName;
|
||||
final source = metadata ?? _metadata;
|
||||
final base = _applyLocalProgress(
|
||||
source.copyWith(serverId: serverId ?? source.serverId, serverName: serverName ?? source.serverName),
|
||||
_withFallbackLibrary(
|
||||
source.copyWith(serverId: serverId ?? source.serverId, serverName: serverName ?? source.serverName),
|
||||
_metadata,
|
||||
),
|
||||
);
|
||||
final onDeckWithServerId = onDeckEpisode == null
|
||||
? null
|
||||
: _applyLocalProgress(
|
||||
onDeckEpisode.copyWith(
|
||||
serverId: serverId ?? onDeckEpisode.serverId,
|
||||
serverName: serverName ?? onDeckEpisode.serverName,
|
||||
_withFallbackLibrary(
|
||||
onDeckEpisode.copyWith(
|
||||
serverId: serverId ?? onDeckEpisode.serverId,
|
||||
serverName: serverName ?? onDeckEpisode.serverName,
|
||||
),
|
||||
base,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1081,7 +1108,12 @@ class _MediaDetailScreenState extends State<MediaDetailScreen>
|
||||
|
||||
// Preserve serverId for each season.
|
||||
final seasonsWithServerId = seasons
|
||||
.map((season) => season.copyWith(serverId: serverId, serverName: _metadata.serverName ?? season.serverName))
|
||||
.map(
|
||||
(season) => _withFallbackLibrary(
|
||||
season.copyWith(serverId: serverId, serverName: _metadata.serverName ?? season.serverName),
|
||||
_metadata,
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
|
||||
// Plex can override the library season mode per show; Jellyfin falls
|
||||
@@ -1157,6 +1189,8 @@ class _MediaDetailScreenState extends State<MediaDetailScreen>
|
||||
leafCount: entry.value.length,
|
||||
thumbPath: firstEp.parentThumbPath,
|
||||
parentId: firstEp.grandparentId,
|
||||
libraryId: firstEp.libraryId ?? _metadata.libraryId,
|
||||
libraryTitle: firstEp.libraryTitle ?? _metadata.libraryTitle,
|
||||
serverId: _metadata.serverId,
|
||||
serverName: _metadata.serverName,
|
||||
);
|
||||
@@ -1270,11 +1304,14 @@ class _MediaDetailScreenState extends State<MediaDetailScreen>
|
||||
final episodes = await mediaClient.fetchChildren(season.id);
|
||||
final episodesWithServerId = episodes
|
||||
.map(
|
||||
(e) => e.copyWith(
|
||||
serverId: _metadata.serverId ?? e.serverId,
|
||||
serverName: _metadata.serverName ?? e.serverName,
|
||||
grandparentId: _metadata.id,
|
||||
grandparentTitle: _metadata.title ?? e.grandparentTitle,
|
||||
(e) => _withFallbackLibrary(
|
||||
e.copyWith(
|
||||
serverId: _metadata.serverId ?? e.serverId,
|
||||
serverName: _metadata.serverName ?? e.serverName,
|
||||
grandparentId: _metadata.id,
|
||||
grandparentTitle: _metadata.title ?? e.grandparentTitle,
|
||||
),
|
||||
season.libraryId != null ? season : _metadata,
|
||||
),
|
||||
)
|
||||
.map(_applyLocalProgress)
|
||||
@@ -2002,11 +2039,14 @@ class _MediaDetailScreenState extends State<MediaDetailScreen>
|
||||
: _metadata.title;
|
||||
final enriched = episodes
|
||||
.map(
|
||||
(e) => e.copyWith(
|
||||
serverId: serverId,
|
||||
serverName: _metadata.serverName ?? e.serverName,
|
||||
grandparentId: e.grandparentId ?? fallbackGrandparentId,
|
||||
grandparentTitle: e.grandparentTitle ?? fallbackGrandparentTitle,
|
||||
(e) => _withFallbackLibrary(
|
||||
e.copyWith(
|
||||
serverId: serverId,
|
||||
serverName: _metadata.serverName ?? e.serverName,
|
||||
grandparentId: e.grandparentId ?? fallbackGrandparentId,
|
||||
grandparentTitle: e.grandparentTitle ?? fallbackGrandparentTitle,
|
||||
),
|
||||
_metadata,
|
||||
),
|
||||
)
|
||||
.map(_applyLocalProgress)
|
||||
@@ -2092,6 +2132,8 @@ class _MediaDetailScreenState extends State<MediaDetailScreen>
|
||||
final episodeWithServerId = firstEpisode.copyWith(
|
||||
serverId: _metadata.serverId ?? firstEpisode.serverId,
|
||||
serverName: _metadata.serverName ?? firstEpisode.serverName,
|
||||
libraryId: firstEpisode.libraryId ?? _metadata.libraryId,
|
||||
libraryTitle: firstEpisode.libraryTitle ?? _metadata.libraryTitle,
|
||||
);
|
||||
if (mounted) {
|
||||
appLogger.d('Playing first episode: ${episodeWithServerId.title}');
|
||||
|
||||
@@ -53,11 +53,21 @@ extension _VideoPlayerEpisodeQueueMethods on VideoPlayerScreenState {
|
||||
showRatingKey: showRatingKey,
|
||||
shuffle: 0,
|
||||
startingEpisodeKey: _currentMetadata.id,
|
||||
librarySectionID: _currentMetadata.libraryId,
|
||||
librarySectionTitle: _currentMetadata.libraryTitle,
|
||||
);
|
||||
|
||||
if (playQueue != null && playQueue.items != null && playQueue.items!.isNotEmpty) {
|
||||
await playbackState.setPlaybackFromPlayQueue(playQueue, showRatingKey);
|
||||
playbackState.setPlayQueueWindowFetcher(client.getPlayQueue);
|
||||
playbackState.setPlayQueueWindowFetcher(
|
||||
(id, {center, window = 50}) => client.getPlayQueue(
|
||||
id,
|
||||
center: center,
|
||||
window: window,
|
||||
librarySectionID: _currentMetadata.libraryId,
|
||||
librarySectionTitle: _currentMetadata.libraryTitle,
|
||||
),
|
||||
);
|
||||
|
||||
appLogger.d('Sequential play queue created with ${playQueue.items!.length} items');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user