feat: rework Lidarr library integration
This commit is contained in:
@@ -40,15 +40,29 @@ def _hit_public(hit) -> dict:
|
||||
|
||||
|
||||
def _job_public(job) -> dict:
|
||||
return {"message": job.message, "job_id": job.id, "status": job.status,
|
||||
body = {"message": job.message, "job_id": job.id, "status": job.status,
|
||||
"hit": _hit_public(job.hit) if job.hit is not None else None,
|
||||
"result": job.result, "error": job.error}
|
||||
# Only a parked job carries choices; keep the key out of every other body.
|
||||
if job.choices:
|
||||
body["choices"] = job.choices
|
||||
return body
|
||||
|
||||
|
||||
@app.post("/fetch", dependencies=[Depends(require_key)])
|
||||
def fetch(q: str = Query(..., min_length=1),
|
||||
quality: str = Query("best"),
|
||||
source: str = Query("auto")):
|
||||
source: str = Query("auto"),
|
||||
auto: bool = Query(False),
|
||||
limit: int = Query(10, ge=1, le=25)):
|
||||
"""The single entry point.
|
||||
|
||||
A URL is grabbed directly — no prompt. Free text goes through Lidarr: the
|
||||
artist and album land in the library, Lidarr runs an interactive search, and
|
||||
the job parks at `awaiting_choice` with the releases. Pick one with
|
||||
POST /choose. `auto=true` grabs the best release and never asks.
|
||||
`source=youtube` skips Lidarr; `source=lidarr` drops the YouTube fallback.
|
||||
"""
|
||||
if quality not in mf.QUALITY_CHOICES:
|
||||
raise HTTPException(status_code=422, detail=f"Invalid quality '{quality}'.")
|
||||
|
||||
@@ -70,7 +84,7 @@ def fetch(q: str = Query(..., min_length=1),
|
||||
# Non-direct link (Spotify/Apple/...): resolve via Odesli, then run the
|
||||
# normal Lidarr-first pick/dispatch with the exact YouTube track fallback.
|
||||
try:
|
||||
query, hits = mf.resolve_link_hits(q, 10)
|
||||
query, hits = mf.resolve_link_hits(q, limit)
|
||||
except mf.OdesliError:
|
||||
raise HTTPException(status_code=422,
|
||||
detail=f"Couldn't resolve {q}. Try the direct YouTube or SoundCloud link.")
|
||||
@@ -83,9 +97,10 @@ def fetch(q: str = Query(..., min_length=1),
|
||||
response = _job_public(job)
|
||||
jobs.run_job(
|
||||
job.id,
|
||||
lambda: actions.perform_fetch(chosen, hits, quality, ROOT),
|
||||
done_message=actions.done_message(chosen),
|
||||
fail_message=actions.failed_message(chosen),
|
||||
# A URL is a direct grab, so never park it for a choice.
|
||||
lambda: actions.perform_fetch(chosen, hits, quality, ROOT, auto=True),
|
||||
done_message=lambda res: actions.done_message(chosen, res),
|
||||
fail_message=lambda e: actions.fail_text(chosen, e),
|
||||
)
|
||||
return response
|
||||
|
||||
@@ -93,7 +108,7 @@ def fetch(q: str = Query(..., min_length=1),
|
||||
raise HTTPException(status_code=422, detail=f"Invalid source '{source}'.")
|
||||
|
||||
yt_first = source == "youtube"
|
||||
hits = mf.build_combined_hits(q, 10, yt_first,
|
||||
hits = mf.build_combined_hits(q, limit, yt_first,
|
||||
lidarr_only=(source == "lidarr"),
|
||||
yt_only=(source == "youtube"))
|
||||
if not hits:
|
||||
@@ -105,11 +120,38 @@ def fetch(q: str = Query(..., min_length=1),
|
||||
|
||||
job = jobs.create_job(hit=chosen, message=actions.started_message(chosen))
|
||||
response = _job_public(job) # snapshot "queued" state before background thread starts
|
||||
jobs.run_choice_job(
|
||||
job.id,
|
||||
lambda: actions.perform_fetch(chosen, hits, quality, ROOT, auto=auto,
|
||||
allow_youtube=(source != "lidarr")),
|
||||
choice_message=lambda res: actions.choice_message(chosen, res),
|
||||
done_message=lambda res: actions.done_message(chosen, res),
|
||||
fail_message=lambda e: actions.fail_text(chosen, e),
|
||||
)
|
||||
return response
|
||||
|
||||
|
||||
@app.post("/choose", dependencies=[Depends(require_key)])
|
||||
def choose(job_id: str = Query(..., min_length=1), n: int = Query(..., ge=1)):
|
||||
"""Send the release the client picked to the download client. `n` is the
|
||||
number of a choice from the same job's `choices` list."""
|
||||
try:
|
||||
job, release = jobs.take_choice(job_id, n)
|
||||
except KeyError:
|
||||
raise HTTPException(status_code=404, detail="No such job.")
|
||||
except jobs.ChoiceError as e:
|
||||
raise HTTPException(status_code=409, detail=str(e))
|
||||
|
||||
prev = job.result or {}
|
||||
artist, album = prev.get("artist", ""), prev.get("album", "")
|
||||
jobs.requeue(job, actions.grab_started_message(artist, album))
|
||||
response = _job_public(job)
|
||||
jobs.run_job(
|
||||
job.id,
|
||||
lambda: actions.perform_fetch(chosen, hits, quality, ROOT),
|
||||
done_message=actions.done_message(chosen),
|
||||
fail_message=actions.failed_message(chosen),
|
||||
lambda: actions.perform_grab(release, artist, album),
|
||||
done_message=actions.grab_message,
|
||||
fail_message=lambda e: (str(e) if isinstance(e, actions.FetchError) and str(e)
|
||||
else "Lidarr refused that release."),
|
||||
)
|
||||
return response
|
||||
|
||||
|
||||
Reference in New Issue
Block a user