Implement ability to update files on remote

This commit is contained in:
Vishnu Mohandas 2020-12-01 00:12:11 +05:30
parent 51f083d290
commit d318a57d23
3 changed files with 167 additions and 11 deletions

View file

@ -273,6 +273,79 @@ class FilesDB {
return _convertToFiles(results);
}
Future<List<int>> getUploadedFileIDsToBeUpdated() async {
final db = await instance.database;
final rows = await db.query(
table,
columns: [columnUploadedFileID],
where:
'($columnUploadedFileID IS NOT NULL AND $columnUpdationTime IS NULL AND $columnIsDeleted = 0)',
orderBy: '$columnCreationTime DESC',
distinct: true,
);
final uploadedFileIDs = List<int>();
for (final row in rows) {
uploadedFileIDs.add(row[columnUploadedFileID]);
}
return uploadedFileIDs;
}
Future<File> getUploadedFileInAnyCollection(int uploadedFileID) async {
final db = await instance.database;
final results = await db.query(
table,
where: '$columnUploadedFileID = ?',
whereArgs: [
uploadedFileID,
],
limit: 1,
);
if (results.isEmpty) {
return null;
}
return _convertToFiles(results)[0];
}
Future<Set<String>> getUploadedLocalFileIDs() async {
final db = await instance.database;
final rows = await db.query(
table,
columns: [columnLocalID],
distinct: true,
where:
'$columnLocalID IS NOT NULL AND $columnUploadedFileID IS NOT NULL AND $columnIsDeleted = 0',
);
final result = Set<String>();
for (final row in rows) {
result.add(row[columnLocalID]);
}
return result;
}
Future<int> updateUploadedFile(
String localID,
String title,
Location location,
int creationTime,
int modificationTime,
int updationTime,
) async {
final db = await instance.database;
return await db.update(
table,
{
columnTitle: title,
columnLatitude: location.latitude,
columnLongitude: location.longitude,
columnCreationTime: creationTime,
columnModificationTime: modificationTime,
columnUpdationTime: updationTime,
},
where: '$columnLocalID = ?',
whereArgs: [localID],
);
}
Future<Map<int, File>> getLastCreatedFilesInCollections(
List<int> collectionIDs) async {
final db = await instance.database;
@ -398,6 +471,16 @@ class FilesDB {
);
}
Future<int> updateUploadedFileAcrossCollections(File file) async {
final db = await instance.database;
return await db.update(
table,
_getRowForFileWithoutCollection(file),
where: '$columnUploadedFileID = ?',
whereArgs: [file.uploadedFileID],
);
}
Future<int> markForDeletion(int uploadedFileID) async {
final db = await instance.database;
final values = new Map<String, dynamic>();
@ -573,6 +656,37 @@ class FilesDB {
return row;
}
Map<String, dynamic> _getRowForFileWithoutCollection(File file) {
final row = new Map<String, dynamic>();
row[columnLocalID] = file.localID;
row[columnUploadedFileID] = file.uploadedFileID;
row[columnOwnerID] = file.ownerID;
row[columnTitle] = file.title;
row[columnDeviceFolder] = file.deviceFolder;
if (file.location != null) {
row[columnLatitude] = file.location.latitude;
row[columnLongitude] = file.location.longitude;
}
switch (file.fileType) {
case FileType.image:
row[columnFileType] = 0;
break;
case FileType.video:
row[columnFileType] = 1;
break;
default:
row[columnFileType] = -1;
}
row[columnIsEncrypted] = file.isEncrypted ? 1 : 0;
row[columnCreationTime] = file.creationTime;
row[columnModificationTime] = file.modificationTime;
row[columnUpdationTime] = file.updationTime;
row[columnFileDecryptionHeader] = file.fileDecryptionHeader;
row[columnThumbnailDecryptionHeader] = file.thumbnailDecryptionHeader;
row[columnMetadataDecryptionHeader] = file.metadataDecryptionHeader;
return row;
}
File _getFileFromRow(Map<String, dynamic> row) {
final file = File();
file.generatedID = row[columnGeneratedID];

View file

@ -30,6 +30,7 @@ class SyncService {
final _uploader = FileUploader.instance;
final _collectionsService = CollectionsService.instance;
final _downloader = DiffFetcher();
final _uploadedLocalFileIDs = Set<String>();
bool _isSyncInProgress = false;
bool _syncStopRequested = false;
Future<void> _existingSync;
@ -67,6 +68,7 @@ class SyncService {
}
Future<void> sync() async {
_uploadedLocalFileIDs.addAll(await _db.getUploadedLocalFileIDs());
_syncStopRequested = false;
if (_isSyncInProgress) {
_logger.warning("Sync already in progress, skipping.");
@ -147,7 +149,20 @@ class SyncService {
final files = await getDeviceFiles(fromTime, toTime);
if (files.isNotEmpty) {
_logger.info("Fetched " + files.length.toString() + " files.");
// Deal with file updates
final updatedFiles =
files.where((file) => _uploadedLocalFileIDs.contains(file.localID));
_logger.info(updatedFiles.length.toString() + " files were updated.");
for (final file in updatedFiles) {
await _db.updateUploadedFile(
file.localID,
file.title,
file.location,
file.creationTime,
file.modificationTime,
null,
);
}
files.removeWhere((file) => _uploadedLocalFileIDs.contains(file.localID));
await _db.insertMultiple(files);
_logger.info("Inserted " + files.length.toString() + " files.");
await _prefs.setInt(_dbUpdationTimeKey, toTime);
@ -201,30 +216,53 @@ class SyncService {
Future<void> _uploadDiff() async {
final foldersToBackUp = Configuration.instance.getPathsToBackUp();
List<File> filesToBeUploaded =
final filesToBeUploaded =
await _db.getFilesToBeUploadedWithinFolders(foldersToBackUp);
if (kDebugMode) {
filesToBeUploaded = filesToBeUploaded
.where((element) => element.fileType != FileType.video)
.toList();
filesToBeUploaded
.removeWhere((element) => element.fileType == FileType.video);
}
final futures = List<Future>();
for (int i = 0; i < filesToBeUploaded.length; i++) {
final updatedFileIDs = await _db.getUploadedFileIDsToBeUpdated();
int uploadCounter = 0;
final totalUploads = filesToBeUploaded.length + updatedFileIDs.length;
for (final uploadedFileID in updatedFileIDs) {
if (_syncStopRequested) {
_syncStopRequested = false;
Bus.instance
.fire(SyncStatusUpdate(SyncStatus.completed, wasStopped: true));
return;
}
final file = await _db.getUploadedFileInAnyCollection(uploadedFileID);
final future = _uploader.upload(file, file.collectionID).then((value) {
uploadCounter++;
Bus.instance
.fire(CollectionUpdatedEvent(collectionID: file.collectionID));
Bus.instance.fire(SyncStatusUpdate(SyncStatus.in_progress,
completed: uploadCounter, total: totalUploads));
});
futures.add(future);
}
for (final file in filesToBeUploaded) {
if (_syncStopRequested) {
_syncStopRequested = false;
Bus.instance
.fire(SyncStatusUpdate(SyncStatus.completed, wasStopped: true));
return;
}
File file = filesToBeUploaded[i];
final collectionID = (await CollectionsService.instance
.getOrCreateForPath(file.deviceFolder))
.id;
final future = _uploader.upload(file, collectionID).then((value) {
uploadCounter++;
Bus.instance
.fire(CollectionUpdatedEvent(collectionID: file.collectionID));
Bus.instance.fire(SyncStatusUpdate(SyncStatus.in_progress,
completed: i + 1, total: filesToBeUploaded.length));
completed: uploadCounter, total: totalUploads));
});
futures.add(future);
}

View file

@ -125,7 +125,6 @@ class FileUploader {
_currentlyUploading++;
try {
final uploadedFile = await _tryToUpload(file, collectionID, forcedUpload);
await FilesDB.instance.update(uploadedFile);
_queue.remove(file.generatedID).completer.complete(uploadedFile);
} catch (e) {
_queue.remove(file.generatedID).completer.completeError(e);
@ -222,7 +221,7 @@ class FileUploader {
final metadataDecryptionHeader =
Sodium.bin2base64(encryptedMetadataData.header);
if (isAlreadyUploadedFile) {
return await _updateFile(
final updatedFile = await _updateFile(
file,
fileObjectKey,
fileDecryptionHeader,
@ -231,8 +230,11 @@ class FileUploader {
encryptedMetadata,
metadataDecryptionHeader,
);
// Update across all collections
await FilesDB.instance.updateUploadedFileAcrossCollections(updatedFile);
return updatedFile;
} else {
return await _uploadFile(
final uploadedFile = await _uploadFile(
file,
collectionID,
fileAttributes,
@ -243,6 +245,8 @@ class FileUploader {
encryptedMetadata,
metadataDecryptionHeader,
);
await FilesDB.instance.update(uploadedFile);
return uploadedFile;
}
} catch (e, s) {
_logger.severe(