refactor(detail): share the season-download filter and the download-retry pipeline

The downloaded-episodes-for-a-season filter+sort was written out three times in media_detail_screen (with drifting show-id expressions, preserved per call site), and the failed/cancelled download branches in the action buttons duplicated the same six-step retry pipeline verbatim. One helper each.
This commit is contained in:
edde746
2026-08-17 19:02:20 +02:00
parent 3665d416c4
commit c390e0fb0c
2 changed files with 33 additions and 40 deletions
+23 -29
View File
@@ -437,6 +437,27 @@ extension _MediaDetailActionButtons on _MediaDetailScreenState {
);
}
/// Shared retry pipeline for failed/cancelled downloads: confirm download
/// restrictions, re-resolve the version, then replace the existing download
/// with a fresh queue entry.
Future<void> _retryDownload(DownloadProvider downloadProvider, MediaItem metadata, String globalKey) async {
if (!await confirmBackgroundDownloadRestrictions(context) || !mounted) return;
final client = _getMediaClientForMetadata(context);
if (client == null) return;
final versionConfig = await _resolveDownloadVersion(context, metadata, client);
if (versionConfig == null || !mounted) return;
await downloadProvider.deleteDownload(globalKey);
try {
await downloadProvider.queueDownload(metadata, client, versionConfig: versionConfig);
if (mounted) showSuccessSnackBar(context, t.downloads.downloadQueued);
} on CellularDownloadBlockedException {
if (mounted) showErrorSnackBar(context, t.settings.cellularDownloadBlocked);
}
}
Future<void> _handleDownloadButtonPressed(MediaItem metadata) async {
final downloadProvider = context.read<DownloadProvider>();
final globalKey = metadata.globalKey;
@@ -460,21 +481,7 @@ extension _MediaDetailActionButtons on _MediaDetailScreenState {
if (progress?.status == DownloadStatus.failed) {
// A failed download is the likeliest moment for the restriction to be
// the actual cause, so check before spending another attempt on it.
if (!await confirmBackgroundDownloadRestrictions(context) || !mounted) return;
final client = _getMediaClientForMetadata(context);
if (client == null) return;
final versionConfig = await _resolveDownloadVersion(context, metadata, client);
if (versionConfig == null || !mounted) return;
await downloadProvider.deleteDownload(globalKey);
try {
await downloadProvider.queueDownload(metadata, client, versionConfig: versionConfig);
if (mounted) showSuccessSnackBar(context, t.downloads.downloadQueued);
} on CellularDownloadBlockedException {
if (mounted) showErrorSnackBar(context, t.settings.cellularDownloadBlocked);
}
await _retryDownload(downloadProvider, metadata, globalKey);
return;
}
@@ -491,20 +498,7 @@ extension _MediaDetailActionButtons on _MediaDetailScreenState {
await downloadProvider.deleteDownload(globalKey);
if (mounted) showSuccessSnackBar(context, t.downloads.downloadDeleted);
} else if (retry && mounted) {
if (!await confirmBackgroundDownloadRestrictions(context) || !mounted) return;
final client = _getMediaClientForMetadata(context);
if (client == null) return;
final versionConfig = await _resolveDownloadVersion(context, metadata, client);
if (versionConfig == null || !mounted) return;
await downloadProvider.deleteDownload(globalKey);
try {
await downloadProvider.queueDownload(metadata, client, versionConfig: versionConfig);
if (mounted) showSuccessSnackBar(context, t.downloads.downloadQueued);
} on CellularDownloadBlockedException {
if (mounted) showErrorSnackBar(context, t.settings.cellularDownloadBlocked);
}
await _retryDownload(downloadProvider, metadata, globalKey);
}
return;
}
+10 -11
View File
@@ -1540,12 +1540,16 @@ class _MediaDetailScreenState extends State<MediaDetailScreen>
}
}
/// Downloaded episodes of [showId] belonging to the season with [seasonIndex], sorted by episode number.
List<MediaItem> _downloadedEpisodesForSeason(DownloadProvider downloadProvider, String showId, int? seasonIndex) {
return downloadProvider.getDownloadedEpisodesForShow(showId).where((ep) => ep.parentIndex == seasonIndex).toList()
..sort((a, b) => (a.index ?? 0).compareTo(b.index ?? 0));
}
/// Load episodes from downloaded content for a season
void _loadEpisodesFromDownloads() {
final downloadProvider = context.read<DownloadProvider>();
final allEpisodes = downloadProvider.getDownloadedEpisodesForShow(_metadata.parentId ?? '');
final seasonEpisodes = allEpisodes.where((ep) => ep.parentIndex == _metadata.index).toList()
..sort((a, b) => (a.index ?? 0).compareTo(b.index ?? 0));
final seasonEpisodes = _downloadedEpisodesForSeason(downloadProvider, _metadata.parentId ?? '', _metadata.index);
setState(() {
_allEpisodes = _allEpisodes.completeInitialLoad(seasonEpisodes, seasonEpisodes.length);
@@ -1619,9 +1623,7 @@ class _MediaDetailScreenState extends State<MediaDetailScreen>
if (widget.isOffline) {
// Offline: load from downloads (already the complete set).
final downloadProvider = context.read<DownloadProvider>();
final allEpisodes = downloadProvider.getDownloadedEpisodesForShow(_metadata.id);
final seasonEpisodes = allEpisodes.where((ep) => ep.parentIndex == season.index).toList()
..sort((a, b) => (a.index ?? 0).compareTo(b.index ?? 0));
final seasonEpisodes = _downloadedEpisodesForSeason(downloadProvider, _metadata.id, season.index);
_completeSeasonEpisodesLoad(
seasonIndex: seasonIndex,
seasonId: seasonId,
@@ -2895,12 +2897,9 @@ class _MediaDetailScreenState extends State<MediaDetailScreen>
MediaItem? firstEpisode;
if (!mounted) return;
if (widget.isOffline) {
// In offline mode, get episodes from downloads
// In offline mode, get episodes from downloads (filtered to this season).
final downloadProvider = context.read<DownloadProvider>();
final allEpisodes = downloadProvider.getDownloadedEpisodesForShow(_metadata.id);
// Filter to episodes of this season
final episodes = allEpisodes.where((ep) => ep.parentIndex == firstSeason.index).toList()
..sort((a, b) => (a.index ?? 0).compareTo(b.index ?? 0));
final episodes = _downloadedEpisodesForSeason(downloadProvider, _metadata.id, firstSeason.index);
firstEpisode = episodes.isEmpty ? null : episodes.first;
} else {
final client = getServerBoundMediaClient(context);