refactor: remove superseded code paths

This commit is contained in:
edde746
2026-07-12 08:42:19 +02:00
parent dca51a752a
commit bc55aea701
11 changed files with 14 additions and 365 deletions
+14 -19
View File
@@ -22,11 +22,6 @@ extension DownloadDatabaseOperations on AppDatabase {
await (delete(downloadOwners)..where((t) => t.profileId.equals(profileId) & t.globalKey.equals(globalKey))).go();
}
Future<void> removeDownloadOwnersForProfile(String profileId) async {
if (profileId.isEmpty) return;
await (delete(downloadOwners)..where((t) => t.profileId.equals(profileId))).go();
}
Future<void> clearAllDownloadOwners() async {
await delete(downloadOwners).go();
}
@@ -59,12 +54,7 @@ extension DownloadDatabaseOperations on AppDatabase {
final connectionRows = await select(connections).get();
final connectionIds = connectionRows.map((row) => row.id).toSet();
return candidates
.where((row) {
if (localProfileIds.contains(row.profileId)) return true;
final plexHome = parsePlexHomeProfileId(row.profileId);
if (plexHome != null) return connectionIds.contains(plexHome.accountConnectionId);
return localProfileIds.isEmpty;
})
.where((row) => _isValidDownloadOwner(row, localProfileIds: localProfileIds, connectionIds: connectionIds))
.toList(growable: false);
}
@@ -83,16 +73,10 @@ extension DownloadDatabaseOperations on AppDatabase {
final owners = await select(downloadOwners).get();
final localProfileIds = (await select(profiles).get()).map((row) => row.id).toSet();
final connectionIds = (await select(connections).get()).map((row) => row.id).toSet();
bool valid(DownloadOwnerItem owner) {
if (localProfileIds.contains(owner.profileId)) return true;
final plexHome = parsePlexHomeProfileId(owner.profileId);
if (plexHome != null) return connectionIds.contains(plexHome.accountConnectionId);
return localProfileIds.isEmpty;
}
final ownedKeys = <String>{
for (final owner in owners)
if (valid(owner)) owner.globalKey,
if (_isValidDownloadOwner(owner, localProfileIds: localProfileIds, connectionIds: connectionIds))
owner.globalKey,
};
for (final row in rows) {
if (!ownedKeys.contains(row.globalKey)) {
@@ -333,3 +317,14 @@ extension DownloadDatabaseOperations on AppDatabase {
return item?.bgTaskId;
}
}
bool _isValidDownloadOwner(
DownloadOwnerItem owner, {
required Set<String> localProfileIds,
required Set<String> connectionIds,
}) {
if (localProfileIds.contains(owner.profileId)) return true;
final plexHome = parsePlexHomeProfileId(owner.profileId);
if (plexHome != null) return connectionIds.contains(plexHome.accountConnectionId);
return localProfileIds.isEmpty;
}
-35
View File
@@ -198,31 +198,6 @@ class DownloadProvider extends ChangeNotifier with DisposableChangeNotifierMixin
await _releaseDownloadsForProfileWhere(profileId, (_) => true);
}
Future<void> deleteAllDownloads() async {
final downloads = await _downloadManager.getAllDownloads();
for (final row in downloads) {
await _downloadManager.deleteDownload(row.globalKey);
}
await _database.clearAllDownloadOwners();
try {
final artworkDirectory = await DownloadStorageService.instance.getArtworkDirectory();
if (await artworkDirectory.exists()) {
await artworkDirectory.delete(recursive: true);
}
} catch (e, stackTrace) {
appLogger.w('Failed to delete shared download artwork directory', error: e, stackTrace: stackTrace);
}
_downloads.clear();
_metadata.clear();
_artworkPaths.clear();
_queueing.clear();
_ownedDownloadKeys.clear();
_deletionProgress.clear();
safeNotifyListeners();
}
/// Preserve physical downloads across a full logout while detaching them
/// from profiles that are about to be deleted. The next selected profile
/// adopts the ownerless rows through [_loadDownloadOwners].
@@ -1695,16 +1670,6 @@ class DownloadProvider extends ChangeNotifier with DisposableChangeNotifierMixin
return buildProfileScopedGlobalKey(owner, ServerId(serverId), ratingKey);
}
String syncRuleKeyForGlobalKey(String globalKey) {
final scoped = parseProfileScopedGlobalKey(globalKey);
if (scoped != null) {
return syncRuleKeyFor(scoped.serverId, scoped.ratingKey, profileId: scoped.profileId);
}
final parsed = parseGlobalKey(globalKey);
if (parsed == null) return globalKey;
return syncRuleKeyFor(parsed.serverId, parsed.ratingKey);
}
String syncRuleKeyForClient(MediaServerClient client, String ratingKey, {ServerId? serverId}) {
return syncRuleKeyFor(serverId ?? client.serverId, ratingKey);
}
-8
View File
@@ -178,14 +178,6 @@ class _SeasonEpisodePager {
}
}
void updateEpisode(String seasonId, int index, MediaItem updated) {
final state = _states[seasonId];
if (state == null || index < 0 || index >= state.items.length) return;
final next = List<MediaItem>.of(state.items);
next[index] = updated;
_states[seasonId] = state.replaceItems(next);
}
void patchEpisode(String episodeId, MediaItem Function(MediaItem existing) patch) {
for (final entry in _states.entries.toList()) {
var changed = false;
@@ -110,29 +110,6 @@ class CompanionRemotePeerService with KeepaliveMixin {
}
}
/// Create a host session — starts WebSocket server, returns local addresses and port.
Future<({List<String> addresses, int port})> createSession(
String deviceName,
String platform,
List<int> homeSecret,
String clientIdentifier,
List<String> homeUserUUIDs,
) async {
final auth = RemoteAuthService.instance;
return createSessionForContexts(deviceName, platform, [
RemoteAuthContext(
id: auth.computeAuthContextId(homeSecret),
backend: 'legacy',
connectionId: '',
homeSecret: homeSecret,
discoveryKey: const [],
clientIdentifier: clientIdentifier,
userUuid: homeUserUUIDs.isEmpty ? '' : homeUserUUIDs.first,
allowedUserUuids: homeUserUUIDs,
),
]);
}
/// Create a host session that accepts any of the provided remote identities.
Future<({List<String> addresses, int port})> createSessionForContexts(
String deviceName,
@@ -425,37 +402,6 @@ class CompanionRemotePeerService with KeepaliveMixin {
}
}
/// Join a host session as a remote client.
Future<void> joinSession(
String deviceName,
String platform,
String hostAddress,
List<int> homeSecret,
String hostClientId,
String userUUID,
String clientIdentifier,
) async {
final auth = RemoteAuthService.instance;
final context = RemoteAuthContext(
id: auth.computeAuthContextId(homeSecret),
backend: 'legacy',
connectionId: '',
homeSecret: homeSecret,
discoveryKey: const [],
clientIdentifier: clientIdentifier,
userUuid: userUUID,
allowedUserUuids: [userUUID],
);
return joinSessionWithContexts(
deviceName,
platform,
hostAddress,
[context],
authContextId: context.id,
expectedHostClientId: hostClientId,
);
}
/// Join a host session with any local auth context that the host also supports.
Future<void> joinSessionWithContexts(
String deviceName,
@@ -720,37 +666,6 @@ class CompanionRemotePeerService with KeepaliveMixin {
);
}
/// Race WebSocket connections to multiple host addresses in parallel.
Future<String> joinSessionRacing(
String deviceName,
String platform,
List<String> hostAddresses,
List<int> homeSecret,
String hostClientId,
String userUUID,
String clientIdentifier,
) async {
final auth = RemoteAuthService.instance;
final context = RemoteAuthContext(
id: auth.computeAuthContextId(homeSecret),
backend: 'legacy',
connectionId: '',
homeSecret: homeSecret,
discoveryKey: const [],
clientIdentifier: clientIdentifier,
userUuid: userUUID,
allowedUserUuids: [userUUID],
);
return joinSessionRacingWithContexts(
deviceName,
platform,
hostAddresses,
[context],
authContextId: context.id,
expectedHostClientId: hostClientId,
);
}
/// Race WebSocket connections and authenticate with the selected shared identity.
Future<String> joinSessionRacingWithContexts(
String deviceName,
@@ -59,34 +59,6 @@ class LanDiscoveryService {
// ── Host: Broadcasting ──
Future<void> startBroadcasting({
required List<int> discoveryKey,
required String deviceName,
required String platform,
required String clientId,
required int wsPort,
required List<String> ips,
}) async {
return startBroadcastingForContexts(
contexts: [
RemoteAuthContext(
id: clientId,
backend: 'legacy',
connectionId: clientId,
homeSecret: const [],
discoveryKey: discoveryKey,
clientIdentifier: clientId,
userUuid: '',
allowedUserUuids: const [],
),
],
deviceName: deviceName,
platform: platform,
wsPort: wsPort,
ips: ips,
);
}
Future<void> startBroadcastingForContexts({
required List<RemoteAuthContext> contexts,
required String deviceName,
@@ -168,23 +140,6 @@ class LanDiscoveryService {
// ── Client: Listening ──
/// Start listening for host beacons.
/// Returns a stream of currently-visible hosts, updated on each beacon or stale cleanup.
Stream<List<DiscoveredHost>> startListening({required List<int> discoveryKey}) {
return startListeningForContexts([
RemoteAuthContext(
id: '',
backend: 'legacy',
connectionId: '',
homeSecret: const [],
discoveryKey: discoveryKey,
clientIdentifier: '',
userUuid: '',
allowedUserUuids: const [],
),
]);
}
Stream<List<DiscoveredHost>> startListeningForContexts(List<RemoteAuthContext> contexts) {
_stopListeningInternal();
_discoveredHosts.clear();
-16
View File
@@ -528,22 +528,6 @@ class MultiServerManager {
return bound;
}
/// Tear down all servers belonging to the given Plex account. Called when
/// the user removes the account from the Connections screen. Idempotent —
/// servers already gone are silently skipped.
void removePlexAccount(PlexAccountConnection connection) {
for (final server in connection.servers) {
final id = server.clientIdentifier;
final client = _clients.remove(id);
if (client != null) _closeClient(client);
_plexServers.remove(id);
_serverStatus.remove(id);
_authErrorServers.remove(id);
_clientIdByServer.remove(id);
}
_statusController.add(Map.from(_serverStatus));
}
/// Add a Jellyfin server backed by an authenticated [JellyfinConnection].
/// Returns true on success.
///
-36
View File
@@ -769,42 +769,6 @@ class PlexServer {
return _ConnectionCandidate(httpsConnection, httpsUrl, resultingIsPlexDirect, true);
}
Future<PlexConnection?> upgradeConnectionToHttps(PlexConnection current) async {
if (current.uri.startsWith('https://')) {
return current;
}
final baseConnection = _findMatchingBaseConnection(current);
if (baseConnection == null) {
return null;
}
final candidate = _ConnectionCandidate(
baseConnection,
current.uri,
current.uri.contains('.plex.direct'),
current.uri.startsWith('https://'),
);
final upgradedCandidate = await _upgradeCandidateToHttpsIfPossible(candidate);
if (upgradedCandidate == null) {
return null;
}
return _updateConnectionUrl(upgradedCandidate.connection, upgradedCandidate.url);
}
PlexConnection? _findMatchingBaseConnection(PlexConnection connection) {
for (final base in connections) {
final sameAddress = base.address == connection.address;
final samePort = base.port == connection.port;
final sameLocal = base.local == connection.local;
final sameRelay = base.relay == connection.relay;
if (sameAddress && samePort && sameLocal && sameRelay) {
return base;
}
}
return null;
}
/// Select the best candidate considering priority, latency, and URL type preference
_ConnectionCandidate? _selectBestCandidateWithLatency(Map<_ConnectionCandidate, ConnectionTestResult> results) {
// Group candidates by connection type (local/remote/relay)
-53
View File
@@ -2083,11 +2083,6 @@ class PlexClient
Future<_LibraryContentResult> _getPlaylist(String playlistId, {int? start, int? size, AbortController? abort}) =>
_fetchPaginatedList('/playlists/$playlistId/items', start: start, size: size, abort: abort);
/// Fetch every page of a playlist's items. For callers that need the full list
/// (downloads, sync rules, context-menu shuffle).
Future<List<PlexMetadataDto>> _fetchAllPlaylistItemsDto(String playlistId) =>
_fetchAllPages((start, size, abort) => _getPlaylist(playlistId, start: start, size: size, abort: abort));
/// Get all playlists.
/// Filters by playlistType=video by default.
/// Set smart to true/false to filter smart playlists, or null for all.
@@ -2509,22 +2504,6 @@ class PlexClient
librarySectionTitle: librarySectionTitle,
);
/// Fetch every item in a collection (downloads, sync rules, context-menu shuffle).
Future<List<PlexMetadataDto>> _fetchAllCollectionItemsDto(
String collectionId, {
String? librarySectionID,
String? librarySectionTitle,
}) => _fetchAllPages(
(start, size, abort) => _getCollectionItems(
collectionId,
start: start,
size: size,
abort: abort,
librarySectionID: librarySectionID,
librarySectionTitle: librarySectionTitle,
),
);
/// Get media featuring a specific person (actor/director), paginated.
Future<_LibraryContentResult> _getPersonMedia(String personId, {int? start, int? size, AbortController? abort}) =>
_fetchPaginatedList('/library/people/$personId/media', start: start, size: size, abort: abort);
@@ -2941,12 +2920,6 @@ class PlexClient
/// Get library-specific playlists
/// Filters playlists by checking if they contain items from the specified library
/// This is a client-side filter since the API doesn't support sectionId for playlists
Future<List<PlexPlaylistDto>> _getLibraryPlaylists({String playlistType = 'video'}) {
// For now, return all video playlists
// Future enhancement: filter by checking playlist items' library
return _getPlaylists(playlistType: playlistType);
}
/// Scan/refresh a library section to detect new files
Future<void> scanLibrary(String sectionId) async {
await _getWithFailover('/library/sections/$sectionId/refresh');
@@ -4213,26 +4186,6 @@ class PlexClient
);
}
/// Plex-specific: full collection contents across pages.
Future<List<MediaItem>> fetchAllCollectionItemsAsMediaItems(
String collectionId, {
String? libraryId,
String? libraryTitle,
}) async {
final raw = await _fetchAllCollectionItemsDto(
collectionId,
librarySectionID: libraryId,
librarySectionTitle: libraryTitle,
);
return raw.map((m) => PlexMappers.mediaItem(m)).toList();
}
/// Plex-specific: full playlist contents across pages.
Future<List<MediaItem>> fetchAllPlaylistItemsAsMediaItems(String playlistId) async {
final raw = await _fetchAllPlaylistItemsDto(playlistId);
return raw.map((m) => PlexMappers.mediaItem(m)).toList();
}
@override
Future<LibraryPage<MediaItem>> fetchPersonMediaPage(
String personId, {
@@ -4330,12 +4283,6 @@ class PlexClient
return raw.map((m) => PlexMappers.mediaItem(m)).toList();
}
/// Plex-specific: library-scoped playlists.
Future<List<MediaPlaylist>> fetchLibraryPlaylists({String playlistType = 'video'}) async {
final raw = await _getLibraryPlaylists(playlistType: playlistType);
return raw.map((p) => PlexMappers.mediaPlaylist(p)).toList();
}
/// Plex-specific: paginated library content with raw Plex filter map,
/// returning neutral [MediaItem]s. The aggregation bridge uses this when it
/// has Plex-specific filter strings (`unwatched=1`, `genre=...`) to forward.
@@ -28,7 +28,6 @@ mixin _PlexLiveTvClientMethods on MediaServerCacheMixin {
Map<String, dynamic>? _getMediaContainer(MediaServerResponse response);
PlexMetadataDto _createTaggedMetadata(Map<String, dynamic> json);
List<PlexMetadataDto> _extractMetadataList(MediaServerResponse response);
Future<List<T>> _wrapListApiCall<T>(
Future<MediaServerResponse> Function() apiCall,
@@ -1061,15 +1060,6 @@ mixin _PlexLiveTvClientMethods on MediaServerCacheMixin {
return '${config.baseUrl}$streamPath'.withPlexToken(config.token);
}
/// Get active live TV sessions
Future<List<PlexMetadataDto>> _getLiveTvSessions() {
return _wrapListApiCall<PlexMetadataDto>(
() => _http.get('/livetv/sessions'),
_extractMetadataList,
'Failed to get live TV sessions',
);
}
Future<List<LiveTvSession>> getLiveTvSessionsDetailed() async {
final response = await _getWithFailover('/livetv/sessions');
return _extractContainerList(response, const [
@@ -1153,12 +1143,6 @@ mixin _PlexLiveTvClientMethods on MediaServerCacheMixin {
}
}
/// Plex-specific: live TV sessions (active recordings/playback).
Future<List<MediaItem>> fetchLiveTvSessions() async {
final raw = await _getLiveTvSessions();
return raw.map((m) => PlexMappers.mediaItem(m)).toList();
}
@override
LiveTvSupport get liveTv => _PlexLiveTvSupport(this as PlexClient);
}