diff --git a/app/lib/domain/extensions/media_list_extension.dart b/app/lib/domain/extensions/media_list_extension.dart new file mode 100644 index 0000000..738981e --- /dev/null +++ b/app/lib/domain/extensions/media_list_extension.dart @@ -0,0 +1,60 @@ +import 'package:data/extensions/iterable_extension.dart'; +import 'package:data/models/app_process/app_process.dart'; +import 'package:data/models/media/media.dart'; + +extension MediaListHelper on List { + void removeGoogleDriveRefFromMedias({List? removeFromIds}) { + for (int index = 0; index < length; index++) { + if (this[index].isGoogleDriveStored && + (removeFromIds?.contains(this[index].id) ?? true)) { + removeAt(index); + } else if (this[index].isCommonStored && + (removeFromIds?.contains(this[index].id) ?? true)) { + this[index] = this[index].copyWith( + sources: this[index].sources.toList() + ..remove(AppMediaSource.googleDrive), + thumbnailLink: null, + driveMediaRefId: null, + ); + } + } + } + + void addGoogleDriveRefInMedia( + {required List process, required List processIds}) { + updateWhere( + where: (media) => processIds.contains(media.id), + update: (media) { + final res = process + .where((element) => element.id == media.id) + .first + .response as AppMedia?; + return media.copyWith( + thumbnailLink: res?.thumbnailLink, + driveMediaRefId: res?.id, + sources: media.sources.toList()..add(AppMediaSource.googleDrive), + ); + }, + ); + } + + void addLocalRefInMedias( + {required List process, required List processIds}) { + updateWhere( + where: (media) => processIds.contains(media.id), + update: (media) { + final res = process + .where((element) => element.id == media.id) + .first + .response as AppMedia?; + + if (res == null) return media; + return res.copyWith( + thumbnailLink: media.thumbnailLink, + driveMediaRefId: media.id, + sources: res.sources.toList()..add(AppMediaSource.googleDrive), + ); + }, + ); + } +} diff --git a/data/lib/models/media/media.dart b/data/lib/models/media/media.dart index a593d3b..f7d629c 100644 --- a/data/lib/models/media/media.dart +++ b/data/lib/models/media/media.dart @@ -1,10 +1,8 @@ -import 'dart:io'; -import 'dart:typed_data'; -import 'dart:ui' show Size; +import 'dart:async'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:googleapis/drive/v3.dart' as drive show File; import 'package:photo_manager/photo_manager.dart' - show AssetEntity, ThumbnailFormat, ThumbnailSize; + show AssetEntity; part 'media.freezed.dart'; @@ -174,25 +172,3 @@ class AppMedia with _$AppMedia { } } -extension AppMediaExtension on AppMedia { - Future get isExist async { - return await File(path).exists(); - } - - Future thumbnailDataWithSize(Size size) async { - return await AssetEntity(id: id, typeInt: type.index, width: 0, height: 0) - .thumbnailDataWithSize( - ThumbnailSize(size.width.toInt(), size.height.toInt()), - format: ThumbnailFormat.png, - quality: 70, - ); - } - - bool get isGoogleDriveStored => - sources.contains(AppMediaSource.googleDrive) && sources.length == 1; - - bool get isLocalStored => - sources.contains(AppMediaSource.local) && sources.length == 1; - - bool get isCommonStored => sources.length > 1; -} diff --git a/data/lib/models/media/media_extension.dart b/data/lib/models/media/media_extension.dart new file mode 100644 index 0000000..2e09b84 --- /dev/null +++ b/data/lib/models/media/media_extension.dart @@ -0,0 +1,63 @@ +import 'dart:async'; +import 'dart:io'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:photo_manager/photo_manager.dart'; +import 'media.dart'; + +extension AppMediaExtension on AppMedia { + Future get isExist async { + return await File(path).exists(); + } + + Future loadThumbnail({Size size = const Size(300, 300)}) async { + var rootToken = RootIsolateToken.instance!; + final ThumbNailParameter thumbNailParameter = + ThumbNailParameter(rootToken, size, id, type); + final bytes = await compute(_loadThumbnailInBackground, thumbNailParameter); + return bytes; + } + + FutureOr _loadThumbnailInBackground( + ThumbNailParameter parameters) async { + BackgroundIsolateBinaryMessenger.ensureInitialized(parameters.token); + return await AssetEntity( + id: parameters.id, + typeInt: parameters.type.index, + width: 0, + height: 0, + ).thumbnailDataWithSize( + ThumbnailSize( + parameters.size.width.toInt(), + parameters.size.height.toInt(), + ), + format: ThumbnailFormat.png, + quality: 70, + ); + } + + AppMedia margeGoogleDriveMedia(AppMedia media){ + return copyWith( + thumbnailLink: media.thumbnailLink, + driveMediaRefId: media.driveMediaRefId, + sources: sources.toList()..add(AppMediaSource.googleDrive), + ); + } + + bool get isGoogleDriveStored => + sources.contains(AppMediaSource.googleDrive) && sources.length == 1; + + bool get isLocalStored => + sources.contains(AppMediaSource.local) && sources.length == 1; + + bool get isCommonStored => sources.length > 1; +} + +class ThumbNailParameter { + final RootIsolateToken token; + final Size size; + final String id; + final AppMediaType type; + + ThumbNailParameter(this.token, this.size, this.id, this.type); +} diff --git a/data/lib/repositories/google_drive_process_repo.dart b/data/lib/repositories/google_drive_process_repo.dart index bed11d2..ff97b59 100644 --- a/data/lib/repositories/google_drive_process_repo.dart +++ b/data/lib/repositories/google_drive_process_repo.dart @@ -3,6 +3,7 @@ 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'; +import 'package:data/models/media/media_extension.dart'; import 'package:data/services/google_drive_service.dart'; import 'package:data/services/local_media_service.dart'; import 'package:flutter/cupertino.dart'; @@ -186,17 +187,6 @@ class GoogleDriveProcessRepo extends ChangeNotifier { total: mediaContent.length ?? 0, chunk: bytes.length)), ); notifyListeners(); - }, onDone: () async { - final res = await _localMediaService.saveMedia( - process.media, Uint8List.fromList(bytes)); - - _downloadQueue.updateWhere( - where: (element) => element.id == process.id, - update: (element) => - element.copyWith(status: AppProcessStatus.success, response: res), - ); - notifyListeners(); - subscription?.cancel(); }, onError: (error) { _downloadQueue.updateWhere( where: (element) => element.id == process.id, @@ -206,6 +196,23 @@ class GoogleDriveProcessRepo extends ChangeNotifier { notifyListeners(); subscription?.cancel(); }); + await subscription.asFuture(); + + final localMedia = await _localMediaService.saveMedia( + process.media, Uint8List.fromList(bytes)); + + final updatedMedia = await _googleDriveService.updateMediaDescription( + process.media.id, localMedia?.path ?? ""); + + _downloadQueue.updateWhere( + where: (element) => element.id == process.id, + update: (element) => element.copyWith( + status: AppProcessStatus.success, + response: localMedia?.margeGoogleDriveMedia(updatedMedia)), + ); + + notifyListeners(); + subscription.cancel(); } catch (error) { _downloadQueue.updateWhere( where: (element) => element.id == process.id, diff --git a/data/lib/services/google_drive_service.dart b/data/lib/services/google_drive_service.dart index 2d556e8..6191b62 100644 --- a/data/lib/services/google_drive_service.dart +++ b/data/lib/services/google_drive_service.dart @@ -73,6 +73,17 @@ class GoogleDriveService { } } + Future updateMediaDescription(String id, String description) async { + try { + final driveApi = await _getGoogleDriveAPI(); + final file = drive.File(description: description); + final updatedFile = await driveApi.files.update(file, id); + return AppMedia.fromGoogleDriveFile(updatedFile); + } catch (e) { + throw AppError.fromError(e); + } + } + Future deleteMedia(String id) async { try { final driveApi = await _getGoogleDriveAPI(); diff --git a/data/lib/services/local_media_service.dart b/data/lib/services/local_media_service.dart index 88dbebc..dbb02f7 100644 --- a/data/lib/services/local_media_service.dart +++ b/data/lib/services/local_media_service.dart @@ -56,17 +56,25 @@ class LocalMediaService { } } - Future saveMedia(AppMedia media, Uint8List bytes) async { + 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) { final tempDir = await getTemporaryDirectory(); - final tempVideoFile = File('${tempDir.path}/temp_video.mp4'); + final tempVideoFile = File('${tempDir.path}/temp_video'); await tempVideoFile.writeAsBytes(bytes); - return await PhotoManager.editor.saveVideo(tempVideoFile, - title: media.name ?? "${DateTime.now()}_cloud_gallery"); + asset = await PhotoManager.editor.saveVideo( + tempVideoFile, + title: "${media.name ?? DateTime.now()}_gd_cloud_gallery.$extension", + ); } else if (media.type.isImage) { - return await PhotoManager.editor.saveImage(bytes, - title: media.name ?? "${DateTime.now()}_cloud_gallery"); + asset = await PhotoManager.editor.saveImage(bytes, + title: "${media.name ?? DateTime.now()}_gd_cloud_gallery.$extension"); } - return null; + return asset != null ? AppMedia.fromAssetEntity(asset) : null; } }