mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-07-28 23:08:59 +02:00
refactor(providers): remove dead lookup/history APIs and dedup provider resolution
Delete the verbatim-duplicate LocalLibraryLookup class + provider, the unused localLibrarySummaryProvider, notifier search()/getCount()/existsInLibrary wrapper, and the now-orphaned LibraryDatabase.search(); the notifier's async getById/findExisting lookups (the real ones) stay. Drop dead download-history removeBySpotifyId/getDatabaseCount and the orphaned history deleteBySpotifyId, and fold the byte-identical addToHistory/ adoptNativeHistoryItem into one _persistHistoryItem keeping distinct log text. Delete dead extension_provider members (ensureSpotifyWebExtensionReady, enabledExtensions, getExtension, resolveProviderDisplayName, URLHandler.matchesURL, Extension.hasBrowseCategories) and collapse resolveEffectiveDownloadService/ MetadataProvider into one predicate-parameterized helper.
This commit is contained in:
@@ -1029,26 +1029,20 @@ class DownloadHistoryNotifier extends Notifier<DownloadHistoryState> {
|
||||
return byId.values.toList(growable: false);
|
||||
}
|
||||
|
||||
void addToHistory(DownloadHistoryItem item) {
|
||||
unawaited(
|
||||
() async {
|
||||
final mergedItem = await _putInMemoryHistory(item);
|
||||
await _db.upsert(mergedItem.toJson());
|
||||
_bumpHistoryRevision();
|
||||
}().catchError((Object e, StackTrace stack) {
|
||||
_historyLog.e('Failed to save to database: $e', e, stack);
|
||||
}),
|
||||
);
|
||||
}
|
||||
void addToHistory(DownloadHistoryItem item) =>
|
||||
_persistHistoryItem(item, 'save to database');
|
||||
|
||||
void adoptNativeHistoryItem(DownloadHistoryItem item) {
|
||||
void adoptNativeHistoryItem(DownloadHistoryItem item) =>
|
||||
_persistHistoryItem(item, 'adopt native history item');
|
||||
|
||||
void _persistHistoryItem(DownloadHistoryItem item, String action) {
|
||||
unawaited(
|
||||
() async {
|
||||
final mergedItem = await _putInMemoryHistory(item);
|
||||
await _db.upsert(mergedItem.toJson());
|
||||
_bumpHistoryRevision();
|
||||
}().catchError((Object e, StackTrace stack) {
|
||||
_historyLog.e('Failed to adopt native history item: $e', e, stack);
|
||||
_historyLog.e('Failed to $action: $e', e, stack);
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -1073,26 +1067,6 @@ class DownloadHistoryNotifier extends Notifier<DownloadHistoryState> {
|
||||
});
|
||||
}
|
||||
|
||||
void removeBySpotifyId(String spotifyId) {
|
||||
state = state.copyWith(
|
||||
items: state.items.where((item) => item.spotifyId != spotifyId).toList(),
|
||||
lookupItems: state.lookupItems
|
||||
.where((item) => item.spotifyId != spotifyId)
|
||||
.toList(growable: false),
|
||||
);
|
||||
unawaited(
|
||||
() async {
|
||||
final deleted = await _db.deleteBySpotifyId(spotifyId);
|
||||
final totalCount = await _db.getCount();
|
||||
state = state.copyWith(totalCount: totalCount);
|
||||
_bumpHistoryRevision();
|
||||
_historyLog.d('Removed $deleted item(s) with spotifyId: $spotifyId');
|
||||
}().catchError((Object e, StackTrace stack) {
|
||||
_historyLog.e('Failed to delete from database: $e', e, stack);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
DownloadHistoryItem? getBySpotifyId(String spotifyId) {
|
||||
return state.getBySpotifyId(spotifyId);
|
||||
}
|
||||
@@ -1521,10 +1495,6 @@ class DownloadHistoryNotifier extends Notifier<DownloadHistoryState> {
|
||||
});
|
||||
}
|
||||
|
||||
Future<int> getDatabaseCount() async {
|
||||
return await _db.getCount();
|
||||
}
|
||||
|
||||
/// Replaces all download history with [items] (each in the
|
||||
/// [DownloadHistoryItem.toJson] shape) from a restored backup, then reloads
|
||||
/// the in-memory state from storage.
|
||||
|
||||
@@ -13,7 +13,6 @@ final _log = AppLogger('ExtensionProvider');
|
||||
|
||||
const _metadataProviderPriorityKey = 'metadata_provider_priority';
|
||||
const _providerPriorityKey = 'provider_priority';
|
||||
const _spotifyWebExtensionId = 'spotify-web';
|
||||
const _storeRegistryUrlPrefKey = 'store_registry_url';
|
||||
|
||||
/// Result of restoring extensions from a backup.
|
||||
@@ -240,7 +239,6 @@ class Extension {
|
||||
bool get hasPostProcessing => postProcessing?.enabled ?? false;
|
||||
bool get hasServiceHealth => serviceHealth.isNotEmpty;
|
||||
bool get hasHomeFeed => capabilities['homeFeed'] == true;
|
||||
bool get hasBrowseCategories => capabilities['browseCategories'] == true;
|
||||
bool get requiresNativeContainerConversion =>
|
||||
capabilities['requiresContainerConversion'] == true ||
|
||||
capabilities['requiresNativeContainerConversion'] == true;
|
||||
@@ -283,61 +281,50 @@ class Extension {
|
||||
String resolveEffectiveDownloadService(
|
||||
String requestedService,
|
||||
ExtensionState extensionState,
|
||||
) {
|
||||
final normalizedRequested = requestedService.trim().toLowerCase();
|
||||
final enabledDownloadExtensions = extensionState.extensions
|
||||
.where((ext) => ext.enabled && ext.hasDownloadProvider)
|
||||
.toList(growable: false);
|
||||
|
||||
if (normalizedRequested.isNotEmpty) {
|
||||
final matchingExtension = enabledDownloadExtensions
|
||||
.where((ext) => ext.id.trim().toLowerCase() == normalizedRequested)
|
||||
.firstOrNull;
|
||||
if (matchingExtension != null) {
|
||||
return matchingExtension.id;
|
||||
}
|
||||
|
||||
final replacementExtension = enabledDownloadExtensions
|
||||
.where(
|
||||
(ext) => ext.replacesBuiltInProviders.contains(normalizedRequested),
|
||||
)
|
||||
.firstOrNull;
|
||||
if (replacementExtension != null) {
|
||||
return replacementExtension.id;
|
||||
}
|
||||
}
|
||||
|
||||
return enabledDownloadExtensions.firstOrNull?.id ?? '';
|
||||
}
|
||||
) => _resolveEffectiveProvider(
|
||||
requestedService,
|
||||
extensionState,
|
||||
(ext) => ext.hasDownloadProvider,
|
||||
);
|
||||
|
||||
String resolveEffectiveMetadataProvider(
|
||||
String requestedProvider,
|
||||
ExtensionState extensionState,
|
||||
) => _resolveEffectiveProvider(
|
||||
requestedProvider,
|
||||
extensionState,
|
||||
(ext) => ext.hasMetadataProvider,
|
||||
);
|
||||
|
||||
String _resolveEffectiveProvider(
|
||||
String requested,
|
||||
ExtensionState extensionState,
|
||||
bool Function(Extension) hasProvider,
|
||||
) {
|
||||
final normalizedRequested = requestedProvider.trim().toLowerCase();
|
||||
final enabledMetadataExtensions = extensionState.extensions
|
||||
.where((ext) => ext.enabled && ext.hasMetadataProvider)
|
||||
final normalizedRequested = requested.trim().toLowerCase();
|
||||
final enabled = extensionState.extensions
|
||||
.where((ext) => ext.enabled && hasProvider(ext))
|
||||
.toList(growable: false);
|
||||
|
||||
if (normalizedRequested.isNotEmpty) {
|
||||
final matchingExtension = enabledMetadataExtensions
|
||||
final matching = enabled
|
||||
.where((ext) => ext.id.trim().toLowerCase() == normalizedRequested)
|
||||
.firstOrNull;
|
||||
if (matchingExtension != null) {
|
||||
return matchingExtension.id;
|
||||
if (matching != null) {
|
||||
return matching.id;
|
||||
}
|
||||
|
||||
final replacementExtension = enabledMetadataExtensions
|
||||
final replacement = enabled
|
||||
.where(
|
||||
(ext) => ext.replacesBuiltInProviders.contains(normalizedRequested),
|
||||
)
|
||||
.firstOrNull;
|
||||
if (replacementExtension != null) {
|
||||
return replacementExtension.id;
|
||||
if (replacement != null) {
|
||||
return replacement.id;
|
||||
}
|
||||
}
|
||||
|
||||
return enabledMetadataExtensions.firstOrNull?.id ?? '';
|
||||
return enabled.firstOrNull?.id ?? '';
|
||||
}
|
||||
|
||||
bool isDeezerCompatibleDownloadService(
|
||||
@@ -358,19 +345,6 @@ bool isDeezerCompatibleDownloadService(
|
||||
);
|
||||
}
|
||||
|
||||
String resolveProviderDisplayName(
|
||||
String providerId, {
|
||||
Iterable<Extension> extensions = const [],
|
||||
}) {
|
||||
for (final extension in extensions) {
|
||||
if (extension.id == providerId) {
|
||||
return extension.displayName;
|
||||
}
|
||||
}
|
||||
|
||||
return providerId;
|
||||
}
|
||||
|
||||
class SearchFilter {
|
||||
final String id;
|
||||
final String? label;
|
||||
@@ -495,16 +469,6 @@ class URLHandler {
|
||||
);
|
||||
}
|
||||
|
||||
bool matchesURL(String url) {
|
||||
if (!enabled || patterns.isEmpty) return false;
|
||||
final lowerUrl = url.toLowerCase();
|
||||
for (final pattern in patterns) {
|
||||
if (lowerUrl.contains(pattern.toLowerCase())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class ExtensionServiceHealthCheck {
|
||||
@@ -1438,68 +1402,6 @@ class ExtensionNotifier extends Notifier<ExtensionState> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> ensureSpotifyWebExtensionReady({
|
||||
bool setAsSearchProvider = true,
|
||||
}) async {
|
||||
try {
|
||||
await refreshExtensions();
|
||||
|
||||
var ext = state.extensions
|
||||
.where((e) => e.id == _spotifyWebExtensionId)
|
||||
.firstOrNull;
|
||||
|
||||
if (ext == null) {
|
||||
final cacheDir = await getTemporaryDirectory();
|
||||
await PlatformBridge.initExtensionRepo(cacheDir.path);
|
||||
|
||||
final tempRoot = await getTemporaryDirectory();
|
||||
final installDir = await Directory(
|
||||
'${tempRoot.path}/spotiflac_bootstrap_spotify_web',
|
||||
).create(recursive: true);
|
||||
|
||||
final downloadPath = await PlatformBridge.downloadRepoExtension(
|
||||
_spotifyWebExtensionId,
|
||||
installDir.path,
|
||||
);
|
||||
|
||||
final installed = await installExtension(downloadPath);
|
||||
if (!installed) {
|
||||
_log.w('Failed to install spotify-web extension from store');
|
||||
return false;
|
||||
}
|
||||
|
||||
await refreshExtensions();
|
||||
ext = state.extensions
|
||||
.where((e) => e.id == _spotifyWebExtensionId)
|
||||
.firstOrNull;
|
||||
}
|
||||
|
||||
if (ext == null) {
|
||||
_log.w('spotify-web extension is still not available after install');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ext.enabled) {
|
||||
await setExtensionEnabled(_spotifyWebExtensionId, true);
|
||||
}
|
||||
|
||||
if (setAsSearchProvider) {
|
||||
final settings = ref.read(settingsProvider);
|
||||
if (settings.searchProvider != _spotifyWebExtensionId) {
|
||||
ref
|
||||
.read(settingsProvider.notifier)
|
||||
.setSearchProvider(_spotifyWebExtensionId);
|
||||
}
|
||||
}
|
||||
|
||||
_log.i('spotify-web extension is ready');
|
||||
return true;
|
||||
} catch (e) {
|
||||
_log.w('Failed to ensure spotify-web extension is ready: $e');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> getExtensionSettings(String extensionId) async {
|
||||
try {
|
||||
return await PlatformBridge.getExtensionSettings(extensionId);
|
||||
@@ -1674,18 +1576,6 @@ class ExtensionNotifier extends Notifier<ExtensionState> {
|
||||
await _cleanupExtensions(reason: 'manual');
|
||||
}
|
||||
|
||||
Extension? getExtension(String extensionId) {
|
||||
try {
|
||||
return state.extensions.firstWhere((ext) => ext.id == extensionId);
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
List<Extension> enabledExtensions() {
|
||||
return state.extensions.where((ext) => ext.enabled).toList();
|
||||
}
|
||||
|
||||
List<String> getAllDownloadProviders() {
|
||||
return _distinctProviderIds(
|
||||
state.extensions
|
||||
|
||||
@@ -857,14 +857,6 @@ class LocalLibraryNotifier extends Notifier<LocalLibraryState> {
|
||||
await _refreshSummaryFromStorage();
|
||||
}
|
||||
|
||||
bool existsInLibrary({String? isrc, String? trackName, String? artistName}) {
|
||||
return state.existsInLibrary(
|
||||
isrc: isrc,
|
||||
trackName: trackName,
|
||||
artistName: artistName,
|
||||
);
|
||||
}
|
||||
|
||||
Future<LocalLibraryItem?> getById(String id) async {
|
||||
final json = await _db.getById(id);
|
||||
return json == null ? null : LocalLibraryItem.fromJson(json);
|
||||
@@ -903,17 +895,6 @@ class LocalLibraryNotifier extends Notifier<LocalLibraryState> {
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<List<LocalLibraryItem>> search(String query) async {
|
||||
if (query.isEmpty) return [];
|
||||
|
||||
final results = await _db.search(query);
|
||||
return results.map((e) => LocalLibraryItem.fromJson(e)).toList();
|
||||
}
|
||||
|
||||
Future<int> getCount() async {
|
||||
return await _db.getCount();
|
||||
}
|
||||
|
||||
Future<Map<String, int>> _backfillLegacyFileModTimes({
|
||||
required bool isSaf,
|
||||
required Map<String, int> existingFiles,
|
||||
@@ -992,59 +973,6 @@ final localLibraryProvider =
|
||||
LocalLibraryNotifier.new,
|
||||
);
|
||||
|
||||
final localLibrarySummaryProvider = Provider<LocalLibraryState>((ref) {
|
||||
return ref.watch(localLibraryProvider);
|
||||
});
|
||||
|
||||
class LocalLibraryLookup {
|
||||
final LibraryDatabase _db;
|
||||
|
||||
const LocalLibraryLookup(this._db);
|
||||
|
||||
Future<LocalLibraryItem?> byId(String id) async {
|
||||
final json = await _db.getById(id);
|
||||
return json == null ? null : LocalLibraryItem.fromJson(json);
|
||||
}
|
||||
|
||||
Future<LocalLibraryItem?> byIsrc(String isrc) async {
|
||||
final json = await _db.getByIsrc(isrc);
|
||||
return json == null ? null : LocalLibraryItem.fromJson(json);
|
||||
}
|
||||
|
||||
Future<LocalLibraryItem?> byTrackAndArtist(
|
||||
String trackName,
|
||||
String artistName,
|
||||
) async {
|
||||
final json = await _db.findFirstByTrackAndArtist(trackName, artistName);
|
||||
return json == null ? null : LocalLibraryItem.fromJson(json);
|
||||
}
|
||||
|
||||
Future<LocalLibraryItem?> existing({
|
||||
String? id,
|
||||
String? isrc,
|
||||
String? trackName,
|
||||
String? artistName,
|
||||
}) async {
|
||||
if (id != null && id.isNotEmpty) {
|
||||
final item = await byId(id);
|
||||
if (item != null) return item;
|
||||
}
|
||||
if (isrc != null && isrc.isNotEmpty) {
|
||||
final item = await byIsrc(isrc);
|
||||
if (item != null) return item;
|
||||
}
|
||||
if (trackName != null && artistName != null) {
|
||||
return byTrackAndArtist(trackName, artistName);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
final localLibraryLookupProvider = Provider<LocalLibraryLookup>((ref) {
|
||||
ref.watch(localLibraryProvider.select((state) => state.loadedIndexVersion));
|
||||
return LocalLibraryLookup(LibraryDatabase.instance);
|
||||
});
|
||||
|
||||
class LocalLibraryCoverRequest {
|
||||
final String? isrc;
|
||||
final String trackName;
|
||||
|
||||
@@ -765,31 +765,6 @@ class HistoryDatabase {
|
||||
});
|
||||
}
|
||||
|
||||
Future<int> deleteBySpotifyId(String spotifyId) async {
|
||||
final db = await database;
|
||||
final rows = await db.query(
|
||||
'history',
|
||||
columns: ['id'],
|
||||
where: 'spotify_id = ?',
|
||||
whereArgs: [spotifyId],
|
||||
);
|
||||
final ids = rows.map((row) => row['id'] as String).toList(growable: false);
|
||||
return db.transaction<int>((txn) async {
|
||||
for (final id in ids) {
|
||||
await txn.delete(
|
||||
'history_path_keys',
|
||||
where: 'item_id = ?',
|
||||
whereArgs: [id],
|
||||
);
|
||||
}
|
||||
return txn.delete(
|
||||
'history',
|
||||
where: 'spotify_id = ?',
|
||||
whereArgs: [spotifyId],
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> clearAll() async {
|
||||
final db = await database;
|
||||
await db.transaction((txn) async {
|
||||
|
||||
@@ -1862,23 +1862,6 @@ class LibraryDatabase {
|
||||
return Sqflite.firstIntValue(result) ?? 0;
|
||||
}
|
||||
|
||||
Future<List<Map<String, dynamic>>> search(
|
||||
String query, {
|
||||
int limit = 50,
|
||||
}) async {
|
||||
final db = await database;
|
||||
final searchQuery = '%${_escapeLikePattern(query.toLowerCase())}%';
|
||||
final rows = await db.query(
|
||||
'library',
|
||||
where:
|
||||
"LOWER(track_name) LIKE ? ESCAPE '\\' OR LOWER(artist_name) LIKE ? ESCAPE '\\' OR LOWER(album_name) LIKE ? ESCAPE '\\'",
|
||||
whereArgs: [searchQuery, searchQuery, searchQuery],
|
||||
orderBy: 'track_name',
|
||||
limit: limit,
|
||||
);
|
||||
return rows.map(_dbRowToJson).toList();
|
||||
}
|
||||
|
||||
Future<void> close() async {
|
||||
final db = await database;
|
||||
await db.close();
|
||||
|
||||
Reference in New Issue
Block a user