Add anikoto backup downloader
- add a config toggle for backup downloads and retry failed ani-cli jobs with anikoto-cli - install anikoto-cli systemwide in the container with its required dependencies - bump the project version to 0.46.1 and publish the release notes
This commit is contained in:
+63
-1
@@ -624,7 +624,7 @@ class DownloadQueueWorkerFailureTests(unittest.TestCase):
|
||||
stored = queue._find(job["id"])
|
||||
self.assertEqual(stored["status"], "failed")
|
||||
self.assertEqual(stored["exit_code"], 1)
|
||||
self.assertIn("Could not start download", stored["log"][-1])
|
||||
self.assertIn("Could not start ani-cli", "\n".join(stored["log"]))
|
||||
self.assertFalse(staging_dir.exists())
|
||||
|
||||
def test_failed_download_cleans_staging_dir(self):
|
||||
@@ -669,6 +669,46 @@ class DownloadQueueWorkerFailureTests(unittest.TestCase):
|
||||
self.assertIn("without downloading any files", "\n".join(stored["log"]))
|
||||
self.assertFalse(staging_dir.exists())
|
||||
|
||||
def test_failed_ani_cli_retries_with_anikoto_backup(self):
|
||||
queue = APP.DownloadQueue(
|
||||
lambda: {
|
||||
"mode": "sub",
|
||||
"quality": "best",
|
||||
"download_dir": "/tmp/example",
|
||||
"download_backup_enabled": True,
|
||||
},
|
||||
start_worker=False,
|
||||
)
|
||||
job = queue.add({"query": "Queue Show", "title": "Queue Show", "season": "1", "episodes": "1"})
|
||||
|
||||
class FailedProc:
|
||||
pid = 1111
|
||||
stdout = []
|
||||
|
||||
def wait(self):
|
||||
return 1
|
||||
|
||||
class SuccessProc:
|
||||
pid = 2222
|
||||
stdout = []
|
||||
|
||||
def wait(self):
|
||||
return 0
|
||||
|
||||
with mock.patch.object(queue_jobs.subprocess, "Popen", side_effect=[FailedProc(), SuccessProc()]), mock.patch.object(
|
||||
queue, "_command_available", return_value=True
|
||||
), mock.patch.object(
|
||||
queue_jobs, "finalize_library_files", return_value=["/tmp/example/tv/Queue Show/Season 01/Queue Show - S01E01.mp4"]
|
||||
):
|
||||
queue._run_job(job)
|
||||
|
||||
stored = queue._find(job["id"])
|
||||
self.assertEqual(stored["status"], "done")
|
||||
log_text = "\n".join(stored["log"])
|
||||
self.assertIn("Retrying with backup downloader: anikoto-cli.", log_text)
|
||||
self.assertIn("anikoto-cli does not support ani-cli quality flags", log_text)
|
||||
self.assertIn("Download completed.", log_text)
|
||||
|
||||
def test_run_job_marks_post_start_exception_failed_without_killing_worker_state(self):
|
||||
queue = APP.DownloadQueue(lambda: {"mode": "sub", "quality": "best", "download_dir": "/tmp/example"}, start_worker=False)
|
||||
job = queue.add({"query": "Queue Show", "title": "Queue Show", "season": "1", "episodes": "1"})
|
||||
@@ -1597,6 +1637,22 @@ class WatchlistCompletionTests(unittest.TestCase):
|
||||
|
||||
self.assertNotIn("-S", command)
|
||||
|
||||
def test_command_for_job_builds_anikoto_backup_command_without_quality_flag(self):
|
||||
command = APP.app_support.command_for_job(
|
||||
{
|
||||
"query": "Queue Show",
|
||||
"quality": "best",
|
||||
"episodes": "1-10",
|
||||
"mode": "sub",
|
||||
},
|
||||
downloader="anikoto-cli",
|
||||
)
|
||||
|
||||
self.assertEqual(command[0], APP.app_support.ANIKOTO_CLI)
|
||||
self.assertIn("-d", command)
|
||||
self.assertIn("--sub", command)
|
||||
self.assertNotIn("-q", command)
|
||||
|
||||
def test_download_watchlist_item_queues_full_episode_range_for_selected_mode(self):
|
||||
item = {
|
||||
"show_id": "show-42",
|
||||
@@ -2074,6 +2130,7 @@ class ConfigSnapshotTests(unittest.TestCase):
|
||||
|
||||
def test_normalize_config_includes_watchlist_schedule_defaults(self):
|
||||
config = APP.normalize_config({})
|
||||
self.assertFalse(config["download_backup_enabled"])
|
||||
self.assertFalse(config["watchlist_auto_refresh_enabled"])
|
||||
self.assertEqual(config["watchlist_auto_refresh_minutes"], 60)
|
||||
self.assertEqual(config["watchlist_refresh_delay_seconds"], 5)
|
||||
@@ -2089,6 +2146,7 @@ class ConfigSnapshotTests(unittest.TestCase):
|
||||
def test_normalize_config_parses_watchlist_schedule_fields(self):
|
||||
config = APP.normalize_config(
|
||||
{
|
||||
"download_backup_enabled": "true",
|
||||
"watchlist_auto_refresh_enabled": "true",
|
||||
"watchlist_auto_refresh_minutes": "15",
|
||||
"watchlist_refresh_delay_seconds": "7",
|
||||
@@ -2100,6 +2158,7 @@ class ConfigSnapshotTests(unittest.TestCase):
|
||||
"jellyfin_movie_dir": "~/jellyfin/movies",
|
||||
}
|
||||
)
|
||||
self.assertTrue(config["download_backup_enabled"])
|
||||
self.assertTrue(config["watchlist_auto_refresh_enabled"])
|
||||
self.assertEqual(config["watchlist_auto_refresh_minutes"], 15)
|
||||
self.assertEqual(config["watchlist_refresh_delay_seconds"], 7)
|
||||
@@ -3244,7 +3303,10 @@ class TemplateHelperTests(unittest.TestCase):
|
||||
self.assertIn("@media (max-width: 1080px)", APP.CONFIG_HTML)
|
||||
self.assertIn("justify-items: stretch;", APP.CONFIG_HTML)
|
||||
self.assertIn('id="aniCliVersionLine"', APP.CONFIG_HTML)
|
||||
self.assertIn('id="anikotoCliVersionLine"', APP.CONFIG_HTML)
|
||||
self.assertIn('ani-cli ${data.ani_cli_version || "Unavailable"}', APP.CONFIG_HTML)
|
||||
self.assertIn('anikoto-cli ${data.anikoto_cli_version || "Unavailable"}', APP.CONFIG_HTML)
|
||||
self.assertIn('id="downloadBackupEnabled"', APP.CONFIG_HTML)
|
||||
self.assertIn('id="openChangelogBtn"', APP.CONFIG_HTML)
|
||||
self.assertIn('id="changelogOverlay"', APP.CONFIG_HTML)
|
||||
self.assertIn('const data = await api("/api/changelog");', APP.CONFIG_HTML)
|
||||
|
||||
Reference in New Issue
Block a user