Fix library playback from search — album art, double-fire, path resolution

- Strip cloned inline onclick on global search play button swap to prevent
  simultaneous stream search + library play
- Include album thumb_url in library-check response and resolve relative
  Plex paths to full URLs with base URL + token
- Pass album art through to playLibraryTrack from both global and enhanced
  search library check handlers
- Add Plex music library locations as candidate dirs in _resolve_library_file_path
- Remove debug console.log from _gsDeactivate
This commit is contained in:
Broque Thomas 2026-04-01 22:56:06 -07:00
parent 13629720e8
commit 800727299f
2 changed files with 33 additions and 7 deletions

View file

@ -7404,13 +7404,13 @@ def enhanced_search_library_check():
owned_albums = {r[0] for r in cursor.fetchall()}
cursor.execute("""
SELECT LOWER(t.title) || '|||' || LOWER(a.name), t.id, t.file_path, t.title, a.name, al.title
SELECT LOWER(t.title) || '|||' || LOWER(a.name), t.id, t.file_path, t.title, a.name, al.title, al.thumb_url
FROM tracks t JOIN artists a ON a.id = t.artist_id JOIN albums al ON al.id = t.album_id
""")
owned_tracks = {}
for r in cursor.fetchall():
if r[0] not in owned_tracks: # Keep first match only
owned_tracks[r[0]] = {'track_id': r[1], 'file_path': r[2], 'title': r[3], 'artist_name': r[4], 'album_title': r[5]}
owned_tracks[r[0]] = {'track_id': r[1], 'file_path': r[2], 'title': r[3], 'artist_name': r[4], 'album_title': r[5], 'album_thumb_url': r[6]}
# O(1) lookups per item
album_results = []
@ -7418,11 +7418,27 @@ def enhanced_search_library_check():
key = (a.get('name', '').lower() + '|||' + a.get('artist', '').split(',')[0].strip().lower())
album_results.append(key in owned_albums)
# Resolve Plex thumb URLs (relative paths need base URL + token)
_plex_base = ''
_plex_token = ''
if plex_client and plex_client.server:
_plex_base = getattr(plex_client.server, '_baseurl', '') or ''
_plex_token = getattr(plex_client.server, '_token', '') or ''
if not _plex_base:
_pc = config_manager.get_plex_config()
_plex_base = (_pc.get('base_url', '') or '').rstrip('/')
_plex_token = _plex_token or _pc.get('token', '')
track_results = []
for t in tracks:
key = (t.get('name', '').lower() + '|||' + t.get('artist', '').split(',')[0].strip().lower())
match = owned_tracks.get(key)
if match:
# Resolve thumb URL
thumb = match.get('album_thumb_url') or ''
if thumb and not thumb.startswith('http') and _plex_base and thumb.startswith('/'):
thumb = f"{_plex_base}{thumb}?X-Plex-Token={_plex_token}" if _plex_token else f"{_plex_base}{thumb}"
match['album_thumb_url'] = thumb
track_results.append({'in_library': True, **match})
else:
track_results.append({'in_library': False})
@ -12234,14 +12250,24 @@ def _resolve_library_file_path(file_path):
return file_path
# Try resolving server-side paths to local directories
# Check both transfer path (final music location) and download path (intermediate)
# Check transfer path, download path, and media server library paths
transfer_dir = docker_resolve_path(config_manager.get('soulseek.transfer_path', './Transfer'))
download_dir = docker_resolve_path(config_manager.get('soulseek.download_path', './downloads'))
# Also check the media server's music library path (handles Docker↔host path mismatch)
library_dirs = set()
try:
if plex_client and plex_client.server and plex_client.music_library:
for loc in plex_client.music_library.locations:
library_dirs.add(loc)
except Exception:
pass
path_parts = file_path.replace('\\', '/').split('/')
# Try progressively shorter path suffixes against each candidate directory
# (skip index 0 to avoid drive letter issues)
for base_dir in [transfer_dir, download_dir]:
for base_dir in [transfer_dir, download_dir] + list(library_dirs):
if not base_dir or not os.path.isdir(base_dir):
continue
for i in range(1, len(path_parts)):

View file

@ -8269,7 +8269,7 @@ function initializeSearchModeToggle() {
newBtn.addEventListener('click', (e) => {
e.stopPropagation();
playLibraryTrack(
{ id: trackInfo.track_id, title: trackInfo.title, file_path: trackInfo.file_path },
{ id: trackInfo.track_id, title: trackInfo.title, file_path: trackInfo.file_path, _stats_image: trackInfo.album_thumb_url || null },
trackInfo.album_title || '',
trackInfo.artist_name || ''
);
@ -16921,7 +16921,6 @@ function _gsUpdateVisibility() {
}
function _gsDeactivate() {
console.log('[GSearch] _gsDeactivate called', new Error().stack.split('\n')[2]?.trim());
const bar = document.getElementById('gsearch-bar');
const shortcut = document.getElementById('gsearch-shortcut');
if (bar) bar.classList.remove('active');
@ -17243,13 +17242,14 @@ async function _gsLibraryCheck() {
const playBtn = el.querySelector('.gsearch-play-btn');
if (playBtn) {
const newBtn = playBtn.cloneNode(true);
newBtn.removeAttribute('onclick');
newBtn.title = 'Play from library';
newBtn.style.background = 'rgba(76,175,80,0.15)';
newBtn.style.color = '#4caf50';
newBtn.addEventListener('click', e => {
e.stopPropagation();
playLibraryTrack(
{ id: tr.track_id, title: tr.title, file_path: tr.file_path },
{ id: tr.track_id, title: tr.title, file_path: tr.file_path, _stats_image: tr.album_thumb_url || null },
tr.album_title || '',
tr.artist_name || ''
);