mirror of
https://github.com/ente-io/ente.git
synced 2025-08-08 07:28:26 +00:00
[mob][photos] fuction to handle deeplinks
This commit is contained in:
parent
130418e443
commit
add3278c89
@ -4,6 +4,7 @@ import 'dart:math';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import "package:fast_base58/fast_base58.dart";
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:photos/core/configuration.dart';
|
||||
@ -64,6 +65,8 @@ class CollectionsService {
|
||||
Collection? cachedUncategorizedCollection;
|
||||
final Map<String, EnteFile> _coverCache = <String, EnteFile>{};
|
||||
final Map<int, int> _countCache = <int, int>{};
|
||||
final _cachedPublicAlbumToken = <int, String>{};
|
||||
final _cachedPublicAlbumJWTToken = <int, String>{};
|
||||
|
||||
CollectionsService._privateConstructor() {
|
||||
_db = CollectionsDB.instance;
|
||||
@ -172,6 +175,8 @@ class CollectionsService {
|
||||
_collectionIDToCollections.clear();
|
||||
cachedDefaultHiddenCollection = null;
|
||||
cachedUncategorizedCollection = null;
|
||||
_cachedPublicAlbumToken.clear();
|
||||
_cachedPublicAlbumJWTToken.clear();
|
||||
_cachedKeys.clear();
|
||||
}
|
||||
|
||||
@ -1030,6 +1035,84 @@ class CollectionsService {
|
||||
}
|
||||
}
|
||||
|
||||
Future<Collection> getPublicCollection(Uri uri) async {
|
||||
final String authToken = uri.queryParameters["t"] ?? "Not found";
|
||||
final String albumKey = uri.fragment;
|
||||
try {
|
||||
final response = await _enteDio.get(
|
||||
"/public-collection/info",
|
||||
options: Options(
|
||||
headers: {"X-Auth-Access-Token": authToken},
|
||||
),
|
||||
);
|
||||
|
||||
final collectionData = response.data["collection"];
|
||||
final Collection collection = Collection.fromMap(collectionData);
|
||||
final Uint8List collectionKey =
|
||||
Uint8List.fromList(Base58Decode(albumKey));
|
||||
|
||||
_logger.severe("Public collection fetched: $collectionData");
|
||||
_cachedKeys[collection.id] = collectionKey;
|
||||
_cachedPublicAlbumToken[collection.id] = authToken;
|
||||
return collection;
|
||||
} catch (e, s) {
|
||||
_logger.warning(e, s);
|
||||
_logger.severe("Failed to fetch public collection");
|
||||
if (e is DioError && e.response?.statusCode == 401) {
|
||||
throw UnauthorizedError();
|
||||
}
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> verifyPublicCollectionPassword(
|
||||
String passwordHash,
|
||||
int collectioID,
|
||||
) async {
|
||||
final authToken = await getPublicAlbumToken(collectioID);
|
||||
try {
|
||||
final response = await _enteDio.post(
|
||||
"https://api.ente.io/public-collection/verify-password",
|
||||
data: {"passHash": passwordHash},
|
||||
options: Options(
|
||||
headers: {
|
||||
"X-Auth-Access-Token": authToken,
|
||||
"Cache-Control": "no-cache",
|
||||
},
|
||||
),
|
||||
);
|
||||
final jwtToken = response.data["jwtToken"];
|
||||
_logger.severe("TOKEN $authToken");
|
||||
_logger.severe("JWT TOKEN $jwtToken");
|
||||
if (response.statusCode == 200) {
|
||||
await setPublicAlbumTokenJWT(collectioID, jwtToken);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (e) {
|
||||
_logger.severe("Failed to verify public collection password $e");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<String?> getPublicAlbumToken(int collectionID) async {
|
||||
if (_cachedPublicAlbumToken.containsKey(collectionID)) {
|
||||
return _cachedPublicAlbumToken[collectionID];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<String?> getPublicAlbumTokenJWT(int collectionID) async {
|
||||
if (_cachedPublicAlbumJWTToken.containsKey(collectionID)) {
|
||||
return _cachedPublicAlbumJWTToken[collectionID];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<void> setPublicAlbumTokenJWT(int collectionID, String token) async {
|
||||
_cachedPublicAlbumJWTToken[collectionID] = token;
|
||||
}
|
||||
|
||||
Future<Collection> _fromRemoteCollection(
|
||||
Map<String, dynamic>? collectionData,
|
||||
) async {
|
||||
|
@ -1,4 +1,5 @@
|
||||
import 'dart:async';
|
||||
import "dart:convert";
|
||||
import "dart:io";
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
@ -24,7 +25,10 @@ import 'package:photos/events/tab_changed_event.dart';
|
||||
import 'package:photos/events/trigger_logout_event.dart';
|
||||
import 'package:photos/events/user_logged_out_event.dart';
|
||||
import "package:photos/generated/l10n.dart";
|
||||
import "package:photos/models/collection/collection.dart";
|
||||
import 'package:photos/models/collection/collection_items.dart';
|
||||
import "package:photos/models/file/file.dart";
|
||||
// import "package:photos/models/file/file.dart";
|
||||
import 'package:photos/models/selected_files.dart';
|
||||
import 'package:photos/services/app_lifecycle_service.dart';
|
||||
import 'package:photos/services/collections_service.dart';
|
||||
@ -54,7 +58,9 @@ import "package:photos/ui/tabs/user_collections_tab.dart";
|
||||
import "package:photos/ui/viewer/gallery/collection_page.dart";
|
||||
import "package:photos/ui/viewer/search/search_widget.dart";
|
||||
import 'package:photos/ui/viewer/search_tab/search_tab.dart';
|
||||
import "package:photos/utils/crypto_util.dart";
|
||||
import 'package:photos/utils/dialog_util.dart';
|
||||
import "package:photos/utils/diff_fetcher.dart";
|
||||
import "package:photos/utils/navigation_util.dart";
|
||||
import 'package:receive_sharing_intent/receive_sharing_intent.dart';
|
||||
import 'package:uni_links/uni_links.dart';
|
||||
@ -100,6 +106,7 @@ class _HomeWidgetState extends State<HomeWidget> {
|
||||
late StreamSubscription<BackupFoldersUpdatedEvent> _backupFoldersUpdatedEvent;
|
||||
late StreamSubscription<AccountConfiguredEvent> _accountConfiguredEvent;
|
||||
late StreamSubscription<CollectionUpdatedEvent> _collectionUpdatedEvent;
|
||||
final DiffFetcher _diffFetcher = DiffFetcher();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@ -224,6 +231,89 @@ class _HomeWidgetState extends State<HomeWidget> {
|
||||
NotificationService.instance
|
||||
.initialize(_onDidReceiveNotificationResponse)
|
||||
.ignore();
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
await _initDeeplinkPublicAlbum();
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _initDeeplinkPublicAlbum() async {
|
||||
// Handle the terminated state
|
||||
final Uri? uri = await getInitialUri();
|
||||
if (uri != null) {
|
||||
await _handlePublicAlbumLink(uri);
|
||||
}
|
||||
|
||||
// Handle the background state
|
||||
uriLinkStream.listen((Uri? uri) async {
|
||||
if (uri != null) {
|
||||
await _handlePublicAlbumLink(uri);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _handlePublicAlbumLink(Uri uri) async {
|
||||
bool result = true;
|
||||
final Collection collection =
|
||||
await CollectionsService.instance.getPublicCollection(uri);
|
||||
final publicUrl = collection.publicURLs![0];
|
||||
if (publicUrl!.passwordEnabled) {
|
||||
await showTextInputDialog(
|
||||
context,
|
||||
title: S.of(context).enterPassword,
|
||||
submitButtonLabel: S.of(context).ok,
|
||||
alwaysShowSuccessState: false,
|
||||
onSubmit: (String text) async {
|
||||
if (text.trim() == "") {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
final hashedPassword = await CryptoUtil.deriveKey(
|
||||
utf8.encode(text) as Uint8List,
|
||||
CryptoUtil.base642bin(publicUrl.nonce!),
|
||||
publicUrl.memLimit!,
|
||||
publicUrl.opsLimit!,
|
||||
);
|
||||
_logger.info(
|
||||
"Hashed password: ${CryptoUtil.bin2base64(hashedPassword)}",
|
||||
);
|
||||
|
||||
result = await CollectionsService.instance
|
||||
.verifyPublicCollectionPassword(
|
||||
CryptoUtil.bin2base64(hashedPassword),
|
||||
collection.id,
|
||||
);
|
||||
if (!result) {
|
||||
await showErrorDialog(
|
||||
context,
|
||||
"Incorrect Password",
|
||||
"Plesae try again",
|
||||
);
|
||||
}
|
||||
} catch (e, s) {
|
||||
_logger.severe("Failed to decrypt password for album", e, s);
|
||||
rethrow;
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if (result) {
|
||||
final List<EnteFile> sharedFiles =
|
||||
await _diffFetcher.getPublicFiles(context, collection.id);
|
||||
|
||||
await routeToPage(
|
||||
context,
|
||||
CollectionPage(
|
||||
isFromPublicShareLink: true,
|
||||
sharedLinkFiles: sharedFiles,
|
||||
CollectionWithThumbnail(
|
||||
collection,
|
||||
null,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _autoLogoutAlert() async {
|
||||
|
@ -24,6 +24,8 @@ class CollectionPage extends StatelessWidget {
|
||||
final String tagPrefix;
|
||||
final bool? hasVerifiedLock;
|
||||
final bool isFromCollectPhotos;
|
||||
final bool isFromPublicShareLink;
|
||||
final List<EnteFile> sharedLinkFiles;
|
||||
|
||||
CollectionPage(
|
||||
this.c, {
|
||||
@ -31,6 +33,8 @@ class CollectionPage extends StatelessWidget {
|
||||
this.hasVerifiedLock = false,
|
||||
this.isFromCollectPhotos = false,
|
||||
Key? key,
|
||||
this.isFromPublicShareLink = false,
|
||||
this.sharedLinkFiles = const [],
|
||||
}) : super(key: key);
|
||||
|
||||
final _selectedFiles = SelectedFiles();
|
||||
@ -49,6 +53,9 @@ class CollectionPage extends StatelessWidget {
|
||||
c.thumbnail != null ? [c.thumbnail!] : null;
|
||||
final gallery = Gallery(
|
||||
asyncLoader: (creationStartTime, creationEndTime, {limit, asc}) async {
|
||||
if (isFromPublicShareLink) {
|
||||
return FileLoadResult(sharedLinkFiles, false);
|
||||
}
|
||||
final FileLoadResult result =
|
||||
await FilesDB.instance.getFilesInCollection(
|
||||
c.collection.id,
|
||||
@ -107,6 +114,8 @@ class CollectionPage extends StatelessWidget {
|
||||
_selectedFiles,
|
||||
collection: c.collection,
|
||||
isFromCollectPhotos: isFromCollectPhotos,
|
||||
isFromPublicShareLink: isFromPublicShareLink,
|
||||
files: sharedLinkFiles,
|
||||
),
|
||||
),
|
||||
bottomNavigationBar: isFromCollectPhotos
|
||||
|
@ -17,6 +17,7 @@ import "package:photos/l10n/l10n.dart";
|
||||
import 'package:photos/models/backup_status.dart';
|
||||
import 'package:photos/models/collection/collection.dart';
|
||||
import 'package:photos/models/device_collection.dart';
|
||||
import "package:photos/models/file/file.dart";
|
||||
import 'package:photos/models/gallery_type.dart';
|
||||
import "package:photos/models/metadata/common_keys.dart";
|
||||
import 'package:photos/models/selected_files.dart';
|
||||
@ -41,6 +42,7 @@ import "package:photos/ui/viewer/gallery/hooks/add_photos_sheet.dart";
|
||||
import 'package:photos/ui/viewer/gallery/hooks/pick_cover_photo.dart';
|
||||
import 'package:photos/utils/data_util.dart';
|
||||
import 'package:photos/utils/dialog_util.dart';
|
||||
import "package:photos/utils/file_download_util.dart";
|
||||
import 'package:photos/utils/magic_util.dart';
|
||||
import 'package:photos/utils/navigation_util.dart';
|
||||
import 'package:photos/utils/toast_util.dart';
|
||||
@ -53,16 +55,20 @@ class GalleryAppBarWidget extends StatefulWidget {
|
||||
final DeviceCollection? deviceCollection;
|
||||
final Collection? collection;
|
||||
final bool isFromCollectPhotos;
|
||||
final bool isFromPublicShareLink;
|
||||
final List<EnteFile> files;
|
||||
|
||||
const GalleryAppBarWidget(
|
||||
this.type,
|
||||
this.title,
|
||||
this.selectedFiles, {
|
||||
Key? key,
|
||||
super.key,
|
||||
this.deviceCollection,
|
||||
this.collection,
|
||||
this.isFromCollectPhotos = false,
|
||||
}) : super(key: key);
|
||||
this.isFromPublicShareLink = false,
|
||||
this.files = const [],
|
||||
});
|
||||
|
||||
@override
|
||||
State<GalleryAppBarWidget> createState() => _GalleryAppBarWidgetState();
|
||||
@ -84,6 +90,7 @@ enum AlbumPopupAction {
|
||||
pinAlbum,
|
||||
removeLink,
|
||||
cleanUncategorized,
|
||||
downloadAlbum,
|
||||
}
|
||||
|
||||
class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
|
||||
@ -457,6 +464,17 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
|
||||
),
|
||||
],
|
||||
);
|
||||
if (widget.isFromPublicShareLink) {
|
||||
actions.clear();
|
||||
items.clear();
|
||||
items.add(
|
||||
EntePopupMenuItem(
|
||||
"Download album",
|
||||
value: AlbumPopupAction.downloadAlbum,
|
||||
icon: Icons.download_outlined,
|
||||
),
|
||||
);
|
||||
}
|
||||
if (items.isNotEmpty) {
|
||||
actions.add(
|
||||
PopupMenuButton(
|
||||
@ -512,6 +530,8 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
|
||||
await showOnMap();
|
||||
} else if (value == AlbumPopupAction.cleanUncategorized) {
|
||||
await onCleanUncategorizedClick(context);
|
||||
} else if (value == AlbumPopupAction.downloadAlbum) {
|
||||
await _downloadPublicAlbumToGallery(widget.files);
|
||||
} else {
|
||||
showToast(context, S.of(context).somethingWentWrong);
|
||||
}
|
||||
@ -523,6 +543,18 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
|
||||
return actions;
|
||||
}
|
||||
|
||||
Future<void> _downloadPublicAlbumToGallery(List<EnteFile> files) async {
|
||||
final dialog = createProgressDialog(context, "Downloading album");
|
||||
await dialog.show();
|
||||
try {
|
||||
await downloadPublicAlbumToGallery(files);
|
||||
} catch (e, s) {
|
||||
_logger.severe("Failed to download album", e, s);
|
||||
await showGenericErrorDialog(context: context, error: e);
|
||||
}
|
||||
await dialog.hide();
|
||||
}
|
||||
|
||||
Future<void> onCleanUncategorizedClick(BuildContext buildContext) async {
|
||||
final actionResult = await showChoiceActionSheet(
|
||||
context,
|
||||
|
@ -1,11 +1,14 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
|
||||
import "package:dio/dio.dart";
|
||||
import "package:flutter/material.dart";
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:photos/core/network/network.dart';
|
||||
import 'package:photos/db/files_db.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/crypto_util.dart';
|
||||
import 'package:photos/utils/file_download_util.dart';
|
||||
|
||||
@ -13,6 +16,79 @@ class DiffFetcher {
|
||||
final _logger = Logger("DiffFetcher");
|
||||
final _enteDio = NetworkClient.instance.enteDio;
|
||||
|
||||
Future<List<EnteFile>> getPublicFiles(
|
||||
BuildContext context,
|
||||
int collectionID,
|
||||
) async {
|
||||
try {
|
||||
final authToken =
|
||||
await CollectionsService.instance.getPublicAlbumToken(collectionID);
|
||||
final authJWTToken = await CollectionsService.instance
|
||||
.getPublicAlbumTokenJWT(collectionID);
|
||||
final time =
|
||||
CollectionsService.instance.getCollectionSyncTime(collectionID);
|
||||
|
||||
final headers = {
|
||||
"X-Auth-Access-Token": authToken,
|
||||
"Cache-Control": "no-cache",
|
||||
if (authJWTToken != null) "X-Auth-Access-Token-JWT": authJWTToken,
|
||||
};
|
||||
|
||||
final response = await _enteDio.get(
|
||||
"/public-collection/diff",
|
||||
options: Options(headers: headers),
|
||||
queryParameters: {"sinceTime": time},
|
||||
);
|
||||
|
||||
final diff = response.data["diff"] as List;
|
||||
final startTime = DateTime.now();
|
||||
final sharedFiles = <EnteFile>[];
|
||||
|
||||
for (final item in diff) {
|
||||
final file = EnteFile();
|
||||
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!);
|
||||
}
|
||||
sharedFiles.add(file);
|
||||
}
|
||||
|
||||
_logger.info('[Collection-$collectionID] parsed ${diff.length} '
|
||||
'diff items ( ${sharedFiles.length} updated) in ${DateTime.now().difference(startTime).inMilliseconds}ms');
|
||||
return sharedFiles;
|
||||
} catch (e, s) {
|
||||
_logger.severe("Failed to decrypt collection $e", s);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<Diff> getEncryptedFilesDiff(int collectionID, int sinceTime) async {
|
||||
try {
|
||||
final response = await _enteDio.get(
|
||||
|
@ -24,14 +24,96 @@ import "package:photos/utils/file_util.dart";
|
||||
|
||||
final _logger = Logger("file_download_util");
|
||||
|
||||
Future<File?> downloadAndDecryptPublicFile(
|
||||
EnteFile file,
|
||||
String authToken, {
|
||||
ProgressCallback? progressCallback,
|
||||
}) async {
|
||||
final String logPrefix = 'Public File-${file.uploadedFileID}:';
|
||||
_logger
|
||||
.info('$logPrefix starting download ${formatBytes(file.fileSize ?? 0)}');
|
||||
_logger.severe("File id ${file.uploadedFileID}");
|
||||
|
||||
final String tempDir = Configuration.instance.getTempDirectory();
|
||||
final String encryptedFilePath = "$tempDir${file.uploadedFileID}.encrypted";
|
||||
final String decryptedFilePath = "$tempDir${file.uploadedFileID}.decrypted";
|
||||
|
||||
try {
|
||||
final authJWTToken = await CollectionsService.instance
|
||||
.getPublicAlbumTokenJWT(file.collectionID!);
|
||||
|
||||
final headers = {
|
||||
"X-Auth-Access-Token": authToken,
|
||||
if (authJWTToken != null) "X-Auth-Access-Token-JWT": authJWTToken,
|
||||
};
|
||||
final response = (await NetworkClient.instance.getDio().download(
|
||||
"https://public-albums.ente.io/download/?fileID=${file.uploadedFileID}",
|
||||
encryptedFilePath,
|
||||
options: Options(
|
||||
headers: headers,
|
||||
responseType: ResponseType.bytes,
|
||||
),
|
||||
onReceiveProgress: (a, b) {
|
||||
progressCallback?.call(a, b);
|
||||
},
|
||||
));
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
_logger.warning('$logPrefix download failed ${response.toString()}');
|
||||
return null;
|
||||
}
|
||||
|
||||
final int sizeInBytes = file.fileSize!;
|
||||
final FakePeriodicProgress? fakeProgress = file.fileType == FileType.video
|
||||
? FakePeriodicProgress(
|
||||
callback: (count) {
|
||||
progressCallback?.call(sizeInBytes, sizeInBytes);
|
||||
},
|
||||
duration: const Duration(milliseconds: 5000),
|
||||
)
|
||||
: null;
|
||||
try {
|
||||
fakeProgress?.start();
|
||||
await CryptoUtil.decryptFile(
|
||||
encryptedFilePath,
|
||||
decryptedFilePath,
|
||||
CryptoUtil.base642bin(file.fileDecryptionHeader!),
|
||||
getFileKey(file),
|
||||
);
|
||||
fakeProgress?.stop();
|
||||
_logger.info('$logPrefix file saved at $decryptedFilePath');
|
||||
} catch (e, s) {
|
||||
fakeProgress?.stop();
|
||||
_logger.severe("Critical: $logPrefix failed to decrypt", e, s);
|
||||
return null;
|
||||
}
|
||||
return File(decryptedFilePath);
|
||||
} catch (e, s) {
|
||||
_logger.severe("$logPrefix failed to download", e, s);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<File?> downloadAndDecrypt(
|
||||
EnteFile file, {
|
||||
ProgressCallback? progressCallback,
|
||||
}) async {
|
||||
final authToken =
|
||||
await CollectionsService.instance.getPublicAlbumToken(file.collectionID!);
|
||||
if (authToken != null) {
|
||||
final authToken = await CollectionsService.instance
|
||||
.getPublicAlbumToken(file.collectionID!);
|
||||
|
||||
return await downloadAndDecryptPublicFile(
|
||||
file,
|
||||
authToken!,
|
||||
progressCallback: progressCallback,
|
||||
);
|
||||
}
|
||||
|
||||
final String logPrefix = 'File-${file.uploadedFileID}:';
|
||||
_logger
|
||||
.info('$logPrefix starting download ${formatBytes(file.fileSize ?? 0)}');
|
||||
|
||||
final String tempDir = Configuration.instance.getTempDirectory();
|
||||
final String encryptedFilePath = "$tempDir${file.generatedID}.encrypted";
|
||||
final encryptedFile = File(encryptedFilePath);
|
||||
@ -125,6 +207,12 @@ Future<Uint8List> getFileKeyUsingBgWorker(EnteFile file) async {
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> downloadPublicAlbumToGallery(List<EnteFile> files) async {
|
||||
for (final file in files) {
|
||||
await downloadToGallery(file);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> downloadToGallery(EnteFile file) async {
|
||||
try {
|
||||
final FileType type = file.fileType;
|
||||
|
@ -12,6 +12,7 @@ import 'package:photos/core/constants.dart';
|
||||
import 'package:photos/core/errors.dart';
|
||||
import 'package:photos/core/network/network.dart';
|
||||
import 'package:photos/models/file/file.dart';
|
||||
import "package:photos/services/collections_service.dart";
|
||||
import 'package:photos/utils/crypto_util.dart';
|
||||
import 'package:photos/utils/file_download_util.dart';
|
||||
import 'package:photos/utils/file_uploader_util.dart';
|
||||
@ -160,15 +161,36 @@ Future<void> _downloadAndDecryptThumbnail(FileDownloadItem item) async {
|
||||
final file = item.file;
|
||||
Uint8List encryptedThumbnail;
|
||||
try {
|
||||
encryptedThumbnail = (await NetworkClient.instance.getDio().get(
|
||||
file.thumbnailUrl,
|
||||
options: Options(
|
||||
headers: {"X-Auth-Token": Configuration.instance.getToken()},
|
||||
responseType: ResponseType.bytes,
|
||||
),
|
||||
cancelToken: item.cancelToken,
|
||||
))
|
||||
.data;
|
||||
final authToken = await CollectionsService.instance
|
||||
.getPublicAlbumToken(file.collectionID!);
|
||||
if (authToken != null) {
|
||||
final authJWTToken = await CollectionsService.instance
|
||||
.getPublicAlbumTokenJWT(file.collectionID!);
|
||||
|
||||
final headers = {
|
||||
"X-Auth-Access-Token": authToken,
|
||||
if (authJWTToken != null) "X-Auth-Access-Token-JWT": authJWTToken,
|
||||
};
|
||||
|
||||
encryptedThumbnail = (await NetworkClient.instance.getDio().get(
|
||||
"https://public-albums.ente.io/preview/?fileID=${file.uploadedFileID}",
|
||||
options: Options(
|
||||
headers: headers,
|
||||
responseType: ResponseType.bytes,
|
||||
),
|
||||
))
|
||||
.data;
|
||||
} else {
|
||||
encryptedThumbnail = (await NetworkClient.instance.getDio().get(
|
||||
file.thumbnailUrl,
|
||||
options: Options(
|
||||
headers: {"X-Auth-Token": Configuration.instance.getToken()},
|
||||
responseType: ResponseType.bytes,
|
||||
),
|
||||
cancelToken: item.cancelToken,
|
||||
))
|
||||
.data;
|
||||
}
|
||||
} catch (e) {
|
||||
if (e is DioError && CancelToken.isCancel(e)) {
|
||||
return;
|
||||
|
Loading…
x
Reference in New Issue
Block a user