@@ -11,10 +11,13 @@ import 'package:anytime/entities/episode.dart';
1111import 'package:anytime/repository/repository.dart' ;
1212import 'package:anytime/services/download/download_manager.dart' ;
1313import 'package:anytime/services/download/download_service.dart' ;
14+ import 'package:anytime/services/settings/settings_service.dart' ;
1415import 'package:collection/collection.dart' show IterableExtension;
16+ import 'package:flutter_downloader/flutter_downloader.dart' ;
1517import 'package:logging/logging.dart' ;
1618import 'package:mp3_info/mp3_info.dart' ;
1719import 'package:rxdart/rxdart.dart' ;
20+ import 'package:synchronized/synchronized.dart' ;
1821
1922/// An implementation of a [DownloadService] that handles downloading
2023/// of episodes on mobile.
@@ -23,18 +26,33 @@ class MobileDownloadService extends DownloadService {
2326
2427 final log = Logger ('MobileDownloadService' );
2528 final Repository repository;
29+ final SettingsService settingsService;
2630 final DownloadManager downloadManager;
27-
28- MobileDownloadService ({required this .repository, required this .downloadManager}) {
29- downloadManager.downloadProgress.pipe (downloadProgress);
30- downloadProgress.listen ((progress) {
31- _updateDownloadProgress (progress);
32- });
31+ late final StreamSubscription _downloadProgressSubscription;
32+
33+ /// Lock ensures we wait for task creation and local save
34+ /// before handling subsequent [Download update events] .
35+ final _downloadProgressLock = Lock ();
36+
37+ MobileDownloadService ({
38+ required this .repository,
39+ required this .downloadManager,
40+ required this .settingsService,
41+ }) {
42+ _downloadProgressSubscription = downloadManager.downloadProgress.listen (
43+ (event) async => await _downloadProgressLock.synchronized (
44+ () {
45+ downloadProgress.add (event);
46+ _updateDownloadProgress (event);
47+ },
48+ ),
49+ );
3350 }
3451
3552 @override
3653 void dispose () {
3754 downloadManager.dispose ();
55+ _downloadProgressSubscription.cancel ();
3856 }
3957
4058 @override
@@ -91,16 +109,22 @@ class MobileDownloadService extends DownloadService {
91109 /// the URL before calling download and ensure it is https.
92110 var url = await resolveUrl (episode.contentUrl! , forceHttps: true );
93111
94- final taskId = await downloadManager.enqueueTask (url, downloadPath, filename);
112+ await _downloadProgressLock.synchronized (() async {
113+ final taskId = await downloadManager.enqueueTask (
114+ url,
115+ downloadPath,
116+ filename! ,
117+ );
95118
96- // Update the episode with download data
97- episode.filepath = episodePath;
98- episode.filename = filename;
99- episode.downloadTaskId = taskId;
100- episode.downloadState = DownloadState .downloading;
101- episode.downloadPercentage = 0 ;
119+ // Update the episode with download data
120+ episode.filepath = episodePath;
121+ episode.filename = filename;
122+ episode.downloadTaskId = taskId;
123+ episode.downloadState = DownloadState .downloading;
124+ episode.downloadPercentage = 0 ;
102125
103- await repository.saveEpisode (episode);
126+ await repository.saveEpisode (episode);
127+ });
104128
105129 return Future .value (true );
106130 }
@@ -109,6 +133,41 @@ class MobileDownloadService extends DownloadService {
109133 return Future .value (false );
110134 }
111135
136+ @override
137+ Future <void > deleteDownload (Episode episode) async => _downloadProgressLock.synchronized (() async {
138+ // If this episode is currently downloading, cancel the download first.
139+ if (episode.downloadState == DownloadState .downloaded) {
140+ if (settingsService.markDeletedEpisodesAsPlayed) {
141+ episode.played = true ;
142+ }
143+ } else if (episode.downloadState == DownloadState .downloading && episode.downloadPercentage! < 100 ) {
144+ await FlutterDownloader .cancel (taskId: episode.downloadTaskId! );
145+ }
146+
147+ episode.downloadTaskId = null ;
148+ episode.downloadPercentage = 0 ;
149+ episode.position = 0 ;
150+ episode.downloadState = DownloadState .none;
151+
152+ if (episode.transcriptId != null && episode.transcriptId! > 0 ) {
153+ await repository.deleteTranscriptById (episode.transcriptId! );
154+ }
155+
156+ await repository.saveEpisode (episode);
157+
158+ if (await hasStoragePermission ()) {
159+ final f = File .fromUri (Uri .file (await resolvePath (episode)));
160+
161+ log.fine ('Deleting file ${f .path }' );
162+
163+ if (await f.exists ()) {
164+ f.delete ();
165+ }
166+ }
167+
168+ return ;
169+ });
170+
112171 @override
113172 Future <Episode ?> findEpisodeByTaskId (String taskId) {
114173 return repository.findEpisodeByTaskId (taskId);
0 commit comments