Mark available downloads complete
This commit is contained in:
@@ -420,6 +420,10 @@ def _build_presentation(
|
||||
missing = int(availability.get("missing") or 0)
|
||||
total = int(availability.get("total") or 0)
|
||||
partial = available > 0 and missing > 0
|
||||
jellyfin_partial = bool(
|
||||
jellyfin_found and snapshot.request_type == RequestType.tv and missing > 0
|
||||
)
|
||||
fully_available = bool(jellyfin_found and not jellyfin_partial)
|
||||
download_visible = bool(download.get("visible"))
|
||||
download_state = str(download.get("state") or "not_started")
|
||||
|
||||
@@ -432,7 +436,7 @@ def _build_presentation(
|
||||
f"The request was approved, but it has not reached the {collector} collector yet. "
|
||||
"Adding it to the library queue is the next step."
|
||||
)
|
||||
elif jellyfin_found and partial:
|
||||
elif jellyfin_partial:
|
||||
status_label = f"Partially available — {available} of {total} episodes collected"
|
||||
meaning = (
|
||||
f"Some of this request is ready to watch. {collector} is still looking for "
|
||||
@@ -497,7 +501,11 @@ def _build_presentation(
|
||||
meaning = snapshot.state_reason or "Magent is checking where this request is in the collection process."
|
||||
|
||||
action_ids = [action.id for action in snapshot.actions]
|
||||
if "resume_torrent" in action_ids:
|
||||
if fully_available:
|
||||
next_title = "Ready to watch"
|
||||
next_description = "Collection is complete. Open the title on the media server when you are ready."
|
||||
recommended = []
|
||||
elif "resume_torrent" in action_ids:
|
||||
next_title = "Resume the interrupted download"
|
||||
next_description = "The download exists but is not currently progressing. Resume it to continue collection."
|
||||
recommended = ["resume_torrent"]
|
||||
@@ -562,7 +570,9 @@ def _build_presentation(
|
||||
else:
|
||||
library_state, library_summary = "waiting", "Waiting for collector information"
|
||||
|
||||
if download_visible:
|
||||
if fully_available:
|
||||
search_state, search_summary = "complete", "No further search needed"
|
||||
elif download_visible:
|
||||
search_state = "complete"
|
||||
search_summary = "A release was found"
|
||||
elif arr_state in {"added", "searching"} and (missing or snapshot.request_type == RequestType.movie):
|
||||
@@ -575,7 +585,15 @@ def _build_presentation(
|
||||
else:
|
||||
search_state, search_summary = "waiting", "Search has not started"
|
||||
|
||||
if download_visible:
|
||||
completed_download_summary = (
|
||||
"The requested content has been collected and is available to watch. "
|
||||
"No further action is needed."
|
||||
)
|
||||
if fully_available:
|
||||
download_stage_state, download_summary = "complete", completed_download_summary
|
||||
pipeline_download_visible = False
|
||||
pipeline_torrents: List[Dict[str, Any]] = []
|
||||
elif download_visible:
|
||||
download_stage_state = {
|
||||
"downloading": "active",
|
||||
"paused": "attention",
|
||||
@@ -584,19 +602,34 @@ def _build_presentation(
|
||||
"error": "attention",
|
||||
}.get(download_state, "waiting")
|
||||
download_summary = str(download.get("summary") or "A prior download attempt was found")
|
||||
pipeline_download_visible = True
|
||||
pipeline_torrents = download.get("torrents") or []
|
||||
else:
|
||||
download_stage_state, download_summary = "waiting", "No download attempt yet"
|
||||
pipeline_download_visible = False
|
||||
pipeline_torrents = []
|
||||
|
||||
if jellyfin_found and partial:
|
||||
if jellyfin_partial:
|
||||
available_state, available_summary = "partial", f"{available} of {total} episodes available"
|
||||
elif jellyfin_found:
|
||||
available_state, available_summary = "complete", "Available to watch"
|
||||
else:
|
||||
available_state, available_summary = "waiting", "Not available on the media server yet"
|
||||
|
||||
display_download = dict(download)
|
||||
if fully_available:
|
||||
display_download.update(
|
||||
{
|
||||
"visible": False,
|
||||
"state": "completed",
|
||||
"summary": completed_download_summary,
|
||||
"torrents": [],
|
||||
}
|
||||
)
|
||||
|
||||
return {
|
||||
"status": {"label": status_label, "meaning": meaning},
|
||||
"download": download,
|
||||
"download": display_download,
|
||||
"nextStep": {
|
||||
"title": next_title,
|
||||
"description": next_description,
|
||||
@@ -621,15 +654,19 @@ def _build_presentation(
|
||||
"label": "Release search",
|
||||
"state": search_state,
|
||||
"summary": search_summary,
|
||||
"actionIds": [action_id for action_id in ("search_auto", "search_releases") if action_id in action_ids],
|
||||
"actionIds": [] if fully_available else [
|
||||
action_id
|
||||
for action_id in ("search_auto", "search_releases")
|
||||
if action_id in action_ids
|
||||
],
|
||||
},
|
||||
{
|
||||
"id": "download",
|
||||
"label": "Download",
|
||||
"label": "Download complete" if fully_available else "Download",
|
||||
"state": download_stage_state,
|
||||
"summary": download_summary,
|
||||
"visible": download_visible,
|
||||
"torrents": download.get("torrents") or [],
|
||||
"visible": pipeline_download_visible,
|
||||
"torrents": pipeline_torrents,
|
||||
},
|
||||
{
|
||||
"id": "available",
|
||||
@@ -1158,7 +1195,13 @@ async def build_snapshot(request_id: str) -> Snapshot:
|
||||
query = quote(snapshot.title or "")
|
||||
jellyfin_link = f"{base_url}/web/index.html#!/search?query={query}"
|
||||
availability = arr_details.get("availability") or {}
|
||||
is_partial = bool(jellyfin_available and int(availability.get("missing") or 0) > 0)
|
||||
is_partial = bool(
|
||||
jellyfin_available
|
||||
and snapshot.request_type == RequestType.tv
|
||||
and int(availability.get("missing") or 0) > 0
|
||||
)
|
||||
if jellyfin_available and not is_partial:
|
||||
snapshot.actions = []
|
||||
snapshot.raw = {
|
||||
"jellyseerr": jelly_request,
|
||||
"arr": {
|
||||
|
||||
@@ -228,6 +228,82 @@ class RequestPresentationTests(unittest.TestCase):
|
||||
download_stage = next(stage for stage in presentation["pipeline"] if stage["id"] == "download")
|
||||
self.assertEqual(download_stage["summary"], "No download attempt yet")
|
||||
|
||||
def test_available_content_replaces_stale_download_warning_with_completion(self) -> None:
|
||||
snapshot = Snapshot(
|
||||
request_id="3909",
|
||||
title="Example",
|
||||
request_type=RequestType.tv,
|
||||
state=NormalizedState.completed,
|
||||
actions=[],
|
||||
)
|
||||
|
||||
presentation = _build_presentation(
|
||||
snapshot,
|
||||
approved=True,
|
||||
arr_state="available",
|
||||
arr_details={
|
||||
"availability": {"available": 6, "missing": 0, "total": 6, "seasons": []}
|
||||
},
|
||||
prowlarr_state="ok",
|
||||
download={
|
||||
"visible": True,
|
||||
"state": "missing",
|
||||
"summary": "A previous download was observed, but it is not currently visible in qBittorrent.",
|
||||
"torrents": [],
|
||||
},
|
||||
jellyfin_found=True,
|
||||
jellyfin_link="https://media.test/title/3909",
|
||||
)
|
||||
|
||||
self.assertFalse(presentation["download"]["visible"])
|
||||
self.assertEqual(presentation["download"]["state"], "completed")
|
||||
self.assertEqual(presentation["nextStep"]["title"], "Ready to watch")
|
||||
self.assertEqual(presentation["nextStep"]["actionIds"], [])
|
||||
download_stage = next(stage for stage in presentation["pipeline"] if stage["id"] == "download")
|
||||
self.assertEqual(download_stage["label"], "Download complete")
|
||||
self.assertEqual(download_stage["state"], "complete")
|
||||
self.assertFalse(download_stage["visible"])
|
||||
self.assertEqual(
|
||||
download_stage["summary"],
|
||||
"The requested content has been collected and is available to watch. No further action is needed.",
|
||||
)
|
||||
search_stage = next(stage for stage in presentation["pipeline"] if stage["id"] == "search")
|
||||
self.assertEqual(search_stage["state"], "complete")
|
||||
self.assertEqual(search_stage["actionIds"], [])
|
||||
|
||||
def test_partially_available_content_keeps_missing_download_attention(self) -> None:
|
||||
snapshot = Snapshot(
|
||||
request_id="3909",
|
||||
title="Example",
|
||||
request_type=RequestType.tv,
|
||||
state=NormalizedState.importing,
|
||||
actions=[],
|
||||
)
|
||||
|
||||
presentation = _build_presentation(
|
||||
snapshot,
|
||||
approved=True,
|
||||
arr_state="added",
|
||||
arr_details={
|
||||
"availability": {"available": 3, "missing": 3, "total": 6, "seasons": []}
|
||||
},
|
||||
prowlarr_state="ok",
|
||||
download={
|
||||
"visible": True,
|
||||
"state": "missing",
|
||||
"summary": "A previous download is no longer visible.",
|
||||
"torrents": [],
|
||||
},
|
||||
jellyfin_found=True,
|
||||
jellyfin_link="https://media.test/title/3909",
|
||||
)
|
||||
|
||||
self.assertTrue(presentation["download"]["visible"])
|
||||
download_stage = next(stage for stage in presentation["pipeline"] if stage["id"] == "download")
|
||||
self.assertEqual(download_stage["label"], "Download")
|
||||
self.assertEqual(download_stage["state"], "attention")
|
||||
self.assertTrue(download_stage["visible"])
|
||||
|
||||
|
||||
class LiveDownloadProgressTests(unittest.IsolatedAsyncioTestCase):
|
||||
async def test_live_download_progress_uses_saved_hash_and_current_qbittorrent_value(self) -> None:
|
||||
|
||||
Reference in New Issue
Block a user