refactor: type server identifiers
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import '../media/media_item.dart';
|
||||
import '../media/ids.dart';
|
||||
import 'app_logger.dart';
|
||||
import 'base_notifier.dart';
|
||||
import 'global_key_utils.dart';
|
||||
@@ -16,7 +17,7 @@ class DeletionEvent with HierarchicalEventMixin {
|
||||
|
||||
/// Server this item belongs to
|
||||
@override
|
||||
final String serverId;
|
||||
final ServerId serverId;
|
||||
|
||||
/// Parent chain for hierarchical invalidation
|
||||
/// For an episode: [seasonId, showId]
|
||||
@@ -43,7 +44,7 @@ class DeletionEvent with HierarchicalEventMixin {
|
||||
required this.mediaType,
|
||||
this.leafCount = 1,
|
||||
this.isDownloadOnly = false,
|
||||
}) : globalKey = buildGlobalKey(serverId, itemId);
|
||||
}) : globalKey = buildGlobalKey(ServerId(serverId), itemId);
|
||||
|
||||
@override
|
||||
String toString() => 'DeletionEvent(deleted: $globalKey, type: $mediaType, parents: $parentChain)';
|
||||
@@ -60,7 +61,7 @@ class DeletionNotifier extends BaseNotifier<DeletionEvent> {
|
||||
|
||||
DeletionNotifier._internal();
|
||||
|
||||
Stream<DeletionEvent> forServer(String serverId) => stream.where((e) => e.serverId == serverId);
|
||||
Stream<DeletionEvent> forServer(ServerId serverId) => stream.where((e) => e.serverId == serverId);
|
||||
|
||||
Stream<DeletionEvent> forItem(String itemId) => stream.where((e) => e.affectsItem(itemId));
|
||||
|
||||
@@ -75,7 +76,7 @@ class DeletionNotifier extends BaseNotifier<DeletionEvent> {
|
||||
notify(
|
||||
DeletionEvent(
|
||||
itemId: item.id,
|
||||
serverId: item.serverId ?? '',
|
||||
serverId: ServerId(item.serverId ?? ''),
|
||||
parentChain: item.parentChain,
|
||||
mediaType: item.kind.id,
|
||||
leafCount: item.leafCount ?? 1,
|
||||
|
||||
@@ -78,7 +78,7 @@ class DesktopAppBarHelper {
|
||||
final leftPadding = isFullscreen ? DesktopWindowPadding.macOSLeftFullscreen : DesktopWindowPadding.macOSLeft;
|
||||
|
||||
final paddedWidget = Padding(
|
||||
padding: EdgeInsets.only(left: leftPadding),
|
||||
padding: .only(left: leftPadding),
|
||||
child: leading,
|
||||
);
|
||||
|
||||
@@ -167,7 +167,7 @@ class DesktopTitleBarPadding extends StatelessWidget {
|
||||
return child;
|
||||
}
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(right: right),
|
||||
padding: .only(right: right),
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
@@ -186,7 +186,7 @@ class DesktopTitleBarPadding extends StatelessWidget {
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(left: left, right: right),
|
||||
padding: .only(left: left, right: right),
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../media/ids.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
import '../i18n/strings.g.dart';
|
||||
@@ -139,11 +140,11 @@ Future<DownloadResult?> showDownloadOptionsAndQueue(
|
||||
bool syncRuleUpdated = false;
|
||||
if (keepSynced) {
|
||||
final syncCount = maxCount ?? 0; // 0 means "all unwatched" for the rule
|
||||
final ruleKey = downloadProvider.syncRuleKeyFor(metadata.serverId ?? client.serverId, metadata.id);
|
||||
final ruleKey = downloadProvider.syncRuleKeyFor(ServerId(metadata.serverId ?? client.serverId), metadata.id);
|
||||
syncRuleUpdated = downloadProvider.hasSyncRule(ruleKey);
|
||||
|
||||
await downloadProvider.createSyncRule(
|
||||
serverId: metadata.serverId ?? client.serverId,
|
||||
serverId: ServerId(metadata.serverId ?? client.serverId),
|
||||
ratingKey: metadata.id,
|
||||
targetType: metadata.kind.id.isNotEmpty ? metadata.kind.id : ContentTypes.show,
|
||||
episodeCount: syncCount,
|
||||
@@ -212,13 +213,13 @@ Future<DownloadResult?> showListDownloadOptionsAndQueue(
|
||||
bool syncRuleUpdated = false;
|
||||
|
||||
if (syncChoice == _SyncChoice.keepSynced) {
|
||||
final ruleKey = downloadProvider.syncRuleKeyFor(serverId, rootMetadata.id);
|
||||
final ruleKey = downloadProvider.syncRuleKeyFor(ServerId(serverId), rootMetadata.id);
|
||||
if (downloadProvider.hasSyncRule(ruleKey)) {
|
||||
await downloadProvider.updateSyncRuleFilter(ruleKey, filterString);
|
||||
syncRuleUpdated = true;
|
||||
} else {
|
||||
await downloadProvider.createSyncRule(
|
||||
serverId: serverId,
|
||||
serverId: ServerId(serverId),
|
||||
ratingKey: rootMetadata.id,
|
||||
targetType: targetType,
|
||||
episodeCount: 0,
|
||||
|
||||
@@ -114,7 +114,7 @@ String formatSyncOffset(double offsetMs) {
|
||||
final absMs = offsetMs.abs().round();
|
||||
final durationLocale = _getDurationLocale();
|
||||
|
||||
if (absMs >= 10000) {
|
||||
if (absMs >= 10_000) {
|
||||
final seconds = (offsetMs.abs() / 1000).toStringAsFixed(1);
|
||||
final unit = durationLocale.second(1, true);
|
||||
return '$sign$seconds$unit';
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
String buildGlobalKey(String serverId, String ratingKey) => '$serverId:$ratingKey';
|
||||
import '../media/ids.dart';
|
||||
|
||||
String buildGlobalKey(ServerId serverId, String ratingKey) => '$serverId:$ratingKey';
|
||||
|
||||
/// Separator used by profile-owned rows whose public media identity is still
|
||||
/// [buildGlobalKey]. Profile ids are generated by the app and do not contain
|
||||
@@ -6,7 +8,7 @@ String buildGlobalKey(String serverId, String ratingKey) => '$serverId:$ratingKe
|
||||
const String profileScopedGlobalKeySeparator = '|';
|
||||
|
||||
/// Builds a profile-owned sync-rule key from [profileId] and public media id.
|
||||
String buildProfileScopedGlobalKey(String profileId, String serverId, String ratingKey) {
|
||||
String buildProfileScopedGlobalKey(String profileId, ServerId serverId, String ratingKey) {
|
||||
return '$profileId$profileScopedGlobalKeySeparator${buildGlobalKey(serverId, ratingKey)}';
|
||||
}
|
||||
|
||||
@@ -14,14 +16,14 @@ String buildProfileScopedGlobalKey(String profileId, String serverId, String rat
|
||||
///
|
||||
/// Returns `null` if the key does not contain a colon separator.
|
||||
/// Uses [indexOf] so ratingKeys containing colons are handled correctly.
|
||||
({String serverId, String ratingKey})? parseGlobalKey(String globalKey) {
|
||||
({ServerId serverId, String ratingKey})? parseGlobalKey(String globalKey) {
|
||||
final idx = globalKey.indexOf(':');
|
||||
if (idx < 0) return null;
|
||||
return (serverId: globalKey.substring(0, idx), ratingKey: globalKey.substring(idx + 1));
|
||||
return (serverId: ServerId(globalKey.substring(0, idx)), ratingKey: globalKey.substring(idx + 1));
|
||||
}
|
||||
|
||||
/// Parses a profile-owned sync-rule key, returning `null` for legacy public keys.
|
||||
({String profileId, String serverId, String ratingKey})? parseProfileScopedGlobalKey(String globalKey) {
|
||||
({String profileId, ServerId serverId, String ratingKey})? parseProfileScopedGlobalKey(String globalKey) {
|
||||
final idx = globalKey.indexOf(profileScopedGlobalKeySeparator);
|
||||
if (idx < 0) return null;
|
||||
final publicKey = parseGlobalKey(globalKey.substring(idx + 1));
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'global_key_utils.dart';
|
||||
import '../media/ids.dart';
|
||||
|
||||
/// Mixin providing hierarchical event matching methods.
|
||||
///
|
||||
@@ -11,7 +12,7 @@ mixin HierarchicalEventMixin {
|
||||
|
||||
String get globalKey;
|
||||
|
||||
String get serverId;
|
||||
ServerId get serverId;
|
||||
|
||||
/// Parent chain for hierarchical matching.
|
||||
/// For an episode: [seasonId, showId]
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
/// across mappers, the client, and the playback bundle.
|
||||
library;
|
||||
|
||||
const int _ticksPerMs = 10000;
|
||||
const int _ticksPerMs = 10_000;
|
||||
|
||||
/// Jellyfin ticks → milliseconds. Returns `null` for non-numeric input.
|
||||
int? jellyfinTicksToMs(Object? ticks) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'dart:async';
|
||||
import '../media/ids.dart';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
@@ -92,7 +93,7 @@ Future<void> tuneAndNavigateToLiveTv(
|
||||
return;
|
||||
}
|
||||
|
||||
final genericClient = multiServer.getClientForServer(serverInfo.serverId);
|
||||
final genericClient = multiServer.getClientForServer(ServerId(serverInfo.serverId));
|
||||
if (genericClient == null) {
|
||||
showErrorSnackBar(context, 'Live TV server is not connected.');
|
||||
return;
|
||||
@@ -112,7 +113,7 @@ Future<void> tuneAndNavigateToLiveTv(
|
||||
return;
|
||||
}
|
||||
|
||||
final plexClient = multiServer.getPlexClientForServer(serverInfo.serverId);
|
||||
final plexClient = multiServer.getPlexClientForServer(ServerId(serverInfo.serverId));
|
||||
if (plexClient == null) {
|
||||
appLogger.w('Failed to resolve live stream URL for ${channel.displayName} on ${genericClient.backend.id}');
|
||||
showErrorSnackBar(context, 'Unable to start this live TV channel.');
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import '../media/media_hub.dart';
|
||||
import '../media/ids.dart';
|
||||
import '../media/media_library.dart';
|
||||
import 'global_key_utils.dart';
|
||||
|
||||
@@ -35,13 +36,13 @@ bool sortMediaHubsByLibraryOrder(List<MediaHub> hubs, List<MediaLibrary> library
|
||||
}
|
||||
|
||||
int? _hubLibraryOrderIndex(MediaHub hub, Map<String, int> orderByGlobalKey) {
|
||||
final hubLibraryKey = _globalKey(hub.serverId, hub.libraryId);
|
||||
final hubLibraryKey = _globalKey(serverIdOrNull(hub.serverId), hub.libraryId);
|
||||
final hubIndex = hubLibraryKey == null ? null : orderByGlobalKey[hubLibraryKey];
|
||||
if (hubIndex != null) return hubIndex;
|
||||
|
||||
int? bestIndex;
|
||||
for (final item in hub.items) {
|
||||
final key = _globalKey(item.serverId ?? hub.serverId, item.libraryId);
|
||||
final key = _globalKey(serverIdOrNull(item.serverId ?? hub.serverId), item.libraryId);
|
||||
final index = key == null ? null : orderByGlobalKey[key];
|
||||
if (index != null && (bestIndex == null || index < bestIndex)) {
|
||||
bestIndex = index;
|
||||
@@ -50,7 +51,7 @@ int? _hubLibraryOrderIndex(MediaHub hub, Map<String, int> orderByGlobalKey) {
|
||||
return bestIndex;
|
||||
}
|
||||
|
||||
String? _globalKey(String? serverId, String? libraryId) {
|
||||
String? _globalKey(ServerId? serverId, String? libraryId) {
|
||||
if (serverId == null || libraryId == null) return null;
|
||||
return buildGlobalKey(serverId, libraryId);
|
||||
return buildGlobalKey(ServerId(serverId), libraryId);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../media/ids.dart';
|
||||
import '../media/media_item.dart';
|
||||
import '../media/media_kind.dart';
|
||||
import '../media/media_playlist.dart';
|
||||
@@ -72,7 +73,7 @@ Future<MediaNavigationResult> navigateToMediaItem(
|
||||
if (mi.isLibrarySection) {
|
||||
final sectionKey = mi.librarySectionKey;
|
||||
if (sectionKey != null && mi.serverId != null) {
|
||||
final libraryGlobalKey = buildGlobalKey(mi.serverId!, sectionKey);
|
||||
final libraryGlobalKey = buildGlobalKey(ServerId(mi.serverId!), sectionKey);
|
||||
MainScreenFocusScope.of(context, listen: false)?.selectLibrary?.call(libraryGlobalKey);
|
||||
return MediaNavigationResult.librarySelected;
|
||||
}
|
||||
|
||||
@@ -399,7 +399,7 @@ class MediaServerHttpClient {
|
||||
|
||||
void add(String key, Object? value) {
|
||||
if (value == null) return;
|
||||
if (value is Iterable && value is! String) {
|
||||
if (value is Iterable) {
|
||||
for (final item in value) {
|
||||
add(key, item);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../media/ids.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import '../media/media_item.dart';
|
||||
import '../media/media_library.dart';
|
||||
@@ -24,7 +25,7 @@ extension ProviderExtensions on BuildContext {
|
||||
/// Plex-only flows that have no neutral equivalent (DVR tuning, match).
|
||||
/// Backend-agnostic flows use the [_resolveMediaClient]
|
||||
/// helpers below.
|
||||
PlexClient? _resolveClient(String? serverId) {
|
||||
PlexClient? _resolveClient(ServerId? serverId) {
|
||||
final provider = Provider.of<MultiServerProvider>(this, listen: false);
|
||||
return _resolvePrioritized(serverId, provider.onlineServerIds, provider.getPlexClientForServer);
|
||||
}
|
||||
@@ -32,7 +33,7 @@ extension ProviderExtensions on BuildContext {
|
||||
/// Internal: like [_resolveClient] but throws a localized exception when
|
||||
/// no client is available. The thrown message is the canonical
|
||||
/// `t.errors.noClientAvailable` so callers can surface it directly.
|
||||
PlexClient _requireClient(String? serverId, {bool fallback = true}) {
|
||||
PlexClient _requireClient(ServerId? serverId, {bool fallback = true}) {
|
||||
final provider = Provider.of<MultiServerProvider>(this, listen: false);
|
||||
if (serverId != null) {
|
||||
final client = provider.getPlexClientForServer(serverId);
|
||||
@@ -49,17 +50,17 @@ extension ProviderExtensions on BuildContext {
|
||||
return client;
|
||||
}
|
||||
|
||||
PlexClient getPlexClientForServer(String serverId) => _requireClient(serverId, fallback: false);
|
||||
PlexClient getPlexClientForServer(ServerId serverId) => _requireClient(serverId, fallback: false);
|
||||
|
||||
PlexClient? tryGetPlexClientForServer(String? serverId) {
|
||||
PlexClient? tryGetPlexClientForServer(ServerId? serverId) {
|
||||
if (serverId == null) return null;
|
||||
final provider = Provider.of<MultiServerProvider>(this, listen: false);
|
||||
return provider.getPlexClientForServer(serverId);
|
||||
}
|
||||
|
||||
PlexClient getPlexClientForLibrary(MediaLibrary library) => _requireClient(library.serverId);
|
||||
PlexClient getPlexClientForLibrary(MediaLibrary library) => _requireClient(serverIdOrNull(library.serverId));
|
||||
|
||||
PlexClient getPlexClientWithFallback(String? serverId) => _requireClient(serverId);
|
||||
PlexClient getPlexClientWithFallback(ServerId? serverId) => _requireClient(serverId);
|
||||
|
||||
// ── Backend-neutral helpers ──────────────────────────────────────
|
||||
// These return [MediaServerClient] regardless of backend kind so callers
|
||||
@@ -68,12 +69,12 @@ extension ProviderExtensions on BuildContext {
|
||||
// when you specifically need a [PlexClient] (Plex-only flows like Live TV or
|
||||
// match/fix-match).
|
||||
|
||||
MediaServerClient? _resolveMediaClient(String? serverId) {
|
||||
MediaServerClient? _resolveMediaClient(ServerId? serverId) {
|
||||
final provider = Provider.of<MultiServerProvider>(this, listen: false);
|
||||
return _resolvePrioritized(serverId, provider.onlineServerIds, provider.getClientForServer);
|
||||
}
|
||||
|
||||
MediaServerClient? tryGetMediaClientForServer(String? serverId) {
|
||||
MediaServerClient? tryGetMediaClientForServer(ServerId? serverId) {
|
||||
if (serverId == null) return null;
|
||||
final provider = Provider.of<MultiServerProvider>(this, listen: false);
|
||||
return provider.getClientForServer(serverId);
|
||||
@@ -82,14 +83,14 @@ extension ProviderExtensions on BuildContext {
|
||||
/// Get a [MediaServerClient] for the given serverId. Throws when the
|
||||
/// server isn't registered or is offline. Mirrors the throwing variant of
|
||||
/// the Plex-typed [getPlexClientForServer] helpers.
|
||||
MediaServerClient getMediaClientForServer(String serverId) {
|
||||
MediaServerClient getMediaClientForServer(ServerId serverId) {
|
||||
final c = tryGetMediaClientForServer(serverId);
|
||||
if (c == null) throw Exception(t.errors.noClientAvailable);
|
||||
return c;
|
||||
}
|
||||
|
||||
MediaServerClient getMediaClientForLibrary(MediaLibrary library) {
|
||||
final c = _resolveMediaClient(library.serverId);
|
||||
final c = _resolveMediaClient(serverIdOrNull(library.serverId));
|
||||
if (c == null) throw Exception(t.errors.noClientAvailable);
|
||||
return c;
|
||||
}
|
||||
@@ -98,12 +99,12 @@ extension ProviderExtensions on BuildContext {
|
||||
/// when the server isn't online.
|
||||
MediaServerClient? getMediaClientForItemOrNull(MediaItem item, {bool isOffline = false}) {
|
||||
if (isOffline) return null;
|
||||
return tryGetMediaClientForServer(item.serverId);
|
||||
return tryGetMediaClientForServer(serverIdOrNull(item.serverId));
|
||||
}
|
||||
|
||||
/// Get a [MediaServerClient] for [serverId], falling back to the first
|
||||
/// online server when not found. Throws if no client is available.
|
||||
MediaServerClient getMediaClientWithFallback(String? serverId) {
|
||||
MediaServerClient getMediaClientWithFallback(ServerId? serverId) {
|
||||
final c = _resolveMediaClient(serverId);
|
||||
if (c == null) throw Exception(t.errors.noClientAvailable);
|
||||
return c;
|
||||
@@ -113,19 +114,19 @@ extension ProviderExtensions on BuildContext {
|
||||
/// when no client is registered. Use this for non-critical surfaces (image
|
||||
/// loaders, list cards) that can render a fallback when the client isn't
|
||||
/// available — throwing during `build` would crash the widget instead.
|
||||
MediaServerClient? tryGetMediaClientWithFallback(String? serverId) => _resolveMediaClient(serverId);
|
||||
MediaServerClient? tryGetMediaClientWithFallback(ServerId? serverId) => _resolveMediaClient(serverId);
|
||||
}
|
||||
|
||||
/// Try [preferred] first, then fall back through [fallbacks] in order. Returns
|
||||
/// the first non-null result from [resolve], or `null` if every candidate
|
||||
/// resolves to null.
|
||||
T? _resolvePrioritized<T>(String? preferred, Iterable<String> fallbacks, T? Function(String) resolve) {
|
||||
T? _resolvePrioritized<T>(String? preferred, Iterable<String> fallbacks, T? Function(ServerId) resolve) {
|
||||
if (preferred != null) {
|
||||
final c = resolve(preferred);
|
||||
final c = resolve(ServerId(preferred));
|
||||
if (c != null) return c;
|
||||
}
|
||||
for (final id in fallbacks) {
|
||||
final c = resolve(id);
|
||||
final c = resolve(ServerId(id));
|
||||
if (c != null) return c;
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -34,8 +34,8 @@ double mediaSearchRelevanceScore(MediaItem item, String query) {
|
||||
(value: item.title, weight: 1.0),
|
||||
(value: item.titleSort, weight: 0.98),
|
||||
(value: item.originalTitle, weight: 0.96),
|
||||
(value: item.grandparentTitle, weight: 0.90),
|
||||
(value: item.parentTitle, weight: 0.80),
|
||||
(value: item.grandparentTitle, weight: 0.9),
|
||||
(value: item.parentTitle, weight: 0.8),
|
||||
];
|
||||
|
||||
var best = 0.0;
|
||||
|
||||
@@ -45,7 +45,7 @@ class SmartDeletionHandler {
|
||||
if (progress == null) {
|
||||
return AlertDialog(
|
||||
content: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: .min,
|
||||
children: [const CircularProgressIndicator(), const SizedBox(width: 20), Text(t.downloads.deleting)],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -21,8 +21,8 @@ Future<void> showUpdateAvailableDialog(
|
||||
return AlertDialog(
|
||||
title: Text(title),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: .min,
|
||||
crossAxisAlignment: .start,
|
||||
children: [
|
||||
Text(
|
||||
t.update.versionAvailable(version: latestVersion),
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'dart:async';
|
||||
import '../media/ids.dart';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
@@ -65,8 +66,8 @@ Future<bool?> navigateToVideoPlayer(
|
||||
final manager = context.read<MultiServerProvider>().serverManager;
|
||||
final offlineWatchService = context.read<OfflineWatchSyncService>();
|
||||
final serverId = metadata.serverId ?? '';
|
||||
final mediaClient = serverId.isNotEmpty && (!isOffline || manager.isClientOnline(serverId))
|
||||
? manager.getClient(serverId)
|
||||
final mediaClient = serverId.isNotEmpty && (!isOffline || manager.isClientOnline(ServerId(serverId)))
|
||||
? manager.getClient(ServerId(serverId))
|
||||
: null;
|
||||
|
||||
int mediaIndex = selectedMediaIndex ?? 0;
|
||||
@@ -203,11 +204,11 @@ Future<bool?> navigateToVideoPlayerWithRefresh(
|
||||
Future<void> navigateToWatchTogetherPlayback(
|
||||
BuildContext context, {
|
||||
required String ratingKey,
|
||||
required String serverId,
|
||||
required ServerId serverId,
|
||||
VoidCallback? onBeforeNavigate,
|
||||
}) async {
|
||||
final multiServer = context.read<MultiServerProvider>();
|
||||
final client = multiServer.getClientForServer(serverId);
|
||||
final client = multiServer.getClientForServer(ServerId(serverId));
|
||||
|
||||
if (client == null) {
|
||||
throw const WatchTogetherPlaybackNavigationException('Watch Together server is unavailable');
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import '../media/media_item.dart';
|
||||
import '../media/ids.dart';
|
||||
import 'app_logger.dart';
|
||||
import 'base_notifier.dart';
|
||||
import 'global_key_utils.dart';
|
||||
@@ -18,7 +19,7 @@ class WatchStateEvent with HierarchicalEventMixin {
|
||||
|
||||
/// Server this item belongs to
|
||||
@override
|
||||
final String serverId;
|
||||
final ServerId serverId;
|
||||
|
||||
/// Optional backend-private cache namespace for user-scoped servers.
|
||||
///
|
||||
@@ -60,12 +61,13 @@ class WatchStateEvent with HierarchicalEventMixin {
|
||||
this.viewOffset,
|
||||
this.isNowWatched,
|
||||
this.librarySectionID,
|
||||
}) : globalKey = buildGlobalKey(serverId, itemId);
|
||||
}) : globalKey = buildGlobalKey(ServerId(serverId), itemId);
|
||||
|
||||
/// `serverId:librarySectionID`, matching [MediaLibrary.globalKey]. Null when
|
||||
/// 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;
|
||||
String? get librarySectionGlobalKey =>
|
||||
librarySectionID != null ? buildGlobalKey(ServerId(serverId), librarySectionID!) : null;
|
||||
|
||||
@override
|
||||
String toString() => 'WatchStateEvent($changeType, $globalKey, parents: $parentChain)';
|
||||
@@ -82,7 +84,7 @@ class WatchStateNotifier extends BaseNotifier<WatchStateEvent> {
|
||||
|
||||
WatchStateNotifier._internal();
|
||||
|
||||
Stream<WatchStateEvent> forServer(String serverId) => stream.where((e) => e.serverId == serverId);
|
||||
Stream<WatchStateEvent> forServer(ServerId serverId) => stream.where((e) => e.serverId == serverId);
|
||||
|
||||
Stream<WatchStateEvent> forItem(String itemId) => stream.where((e) => e.affectsItem(itemId));
|
||||
|
||||
@@ -98,7 +100,7 @@ class WatchStateNotifier extends BaseNotifier<WatchStateEvent> {
|
||||
notify(
|
||||
WatchStateEvent(
|
||||
itemId: item.id,
|
||||
serverId: item.serverId ?? '',
|
||||
serverId: ServerId(item.serverId ?? ''),
|
||||
cacheServerId: cacheServerId,
|
||||
changeType: isNowWatched ? WatchStateChangeType.watched : WatchStateChangeType.unwatched,
|
||||
parentChain: item.parentChain,
|
||||
@@ -123,7 +125,7 @@ class WatchStateNotifier extends BaseNotifier<WatchStateEvent> {
|
||||
notify(
|
||||
WatchStateEvent(
|
||||
itemId: item.id,
|
||||
serverId: item.serverId ?? '',
|
||||
serverId: ServerId(item.serverId ?? ''),
|
||||
changeType: WatchStateChangeType.progressUpdate,
|
||||
parentChain: item.parentChain,
|
||||
mediaType: item.kind.id,
|
||||
@@ -139,7 +141,7 @@ class WatchStateNotifier extends BaseNotifier<WatchStateEvent> {
|
||||
notify(
|
||||
WatchStateEvent(
|
||||
itemId: item.id,
|
||||
serverId: item.serverId ?? '',
|
||||
serverId: ServerId(item.serverId ?? ''),
|
||||
changeType: WatchStateChangeType.removedFromContinueWatching,
|
||||
parentChain: item.parentChain,
|
||||
mediaType: item.kind.id,
|
||||
|
||||
Reference in New Issue
Block a user