Use new abstraction to fetch public files

This commit is contained in:
Neeraj Gupta
2025-06-20 14:22:02 +05:30
parent cecdea3f93
commit 84e9336672
4 changed files with 108 additions and 109 deletions

View File

@@ -55,6 +55,40 @@ class CollectionFilesService {
}
}
Future<DiffResult> getPublicCollectionDiff(
int collectionID,
int sinceTime,
Uint8List collectionKey,
Map<String, String> headers,
) async {
final response = await _enteDio.get(
"/public-collection/diff",
options: Options(headers: headers),
queryParameters: {"sinceTime": sinceTime},
);
final List diff = response.data["diff"] as List;
if (diff.isEmpty) {
return DiffResult([], [], response.data["hasMore"] as bool, 0);
}
final String encodedKey = base64Encode(Uint8List.fromList(collectionKey));
final startTime = DateTime.now();
final DiffResult result =
await Computer.shared().compute<Map<String, dynamic>, DiffResult>(
_parseDiff,
param: {
"collectionKey": encodedKey,
"diff": diff,
"hasMore": response.data["hasMore"] as bool,
},
taskName: "parseDiff",
);
devLog(
'[Collection-$collectionID] $result in ${DateTime.now().difference(startTime).inMilliseconds} ms',
name: "CollectionFilesService.getCollectionItemsDiff",
);
return result;
}
DiffResult _parseDiff(Map<String, dynamic> args) {
final Uint8List collectionKey = base64Decode(args['collectionKey']);
final List diff = args['diff'] as List;

View File

@@ -1,103 +0,0 @@
import 'dart:convert';
import 'package:dio/dio.dart';
import 'package:ente_crypto/ente_crypto.dart';
import 'package:flutter/material.dart';
import 'package:logging/logging.dart';
import 'package:photos/core/network/network.dart';
import 'package:photos/generated/l10n.dart';
import 'package:photos/models/file/file.dart';
import "package:photos/models/metadata/file_magic.dart";
import "package:photos/services/collections_service.dart";
import "package:photos/utils/dialog_util.dart";
import "package:photos/utils/file_key.dart";
class DiffFetcher {
final _logger = Logger("DiffFetcher");
final _enteDio = NetworkClient.instance.enteDio;
Future<List<EnteFile>> getPublicFiles(
BuildContext context,
int collectionID,
bool sortAsc,
) async {
try {
bool hasMore = false;
final sharedFiles = <EnteFile>[];
final headers =
CollectionsService.instance.publicCollectionHeaders(collectionID);
int sinceTime = 0;
do {
final response = await _enteDio.get(
"/public-collection/diff",
options: Options(headers: headers),
queryParameters: {"sinceTime": sinceTime},
);
final diff = response.data["diff"] as List;
hasMore = response.data["hasMore"] as bool;
for (final item in diff) {
final file = EnteFile();
if (item["isDeleted"]) {
continue;
}
file.uploadedFileID = item["id"];
file.collectionID = item["collectionID"];
file.ownerID = item["ownerID"];
file.encryptedKey = item["encryptedKey"];
file.keyDecryptionNonce = item["keyDecryptionNonce"];
file.fileDecryptionHeader = item["file"]["decryptionHeader"];
file.thumbnailDecryptionHeader =
item["thumbnail"]["decryptionHeader"];
file.metadataDecryptionHeader = item["metadata"]["decryptionHeader"];
if (item["info"] != null) {
file.fileSize = item["info"]["fileSize"];
}
final fileKey = getFileKey(file);
final encodedMetadata = await CryptoUtil.decryptChaCha(
CryptoUtil.base642bin(item["metadata"]["encryptedData"]),
fileKey,
CryptoUtil.base642bin(file.metadataDecryptionHeader!),
);
final Map<String, dynamic> metadata =
jsonDecode(utf8.decode(encodedMetadata));
file.applyMetadata(metadata);
if (item['pubMagicMetadata'] != null) {
final utfEncodedMmd = await CryptoUtil.decryptChaCha(
CryptoUtil.base642bin(item['pubMagicMetadata']['data']),
fileKey,
CryptoUtil.base642bin(item['pubMagicMetadata']['header']),
);
file.pubMmdEncodedJson = utf8.decode(utfEncodedMmd);
file.pubMmdVersion = item['pubMagicMetadata']['version'];
file.pubMagicMetadata =
PubMagicMetadata.fromEncodedJson(file.pubMmdEncodedJson!);
}
// To avoid local file to be used as thumbnail or full file.
file.localID = null;
sharedFiles.add(file);
}
if (diff.isNotEmpty) {
sinceTime = diff.last["updationTime"];
}
} while (hasMore);
if (sortAsc) {
sharedFiles.sort((a, b) => a.creationTime!.compareTo(b.creationTime!));
}
return sharedFiles;
} catch (e, s) {
_logger.severe("Failed to decrypt collection ", e, s);
await showErrorDialog(
context,
S.of(context).somethingWentWrong,
e.toString(),
);
rethrow;
}
}
}

View File

@@ -0,0 +1,71 @@
import 'package:flutter/material.dart';
import 'package:logging/logging.dart';
import 'package:photos/core/network/network.dart';
import 'package:photos/generated/l10n.dart';
import 'package:photos/models/file/file.dart';
import "package:photos/models/file/remote/asset.dart";
import "package:photos/models/file/remote/file_entry.dart";
import "package:photos/services/collections_service.dart";
import "package:photos/services/remote/fetch/collection_files.dart";
import "package:photos/utils/dialog_util.dart";
Future<List<EnteFile>> getPublicFiles(
BuildContext context,
int collectionID,
bool sortAsc,
) async {
try {
final collectionFilService =
CollectionFilesService(NetworkClient.instance.enteDio);
bool hasMore = false;
final sharedFiles = <EnteFile>[];
final headers =
CollectionsService.instance.publicCollectionHeaders(collectionID);
int sinceTime = 0;
final collectionKey =
CollectionsService.instance.getCollectionKey(collectionID);
do {
final diffResult = await collectionFilService.getPublicCollectionDiff(
collectionID,
sinceTime,
collectionKey,
headers,
);
for (final item in diffResult.updatedItems) {
final cf = CollectionFileEntry(
collectionID: item.collectionID,
fileID: item.fileID,
encFileKey: item.encFileKey!,
encFileKeyNonce: item.encFileKeyNonce!,
updatedAt: item.updatedAt,
createdAt: item.createdAt ?? 0,
);
final rAsset = RemoteAsset(
id: item.fileItem.fileID,
ownerID: item.fileItem.ownerID,
fileHeader: item.fileItem.fileDecryotionHeader!,
thumbHeader: item.fileItem.thumnailDecryptionHeader!,
metadata: item.fileItem.metadata!,
publicMetadata: item.fileItem.pubMagicMetadata,
privateMetadata: item.fileItem.magicMetadata,
info: item.fileItem.info,
);
sharedFiles.add(EnteFile.fromRemoteAsset(rAsset, cf));
}
sinceTime = diffResult.maxUpdatedAtTime;
hasMore = diffResult.hasMore;
} while (hasMore);
if (sortAsc) {
sharedFiles.sort((a, b) => a.creationTime!.compareTo(b.creationTime!));
}
return sharedFiles;
} catch (e, s) {
Logger("getPublicFiles").severe("Failed to decrypt collection ", e, s);
await showErrorDialog(
context,
S.of(context).somethingWentWrong,
e.toString(),
);
rethrow;
}
}

View File

@@ -44,8 +44,8 @@ import "package:photos/services/machine_learning/face_ml/person/person_service.d
import "package:photos/services/memory_home_widget_service.dart";
import "package:photos/services/notification_service.dart";
import "package:photos/services/people_home_widget_service.dart";
import "package:photos/services/sync/diff_fetcher.dart";
import "package:photos/services/sync/remote_sync_service.dart";
import "package:photos/services/sync/sync_public_collection.dart";
import 'package:photos/states/user_details_state.dart';
import 'package:photos/theme/colors.dart';
import "package:photos/theme/effects.dart";
@@ -120,8 +120,6 @@ class _HomeWidgetState extends State<HomeWidget> {
late StreamSubscription<CollectionUpdatedEvent> _collectionUpdatedEvent;
late StreamSubscription _publicAlbumLinkSubscription;
final DiffFetcher _diffFetcher = DiffFetcher();
@override
void initState() {
_logger.info("Building initstate");
@@ -337,8 +335,7 @@ class _HomeWidgetState extends State<HomeWidget> {
if (result) {
await dialog.show();
final List<EnteFile> sharedFiles =
await _diffFetcher.getPublicFiles(
final sharedFiles = await getPublicFiles(
context,
collection.id,
collection.pubMagicMetadata.asc ?? false,
@@ -369,7 +366,7 @@ class _HomeWidgetState extends State<HomeWidget> {
} else {
await dialog.show();
final List<EnteFile> sharedFiles = await _diffFetcher.getPublicFiles(
final List<EnteFile> sharedFiles = await getPublicFiles(
context,
collection.id,
collection.pubMagicMetadata.asc ?? false,