[mob][photos] Unify all caching in cache service only
This commit is contained in:
@@ -22,6 +22,7 @@ import 'package:photos/events/user_logged_out_event.dart';
|
||||
import 'package:photos/models/key_attributes.dart';
|
||||
import 'package:photos/models/key_gen_result.dart';
|
||||
import 'package:photos/models/private_key_attributes.dart';
|
||||
import "package:photos/service_locator.dart";
|
||||
import 'package:photos/services/collections_service.dart';
|
||||
import 'package:photos/services/favorites_service.dart';
|
||||
import "package:photos/services/home_widget_service.dart";
|
||||
@@ -29,7 +30,6 @@ import 'package:photos/services/ignored_files_service.dart';
|
||||
import "package:photos/services/machine_learning/face_ml/person/person_service.dart";
|
||||
import 'package:photos/services/memories_service.dart';
|
||||
import 'package:photos/services/search_service.dart';
|
||||
import "package:photos/services/smart_memories_service.dart";
|
||||
import 'package:photos/services/sync_service.dart';
|
||||
import 'package:photos/utils/crypto_util.dart';
|
||||
import 'package:photos/utils/file_uploader.dart';
|
||||
@@ -217,7 +217,7 @@ class Configuration {
|
||||
CollectionsService.instance.clearCache();
|
||||
FavoritesService.instance.clearCache();
|
||||
MemoriesService.instance.clearCache();
|
||||
SmartMemoriesService.instance.clearCache();
|
||||
unawaited(memoriesCacheService.clearMemoriesCache());
|
||||
SearchService.instance.clearCache();
|
||||
PersonService.instance.clearCache();
|
||||
unawaited(HomeWidgetService.instance.clearHomeWidget());
|
||||
|
||||
@@ -22,7 +22,7 @@ MemoryType memoryTypeFromString(String type) {
|
||||
}
|
||||
}
|
||||
|
||||
abstract class SmartMemory {
|
||||
class SmartMemory {
|
||||
final List<Memory> memories;
|
||||
final MemoryType type;
|
||||
String? name;
|
||||
@@ -33,13 +33,14 @@ abstract class SmartMemory {
|
||||
int? lastDateToShow;
|
||||
// TODO: lau: actually use this in calculated filters
|
||||
|
||||
|
||||
SmartMemory(
|
||||
this.memories,
|
||||
this.type, {
|
||||
name,
|
||||
this.firstCreationTime,
|
||||
this.lastCreationTime,
|
||||
this.firstDateToShow,
|
||||
this.lastDateToShow,
|
||||
}) : name = name != null ? name + "(I)" : null;
|
||||
// TODO: lau: remove (I) from name when opening up the feature flag
|
||||
|
||||
@@ -59,11 +60,4 @@ abstract class SmartMemory {
|
||||
lastCreationTime ??= creationTimes.last;
|
||||
return (firstCreationTime! + lastCreationTime!) ~/ 2;
|
||||
}
|
||||
|
||||
SmartMemory copyWith({
|
||||
List<Memory>? memories,
|
||||
String? name,
|
||||
int? firstCreationTime,
|
||||
int? lastCreationTime,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,15 +1,23 @@
|
||||
import "dart:async";
|
||||
import "dart:convert";
|
||||
import "dart:io" show File;
|
||||
|
||||
import "package:flutter/foundation.dart" show kDebugMode;
|
||||
import "package:logging/logging.dart";
|
||||
import "package:path_provider/path_provider.dart";
|
||||
import "package:photos/core/event_bus.dart";
|
||||
import "package:photos/db/memories_db.dart";
|
||||
import "package:photos/events/files_updated_event.dart";
|
||||
import "package:photos/extensions/stop_watch.dart";
|
||||
import "package:photos/models/file/file.dart";
|
||||
import "package:photos/models/location/location.dart";
|
||||
import "package:photos/models/memory.dart";
|
||||
import "package:photos/models/people_memory.dart";
|
||||
import "package:photos/models/smart_memory.dart";
|
||||
import "package:photos/models/trip_memory.dart";
|
||||
import "package:photos/service_locator.dart";
|
||||
import "package:photos/services/location_service.dart";
|
||||
import "package:photos/services/search_service.dart";
|
||||
import "package:photos/services/smart_memories_service.dart";
|
||||
import "package:shared_preferences/shared_preferences.dart";
|
||||
|
||||
@@ -181,24 +189,50 @@ class TripsShownLogs {
|
||||
|
||||
class MemoriesCacheService {
|
||||
static const _lastMemoriesCacheUpdateTimeKey = "lastMemoriesCacheUpdateTime";
|
||||
static const _showAnyMemoryKey = "memories.enabled";
|
||||
|
||||
/// Delay is for cache update to be done not during app init, during which a
|
||||
/// lot of other things are happening.
|
||||
static const _kCacheUpdateDelay = Duration(seconds: 10);
|
||||
|
||||
static const _kUpdateFrequency = Duration(days: 7);
|
||||
|
||||
final SharedPreferences _prefs;
|
||||
late final Logger _logger = Logger("MemoriesCacheService");
|
||||
|
||||
Future<MemoriesCache?>? _memoriesCacheFuture;
|
||||
final _memoriesDB = MemoriesDB.instance;
|
||||
|
||||
List<SmartMemory>? _cachedMemories;
|
||||
bool _shouldUpdate = false;
|
||||
bool _isUpdateInProgress = false;
|
||||
|
||||
late Map<int, int> _seenTimes;
|
||||
|
||||
MemoriesCacheService(this._prefs) {
|
||||
_logger.fine("MemoriesCacheService constructor");
|
||||
|
||||
Future.delayed(_kCacheUpdateDelay, () {
|
||||
_updateCacheIfTheTimeHasCome();
|
||||
_checkIfTimeToUpdateCache();
|
||||
});
|
||||
|
||||
unawaited(_memoriesDB.getSeenTimes().then((value) => _seenTimes = value));
|
||||
unawaited(
|
||||
_memoriesDB.clearMemoriesSeenBeforeTime(
|
||||
DateTime.now().subtract(_kUpdateFrequency).microsecondsSinceEpoch,
|
||||
),
|
||||
);
|
||||
|
||||
Bus.instance.on<FilesUpdatedEvent>().where((event) {
|
||||
return event.type == EventType.deletedFromEverywhere;
|
||||
}).listen((event) {
|
||||
if (_cachedMemories == null) return;
|
||||
final generatedIDs = event.updatedFiles
|
||||
.where((element) => element.generatedID != null)
|
||||
.map((e) => e.generatedID!)
|
||||
.toSet();
|
||||
for (final memory in _cachedMemories!) {
|
||||
memory.memories
|
||||
.removeWhere((m) => generatedIDs.contains(m.file.generatedID));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -213,7 +247,13 @@ class MemoriesCacheService {
|
||||
return _prefs.getInt(_lastMemoriesCacheUpdateTimeKey) ?? 0;
|
||||
}
|
||||
|
||||
Future<void> _updateCacheIfTheTimeHasCome() async {
|
||||
bool get showAnyMemories {
|
||||
return _prefs.getBool(_showAnyMemoryKey) ?? true;
|
||||
}
|
||||
|
||||
bool get enableSmartMemories => flagService.showSmartMemories;
|
||||
|
||||
Future<void> _checkIfTimeToUpdateCache() async {
|
||||
if (lastMemoriesCacheUpdateTime <
|
||||
DateTime.now().subtract(_kUpdateFrequency).microsecondsSinceEpoch) {
|
||||
_shouldUpdate = true;
|
||||
@@ -226,8 +266,26 @@ class MemoriesCacheService {
|
||||
// TODO: lau: remove the test1 directory after testing
|
||||
}
|
||||
|
||||
Future markMemoryAsSeen(Memory memory) async {
|
||||
memory.markSeen();
|
||||
await _memoriesDB.markMemoryAsSeen(
|
||||
memory,
|
||||
DateTime.now().microsecondsSinceEpoch,
|
||||
);
|
||||
if (_cachedMemories != null && memory.file.generatedID != null) {
|
||||
final generatedID = memory.file.generatedID!;
|
||||
for (final smartMemory in _cachedMemories!) {
|
||||
for (final mem in smartMemory.memories) {
|
||||
if (mem.file.generatedID == generatedID) {
|
||||
mem.markSeen();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> updateCache({bool forced = false}) async {
|
||||
if (!SmartMemoriesService.instance.enableSmartMemories) {
|
||||
if (!showAnyMemories || !enableSmartMemories) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
@@ -241,18 +299,18 @@ class MemoriesCacheService {
|
||||
_isUpdateInProgress = true;
|
||||
final EnteWatch? w = kDebugMode ? EnteWatch("memoriesCacheWatch") : null;
|
||||
w?.start();
|
||||
final memories = await SmartMemoriesService.instance.getMemories(null);
|
||||
final memories = await SmartMemoriesService.instance.calcMemories();
|
||||
w?.log("calculated new memories");
|
||||
final oldCache = await getMemoriesCache();
|
||||
final oldCache = await _readCacheFromDisk();
|
||||
w?.log("gotten old cache");
|
||||
final MemoriesCache memoryCache =
|
||||
getCacheFromMemories(memories, oldCache);
|
||||
_fromMemoriesToCache(memories, oldCache);
|
||||
w?.log("gotten cache from memories");
|
||||
final file = File(await _getCachePath());
|
||||
if (!file.existsSync()) {
|
||||
file.createSync(recursive: true);
|
||||
}
|
||||
_memoriesCacheFuture = Future.value(memoryCache);
|
||||
_cachedMemories = memories;
|
||||
await file.writeAsBytes(
|
||||
MemoriesCache.encodeToJsonString(memoryCache).codeUnits,
|
||||
);
|
||||
@@ -267,7 +325,7 @@ class MemoriesCacheService {
|
||||
}
|
||||
}
|
||||
|
||||
MemoriesCache getCacheFromMemories(
|
||||
MemoriesCache _fromMemoriesToCache(
|
||||
List<SmartMemory> memories,
|
||||
MemoriesCache? oldCache,
|
||||
) {
|
||||
@@ -340,15 +398,61 @@ class MemoriesCacheService {
|
||||
);
|
||||
}
|
||||
|
||||
Future<MemoriesCache?> getMemoriesCache() async {
|
||||
if (_memoriesCacheFuture != null) {
|
||||
return _memoriesCacheFuture!;
|
||||
Future<List<SmartMemory>> _fromCacheToMemories(MemoriesCache cache) async {
|
||||
final List<SmartMemory> memories = [];
|
||||
final allFiles = Set<EnteFile>.from(
|
||||
await SearchService.instance.getAllFilesForSearch(),
|
||||
);
|
||||
final allFileIdsToFile = <int, EnteFile>{};
|
||||
for (final file in allFiles) {
|
||||
if (file.uploadedFileID != null) {
|
||||
allFileIdsToFile[file.uploadedFileID!] = file;
|
||||
}
|
||||
}
|
||||
_memoriesCacheFuture = _readResultFromDisk();
|
||||
return _memoriesCacheFuture!;
|
||||
|
||||
for (final ToShowMemory memory in cache.toShowMemories) {
|
||||
if (memory.shouldShowNow) {
|
||||
memories.add(
|
||||
SmartMemory(
|
||||
memory.fileUploadedIDs
|
||||
.map(
|
||||
(fileID) =>
|
||||
Memory.fromFile(allFileIdsToFile[fileID]!, _seenTimes),
|
||||
)
|
||||
.toList(),
|
||||
memory.type,
|
||||
name: memory.title,
|
||||
firstDateToShow: memory.firstTimeToShow,
|
||||
lastDateToShow: memory.lastTimeToShow,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
return memories;
|
||||
}
|
||||
|
||||
Future<MemoriesCache?> _readResultFromDisk() async {
|
||||
Future<List<SmartMemory>> _getMemoriesFromCache() async {
|
||||
final cache = await _readCacheFromDisk();
|
||||
if (cache == null) {
|
||||
// TODO: lau: if there's no cache, maybe we fall back to old memories?
|
||||
return [];
|
||||
}
|
||||
final result = await _fromCacheToMemories(cache);
|
||||
return result;
|
||||
}
|
||||
|
||||
Future<List<SmartMemory>> getMemories(int? limit) async {
|
||||
if (!showAnyMemories) {
|
||||
return [];
|
||||
}
|
||||
if (_cachedMemories != null) {
|
||||
return _cachedMemories!;
|
||||
}
|
||||
_cachedMemories = await _getMemoriesFromCache();
|
||||
return _cachedMemories!;
|
||||
}
|
||||
|
||||
Future<MemoriesCache?> _readCacheFromDisk() async {
|
||||
_logger.info("Reading memories cache result from disk");
|
||||
final file = File(await _getCachePath());
|
||||
if (!file.existsSync()) {
|
||||
@@ -361,5 +465,6 @@ class MemoriesCacheService {
|
||||
|
||||
Future<void> clearMemoriesCache() async {
|
||||
await File(await _getCachePath()).delete();
|
||||
_cachedMemories = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1194,7 +1194,7 @@ class SearchService {
|
||||
BuildContext context,
|
||||
int? limit,
|
||||
) async {
|
||||
final memories = await SmartMemoriesService.instance.getMemories(limit);
|
||||
final memories = await memoriesCacheService.getMemories(limit);
|
||||
final searchResults = <GenericSearchResult>[];
|
||||
for (final memory in memories) {
|
||||
final name = memory.name ?? "memory";
|
||||
|
||||
@@ -7,10 +7,8 @@ import "package:logging/logging.dart";
|
||||
import "package:ml_linalg/vector.dart";
|
||||
import "package:photos/core/configuration.dart";
|
||||
import "package:photos/core/constants.dart";
|
||||
import "package:photos/core/event_bus.dart";
|
||||
import "package:photos/db/memories_db.dart";
|
||||
import "package:photos/db/ml/db.dart";
|
||||
import "package:photos/events/files_updated_event.dart";
|
||||
import "package:photos/l10n/l10n.dart";
|
||||
import "package:photos/models/base_location.dart";
|
||||
import "package:photos/models/file/extensions/file_props.dart";
|
||||
@@ -30,7 +28,6 @@ import "package:photos/services/location_service.dart";
|
||||
import "package:photos/services/machine_learning/face_ml/person/person_service.dart";
|
||||
import "package:photos/services/machine_learning/ml_computer.dart";
|
||||
import "package:photos/services/machine_learning/ml_result.dart";
|
||||
import "package:photos/services/memories_service.dart";
|
||||
import "package:photos/services/search_service.dart";
|
||||
|
||||
class SmartMemoriesService {
|
||||
@@ -42,17 +39,12 @@ class SmartMemoriesService {
|
||||
Locale? _locale;
|
||||
late Map<int, int> _seenTimes;
|
||||
|
||||
List<SmartMemory>? _cachedMemories;
|
||||
Future<List<SmartMemory>>? _future;
|
||||
|
||||
Vector? _clipPositiveTextVector;
|
||||
static const String clipPositiveQuery =
|
||||
'Photo of a precious and nostalgic memory radiating warmth, vibrant energy, or quiet beauty — alive with color, light, or emotion';
|
||||
|
||||
final Map<PeopleActivity, Vector> _clipPeopleActivityVectors = {};
|
||||
|
||||
static const int _calculationWindowDays = 14;
|
||||
|
||||
static const _clipSimilarImageThreshold = 0.75;
|
||||
static const _clipActivityQueryThreshold = 0.25;
|
||||
|
||||
@@ -65,25 +57,6 @@ class SmartMemoriesService {
|
||||
if (_isInit) return;
|
||||
_locale = await getLocale();
|
||||
|
||||
unawaited(
|
||||
_memoriesDB.clearMemoriesSeenBeforeTime(
|
||||
DateTime.now().microsecondsSinceEpoch -
|
||||
(_calculationWindowDays * microSecondsInDay),
|
||||
),
|
||||
);
|
||||
Bus.instance.on<FilesUpdatedEvent>().where((event) {
|
||||
return event.type == EventType.deletedFromEverywhere;
|
||||
}).listen((event) {
|
||||
if (_cachedMemories == null) return;
|
||||
final generatedIDs = event.updatedFiles
|
||||
.where((element) => element.generatedID != null)
|
||||
.map((e) => e.generatedID!)
|
||||
.toSet();
|
||||
for (final memory in _cachedMemories!) {
|
||||
memory.memories
|
||||
.removeWhere((m) => generatedIDs.contains(m.file.generatedID));
|
||||
}
|
||||
});
|
||||
unawaited(
|
||||
MLComputer.instance.runClipText(clipPositiveQuery).then((embedding) {
|
||||
_clipPositiveTextVector ??= Vector.fromList(embedding);
|
||||
@@ -103,47 +76,8 @@ class SmartMemoriesService {
|
||||
_logger.info("Smart memories service initialized");
|
||||
}
|
||||
|
||||
bool get enableSmartMemories => flagService.showSmartMemories;
|
||||
|
||||
void clearCache() {
|
||||
_cachedMemories = null;
|
||||
_future = null;
|
||||
}
|
||||
|
||||
Future markMemoryAsSeen(Memory memory) async {
|
||||
memory.markSeen();
|
||||
await _memoriesDB.markMemoryAsSeen(
|
||||
memory,
|
||||
DateTime.now().microsecondsSinceEpoch,
|
||||
);
|
||||
if (_cachedMemories != null && memory.file.generatedID != null) {
|
||||
final generatedID = memory.file.generatedID!;
|
||||
for (final smartMemory in _cachedMemories!) {
|
||||
for (final mem in smartMemory.memories) {
|
||||
if (mem.file.generatedID == generatedID) {
|
||||
mem.markSeen();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<SmartMemory>> getMemories(int? limit) async {
|
||||
if (!MemoriesService.instance.showMemories) {
|
||||
return [];
|
||||
}
|
||||
if (_cachedMemories != null) {
|
||||
return _cachedMemories!;
|
||||
}
|
||||
if (_future != null) {
|
||||
return _future!;
|
||||
}
|
||||
_future = _calcMemories();
|
||||
return _future!;
|
||||
}
|
||||
|
||||
// One general method to get all memories, which calls on internal methods for each separate memory type
|
||||
Future<List<SmartMemory>> _calcMemories() async {
|
||||
Future<List<SmartMemory>> calcMemories() async {
|
||||
try {
|
||||
await init();
|
||||
final List<SmartMemory> memories = [];
|
||||
@@ -178,8 +112,6 @@ class SmartMemoriesService {
|
||||
// _deductUsedMemories(allFiles, fillerMemories);
|
||||
// memories.addAll(fillerMemories);
|
||||
// _logger.finest("All files length: ${allFiles.length}");
|
||||
|
||||
_cachedMemories = memories;
|
||||
return memories;
|
||||
} catch (e, s) {
|
||||
_logger.severe("Error calculating smart memories", e, s);
|
||||
|
||||
@@ -6,8 +6,8 @@ import "package:photos/core/event_bus.dart";
|
||||
import "package:photos/events/memories_setting_changed.dart";
|
||||
import 'package:photos/models/memory.dart';
|
||||
import "package:photos/models/smart_memory.dart";
|
||||
import "package:photos/service_locator.dart";
|
||||
import 'package:photos/services/memories_service.dart';
|
||||
import "package:photos/services/smart_memories_service.dart";
|
||||
import "package:photos/ui/common/loading_widget.dart";
|
||||
import 'package:photos/ui/home/memories/memory_cover_widget.dart';
|
||||
|
||||
@@ -57,11 +57,11 @@ class _MemoriesWidgetState extends State<MemoriesWidget> {
|
||||
if (!MemoriesService.instance.showMemories) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
if (SmartMemoriesService.instance.enableSmartMemories) {
|
||||
if (memoriesCacheService.enableSmartMemories) {
|
||||
return FutureBuilder<List<SmartMemory>>(
|
||||
future: SmartMemoriesService.instance.getMemories(
|
||||
future: memoriesCacheService.getMemories(
|
||||
null,
|
||||
), // This is where I should hook in my feature flag [showSmartMemories]
|
||||
),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData && snapshot.data!.isEmpty) {
|
||||
return const SizedBox.shrink();
|
||||
|
||||
Reference in New Issue
Block a user