diff --git a/lib/screens/media_detail/action_buttons.dart b/lib/screens/media_detail/action_buttons.dart index b0fbcc3bb..d715edb14 100644 --- a/lib/screens/media_detail/action_buttons.dart +++ b/lib/screens/media_detail/action_buttons.dart @@ -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 _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 _handleDownloadButtonPressed(MediaItem metadata) async { final downloadProvider = context.read(); 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; } diff --git a/lib/screens/media_detail_screen.dart b/lib/screens/media_detail_screen.dart index e190cb1d0..d976329e2 100644 --- a/lib/screens/media_detail_screen.dart +++ b/lib/screens/media_detail_screen.dart @@ -1540,12 +1540,16 @@ class _MediaDetailScreenState extends State } } + /// Downloaded episodes of [showId] belonging to the season with [seasonIndex], sorted by episode number. + List _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(); - 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 if (widget.isOffline) { // Offline: load from downloads (already the complete set). final downloadProvider = context.read(); - 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 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(); - 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);