From cd6cf0522704cd2354bad2d877afbae4f39060d8 Mon Sep 17 00:00:00 2001 From: zarzet Date: Tue, 14 Jul 2026 18:50:52 +0700 Subject: [PATCH] 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. --- lib/providers/download_history_provider.dart | 44 +---- lib/providers/extension_provider.dart | 160 +++---------------- lib/providers/local_library_provider.dart | 72 --------- lib/services/history_database.dart | 25 --- lib/services/library_database.dart | 17 -- 5 files changed, 32 insertions(+), 286 deletions(-) diff --git a/lib/providers/download_history_provider.dart b/lib/providers/download_history_provider.dart index 646708b4..73518c4d 100644 --- a/lib/providers/download_history_provider.dart +++ b/lib/providers/download_history_provider.dart @@ -1029,26 +1029,20 @@ class DownloadHistoryNotifier extends Notifier { 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 { }); } - 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 { }); } - Future 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. diff --git a/lib/providers/extension_provider.dart b/lib/providers/extension_provider.dart index db82ae2b..98d18236 100644 --- a/lib/providers/extension_provider.dart +++ b/lib/providers/extension_provider.dart @@ -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 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 { } } - Future 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> getExtensionSettings(String extensionId) async { try { return await PlatformBridge.getExtensionSettings(extensionId); @@ -1674,18 +1576,6 @@ class ExtensionNotifier extends Notifier { await _cleanupExtensions(reason: 'manual'); } - Extension? getExtension(String extensionId) { - try { - return state.extensions.firstWhere((ext) => ext.id == extensionId); - } catch (_) { - return null; - } - } - - List enabledExtensions() { - return state.extensions.where((ext) => ext.enabled).toList(); - } - List getAllDownloadProviders() { return _distinctProviderIds( state.extensions diff --git a/lib/providers/local_library_provider.dart b/lib/providers/local_library_provider.dart index 49e3139c..5e89c9cf 100644 --- a/lib/providers/local_library_provider.dart +++ b/lib/providers/local_library_provider.dart @@ -857,14 +857,6 @@ class LocalLibraryNotifier extends Notifier { await _refreshSummaryFromStorage(); } - bool existsInLibrary({String? isrc, String? trackName, String? artistName}) { - return state.existsInLibrary( - isrc: isrc, - trackName: trackName, - artistName: artistName, - ); - } - Future 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 { return null; } - Future> search(String query) async { - if (query.isEmpty) return []; - - final results = await _db.search(query); - return results.map((e) => LocalLibraryItem.fromJson(e)).toList(); - } - - Future getCount() async { - return await _db.getCount(); - } - Future> _backfillLegacyFileModTimes({ required bool isSaf, required Map existingFiles, @@ -992,59 +973,6 @@ final localLibraryProvider = LocalLibraryNotifier.new, ); -final localLibrarySummaryProvider = Provider((ref) { - return ref.watch(localLibraryProvider); -}); - -class LocalLibraryLookup { - final LibraryDatabase _db; - - const LocalLibraryLookup(this._db); - - Future byId(String id) async { - final json = await _db.getById(id); - return json == null ? null : LocalLibraryItem.fromJson(json); - } - - Future byIsrc(String isrc) async { - final json = await _db.getByIsrc(isrc); - return json == null ? null : LocalLibraryItem.fromJson(json); - } - - Future byTrackAndArtist( - String trackName, - String artistName, - ) async { - final json = await _db.findFirstByTrackAndArtist(trackName, artistName); - return json == null ? null : LocalLibraryItem.fromJson(json); - } - - Future 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((ref) { - ref.watch(localLibraryProvider.select((state) => state.loadedIndexVersion)); - return LocalLibraryLookup(LibraryDatabase.instance); -}); - class LocalLibraryCoverRequest { final String? isrc; final String trackName; diff --git a/lib/services/history_database.dart b/lib/services/history_database.dart index cde69b50..0205d79f 100644 --- a/lib/services/history_database.dart +++ b/lib/services/history_database.dart @@ -765,31 +765,6 @@ class HistoryDatabase { }); } - Future 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((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 clearAll() async { final db = await database; await db.transaction((txn) async { diff --git a/lib/services/library_database.dart b/lib/services/library_database.dart index c30d6584..7f115cfd 100644 --- a/lib/services/library_database.dart +++ b/lib/services/library_database.dart @@ -1862,23 +1862,6 @@ class LibraryDatabase { return Sqflite.firstIntValue(result) ?? 0; } - Future>> 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 close() async { final db = await database; await db.close();