fix(plex): use provider continue watching hub

close #1166
This commit is contained in:
edde746
2026-05-28 01:29:29 +02:00
parent 3747a74cb3
commit 0b29673a5e
2 changed files with 59 additions and 3 deletions
+24 -3
View File
@@ -248,6 +248,9 @@ class PlexClient
/// Promoted home hub endpoint advertised by /media/providers (usually /hubs/promoted).
String? _providerPromotedHubKey;
/// Dedicated Continue Watching hub endpoint advertised by /media/providers.
String? _providerContinueWatchingHubKey;
/// EPG providers parsed from /media/providers
@override
List<({String identifier, String gridEndpoint})> _providerEpg = const [];
@@ -348,6 +351,7 @@ class PlexClient
List<({String identifier, String gridEndpoint})> epgProviders = const [],
String? homeHubKey,
String? promotedHubKey,
String? continueWatchingHubKey,
}) {
final client = PlexClient._(
config,
@@ -360,6 +364,7 @@ class PlexClient
client._providerEpg = epgProviders;
client._providerHomeHubKey = homeHubKey;
client._providerPromotedHubKey = promotedHubKey;
client._providerContinueWatchingHubKey = continueWatchingHubKey;
return client;
}
@@ -459,6 +464,7 @@ class PlexClient
_providerEpg = [];
_providerHomeHubKey = null;
_providerPromotedHubKey = null;
_providerContinueWatchingHubKey = null;
return;
}
@@ -468,6 +474,7 @@ class PlexClient
_providerEpg = [];
_providerHomeHubKey = null;
_providerPromotedHubKey = null;
_providerContinueWatchingHubKey = null;
return;
}
@@ -475,6 +482,7 @@ class PlexClient
final epg = <({String identifier, String gridEndpoint})>[];
String? homeHubKey;
String? promotedHubKey;
String? continueWatchingHubKey;
for (final provider in providers) {
if (provider is! Map) continue;
@@ -493,6 +501,10 @@ class PlexClient
promotedHubKey ??= feature['key'] as String?;
}
if (feature['type'] == 'continuewatching') {
continueWatchingHubKey ??= feature['key'] as String?;
}
if (feature['type'] != 'content') continue;
final directories = feature['Directory'] as List?;
@@ -552,6 +564,7 @@ class PlexClient
_providerEpg = epg;
_providerHomeHubKey = homeHubKey;
_providerPromotedHubKey = promotedHubKey;
_providerContinueWatchingHubKey = continueWatchingHubKey;
appLogger.d('Media providers: ${libraries.length} libraries, ${epg.length} EPG provider(s)');
} catch (e) {
appLogger.w('Failed to fetch /media/providers, will fall back to /library/sections', error: e);
@@ -559,6 +572,7 @@ class PlexClient
_providerEpg = [];
_providerHomeHubKey = null;
_providerPromotedHubKey = null;
_providerContinueWatchingHubKey = null;
}
}
@@ -1326,15 +1340,22 @@ class PlexClient
}
/// Get continue watching items via the hubs system.
/// Uses /hubs?identifier=home.continue,home.ondeck which respects the
/// Prefer the provider's dedicated Continue Watching feature key when
/// advertised; fall back to Plex Web's legacy hubs query. Both respect the
/// server's OnDeckWindow preference (unlike /library/onDeck).
Future<List<PlexMetadataDto>> _getContinueWatching({int? count = 20}) async {
final continueWatchingHubKey = _providerContinueWatchingHubKey;
final queryParameters = <String, dynamic>{'count': ?count, 'includeGuids': 1};
if (continueWatchingHubKey == null) {
queryParameters['identifier'] = 'home.continue,home.ondeck';
}
final response = await retryTransientMediaServerCall(
operation: 'Plex continue watching hubs',
attemptTimeouts: MediaServerTimeouts.homeHubAttemptTimeouts,
call: (timeout, abort) => _getWithFailover(
'/hubs',
queryParameters: {'identifier': 'home.continue,home.ondeck', 'count': ?count, 'includeGuids': 1},
continueWatchingHubKey ?? '/hubs',
queryParameters: queryParameters,
timeout: timeout,
abort: abort,
allowEndpointFailover: false,
+35
View File
@@ -127,6 +127,40 @@ void main() {
expect(httpClient.requests.last.url.queryParameters['count'], '12');
});
test('fetchContinueWatching uses advertised provider feature endpoint', () async {
final db = AppDatabase.forTesting(NativeDatabase.memory());
PlexApiCache.initialize(db);
addTearDown(db.close);
final httpClient = _SequenceClient([
(_) async => _jsonResponse(_mediaProvidersPayload()),
(_) async => _jsonResponse(_continueWatchingPayload()),
]);
final client = await PlexClient.create(
PlexConfig(
baseUrl: 'http://server:32400',
token: 'token',
clientIdentifier: 'client-id',
product: 'Plezy',
version: 'test',
),
serverId: 'server-id',
serverName: 'Server',
httpClient: httpClient,
seedTranscoderVideoSupport: true,
);
addTearDown(client.close);
final items = await client.fetchContinueWatching(count: 21);
expect(items, hasLength(1));
expect(items.single.title, 'Movie A');
expect(httpClient.requests.map((r) => r.url.path), ['/media/providers', '/hubs/continueWatching']);
expect(httpClient.requests.last.url.queryParameters['count'], '21');
expect(httpClient.requests.last.url.queryParameters['includeGuids'], '1');
expect(httpClient.requests.last.url.queryParameters.containsKey('identifier'), isFalse);
});
test('fetchContinueWatching omits count when uncapped', () async {
final db = AppDatabase.forTesting(NativeDatabase.memory());
PlexApiCache.initialize(db);
@@ -260,6 +294,7 @@ Map<String, dynamic> _mediaProvidersPayload() => {
],
},
{'type': 'promoted', 'key': '/hubs/promoted'},
{'type': 'continuewatching', 'key': '/hubs/continueWatching'},
],
},
],