remote_fileml_service.dart 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. import "dart:async";
  2. import "dart:convert";
  3. import "package:computer/computer.dart";
  4. import "package:logging/logging.dart";
  5. import "package:photos/core/network/network.dart";
  6. import "package:photos/db/files_db.dart";
  7. import "package:photos/models/file/file.dart";
  8. import 'package:photos/services/machine_learning/file_ml/file_ml.dart';
  9. import "package:photos/services/machine_learning/file_ml/files_ml_data_response.dart";
  10. import "package:photos/services/machine_learning/semantic_search/embedding_store.dart";
  11. import "package:photos/services/machine_learning/semantic_search/remote_embedding.dart";
  12. import "package:photos/utils/crypto_util.dart";
  13. import "package:photos/utils/file_download_util.dart";
  14. import "package:shared_preferences/shared_preferences.dart";
  15. class RemoteFileMLService {
  16. RemoteFileMLService._privateConstructor();
  17. static final Computer _computer = Computer.shared();
  18. static final RemoteFileMLService instance =
  19. RemoteFileMLService._privateConstructor();
  20. final _logger = Logger("RemoteFileMLService");
  21. final _dio = NetworkClient.instance.enteDio;
  22. void init(SharedPreferences prefs) {}
  23. Future<void> putFileEmbedding(EnteFile file, FileMl fileML) async {
  24. final encryptionKey = getFileKey(file);
  25. final embeddingJSON = jsonEncode(fileML.toJson());
  26. final encryptedEmbedding = await CryptoUtil.encryptChaCha(
  27. utf8.encode(embeddingJSON),
  28. encryptionKey,
  29. );
  30. final encryptedData =
  31. CryptoUtil.bin2base64(encryptedEmbedding.encryptedData!);
  32. final header = CryptoUtil.bin2base64(encryptedEmbedding.header!);
  33. try {
  34. final _ = await _dio.put(
  35. "/embeddings",
  36. data: {
  37. "fileID": file.uploadedFileID!,
  38. "model": 'file-ml-clip-face',
  39. "encryptedEmbedding": encryptedData,
  40. "decryptionHeader": header,
  41. },
  42. );
  43. // final updationTime = response.data["updatedAt"];
  44. } catch (e, s) {
  45. _logger.severe("Failed to put embedding", e, s);
  46. rethrow;
  47. }
  48. }
  49. Future<FilesMLDataResponse> getFilessEmbedding(
  50. Set<int> fileIds,
  51. ) async {
  52. try {
  53. final res = await _dio.post(
  54. "/embeddings/files",
  55. data: {
  56. "fileIDs": fileIds.toList(),
  57. "model": 'file-ml-clip-face',
  58. },
  59. );
  60. final remoteEmb = res.data['embeddings'] as List;
  61. final pendingIndexFiles = res.data['pendingIndexFileIDs'] as List;
  62. final noEmbeddingFiles = res.data['noEmbeddingFileIDs'] as List;
  63. final errFileIds = res.data['errFileIDs'] as List;
  64. final List<RemoteEmbedding> remoteEmbeddings = <RemoteEmbedding>[];
  65. for (var entry in remoteEmb) {
  66. final embedding = RemoteEmbedding.fromMap(entry);
  67. remoteEmbeddings.add(embedding);
  68. }
  69. final fileIDToFileMl = await decryptFileMLData(remoteEmbeddings);
  70. return FilesMLDataResponse(
  71. fileIDToFileMl,
  72. noEmbeddingFileIDs:
  73. Set<int>.from(noEmbeddingFiles.map((x) => x as int)),
  74. fetchErrorFileIDs: Set<int>.from(errFileIds.map((x) => x as int)),
  75. pendingIndexFileIDs:
  76. Set<int>.from(pendingIndexFiles.map((x) => x as int)),
  77. );
  78. } catch (e, s) {
  79. _logger.severe("Failed to get embeddings", e, s);
  80. rethrow;
  81. }
  82. }
  83. Future<Map<int, FileMl>> decryptFileMLData(
  84. List<RemoteEmbedding> remoteEmbeddings,
  85. ) async {
  86. final result = <int, FileMl>{};
  87. if (remoteEmbeddings.isEmpty) {
  88. return result;
  89. }
  90. final inputs = <EmbeddingsDecoderInput>[];
  91. final fileMap = await FilesDB.instance
  92. .getFilesFromIDs(remoteEmbeddings.map((e) => e.fileID).toList());
  93. for (final embedding in remoteEmbeddings) {
  94. final file = fileMap[embedding.fileID];
  95. if (file == null) {
  96. continue;
  97. }
  98. final fileKey = getFileKey(file);
  99. final input = EmbeddingsDecoderInput(embedding, fileKey);
  100. inputs.add(input);
  101. }
  102. return _computer.compute<Map<String, dynamic>, Map<int, FileMl>>(
  103. _decryptFileMLComputer,
  104. param: {
  105. "inputs": inputs,
  106. },
  107. );
  108. }
  109. }
  110. Future<Map<int, FileMl>> _decryptFileMLComputer(
  111. Map<String, dynamic> args,
  112. ) async {
  113. final result = <int, FileMl>{};
  114. final inputs = args["inputs"] as List<EmbeddingsDecoderInput>;
  115. for (final input in inputs) {
  116. final decryptArgs = <String, dynamic>{};
  117. decryptArgs["source"] =
  118. CryptoUtil.base642bin(input.embedding.encryptedEmbedding);
  119. decryptArgs["key"] = input.decryptionKey;
  120. decryptArgs["header"] =
  121. CryptoUtil.base642bin(input.embedding.decryptionHeader);
  122. final embeddingData = chachaDecryptData(decryptArgs);
  123. final decodedJson = jsonDecode(utf8.decode(embeddingData));
  124. final FileMl decodedEmbedding =
  125. FileMl.fromJson(decodedJson as Map<String, dynamic>);
  126. result[input.embedding.fileID] = decodedEmbedding;
  127. }
  128. return result;
  129. }