Video automations: Clean Completed Downloads twin (phase 3)

Same pattern as phase 2: video_clean_completed_downloads action (scope='video'
block + registry), reuses the shared auto_clean_completed_downloads handler, and
an owned_by='video' system automation on the music cadence (every 5 min). Music
copy untouched. Seam tests for scope isolation, single video-owned seed, and
shared-handler reuse; EXPECTED_ACTION_NAMES updated.
This commit is contained in:
BoulderBadgeDad 2026-06-21 23:32:28 -07:00
parent 3ef0990ffc
commit a6b2737a5c
5 changed files with 47 additions and 7 deletions

View file

@ -272,6 +272,8 @@ ACTIONS: list[dict] = [
# them on the video builder only; the music blocks above are untouched.
{"type": "video_clean_search_history", "label": "Clean Search History", "icon": "trash-2", "scope": "video",
"description": "Remove old searches from Soulseek", "available": True},
{"type": "video_clean_completed_downloads", "label": "Clean Completed Downloads", "icon": "check-square", "scope": "video",
"description": "Clear completed downloads and empty directories", "available": True},
]

View file

@ -160,6 +160,10 @@ def register_all(deps: AutomationDeps) -> None:
'video_clean_search_history',
lambda config: auto_clean_search_history(config, deps),
)
engine.register_action_handler(
'video_clean_completed_downloads',
lambda config: auto_clean_completed_downloads(config, deps),
)
engine.register_action_handler(
'clean_completed_downloads',
lambda config: auto_clean_completed_downloads(config, deps),

View file

@ -193,6 +193,14 @@ SYSTEM_AUTOMATIONS = [
'initial_delay': 600,
'owned_by': 'video',
},
{
'name': 'Clean Completed Downloads',
'trigger_type': 'schedule',
'trigger_config': {'interval': 5, 'unit': 'minutes'},
'action_type': 'video_clean_completed_downloads',
'initial_delay': 300,
'owned_by': 'video',
},
# Video twin of music's 'Auto-Deep Scan Library', split into TWO because Movies
# and TV are independent libraries — a TV scan never pulls in new movies and
# vice-versa. Fixed weekly deep scan (re-read + prune removed) at 02:00 server-

View file

@ -61,6 +61,7 @@ EXPECTED_ACTION_NAMES = frozenset({
'video_update_database',
'video_add_airing_episodes',
'video_clean_search_history',
'video_clean_completed_downloads',
})
# Action names that MUST register a guard (duplicate-run prevention).

View file

@ -48,8 +48,7 @@ def test_video_clean_search_history_seeds_one_video_owned_system_row():
assert row["trigger_config"] == {"interval": 1, "unit": "hours"}
def test_video_clean_search_history_reuses_the_music_handler():
# Registered to the SAME handler function as the music action (identical behaviour).
def _registered_handlers():
from core.automation.handlers import register_all
class _Eng:
@ -60,10 +59,36 @@ def test_video_clean_search_history_reuses_the_music_handler():
def register_progress_callbacks(self, *a, **k):
pass
import core.automation.handlers.registration as reg
eng = _Eng()
# Build minimal deps via the test helper the registration suite uses.
from tests.automation.test_handler_registration import _build_deps
eng = _Eng()
register_all(_build_deps(eng))
assert "video_clean_search_history" in eng.handlers
assert "clean_search_history" in eng.handlers
return eng.handlers
def test_video_clean_search_history_reuses_the_music_handler():
# Registered to the SAME handler function as the music action (identical behaviour).
handlers = _registered_handlers()
assert "video_clean_search_history" in handlers
assert "clean_search_history" in handlers
# ── Phase 3: Clean Completed Downloads ──────────────────────────────────────
def test_video_clean_completed_downloads_is_video_scoped_only():
assert "video_clean_completed_downloads" in _action_types("video")
assert "video_clean_completed_downloads" not in _action_types("music")
# music original untouched
assert "clean_completed_downloads" in _action_types("music")
assert "clean_completed_downloads" not in _action_types("video")
def test_video_clean_completed_downloads_seeds_one_video_owned_system_row():
rows = _system_by_action("video_clean_completed_downloads")
assert len(rows) == 1 and rows[0]["owned_by"] == "video"
assert rows[0]["trigger_config"] == {"interval": 5, "unit": "minutes"}
def test_video_clean_completed_downloads_reuses_the_music_handler():
handlers = _registered_handlers()
assert "video_clean_completed_downloads" in handlers
assert "clean_completed_downloads" in handlers