refactor: trakt/download hardening and cleanup sweep
This commit is contained in:
@@ -5,9 +5,11 @@ import '../models/download_models.dart';
|
||||
import '../models/plex_metadata.dart';
|
||||
import '../utils/app_logger.dart';
|
||||
import '../utils/content_utils.dart';
|
||||
import '../utils/episode_collection.dart';
|
||||
import '../utils/global_key_utils.dart';
|
||||
import 'download_manager_service.dart';
|
||||
import 'multi_server_manager.dart';
|
||||
import 'offline_mode_source.dart';
|
||||
import 'plex_client.dart';
|
||||
|
||||
/// Sync-rule filter values stored in `SyncRules.downloadFilter`.
|
||||
@@ -40,10 +42,18 @@ class SyncRuleExecutor {
|
||||
static const Duration _cooldownWifi = Duration(minutes: 30);
|
||||
static const Duration _cooldownCellular = Duration(hours: 3);
|
||||
|
||||
OfflineModeSource? _offlineSource;
|
||||
|
||||
SyncRuleExecutor({required AppDatabase database}) : _database = database;
|
||||
|
||||
bool get isExecuting => _isExecuting;
|
||||
|
||||
/// Inject the offline-mode source so we can skip running rules when the
|
||||
/// device has no Plex connectivity (every `getChildren` call would fail).
|
||||
void setOfflineSource(OfflineModeSource? source) {
|
||||
_offlineSource = source;
|
||||
}
|
||||
|
||||
/// Execute every enabled sync rule.
|
||||
///
|
||||
/// The adaptive cooldown (30 min on WiFi/Ethernet, 3 h on cellular) only
|
||||
@@ -67,6 +77,11 @@ class SyncRuleExecutor {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (_offlineSource?.isOffline ?? false) {
|
||||
appLogger.d('Skipping sync rules — offline');
|
||||
return [];
|
||||
}
|
||||
|
||||
// Read connectivity once for both the WiFi-only gate and the cooldown pick.
|
||||
final List<ConnectivityResult> connectivity = await _readConnectivity();
|
||||
if (await DownloadManagerService.shouldBlockDownloadOnCellularWith(connectivity)) {
|
||||
@@ -134,6 +149,11 @@ class SyncRuleExecutor {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (_offlineSource?.isOffline ?? false) {
|
||||
appLogger.d('Skipping single sync rule $globalKey — offline');
|
||||
return null;
|
||||
}
|
||||
|
||||
if (await DownloadManagerService.shouldBlockDownloadOnCellular()) {
|
||||
appLogger.d('Skipping single sync rule $globalKey — cellular download blocked');
|
||||
return null;
|
||||
@@ -210,9 +230,9 @@ class SyncRuleExecutor {
|
||||
}) async {
|
||||
final unwatchedEpisodes = <PlexMetadata>[];
|
||||
if (rule.targetType == ContentTypes.show) {
|
||||
await _collectEpisodesForShow(client, rule.ratingKey, unwatchedOnly: true, out: unwatchedEpisodes);
|
||||
await collectEpisodesForShow(client, rule.ratingKey, unwatchedOnly: true, out: unwatchedEpisodes);
|
||||
} else {
|
||||
await _collectEpisodesForSeason(client, rule.ratingKey, unwatchedOnly: true, out: unwatchedEpisodes);
|
||||
await collectEpisodesForSeason(client, rule.ratingKey, unwatchedOnly: true, out: unwatchedEpisodes);
|
||||
}
|
||||
|
||||
if (unwatchedEpisodes.isEmpty) {
|
||||
@@ -333,9 +353,9 @@ class SyncRuleExecutor {
|
||||
if (unwatchedOnly && item.isWatched && !item.hasActiveProgress) break;
|
||||
out.add(item);
|
||||
case ContentTypes.show:
|
||||
await _collectEpisodesForShow(client, item.ratingKey, unwatchedOnly: unwatchedOnly, out: out);
|
||||
await collectEpisodesForShow(client, item.ratingKey, unwatchedOnly: unwatchedOnly, out: out);
|
||||
case ContentTypes.season:
|
||||
await _collectEpisodesForSeason(client, item.ratingKey, unwatchedOnly: unwatchedOnly, out: out);
|
||||
await collectEpisodesForSeason(client, item.ratingKey, unwatchedOnly: unwatchedOnly, out: out);
|
||||
default:
|
||||
// Skip music, clips, nested collections/playlists, unknown types.
|
||||
break;
|
||||
@@ -343,34 +363,6 @@ class SyncRuleExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _collectEpisodesForShow(
|
||||
PlexClient client,
|
||||
String showRatingKey, {
|
||||
required bool unwatchedOnly,
|
||||
required List<PlexMetadata> out,
|
||||
}) async {
|
||||
final seasons = await client.getChildren(showRatingKey);
|
||||
for (final season in seasons) {
|
||||
if (season.type == ContentTypes.season) {
|
||||
await _collectEpisodesForSeason(client, season.ratingKey, unwatchedOnly: unwatchedOnly, out: out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _collectEpisodesForSeason(
|
||||
PlexClient client,
|
||||
String seasonRatingKey, {
|
||||
required bool unwatchedOnly,
|
||||
required List<PlexMetadata> out,
|
||||
}) async {
|
||||
final episodes = await client.getChildren(seasonRatingKey);
|
||||
for (final ep in episodes) {
|
||||
if (ep.type != ContentTypes.episode) continue;
|
||||
if (unwatchedOnly && ep.isWatched && !ep.hasActiveProgress) continue;
|
||||
out.add(ep);
|
||||
}
|
||||
}
|
||||
|
||||
static bool _isActiveDownload(DownloadProgress? p) =>
|
||||
p != null &&
|
||||
(p.status == DownloadStatus.completed ||
|
||||
|
||||
Reference in New Issue
Block a user