2020-03-24 19:59:36 +00:00
import ' dart:io ' ;
2020-05-27 21:03:30 +00:00
import ' package:logging/logging.dart ' ;
2021-07-12 10:24:30 +00:00
import ' package:path/path.dart ' ;
import ' package:path_provider/path_provider.dart ' ;
2021-06-28 06:40:19 +00:00
import ' package:photos/models/backup_status.dart ' ;
2021-07-12 10:24:30 +00:00
import ' package:photos/models/file.dart ' ;
2021-06-29 05:13:16 +00:00
import ' package:photos/models/file_load_result.dart ' ;
2020-06-19 23:03:26 +00:00
import ' package:photos/models/file_type.dart ' ;
2020-06-03 16:06:49 +00:00
import ' package:photos/models/location.dart ' ;
2021-10-20 10:58:11 +00:00
import ' package:photos/models/magic_metadata.dart ' ;
2022-08-27 09:54:06 +00:00
import ' package:photos/services/feature_flag_service.dart ' ;
2022-08-29 07:58:49 +00:00
import ' package:photos/utils/file_uploader_util.dart ' ;
2020-03-24 19:59:36 +00:00
import ' package:sqflite/sqflite.dart ' ;
2021-01-23 12:43:30 +00:00
import ' package:sqflite_migration/sqflite_migration.dart ' ;
2020-03-24 19:59:36 +00:00
2020-07-20 11:03:09 +00:00
class FilesDB {
2021-05-03 22:48:02 +00:00
/ *
Note: columnUploadedFileID and columnCollectionID have to be compared against
both NULL and - 1 because older clients might have entries where the DEFAULT
was unset , and a migration script to set the DEFAULT would break in case of
duplicate entries for un - uploaded files that were created due to a collision
in background and foreground syncs .
* /
2022-07-04 06:02:17 +00:00
static const _databaseName = " ente.files.db " ;
2020-03-24 19:59:36 +00:00
2020-07-20 11:03:09 +00:00
static final Logger _logger = Logger ( " FilesDB " ) ;
2020-05-27 21:03:30 +00:00
2022-07-04 06:02:17 +00:00
static const table = ' files ' ;
static const tempTable = ' temp_files ' ;
2020-03-26 14:39:31 +00:00
2022-07-04 06:02:17 +00:00
static const columnGeneratedID = ' _id ' ;
static const columnUploadedFileID = ' uploaded_file_id ' ;
static const columnOwnerID = ' owner_id ' ;
static const columnCollectionID = ' collection_id ' ;
static const columnLocalID = ' local_id ' ;
static const columnTitle = ' title ' ;
static const columnDeviceFolder = ' device_folder ' ;
static const columnLatitude = ' latitude ' ;
static const columnLongitude = ' longitude ' ;
static const columnFileType = ' file_type ' ;
static const columnFileSubType = ' file_sub_type ' ;
static const columnDuration = ' duration ' ;
static const columnExif = ' exif ' ;
static const columnHash = ' hash ' ;
static const columnMetadataVersion = ' metadata_version ' ;
static const columnIsDeleted = ' is_deleted ' ;
static const columnCreationTime = ' creation_time ' ;
static const columnModificationTime = ' modification_time ' ;
static const columnUpdationTime = ' updation_time ' ;
static const columnEncryptedKey = ' encrypted_key ' ;
static const columnKeyDecryptionNonce = ' key_decryption_nonce ' ;
static const columnFileDecryptionHeader = ' file_decryption_header ' ;
static const columnThumbnailDecryptionHeader = ' thumbnail_decryption_header ' ;
static const columnMetadataDecryptionHeader = ' metadata_decryption_header ' ;
2020-03-24 19:59:36 +00:00
2021-09-16 13:14:51 +00:00
// MMD -> Magic Metadata
2022-07-04 06:02:17 +00:00
static const columnMMdEncodedJson = ' mmd_encoded_json ' ;
static const columnMMdVersion = ' mmd_ver ' ;
2021-09-16 13:14:51 +00:00
2022-07-04 06:02:17 +00:00
static const columnPubMMdEncodedJson = ' pub_mmd_encoded_json ' ;
static const columnPubMMdVersion = ' pub_mmd_ver ' ;
2021-10-26 05:25:42 +00:00
2021-09-16 13:14:51 +00:00
// part of magic metadata
// Only parse & store selected fields from JSON in separate columns if
// we need to write query based on that field
2022-07-04 06:02:17 +00:00
static const columnMMdVisibility = ' mmd_visibility ' ;
2021-09-16 13:14:51 +00:00
2021-07-21 20:21:31 +00:00
static final initializationScript = [ . . . createTable ( table ) ] ;
2021-07-21 19:55:24 +00:00
static final migrationScripts = [
. . . alterDeviceFolderToAllowNULL ( ) ,
. . . alterTimestampColumnTypes ( ) ,
2021-07-21 20:21:31 +00:00
. . . addIndices ( ) ,
2021-08-18 13:23:42 +00:00
. . . addMetadataColumns ( ) ,
2021-09-16 13:14:51 +00:00
. . . addMagicMetadataColumns ( ) ,
2021-09-24 11:19:03 +00:00
. . . addUniqueConstraintOnCollectionFiles ( ) ,
2021-10-26 05:25:42 +00:00
. . . addPubMagicMetadataColumns ( )
2021-07-21 19:55:24 +00:00
] ;
2021-01-23 12:43:30 +00:00
final dbConfig = MigrationConfig (
2022-06-11 08:23:52 +00:00
initializationScript: initializationScript ,
migrationScripts: migrationScripts ,
) ;
2020-03-24 19:59:36 +00:00
// make this a singleton class
2020-07-20 11:03:09 +00:00
FilesDB . _privateConstructor ( ) ;
2021-10-26 14:46:58 +00:00
2020-07-20 11:03:09 +00:00
static final FilesDB instance = FilesDB . _privateConstructor ( ) ;
2020-03-24 19:59:36 +00:00
// only have a single app-wide reference to the database
2021-07-21 19:55:24 +00:00
static Future < Database > _dbFuture ;
2020-03-24 19:59:36 +00:00
Future < Database > get database async {
// lazily instantiate the db the first time it is accessed
2021-07-21 19:55:24 +00:00
_dbFuture ? ? = _initDatabase ( ) ;
return _dbFuture ;
2020-03-24 19:59:36 +00:00
}
2020-03-26 14:39:31 +00:00
2020-03-24 19:59:36 +00:00
// this opens the database (and creates it if it doesn't exist)
2021-07-21 19:55:24 +00:00
Future < Database > _initDatabase ( ) async {
2022-08-30 06:43:51 +00:00
final Directory documentsDirectory =
await getApplicationDocumentsDirectory ( ) ;
2022-08-29 14:43:31 +00:00
final String path = join ( documentsDirectory . path , _databaseName ) ;
2021-07-21 19:55:24 +00:00
_logger . info ( " DB path " + path ) ;
2021-01-23 12:43:30 +00:00
return await openDatabaseWithMigration ( path , dbConfig ) ;
2020-03-24 19:59:36 +00:00
}
// SQL code to create the database table
2021-01-25 06:15:37 +00:00
static List < String > createTable ( String tableName ) {
2021-01-23 13:49:37 +00:00
return [
'''
2021-01-25 06:15:37 +00:00
CREATE TABLE $tableName (
2021-01-24 18:59:17 +00:00
$columnGeneratedID INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL ,
$columnLocalID TEXT ,
2021-05-03 22:48:02 +00:00
$columnUploadedFileID INTEGER DEFAULT - 1 ,
2021-01-24 18:59:17 +00:00
$columnOwnerID INTEGER ,
2021-05-03 22:48:02 +00:00
$columnCollectionID INTEGER DEFAULT - 1 ,
2021-01-24 18:59:17 +00:00
$columnTitle TEXT NOT NULL ,
$columnDeviceFolder TEXT ,
$columnLatitude REAL ,
$columnLongitude REAL ,
$columnFileType INTEGER ,
$columnModificationTime TEXT NOT NULL ,
$columnEncryptedKey TEXT ,
$columnKeyDecryptionNonce TEXT ,
$columnFileDecryptionHeader TEXT ,
$columnThumbnailDecryptionHeader TEXT ,
$columnMetadataDecryptionHeader TEXT ,
$columnIsDeleted INTEGER DEFAULT 0 ,
$columnCreationTime TEXT NOT NULL ,
$columnUpdationTime TEXT ,
2021-03-26 06:14:18 +00:00
UNIQUE ( $columnLocalID , $columnUploadedFileID , $columnCollectionID )
2021-01-24 18:59:17 +00:00
) ;
''' ,
2021-01-24 11:51:53 +00:00
] ;
}
2021-07-21 20:21:31 +00:00
static List < String > addIndices ( ) {
2021-01-24 11:51:53 +00:00
return [
2021-01-23 13:49:37 +00:00
'''
2021-07-21 20:21:31 +00:00
CREATE INDEX IF NOT EXISTS collection_id_index ON $table ( $columnCollectionID ) ;
2021-07-21 19:55:24 +00:00
''' ,
'''
2021-07-21 20:21:31 +00:00
CREATE INDEX IF NOT EXISTS device_folder_index ON $table ( $columnDeviceFolder ) ;
2021-07-21 19:55:24 +00:00
''' ,
'''
2021-07-21 20:21:31 +00:00
CREATE INDEX IF NOT EXISTS creation_time_index ON $table ( $columnCreationTime ) ;
2021-07-21 19:55:24 +00:00
''' ,
'''
2021-07-21 20:21:31 +00:00
CREATE INDEX IF NOT EXISTS updation_time_index ON $table ( $columnUpdationTime ) ;
2021-01-23 13:49:37 +00:00
'''
] ;
2021-01-23 12:43:30 +00:00
}
2021-01-23 13:49:37 +00:00
static List < String > alterDeviceFolderToAllowNULL ( ) {
return [
2021-01-25 06:15:37 +00:00
. . . createTable ( tempTable ) ,
2021-01-23 13:49:37 +00:00
'''
2021-01-25 05:20:57 +00:00
INSERT INTO $tempTable
2021-01-24 07:23:42 +00:00
SELECT *
FROM $table ;
2021-01-24 18:59:17 +00:00
2021-01-24 07:23:42 +00:00
DROP TABLE $table ;
2021-01-24 18:59:17 +00:00
2021-01-25 05:20:57 +00:00
ALTER TABLE $tempTable
2021-01-24 07:23:42 +00:00
RENAME TO $table ;
2021-01-23 13:49:37 +00:00
'''
] ;
2020-03-24 19:59:36 +00:00
}
2020-03-26 14:39:31 +00:00
2021-07-21 19:55:24 +00:00
static List < String > alterTimestampColumnTypes ( ) {
return [
'''
DROP TABLE IF EXISTS $tempTable ;
''' ,
'''
CREATE TABLE $tempTable (
$columnGeneratedID INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL ,
$columnLocalID TEXT ,
$columnUploadedFileID INTEGER DEFAULT - 1 ,
$columnOwnerID INTEGER ,
$columnCollectionID INTEGER DEFAULT - 1 ,
$columnTitle TEXT NOT NULL ,
$columnDeviceFolder TEXT ,
$columnLatitude REAL ,
$columnLongitude REAL ,
$columnFileType INTEGER ,
$columnModificationTime INTEGER NOT NULL ,
$columnEncryptedKey TEXT ,
$columnKeyDecryptionNonce TEXT ,
$columnFileDecryptionHeader TEXT ,
$columnThumbnailDecryptionHeader TEXT ,
$columnMetadataDecryptionHeader TEXT ,
$columnCreationTime INTEGER NOT NULL ,
$columnUpdationTime INTEGER ,
UNIQUE ( $columnLocalID , $columnUploadedFileID , $columnCollectionID )
) ;
''' ,
'''
INSERT INTO $tempTable
SELECT
$columnGeneratedID ,
$columnLocalID ,
$columnUploadedFileID ,
$columnOwnerID ,
$columnCollectionID ,
$columnTitle ,
$columnDeviceFolder ,
$columnLatitude ,
$columnLongitude ,
$columnFileType ,
CAST ( $columnModificationTime AS INTEGER ) ,
$columnEncryptedKey ,
$columnKeyDecryptionNonce ,
$columnFileDecryptionHeader ,
$columnThumbnailDecryptionHeader ,
$columnMetadataDecryptionHeader ,
CAST ( $columnCreationTime AS INTEGER ) ,
CAST ( $columnUpdationTime AS INTEGER )
FROM $table ;
''' ,
'''
DROP TABLE $table ;
''' ,
'''
ALTER TABLE $tempTable
RENAME TO $table ;
''' ,
] ;
}
2021-08-18 13:23:42 +00:00
static List < String > addMetadataColumns ( ) {
return [
'''
ALTER TABLE $table ADD COLUMN $columnFileSubType INTEGER ;
''' ,
'''
ALTER TABLE $table ADD COLUMN $columnDuration INTEGER ;
''' ,
'''
ALTER TABLE $table ADD COLUMN $columnExif TEXT ;
''' ,
'''
ALTER TABLE $table ADD COLUMN $columnHash TEXT ;
''' ,
'''
ALTER TABLE $table ADD COLUMN $columnMetadataVersion INTEGER ;
''' ,
] ;
}
2021-09-16 13:14:51 +00:00
static List < String > addMagicMetadataColumns ( ) {
return [
'''
2021-09-20 06:41:38 +00:00
ALTER TABLE $table ADD COLUMN $columnMMdEncodedJson TEXT DEFAULT ' {} ' ;
2021-09-16 13:14:51 +00:00
''' ,
'''
ALTER TABLE $table ADD COLUMN $columnMMdVersion INTEGER DEFAULT 0 ;
''' ,
'''
2021-09-17 10:52:19 +00:00
ALTER TABLE $table ADD COLUMN $columnMMdVisibility INTEGER DEFAULT $kVisibilityVisible ;
2021-09-16 13:14:51 +00:00
'''
] ;
}
2021-09-24 11:19:03 +00:00
static List < String > addUniqueConstraintOnCollectionFiles ( ) {
return [
'''
2021-10-01 18:47:15 +00:00
DELETE from $table where $columnCollectionID | | ' - ' | | $columnUploadedFileID IN
( SELECT $columnCollectionID | | ' - ' | | $columnUploadedFileID from $table WHERE
$columnCollectionID is not NULL AND $columnUploadedFileID is NOT NULL
AND $columnCollectionID ! = - 1 AND $columnUploadedFileID ! = - 1
GROUP BY ( $columnCollectionID | | ' - ' | | $columnUploadedFileID ) HAVING count ( * ) > 1 )
AND ( $columnCollectionID | | ' - ' | | $columnUploadedFileID | | ' - ' | | $columnGeneratedID ) NOT IN
( SELECT $columnCollectionID | | ' - ' | | $columnUploadedFileID | | ' - ' | | max ( $columnGeneratedID )
from $table WHERE
$columnCollectionID is not NULL AND $columnUploadedFileID is NOT NULL
AND $columnCollectionID ! = - 1 AND $columnUploadedFileID ! = - 1 GROUP BY
( $columnCollectionID | | ' - ' | | $columnUploadedFileID ) HAVING count ( * ) > 1 ) ;
2021-09-24 11:19:03 +00:00
''' ,
'''
2021-09-27 04:31:27 +00:00
CREATE UNIQUE INDEX IF NOT EXISTS cid_uid ON $table ( $columnCollectionID , $columnUploadedFileID )
2021-09-24 11:43:35 +00:00
WHERE $columnCollectionID is not NULL AND $columnUploadedFileID is not NULL
AND $columnCollectionID ! = - 1 AND $columnUploadedFileID ! = - 1 ;
2021-09-24 11:19:03 +00:00
'''
] ;
}
2021-10-26 05:25:42 +00:00
static List < String > addPubMagicMetadataColumns ( ) {
return [
'''
ALTER TABLE $table ADD COLUMN $columnPubMMdEncodedJson TEXT DEFAULT ' {} ' ;
''' ,
'''
ALTER TABLE $table ADD COLUMN $columnPubMMdVersion INTEGER DEFAULT 0 ;
'''
] ;
}
2021-03-17 21:07:17 +00:00
Future < void > clearTable ( ) async {
final db = await instance . database ;
await db . delete ( table ) ;
}
2020-11-20 11:05:17 +00:00
Future < void > insertMultiple ( List < File > files ) async {
2021-04-19 12:46:42 +00:00
final startTime = DateTime . now ( ) ;
2020-05-06 18:13:24 +00:00
final db = await instance . database ;
2020-03-30 14:28:46 +00:00
var batch = db . batch ( ) ;
int batchCounter = 0 ;
2020-06-19 23:03:26 +00:00
for ( File file in files ) {
2020-03-30 14:28:46 +00:00
if ( batchCounter = = 400 ) {
2021-05-06 20:31:21 +00:00
await batch . commit ( noResult: true ) ;
2020-03-30 14:28:46 +00:00
batch = db . batch ( ) ;
2021-04-19 14:14:34 +00:00
batchCounter = 0 ;
2020-03-30 14:28:46 +00:00
}
2020-10-24 10:32:45 +00:00
batch . insert (
table ,
_getRowForFile ( file ) ,
conflictAlgorithm: ConflictAlgorithm . replace ,
) ;
2020-03-30 14:28:46 +00:00
batchCounter + + ;
}
2020-11-20 11:05:17 +00:00
await batch . commit ( noResult: true ) ;
2021-04-19 12:46:42 +00:00
final endTime = DateTime . now ( ) ;
final duration = Duration (
2022-06-11 08:23:52 +00:00
microseconds:
endTime . microsecondsSinceEpoch - startTime . microsecondsSinceEpoch ,
) ;
_logger . info (
" Batch insert of " +
files . length . toString ( ) +
" took " +
duration . inMilliseconds . toString ( ) +
" ms. " ,
) ;
2020-03-26 14:39:31 +00:00
}
2021-06-09 15:32:30 +00:00
Future < int > insert ( File file ) async {
final db = await instance . database ;
return db . insert (
table ,
_getRowForFile ( file ) ,
conflictAlgorithm: ConflictAlgorithm . replace ,
) ;
}
2020-10-24 20:05:50 +00:00
Future < File > getFile ( int generatedID ) async {
final db = await instance . database ;
2022-06-11 08:23:52 +00:00
final results = await db . query (
table ,
where: ' $ columnGeneratedID = ? ' ,
whereArgs: [ generatedID ] ,
) ;
2020-11-01 07:37:23 +00:00
if ( results . isEmpty ) {
return null ;
}
2020-10-24 21:25:28 +00:00
return _convertToFiles ( results ) [ 0 ] ;
2020-10-24 20:05:50 +00:00
}
2020-11-30 06:28:37 +00:00
Future < File > getUploadedFile ( int uploadedID , int collectionID ) async {
final db = await instance . database ;
final results = await db . query (
table ,
where: ' $ columnUploadedFileID = ? AND $ columnCollectionID = ? ' ,
whereArgs: [
uploadedID ,
collectionID ,
] ,
) ;
if ( results . isEmpty ) {
return null ;
}
return _convertToFiles ( results ) [ 0 ] ;
}
2021-04-19 11:27:52 +00:00
Future < Set < int > > getUploadedFileIDs ( int collectionID ) async {
final db = await instance . database ;
final results = await db . query (
table ,
columns: [ columnUploadedFileID ] ,
where: ' $ columnCollectionID = ? ' ,
whereArgs: [
collectionID ,
] ,
) ;
2021-07-21 19:55:24 +00:00
final ids = < int > { } ;
2021-04-19 11:27:52 +00:00
for ( final result in results ) {
ids . add ( result [ columnUploadedFileID ] ) ;
}
return ids ;
}
2021-06-28 06:40:19 +00:00
Future < BackedUpFileIDs > getBackedUpIDs ( ) async {
2021-06-28 05:30:58 +00:00
final db = await instance . database ;
final results = await db . query (
table ,
2021-06-28 06:40:19 +00:00
columns: [ columnLocalID , columnUploadedFileID ] ,
2021-06-28 05:30:58 +00:00
where:
' $ columnLocalID IS NOT NULL AND ( $ columnUploadedFileID IS NOT NULL AND $ columnUploadedFileID IS NOT -1) ' ,
) ;
2021-07-21 19:55:24 +00:00
final localIDs = < String > { } ;
final uploadedIDs = < int > { } ;
2021-06-28 05:30:58 +00:00
for ( final result in results ) {
2021-06-28 06:40:19 +00:00
localIDs . add ( result [ columnLocalID ] ) ;
uploadedIDs . add ( result [ columnUploadedFileID ] ) ;
2021-06-28 05:30:58 +00:00
}
2021-06-28 06:40:19 +00:00
return BackedUpFileIDs ( localIDs . toList ( ) , uploadedIDs . toList ( ) ) ;
2021-06-28 05:30:58 +00:00
}
2021-09-30 09:28:29 +00:00
Future < FileLoadResult > getAllUploadedFiles (
2022-06-11 08:23:52 +00:00
int startTime ,
int endTime ,
int ownerID , {
int limit ,
bool asc ,
int visibility = kVisibilityVisible ,
Set < int > ignoredCollectionIDs ,
} ) async {
2020-05-06 18:13:24 +00:00
final db = await instance . database ;
2021-06-09 13:45:42 +00:00
final order = ( asc ? ? false ? ' ASC ' : ' DESC ' ) ;
2020-05-06 18:13:24 +00:00
final results = await db . query (
table ,
2021-04-20 20:11:39 +00:00
where:
2021-09-16 17:33:16 +00:00
' $ columnCreationTime >= ? AND $ columnCreationTime <= ? AND $ columnOwnerID = ? AND ( $ columnCollectionID IS NOT NULL AND $ columnCollectionID IS NOT -1) '
2021-09-30 09:28:29 +00:00
' AND $ columnMMdVisibility = ? ' ,
2021-09-17 10:52:19 +00:00
whereArgs: [ startTime , endTime , ownerID , visibility ] ,
2021-06-09 13:45:42 +00:00
orderBy:
' $ columnCreationTime ' + order + ' , $ columnModificationTime ' + order ,
2021-04-20 20:11:39 +00:00
limit: limit ,
2020-05-06 18:13:24 +00:00
) ;
2021-06-29 05:13:16 +00:00
final files = _convertToFiles ( results ) ;
2022-08-29 14:43:31 +00:00
final List < File > deduplicatedFiles =
2022-03-21 09:32:24 +00:00
_deduplicatedAndFilterIgnoredFiles ( files , ignoredCollectionIDs ) ;
2021-09-17 10:52:19 +00:00
return FileLoadResult ( deduplicatedFiles , files . length = = limit ) ;
2020-04-12 12:38:49 +00:00
}
2022-08-26 14:44:11 +00:00
Future < Set < int > > getCollectionIDsOfHiddenFiles (
int ownerID , {
int visibility = kVisibilityArchive ,
} ) async {
final db = await instance . database ;
final results = await db . query (
table ,
2022-08-29 14:44:07 +00:00
where:
' $ columnOwnerID = ? AND $ columnMMdVisibility = ? AND $ columnCollectionID != -1 ' ,
2022-08-26 14:44:11 +00:00
columns: [ columnCollectionID ] ,
whereArgs: [ ownerID , visibility ] ,
2022-08-30 06:43:51 +00:00
distinct: true ,
2022-08-26 14:44:11 +00:00
) ;
2022-08-30 08:16:40 +00:00
Set < int > collectionIDsOfHiddenFiles = { } ;
2022-08-26 14:44:11 +00:00
for ( var result in results ) {
collectionIDsOfHiddenFiles . add ( result [ ' collection_id ' ] ) ;
}
2022-08-30 08:16:40 +00:00
return collectionIDsOfHiddenFiles ;
2022-08-26 14:44:11 +00:00
}
2021-09-30 09:28:29 +00:00
Future < FileLoadResult > getAllLocalAndUploadedFiles (
2022-06-11 08:23:52 +00:00
int startTime ,
int endTime ,
int ownerID , {
int limit ,
bool asc ,
Set < int > ignoredCollectionIDs ,
} ) async {
2021-07-01 11:26:47 +00:00
final db = await instance . database ;
final order = ( asc ? ? false ? ' ASC ' : ' DESC ' ) ;
final results = await db . query (
table ,
where:
2021-09-17 10:04:24 +00:00
' $ columnCreationTime >= ? AND $ columnCreationTime <= ? AND ( $ columnOwnerID IS NULL OR $ columnOwnerID = ?) AND ( $ columnMMdVisibility IS NULL OR $ columnMMdVisibility = ?) '
2021-09-30 09:28:29 +00:00
' AND ( $ columnLocalID IS NOT NULL OR ( $ columnCollectionID IS NOT NULL AND $ columnCollectionID IS NOT -1)) ' ,
2021-09-17 10:04:24 +00:00
whereArgs: [ startTime , endTime , ownerID , kVisibilityVisible ] ,
2021-07-01 11:26:47 +00:00
orderBy:
' $ columnCreationTime ' + order + ' , $ columnModificationTime ' + order ,
limit: limit ,
) ;
final files = _convertToFiles ( results ) ;
2022-08-29 14:43:31 +00:00
final List < File > deduplicatedFiles =
2022-03-21 09:32:24 +00:00
_deduplicatedAndFilterIgnoredFiles ( files , ignoredCollectionIDs ) ;
2021-10-30 07:05:50 +00:00
return FileLoadResult ( deduplicatedFiles , files . length = = limit ) ;
2021-07-01 11:26:47 +00:00
}
2021-07-01 10:51:13 +00:00
Future < FileLoadResult > getImportantFiles (
2022-06-11 08:23:52 +00:00
int startTime ,
int endTime ,
int ownerID ,
List < String > paths , {
int limit ,
bool asc ,
Set < int > ignoredCollectionIDs ,
} ) async {
2021-04-25 12:40:10 +00:00
final db = await instance . database ;
2021-05-13 11:13:32 +00:00
String inParam = " " ;
for ( final path in paths ) {
2021-05-25 12:19:20 +00:00
inParam + = " ' " + path . replaceAll ( " ' " , " '' " ) + " ', " ;
2021-05-13 11:13:32 +00:00
}
inParam = inParam . substring ( 0 , inParam . length - 1 ) ;
2021-06-09 13:45:42 +00:00
final order = ( asc ? ? false ? ' ASC ' : ' DESC ' ) ;
2021-04-25 12:40:10 +00:00
final results = await db . query (
table ,
where:
2021-09-17 10:04:24 +00:00
' $ columnCreationTime >= ? AND $ columnCreationTime <= ? AND ( $ columnOwnerID IS NULL OR $ columnOwnerID = ?) AND ( $ columnMMdVisibility IS NULL OR $ columnMMdVisibility = ?) '
2021-09-30 09:28:29 +00:00
' AND (( $ columnLocalID IS NOT NULL AND $ columnDeviceFolder IN ( $ inParam )) OR ( $ columnCollectionID IS NOT NULL AND $ columnCollectionID IS NOT -1)) ' ,
2021-09-17 10:04:24 +00:00
whereArgs: [ startTime , endTime , ownerID , kVisibilityVisible ] ,
2021-06-09 13:45:42 +00:00
orderBy:
' $ columnCreationTime ' + order + ' , $ columnModificationTime ' + order ,
2021-04-25 12:40:10 +00:00
limit: limit ,
) ;
2021-05-13 19:57:27 +00:00
final files = _convertToFiles ( results ) ;
2022-08-29 14:43:31 +00:00
final List < File > deduplicatedFiles =
2022-03-21 09:32:24 +00:00
_deduplicatedAndFilterIgnoredFiles ( files , ignoredCollectionIDs ) ;
2021-09-17 05:26:26 +00:00
return FileLoadResult ( deduplicatedFiles , files . length = = limit ) ;
}
2022-07-11 04:04:12 +00:00
List < File > _deduplicateByLocalID ( List < File > files ) {
final localIDs = < String > { } ;
final List < File > deduplicatedFiles = [ ] ;
for ( final file in files ) {
final id = file . localID ;
if ( id ! = null & & localIDs . contains ( id ) ) {
continue ;
}
localIDs . add ( id ) ;
deduplicatedFiles . add ( file ) ;
}
return deduplicatedFiles ;
}
2022-03-21 09:32:24 +00:00
List < File > _deduplicatedAndFilterIgnoredFiles (
2022-06-11 08:23:52 +00:00
List < File > files ,
Set < int > ignoredCollectionIDs ,
) {
2021-09-17 05:26:26 +00:00
final uploadedFileIDs = < int > { } ;
2021-05-13 19:57:27 +00:00
final List < File > deduplicatedFiles = [ ] ;
for ( final file in files ) {
final id = file . uploadedFileID ;
2022-03-21 09:32:24 +00:00
if ( ignoredCollectionIDs ! = null & &
ignoredCollectionIDs . contains ( file . collectionID ) ) {
continue ;
}
2021-05-13 19:57:27 +00:00
if ( id ! = null & & id ! = - 1 & & uploadedFileIDs . contains ( id ) ) {
continue ;
}
uploadedFileIDs . add ( id ) ;
deduplicatedFiles . add ( file ) ;
}
2021-09-17 05:26:26 +00:00
return deduplicatedFiles ;
2021-04-25 12:40:10 +00:00
}
2021-06-29 05:13:16 +00:00
Future < FileLoadResult > getFilesInCollection (
2022-06-11 08:23:52 +00:00
int collectionID ,
int startTime ,
int endTime , {
int limit ,
bool asc ,
2022-08-26 12:14:40 +00:00
int visibility = kVisibilityVisible ,
2022-06-11 08:23:52 +00:00
} ) async {
2021-04-20 20:11:39 +00:00
final db = await instance . database ;
2021-06-09 13:45:42 +00:00
final order = ( asc ? ? false ? ' ASC ' : ' DESC ' ) ;
2022-08-27 09:54:06 +00:00
String whereClause ;
List < Object > whereArgs ;
if ( FeatureFlagService . instance . isInternalUserOrDebugBuild ( ) ) {
whereClause =
' $ columnCollectionID = ? AND $ columnCreationTime >= ? AND $ columnCreationTime <= ? AND $ columnMMdVisibility = ? ' ;
whereArgs = [ collectionID , startTime , endTime , visibility ] ;
} else {
whereClause =
' $ columnCollectionID = ? AND $ columnCreationTime >= ? AND $ columnCreationTime <= ? ' ;
whereArgs = [ collectionID , startTime , endTime ] ;
}
2021-04-20 20:11:39 +00:00
final results = await db . query (
table ,
2022-08-27 09:54:06 +00:00
where: whereClause ,
whereArgs: whereArgs ,
2021-06-09 13:45:42 +00:00
orderBy:
' $ columnCreationTime ' + order + ' , $ columnModificationTime ' + order ,
2021-04-20 20:11:39 +00:00
limit: limit ,
) ;
final files = _convertToFiles ( results ) ;
_logger . info ( " Fetched " + files . length . toString ( ) + " files " ) ;
2021-06-29 05:13:16 +00:00
return FileLoadResult ( files , files . length = = limit ) ;
2021-04-20 20:11:39 +00:00
}
2022-06-11 08:23:52 +00:00
Future < FileLoadResult > getFilesInPath (
String path ,
int startTime ,
int endTime , {
int limit ,
bool asc ,
} ) async {
2021-04-20 20:11:39 +00:00
final db = await instance . database ;
2021-06-09 13:45:42 +00:00
final order = ( asc ? ? false ? ' ASC ' : ' DESC ' ) ;
2021-04-20 20:11:39 +00:00
final results = await db . query (
table ,
where:
2021-07-21 19:55:24 +00:00
' $ columnDeviceFolder = ? AND $ columnCreationTime >= ? AND $ columnCreationTime <= ? AND $ columnLocalID IS NOT NULL ' ,
2021-04-20 20:11:39 +00:00
whereArgs: [ path , startTime , endTime ] ,
2021-06-09 13:45:42 +00:00
orderBy:
' $ columnCreationTime ' + order + ' , $ columnModificationTime ' + order ,
2021-07-21 19:55:24 +00:00
groupBy: columnLocalID ,
2021-04-20 20:11:39 +00:00
limit: limit ,
) ;
2021-06-29 05:13:16 +00:00
final files = _convertToFiles ( results ) ;
return FileLoadResult ( files , files . length = = limit ) ;
2021-04-20 20:11:39 +00:00
}
2022-07-09 17:23:11 +00:00
Future < FileLoadResult > getLocalDeviceFiles (
int startTime ,
int endTime , {
int limit ,
bool asc ,
} ) async {
final db = await instance . database ;
final order = ( asc ? ? false ? ' ASC ' : ' DESC ' ) ;
final results = await db . query (
table ,
where:
' $ columnCreationTime >= ? AND $ columnCreationTime <= ? AND $ columnLocalID IS NOT NULL ' ,
whereArgs: [ startTime , endTime ] ,
orderBy:
' $ columnCreationTime ' + order + ' , $ columnModificationTime ' + order ,
limit: limit ,
) ;
final files = _convertToFiles ( results ) ;
2022-07-11 04:04:12 +00:00
final result = _deduplicateByLocalID ( files ) ;
return FileLoadResult ( result , files . length = = limit ) ;
2022-07-09 17:23:11 +00:00
}
2020-06-21 00:05:15 +00:00
Future < List < File > > getAllVideos ( ) async {
final db = await instance . database ;
final results = await db . query (
table ,
2021-07-21 19:55:24 +00:00
where: ' $ columnLocalID IS NOT NULL AND $ columnFileType = 1 ' ,
2020-07-06 19:09:47 +00:00
orderBy: ' $ columnCreationTime DESC ' ,
2020-06-21 00:05:15 +00:00
) ;
return _convertToFiles ( results ) ;
}
2020-11-14 10:57:08 +00:00
Future < List < File > > getAllInPath ( String path ) async {
final db = await instance . database ;
final results = await db . query (
table ,
2021-07-21 19:55:24 +00:00
where: ' $ columnLocalID IS NOT NULL AND $ columnDeviceFolder = ? ' ,
2020-11-14 10:57:08 +00:00
whereArgs: [ path ] ,
orderBy: ' $ columnCreationTime DESC ' ,
2021-07-21 19:55:24 +00:00
groupBy: columnLocalID ,
2020-11-14 10:57:08 +00:00
) ;
return _convertToFiles ( results ) ;
}
2021-04-27 16:02:33 +00:00
Future < List < File > > getFilesCreatedWithinDurations (
2022-06-11 08:23:52 +00:00
List < List < int > > durations ,
2022-08-13 13:10:01 +00:00
Set < int > ignoredCollectionIDs , {
String order = ' ASC ' ,
} ) async {
2020-07-20 11:53:42 +00:00
final db = await instance . database ;
2022-06-21 13:13:16 +00:00
String whereClause = " ( " ;
2021-04-27 16:02:33 +00:00
for ( int index = 0 ; index < durations . length ; index + + ) {
whereClause + = " ( $ columnCreationTime > " +
durations [ index ] [ 0 ] . toString ( ) +
" AND $ columnCreationTime < " +
durations [ index ] [ 1 ] . toString ( ) +
" ) " ;
if ( index ! = durations . length - 1 ) {
whereClause + = " OR " ;
}
}
2022-06-21 13:13:16 +00:00
whereClause + = " ) AND $ columnMMdVisibility = $ kVisibilityVisible " ;
2020-07-20 11:53:42 +00:00
final results = await db . query (
table ,
2021-07-21 19:55:24 +00:00
where: whereClause ,
2022-08-13 13:10:01 +00:00
orderBy: ' $ columnCreationTime ' + order ,
2020-07-20 11:53:42 +00:00
) ;
2022-06-21 12:55:39 +00:00
final files = _convertToFiles ( results ) ;
2022-06-21 13:13:16 +00:00
return _deduplicatedAndFilterIgnoredFiles ( files , ignoredCollectionIDs ) ;
2020-07-20 11:53:42 +00:00
}
2020-09-17 18:48:25 +00:00
Future < List < File > > getFilesToBeUploadedWithinFolders (
2022-06-11 08:23:52 +00:00
Set < String > folders ,
) async {
2021-03-22 06:29:21 +00:00
if ( folders . isEmpty ) {
return [ ] ;
}
2020-05-06 18:13:24 +00:00
final db = await instance . database ;
2020-09-17 18:48:25 +00:00
String inParam = " " ;
for ( final folder in folders ) {
2021-06-04 19:05:13 +00:00
inParam + = " ' " + folder . replaceAll ( " ' " , " '' " ) + " ', " ;
2020-09-17 18:48:25 +00:00
}
inParam = inParam . substring ( 0 , inParam . length - 1 ) ;
2020-05-06 18:13:24 +00:00
final results = await db . query (
table ,
2020-09-17 18:48:25 +00:00
where:
2021-05-03 22:48:02 +00:00
' ( $ columnUploadedFileID IS NULL OR $ columnUploadedFileID IS -1) AND $ columnDeviceFolder IN ( $ inParam ) ' ,
2020-07-06 19:09:47 +00:00
orderBy: ' $ columnCreationTime DESC ' ,
2021-07-21 19:55:24 +00:00
groupBy: columnLocalID ,
2020-05-06 18:13:24 +00:00
) ;
2020-06-19 23:03:26 +00:00
return _convertToFiles ( results ) ;
2020-03-24 19:59:36 +00:00
}
2022-02-13 09:12:05 +00:00
// Files which user added to a collection manually but they are not uploaded yet.
2022-02-13 09:23:10 +00:00
Future < List < File > > getPendingManualUploads ( ) async {
2022-02-13 09:12:05 +00:00
final db = await instance . database ;
final results = await db . query (
table ,
where:
' ( $ columnUploadedFileID IS NULL OR $ columnUploadedFileID IS -1) AND '
2022-02-13 09:23:10 +00:00
' $ columnCollectionID IS NOT NULL AND $ columnCollectionID IS NOT -1 AND '
2022-02-13 09:12:05 +00:00
' $ columnLocalID IS NOT NULL AND $ columnLocalID IS NOT -1 ' ,
orderBy: ' $ columnCreationTime DESC ' ,
groupBy: columnLocalID ,
) ;
2022-08-29 14:43:31 +00:00
final files = _convertToFiles ( results ) ;
2022-02-13 11:05:11 +00:00
// future-safe filter just to ensure that the query doesn't end up returning files
// which should not be backed up
2022-06-11 08:23:52 +00:00
files . removeWhere (
( e ) = >
e . collectionID = = null | |
e . localID = = null | |
e . uploadedFileID ! = null ,
) ;
2022-02-13 11:05:11 +00:00
return files ;
2022-02-13 09:12:05 +00:00
}
2021-07-01 11:06:20 +00:00
Future < List < File > > getAllLocalFiles ( ) async {
final db = await instance . database ;
final results = await db . query (
table ,
where:
' ( $ columnUploadedFileID IS NULL OR $ columnUploadedFileID IS -1) AND $ columnLocalID IS NOT NULL ' ,
orderBy: ' $ columnCreationTime DESC ' ,
2021-07-21 19:55:24 +00:00
groupBy: columnLocalID ,
2021-07-01 11:06:20 +00:00
) ;
return _convertToFiles ( results ) ;
}
2021-06-04 19:11:39 +00:00
Future < List < File > > getEditedRemoteFiles ( ) async {
final db = await instance . database ;
final results = await db . query (
table ,
where:
' ( $ columnCollectionID IS NOT NULL AND $ columnCollectionID IS NOT -1) AND ( $ columnUploadedFileID IS NULL OR $ columnUploadedFileID IS -1) ' ,
orderBy: ' $ columnCreationTime DESC ' ,
2021-07-21 19:55:24 +00:00
groupBy: columnLocalID ,
2021-06-04 19:11:39 +00:00
) ;
return _convertToFiles ( results ) ;
}
2020-11-30 18:42:11 +00:00
Future < List < int > > getUploadedFileIDsToBeUpdated ( ) async {
final db = await instance . database ;
final rows = await db . query (
table ,
columns: [ columnUploadedFileID ] ,
where:
2021-07-21 19:55:24 +00:00
' ( $ columnLocalID IS NOT NULL AND ( $ columnUploadedFileID IS NOT NULL AND $ columnUploadedFileID IS NOT -1) AND $ columnUpdationTime IS NULL) ' ,
2020-11-30 18:42:11 +00:00
orderBy: ' $ columnCreationTime DESC ' ,
distinct: true ,
) ;
2021-07-21 19:55:24 +00:00
final uploadedFileIDs = < int > [ ] ;
2020-11-30 18:42:11 +00:00
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 ] ;
}
2020-12-01 08:09:02 +00:00
Future < Set < String > > getExistingLocalFileIDs ( ) async {
2020-11-30 18:42:11 +00:00
final db = await instance . database ;
final rows = await db . query (
table ,
columns: [ columnLocalID ] ,
distinct: true ,
2020-12-01 08:09:02 +00:00
where: ' $ columnLocalID IS NOT NULL ' ,
2020-11-30 18:42:11 +00:00
) ;
2021-07-21 19:55:24 +00:00
final result = < String > { } ;
2020-11-30 18:42:11 +00:00
for ( final row in rows ) {
result . add ( row [ columnLocalID ] ) ;
}
return result ;
}
2021-03-25 19:05:23 +00:00
Future < int > getNumberOfUploadedFiles ( ) async {
final db = await instance . database ;
final rows = await db . query (
table ,
columns: [ columnUploadedFileID ] ,
where:
2021-07-21 19:55:24 +00:00
' ( $ columnLocalID IS NOT NULL AND ( $ columnUploadedFileID IS NOT NULL AND $ columnUploadedFileID IS NOT -1) AND $ columnUpdationTime IS NOT NULL) ' ,
2021-03-25 19:05:23 +00:00
distinct: true ,
) ;
return rows . length ;
}
2020-11-30 18:42:11 +00:00
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 ] ,
) ;
}
2020-10-29 00:14:48 +00:00
Future < List < File > > getMatchingFiles (
2020-11-20 11:05:17 +00:00
String title ,
String deviceFolder ,
) async {
2020-05-17 18:42:03 +00:00
final db = await instance . database ;
2021-06-13 11:15:24 +00:00
final rows = await db . query (
table ,
where: ''' $ columnTitle =? AND $ columnDeviceFolder =? ''' ,
whereArgs: [
title ,
deviceFolder ,
] ,
) ;
2020-05-17 18:42:03 +00:00
if ( rows . isNotEmpty ) {
2020-10-29 00:14:48 +00:00
return _convertToFiles ( rows ) ;
2020-05-17 18:42:03 +00:00
} else {
2020-10-29 00:14:48 +00:00
return null ;
2020-05-17 18:42:03 +00:00
}
}
2022-08-05 09:50:03 +00:00
Future < List < File > > getUploadedFilesWithHashes (
2022-08-29 07:58:49 +00:00
FileHashData hashData ,
2022-08-05 09:50:03 +00:00
FileType fileType ,
int ownerID ,
) async {
2022-08-29 07:58:49 +00:00
String inParam = " ' ${ hashData . fileHash } ' " ;
if ( fileType = = FileType . livePhoto & & hashData . zipHash ! = null ) {
inParam + = " ,' ${ hashData . zipHash } ' " ;
}
2022-08-05 09:50:03 +00:00
final db = await instance . database ;
2022-08-29 07:58:49 +00:00
final rows = await db . query (
table ,
where: ' ( $ columnUploadedFileID != NULL OR $ columnUploadedFileID != -1) '
' AND $ columnOwnerID = ? AND $ columnFileType = '
' ? '
' AND $ columnHash IN ( $ inParam ) ' ,
whereArgs: [
ownerID ,
getInt ( fileType ) ,
] ,
) ;
2022-08-29 17:43:17 +00:00
return _convertToFiles ( rows ) ;
2022-08-05 09:50:03 +00:00
}
2020-10-24 23:39:46 +00:00
Future < int > update ( File file ) async {
2020-05-06 18:13:24 +00:00
final db = await instance . database ;
return await db . update (
table ,
2020-10-24 23:39:46 +00:00
_getRowForFile ( file ) ,
2020-08-09 22:34:59 +00:00
where: ' $ columnGeneratedID = ? ' ,
2020-10-24 23:39:46 +00:00
whereArgs: [ file . generatedID ] ,
2020-05-06 18:13:24 +00:00
) ;
2020-03-24 19:59:36 +00:00
}
2020-11-30 18:42:11 +00:00
Future < int > updateUploadedFileAcrossCollections ( File file ) async {
final db = await instance . database ;
return await db . update (
table ,
_getRowForFileWithoutCollection ( file ) ,
where: ' $ columnUploadedFileID = ? ' ,
whereArgs: [ file . uploadedFileID ] ,
) ;
}
2021-04-27 20:49:00 +00:00
Future < int > delete ( int uploadedFileID ) async {
2020-05-06 18:13:24 +00:00
final db = await instance . database ;
2021-04-27 20:49:00 +00:00
return db . delete (
2020-05-06 18:13:24 +00:00
table ,
2020-10-30 23:25:28 +00:00
where: ' $ columnUploadedFileID =? ' ,
whereArgs: [ uploadedFileID ] ,
2020-05-06 18:13:24 +00:00
) ;
2020-04-12 12:38:49 +00:00
}
2022-08-05 09:50:03 +00:00
Future < int > deleteByGeneratedID ( int genID ) async {
final db = await instance . database ;
return db . delete (
table ,
where: ' $ columnGeneratedID =? ' ,
whereArgs: [ genID ] ,
) ;
}
2021-04-27 20:49:00 +00:00
Future < int > deleteMultipleUploadedFiles ( List < int > uploadedFileIDs ) async {
2020-05-06 18:13:24 +00:00
final db = await instance . database ;
2021-04-27 22:17:19 +00:00
return await db . delete (
2020-05-06 18:13:24 +00:00
table ,
2021-04-27 22:17:19 +00:00
where: ' $ columnUploadedFileID IN ( ${ uploadedFileIDs . join ( ' , ' ) } ) ' ,
2020-05-06 18:13:24 +00:00
) ;
}
2021-07-24 17:22:46 +00:00
Future < int > deleteLocalFile ( File file ) async {
2020-11-01 07:37:23 +00:00
final db = await instance . database ;
2021-07-24 17:22:46 +00:00
if ( file . localID ! = null ) {
// delete all files with same local ID
return db . delete (
table ,
where: ' $ columnLocalID =? ' ,
whereArgs: [ file . localID ] ,
) ;
} else {
return db . delete (
table ,
where: ' $ columnGeneratedID =? ' ,
whereArgs: [ file . generatedID ] ,
) ;
}
2020-11-01 07:37:23 +00:00
}
2021-06-28 10:12:12 +00:00
Future < void > deleteLocalFiles ( List < String > localIDs ) async {
String inParam = " " ;
for ( final localID in localIDs ) {
inParam + = " ' " + localID + " ', " ;
}
inParam = inParam . substring ( 0 , inParam . length - 1 ) ;
final db = await instance . database ;
2022-06-11 08:23:52 +00:00
await db . rawQuery (
'''
2021-06-28 10:12:12 +00:00
UPDATE $table
SET $columnLocalID = NULL
WHERE $columnLocalID IN ( $inParam ) ;
2022-06-11 08:23:52 +00:00
''' ,
) ;
2021-06-28 10:12:12 +00:00
}
Future < List < File > > getLocalFiles ( List < String > localIDs ) async {
String inParam = " " ;
for ( final localID in localIDs ) {
inParam + = " ' " + localID + " ', " ;
}
inParam = inParam . substring ( 0 , inParam . length - 1 ) ;
final db = await instance . database ;
final results = await db . query (
table ,
where: ' $ columnLocalID IN ( $ inParam ) ' ,
) ;
return _convertToFiles ( results ) ;
}
2021-10-20 10:58:11 +00:00
Future < int > deleteUnSyncedLocalFiles ( List < String > localIDs ) async {
String inParam = " " ;
for ( final localID in localIDs ) {
inParam + = " ' " + localID + " ', " ;
}
inParam = inParam . substring ( 0 , inParam . length - 1 ) ;
final db = await instance . database ;
return db . delete (
table ,
where:
' ( $ columnUploadedFileID is NULL OR $ columnUploadedFileID = -1 ) AND $ columnLocalID IN ( $ inParam ) ' ,
) ;
}
2020-10-23 14:59:51 +00:00
Future < int > deleteFromCollection ( int uploadedFileID , int collectionID ) async {
final db = await instance . database ;
return db . delete (
table ,
where: ' $ columnUploadedFileID = ? AND $ columnCollectionID = ? ' ,
whereArgs: [ uploadedFileID , collectionID ] ,
) ;
}
2021-09-30 09:28:29 +00:00
Future < int > deleteFilesFromCollection (
2022-06-11 08:23:52 +00:00
int collectionID ,
List < int > uploadedFileIDs ,
) async {
2021-09-30 09:28:29 +00:00
final db = await instance . database ;
return db . delete (
table ,
where:
' $ columnCollectionID = ? AND $ columnUploadedFileID IN ( ${ uploadedFileIDs . join ( ' , ' ) } ) ' ,
whereArgs: [ collectionID ] ,
) ;
}
2022-05-05 06:46:31 +00:00
Future < int > collectionFileCount ( int collectionID ) async {
final db = await instance . database ;
2022-08-29 14:43:31 +00:00
final count = Sqflite . firstIntValue (
2022-06-11 08:23:52 +00:00
await db . rawQuery (
' SELECT COUNT(*) FROM $ table where $ columnCollectionID = $ collectionID ' ,
) ,
) ;
2022-05-05 06:46:31 +00:00
return count ;
}
2022-05-17 12:54:56 +00:00
Future < int > fileCountWithVisibility ( int visibility , int ownerID ) async {
2022-05-05 06:46:31 +00:00
final db = await instance . database ;
2022-08-29 14:43:31 +00:00
final count = Sqflite . firstIntValue (
2022-06-11 08:23:52 +00:00
await db . rawQuery (
' SELECT COUNT(*) FROM $ table where $ columnMMdVisibility = $ visibility AND $ columnOwnerID = $ ownerID ' ,
) ,
) ;
2022-05-05 06:46:31 +00:00
return count ;
}
2020-10-31 12:48:41 +00:00
Future < int > deleteCollection ( int collectionID ) async {
final db = await instance . database ;
return db . delete (
table ,
where: ' $ columnCollectionID = ? ' ,
whereArgs: [ collectionID ] ,
) ;
}
2020-10-28 14:07:32 +00:00
Future < int > removeFromCollection ( int collectionID , List < int > fileIDs ) async {
2020-05-22 18:22:55 +00:00
final db = await instance . database ;
return db . delete (
table ,
2020-10-28 15:25:32 +00:00
where:
' $ columnCollectionID =? AND $ columnUploadedFileID IN ( ${ fileIDs . join ( ' , ' ) } ) ' ,
2020-10-28 14:07:32 +00:00
whereArgs: [ collectionID ] ,
2020-05-22 18:22:55 +00:00
) ;
}
2021-04-19 17:08:12 +00:00
Future < List < File > > getLatestLocalFiles ( ) async {
final db = await instance . database ;
2022-06-11 08:23:52 +00:00
final rows = await db . rawQuery (
'''
2021-04-19 17:08:12 +00:00
SELECT $table . *
FROM $table
INNER JOIN
(
SELECT $columnDeviceFolder , MAX ( $columnCreationTime ) AS max_creation_time
FROM $table
2021-04-27 22:27:54 +00:00
WHERE $table . $columnLocalID IS NOT NULL
2021-04-19 17:08:12 +00:00
GROUP BY $columnDeviceFolder
) latest_files
ON $table . $columnDeviceFolder = latest_files . $columnDeviceFolder
2021-04-27 22:27:54 +00:00
AND $table . $columnCreationTime = latest_files . max_creation_time ;
2022-06-11 08:23:52 +00:00
''' ,
) ;
2021-04-27 22:27:54 +00:00
final files = _convertToFiles ( rows ) ;
// TODO: Do this de-duplication within the SQL Query
2021-07-21 19:55:24 +00:00
final folderMap = < String , File > { } ;
2021-04-27 22:27:54 +00:00
for ( final file in files ) {
if ( folderMap . containsKey ( file . deviceFolder ) ) {
if ( folderMap [ file . deviceFolder ] . updationTime < file . updationTime ) {
continue ;
}
}
folderMap [ file . deviceFolder ] = file ;
}
return folderMap . values . toList ( ) ;
2021-04-19 17:08:12 +00:00
}
2021-04-19 17:46:52 +00:00
Future < List < File > > getLatestCollectionFiles ( ) async {
2022-08-27 09:54:06 +00:00
String query ;
if ( FeatureFlagService . instance . isInternalUserOrDebugBuild ( ) ) {
query = '''
2021-04-19 17:46:52 +00:00
SELECT $table . *
FROM $table
INNER JOIN
(
SELECT $columnCollectionID , MAX ( $columnCreationTime ) AS max_creation_time
FROM $table
2022-08-26 12:27:59 +00:00
WHERE ( $columnCollectionID IS NOT NULL AND $columnCollectionID IS NOT - 1 AND $columnMMdVisibility = $kVisibilityVisible )
2021-04-19 17:46:52 +00:00
GROUP BY $columnCollectionID
) latest_files
ON $table . $columnCollectionID = latest_files . $columnCollectionID
AND $table . $columnCreationTime = latest_files . max_creation_time ;
2022-08-27 09:54:06 +00:00
''' ;
} else {
query = '''
2021-04-19 17:46:52 +00:00
SELECT $table . *
FROM $table
INNER JOIN
(
SELECT $columnCollectionID , MAX ( $columnCreationTime ) AS max_creation_time
FROM $table
2021-05-04 15:25:59 +00:00
WHERE ( $columnCollectionID IS NOT NULL AND $columnCollectionID IS NOT - 1 )
2021-04-19 17:46:52 +00:00
GROUP BY $columnCollectionID
) latest_files
ON $table . $columnCollectionID = latest_files . $columnCollectionID
AND $table . $columnCreationTime = latest_files . max_creation_time ;
2022-08-27 09:54:06 +00:00
''' ;
}
final db = await instance . database ;
final rows = await db . rawQuery (
query ,
2022-06-11 08:23:52 +00:00
) ;
2021-04-19 17:49:13 +00:00
final files = _convertToFiles ( rows ) ;
// TODO: Do this de-duplication within the SQL Query
2021-07-21 19:55:24 +00:00
final collectionMap = < int , File > { } ;
2021-04-19 17:49:13 +00:00
for ( final file in files ) {
if ( collectionMap . containsKey ( file . collectionID ) ) {
if ( collectionMap [ file . collectionID ] . updationTime < file . updationTime ) {
continue ;
}
}
collectionMap [ file . collectionID ] = file ;
}
return collectionMap . values . toList ( ) ;
2021-04-19 17:46:52 +00:00
}
2021-07-12 10:24:30 +00:00
Future < Map < String , int > > getFileCountInDeviceFolders ( ) async {
final db = await instance . database ;
2022-06-11 08:23:52 +00:00
final rows = await db . rawQuery (
'''
2021-07-12 10:24:30 +00:00
SELECT COUNT ( $columnGeneratedID ) as count , $columnDeviceFolder
FROM $table
WHERE $columnLocalID IS NOT NULL
GROUP BY $columnDeviceFolder
2022-06-11 08:23:52 +00:00
''' ,
) ;
2021-07-21 19:55:24 +00:00
final result = < String , int > { } ;
2021-07-12 10:24:30 +00:00
for ( final row in rows ) {
result [ row [ columnDeviceFolder ] ] = row [ " count " ] ;
}
return result ;
}
2022-06-08 08:49:23 +00:00
Future < List < String > > getLocalFilesBackedUpWithoutLocation ( ) 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 $ columnUploadedFileID IS NOT -1) '
2022-06-08 09:20:03 +00:00
' AND ( $ columnLatitude IS NULL OR $ columnLongitude IS NULL OR $ columnLongitude = 0.0 or $ columnLongitude = 0.0) ' ,
2022-06-08 08:49:23 +00:00
) ;
final result = < String > [ ] ;
for ( final row in rows ) {
result . add ( row [ columnLocalID ] ) ;
}
return result ;
}
Future < void > markForReUploadIfLocationMissing ( List < String > localIDs ) async {
if ( localIDs . isEmpty ) {
return ;
}
String inParam = " " ;
for ( final localID in localIDs ) {
inParam + = " ' " + localID + " ', " ;
}
inParam = inParam . substring ( 0 , inParam . length - 1 ) ;
final db = await instance . database ;
2022-06-11 08:23:52 +00:00
await db . rawUpdate (
'''
2022-06-08 08:49:23 +00:00
UPDATE $table
SET $columnUpdationTime = NULL
WHERE $columnLocalID IN ( $inParam )
2022-06-08 10:29:29 +00:00
AND ( $columnLatitude IS NULL OR $columnLongitude IS NULL OR $columnLongitude = 0.0 or $columnLongitude = 0.0 ) ;
2022-06-11 08:23:52 +00:00
''' ,
) ;
2022-06-08 08:49:23 +00:00
}
2020-10-30 20:17:06 +00:00
Future < bool > doesFileExistInCollection (
2022-06-11 08:23:52 +00:00
int uploadedFileID ,
int collectionID ,
) async {
2020-10-30 20:17:06 +00:00
final db = await instance . database ;
final rows = await db . query (
table ,
where: ' $ columnUploadedFileID = ? AND $ columnCollectionID = ? ' ,
whereArgs: [ uploadedFileID , collectionID ] ,
limit: 1 ,
) ;
return rows . isNotEmpty ;
}
2021-09-14 18:28:14 +00:00
Future < Map < int , File > > getFilesFromIDs ( List < int > ids ) async {
2021-09-24 13:39:51 +00:00
final result = < int , File > { } ;
if ( ids . isEmpty ) {
return result ;
}
2021-09-14 18:28:14 +00:00
String inParam = " " ;
for ( final id in ids ) {
inParam + = " ' " + id . toString ( ) + " ', " ;
}
inParam = inParam . substring ( 0 , inParam . length - 1 ) ;
final db = await instance . database ;
final results = await db . query (
table ,
where: ' $ columnUploadedFileID IN ( $ inParam ) ' ,
) ;
final files = _convertToFiles ( results ) ;
for ( final file in files ) {
result [ file . uploadedFileID ] = file ;
}
return result ;
}
2020-06-19 23:03:26 +00:00
List < File > _convertToFiles ( List < Map < String , dynamic > > results ) {
2021-04-25 12:26:55 +00:00
final List < File > files = [ ] ;
2020-07-20 11:49:00 +00:00
for ( final result in results ) {
2020-06-19 23:03:26 +00:00
files . add ( _getFileFromRow ( result ) ) ;
2020-03-28 13:56:06 +00:00
}
2020-06-19 23:03:26 +00:00
return files ;
2020-03-28 13:56:06 +00:00
}
2020-03-30 14:28:46 +00:00
2022-08-01 15:36:24 +00:00
Future < List < File > > getAllFilesFromDB ( ) async {
final db = await instance . database ;
2022-08-29 14:43:31 +00:00
final List < Map < String , dynamic > > result = await db . query ( table ) ;
final List < File > files = _convertToFiles ( result ) ;
final List < File > deduplicatedFiles =
2022-08-04 05:59:15 +00:00
_deduplicatedAndFilterIgnoredFiles ( files , null ) ;
return deduplicatedFiles ;
2022-08-01 15:36:24 +00:00
}
2020-06-19 23:03:26 +00:00
Map < String , dynamic > _getRowForFile ( File file ) {
2021-07-21 19:55:24 +00:00
final row = < String , dynamic > { } ;
2021-05-16 08:40:11 +00:00
if ( file . generatedID ! = null ) {
row [ columnGeneratedID ] = file . generatedID ;
}
2020-08-09 22:34:59 +00:00
row [ columnLocalID ] = file . localID ;
2021-05-03 22:48:02 +00:00
row [ columnUploadedFileID ] = file . uploadedFileID ? ? - 1 ;
2020-08-09 22:34:59 +00:00
row [ columnOwnerID ] = file . ownerID ;
2021-05-03 22:48:02 +00:00
row [ columnCollectionID ] = file . collectionID ? ? - 1 ;
2020-06-19 23:03:26 +00:00
row [ columnTitle ] = file . title ;
row [ columnDeviceFolder ] = file . deviceFolder ;
if ( file . location ! = null ) {
row [ columnLatitude ] = file . location . latitude ;
row [ columnLongitude ] = file . location . longitude ;
}
2021-07-29 20:27:12 +00:00
row [ columnFileType ] = getInt ( file . fileType ) ;
2020-07-06 19:09:47 +00:00
row [ columnCreationTime ] = file . creationTime ;
row [ columnModificationTime ] = file . modificationTime ;
row [ columnUpdationTime ] = file . updationTime ;
2020-10-06 22:55:59 +00:00
row [ columnEncryptedKey ] = file . encryptedKey ;
row [ columnKeyDecryptionNonce ] = file . keyDecryptionNonce ;
row [ columnFileDecryptionHeader ] = file . fileDecryptionHeader ;
row [ columnThumbnailDecryptionHeader ] = file . thumbnailDecryptionHeader ;
row [ columnMetadataDecryptionHeader ] = file . metadataDecryptionHeader ;
2021-08-18 13:23:42 +00:00
row [ columnFileSubType ] = file . fileSubType ? ? - 1 ;
row [ columnDuration ] = file . duration ? ? 0 ;
row [ columnExif ] = file . exif ;
row [ columnHash ] = file . hash ;
row [ columnMetadataVersion ] = file . metadataVersion ;
2021-09-16 13:14:51 +00:00
row [ columnMMdVersion ] = file . mMdVersion ? ? 0 ;
row [ columnMMdEncodedJson ] = file . mMdEncodedJson ? ? ' {} ' ;
2021-09-17 10:52:19 +00:00
row [ columnMMdVisibility ] =
2021-09-20 06:41:38 +00:00
file . magicMetadata ? . visibility ? ? kVisibilityVisible ;
2021-10-26 10:37:14 +00:00
row [ columnPubMMdVersion ] = file . pubMmdVersion ? ? 0 ;
row [ columnPubMMdEncodedJson ] = file . pubMmdEncodedJson ? ? ' {} ' ;
if ( file . pubMagicMetadata ! = null & &
file . pubMagicMetadata . editedTime ! = null ) {
// override existing creationTime to avoid re-writing all queries related
// to loading the gallery
row [ columnCreationTime ] = file . pubMagicMetadata . editedTime ;
}
2020-03-30 14:28:46 +00:00
return row ;
}
2020-04-12 12:38:49 +00:00
2020-11-30 18:42:11 +00:00
Map < String , dynamic > _getRowForFileWithoutCollection ( File file ) {
2021-07-21 19:55:24 +00:00
final row = < String , dynamic > { } ;
2020-11-30 18:42:11 +00:00
row [ columnLocalID ] = file . localID ;
2021-05-03 22:48:02 +00:00
row [ columnUploadedFileID ] = file . uploadedFileID ? ? - 1 ;
2020-11-30 18:42:11 +00:00
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 ;
}
2021-07-29 20:27:12 +00:00
row [ columnFileType ] = getInt ( file . fileType ) ;
2020-11-30 18:42:11 +00:00
row [ columnCreationTime ] = file . creationTime ;
row [ columnModificationTime ] = file . modificationTime ;
row [ columnUpdationTime ] = file . updationTime ;
row [ columnFileDecryptionHeader ] = file . fileDecryptionHeader ;
row [ columnThumbnailDecryptionHeader ] = file . thumbnailDecryptionHeader ;
row [ columnMetadataDecryptionHeader ] = file . metadataDecryptionHeader ;
2021-08-18 13:23:42 +00:00
row [ columnFileSubType ] = file . fileSubType ? ? - 1 ;
row [ columnDuration ] = file . duration ? ? 0 ;
row [ columnExif ] = file . exif ;
row [ columnHash ] = file . hash ;
row [ columnMetadataVersion ] = file . metadataVersion ;
2021-09-16 13:14:51 +00:00
row [ columnMMdVersion ] = file . mMdVersion ? ? 0 ;
2021-10-31 14:18:36 +00:00
row [ columnMMdEncodedJson ] = file . mMdEncodedJson ? ? ' {} ' ;
2021-09-17 10:52:19 +00:00
row [ columnMMdVisibility ] =
2021-09-20 06:41:38 +00:00
file . magicMetadata ? . visibility ? ? kVisibilityVisible ;
2021-10-26 05:25:42 +00:00
row [ columnPubMMdVersion ] = file . pubMmdVersion ? ? 0 ;
2021-10-26 10:37:14 +00:00
row [ columnPubMMdEncodedJson ] = file . pubMmdEncodedJson ? ? ' {} ' ;
2021-10-26 05:25:42 +00:00
if ( file . pubMagicMetadata ! = null & &
file . pubMagicMetadata . editedTime ! = null ) {
// override existing creationTime to avoid re-writing all queries related
// to loading the gallery
row [ columnCreationTime ] = file . pubMagicMetadata . editedTime ;
}
2021-10-27 19:42:30 +00:00
return row ;
2020-11-30 18:42:11 +00:00
}
2020-06-19 23:03:26 +00:00
File _getFileFromRow ( Map < String , dynamic > row ) {
2020-07-20 11:49:00 +00:00
final file = File ( ) ;
2020-08-09 22:34:59 +00:00
file . generatedID = row [ columnGeneratedID ] ;
file . localID = row [ columnLocalID ] ;
2021-05-03 22:48:02 +00:00
file . uploadedFileID =
row [ columnUploadedFileID ] = = - 1 ? null : row [ columnUploadedFileID ] ;
2020-10-13 21:40:22 +00:00
file . ownerID = row [ columnOwnerID ] ;
2021-05-03 22:48:02 +00:00
file . collectionID =
row [ columnCollectionID ] = = - 1 ? null : row [ columnCollectionID ] ;
2020-06-19 23:03:26 +00:00
file . title = row [ columnTitle ] ;
file . deviceFolder = row [ columnDeviceFolder ] ;
2020-06-06 21:12:49 +00:00
if ( row [ columnLatitude ] ! = null & & row [ columnLongitude ] ! = null ) {
2020-06-19 23:03:26 +00:00
file . location = Location ( row [ columnLatitude ] , row [ columnLongitude ] ) ;
2020-06-06 21:12:49 +00:00
}
2020-06-19 23:03:26 +00:00
file . fileType = getFileType ( row [ columnFileType ] ) ;
2021-07-21 19:55:24 +00:00
file . creationTime = row [ columnCreationTime ] ;
file . modificationTime = row [ columnModificationTime ] ;
file . updationTime = row [ columnUpdationTime ] ? ? - 1 ;
2020-10-06 22:55:59 +00:00
file . encryptedKey = row [ columnEncryptedKey ] ;
file . keyDecryptionNonce = row [ columnKeyDecryptionNonce ] ;
file . fileDecryptionHeader = row [ columnFileDecryptionHeader ] ;
file . thumbnailDecryptionHeader = row [ columnThumbnailDecryptionHeader ] ;
file . metadataDecryptionHeader = row [ columnMetadataDecryptionHeader ] ;
2021-08-18 13:23:42 +00:00
file . fileSubType = row [ columnFileSubType ] ? ? - 1 ;
file . duration = row [ columnDuration ] ? ? 0 ;
file . exif = row [ columnExif ] ;
file . hash = row [ columnHash ] ;
file . metadataVersion = row [ columnMetadataVersion ] ? ? 0 ;
2021-09-16 13:14:51 +00:00
2021-09-30 09:28:29 +00:00
file . mMdVersion = row [ columnMMdVersion ] ? ? 0 ;
2021-09-16 13:14:51 +00:00
file . mMdEncodedJson = row [ columnMMdEncodedJson ] ? ? ' {} ' ;
2021-10-26 05:25:42 +00:00
file . pubMmdVersion = row [ columnPubMMdVersion ] ? ? 0 ;
file . pubMmdEncodedJson = row [ columnPubMMdEncodedJson ] ? ? ' {} ' ;
2020-06-19 23:03:26 +00:00
return file ;
2020-04-12 12:38:49 +00:00
}
2020-03-26 14:39:31 +00:00
}