feat: jellyfin
This commit is contained in:
@@ -1,18 +1,18 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
|
||||
import '../../../../services/plex_client.dart';
|
||||
import '../../../i18n/strings.g.dart';
|
||||
import '../../../media/media_hub.dart';
|
||||
import '../../../media/media_item.dart';
|
||||
import '../../../mixins/item_updatable.dart';
|
||||
import '../../../models/plex_hub.dart';
|
||||
import '../../../models/plex_metadata.dart';
|
||||
import '../../../utils/provider_extensions.dart';
|
||||
import '../../../widgets/hub_section.dart';
|
||||
import '../../main_screen.dart';
|
||||
import 'base_library_tab.dart';
|
||||
|
||||
/// Recommended tab for library screen
|
||||
/// Shows library-specific hubs and recommendations, including dedicated Continue Watching
|
||||
class LibraryRecommendedTab extends BaseLibraryTab<PlexHub> {
|
||||
class LibraryRecommendedTab extends BaseLibraryTab<MediaHub> {
|
||||
const LibraryRecommendedTab({
|
||||
super.key,
|
||||
required super.library,
|
||||
@@ -26,20 +26,24 @@ class LibraryRecommendedTab extends BaseLibraryTab<PlexHub> {
|
||||
State<LibraryRecommendedTab> createState() => _LibraryRecommendedTabState();
|
||||
}
|
||||
|
||||
class _LibraryRecommendedTabState extends BaseLibraryTabState<PlexHub, LibraryRecommendedTab> with ItemUpdatable {
|
||||
class _LibraryRecommendedTabState extends BaseLibraryTabState<MediaHub, LibraryRecommendedTab> with ItemUpdatable {
|
||||
/// GlobalKeys for each hub section to enable vertical navigation
|
||||
final List<GlobalKey<HubSectionState>> _hubKeys = [];
|
||||
|
||||
@override
|
||||
PlexClient get client => getClientForLibrary();
|
||||
String? get itemServerId => widget.library.serverId;
|
||||
|
||||
@override
|
||||
void updateItemInLists(String ratingKey, PlexMetadata updatedMetadata) {
|
||||
// Update the item in any hub that contains it
|
||||
for (final hub in items) {
|
||||
final itemIndex = hub.items.indexWhere((item) => item.ratingKey == ratingKey);
|
||||
void updateItemInLists(String itemId, MediaItem updatedItem) {
|
||||
// Update the item in any hub that contains it. MediaHub items are
|
||||
// immutable lists; rebuild the affected hub in-place.
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
final hub = items[i];
|
||||
final itemIndex = hub.items.indexWhere((item) => item.id == itemId);
|
||||
if (itemIndex != -1) {
|
||||
hub.items[itemIndex] = updatedMetadata;
|
||||
final newItems = List<MediaItem>.from(hub.items);
|
||||
newItems[itemIndex] = updatedItem;
|
||||
items[i] = hub.copyWith(items: newItems);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,20 +57,22 @@ class _LibraryRecommendedTabState extends BaseLibraryTabState<PlexHub, LibraryRe
|
||||
@override
|
||||
String get errorContext => t.libraries.tabs.recommended;
|
||||
|
||||
/// Detects Continue Watching hubs by hubIdentifier.
|
||||
/// Detects Continue Watching hubs by hub identifier.
|
||||
/// Section-specific CW hubs use identifiers like "movie.inprogress.1".
|
||||
static bool _isContinueWatchingHub(PlexHub hub) {
|
||||
final hubId = hub.hubIdentifier?.toLowerCase() ?? '';
|
||||
static bool _isContinueWatchingHub(MediaHub hub) {
|
||||
final hubId = hub.identifier?.toLowerCase() ?? '';
|
||||
return hubId.contains('inprogress');
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<PlexHub>> loadData() async {
|
||||
Future<List<MediaHub>> loadData() async {
|
||||
// Clear hub keys before loading new hubs to prevent stale references
|
||||
_hubKeys.clear();
|
||||
|
||||
final client = getClientForLibrary();
|
||||
final hubs = await client.getLibraryHubs(widget.library.key, limit: 12);
|
||||
// Backend-aware fetch: Plex hits /hubs/sections, Jellyfin synthesises
|
||||
// Continue Watching + Next Up + Recently Added.
|
||||
final client = context.tryGetMediaClientForServer(widget.library.serverId);
|
||||
final hubs = client == null ? <MediaHub>[] : List.of(await client.fetchLibraryHubs(widget.library.id, limit: 12));
|
||||
|
||||
// Move Continue Watching hub to the front if present
|
||||
final cwIndex = hubs.indexWhere(_isContinueWatchingHub);
|
||||
@@ -127,7 +133,7 @@ class _LibraryRecommendedTabState extends BaseLibraryTabState<PlexHub, LibraryRe
|
||||
static const double _focusDecorationPadding = 8.0;
|
||||
|
||||
@override
|
||||
Widget buildContent(List<PlexHub> items) {
|
||||
Widget buildContent(List<MediaHub> items) {
|
||||
_ensureHubKeys(items.length);
|
||||
|
||||
return ListView.builder(
|
||||
@@ -161,7 +167,7 @@ class _LibraryRecommendedTabState extends BaseLibraryTabState<PlexHub, LibraryRe
|
||||
loadItems();
|
||||
}
|
||||
|
||||
IconData _getHubIcon(PlexHub hub) {
|
||||
IconData _getHubIcon(MediaHub hub) {
|
||||
final title = hub.title.toLowerCase();
|
||||
if (title.contains('continue watching') || title.contains('on deck')) {
|
||||
return Symbols.play_circle_rounded;
|
||||
|
||||
Reference in New Issue
Block a user