slskd search: supply our own search id → fixes 'search didn't run'
root cause of the fast 'no results' / 'search didn't run': slskd WAS creating the searches (they showed up + returned results), but start_search couldn't read the search id back out of the POST response, returned no id, and the caller bailed instantly — then raced to the next, which is why slskd showed different titles than the log. fix: generate the search id client-side and pass it to slskd (it honors a supplied id), so we never depend on parsing the response. still prefer slskd's echoed id if present (dict/list/bare-string), fall back to ours. also send Accept: application/json so slskd answers in json.
This commit is contained in:
parent
eb8498a792
commit
1b3c18e4e6
1 changed files with 28 additions and 10 deletions
|
|
@ -30,7 +30,10 @@ def _conn():
|
||||||
from config.settings import config_manager
|
from config.settings import config_manager
|
||||||
base = str(config_manager.get("soulseek.slskd_url", "") or "").rstrip("/")
|
base = str(config_manager.get("soulseek.slskd_url", "") or "").rstrip("/")
|
||||||
key = config_manager.get("soulseek.api_key", "") or ""
|
key = config_manager.get("soulseek.api_key", "") or ""
|
||||||
return base, ({"X-API-Key": key} if key else {})
|
headers = {"Accept": "application/json"} # make slskd answer in JSON, not whatever default
|
||||||
|
if key:
|
||||||
|
headers["X-API-Key"] = key
|
||||||
|
return base, headers
|
||||||
|
|
||||||
|
|
||||||
def build_query(scope: str, title: Any, *, year: Any = None, season: Any = None,
|
def build_query(scope: str, title: Any, *, year: Any = None, season: Any = None,
|
||||||
|
|
@ -128,23 +131,38 @@ def search_timeout_ms() -> int:
|
||||||
|
|
||||||
def start_search(query: str) -> dict:
|
def start_search(query: str) -> dict:
|
||||||
"""Kick off a slskd search (don't wait). Returns {configured[, id][, error]}.
|
"""Kick off a slskd search (don't wait). Returns {configured[, id][, error]}.
|
||||||
The caller polls ``poll_responses(id)`` until satisfied (like the music side)."""
|
The caller polls ``poll_responses(id)`` until satisfied (like the music side).
|
||||||
|
|
||||||
|
We generate the search id OURSELVES and pass it to slskd — it honors a client-supplied
|
||||||
|
``id`` — so we never depend on parsing it back out of the POST response. Some slskd builds
|
||||||
|
return the id in a Location header / a non-dict body, which made us think the search
|
||||||
|
'didn't run' even though slskd created it (the bug behind the fast 'no results')."""
|
||||||
|
import uuid
|
||||||
base, headers = _conn()
|
base, headers = _conn()
|
||||||
if not base:
|
if not base:
|
||||||
return {"configured": False}
|
return {"configured": False}
|
||||||
payload = {"searchText": query, "timeout": search_timeout_ms(), "filterResponses": True,
|
search_id = str(uuid.uuid4())
|
||||||
"minimumResponseFileCount": 1, "minimumPeerUploadSpeed": _min_speed_bytes()}
|
payload = {"id": search_id, "searchText": query, "timeout": search_timeout_ms(),
|
||||||
|
"filterResponses": True, "minimumResponseFileCount": 1,
|
||||||
|
"minimumPeerUploadSpeed": _min_speed_bytes()}
|
||||||
try:
|
try:
|
||||||
r = requests.post(base + "/api/v0/searches", json=payload, headers=headers, timeout=15)
|
r = requests.post(base + "/api/v0/searches", json=payload, headers=headers, timeout=15)
|
||||||
r.raise_for_status()
|
r.raise_for_status()
|
||||||
data = r.json()
|
|
||||||
except Exception as e: # noqa: BLE001 - surface any slskd/network failure to the UI
|
except Exception as e: # noqa: BLE001 - surface any slskd/network failure to the UI
|
||||||
return {"configured": True, "error": str(e)}
|
return {"configured": True, "error": str(e)}
|
||||||
sid = data.get("id") if isinstance(data, dict) else (
|
# Prefer the id slskd echoes back if present; otherwise the one we supplied (it's honored).
|
||||||
data[0].get("id") if isinstance(data, list) and data and isinstance(data[0], dict) else None)
|
sid = None
|
||||||
if not sid:
|
try:
|
||||||
return {"configured": True, "error": "slskd returned no search id"}
|
data = r.json()
|
||||||
return {"configured": True, "id": sid}
|
if isinstance(data, dict):
|
||||||
|
sid = data.get("id")
|
||||||
|
elif isinstance(data, str) and data.strip():
|
||||||
|
sid = data.strip().strip('"')
|
||||||
|
elif isinstance(data, list) and data and isinstance(data[0], dict):
|
||||||
|
sid = data[0].get("id")
|
||||||
|
except Exception: # noqa: BLE001, S110 - non-JSON / empty body → fall back to our id
|
||||||
|
pass
|
||||||
|
return {"configured": True, "id": sid or search_id}
|
||||||
|
|
||||||
|
|
||||||
def stop_search(search_id) -> None:
|
def stop_search(search_id) -> None:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue