import 'dart:async'; import 'package:drift/drift.dart'; import 'package:twonly/globals.dart'; import 'package:twonly/src/database/tables/mediafiles.table.dart'; import 'package:twonly/src/database/tables/messages.table.dart'; import 'package:twonly/src/database/twonly.db.dart'; import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart'; import 'package:twonly/src/services/api/mediafiles/download.service.dart'; import 'package:twonly/src/services/api/utils.dart'; import 'package:twonly/src/services/mediafiles/mediafile.service.dart'; import 'package:twonly/src/utils/log.dart'; Future handleMedia( int fromUserId, String groupId, EncryptedContent_Media media, ) async { Log.info( 'Got a media message: ${media.senderMessageId} from $groupId with type ${media.type}', ); late MediaType mediaType; switch (media.type) { case EncryptedContent_Media_Type.REUPLOAD: final message = await twonlyDB.messagesDao .getMessageById(media.senderMessageId) .getSingleOrNull(); if (message == null || message.senderId != fromUserId || message.mediaId == null) { return; } // in case there was already a downloaded file delete it... final mediaService = await MediaFileService.fromMediaId(message.mediaId!); if (mediaService != null) { mediaService.tempPath.deleteSync(); } await twonlyDB.mediaFilesDao.updateMedia( message.mediaId!, MediaFilesCompanion( downloadState: const Value(DownloadState.pending), downloadToken: Value(Uint8List.fromList(media.downloadToken)), encryptionKey: Value(Uint8List.fromList(media.encryptionKey)), encryptionMac: Value(Uint8List.fromList(media.encryptionMac)), encryptionNonce: Value(Uint8List.fromList(media.encryptionNonce)), ), ); final mediaFile = await twonlyDB.mediaFilesDao.getMediaFileById(message.mediaId!); if (mediaFile != null) { unawaited(startDownloadMedia(mediaFile, false)); } return; case EncryptedContent_Media_Type.IMAGE: mediaType = MediaType.image; case EncryptedContent_Media_Type.VIDEO: mediaType = MediaType.video; case EncryptedContent_Media_Type.GIF: mediaType = MediaType.gif; case EncryptedContent_Media_Type.AUDIO: mediaType = MediaType.audio; } int? displayLimitInMilliseconds; if (media.hasDisplayLimitInMilliseconds()) { if (media.displayLimitInMilliseconds.toInt() < 1000) { displayLimitInMilliseconds = media.displayLimitInMilliseconds.toInt() * 1000; } else { displayLimitInMilliseconds = media.displayLimitInMilliseconds.toInt(); } } final mediaFile = await twonlyDB.mediaFilesDao.insertMedia( MediaFilesCompanion( downloadState: const Value(DownloadState.pending), type: Value(mediaType), requiresAuthentication: Value(media.requiresAuthentication), displayLimitInMilliseconds: Value( displayLimitInMilliseconds, ), downloadToken: Value(Uint8List.fromList(media.downloadToken)), encryptionKey: Value(Uint8List.fromList(media.encryptionKey)), encryptionMac: Value(Uint8List.fromList(media.encryptionMac)), encryptionNonce: Value(Uint8List.fromList(media.encryptionNonce)), createdAt: Value(fromTimestamp(media.timestamp)), ), ); if (mediaFile == null) { Log.error('Could not insert media file into database'); return; } final message = await twonlyDB.messagesDao.insertMessage( MessagesCompanion( messageId: Value(media.senderMessageId), senderId: Value(fromUserId), groupId: Value(groupId), mediaId: Value(mediaFile.mediaId), type: const Value(MessageType.media), quotesMessageId: Value( media.hasQuoteMessageId() ? media.quoteMessageId : null, ), createdAt: Value(fromTimestamp(media.timestamp)), ), ); if (message != null) { await twonlyDB.groupsDao .increaseLastMessageExchange(groupId, fromTimestamp(media.timestamp)); Log.info('Inserted a new media message with ID: ${message.messageId}'); await twonlyDB.groupsDao.incFlameCounter( message.groupId, true, fromTimestamp(media.timestamp), ); unawaited(startDownloadMedia(mediaFile, false)); } } Future handleMediaUpdate( int fromUserId, EncryptedContent_MediaUpdate mediaUpdate, ) async { final message = await twonlyDB.messagesDao .getMessageById(mediaUpdate.targetMessageId) .getSingleOrNull(); if (message == null) { Log.error( 'Got media update to message ${mediaUpdate.targetMessageId} but message not found.', ); } final mediaFile = await twonlyDB.mediaFilesDao.getMediaFileById(message!.mediaId!); if (mediaFile == null) { Log.info( 'Got media file update, but media file was not found ${message.mediaId}', ); return; } switch (mediaUpdate.type) { case EncryptedContent_MediaUpdate_Type.REOPENED: Log.info('Got media file reopened ${mediaFile.mediaId}'); await twonlyDB.mediaFilesDao.updateMedia( mediaFile.mediaId, const MediaFilesCompanion( reopenByContact: Value(true), ), ); case EncryptedContent_MediaUpdate_Type.STORED: Log.info('Got media file stored ${mediaFile.mediaId}'); final mediaService = await MediaFileService.fromMedia(mediaFile); await mediaService.storeMediaFile(); case EncryptedContent_MediaUpdate_Type.DECRYPTION_ERROR: Log.info('Got media file decryption error ${mediaFile.mediaId}'); final reuploadRequestedBy = mediaFile.reuploadRequestedBy ?? []; reuploadRequestedBy.add(fromUserId); await twonlyDB.mediaFilesDao.updateMedia( mediaFile.mediaId, MediaFilesCompanion( uploadState: const Value(UploadState.uploading), reuploadRequestedBy: Value(reuploadRequestedBy), ), ); } }