diff --git a/lib/providers/apps_provider.dart b/lib/providers/apps_provider.dart index a28a8a9..0132b4d 100644 --- a/lib/providers/apps_provider.dart +++ b/lib/providers/apps_provider.dart @@ -151,13 +151,15 @@ Future downloadFileWithRetry(String url, String fileName, {bool useExisting = true, Map? headers, int retries = 3, - bool allowInsecure = false}) async { + bool allowInsecure = false, + LogsProvider? logs}) async { try { return await downloadFile( url, fileName, fileNameHasExt, onProgress, destDir, useExisting: useExisting, headers: headers, - allowInsecure: allowInsecure); + allowInsecure: allowInsecure, + logs: logs); } catch (e) { if (retries > 0 && e is ClientException) { await Future.delayed(const Duration(seconds: 5)); @@ -166,7 +168,8 @@ Future downloadFileWithRetry(String url, String fileName, useExisting: useExisting, headers: headers, retries: (retries - 1), - allowInsecure: allowInsecure); + allowInsecure: allowInsecure, + logs: logs); } else { rethrow; } @@ -219,7 +222,8 @@ Future downloadFile(String url, String fileName, bool fileNameHasExt, Function? onProgress, String destDir, {bool useExisting = true, Map? headers, - bool allowInsecure = false}) async { + bool allowInsecure = false, + LogsProvider? logs}) async { // Send the initial request but cancel it as soon as you have the headers var reqHeaders = headers ?? {}; var req = Request('GET', Uri.parse(url)); @@ -280,6 +284,42 @@ Future downloadFile(String url, String fileName, bool fileNameHasExt, // Download to a '.temp' file (to distinguish btn. complete/incomplete files) File tempDownloadedFile = File('${downloadedFile.path}.part'); + // If there is already a temp file, a download may already be in progress - account for this (see #2073) + bool tempFileExists = tempDownloadedFile.existsSync(); + if (tempFileExists && useExisting) { + logs?.add( + 'Partial download exists - will wait: ${tempDownloadedFile.uri.pathSegments.last}'); + bool isDownloading = true; + int currentTempFileSize = await tempDownloadedFile.length(); + bool shouldReturn = false; + while (isDownloading) { + await Future.delayed(Duration(seconds: 7)); + if (tempDownloadedFile.existsSync()) { + int newTempFileSize = await tempDownloadedFile.length(); + if (newTempFileSize > currentTempFileSize) { + currentTempFileSize = newTempFileSize; + logs?.add( + 'Existing partial download still in progress: ${tempDownloadedFile.uri.pathSegments.last}'); + } else { + logs?.add( + 'Ignoring existing partial download: ${tempDownloadedFile.uri.pathSegments.last}'); + break; + } + } else { + shouldReturn = downloadedFile.existsSync(); + } + } + if (shouldReturn) { + logs?.add( + 'Existing partial download completed - not repeating: ${tempDownloadedFile.uri.pathSegments.last}'); + client.close(); + return downloadedFile; + } else { + logs?.add( + 'Existing partial download not in progress: ${tempDownloadedFile.uri.pathSegments.last}'); + } + } + // If the range feature is not available (or you need to start a ranged req from 0), // complete the already-started request, else cancel it and start a ranged request, // and open the file for writing in the appropriate mode @@ -419,9 +459,7 @@ class AppsProvider with ChangeNotifier { // Delete any partial APKs (if safe to do so) var cutoff = DateTime.now().subtract(const Duration(days: 7)); APKDir.listSync() - .where((element) => - element.path.endsWith('.part') || - element.statSync().modified.isBefore(cutoff)) + .where((element) => element.statSync().modified.isBefore(cutoff)) .forEach((partialApk) { if (!areDownloadsRunning()) { partialApk.delete(recursive: true); @@ -495,7 +533,8 @@ class AppsProvider with ChangeNotifier { prevProg = prog; }, APKDir.path, useExisting: useExisting, - allowInsecure: app.additionalSettings['allowInsecure'] == true); + allowInsecure: app.additionalSettings['allowInsecure'] == true, + logs: logs); // Set to 90 for remaining steps, will make null in 'finally' if (apps[app.id] != null) { apps[app.id]!.downloadProgress = -1; @@ -1124,7 +1163,8 @@ class AppsProvider with ChangeNotifier { forAPKDownload: fileUrl.key.endsWith('.apk') ? true : false), useExisting: false, - allowInsecure: app.additionalSettings['allowInsecure'] == true); + allowInsecure: app.additionalSettings['allowInsecure'] == true, + logs: logs); notificationsProvider .notify(DownloadedNotification(fileUrl.key, fileUrl.value)); } catch (e) {