diff --git a/lib/src/database/daos/media_uploads_dao.dart b/lib/src/database/daos/media_uploads_dao.dart index f0b9ee1..3361886 100644 --- a/lib/src/database/daos/media_uploads_dao.dart +++ b/lib/src/database/daos/media_uploads_dao.dart @@ -17,7 +17,7 @@ class MediaUploadsDao extends DatabaseAccessor .get(); } - Future updateMediaUpload( + Future updateMediaUpload( int mediaUploadId, MediaUploadsCompanion updatedValues) { return (update(mediaUploads) ..where((c) => c.mediaUploadId.equals(mediaUploadId))) diff --git a/lib/src/database/daos/messages_dao.dart b/lib/src/database/daos/messages_dao.dart index f8d232c..534705c 100644 --- a/lib/src/database/daos/messages_dao.dart +++ b/lib/src/database/daos/messages_dao.dart @@ -88,15 +88,15 @@ class MessagesDao extends DatabaseAccessor .watch(); } - Future> getAllMessagesPendingUploadOlderThanAMinute() { + Future> getAllMessagesPendingUpload() { return (select(messages) ..where( (t) => t.acknowledgeByServer.equals(false) & t.messageOtherId.isNull() & + t.mediaUploadId.isNotNull() & + t.downloadState.equals(DownloadState.pending.index) & t.errorWhileSending.equals(false) & - t.sendAt.isSmallerThanValue( - DateTime.now().subtract(Duration(minutes: 5))) & t.kind.equals(MessageKind.media.name), )) .get(); diff --git a/lib/src/services/api/media_received.dart b/lib/src/services/api/media_received.dart index 05542bb..2a0de3a 100644 --- a/lib/src/services/api/media_received.dart +++ b/lib/src/services/api/media_received.dart @@ -317,6 +317,7 @@ Future purgeMediaFiles(Directory directory) async { } } if (canBeDeleted) { + Log.info("purged media file ${file.path} "); file.deleteSync(); } } else { diff --git a/lib/src/services/api/media_send.dart b/lib/src/services/api/media_send.dart index e2257a0..9b46eac 100644 --- a/lib/src/services/api/media_send.dart +++ b/lib/src/services/api/media_send.dart @@ -63,11 +63,55 @@ Future isAllowedToSend() async { /// Create a new entry in the database +Future checkForFailedUploads() async { + final messages = await twonlyDB.messagesDao.getAllMessagesPendingUpload(); + List mediaUploadIds = []; + for (Message message in messages) { + if (mediaUploadIds.contains(message.mediaUploadId)) { + continue; + } + int affectedRows = await twonlyDB.mediaUploadsDao.updateMediaUpload( + message.mediaUploadId!, + MediaUploadsCompanion( + uploadTokens: Value(null), // reupload them + state: Value(UploadState.pending), + encryptionData: Value( + null, // start from scratch e.q. encrypt the files again if already happen + ), + ), + ); + if (affectedRows == 0) { + Log.error( + "The media from message ${message.messageId} already deleted.", + ); + await twonlyDB.messagesDao.updateMessageByMessageId( + message.messageId, + MessagesCompanion( + errorWhileSending: Value(true), + ), + ); + } else { + mediaUploadIds.add(message.mediaUploadId!); + } + } + Log.error( + "Got ${messages.length} messages (${mediaUploadIds.length} media upload files) that are not correctly uploaded. Trying from scratch again.", + ); + return mediaUploadIds.isNotEmpty; // return true if there are affected +} + final lockingHandleMediaFile = Mutex(); Future retryMediaUpload({int maxRetries = 3}) async { - await lockingHandleMediaFile.protect(() async { + if (maxRetries == 0) { + Log.error("retried media upload 3 times. abort retrying"); + return; + } + bool retry = await lockingHandleMediaFile.protect(() async { final mediaFiles = await twonlyDB.mediaUploadsDao.getMediaUploadsForRetry(); - if (mediaFiles.isEmpty) return; + if (mediaFiles.isEmpty) { + return checkForFailedUploads(); + } + Log.info("re uploading ${mediaFiles.length} media files."); for (final mediaFile in mediaFiles) { if (mediaFile.messageIds == null || mediaFile.metadata == null) { // the media upload was canceled, @@ -89,7 +133,11 @@ Future retryMediaUpload({int maxRetries = 3}) async { await handlePreProcessingState(mediaFile); } } + return false; }); + if (retry) { + await retryMediaUpload(maxRetries: maxRetries - 1); + } } Future initMediaUpload() async { @@ -151,12 +199,17 @@ Future handlePreProcessingState(MediaUpload media) async { videoHandler, ); } catch (e) { + Log.error("${media.mediaUploadId} got error in pre processing: $e"); await handleUploadError(media); } } Future encryptAndPreUploadMediaFiles( - int mediaUploadId, Future imageHandler, Future? videoHandler) async { + int mediaUploadId, + Future imageHandler, + Future? videoHandler, +) async { + Log.info("$mediaUploadId encrypting files"); Uint8List dataToEncrypt = await imageHandler; /// if there is a video wait until it is finished with compression @@ -268,6 +321,7 @@ Future handleNextMediaUploadSteps(int mediaUploadId) async { if (mediaUpload == null) return false; if (mediaUpload.state == UploadState.receiverNotified) { /// Upload done and all users are notified :) + Log.info("$mediaUploadId is already done"); return false; } try { @@ -386,6 +440,8 @@ Future handleMediaUpload(int mediaUploadId) async { filename: "upload", )); + Log.info("Starting upload from $mediaUploadId "); + try { var streamedResponse = await requestMultipart.send();