diff --git a/mobile/lib/services/preview_video_store.dart b/mobile/lib/services/preview_video_store.dart index 4777b5254d..13dbfa376d 100644 --- a/mobile/lib/services/preview_video_store.dart +++ b/mobile/lib/services/preview_video_store.dart @@ -1,5 +1,4 @@ import "dart:async"; -import "dart:convert"; import "dart:io"; import "package:dio/dio.dart"; @@ -11,9 +10,9 @@ import "package:logging/logging.dart"; import "package:path_provider/path_provider.dart"; import "package:photos/core/network/network.dart"; import "package:photos/models/file/file.dart"; -import "package:photos/utils/crypto_util.dart"; import "package:photos/utils/file_download_util.dart"; import "package:photos/utils/file_util.dart"; +import "package:photos/utils/gzip.dart"; import "package:video_compress/video_compress.dart"; class PreviewVideoStore { @@ -27,16 +26,21 @@ class PreviewVideoStore { Future chunkAndUploadVideo(EnteFile enteFile) async { if (!enteFile.isUploaded) return; - final file = await getFile(enteFile, isOrigin: true); if (file == null) return; + try { + // check if playlist already exist + await getPlaylist(enteFile); + return; + } catch (e) { + _logger.warning("Failed to get playlist for $enteFile", e); + } final tmpDirectory = await getApplicationDocumentsDirectory(); final prefix = "${tmpDirectory.path}/${enteFile.generatedID}"; Directory(prefix).createSync(); final mediaInfo = await VideoCompress.compressVideo( file.path, quality: VideoQuality.Res1280x720Quality, - deleteOrigin: true, ); if (mediaInfo?.path == null) return; @@ -49,11 +53,11 @@ class PreviewVideoStore { keyinfo.writeAsStringSync("data:text/plain;base64,${key.base64}\n" "${keyfile.path}\n"); - final session = await FFmpegKit.executeAsync( + final session = await FFmpegKit.execute( '-i "${mediaInfo!.path}" ' '-c copy -f hls -hls_time 10 -hls_flags single_file ' '-hls_list_size 0 -hls_key_info_file ${keyinfo.path} ' - '$prefix/video.m3u8', + '$prefix/output.m3u8', ); final returnCode = await session.getReturnCode(); @@ -61,8 +65,16 @@ class PreviewVideoStore { if (ReturnCode.isSuccess(returnCode)) { final playlistFile = File("$prefix/output.m3u8"); final previewFile = File("$prefix/video.ts"); - await _reportPreview(enteFile, previewFile); - await _reportPlaylist(enteFile, playlistFile); + final result = await _uploadPreviewVideo(enteFile, previewFile); + final String objectID = result.$1; + final objectSize = result.$2; + await _reportVideoPreview( + enteFile, + playlistFile, + objectID: objectID, + objectSize: objectSize, + ); + _logger.info("Video preview uploaded for $enteFile"); } else if (ReturnCode.isCancel(returnCode)) { _logger.warning("FFmpeg command cancelled"); } else { @@ -74,46 +86,61 @@ class PreviewVideoStore { } } - Future _reportPlaylist(EnteFile file, File playlist) async { - _logger.info("Pushing playlist for $file"); - final encryptionKey = getFileKey(file); - final playlistContent = playlist.readAsStringSync(); - final encryptedPlaylist = await CryptoUtil.encryptChaCha( - utf8.encode(playlistContent), - encryptionKey, - ); - final encryptedData = - CryptoUtil.bin2base64(encryptedPlaylist.encryptedData!); - final header = CryptoUtil.bin2base64(encryptedPlaylist.header!); + Future _reportVideoPreview( + EnteFile file, + File playlist, { + required String objectID, + required int objectSize, + }) async { + _logger.info("Pushing playlist for ${file.uploadedFileID}"); try { + final encryptionKey = getFileKey(file); + final playlistContent = playlist.readAsStringSync(); + final result = await gzipAndEncryptJson( + { + "playlist": playlistContent, + 'type': 'hls_video', + }, + encryptionKey, + ); final _ = await _dio.put( - "/files/file-data/playlist", + "/files/video-data", data: { - "fileID": file.generatedID, - "model": "hls_video", - "encryptedEmbedding": encryptedData, - "decryptionHeader": header, + "fileID": file.uploadedFileID!, + "objectID": objectID, + "objectSize": objectSize, + "playlist": result.encData, + "playlistHeader": result.header, }, ); } catch (e, s) { - _logger.severe(e, s); + _logger.severe("Failed to report video preview", e, s); } } - Future _reportPreview(EnteFile file, File preview) async { + Future<(String, int)> _uploadPreviewVideo(EnteFile file, File preview) async { _logger.info("Pushing preview for $file"); try { final response = await _dio.get( - "/files/file-data/preview/upload-url/${file.generatedID}", + "/files/data/preview-upload-url", + queryParameters: { + "fileID": file.uploadedFileID!, + "type": "vid_preview", + }, ); - final uploadURL = response.data["uploadURL"]; + final uploadURL = response.data["url"]; + final String objectID = response.data["objectID"]; + final objectSize = preview.lengthSync(); final _ = await _dio.put( uploadURL, - data: await preview.readAsBytes(), + data: preview.openRead(), options: Options( - contentType: "application/octet-stream", + headers: { + Headers.contentLengthHeader: objectSize, + }, ), ); + return (objectID, objectSize); } catch (e, s) { _logger.severe(e, s); rethrow; @@ -128,25 +155,37 @@ class PreviewVideoStore { _logger.info("Getting playlist for $file"); try { final response = await _dio.get( - "/files/file-data/playlist/${file.generatedID}", + "/files/data/fetch/", + queryParameters: { + "fileID": file.uploadedFileID, + "type": "vid_preview", + }, ); - final encryptedData = response.data["encryptedEmbedding"]; - final header = response.data["decryptionHeader"]; + final encryptedData = response.data["data"]["encryptedData"]; + final header = response.data["data"]["decryptionHeader"]; final encryptionKey = getFileKey(file); - final playlistData = await CryptoUtil.decryptChaCha( - CryptoUtil.base642bin(encryptedData), + final playlistData = await decryptAndUnzipJson( encryptionKey, - CryptoUtil.base642bin(header), + encryptedData: encryptedData, + header: header, ); final response2 = await _dio.get( - "/files/file-data/preview/${file.generatedID}", + "/files/data/preview", + queryParameters: { + "fileID": file.uploadedFileID, + "type": "vid_preview", + }, ); - final previewURL = response2.data["previewURL"]; - final finalPlaylist = - utf8.decode(playlistData).replaceAll('\nvideo.ts', '\n$previewURL'); + final previewURL = response2.data["url"]; + // todo: (prateek/neeraj) review this + final finalPlaylist = (playlistData["playlist"]) + .replaceAll('\nvideo.ts', '\n$previewURL') + .replaceAll('\noutput.ts', '\n$previewURL'); final tempDir = await getTemporaryDirectory(); final playlistFile = File("${tempDir.path}/${file.generatedID}.m3u8"); await playlistFile.writeAsString(finalPlaylist); + _logger.info("Writing playlist to ${playlistFile.path}"); + return playlistFile; } catch (e, s) { _logger.severe(e, s); diff --git a/mobile/pubspec.lock b/mobile/pubspec.lock index cc5c704031..b0574b8fac 100644 --- a/mobile/pubspec.lock +++ b/mobile/pubspec.lock @@ -2615,7 +2615,7 @@ packages: source: hosted version: "1.0.3" uuid: - dependency: "direct main" + dependency: transitive description: name: uuid sha256: f33d6bb662f0e4f79dcd7ada2e6170f3b3a2530c28fc41f49a411ddedd576a77