Streamline issue reporting and automate repairs
This commit is contained in:
@@ -254,6 +254,20 @@ class OperationMessageTests(unittest.TestCase):
|
||||
"The title is not currently available in Jellyfin.",
|
||||
)
|
||||
|
||||
def test_bazarr_subtitle_search_is_explained_in_plain_english(self) -> None:
|
||||
message = _operation_result_message(
|
||||
"Bazarr",
|
||||
"PATCH",
|
||||
"/api/episodes/subtitles",
|
||||
{"status": True},
|
||||
params={"language": "en", "episodeid": 42},
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
message,
|
||||
"Bazarr accepted a fresh EN subtitle search for the selected episode.",
|
||||
)
|
||||
|
||||
def test_http_failures_are_translated_without_exposing_stack_traces(self) -> None:
|
||||
self.assertEqual(
|
||||
_operation_error_message("Radarr", 500),
|
||||
@@ -1359,6 +1373,155 @@ class MediaReplacementTests(unittest.IsolatedAsyncioTestCase):
|
||||
self.assertEqual(result["files"][0]["episodes"], ["S02E03"])
|
||||
self.assertNotIn("/private/library", str(result))
|
||||
|
||||
async def test_issue_options_mark_released_missing_episodes_as_best_fit(self) -> None:
|
||||
snapshot = Snapshot(
|
||||
request_id="3909",
|
||||
title="Target Series",
|
||||
request_type=RequestType.tv,
|
||||
state=NormalizedState.downloading,
|
||||
raw={"arr": {"item": {"id": 22}}},
|
||||
)
|
||||
sonarr = SimpleNamespace(
|
||||
configured=lambda: True,
|
||||
get_episodes=AsyncMock(return_value=[
|
||||
{
|
||||
"id": 101,
|
||||
"seasonNumber": 1,
|
||||
"episodeNumber": 1,
|
||||
"title": "Collected",
|
||||
"monitored": True,
|
||||
"hasFile": True,
|
||||
"episodeFileId": 88,
|
||||
"airDateUtc": "2020-01-01T00:00:00Z",
|
||||
},
|
||||
{
|
||||
"id": 102,
|
||||
"seasonNumber": 1,
|
||||
"episodeNumber": 2,
|
||||
"title": "Missing",
|
||||
"monitored": True,
|
||||
"hasFile": False,
|
||||
"episodeFileId": 0,
|
||||
"airDateUtc": "2020-01-08T00:00:00Z",
|
||||
},
|
||||
]),
|
||||
)
|
||||
runtime = SimpleNamespace(
|
||||
jellyseerr_base_url=None,
|
||||
jellyseerr_api_key=None,
|
||||
sonarr_base_url="http://sonarr",
|
||||
sonarr_api_key="secret",
|
||||
)
|
||||
with (
|
||||
patch.object(requests_router, "get_runtime_settings", return_value=runtime),
|
||||
patch.object(requests_router, "build_snapshot", new=AsyncMock(return_value=snapshot)),
|
||||
patch.object(requests_router, "SonarrClient", return_value=sonarr),
|
||||
):
|
||||
result = await requests_router.issue_target_options(
|
||||
"3909",
|
||||
{"username": "viewer", "role": "user", "auto_search_enabled": True},
|
||||
)
|
||||
|
||||
self.assertEqual(result["seasons"][0]["missing_count"], 1)
|
||||
self.assertTrue(result["seasons"][0]["best_fit"])
|
||||
missing = next(item for item in result["episodes"] if item["id"] == 102)
|
||||
self.assertTrue(missing["missing"])
|
||||
self.assertTrue(missing["best_fit"])
|
||||
collected = next(item for item in result["episodes"] if item["id"] == 101)
|
||||
self.assertEqual(collected["file_id"], 88)
|
||||
self.assertNotIn("file_name", collected)
|
||||
self.assertNotIn("quality", collected)
|
||||
|
||||
async def test_tv_replacement_accepts_multiple_selected_episode_files(self) -> None:
|
||||
snapshot = Snapshot(
|
||||
request_id="3909",
|
||||
title="Replacement Series",
|
||||
request_type=RequestType.tv,
|
||||
state=NormalizedState.available,
|
||||
raw={"arr": {"item": {"id": 22}}},
|
||||
)
|
||||
sonarr = SimpleNamespace(
|
||||
configured=lambda: True,
|
||||
get_episode_files=AsyncMock(return_value=[
|
||||
{"id": 88, "relativePath": "S01E01.mkv"},
|
||||
{"id": 89, "relativePath": "S01E02.mkv"},
|
||||
]),
|
||||
get_episodes=AsyncMock(return_value=[
|
||||
{"id": 101, "episodeFileId": 88},
|
||||
{"id": 102, "episodeFileId": 89},
|
||||
]),
|
||||
delete_episode_file=AsyncMock(return_value=None),
|
||||
search_episodes=AsyncMock(return_value={"id": 1}),
|
||||
)
|
||||
runtime = SimpleNamespace(
|
||||
jellyseerr_base_url=None,
|
||||
jellyseerr_api_key=None,
|
||||
sonarr_base_url="http://sonarr",
|
||||
sonarr_api_key="secret",
|
||||
)
|
||||
with (
|
||||
patch.object(requests_router, "get_runtime_settings", return_value=runtime),
|
||||
patch.object(requests_router, "build_snapshot", new=AsyncMock(return_value=snapshot)),
|
||||
patch.object(requests_router, "SonarrClient", return_value=sonarr),
|
||||
patch.object(requests_router, "save_action"),
|
||||
patch.object(requests_router, "get_portal_item", return_value={
|
||||
"id": 12,
|
||||
"kind": "issue",
|
||||
"external_ref": "/requests/3909",
|
||||
"created_by_username": "viewer",
|
||||
}),
|
||||
patch.object(requests_router, "add_portal_item_activity"),
|
||||
):
|
||||
result = await requests_router.action_replace_media(
|
||||
"3909",
|
||||
{"file_ids": [88, 89], "confirmed": True, "issue_id": 12},
|
||||
{"username": "viewer", "role": "user", "auto_search_enabled": True},
|
||||
)
|
||||
|
||||
self.assertEqual(result["file_ids"], [88, 89])
|
||||
self.assertEqual(sonarr.delete_episode_file.await_count, 2)
|
||||
sonarr.search_episodes.assert_awaited_once_with([101, 102])
|
||||
|
||||
async def test_movie_subtitle_issue_starts_bazarr_search_without_replacement(self) -> None:
|
||||
snapshot = Snapshot(
|
||||
request_id="3914",
|
||||
title="Subtitle Movie",
|
||||
request_type=RequestType.movie,
|
||||
state=NormalizedState.available,
|
||||
raw={"arr": {"item": {"id": 44, "movieFile": {"id": 77}}}},
|
||||
)
|
||||
bazarr = SimpleNamespace(
|
||||
configured=lambda: True,
|
||||
search_movie_subtitles=AsyncMock(return_value={"status": True}),
|
||||
)
|
||||
runtime = SimpleNamespace(
|
||||
bazarr_base_url="http://bazarr",
|
||||
bazarr_api_key="secret",
|
||||
bazarr_default_language="en",
|
||||
)
|
||||
with (
|
||||
patch.object(requests_router, "get_runtime_settings", return_value=runtime),
|
||||
patch.object(requests_router, "build_snapshot", new=AsyncMock(return_value=snapshot)),
|
||||
patch.object(requests_router, "BazarrClient", return_value=bazarr),
|
||||
patch.object(requests_router, "save_action"),
|
||||
patch.object(requests_router, "get_portal_item", return_value={
|
||||
"id": 12,
|
||||
"kind": "issue",
|
||||
"external_ref": "/requests/3914",
|
||||
"created_by_username": "viewer",
|
||||
}),
|
||||
patch.object(requests_router, "add_portal_item_activity") as add_activity,
|
||||
):
|
||||
result = await requests_router.action_repair_subtitles(
|
||||
"3914",
|
||||
{"issue_id": 12, "episode_ids": [], "forced": True},
|
||||
{"username": "viewer", "role": "user", "auto_search_enabled": True},
|
||||
)
|
||||
|
||||
self.assertEqual(result["status"], "ok")
|
||||
bazarr.search_movie_subtitles.assert_awaited_once_with(44, language="en", forced=True)
|
||||
self.assertEqual(add_activity.call_args.kwargs["event_type"], "subtitle_repair_started")
|
||||
|
||||
|
||||
class PortalMediaStatusTests(unittest.IsolatedAsyncioTestCase):
|
||||
def setUp(self) -> None:
|
||||
|
||||
Reference in New Issue
Block a user