You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
SoulSync/core/search/basic.py

80 lines
2.8 KiB

"""Basic download-source file search — flat list of file results sorted by quality.
Used by the basic search UI on the Search page and by ``/api/search``.
``run_basic_search`` replaced ``run_basic_soulseek_search`` so the caller
can target any active download source (not just slskd). The old name is
kept as a thin alias for backwards compat with any callers outside this
module.
"""
from __future__ import annotations
import logging
from typing import Callable, Optional
logger = logging.getLogger(__name__)
def run_basic_search(
query: str,
download_orchestrator,
run_async: Callable,
*,
source: Optional[str] = None,
) -> list[dict]:
"""Search ``source`` (or the active/first hybrid source) for ``query``.
Returns dicts with ``result_type`` set to ``"album"`` or ``"track"``
and sorted by ``quality_score`` descending. Empty list on any failure.
Parameters
----------
source:
Optional source name to override the orchestrator's default selection.
Must be a canonical name from ``DownloadPluginRegistry`` (e.g.
``"soulseek"``, ``"tidal"``, ``"qobuz"``). When ``None``, behaviour
is unchanged from before: orchestrator.search() picks the active
source (single mode) or the first in chain (hybrid).
"""
if source and download_orchestrator:
# Target a specific source: resolve the client and call search()
# directly instead of going through the orchestrator chain.
try:
client = download_orchestrator.client(source)
except Exception as exc:
logger.warning("basic search: could not resolve client for %r: %s", source, exc)
client = None
if client is None:
logger.warning("basic search: no client for source %r — falling back to orchestrator", source)
tracks, albums = run_async(download_orchestrator.search(query))
else:
logger.info("basic search: targeting %r for %r", source, query)
tracks, albums = run_async(client.search(query))
else:
tracks, albums = run_async(download_orchestrator.search(query))
processed_albums = []
for album in albums:
album_dict = album.__dict__.copy()
album_dict['tracks'] = [track.__dict__ for track in album.tracks]
album_dict['result_type'] = 'album'
processed_albums.append(album_dict)
processed_tracks = []
for track in tracks:
track_dict = track.__dict__.copy()
track_dict['result_type'] = 'track'
processed_tracks.append(track_dict)
return sorted(
processed_albums + processed_tracks,
key=lambda x: x.get('quality_score', 0),
reverse=True,
)
# Backwards-compat alias for any callers that haven't been updated yet.
run_basic_soulseek_search = run_basic_search