From 404aee1e1816604b1b65ab982a9a7b032746bd7e Mon Sep 17 00:00:00 2001 From: otsmr Date: Wed, 29 Oct 2025 10:14:25 +0100 Subject: [PATCH] upload non finished uploads after restart --- lib/main.dart | 2 ++ lib/src/database/daos/mediafiles.dao.dart | 10 +++++++ .../api/mediafiles/upload.service.dart | 26 +++++++++++++++++++ .../mediafiles/thumbnail.service.dart | 7 ++--- .../chat_text_entry.dart | 1 - .../memories/memories_item_thumbnail.dart | 13 +++++++++- 6 files changed, 54 insertions(+), 5 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index ec86621..9df9558 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -17,6 +17,7 @@ import 'package:twonly/src/providers/image_editor.provider.dart'; import 'package:twonly/src/providers/settings.provider.dart'; import 'package:twonly/src/services/api.service.dart'; import 'package:twonly/src/services/api/mediafiles/media_background.service.dart'; +import 'package:twonly/src/services/api/mediafiles/upload.service.dart'; import 'package:twonly/src/services/fcm.service.dart'; import 'package:twonly/src/services/mediafiles/mediafile.service.dart'; import 'package:twonly/src/utils/log.dart'; @@ -55,6 +56,7 @@ void main() async { twonlyDB = TwonlyDB(); await initFileDownloader(); + unawaited(finishStartedPreprocessing()); unawaited(MediaFileService.purgeTempFolder()); await twonlyDB.messagesDao.purgeMessageTable(); diff --git a/lib/src/database/daos/mediafiles.dao.dart b/lib/src/database/daos/mediafiles.dao.dart index cc01ca9..b5da4a1 100644 --- a/lib/src/database/daos/mediafiles.dao.dart +++ b/lib/src/database/daos/mediafiles.dao.dart @@ -84,6 +84,16 @@ class MediaFilesDao extends DatabaseAccessor .get(); } + Future> getAllMediaFilesPendingUpload() async { + return (select(mediaFiles) + ..where( + (t) => + t.uploadState.equals(UploadState.initialized.name) | + t.uploadState.equals(UploadState.preprocessing.name), + )) + .get(); + } + Stream> watchAllStoredMediaFiles() { return (select(mediaFiles)..where((t) => t.stored.equals(true))).watch(); } diff --git a/lib/src/services/api/mediafiles/upload.service.dart b/lib/src/services/api/mediafiles/upload.service.dart index 664ea30..1db4ef1 100644 --- a/lib/src/services/api/mediafiles/upload.service.dart +++ b/lib/src/services/api/mediafiles/upload.service.dart @@ -19,6 +19,20 @@ import 'package:twonly/src/services/mediafiles/mediafile.service.dart'; import 'package:twonly/src/utils/log.dart'; import 'package:twonly/src/utils/misc.dart'; +Future finishStartedPreprocessing() async { + final mediaFiles = + await twonlyDB.mediaFilesDao.getAllMediaFilesPendingUpload(); + + for (final mediaFile in mediaFiles) { + try { + final service = await MediaFileService.fromMedia(mediaFile); + await startBackgroundMediaUpload(service); + } catch (e) { + Log.error(e); + } + } +} + Future initializeMediaUpload( MediaType type, int? displayLimitInMilliseconds, @@ -77,15 +91,22 @@ Future startBackgroundMediaUpload(MediaFileService mediaService) async { if (!mediaService.tempPath.existsSync()) { await mediaService.compressMedia(); + if (!mediaService.tempPath.existsSync()) { + return; + } } if (!mediaService.encryptedPath.existsSync()) { await _encryptMediaFiles(mediaService); + if (!mediaService.encryptedPath.existsSync()) { + return; + } } if (!mediaService.uploadRequestPath.existsSync()) { await _createUploadRequest(mediaService); } + if (mediaService.uploadRequestPath.existsSync()) { await mediaService.setUploadState(UploadState.uploading); // at this point the original file is not used any more, so it can be deleted @@ -101,6 +122,11 @@ Future startBackgroundMediaUpload(MediaFileService mediaService) async { Future _encryptMediaFiles(MediaFileService mediaService) async { /// if there is a video wait until it is finished with compression + if (!mediaService.tempPath.existsSync()) { + Log.error('Could not encrypted image as it does not exists'); + return; + } + final dataToEncrypt = await mediaService.tempPath.readAsBytes(); final chacha20 = FlutterChacha20.poly1305Aead(); diff --git a/lib/src/services/mediafiles/thumbnail.service.dart b/lib/src/services/mediafiles/thumbnail.service.dart index cc3437e..df7397c 100644 --- a/lib/src/services/mediafiles/thumbnail.service.dart +++ b/lib/src/services/mediafiles/thumbnail.service.dart @@ -10,7 +10,7 @@ Future createThumbnailsForVideo( final stopwatch = Stopwatch()..start(); final command = - '-i ${sourceFile.path} -ss 00:00:00 -vframes 1 -vf "scale=iw:ih:flags=lanczos" -c:v libwebp -q:v 100 -compression_level 6 ${destinationFile.path}'; + '-i "${sourceFile.path}" -ss 00:00:00 -vframes 1 -vf "scale=iw:ih:flags=lanczos" -c:v libwebp -q:v 100 -compression_level 6 "${destinationFile.path}"'; final session = await FFmpegKit.execute(command); final returnCode = await session.getReturnCode(); @@ -22,8 +22,9 @@ Future createThumbnailsForVideo( ); } else { Log.info(command); - Log.error('Compression failed for the video with exit code $returnCode.'); + Log.error( + 'Thumbnail creation failed for the video with exit code $returnCode.', + ); Log.error(await session.getAllLogsAsString()); - // Report this error to the user? } } diff --git a/lib/src/views/chats/chat_messages_components/chat_text_entry.dart b/lib/src/views/chats/chat_messages_components/chat_text_entry.dart index c9f7d8a..ee5f4b7 100644 --- a/lib/src/views/chats/chat_messages_components/chat_text_entry.dart +++ b/lib/src/views/chats/chat_messages_components/chat_text_entry.dart @@ -108,7 +108,6 @@ class ChatTextEntry extends StatelessWidget { ), ), ), - // if (displayTime) Text( friendlyTime( context, diff --git a/lib/src/views/memories/memories_item_thumbnail.dart b/lib/src/views/memories/memories_item_thumbnail.dart index d4434d4..a0ec884 100644 --- a/lib/src/views/memories/memories_item_thumbnail.dart +++ b/lib/src/views/memories/memories_item_thumbnail.dart @@ -20,9 +20,19 @@ class MemoriesItemThumbnail extends StatefulWidget { class _MemoriesItemThumbnailState extends State { @override void initState() { + initAsync(); super.initState(); } + Future initAsync() async { + if (!widget.galleryItem.mediaService.thumbnailPath.existsSync()) { + if (widget.galleryItem.mediaService.storedPath.existsSync()) { + await widget.galleryItem.mediaService.createThumbnail(); + if (mounted) setState(() {}); + } + } + } + @override void dispose() { super.dispose(); @@ -46,7 +56,8 @@ class _MemoriesItemThumbnailState extends State { children: [ if (media.thumbnailPath.existsSync()) Image.file(media.thumbnailPath) - else if (media.storedPath.existsSync()) + else if (media.storedPath.existsSync() && + media.mediaFile.type == MediaType.image) Image.file(media.storedPath) else const Text('Media file removed.'),