import 'dart:io' as io; import 'package:equatable/equatable.dart'; import 'package:logging/logging.dart'; import 'package:np_codegen/np_codegen.dart'; import 'package:np_common/or_null.dart'; import 'package:np_common/type.dart'; import 'package:np_datetime/np_datetime.dart'; import 'package:np_db/src/entity.dart'; import 'package:np_db_sqlite/np_db_sqlite.dart'; import 'package:to_string/to_string.dart'; part 'api.g.dart'; typedef NpDbComputeCallback = Future Function(NpDb db, T message); /// A data structure that identify a File in db @ToString(ignoreNull: true) class DbFileKey { const DbFileKey({ this.fileId, this.relativePath, }) : assert(fileId != null || relativePath != null); const DbFileKey.byId(int fileId) : this(fileId: fileId); const DbFileKey.byPath(String relativePath) : this(relativePath: relativePath); @override String toString() => _$toString(); bool compareIdentity(DbFileKey other) => fileId == other.fileId || relativePath == other.relativePath; final int? fileId; final String? relativePath; } class DbSyncResult { const DbSyncResult({ required this.insert, required this.delete, required this.update, }); final int insert; final int delete; final int update; } @toString class DbLocationGroup with EquatableMixin { const DbLocationGroup({ required this.place, required this.countryCode, required this.count, required this.latestFileId, required this.latestDateTime, }); @override String toString() => _$toString(); @override List get props => [ place, countryCode, count, latestFileId, latestDateTime, ]; final String place; final String countryCode; final int count; final int latestFileId; final DateTime latestDateTime; } @toString class DbLocationGroupResult { const DbLocationGroupResult({ required this.name, required this.admin1, required this.admin2, required this.countryCode, }); @override String toString() => _$toString(); final List name; final List admin1; final List admin2; final List countryCode; } @npLog abstract class NpDb { factory NpDb() => NpDbSqlite(); /// Initialize the db for the main isolate /// /// If running on android, you must pass the current SDK int to [androidSdk]. /// If running on other platforms, this value will be ignored, you can pass /// null in such case Future initMainIsolate({ required int? androidSdk, }); /// Initialize the db for a background isolate /// /// If running on android, you must pass the current SDK int to [androidSdk]. /// If running on other platforms, this value will be ignored, you can pass /// null in such case Future initBackgroundIsolate({ required int? androidSdk, }); /// Dispose the db /// /// After disposing, you must not call any methods defined here anymore. This /// is typically used before stopping a background isolate Future dispose(); Future export(io.Directory dir); /// Start an isolate with a [NpDb] instance provided to you Future compute(NpDbComputeCallback callback, T args); /// Insert [accounts] to db Future addAccounts(List accounts); /// Clear all data in the database and insert [accounts] /// /// WARNING: ALL data will be dropped! Future clearAndInitWithAccounts(List accounts); Future deleteAccount(DbAccount account); Future> getAlbumsByAlbumFileIds({ required DbAccount account, required List fileIds, }); Future syncAlbum({ required DbAccount account, required DbFile albumFile, required DbAlbum album, }); /// Return all faces provided by the Face Recognition app Future> getFaceRecognitionPersons({ required DbAccount account, }); /// Return faces provided by the Face Recognition app with loosely matched /// [name] Future> searchFaceRecognitionPersonsByName({ required DbAccount account, required String name, }); /// Replace all recognized people for [account] Future syncFaceRecognitionPersons({ required DbAccount account, required List persons, }); /// Return files located inside [dir] Future> getFilesByDirKey({ required DbAccount account, required DbFileKey dir, }); Future> getFilesByDirKeyAndLocation({ required DbAccount account, required String dirRelativePath, required String? place, required String countryCode, }); /// Return [DbFile]s by their corresponding file ids /// /// No error will be thrown even if a file in [fileIds] is not found, it is /// thus the responsibility of the caller to decide how to handle such case. /// Returned files are NOT guaranteed to be sorted as [fileIds] Future> getFilesByFileIds({ required DbAccount account, required List fileIds, }); /// Return [DbFile]s by their date time value Future> getFilesByTimeRange({ required DbAccount account, required List dirRoots, required TimeRange range, }); /// Update one or more file properties of a single file Future updateFileByFileId({ required DbAccount account, required int fileId, String? relativePath, OrNull? isFavorite, OrNull? isArchived, OrNull? overrideDateTime, DateTime? bestDateTime, OrNull? imageData, OrNull? location, }); /// Batch update one or more file properties of multiple files /// /// Only a subset of properties can be updated in batch Future updateFilesByFileIds({ required DbAccount account, required List fileIds, OrNull? isFavorite, OrNull? isArchived, }); /// Add or replace files in db Future syncDirFiles({ required DbAccount account, required DbFileKey dirFile, required List files, }); /// Replace a file in db Future syncFile({ required DbAccount account, required DbFile file, }); /// Add or replace nc albums in db Future syncFavoriteFiles({ required DbAccount account, required List favoriteFileIds, }); /// Return number of files without metadata Future countFilesByFileIdsMissingMetadata({ required DbAccount account, required List fileIds, required List mimes, }); /// Delete a file or dir from db Future deleteFile({ required DbAccount account, required DbFileKey file, }); /// Return a map of file id to etags for all dirs and sub dirs located under /// [relativePath], including the path itself Future> getDirFileIdToEtagByLikeRelativePath({ required DbAccount account, required String relativePath, }); /// Remove all children of a dir Future truncateDir({ required DbAccount account, required DbFileKey dir, }); /// Return [DbFileDescriptor]s /// /// Limit results by their corresponding file ids if [fileIds] is not null. No /// error will be thrown even if a file in [fileIds] is not found, it is thus /// the responsibility of the caller to decide how to handle such case /// /// [includeRelativeRoots] define paths to be included; [excludeRelativeRoots] /// define paths to be excluded. Paths in both lists are matched as prefix /// /// Limit type of files to be returned by specifying [mimes]. The mime types /// are matched as is /// /// Returned files are sorted by [DbFileDescriptor.bestDateTime] in descending /// order Future> getFileDescriptors({ required DbAccount account, List? fileIds, List? includeRelativeRoots, List? excludeRelativeRoots, List? relativePathKeywords, String? location, bool? isFavorite, List? mimes, int? limit, }); Future groupLocations({ required DbAccount account, List? includeRelativeRoots, List? excludeRelativeRoots, }); Future> getNcAlbums({ required DbAccount account, }); Future addNcAlbum({ required DbAccount account, required DbNcAlbum album, }); Future deleteNcAlbum({ required DbAccount account, required DbNcAlbum album, }); /// Add or replace nc albums in db Future syncNcAlbums({ required DbAccount account, required List albums, }); Future> getNcAlbumItemsByParent({ required DbAccount account, required DbNcAlbum parent, }); /// Add or replace nc album items in db Future syncNcAlbumItems({ required DbAccount account, required DbNcAlbum album, required List items, }); /// Return all faces provided by the Recognize app Future> getRecognizeFaces({ required DbAccount account, }); Future> getRecognizeFaceItemsByFaceLabel({ required DbAccount account, required String label, }); Future>> getRecognizeFaceItemsByFaceLabels({ required DbAccount account, required List labels, ErrorWithValueHandler? onError, }); Future> getLatestRecognizeFaceItemsByFaceLabels({ required DbAccount account, required List labels, ErrorWithValueHandler? onError, }); /// Replace all recognized faces for [account] /// /// Return true if any of the faces or items are changed Future syncRecognizeFacesAndItems({ required DbAccount account, required Map> data, }); /// Return all tags Future> getTags({ required DbAccount account, }); /// Return the tag matching [displayName] Future getTagByDisplayName({ required DbAccount account, required String displayName, }); /// Replace all tags for [account] Future syncTags({ required DbAccount account, required List tags, }); /// Migrate to app v55 Future migrateV55(void Function(int current, int count)? onProgress); /// Run vacuum statement on a database backed by sqlite /// /// This method is not necessarily supported by all implementations Future sqlVacuum(); }