This commit is contained in:
Alex 2026-01-08 10:11:47 -06:00
parent 6ef7d2bb47
commit dd215ec758
No known key found for this signature in database
GPG Key ID: 53CD082B3A5E1082
9 changed files with 50 additions and 92 deletions

View File

@ -249,13 +249,14 @@ class BackgroundWorkerBgService extends BackgroundWorkerFlutterApi {
final networkCapabilities = await _ref?.read(connectivityApiProvider).getCapabilities() ?? [];
return _ref
?.read(uploadServiceProvider)
.startForegroundUpload(
.startUploadWithHttp(
currentUser.id,
networkCapabilities.isUnmetered,
_cancellationToken,
(_, __, ___, ____) {}, // onProgress - not needed for background
(_, __) {}, // onSuccess - not needed for background
(_) {}, // onError - not needed for background
onProgress: (_, __, ___, ____) {},
onSuccess: (_, __) {},
onError: (_) {},
onICloudProgress: (_, __) {},
);
},
(error, stack) {

View File

@ -93,7 +93,7 @@ class _DriftBackupPageState extends ConsumerState<DriftBackupPage> {
Logger("DriftBackupPage").warning("Remote sync did not complete successfully, skipping backup");
return;
}
await backupNotifier.startBackup(currentUser.id);
await backupNotifier.startForegroundBackup(currentUser.id);
}
Future<void> stopBackup() async {

View File

@ -116,7 +116,7 @@ class _DriftBackupAlbumSelectionPageState extends ConsumerState<DriftBackupAlbum
backupNotifier.stopBackup().whenComplete(
() => backgroundSync.syncRemote().then((success) {
if (success) {
return backupNotifier.startBackup(user.id);
return backupNotifier.startForegroundBackup(user.id);
} else {
Logger('DriftBackupAlbumSelectionPage').warning('Background sync failed, not starting backup');
}

View File

@ -63,7 +63,7 @@ class DriftBackupOptionsPage extends ConsumerWidget {
backupNotifier.stopBackup().whenComplete(
() => backgroundSync.syncRemote().then((success) {
if (success) {
return backupNotifier.startBackup(currentUser.id);
return backupNotifier.startForegroundBackup(currentUser.id);
} else {
Logger('DriftBackupOptionsPage').warning('Background sync failed, not starting backup');
}

View File

@ -132,7 +132,7 @@ class SplashScreenPageState extends ConsumerState<SplashScreenPage> {
if (isEnableBackup) {
final currentUser = Store.tryGet(StoreKey.currentUser);
if (currentUser != null) {
unawaited(notifier.startBackup(currentUser.id));
unawaited(notifier.startForegroundBackup(currentUser.id));
}
}
}

View File

@ -179,7 +179,10 @@ class AppLifeCycleNotifier extends StateNotifier<AppLifeCycleEnum> {
if (isEnableBackup) {
final currentUser = Store.tryGet(StoreKey.currentUser);
if (currentUser != null) {
await _safeRun(_ref.read(driftBackupProvider.notifier).startBackup(currentUser.id), "handleBackupResume");
await _safeRun(
_ref.read(driftBackupProvider.notifier).startForegroundBackup(currentUser.id),
"handleBackupResume",
);
}
}
}

View File

@ -393,7 +393,7 @@ class DriftBackupNotifier extends StateNotifier<DriftBackupState> {
state = state.copyWith(isSyncing: isSyncing);
}
Future<void> startBackup(String userId) async {
Future<void> startForegroundBackup(String userId) async {
state = state.copyWith(error: BackupError.none);
final cancelToken = CancellationToken();
@ -403,13 +403,13 @@ class DriftBackupNotifier extends StateNotifier<DriftBackupState> {
final hasWifi = networkCapabilities.isUnmetered;
_logger.info('Network capabilities: $networkCapabilities, hasWifi/isUnmetered: $hasWifi');
return _uploadService.startForegroundUpload(
return _uploadService.startUploadWithHttp(
userId,
hasWifi,
cancelToken,
_handleForegroundBackupProgress,
_handleForegroundBackupSuccess,
_handleForegroundBackupError,
onProgress: _handleForegroundBackupProgress,
onSuccess: _handleForegroundBackupSuccess,
onError: _handleForegroundBackupError,
onICloudProgress: _handleICloudProgress,
);
}
@ -497,7 +497,7 @@ class DriftBackupNotifier extends StateNotifier<DriftBackupState> {
if (tasks.isEmpty) {
_logger.info("Start backup with URLSession");
return _uploadService.startBackupWithURLSession(userId);
return _uploadService.startUploadWithURLSession(userId);
}
_logger.info("Tasks to resume: ${tasks.length}");

View File

@ -94,56 +94,7 @@ class UploadRepository {
);
}
Future<UploadResult> uploadSingleAsset({
required File file,
required String originalFileName,
required Map<String, String> headers,
required Map<String, String> fields,
required Client httpClient,
required CancellationToken cancelToken,
required void Function(int bytes, int totalBytes) onProgress,
}) async {
return _uploadFile(
file: file,
originalFileName: originalFileName,
headers: headers,
fields: fields,
httpClient: httpClient,
cancelToken: cancelToken,
onProgress: onProgress,
logContext: 'assetUpload',
);
}
/// Upload live photo video part and return the video asset ID
Future<String?> uploadLivePhotoVideo({
required File livePhotoFile,
required String originalFileName,
required Map<String, String> headers,
required Map<String, String> fields,
required Client httpClient,
required CancellationToken cancelToken,
required void Function(int bytes, int totalBytes) onProgress,
}) async {
final result = await _uploadFile(
file: livePhotoFile,
originalFileName: originalFileName,
headers: headers,
fields: fields,
httpClient: httpClient,
cancelToken: cancelToken,
onProgress: onProgress,
logContext: 'livePhotoVideoUpload',
);
if (result.isSuccess && result.remoteAssetId != null) {
return result.remoteAssetId;
}
return null;
}
Future<UploadResult> _uploadFile({
Future<UploadResult> uploadFile({
required File file,
required String originalFileName,
required Map<String, String> headers,

View File

@ -121,7 +121,7 @@ class UploadService {
/// Find backup candidates
/// Build the upload tasks
/// Enqueue the tasks
Future<void> startBackupWithURLSession(String userId) async {
Future<void> startUploadWithURLSession(String userId) async {
await _storageRepository.clearCache();
shouldAbortQueuingTasks = false;
@ -146,14 +146,14 @@ class UploadService {
}
}
Future<void> startForegroundUpload(
Future<void> startUploadWithHttp(
String userId,
bool hasWifi,
CancellationToken cancelToken,
void Function(String localAssetId, String filename, int bytes, int totalBytes) onProgress,
void Function(String localAssetId, String remoteAssetId) onSuccess,
void Function(String errorMessage) onError, {
void Function(String localAssetId, double progress)? onICloudProgress,
CancellationToken cancelToken, {
required void Function(String localAssetId, String filename, int bytes, int totalBytes) onProgress,
required void Function(String localAssetId, String remoteAssetId) onSuccess,
required void Function(String errorMessage) onError,
required void Function(String localAssetId, double progress) onICloudProgress,
}) async {
const concurrentUploads = 3;
final httpClients = List.generate(concurrentUploads, (_) => Client());
@ -186,7 +186,6 @@ class UploadService {
final requireWifi = _shouldRequireWiFi(asset);
if (requireWifi && !hasWifi) {
_logger.warning('Skipping upload for ${asset.id} because it requires WiFi');
continue;
}
@ -194,9 +193,9 @@ class UploadService {
asset,
httpClient,
cancelToken,
onProgress,
onSuccess,
onError,
onProgress: onProgress,
onSuccess: onSuccess,
onError: onError,
onICloudProgress: onICloudProgress,
);
}
@ -220,11 +219,11 @@ class UploadService {
Future<void> _uploadSingleAsset(
LocalAsset asset,
Client httpClient,
CancellationToken cancelToken,
void Function(String id, String filename, int bytes, int totalBytes) onProgress,
void Function(String localAssetId, String remoteAssetId) onSuccess,
void Function(String errorMessage) onError, {
void Function(String localAssetId, double progress)? onICloudProgress,
CancellationToken cancelToken, {
required void Function(String id, String filename, int bytes, int totalBytes) onProgress,
required void Function(String localAssetId, String remoteAssetId) onSuccess,
required void Function(String errorMessage) onError,
required void Function(String localAssetId, double progress) onICloudProgress,
}) async {
File? file;
File? livePhotoFile;
@ -244,12 +243,10 @@ class UploadService {
PMProgressHandler? progressHandler;
StreamSubscription? progressSubscription;
if (onICloudProgress != null) {
progressHandler = PMProgressHandler();
progressSubscription = progressHandler.stream.listen((event) {
onICloudProgress(asset.localId!, event.progress);
});
}
progressHandler = PMProgressHandler();
progressSubscription = progressHandler.stream.listen((event) {
onICloudProgress(asset.localId!, event.progress);
});
try {
file = await _storageRepository.loadFileFromCloud(asset.id, progressHandler: progressHandler);
@ -260,7 +257,7 @@ class UploadService {
);
}
} finally {
await progressSubscription?.cancel();
await progressSubscription.cancel();
}
} else {
// Get files locally
@ -300,23 +297,28 @@ class UploadService {
String? livePhotoVideoId;
if (entity.isLivePhoto && livePhotoFile != null) {
final livePhotoTitle = p.setExtension(originalFileName, p.extension(livePhotoFile.path));
livePhotoVideoId = await _uploadRepository.uploadLivePhotoVideo(
livePhotoFile: livePhotoFile,
final livePhotoResult = await _uploadRepository.uploadFile(
file: livePhotoFile,
originalFileName: livePhotoTitle,
headers: headers,
fields: fields,
httpClient: httpClient,
cancelToken: cancelToken,
onProgress: (bytes, totalBytes) => onProgress(asset.localId!, livePhotoTitle, bytes, totalBytes),
logContext: 'livePhotoVideo[${asset.localId}]',
);
if (livePhotoResult.isSuccess && livePhotoResult.remoteAssetId != null) {
livePhotoVideoId = livePhotoResult.remoteAssetId;
}
}
// Add livePhotoVideoId to fields if available
if (livePhotoVideoId != null) {
fields['livePhotoVideoId'] = livePhotoVideoId;
}
final result = await _uploadRepository.uploadSingleAsset(
final result = await _uploadRepository.uploadFile(
file: file,
originalFileName: originalFileName,
headers: headers,
@ -324,6 +326,7 @@ class UploadService {
httpClient: httpClient,
cancelToken: cancelToken,
onProgress: (bytes, totalBytes) => onProgress(asset.localId!, originalFileName, bytes, totalBytes),
logContext: 'asset[${asset.localId}]',
);
if (result.isSuccess && result.remoteAssetId != null) {