diff --git a/mobile/apps/auth/assets/custom-icons/_data/custom-icons.json b/mobile/apps/auth/assets/custom-icons/_data/custom-icons.json
index 129e14ca96..266cda8fa1 100644
--- a/mobile/apps/auth/assets/custom-icons/_data/custom-icons.json
+++ b/mobile/apps/auth/assets/custom-icons/_data/custom-icons.json
@@ -10,6 +10,16 @@
{
"title": "3Commas"
},
+ {
+ "title": "Accredible",
+ "slug": "accredible",
+ "altNames": [
+ "Accredible Certificates",
+ "Accredible Badges",
+ "Digital Credentials",
+ "certificates.zaka.ai"
+ ]
+ },
{
"title": "Addy.io",
"slug": "addy_io"
@@ -71,6 +81,13 @@
],
"hex": "fd4b2d"
},
+ {
+ "title": "Autenticacion Digital",
+ "slug": "autenticacion-digital",
+ "altNames": [
+ "autenticaciondigital.and.gov.co"
+ ]
+ },
{
"title": "availity"
},
@@ -279,6 +296,13 @@
"title": "CERN",
"slug": "cern"
},
+ {
+ "title": "Chaturbate",
+ "slug": "chaturbate",
+ "altNames": [
+ "Chaturbate.com"
+ ]
+ },
{
"title": "ChangeNOW"
},
@@ -436,7 +460,7 @@
"title": "emeritihealth",
"altNames": [
"Emeriti Health",
- "Emeriti Retirement Health",
+ "Emeriti Retirement Health"
]
},
{
@@ -742,6 +766,15 @@
{
"title": "Letterboxd"
},
+ {
+ "title": "LifeMiles",
+ "slug": "lifemiles",
+ "altNames": [
+ "Life Miles",
+ "lifemiles.com",
+ "Avianca LifeMiles"
+ ]
+ },
{
"title": "lincolnfinancial",
"altNames": [
@@ -1295,6 +1328,19 @@
"PAYDAY 3"
]
},
+ {
+ "title": "Startmail",
+ "slug": "startmail"
+ },
+ {
+ "title": "Stripchat",
+ "slug": "stripchat",
+ "altNames": [
+ "Strip Chat",
+ "stripchat.com",
+ "StripChat Live"
+ ]
+ },
{
"title": "STRATO",
"hex": "FF8800"
@@ -1313,6 +1359,9 @@
"T-Mobile ID"
]
},
+ {
+ "title": "Tableau"
+ },
{
"title": "TCPShield"
},
@@ -1522,6 +1571,12 @@
{
"title": "WYZE"
},
+ {
+ "title": "X",
+ "altNames": [
+ "Twitter"
+ ]
+ },
{
"title": "Xbox",
"hex": "107C10"
@@ -1577,6 +1632,14 @@
"title": "xAI",
"slug": "xai"
},
+ {
+ "title": "XVideos",
+ "slug": "xvideos",
+ "altNames": [
+ "X Videos",
+ "xvideos.com"
+ ]
+ },
{
"title": "Cronometer",
"slug": "cronometer"
diff --git a/mobile/apps/auth/assets/custom-icons/icons/accredible.svg b/mobile/apps/auth/assets/custom-icons/icons/accredible.svg
new file mode 100644
index 0000000000..707dbf482d
--- /dev/null
+++ b/mobile/apps/auth/assets/custom-icons/icons/accredible.svg
@@ -0,0 +1,8 @@
+
+
diff --git a/mobile/apps/auth/assets/custom-icons/icons/auth_digital.svg b/mobile/apps/auth/assets/custom-icons/icons/auth_digital.svg
new file mode 100644
index 0000000000..173f12977c
--- /dev/null
+++ b/mobile/apps/auth/assets/custom-icons/icons/auth_digital.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/mobile/apps/auth/assets/custom-icons/icons/chaturbate.svg b/mobile/apps/auth/assets/custom-icons/icons/chaturbate.svg
new file mode 100644
index 0000000000..2c6992ebe7
--- /dev/null
+++ b/mobile/apps/auth/assets/custom-icons/icons/chaturbate.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/mobile/apps/auth/assets/custom-icons/icons/lifemiles.svg b/mobile/apps/auth/assets/custom-icons/icons/lifemiles.svg
new file mode 100644
index 0000000000..b08d6f0107
--- /dev/null
+++ b/mobile/apps/auth/assets/custom-icons/icons/lifemiles.svg
@@ -0,0 +1,101 @@
+
+
+
+
\ No newline at end of file
diff --git a/mobile/apps/auth/assets/custom-icons/icons/startmail.svg b/mobile/apps/auth/assets/custom-icons/icons/startmail.svg
new file mode 100755
index 0000000000..3df4bc496b
--- /dev/null
+++ b/mobile/apps/auth/assets/custom-icons/icons/startmail.svg
@@ -0,0 +1,15 @@
+
+
+
diff --git a/mobile/apps/auth/assets/custom-icons/icons/stripchat.svg b/mobile/apps/auth/assets/custom-icons/icons/stripchat.svg
new file mode 100644
index 0000000000..4a338ee588
--- /dev/null
+++ b/mobile/apps/auth/assets/custom-icons/icons/stripchat.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/mobile/apps/auth/assets/custom-icons/icons/tableau.svg b/mobile/apps/auth/assets/custom-icons/icons/tableau.svg
new file mode 100644
index 0000000000..361bb578ac
--- /dev/null
+++ b/mobile/apps/auth/assets/custom-icons/icons/tableau.svg
@@ -0,0 +1,52 @@
+
\ No newline at end of file
diff --git a/mobile/apps/auth/assets/custom-icons/icons/x.svg b/mobile/apps/auth/assets/custom-icons/icons/x.svg
new file mode 100644
index 0000000000..d6464cc0bf
--- /dev/null
+++ b/mobile/apps/auth/assets/custom-icons/icons/x.svg
@@ -0,0 +1,18 @@
+
\ No newline at end of file
diff --git a/mobile/apps/auth/assets/custom-icons/icons/xvideos.svg b/mobile/apps/auth/assets/custom-icons/icons/xvideos.svg
new file mode 100644
index 0000000000..91ccdbba27
--- /dev/null
+++ b/mobile/apps/auth/assets/custom-icons/icons/xvideos.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/mobile/apps/photos/lib/db/files_db.dart b/mobile/apps/photos/lib/db/files_db.dart
index f8900f8912..1957f55179 100644
--- a/mobile/apps/photos/lib/db/files_db.dart
+++ b/mobile/apps/photos/lib/db/files_db.dart
@@ -979,6 +979,55 @@ class FilesDB with SqlDbBase {
return result;
}
+ // remove references for local files which are either already uploaded
+ // or queued for upload but not yet uploaded
+ Future removeQueuedLocalFiles(Set localIDs) async {
+ if (localIDs.isEmpty) {
+ _logger.finest("No local IDs provided for removal");
+ return 0;
+ }
+
+ final db = await instance.sqliteAsyncDB;
+ const batchSize = 10000;
+ int totalRemoved = 0;
+
+ final localIDsList = localIDs.toList();
+
+ for (int i = 0; i < localIDsList.length; i += batchSize) {
+ final endIndex = (i + batchSize > localIDsList.length)
+ ? localIDsList.length
+ : i + batchSize;
+
+ final batch = localIDsList.sublist(i, endIndex);
+ final placeholders = List.filled(batch.length, '?').join(',');
+
+ final r = await db.execute(
+ '''
+ DELETE FROM $filesTable
+ WHERE $columnLocalID IN ($placeholders)
+ AND ($columnCollectionID IS NULL OR $columnCollectionID = -1)
+ AND ($columnUploadedFileID IS NULL OR $columnUploadedFileID = -1)
+ ''',
+ batch,
+ );
+
+ if (r.isNotEmpty) {
+ _logger
+ .fine("Batch ${(i ~/ batchSize) + 1}: Removed ${r.length} files");
+ totalRemoved += r.length;
+ }
+ }
+
+ if (totalRemoved > 0) {
+ _logger.warning(
+ "Removed $totalRemoved potential dups for already queued local files",
+ );
+ } else {
+ _logger.finest("No duplicate id found for queued/uploaded files");
+ }
+ return totalRemoved;
+ }
+
Future> getLocalFileIDsForCollection(int collectionID) async {
final db = await instance.sqliteAsyncDB;
final rows = await db.getAll(
diff --git a/mobile/apps/photos/lib/db/ml/db.dart b/mobile/apps/photos/lib/db/ml/db.dart
index 851e349d00..bc0913f8fe 100644
--- a/mobile/apps/photos/lib/db/ml/db.dart
+++ b/mobile/apps/photos/lib/db/ml/db.dart
@@ -383,8 +383,12 @@ class MLDataDB with SqlDbBase implements IMLDataDB {
}
}
if (personID == null && clusterID == null) {
+ _logger.severe("personID and clusterID cannot be null both");
throw Exception("personID and clusterID cannot be null");
}
+ _logger.severe(
+ "Something went wrong finding a face from `getCoverFaceForPerson` (personID: $personID, clusterID: $clusterID)",
+ );
return null;
}
diff --git a/mobile/apps/photos/lib/services/collections_service.dart b/mobile/apps/photos/lib/services/collections_service.dart
index ee3d662a63..86ed68924b 100644
--- a/mobile/apps/photos/lib/services/collections_service.dart
+++ b/mobile/apps/photos/lib/services/collections_service.dart
@@ -1427,12 +1427,20 @@ class CollectionsService {
}
// group files by collectionID
final Map> filesByCollection = {};
+ final Map> fileSeenByCollection = {};
for (final file in filesToCopy) {
- if (filesByCollection.containsKey(file.collectionID!)) {
- filesByCollection[file.collectionID!]!.add(file.copyWith());
- } else {
- filesByCollection[file.collectionID!] = [file.copyWith()];
+ fileSeenByCollection.putIfAbsent(file.collectionID!, () => {});
+ if (fileSeenByCollection[file.collectionID]!
+ .contains(file.uploadedFileID)) {
+ _logger.warning(
+ "skip copy, duplicate ID: ${file.uploadedFileID} in collection "
+ "${file.collectionID}",
+ );
+ continue;
}
+ filesByCollection
+ .putIfAbsent(file.collectionID!, () => [])
+ .add(file.copyWith());
}
for (final entry in filesByCollection.entries) {
final srcCollectionID = entry.key;
@@ -1579,9 +1587,6 @@ class CollectionsService {
params["files"] = [];
for (final batchFile in batch) {
final fileKey = getFileKey(batchFile);
- _logger.info(
- "srcCollection : $srcCollectionID file: ${batchFile.uploadedFileID} key: ${CryptoUtil.bin2base64(fileKey)} ",
- );
final encryptedKeyData =
CryptoUtil.encryptSync(fileKey, getCollectionKey(dstCollectionID));
batchFile.encryptedKey =
@@ -1643,17 +1648,27 @@ class CollectionsService {
);
final List filesToCopy = [];
final List filesToAdd = [];
+ final Set seenForAdd = {};
+ final Set seenForCopy = {};
+
for (final EnteFile file in othersFile) {
- if (hashToUserFile.containsKey(file.hash ?? '')) {
- final userFile = hashToUserFile[file.hash]!;
- if (userFile.fileType == file.fileType) {
- filesToAdd.add(userFile);
- } else {
- filesToCopy.add(file);
- }
- } else {
- filesToCopy.add(file);
+ final userFile = hashToUserFile[file.hash ?? ''];
+ final bool shouldAdd =
+ userFile != null && userFile.fileType == file.fileType;
+ final targetList = shouldAdd ? filesToAdd : filesToCopy;
+ final seenSet = shouldAdd ? seenForAdd : seenForCopy;
+ final fileToProcess = shouldAdd ? userFile : file;
+ final uploadID = fileToProcess.uploadedFileID;
+
+ if (seenSet.contains(uploadID)) {
+ final action = shouldAdd ? "adding" : "copying";
+ _logger.warning(
+ "skip $action file $uploadID as it is already ${action}ed",
+ );
+ continue;
}
+ targetList.add(fileToProcess);
+ seenSet.add(uploadID!);
}
return (filesToAdd, filesToCopy);
}
diff --git a/mobile/apps/photos/lib/services/date_parse_service.dart b/mobile/apps/photos/lib/services/date_parse_service.dart
new file mode 100644
index 0000000000..ef136d25ed
--- /dev/null
+++ b/mobile/apps/photos/lib/services/date_parse_service.dart
@@ -0,0 +1,365 @@
+import 'dart:collection';
+
+class PartialDate {
+ final int? day;
+ final int? month;
+ final int? year;
+
+ const PartialDate({this.day, this.month, this.year});
+
+ static const empty = PartialDate();
+
+ bool get isEmpty => day == null && month == null && year == null;
+
+ @override
+ bool operator ==(Object other) =>
+ identical(this, other) ||
+ other is PartialDate &&
+ runtimeType == other.runtimeType &&
+ day == other.day &&
+ month == other.month &&
+ year == other.year;
+
+ @override
+ int get hashCode => day.hashCode ^ month.hashCode ^ year.hashCode;
+
+ @override
+ String toString() {
+ return 'PartialDate(day: $day, month: $month, year: $year)';
+ }
+}
+
+class DateParseService {
+ static final DateParseService instance = DateParseService._private();
+ DateParseService._private();
+
+ static const int _MIN_YEAR = 1900;
+ static const int _MAX_YEAR = 2100;
+ static const int _TWO_DIGIT_YEAR_PIVOT = 50;
+
+ static final _ordinalRegex = RegExp(r'\b(\d{1,2})(st|nd|rd|th)\b');
+ static final _normalizeRegex = RegExp(r'\bof\b|[,\.]+|\s+');
+ static final _isoFormatRegex =
+ RegExp(r'^(\d{4})[\/-](\d{1,2})[\/-](\d{1,2})$');
+ static final _standardFormatRegex =
+ RegExp(r'^(\d{1,2})[\/-](\d{1,2})[\/-](\d{2,4})$');
+ static final _dotFormatRegex = RegExp(r'^(\d{1,2})\.(\d{1,2})\.(\d{2,4})$');
+ static final _compactFormatRegex = RegExp(r'^(\d{8})$');
+ static final _yearOnlyRegex = RegExp(r'^\s*(\d{4})\s*$');
+ static final _shortFormatRegex = RegExp(r'^(\d{1,2})[\/-](\d{1,2})$');
+
+ static final Map _monthMap = UnmodifiableMapView({
+ "january": 1,
+ "february": 2,
+ "march": 3,
+ "april": 4,
+ "may": 5,
+ "june": 6,
+ "july": 7,
+ "august": 8,
+ "september": 9,
+ "october": 10,
+ "november": 11,
+ "december": 12,
+ "jan": 1,
+ "feb": 2,
+ "mar": 3,
+ "apr": 4,
+ "jun": 6,
+ "jul": 7,
+ "aug": 8,
+ "sep": 9,
+ "sept": 9,
+ "oct": 10,
+ "nov": 11,
+ "dec": 12,
+ "janu": 1,
+ "febr": 2,
+ "marc": 3,
+ "apri": 4,
+ "juli": 7,
+ "augu": 8,
+ "sepe": 9,
+ "octo": 10,
+ "nove": 11,
+ "dece": 12,
+ });
+
+ static const Map monthNumberToName = {
+ 1: "January",
+ 2: "February",
+ 3: "March",
+ 4: "April",
+ 5: "May",
+ 6: "June",
+ 7: "July",
+ 8: "August",
+ 9: "September",
+ 10: "October",
+ 11: "November",
+ 12: "December",
+ };
+
+ PartialDate parse(String input) {
+ if (input.trim().isEmpty) return PartialDate.empty;
+
+ final lowerInput = input.toLowerCase();
+
+ var result = _parseRelativeDate(lowerInput);
+ if (!result.isEmpty) return result;
+
+ result = _parseStructuredFormats(lowerInput);
+ if (!result.isEmpty) return result;
+
+ final normalized = _normalizeDateString(lowerInput);
+ result = _parseTokenizedDate(normalized);
+
+ return result;
+ }
+
+ String getMonthName(int month) {
+ return monthNumberToName[month] ?? 'Unknown';
+ }
+
+ String _normalizeDateString(String input) {
+ return input
+ .replaceAllMapped(_ordinalRegex, (match) => match.group(1)!)
+ .replaceAll(_normalizeRegex, ' ')
+ .trim();
+ }
+
+ int _convertTwoDigitYear(int year) {
+ return year < _TWO_DIGIT_YEAR_PIVOT ? 2000 + year : 1900 + year;
+ }
+
+ PartialDate _parseRelativeDate(String lowerInput) {
+ final bool hasToday = lowerInput.contains('today');
+ final bool hasTomorrow = lowerInput.contains('tomorrow');
+ final bool hasYesterday = lowerInput.contains('yesterday');
+
+ final int count =
+ (hasToday ? 1 : 0) + (hasTomorrow ? 1 : 0) + (hasYesterday ? 1 : 0);
+
+ if (count > 1) {
+ return PartialDate.empty;
+ }
+
+ final now = DateTime.now();
+ if (hasToday) {
+ return PartialDate(day: now.day, month: now.month, year: now.year);
+ }
+ if (hasTomorrow) {
+ final tomorrow = now.add(const Duration(days: 1));
+ return PartialDate(
+ day: tomorrow.day,
+ month: tomorrow.month,
+ year: tomorrow.year,
+ );
+ }
+ if (hasYesterday) {
+ final yesterday = now.subtract(const Duration(days: 1));
+ return PartialDate(
+ day: yesterday.day,
+ month: yesterday.month,
+ year: yesterday.year,
+ );
+ }
+ return PartialDate.empty;
+ }
+
+ PartialDate _parseStructuredFormats(String input) {
+ final cleanInput = input.replaceAll(' ', '');
+
+ Match? match = _isoFormatRegex.firstMatch(cleanInput);
+ if (match != null) {
+ final yearVal = int.tryParse(match.group(1)!);
+ final monthVal = int.tryParse(match.group(2)!);
+ final dayVal = int.tryParse(match.group(3)!);
+ if (yearVal != null &&
+ yearVal >= _MIN_YEAR &&
+ yearVal <= _MAX_YEAR &&
+ monthVal != null &&
+ monthVal >= 1 &&
+ monthVal <= 12 &&
+ dayVal != null &&
+ dayVal >= 1 &&
+ dayVal <= 31) {
+ return PartialDate(day: dayVal, month: monthVal, year: yearVal);
+ }
+ return PartialDate.empty;
+ }
+
+ match = _standardFormatRegex.firstMatch(cleanInput);
+ if (match != null) {
+ final p1 = int.parse(match.group(1)!);
+ final p2 = int.parse(match.group(2)!);
+ final yearRaw = int.parse(match.group(3)!);
+ final year = yearRaw > 99 ? yearRaw : _convertTwoDigitYear(yearRaw);
+
+ if (year < _MIN_YEAR || year > _MAX_YEAR) return PartialDate.empty;
+
+ if (p1 > 12) {
+ if (p1 >= 1 && p1 <= 31 && p2 >= 1 && p2 <= 12) {
+ return PartialDate(day: p1, month: p2, year: year);
+ }
+ } else if (p2 > 12) {
+ if (p1 >= 1 && p1 <= 12 && p2 >= 1 && p2 <= 31) {
+ return PartialDate(day: p2, month: p1, year: year);
+ }
+ } else {
+ if (p1 >= 1 && p1 <= 12 && p2 >= 1 && p2 <= 31) {
+ return PartialDate(day: p2, month: p1, year: year);
+ }
+ }
+ return PartialDate.empty;
+ }
+
+ match = _shortFormatRegex.firstMatch(cleanInput);
+ if (match != null) {
+ final p1 = int.parse(match.group(1)!);
+ final p2 = int.parse(match.group(2)!);
+
+ if (p1 > 12) {
+ if (p1 >= 1 && p1 <= 31 && p2 >= 1 && p2 <= 12) {
+ return PartialDate(day: p1, month: p2);
+ }
+ } else if (p2 > 12) {
+ if (p1 >= 1 && p1 <= 12 && p2 >= 1 && p2 <= 31) {
+ return PartialDate(day: p2, month: p1);
+ }
+ } else {
+ if (p1 >= 1 && p1 <= 12 && p2 >= 1 && p2 <= 31) {
+ return PartialDate(day: p2, month: p1);
+ }
+ }
+ return PartialDate.empty;
+ }
+
+ match = _dotFormatRegex.firstMatch(cleanInput);
+ if (match != null) {
+ final yearRaw = int.parse(match.group(3)!);
+ final year = yearRaw > 99 ? yearRaw : _convertTwoDigitYear(yearRaw);
+ final dayVal = int.tryParse(match.group(1)!);
+ final monthVal = int.tryParse(match.group(2)!);
+
+ if (year >= _MIN_YEAR &&
+ year <= _MAX_YEAR &&
+ dayVal != null &&
+ dayVal >= 1 &&
+ dayVal <= 31 &&
+ monthVal != null &&
+ monthVal >= 1 &&
+ monthVal <= 12) {
+ return PartialDate(day: dayVal, month: monthVal, year: year);
+ }
+ return PartialDate.empty;
+ }
+
+ match = _compactFormatRegex.firstMatch(cleanInput);
+ if (match != null) {
+ final yearVal = int.tryParse(cleanInput.substring(0, 4));
+ final monthVal = int.tryParse(cleanInput.substring(4, 6));
+ final dayVal = int.tryParse(cleanInput.substring(6, 8));
+
+ if (yearVal != null &&
+ yearVal >= _MIN_YEAR &&
+ yearVal <= _MAX_YEAR &&
+ monthVal != null &&
+ monthVal >= 1 &&
+ monthVal <= 12 &&
+ dayVal != null &&
+ dayVal >= 1 &&
+ dayVal <= 31) {
+ return PartialDate(day: dayVal, month: monthVal, year: yearVal);
+ }
+ return PartialDate.empty;
+ }
+
+ return PartialDate.empty;
+ }
+
+ PartialDate _parseTokenizedDate(String normalized) {
+ final tokens = normalized.split(' ');
+ int? day, month, year;
+
+ if (tokens.length == 1) {
+ final token = tokens[0];
+ final match = _yearOnlyRegex.firstMatch(token);
+ if (match != null) {
+ final parsedYear = int.tryParse(match.group(1)!);
+ if (parsedYear != null &&
+ parsedYear >= _MIN_YEAR &&
+ parsedYear <= _MAX_YEAR) {
+ return PartialDate(year: parsedYear);
+ }
+ }
+ if (_monthMap.containsKey(token)) {
+ return PartialDate(month: _monthMap[token]!);
+ }
+ final singleValue = int.tryParse(token);
+ if (singleValue != null && singleValue >= 1 && singleValue <= 31) {
+ return PartialDate(day: singleValue);
+ }
+ return PartialDate.empty;
+ }
+
+ for (final token in tokens) {
+ if (_monthMap.containsKey(token) && month == null) {
+ month = _monthMap[token];
+ continue;
+ }
+
+ final value = int.tryParse(token);
+ if (value == null) {
+ continue;
+ }
+
+ if (value >= _MIN_YEAR && value <= _MAX_YEAR && year == null) {
+ year = value;
+ } else if (value >= 1 && value <= 31 && day == null) {
+ day = value;
+ } else if (value >= 0 && value <= 99 && year == null) {
+ final convertedYear = _convertTwoDigitYear(value);
+ if (convertedYear >= _MIN_YEAR && convertedYear <= _MAX_YEAR) {
+ year = convertedYear;
+ }
+ } else if (value >= 1 && value <= 12 && month == null) {
+ month = value;
+ }
+ }
+
+ if (day != null && (day < 1 || day > 31)) {
+ day = null;
+ }
+ if (month != null && (month < 1 || month > 12)) {
+ month = null;
+ }
+
+ final bool inputHadMonthWord = tokens.any((t) => _monthMap.containsKey(t));
+ final bool inputHadYearWord = tokens.any((t) {
+ final v = int.tryParse(t);
+ return v != null && v >= 1000 && v <= 9999;
+ });
+
+ if (day != null && month == null && year == null && tokens.length > 1) {
+ if (normalized.contains('of') &&
+ !inputHadMonthWord &&
+ !inputHadYearWord) {
+ return PartialDate.empty;
+ }
+ if (!inputHadMonthWord && !inputHadYearWord && tokens.length > 1) {}
+ }
+
+ if (day == null && month == null && year == null) {
+ return PartialDate.empty;
+ }
+
+ if (day != null && month == null && year == null && tokens.length > 1) {
+ if (!inputHadMonthWord && !inputHadYearWord) {
+ return PartialDate.empty;
+ }
+ }
+
+ return PartialDate(day: day, month: month, year: year);
+ }
+}
diff --git a/mobile/apps/photos/lib/services/machine_learning/face_ml/face_clustering/face_clustering_service.dart b/mobile/apps/photos/lib/services/machine_learning/face_ml/face_clustering/face_clustering_service.dart
index 2e79215cf1..b11ff67fb5 100644
--- a/mobile/apps/photos/lib/services/machine_learning/face_ml/face_clustering/face_clustering_service.dart
+++ b/mobile/apps/photos/lib/services/machine_learning/face_ml/face_clustering/face_clustering_service.dart
@@ -8,11 +8,11 @@ import "package:ml_linalg/dtype.dart";
import "package:ml_linalg/vector.dart";
import "package:photos/generated/protos/ente/common/vector.pb.dart";
import "package:photos/models/base/id.dart";
-import "package:photos/services/isolate_functions.dart";
-import "package:photos/services/isolate_service.dart";
import "package:photos/services/machine_learning/face_ml/face_clustering/face_db_info_for_clustering.dart";
import "package:photos/services/machine_learning/face_ml/face_filtering/face_filtering_constants.dart";
import "package:photos/services/machine_learning/ml_result.dart";
+import "package:photos/utils/isolate/isolate_operations.dart";
+import "package:photos/utils/isolate/super_isolate.dart";
class FaceInfo {
final String faceID;
@@ -507,7 +507,8 @@ ClusteringResult _runCompleteClustering(Map args) {
EVector.fromBuffer(entry.value).values,
dtype: DType.float32,
),
- fileCreationTime: fileIDToCreationTime?[getFileIdFromFaceId(entry.key)],
+ fileCreationTime:
+ fileIDToCreationTime?[getFileIdFromFaceId(entry.key)],
),
);
}
diff --git a/mobile/apps/photos/lib/services/machine_learning/face_thumbnail_generator.dart b/mobile/apps/photos/lib/services/machine_learning/face_thumbnail_generator.dart
index 4980f2e904..6e09744cff 100644
--- a/mobile/apps/photos/lib/services/machine_learning/face_thumbnail_generator.dart
+++ b/mobile/apps/photos/lib/services/machine_learning/face_thumbnail_generator.dart
@@ -1,15 +1,13 @@
import 'dart:async';
import 'dart:typed_data' show Uint8List;
-import "package:computer/computer.dart";
import "package:logging/logging.dart";
import "package:photos/models/ml/face/box.dart";
-import "package:photos/services/isolate_functions.dart";
-import "package:photos/services/isolate_service.dart";
import "package:photos/utils/image_ml_util.dart";
+import "package:photos/utils/isolate/isolate_operations.dart";
+import "package:photos/utils/isolate/super_isolate.dart";
-final Computer _computer = Computer.shared();
-
+@pragma('vm:entry-point')
class FaceThumbnailGenerator extends SuperIsolate {
@override
Logger get logger => _logger;
@@ -37,20 +35,30 @@ class FaceThumbnailGenerator extends SuperIsolate {
String imagePath,
List faceBoxes,
) async {
- final List