diff --git a/app/lib/ui/flow/media_preview/components/download_require_view.dart b/app/lib/ui/flow/media_preview/components/download_require_view.dart index 48df419..9dafe1e 100644 --- a/app/lib/ui/flow/media_preview/components/download_require_view.dart +++ b/app/lib/ui/flow/media_preview/components/download_require_view.dart @@ -61,7 +61,7 @@ class DownloadRequireView extends StatelessWidget { ], ), title: - "${downloadProcess?.progress?.chunk.formatBytes ?? "0.0 B"} - ${downloadProcess?.progress?.total.formatBytes ?? "0.0 B"} ${downloadProcess?.progress?.percentage ?? "0.0"}%", + "${downloadProcess?.progress?.chunk.formatBytes ?? "0.0 B"} - ${downloadProcess?.progress?.total.formatBytes ?? "0.0 B"} ${downloadProcess?.progress?.percentage.toStringAsFixed(0) ?? "0.0"}%", message: context.l10n.download_in_progress_text), ], if (downloadProcess?.status.isWaiting ?? false) ...[ diff --git a/app/lib/ui/flow/media_transfer/components/transfer_item.dart b/app/lib/ui/flow/media_transfer/components/transfer_item.dart index 5e6ddf7..aff7a6a 100644 --- a/app/lib/ui/flow/media_transfer/components/transfer_item.dart +++ b/app/lib/ui/flow/media_transfer/components/transfer_item.dart @@ -69,7 +69,7 @@ class _ProcessItemState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - '${widget.process.progress?.chunk.formatBytes} ${widget.process.progress?.percentage.toStringAsFixed(2)}%', + '${widget.process.progress?.chunk.formatBytes} ${widget.process.progress?.percentage.toStringAsFixed(0)}%', style: AppTextStyles.body2.copyWith( color: context.colorScheme.textSecondary, ), diff --git a/data/lib/repositories/google_drive_process_repo.dart b/data/lib/repositories/google_drive_process_repo.dart index ff97b59..2cd2ed3 100644 --- a/data/lib/repositories/google_drive_process_repo.dart +++ b/data/lib/repositories/google_drive_process_repo.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:typed_data'; import 'package:collection/collection.dart'; import 'package:data/extensions/iterable_extension.dart'; import 'package:data/models/app_process/app_process.dart'; @@ -164,7 +163,6 @@ class GoogleDriveProcessRepo extends ChangeNotifier { } Future _downloadFromGoogleDrive(AppProcess process) async { - StreamSubscription? subscription; try { _downloadQueue.updateWhere( where: (element) => element.id == process.id, @@ -176,30 +174,19 @@ class GoogleDriveProcessRepo extends ChangeNotifier { final mediaContent = await _googleDriveService .fetchMediaBytes(process.media.driveMediaRefId!); - List bytes = []; - - subscription = mediaContent.stream.listen((chunk) { - bytes.addAll(chunk); - _downloadQueue.updateWhere( - where: (element) => element.id == process.id, - update: (element) => element.copyWith( - progress: AppProcessProgress( - total: mediaContent.length ?? 0, chunk: bytes.length)), - ); - notifyListeners(); - }, onError: (error) { - _downloadQueue.updateWhere( - where: (element) => element.id == process.id, - update: (element) => - element.copyWith(status: AppProcessStatus.failed), - ); - notifyListeners(); - subscription?.cancel(); - }); - await subscription.asFuture(); - final localMedia = await _localMediaService.saveMedia( - process.media, Uint8List.fromList(bytes)); + content: mediaContent, + onProgress: (total, chunk) { + _downloadQueue.updateWhere( + where: (element) => element.id == process.id, + update: (element) => element.copyWith( + progress: AppProcessProgress(total: total, chunk: chunk)), + ); + notifyListeners(); + }, + mimeType: process.media.mimeType, + type: process.media.type, + ); final updatedMedia = await _googleDriveService.updateMediaDescription( process.media.id, localMedia?.path ?? ""); @@ -210,14 +197,12 @@ class GoogleDriveProcessRepo extends ChangeNotifier { status: AppProcessStatus.success, response: localMedia?.margeGoogleDriveMedia(updatedMedia)), ); - - notifyListeners(); - subscription.cancel(); } catch (error) { _downloadQueue.updateWhere( where: (element) => element.id == process.id, update: (element) => element.copyWith(status: AppProcessStatus.failed), ); + } finally { notifyListeners(); } } diff --git a/data/lib/services/local_media_service.dart b/data/lib/services/local_media_service.dart index dbb02f7..501c3d2 100644 --- a/data/lib/services/local_media_service.dart +++ b/data/lib/services/local_media_service.dart @@ -1,7 +1,8 @@ +import 'dart:async'; import 'dart:io'; -import 'dart:typed_data'; import 'package:collection/collection.dart'; import 'package:data/models/media/media.dart'; +import 'package:data/models/media_content/media_content.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:path_provider/path_provider.dart'; import 'package:photo_manager/photo_manager.dart'; @@ -56,25 +57,52 @@ class LocalMediaService { } } - Future saveMedia(AppMedia media, Uint8List bytes) async { - final extension = media.mimeType?.trim().isNotEmpty ?? false - ? media.mimeType!.split('/').last - : media.type.isVideo - ? 'mp4' - : 'jpg'; - AssetEntity? asset; - if (media.type.isVideo) { + Future saveMedia({ + required AppMediaType type, + required String? mimeType, + required AppMediaContent content, + required void Function(int total, int chunk) onProgress, + }) async { + try { + final extension = mimeType?.trim().isNotEmpty ?? false + ? mimeType!.split('/').last + : type.isVideo + ? 'mp4' + : 'jpg'; + + AssetEntity? asset; + final tempDir = await getTemporaryDirectory(); - final tempVideoFile = File('${tempDir.path}/temp_video'); - await tempVideoFile.writeAsBytes(bytes); - asset = await PhotoManager.editor.saveVideo( - tempVideoFile, - title: "${media.name ?? DateTime.now()}_gd_cloud_gallery.$extension", - ); - } else if (media.type.isImage) { - asset = await PhotoManager.editor.saveImage(bytes, - title: "${media.name ?? DateTime.now()}_gd_cloud_gallery.$extension"); + final tempFile = File( + '${tempDir.path}${DateTime.now()}_gd_cloud_gallery_temp.$extension'); + await tempFile.create(); + + int chunkLength = 0; + + StreamSubscription> subscription = + content.stream.listen((chunk) { + chunkLength += chunk.length; + onProgress(content.length ?? 0, chunkLength); + tempFile.writeAsBytesSync(chunk, mode: FileMode.append); + }); + await subscription.asFuture(); + subscription.cancel(); + + if (type.isVideo) { + asset = await PhotoManager.editor.saveVideo( + tempFile, + title: "${DateTime.now()}_gd_cloud_gallery.$extension", + ); + } else if (type.isImage) { + asset = await PhotoManager.editor.saveImageWithPath( + tempFile.path, + title: "${DateTime.now()}_gd_cloud_gallery.$extension", + ); + } + await tempFile.delete(); + return asset != null ? AppMedia.fromAssetEntity(asset) : null; + } catch (e) { + throw AppError.fromError(e); } - return asset != null ? AppMedia.fromAssetEntity(asset) : null; } }