video wishlist processor: log WHY nothing grabbed (no results vs quality reject)
'no acceptable release' was ambiguous — couldn't tell if slskd returned nothing or returned hits the quality profile rejected. now each item logs 'No search results for X' vs 'N result(s) for X, none accepted — <reason>' (reason from the top hit), and the summary tallies '… · N had no results, M rejected on quality'. makes the slskd-finds-nothing-for-video situation legible.
This commit is contained in:
parent
fc045cd74e
commit
6531a19c7a
2 changed files with 40 additions and 5 deletions
|
|
@ -213,6 +213,8 @@ def auto_video_process_wishlist(
|
|||
|
||||
grabbed = [0]
|
||||
searched = [0]
|
||||
noresults = [0] # search came back empty (the source had nothing)
|
||||
rejected = [0] # source had hits, but none passed the quality profile
|
||||
total = len(todo)
|
||||
lock = threading.Lock()
|
||||
|
||||
|
|
@ -224,25 +226,45 @@ def auto_video_process_wishlist(
|
|||
if media_type == 'episode':
|
||||
name = "%s S%02dE%02d" % (name, int(it.get('season_number') or 0),
|
||||
int(it.get('episode_number') or 0))
|
||||
# distinguish "source returned nothing" from "returned hits but all rejected"
|
||||
# (the rejected case carries the reason on the top-ranked hit) so the log is useful.
|
||||
if ok:
|
||||
msg, lt = "Grabbed '%s'" % name, 'success'
|
||||
elif not cands:
|
||||
msg, lt = "No search results for '%s'" % name, 'info'
|
||||
else:
|
||||
why = (cands[0].get('rejected') or 'none met your quality profile')
|
||||
msg, lt = "%d result(s) for '%s', none accepted — %s" % (len(cands), name, why), 'info'
|
||||
with lock:
|
||||
searched[0] += 1
|
||||
if ok:
|
||||
grabbed[0] += 1
|
||||
elif not cands:
|
||||
noresults[0] += 1
|
||||
else:
|
||||
rejected[0] += 1
|
||||
deps.update_progress(
|
||||
automation_id, phase='Searching + grabbing…',
|
||||
progress=10 + int(85 * searched[0] / max(total, 1)),
|
||||
log_line=("Grabbed '%s'" % name) if ok else ("No acceptable release for '%s'" % name),
|
||||
log_type='success' if ok else 'info')
|
||||
log_line=msg, log_type=lt)
|
||||
|
||||
with ThreadPoolExecutor(max_workers=concurrency) as ex:
|
||||
list(ex.map(_one, todo))
|
||||
|
||||
done = ('Grabbed %d %s(s) of %d searched' % (grabbed[0], label, searched[0])) if grabbed[0] \
|
||||
else ('Searched %d %s(s) — no acceptable releases found' % (searched[0], label))
|
||||
# Headline with the WHY breakdown: it's the difference between "the source has
|
||||
# nothing" (noresults) and "it has stuff but your quality profile rejects it" (rejected).
|
||||
tail = []
|
||||
if noresults[0]:
|
||||
tail.append('%d had no results' % noresults[0])
|
||||
if rejected[0]:
|
||||
tail.append('%d rejected on quality' % rejected[0])
|
||||
breakdown = (' · ' + ', '.join(tail)) if tail else ''
|
||||
done = ('Grabbed %d %s(s) of %d searched%s' % (grabbed[0], label, searched[0], breakdown)) if grabbed[0] \
|
||||
else ('Searched %d %s(s), grabbed 0%s' % (searched[0], label, breakdown))
|
||||
deps.update_progress(automation_id, status='finished', progress=100, phase='Complete',
|
||||
log_line=done, log_type='success' if grabbed[0] else 'info')
|
||||
return {'status': 'completed', 'searched': searched[0], 'grabbed': grabbed[0],
|
||||
'_manages_own_progress': True}
|
||||
'noresults': noresults[0], 'rejected': rejected[0], '_manages_own_progress': True}
|
||||
except Exception as e: # noqa: BLE001
|
||||
deps.update_progress(automation_id, status='error', phase='Error', log_line=str(e), log_type='error')
|
||||
return {'status': 'error', 'error': str(e), '_manages_own_progress': True}
|
||||
|
|
|
|||
|
|
@ -123,6 +123,19 @@ def test_no_acceptable_release_grabs_nothing():
|
|||
assert res["searched"] == 1 and res["grabbed"] == 0 and enq == []
|
||||
|
||||
|
||||
def test_breakdown_distinguishes_no_results_from_quality_rejection():
|
||||
# the diagnostic: "the source had nothing" vs "had hits but quality rejected them"
|
||||
items = [{"tmdb_id": 1, "title": "A"}, {"tmdb_id": 2, "title": "B"}]
|
||||
res, enq, _, deps = _run(items, searches={
|
||||
("movie", "1"): [], # source had nothing
|
||||
("movie", "2"): [dict(_cand("junk", accepted=False), rejected="Unknown / unsupported quality")],
|
||||
})
|
||||
assert res["grabbed"] == 0 and res["noresults"] == 1 and res["rejected"] == 1
|
||||
logs = " ".join(p.get("log_line") or "" for p in deps.progress)
|
||||
assert "No search results for 'A'" in logs
|
||||
assert "none accepted — Unknown / unsupported quality" in logs # reason surfaced
|
||||
|
||||
|
||||
def test_missing_library_folder_is_a_quiet_skip():
|
||||
res, enq, _, deps = _run([{"tmdb_id": 1, "title": "A"}], root="")
|
||||
assert res["status"] == "completed" and res.get("skipped") == "no_folder"
|
||||
|
|
|
|||
Loading…
Reference in a new issue