diff --git a/database/music_database.py b/database/music_database.py index ef016777..98c13755 100644 --- a/database/music_database.py +++ b/database/music_database.py @@ -1543,6 +1543,29 @@ class MusicDatabase: cursor.execute("CREATE INDEX IF NOT EXISTS idx_tracks_genius_id ON tracks (genius_id)") cursor.execute("CREATE INDEX IF NOT EXISTS idx_tracks_genius_status ON tracks (genius_match_status)") + # One-time reset: clear all Genius matches due to blind-fallback bug in search + # The old search_artist/search_song returned the first result with no name validation, + # causing wrong matches. This reset lets the fixed worker re-enrich everything. + cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='_genius_search_fix_applied'") + if not cursor.fetchone(): + logger.info("Applying one-time Genius search fix: resetting all artist and track matches for re-enrichment") + cursor.execute(""" + UPDATE artists SET + genius_id = NULL, genius_match_status = NULL, genius_last_attempted = NULL, + genius_description = NULL, genius_alt_names = NULL, genius_url = NULL + WHERE genius_match_status IS NOT NULL + """) + artist_count = cursor.rowcount + cursor.execute(""" + UPDATE tracks SET + genius_id = NULL, genius_match_status = NULL, genius_last_attempted = NULL, + genius_lyrics = NULL, genius_description = NULL, genius_url = NULL + WHERE genius_match_status IS NOT NULL + """) + track_count = cursor.rowcount + cursor.execute("CREATE TABLE _genius_search_fix_applied (applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)") + logger.info(f"Genius search fix applied: reset {artist_count} artists and {track_count} tracks") + except Exception as e: logger.error(f"Error adding Last.fm/Genius enrichment columns: {e}") # Don't raise - this is a migration, database can still function diff --git a/webui/static/style.css b/webui/static/style.css index 0dd3993a..2b59a526 100644 --- a/webui/static/style.css +++ b/webui/static/style.css @@ -13495,6 +13495,8 @@ body { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; + position: relative; + cursor: default; } .track-artist { @@ -13504,6 +13506,8 @@ body { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; + position: relative; + cursor: default; } .track-duration { @@ -13521,6 +13525,8 @@ body { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; + position: relative; + cursor: default; } .match-found { @@ -13543,6 +13549,8 @@ body { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; + position: relative; + cursor: default; } .download-searching { @@ -13561,6 +13569,47 @@ body { color: #f44336; } +/* Hover/tap tooltip for truncated track and artist names */ +.track-name:hover::after, +.track-artist:hover::after, +.track-match-status:hover::after, +.track-download-status:hover::after { + content: attr(title); + position: absolute; + left: 0; + bottom: calc(100% + 4px); + background: rgba(20, 20, 20, 0.95); + color: #ffffff; + padding: 8px 12px; + border-radius: 8px; + font-size: 12px; + font-weight: 500; + white-space: normal; + word-break: break-word; + max-width: 320px; + min-width: 120px; + z-index: 100; + pointer-events: none; + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.5); + border: 1px solid rgba(255, 255, 255, 0.1); + line-height: 1.4; +} + +/* Only show tooltip when content is actually truncated (has title attr) */ +.track-name:hover::after, +.track-artist:hover::after, +.track-match-status:hover::after, +.track-download-status:hover::after { + content: attr(title); +} + +.track-name:not([title]):hover::after, +.track-artist:not([title]):hover::after, +.track-match-status:not([title]):hover::after, +.track-download-status:not([title]):hover::after { + display: none; +} + .track-actions { text-align: center; width: 7%;