[mob][photos] Improve caching for memories and magic (#6252)
## Description - Fixes issues with chinese characters - Runs decoding in computer ## Tests Tested in debug mode on my pixel phone.
This commit is contained in:
@@ -24,6 +24,7 @@ import "package:photos/services/machine_learning/semantic_search/semantic_search
|
||||
import "package:photos/services/remote_assets_service.dart";
|
||||
import "package:photos/services/search_service.dart";
|
||||
import "package:photos/ui/viewer/search/result/magic_result_screen.dart";
|
||||
import "package:photos/utils/cache_util.dart";
|
||||
import "package:photos/utils/file_util.dart";
|
||||
import "package:photos/utils/navigation_util.dart";
|
||||
import "package:shared_preferences/shared_preferences.dart";
|
||||
@@ -251,13 +252,12 @@ class MagicCacheService {
|
||||
final List<MagicCache> magicCaches =
|
||||
await _nonEmptyMagicResults(magicPromptsData);
|
||||
w?.log("resultComputed");
|
||||
final file = File(await _getCachePath());
|
||||
if (!file.existsSync()) {
|
||||
file.createSync(recursive: true);
|
||||
}
|
||||
_magicCacheFuture = Future.value(magicCaches);
|
||||
await file
|
||||
.writeAsBytes(MagicCache.encodeListToJson(magicCaches).codeUnits);
|
||||
await writeToJsonFile<List<MagicCache>>(
|
||||
await _getCachePath(),
|
||||
magicCaches,
|
||||
MagicCache.encodeListToJson,
|
||||
);
|
||||
w?.log("cacheWritten");
|
||||
await _resetLastMagicCacheUpdateTime();
|
||||
w?.logAndReset('done');
|
||||
@@ -300,20 +300,11 @@ class MagicCacheService {
|
||||
|
||||
Future<List<MagicCache>> _readResultFromDisk() async {
|
||||
_logger.info("Reading magic cache result from disk");
|
||||
final file = File(await _getCachePath());
|
||||
if (!file.existsSync()) {
|
||||
_logger.info("No magic cache found");
|
||||
return [];
|
||||
}
|
||||
try {
|
||||
final bytes = await file.readAsBytes();
|
||||
final jsonString = String.fromCharCodes(bytes);
|
||||
return MagicCache.decodeJsonToList(jsonString);
|
||||
} catch (e, s) {
|
||||
_logger.severe("Error reading or decoding cache file", e, s);
|
||||
await file.delete();
|
||||
rethrow;
|
||||
}
|
||||
final cache = await decodeJsonFile<List<MagicCache>>(
|
||||
await _getCachePath(),
|
||||
MagicCache.decodeJsonToList,
|
||||
);
|
||||
return cache ?? [];
|
||||
}
|
||||
|
||||
Future<void> clearMagicCache() async {
|
||||
|
||||
@@ -26,6 +26,7 @@ import "package:photos/services/notification_service.dart";
|
||||
import "package:photos/services/search_service.dart";
|
||||
import "package:photos/ui/home/memories/full_screen_memory.dart";
|
||||
import "package:photos/ui/viewer/people/people_page.dart";
|
||||
import "package:photos/utils/cache_util.dart";
|
||||
import "package:photos/utils/navigation_util.dart";
|
||||
import "package:shared_preferences/shared_preferences.dart";
|
||||
import "package:synchronized/synchronized.dart";
|
||||
@@ -40,7 +41,7 @@ class MemoriesCacheService {
|
||||
static const _kCacheUpdateDelay = Duration(seconds: 5);
|
||||
|
||||
final SharedPreferences _prefs;
|
||||
late final Logger _logger = Logger("MemoriesCacheService");
|
||||
static final Logger _logger = Logger("MemoriesCacheService");
|
||||
|
||||
final _memoriesDB = MemoriesDB.instance;
|
||||
|
||||
@@ -195,35 +196,26 @@ class MemoriesCacheService {
|
||||
if (cache == null) {
|
||||
return null;
|
||||
}
|
||||
final result = await _fromCacheToMemories(cache);
|
||||
final result = await fromCacheToMemories(cache);
|
||||
return result;
|
||||
}
|
||||
|
||||
Future<MemoriesCache?> _readCacheFromDisk() async {
|
||||
_logger.info("Reading memories cache result from disk");
|
||||
final file = File(await _getCachePath());
|
||||
if (!file.existsSync()) {
|
||||
_logger.info("No memories cache found");
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
final bytes = await file.readAsBytes();
|
||||
final jsonString = String.fromCharCodes(bytes);
|
||||
final cache = MemoriesCache.decodeFromJsonString(jsonString);
|
||||
_logger.info("Reading memories cache result from disk done");
|
||||
return cache;
|
||||
} catch (e, s) {
|
||||
_logger.severe("Error reading or decoding cache file", e, s);
|
||||
await file.delete();
|
||||
return null;
|
||||
}
|
||||
final cache = decodeJsonFile<MemoriesCache>(
|
||||
await _getCachePath(),
|
||||
MemoriesCache.decodeFromJsonString,
|
||||
);
|
||||
return cache;
|
||||
}
|
||||
|
||||
Future<List<SmartMemory>> _fromCacheToMemories(MemoriesCache cache) async {
|
||||
static Future<List<SmartMemory>> fromCacheToMemories(
|
||||
MemoriesCache cache,
|
||||
) async {
|
||||
try {
|
||||
_logger.info('Processing disk cache memories to smart memories');
|
||||
final List<SmartMemory> memories = [];
|
||||
final seenTimes = await _memoriesDB.getSeenTimes();
|
||||
final seenTimes = await MemoriesDB.instance.getSeenTimes();
|
||||
final minimalFileIDs = <int>{};
|
||||
for (final ToShowMemory memory in cache.toShowMemories) {
|
||||
if (memory.shouldShowNow()) {
|
||||
@@ -325,10 +317,6 @@ class MemoriesCacheService {
|
||||
}
|
||||
newCache.baseLocations.addAll(nowResult.baseLocations);
|
||||
w?.log("added memories to cache");
|
||||
final file = File(await _getCachePath());
|
||||
if (!file.existsSync()) {
|
||||
file.createSync(recursive: true);
|
||||
}
|
||||
_cachedMemories = nowResult.memories
|
||||
.where((memory) => memory.shouldShowNow())
|
||||
.toList();
|
||||
@@ -336,8 +324,10 @@ class MemoriesCacheService {
|
||||
[...nowResult.memories, ...nextResult.memories],
|
||||
);
|
||||
locationService.baseLocations = nowResult.baseLocations;
|
||||
await file.writeAsBytes(
|
||||
MemoriesCache.encodeToJsonString(newCache).codeUnits,
|
||||
await writeToJsonFile<MemoriesCache>(
|
||||
await _getCachePath(),
|
||||
newCache,
|
||||
MemoriesCache.encodeToJsonString,
|
||||
);
|
||||
w?.log("cacheWritten");
|
||||
await _cacheUpdated();
|
||||
|
||||
@@ -5,6 +5,7 @@ import "package:flutter/cupertino.dart";
|
||||
import "package:flutter/material.dart";
|
||||
import "package:intl/intl.dart";
|
||||
import 'package:logging/logging.dart';
|
||||
import "package:path_provider/path_provider.dart";
|
||||
import "package:photos/core/configuration.dart";
|
||||
import "package:photos/core/constants.dart";
|
||||
import 'package:photos/core/event_bus.dart';
|
||||
@@ -24,6 +25,7 @@ import 'package:photos/models/file/file_type.dart';
|
||||
import "package:photos/models/local_entity_data.dart";
|
||||
import "package:photos/models/location/location.dart";
|
||||
import "package:photos/models/location_tag/location_tag.dart";
|
||||
import "package:photos/models/memories/memories_cache.dart";
|
||||
import "package:photos/models/memories/memory.dart";
|
||||
import "package:photos/models/memories/smart_memory.dart";
|
||||
import "package:photos/models/ml/face/person.dart";
|
||||
@@ -47,12 +49,14 @@ import "package:photos/services/location_service.dart";
|
||||
import "package:photos/services/machine_learning/face_ml/face_filtering/face_filtering_constants.dart";
|
||||
import "package:photos/services/machine_learning/face_ml/person/person_service.dart";
|
||||
import 'package:photos/services/machine_learning/semantic_search/semantic_search_service.dart';
|
||||
import "package:photos/services/memories_cache_service.dart";
|
||||
import "package:photos/states/location_screen_state.dart";
|
||||
import "package:photos/ui/viewer/location/add_location_sheet.dart";
|
||||
import "package:photos/ui/viewer/location/location_screen.dart";
|
||||
import "package:photos/ui/viewer/people/cluster_page.dart";
|
||||
import "package:photos/ui/viewer/people/people_page.dart";
|
||||
import "package:photos/ui/viewer/search/result/magic_result_screen.dart";
|
||||
import "package:photos/utils/cache_util.dart";
|
||||
import "package:photos/utils/file_util.dart";
|
||||
import "package:photos/utils/navigation_util.dart";
|
||||
import 'package:photos/utils/standalone/date_time.dart';
|
||||
@@ -1275,7 +1279,33 @@ class SearchService {
|
||||
final memoriesResult = await smartMemoriesService
|
||||
.calcSmartMemories(calcTime, cache, debugSurfaceAll: true);
|
||||
locationService.baseLocations = memoriesResult.baseLocations;
|
||||
memories = memoriesResult.memories;
|
||||
for (final nowMemory in memoriesResult.memories) {
|
||||
cache.toShowMemories
|
||||
.add(ToShowMemory.fromSmartMemory(nowMemory, calcTime));
|
||||
}
|
||||
cache.baseLocations.addAll(memoriesResult.baseLocations);
|
||||
// memories = memoriesResult.memories;
|
||||
final tempCachePath = (await getTemporaryDirectory()).path +
|
||||
"/cache/test/memories_cache_test";
|
||||
await writeToJsonFile(
|
||||
tempCachePath,
|
||||
cache,
|
||||
MemoriesCache.encodeToJsonString,
|
||||
);
|
||||
_logger.info(
|
||||
"Smart memories cache written to $tempCachePath",
|
||||
);
|
||||
final decodedCache = await decodeJsonFile(
|
||||
tempCachePath,
|
||||
MemoriesCache.decodeFromJsonString,
|
||||
);
|
||||
_logger.info(
|
||||
"Smart memories cache decoded from $tempCachePath",
|
||||
);
|
||||
memories = await MemoriesCacheService.fromCacheToMemories(decodedCache!);
|
||||
_logger.info(
|
||||
"Smart memories cache converted to memories",
|
||||
);
|
||||
}
|
||||
final searchResults = <GenericSearchResult>[];
|
||||
for (final memory in memories) {
|
||||
|
||||
98
mobile/lib/utils/cache_util.dart
Normal file
98
mobile/lib/utils/cache_util.dart
Normal file
@@ -0,0 +1,98 @@
|
||||
import "dart:convert" show utf8;
|
||||
import "dart:developer" show log;
|
||||
import "dart:io";
|
||||
|
||||
import "package:computer/computer.dart";
|
||||
import "package:logging/logging.dart";
|
||||
|
||||
final _computer = Computer.shared();
|
||||
|
||||
final _logger = Logger("CacheUtil");
|
||||
|
||||
/// Writes data to a JSON file at the specified path using the provided method, inside computer.
|
||||
/// The method should convert the data to a JSON string.
|
||||
/// The JSON string is then UTF-8 encoded and written to the file.
|
||||
Future<void> writeToJsonFile<P>(
|
||||
String filePath,
|
||||
P data,
|
||||
String Function(P) toJsonString,
|
||||
) async {
|
||||
final args = {
|
||||
"filePath": filePath,
|
||||
"data": data,
|
||||
"toJsonString": toJsonString,
|
||||
};
|
||||
await _computer.compute<Map<String, dynamic>, void>(
|
||||
_writeToJsonFile<P>,
|
||||
param: args,
|
||||
taskName: "writeToJsonFile",
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _writeToJsonFile<P>(Map<String, dynamic> args) async {
|
||||
try {
|
||||
final file = File(args["filePath"] as String);
|
||||
if (!file.existsSync()) {
|
||||
file.createSync(recursive: true);
|
||||
}
|
||||
final toJsonStringMethod = args["toJsonString"] as String Function(P);
|
||||
final jsonString = toJsonStringMethod(args["data"] as P);
|
||||
final encodedData = utf8.encode(jsonString);
|
||||
await file.writeAsBytes(encodedData);
|
||||
} catch (e, s) {
|
||||
log("Error writing to JSON file with UTF-8 encoding, $e, \n $s");
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads a JSON file from the specified path using the provided method, inside computer.
|
||||
/// The method should decode the JSON string into an object.
|
||||
/// The JSON string is expected to be UTF-8 encoded.
|
||||
Future<P?> decodeJsonFile<P>(
|
||||
String filePath,
|
||||
P Function(String) jsonDecodeMethod,
|
||||
) async {
|
||||
final args = {
|
||||
"filePath": filePath,
|
||||
"jsonDecode": jsonDecodeMethod,
|
||||
};
|
||||
final cache = await _computer.compute<Map<String, dynamic>, P?>(
|
||||
_decodeJsonFile<P>,
|
||||
param: args,
|
||||
taskName: "decodeJsonFile",
|
||||
);
|
||||
if (cache == null) {
|
||||
_logger.warning("Failed to decode JSON file at $filePath");
|
||||
} else {
|
||||
_logger.info("Successfully decoded JSON file at $filePath");
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
Future<P?> _decodeJsonFile<P>(Map<String, dynamic> args) async {
|
||||
final file = File(args["filePath"] as String);
|
||||
if (!file.existsSync()) {
|
||||
log("File does not exist: ${args["filePath"]}");
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
final bytes = await file.readAsBytes();
|
||||
final jsonDecodeMethod = args["jsonDecode"] as P Function(String);
|
||||
P decodedData;
|
||||
try {
|
||||
final jsonString = utf8.decode(bytes);
|
||||
decodedData = jsonDecodeMethod(jsonString);
|
||||
log("Successfully decoded JSON file as UTF-8");
|
||||
} catch (e, s) {
|
||||
log("Failed to decode bytes as UTF-8, trying UTF-16 $e \n $s");
|
||||
final jsonString =
|
||||
String.fromCharCodes(bytes); // Fallback to UTF-16 decoding
|
||||
decodedData = jsonDecodeMethod(jsonString);
|
||||
log("Successfully decoded JSON file as UTF-16");
|
||||
}
|
||||
return decodedData;
|
||||
} catch (e, s) {
|
||||
log("Error decoding JSON file, deleting this cache $e, \n $s");
|
||||
await file.delete();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user