mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-10-02 22:26:52 +02:00
refactor(library): remove unused history lookups and replacement paths
This commit is contained in:
1 parent
377710ec22
commit
b3f7f59e2e
3 files changed
+4
-168
No files matched your search
@@ -622,10 +622,7 @@ class DownloadHistoryNotifier extends Notifier<DownloadHistoryState> {
|
||||
'.mp4',
|
||||
];
|
||||
|
||||
Future<String?> _findConvertedSibling(
|
||||
String originalPath, {
|
||||
bool includeAlternateExtensions = true,
|
||||
}) async {
|
||||
Future<String?> _findConvertedSibling(String originalPath) async {
|
||||
final dotIndex = originalPath.lastIndexOf('.');
|
||||
if (dotIndex < 0) return null;
|
||||
final directoryPrefix = originalPath.substring(
|
||||
@@ -638,7 +635,7 @@ class DownloadHistoryNotifier extends Notifier<DownloadHistoryState> {
|
||||
|
||||
for (final candidateName in _conversionRenameCandidates(
|
||||
fileName,
|
||||
includeAlternateExtensions: includeAlternateExtensions,
|
||||
includeAlternateExtensions: true,
|
||||
)) {
|
||||
final candidatePath = '$directoryPrefix$candidateName';
|
||||
if (candidatePath == originalPath) continue;
|
||||
@@ -649,67 +646,6 @@ class DownloadHistoryNotifier extends Notifier<DownloadHistoryState> {
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<bool> verifyOrRepairHistoryItem(DownloadHistoryItem item) async {
|
||||
if (await fileExists(item.filePath)) return true;
|
||||
|
||||
DownloadHistoryItem? repaired;
|
||||
if (item.storageMode == 'saf' &&
|
||||
item.downloadTreeUri != null &&
|
||||
item.downloadTreeUri!.isNotEmpty) {
|
||||
var fileName = (item.safFileName ?? '').trim();
|
||||
if (fileName.isEmpty && isContentUri(item.filePath)) {
|
||||
fileName = _fileNameFromUri(item.filePath);
|
||||
}
|
||||
for (final candidate in _conversionRenameCandidates(fileName)) {
|
||||
try {
|
||||
final resolved = await PlatformBridge.resolveSafFile(
|
||||
treeUri: item.downloadTreeUri!,
|
||||
relativeDir: item.safRelativeDir ?? '',
|
||||
fileName: candidate,
|
||||
);
|
||||
final uri = (resolved['uri'] as String? ?? '').trim();
|
||||
if (uri.isEmpty || !await fileExists(uri)) continue;
|
||||
final relativeDir = (resolved['relative_dir'] as String? ?? '')
|
||||
.trim();
|
||||
repaired = item.copyWith(
|
||||
filePath: uri,
|
||||
safFileName: candidate,
|
||||
safRelativeDir: relativeDir.isEmpty
|
||||
? item.safRelativeDir
|
||||
: relativeDir,
|
||||
safRepaired: true,
|
||||
);
|
||||
break;
|
||||
} catch (error) {
|
||||
_historyLog.w('Failed to resolve renamed SAF file: $error');
|
||||
}
|
||||
}
|
||||
} else if (!isContentUri(item.filePath)) {
|
||||
final sibling = await _findConvertedSibling(
|
||||
item.filePath,
|
||||
includeAlternateExtensions: false,
|
||||
);
|
||||
if (sibling != null) repaired = item.copyWith(filePath: sibling);
|
||||
}
|
||||
|
||||
if (repaired == null) return false;
|
||||
await _db.upsert(repaired.toJson());
|
||||
final updatedItems = state.items
|
||||
.map((entry) => entry.id == repaired!.id ? repaired : entry)
|
||||
.toList(growable: false);
|
||||
final updatedLookupItems = state.lookupItems
|
||||
.map((entry) => entry.id == repaired!.id ? repaired : entry)
|
||||
.toList(growable: false);
|
||||
state = state.copyWith(
|
||||
items: updatedItems,
|
||||
lookupItems: updatedLookupItems,
|
||||
);
|
||||
_historyLog.i(
|
||||
'Reconciled renamed conversion: ${item.filePath} -> ${repaired.filePath}',
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
Future<
|
||||
({
|
||||
List<String> orphanedIds,
|
||||
@@ -1090,28 +1026,6 @@ final downloadHistoryProvider =
|
||||
DownloadHistoryNotifier.new,
|
||||
);
|
||||
|
||||
class DownloadHistoryGroupedCounts {
|
||||
final int albumCount;
|
||||
final int singleTrackCount;
|
||||
|
||||
const DownloadHistoryGroupedCounts({
|
||||
required this.albumCount,
|
||||
required this.singleTrackCount,
|
||||
});
|
||||
}
|
||||
|
||||
final downloadHistoryGroupedCountsProvider =
|
||||
FutureProvider<DownloadHistoryGroupedCounts>((ref) async {
|
||||
ref.watch(
|
||||
downloadHistoryProvider.select((state) => state.loadedIndexVersion),
|
||||
);
|
||||
final counts = await HistoryDatabase.instance.getGroupedCounts();
|
||||
return DownloadHistoryGroupedCounts(
|
||||
albumCount: counts['albums'] ?? 0,
|
||||
singleTrackCount: counts['singles'] ?? 0,
|
||||
);
|
||||
});
|
||||
|
||||
HistoryLookupRequest historyLookupForTrack(Track track) {
|
||||
return HistoryLookupRequest(
|
||||
spotifyId: track.id,
|
||||
@@ -1121,21 +1035,8 @@ HistoryLookupRequest historyLookupForTrack(Track track) {
|
||||
);
|
||||
}
|
||||
|
||||
final downloadHistoryExistsProvider = FutureProvider.autoDispose
|
||||
.family<bool, HistoryLookupRequest>((ref, request) async {
|
||||
ref.watch(
|
||||
downloadHistoryProvider.select((state) => state.loadedIndexVersion),
|
||||
);
|
||||
final notifier = ref.read(downloadHistoryProvider.notifier);
|
||||
final row = await HistoryDatabase.instance.findExistingTrack(request);
|
||||
if (row == null) return false;
|
||||
return notifier.verifyOrRepairHistoryItem(
|
||||
DownloadHistoryItem.fromJson(row),
|
||||
);
|
||||
});
|
||||
|
||||
// Batch lookups deliberately avoid per-row SAF verification. Startup repair
|
||||
// reconciles stale rows; the single-track provider above keeps strict checks.
|
||||
// Batch lookups deliberately avoid per-row SAF verification; startup repair
|
||||
// reconciles stale rows.
|
||||
final downloadHistoryBatchExistsProvider = FutureProvider.autoDispose
|
||||
.family<Set<String>, HistoryBatchLookupRequest>((ref, request) async {
|
||||
ref.watch(
|
||||
|
||||
@@ -1078,25 +1078,6 @@ class HistoryDatabase {
|
||||
return Sqflite.firstIntValue(result) ?? 0;
|
||||
}
|
||||
|
||||
Future<Map<String, int>> getGroupedCounts() async {
|
||||
final db = await database;
|
||||
final rows = await db.rawQuery('''
|
||||
SELECT
|
||||
SUM(CASE WHEN track_count > 1 THEN 1 ELSE 0 END) AS albums,
|
||||
SUM(CASE WHEN track_count = 1 THEN 1 ELSE 0 END) AS singles
|
||||
FROM (
|
||||
SELECT COUNT(*) AS track_count
|
||||
FROM history
|
||||
GROUP BY album_key
|
||||
)
|
||||
''');
|
||||
final row = rows.isEmpty ? const <String, Object?>{} : rows.first;
|
||||
return {
|
||||
'albums': (row['albums'] as num?)?.toInt() ?? 0,
|
||||
'singles': (row['singles'] as num?)?.toInt() ?? 0,
|
||||
};
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>?> findExisting({
|
||||
String? spotifyId,
|
||||
String? isrc,
|
||||
|
||||
@@ -1006,52 +1006,6 @@ class LibraryDatabase {
|
||||
_log.i('Replaced library with ${items.length} items');
|
||||
}
|
||||
|
||||
/// Atomically replaces the Library while consuming bounded scan batches.
|
||||
/// The stream may represent tens of thousands of tracks without requiring a
|
||||
/// second full list of models/maps on the Dart heap.
|
||||
Future<int> replaceAllStream(
|
||||
Stream<Map<String, dynamic>> items, {
|
||||
int batchSize = 300,
|
||||
}) async {
|
||||
if (batchSize <= 0) {
|
||||
throw ArgumentError.value(batchSize, 'batchSize', 'Must be positive');
|
||||
}
|
||||
final db = await database;
|
||||
var inserted = 0;
|
||||
await db.transaction((txn) async {
|
||||
await txn.delete('library_path_keys');
|
||||
await txn.delete('library');
|
||||
|
||||
var batch = txn.batch();
|
||||
var pending = 0;
|
||||
Future<void> flush() async {
|
||||
if (pending == 0) return;
|
||||
await batch.commit(noResult: true);
|
||||
batch = txn.batch();
|
||||
pending = 0;
|
||||
}
|
||||
|
||||
await for (final json in items) {
|
||||
final id = json['id'] as String?;
|
||||
if (id == null || id.trim().isEmpty) {
|
||||
throw const FormatException('Library scan row has no valid id');
|
||||
}
|
||||
batch.insert(
|
||||
'library',
|
||||
_jsonToDbRow(json),
|
||||
conflictAlgorithm: ConflictAlgorithm.replace,
|
||||
);
|
||||
_putPathKeysInBatch(batch, id, json['filePath'] as String?);
|
||||
inserted++;
|
||||
pending++;
|
||||
if (pending >= batchSize) await flush();
|
||||
}
|
||||
await flush();
|
||||
});
|
||||
_log.i('Stream-replaced library with $inserted items');
|
||||
return inserted;
|
||||
}
|
||||
|
||||
/// Stages scan rows in bounded, independently committed batches, then swaps
|
||||
/// only this source in one short transaction. Download-history exclusion is
|
||||
/// an indexed SQLite anti-join, avoiding a full History path set in Dart.
|
||||
|
||||
Reference in new issue
Block a user