Clean up
This commit is contained in:
@@ -421,36 +421,6 @@ class FilesDB with SqlDbBase {
|
||||
await db.execute('DELETE FROM entities');
|
||||
}
|
||||
|
||||
// blocked upload queue (shared assets rendering)
|
||||
Future<int> insertAndGetId(EnteFile file) async {
|
||||
_logger.info("Inserting $file");
|
||||
final db = await instance.sqliteAsyncDB;
|
||||
final columnsAndPlaceholders =
|
||||
_generateColumnsAndPlaceholdersForInsert(fileGenId: file.generatedID);
|
||||
final values = _getParameterSetForFile(file);
|
||||
return await db.writeTransaction((tx) async {
|
||||
await tx.execute(
|
||||
'INSERT OR REPLACE INTO $filesTable (${columnsAndPlaceholders["columns"]}) VALUES (${columnsAndPlaceholders["placeholders"]})',
|
||||
values,
|
||||
);
|
||||
final result = await tx.get('SELECT last_insert_rowid()');
|
||||
return result["last_insert_rowid()"] as int;
|
||||
});
|
||||
}
|
||||
|
||||
// upload queue
|
||||
Future<EnteFile?> getFile(int generatedID) async {
|
||||
final db = await instance.sqliteAsyncDB;
|
||||
final results = await db.getAll(
|
||||
'SELECT * FROM $filesTable WHERE $columnGeneratedID = ?',
|
||||
[generatedID],
|
||||
);
|
||||
if (results.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
return convertToFiles(results)[0];
|
||||
}
|
||||
|
||||
Future<BackedUpFileIDs> getBackedUpIDs() async {
|
||||
final db = await instance.sqliteAsyncDB;
|
||||
final results = await db.getAll(
|
||||
@@ -616,23 +586,6 @@ class FilesDB with SqlDbBase {
|
||||
return files;
|
||||
}
|
||||
|
||||
// todo:rewrite (upload related)
|
||||
// Future<List<int>> getUploadedFileIDsToBeUpdated(int ownerID) async {
|
||||
// final db = await instance.sqliteAsyncDB;
|
||||
// final rows = await db.getAll(
|
||||
// 'SELECT DISTINCT $columnUploadedFileID FROM $filesTable WHERE '
|
||||
// '($columnLocalID IS NOT NULL AND $columnOwnerID = ? AND '
|
||||
// '($columnUploadedFileID IS NOT NULL AND $columnUploadedFileID IS NOT -1) '
|
||||
// 'AND $columnUpdationTime IS NULL) ORDER BY $columnCreationTime DESC ',
|
||||
// [ownerID],
|
||||
// );
|
||||
// final uploadedFileIDs = <int>[];
|
||||
// for (final row in rows) {
|
||||
// uploadedFileIDs.add(row[columnUploadedFileID] as int);
|
||||
// }
|
||||
// return uploadedFileIDs;
|
||||
// }
|
||||
|
||||
// todo:rewrite (upload related)
|
||||
Future<void> markFilesForReUpload(
|
||||
int ownerID,
|
||||
@@ -835,25 +788,6 @@ class FilesDB with SqlDbBase {
|
||||
return deduplicatedFiles;
|
||||
}
|
||||
|
||||
Map<String, String> _generateColumnsAndPlaceholdersForInsert({
|
||||
required int? fileGenId,
|
||||
}) {
|
||||
final columnNames = <String>[];
|
||||
|
||||
for (String columnName in _columnNames) {
|
||||
if (columnName == columnGeneratedID && fileGenId == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
columnNames.add(columnName);
|
||||
}
|
||||
|
||||
return {
|
||||
"columns": columnNames.join(","),
|
||||
"placeholders": List.filled(columnNames.length, "?").join(","),
|
||||
};
|
||||
}
|
||||
|
||||
List<EnteFile> convertToFilesForIsolate(Map args) {
|
||||
final List<EnteFile> files = [];
|
||||
for (final result in args["result"]) {
|
||||
@@ -870,66 +804,6 @@ class FilesDB with SqlDbBase {
|
||||
return files;
|
||||
}
|
||||
|
||||
List<Object?> _getParameterSetForFile(
|
||||
EnteFile file, {
|
||||
bool omitCollectionId = false,
|
||||
}) {
|
||||
final values = <Object?>[];
|
||||
|
||||
double? latitude = file.location?.latitude;
|
||||
double? longitude = file.location?.longitude;
|
||||
|
||||
int? creationTime = file.creationTime;
|
||||
|
||||
if (file.generatedID != null) {
|
||||
values.add(file.generatedID);
|
||||
}
|
||||
values.addAll([
|
||||
file.localID,
|
||||
file.uploadedFileID ?? -1,
|
||||
file.ownerID,
|
||||
file.collectionID ?? -1,
|
||||
file.title,
|
||||
file.deviceFolder,
|
||||
latitude,
|
||||
longitude,
|
||||
getInt(file.fileType),
|
||||
file.modificationTime,
|
||||
// file.encryptedKey,
|
||||
'no_encrypted_key', // encryptedKey is not used in this context
|
||||
// file.keyDecryptionNonce,
|
||||
'no_key_decryption_nonce', // keyDecryptionNonce is not used in this context
|
||||
// file.fileDecryptionHeader,
|
||||
'no_file_decryption_header', // fileDecryptionHeader is not used in this context
|
||||
// file.thumbnailDecryptionHeader,
|
||||
'no_thumbnail_decryption_header', // thumbnailDecryptionHeader is not used in this context
|
||||
'na',
|
||||
creationTime,
|
||||
file.updationTime,
|
||||
file.fileSubType ?? -1,
|
||||
file.duration ?? 0,
|
||||
file.exif,
|
||||
file.hash,
|
||||
file.metadataVersion,
|
||||
// file.mMdEncodedJson ?? '{}',
|
||||
{}, // mMdEncodedJson is not used in this context
|
||||
0, // version
|
||||
0, // default visibility
|
||||
'{}', // pubMmdEncodedJson is not used in this context
|
||||
0, // pubMmdVersion is not used in this context
|
||||
// file.pubMmdEncodedJson ?? '{}',
|
||||
// file.pubMmdVersion,
|
||||
file.fileSize,
|
||||
DateTime.now().microsecondsSinceEpoch, // added Time
|
||||
]);
|
||||
|
||||
if (omitCollectionId) {
|
||||
values.removeAt(3);
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
EnteFile _getFileFromRow(Map<String, dynamic> row) {
|
||||
final file = EnteFile();
|
||||
file.generatedID = row[columnGeneratedID];
|
||||
|
||||
@@ -49,6 +49,34 @@ class LocalDB with SqlDbBase {
|
||||
);
|
||||
}
|
||||
|
||||
// Store time and location metadata inside edited_assets
|
||||
Future<void> trackEdit(
|
||||
String id,
|
||||
int createdAt,
|
||||
int modifiedAt,
|
||||
double? lat,
|
||||
double? lng,
|
||||
) async {
|
||||
final stopwatch = Stopwatch()..start();
|
||||
await _sqliteDB.execute(
|
||||
'INSERT INTO edited_assets (id, created_at, modified_at, latitude, longitude) VALUES (?, ?, ?, ?, ?) ON CONFLICT(id) DO UPDATE SET created_at = ?, modified_at = ?, latitude = ?, longitude = ?',
|
||||
[id, createdAt, modifiedAt, lat, lng, createdAt, modifiedAt, lat, lng],
|
||||
);
|
||||
debugPrint(
|
||||
'$runtimeType editCopy complete in ${stopwatch.elapsed.inMilliseconds}ms for $id',
|
||||
);
|
||||
}
|
||||
) async {
|
||||
final stopwatch = Stopwatch()..start();
|
||||
await _sqliteDB.execute(
|
||||
'INSERT INTO edited_assets (id, created_at, modified_at, latitude, longitude) VALUES (?, ?, ?, ?, ?) ON CONFLICT(id) DO UPDATE SET created_at = ?, modified_at = ?, latitude = ?, longitude = ?',
|
||||
[id, createdAt, modifiedAt, lat, lng, createdAt, modifiedAt, lat, lng],
|
||||
);
|
||||
debugPrint(
|
||||
'$runtimeType editCopy complete in ${stopwatch.elapsed.inMilliseconds}ms for $id',
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> updateMetadata(
|
||||
String id, {
|
||||
DroidMetadata? droid,
|
||||
|
||||
@@ -215,6 +215,17 @@ class LocalDBMigration {
|
||||
'''
|
||||
CREATE INDEX IF NOT EXISTS assets_created_at_desc ON assets(created_at DESC);
|
||||
''',
|
||||
'''
|
||||
CREATE TABLE edited_assets (
|
||||
id String NOT NULL,
|
||||
created_at INTEGER NOT NULL,
|
||||
modified_at INTEGER NOT NULL,
|
||||
latitude REAL,
|
||||
longitude REAL,
|
||||
PRIMARY KEY (id)
|
||||
FOREIGN KEY (id) REFERENCES assets(id) ON DELETE CASCADE
|
||||
);
|
||||
''',
|
||||
];
|
||||
|
||||
static Future<void> migrate(
|
||||
|
||||
@@ -43,6 +43,14 @@ extension UploadQueueTable on LocalDB {
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> existsQueueEntry(AssetUploadQueue entry) async {
|
||||
final result = await sqliteDB.getAll(
|
||||
'SELECT 1 FROM asset_upload_queue WHERE asset_id = ? AND owner_id = ? AND dest_collection_id = ?',
|
||||
[entry.id, entry.ownerId, entry.destCollectionId],
|
||||
);
|
||||
return result.isNotEmpty;
|
||||
}
|
||||
|
||||
Future<int> delete(AssetUploadQueue entry) async {
|
||||
final stopwatch = Stopwatch()..start();
|
||||
final result = await sqliteDB.execute(
|
||||
|
||||
@@ -17,6 +17,8 @@ class FileUploadItem {
|
||||
this.assetQueue,
|
||||
this.status = UploadStatus.notStarted,
|
||||
});
|
||||
|
||||
String get lockKey => assetQueue?.id ?? file.localID!;
|
||||
}
|
||||
|
||||
enum UploadStatus { notStarted, inProgress, inBackground, completed }
|
||||
|
||||
@@ -51,6 +51,7 @@ import "package:photos/utils/file_key.dart";
|
||||
import "package:photos/utils/network_util.dart";
|
||||
import 'package:photos/utils/standalone/data.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import "package:timezone/timezone.dart";
|
||||
import "package:uuid/uuid.dart";
|
||||
|
||||
class FileUploader {
|
||||
@@ -1153,20 +1154,20 @@ class FileUploader {
|
||||
.where((e) => e.value.status == UploadStatus.inBackground)
|
||||
.toList();
|
||||
for (final upload in blockedUploads) {
|
||||
final file = upload.value.file;
|
||||
final String lockKey = upload.value.lockKey;
|
||||
final isStillLocked = await _uploadLocks.isLocked(
|
||||
file.localID!,
|
||||
lockKey,
|
||||
ProcessType.background.toString(),
|
||||
);
|
||||
if (!isStillLocked) {
|
||||
if (!isStillLocked && upload.value.assetQueue != null) {
|
||||
final completer = _queue.remove(upload.key)?.completer;
|
||||
final dbFile =
|
||||
await FilesDB.instance.getFile(upload.value.file.generatedID!);
|
||||
if (dbFile?.uploadedFileID != null) {
|
||||
final exists = await localDB.existsQueueEntry(upload.value.assetQueue!);
|
||||
if (exists) {
|
||||
_logger.info(
|
||||
"Background upload success detected ${upload.value.file.tag}",
|
||||
);
|
||||
completer?.complete(dbFile);
|
||||
// todo:neeraj Instead of cancel, fire event
|
||||
completer?.completeError(SilentlyCancelUploadsError());
|
||||
_allBackups[upload.key] = _allBackups[upload.key]!
|
||||
.copyWith(status: BackupItemStatus.uploaded);
|
||||
} else {
|
||||
@@ -1177,7 +1178,7 @@ class FileUploader {
|
||||
// by the background process. Release any lock taken by the foreground process
|
||||
// and complete the completer with error.
|
||||
await _uploadLocks.releaseLock(
|
||||
file.localID!,
|
||||
lockKey,
|
||||
ProcessType.foreground.toString(),
|
||||
);
|
||||
completer?.completeError(SilentlyCancelUploadsError());
|
||||
|
||||
@@ -191,23 +191,23 @@ class AlbumHomeWidgetService {
|
||||
await remoteCache.geFilesForCollection(collection.id);
|
||||
|
||||
// Then open the specific file
|
||||
final file = await FilesDB.instance.getFile(fileId);
|
||||
// final file = await FilesDB.instance.getFile(fileId);
|
||||
final file = null;
|
||||
if (file == null) {
|
||||
_logger.warning("Cannot launch widget: file with ID $fileId not found");
|
||||
return;
|
||||
}
|
||||
|
||||
routeToPage(
|
||||
context,
|
||||
DetailPage(
|
||||
DetailPageConfiguration(
|
||||
getAllFilesCollection,
|
||||
getAllFilesCollection.indexOf(file),
|
||||
"albumwidget",
|
||||
} else {
|
||||
routeToPage(
|
||||
context,
|
||||
DetailPage(
|
||||
DetailPageConfiguration(
|
||||
getAllFilesCollection,
|
||||
getAllFilesCollection.indexOf(file),
|
||||
"albumwidget",
|
||||
),
|
||||
),
|
||||
),
|
||||
forceCustomPageRoute: true,
|
||||
).ignore();
|
||||
forceCustomPageRoute: true,
|
||||
).ignore();
|
||||
}
|
||||
await _refreshAlbumsWidget();
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,25 @@ class DBFilterOptions {
|
||||
this.ignoreSharedItems = false,
|
||||
});
|
||||
|
||||
// CopyWith method to create a new instance with some options changed
|
||||
DBFilterOptions copyWith({
|
||||
Set<int>? ignoredCollectionIDs,
|
||||
bool? hideIgnoredForUpload,
|
||||
bool? dedupeUploadID,
|
||||
bool? ignoreSavedFiles,
|
||||
bool? onlyUploadedFiles,
|
||||
bool? ignoreSharedItems,
|
||||
}) {
|
||||
return DBFilterOptions(
|
||||
ignoredCollectionIDs: ignoredCollectionIDs ?? this.ignoredCollectionIDs,
|
||||
hideIgnoredForUpload: hideIgnoredForUpload ?? this.hideIgnoredForUpload,
|
||||
dedupeUploadID: dedupeUploadID ?? this.dedupeUploadID,
|
||||
ignoreSavedFiles: ignoreSavedFiles ?? this.ignoreSavedFiles,
|
||||
onlyUploadedFiles: onlyUploadedFiles ?? this.onlyUploadedFiles,
|
||||
ignoreSharedItems: ignoreSharedItems ?? this.ignoreSharedItems,
|
||||
);
|
||||
}
|
||||
|
||||
static DBFilterOptions dedupeOption = DBFilterOptions(
|
||||
dedupeUploadID: true,
|
||||
);
|
||||
|
||||
@@ -162,12 +162,6 @@ class PeopleHomeWidgetService {
|
||||
String personId,
|
||||
BuildContext context,
|
||||
) async {
|
||||
final file = await FilesDB.instance.getFile(fileId);
|
||||
if (file == null) {
|
||||
_logger.warning("Cannot launch widget: file with ID $fileId not found");
|
||||
return;
|
||||
}
|
||||
|
||||
final person = await PersonService.instance.getPerson(personId);
|
||||
if (person == null) {
|
||||
_logger
|
||||
@@ -184,6 +178,12 @@ class PeopleHomeWidgetService {
|
||||
forceCustomPageRoute: true,
|
||||
).ignore();
|
||||
|
||||
// final file = await FilesDB.instance.getFile(fileId);
|
||||
final file = null;
|
||||
if (file == null) {
|
||||
_logger.warning("Cannot launch widget: file with ID $fileId not found");
|
||||
return;
|
||||
}
|
||||
final clusterFiles =
|
||||
await SearchService.instance.getClusterFilesForPersonID(
|
||||
personId,
|
||||
|
||||
@@ -310,11 +310,7 @@ class RemoteSyncService {
|
||||
try {
|
||||
final uploadedFile = await _uploader
|
||||
.upload(file, queueEntry.destCollectionId, queue: queueEntry);
|
||||
await _collectionsService.addOrCopyToCollection(
|
||||
queueEntry.destCollectionId,
|
||||
[uploadedFile],
|
||||
);
|
||||
_onFileUploaded(uploadedFile, queueEntry: queueEntry);
|
||||
_onFileUploaded(uploadedFile);
|
||||
} catch (error, stackTrace) {
|
||||
_onFileUploadError(error, stackTrace, file);
|
||||
}
|
||||
@@ -322,7 +318,7 @@ class RemoteSyncService {
|
||||
|
||||
void _onFileUploaded(
|
||||
EnteFile file, {
|
||||
AssetUploadQueue? queueEntry,
|
||||
|
||||
}) {
|
||||
Bus.instance.fire(
|
||||
CollectionUpdatedEvent(file.collectionID, [file], "fileUpload"),
|
||||
|
||||
@@ -104,7 +104,9 @@ class _HomeGalleryWidgetV2State extends State<HomeGalleryWidgetV2> {
|
||||
final List<EnteFile> allFiles = await merge(
|
||||
localFiles: localFiles,
|
||||
remoteFiles: enteFiles,
|
||||
filterOptions: homeGalleryFilters,
|
||||
filterOptions: homeGalleryFilters.copyWith(
|
||||
ignoreSharedItems: _shouldHideSharedItems,
|
||||
),
|
||||
);
|
||||
_logger.info(
|
||||
"Merged files: ${allFiles.length} (local: ${localFiles.length}, remote: ${enteFiles.length}) files $tl, total ${tl.elapsed}",
|
||||
|
||||
@@ -4,12 +4,13 @@ import 'dart:math';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:photo_manager/photo_manager.dart';
|
||||
import "package:photos/core/configuration.dart";
|
||||
import 'package:photos/core/event_bus.dart';
|
||||
import 'package:photos/db/files_db.dart';
|
||||
import "package:photos/db/local/table/upload_queue_table.dart";
|
||||
import 'package:photos/events/local_photos_updated_event.dart';
|
||||
import 'package:photos/generated/l10n.dart';
|
||||
import 'package:photos/models/file/file.dart';
|
||||
import 'package:photos/models/location/location.dart';
|
||||
import "package:photos/service_locator.dart";
|
||||
import 'package:photos/services/sync/sync_service.dart';
|
||||
import 'package:photos/ui/notification/toast.dart';
|
||||
import 'package:photos/ui/viewer/file/detail_page.dart';
|
||||
@@ -37,32 +38,33 @@ Future<void> saveAsset({
|
||||
originalFile.deviceFolder ?? '',
|
||||
newAsset,
|
||||
);
|
||||
|
||||
newFile.creationTime = originalFile.creationTime;
|
||||
newFile.collectionID = originalFile.collectionID;
|
||||
newFile.location = originalFile.location;
|
||||
if (!newFile.hasLocation && originalFile.localID != null) {
|
||||
final assetEntity = await originalFile.getAsset;
|
||||
if (assetEntity != null) {
|
||||
final latLong = await assetEntity.latlngAsync();
|
||||
newFile.location = Location(
|
||||
latitude: latLong.latitude,
|
||||
longitude: latLong.longitude,
|
||||
);
|
||||
}
|
||||
await localDB.trackEdit(
|
||||
newAsset.id,
|
||||
originalFile.creationTime!,
|
||||
originalFile.modificationTime!,
|
||||
originalFile.location?.latitude,
|
||||
originalFile.location?.longitude,
|
||||
);
|
||||
if (originalFile.collectionID != null) {
|
||||
await localDB.insertOrUpdateQueue(
|
||||
{newAsset.id},
|
||||
originalFile.collectionID!,
|
||||
Configuration.instance.getUserID()!,
|
||||
);
|
||||
}
|
||||
newFile.generatedID = await FilesDB.instance.insertAndGetId(newFile);
|
||||
|
||||
Bus.instance.fire(LocalPhotosUpdatedEvent([newFile], source: "editSave"));
|
||||
unawaited(SyncService.instance.sync());
|
||||
showShortToast(context, S.of(context).editsSaved);
|
||||
logger.info("Original file " + originalFile.toString());
|
||||
logger.info("Saved edits to file " + newFile.toString());
|
||||
final files = detailPageConfig.files;
|
||||
|
||||
// the index could be -1 if the files fetched doesn't contain the newly
|
||||
// edited files
|
||||
int selectionIndex =
|
||||
files.indexWhere((file) => file.generatedID == newFile.generatedID);
|
||||
int selectionIndex = files.indexWhere((file) =>
|
||||
originalFile.localID != null && file.localID == newFile.localID);
|
||||
if (selectionIndex == -1) {
|
||||
files.add(newFile);
|
||||
selectionIndex = files.length - 1;
|
||||
@@ -83,5 +85,9 @@ Future<void> saveAsset({
|
||||
logger.severe(e, s);
|
||||
} finally {
|
||||
await PhotoManager.startChangeNotify();
|
||||
Future.delayed(
|
||||
const Duration(seconds: 2),
|
||||
() => SyncService.instance.sync().ignore(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user