Add metadata source filter to library and fix Discogs enrichment
Library page: new dropdown filter to show artists matched or unmatched to any metadata source (Spotify, MusicBrainz, Deezer, Discogs, etc). Select "No Discogs" to find artists needing manual Discogs matching. Filter applied as WHERE clause on the source ID columns. Discogs enrichment: added to valid_services whitelist, _enrichment_locks, and _run_single_enrichment handler. The Enrich button was returning an error when Discogs was selected from the dropdown.
This commit is contained in:
parent
d597123a40
commit
e65b6bab67
5 changed files with 113 additions and 3 deletions
|
|
@ -7905,7 +7905,7 @@ class MusicDatabase:
|
|||
'server_source': server_source
|
||||
}
|
||||
|
||||
def get_library_artists(self, search_query: str = "", letter: str = "", page: int = 1, limit: int = 50, watchlist_filter: str = "all", profile_id: int = 1) -> Dict[str, Any]:
|
||||
def get_library_artists(self, search_query: str = "", letter: str = "", page: int = 1, limit: int = 50, watchlist_filter: str = "all", profile_id: int = 1, source_filter: str = "") -> Dict[str, Any]:
|
||||
"""
|
||||
Get artists for the library page with search, filtering, and pagination
|
||||
|
||||
|
|
@ -7915,6 +7915,7 @@ class MusicDatabase:
|
|||
page: Page number (1-based)
|
||||
limit: Number of results per page
|
||||
watchlist_filter: Filter by watchlist status ("all", "watched", "unwatched")
|
||||
source_filter: Filter by metadata source match (e.g. "spotify", "!spotify" for unmatched)
|
||||
|
||||
Returns:
|
||||
Dict containing artists list, pagination info, and total count
|
||||
|
|
@ -7940,6 +7941,29 @@ class MusicDatabase:
|
|||
where_conditions.append("UPPER(SUBSTR(name, 1, 1)) = UPPER(?)")
|
||||
params.append(letter)
|
||||
|
||||
# Metadata source filter — match or exclude by enrichment source
|
||||
if source_filter:
|
||||
_source_columns = {
|
||||
'spotify': 'a.spotify_artist_id',
|
||||
'musicbrainz': 'a.musicbrainz_id',
|
||||
'deezer': 'a.deezer_id',
|
||||
'discogs': 'a.discogs_id',
|
||||
'audiodb': 'a.audiodb_id',
|
||||
'itunes': 'a.itunes_artist_id',
|
||||
'lastfm': 'a.lastfm_url',
|
||||
'genius': 'a.genius_url',
|
||||
'tidal': 'a.tidal_id',
|
||||
'qobuz': 'a.qobuz_id',
|
||||
}
|
||||
negate = source_filter.startswith('!')
|
||||
key = source_filter.lstrip('!')
|
||||
col = _source_columns.get(key)
|
||||
if col:
|
||||
if negate:
|
||||
where_conditions.append(f"({col} IS NULL OR {col} = '')")
|
||||
else:
|
||||
where_conditions.append(f"({col} IS NOT NULL AND {col} != '')")
|
||||
|
||||
# Get active server for filtering
|
||||
from config.settings import config_manager
|
||||
active_server = config_manager.get_active_media_server()
|
||||
|
|
|
|||
|
|
@ -9365,6 +9365,7 @@ def get_library_artists():
|
|||
page = int(request.args.get('page', 1))
|
||||
limit = int(request.args.get('limit', 75))
|
||||
watchlist_filter = request.args.get('watchlist', 'all')
|
||||
source_filter = request.args.get('source_filter', '')
|
||||
|
||||
# Get database instance
|
||||
database = get_database()
|
||||
|
|
@ -9376,7 +9377,8 @@ def get_library_artists():
|
|||
page=page,
|
||||
limit=limit,
|
||||
watchlist_filter=watchlist_filter,
|
||||
profile_id=get_current_profile_id()
|
||||
profile_id=get_current_profile_id(),
|
||||
source_filter=source_filter
|
||||
)
|
||||
|
||||
# Fix image URLs for all artists
|
||||
|
|
|
|||
|
|
@ -2633,6 +2633,37 @@
|
|||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Metadata Source Filter -->
|
||||
<div class="library-source-filter">
|
||||
<select id="library-source-filter" class="library-source-filter-select">
|
||||
<option value="">All Sources</option>
|
||||
<optgroup label="Unmatched to">
|
||||
<option value="!spotify">No Spotify</option>
|
||||
<option value="!musicbrainz">No MusicBrainz</option>
|
||||
<option value="!deezer">No Deezer</option>
|
||||
<option value="!discogs">No Discogs</option>
|
||||
<option value="!audiodb">No AudioDB</option>
|
||||
<option value="!itunes">No iTunes</option>
|
||||
<option value="!lastfm">No Last.fm</option>
|
||||
<option value="!genius">No Genius</option>
|
||||
<option value="!tidal">No Tidal</option>
|
||||
<option value="!qobuz">No Qobuz</option>
|
||||
</optgroup>
|
||||
<optgroup label="Matched to">
|
||||
<option value="spotify">Has Spotify</option>
|
||||
<option value="musicbrainz">Has MusicBrainz</option>
|
||||
<option value="deezer">Has Deezer</option>
|
||||
<option value="discogs">Has Discogs</option>
|
||||
<option value="audiodb">Has AudioDB</option>
|
||||
<option value="itunes">Has iTunes</option>
|
||||
<option value="lastfm">Has Last.fm</option>
|
||||
<option value="genius">Has Genius</option>
|
||||
<option value="tidal">Has Tidal</option>
|
||||
<option value="qobuz">Has Qobuz</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Alphabet Selector -->
|
||||
<div class="alphabet-selector" id="alphabet-selector">
|
||||
<div class="alphabet-selector-inner">
|
||||
|
|
|
|||
|
|
@ -41208,7 +41208,8 @@ const libraryPageState = {
|
|||
currentPage: 1,
|
||||
limit: 75,
|
||||
debounceTimer: null,
|
||||
watchlistFilter: "all"
|
||||
watchlistFilter: "all",
|
||||
sourceFilter: ""
|
||||
};
|
||||
|
||||
function initializeLibraryPage() {
|
||||
|
|
@ -41221,6 +41222,9 @@ function initializeLibraryPage() {
|
|||
// Initialize watchlist filter
|
||||
initializeWatchlistFilter();
|
||||
|
||||
// Initialize metadata source filter
|
||||
initializeSourceFilter();
|
||||
|
||||
// Initialize alphabet selector
|
||||
initializeAlphabetSelector();
|
||||
|
||||
|
|
@ -41302,6 +41306,16 @@ function initializeWatchlistFilter() {
|
|||
});
|
||||
}
|
||||
|
||||
function initializeSourceFilter() {
|
||||
const select = document.getElementById('library-source-filter');
|
||||
if (!select) return;
|
||||
select.addEventListener('change', () => {
|
||||
libraryPageState.sourceFilter = select.value;
|
||||
libraryPageState.currentPage = 1;
|
||||
loadLibraryArtists();
|
||||
});
|
||||
}
|
||||
|
||||
function initializeAlphabetSelector() {
|
||||
const alphabetButtons = document.querySelectorAll(".alphabet-btn");
|
||||
|
||||
|
|
@ -41355,6 +41369,7 @@ async function loadLibraryArtists() {
|
|||
limit: libraryPageState.limit,
|
||||
watchlist: libraryPageState.watchlistFilter
|
||||
});
|
||||
if (libraryPageState.sourceFilter) params.set('source_filter', libraryPageState.sourceFilter);
|
||||
|
||||
// Fetch artists from API
|
||||
const response = await fetch(`/api/library/artists?${params}`);
|
||||
|
|
|
|||
|
|
@ -21798,6 +21798,44 @@ body.helper-mode-active #dashboard-activity-feed:hover {
|
|||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.library-source-filter {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 0 0 4px;
|
||||
}
|
||||
.library-source-filter-select {
|
||||
padding: 6px 30px 6px 12px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='rgba(255,255,255,0.35)' d='M2 4l4 4 4-4'/%3E%3C/svg%3E");
|
||||
background-repeat: no-repeat;
|
||||
background-position: right 10px center;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
font-family: inherit;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.2s, color 0.2s;
|
||||
min-width: 140px;
|
||||
}
|
||||
.library-source-filter-select:hover {
|
||||
border-color: rgba(255, 255, 255, 0.2);
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
.library-source-filter-select:focus {
|
||||
border-color: rgba(var(--accent-rgb), 0.4);
|
||||
outline: none;
|
||||
}
|
||||
.library-source-filter-select option,
|
||||
.library-source-filter-select optgroup {
|
||||
background: #1a1a1e;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.watchlist-filter-btn {
|
||||
padding: 6px 16px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
|
|
|
|||
Loading…
Reference in a new issue