From fd66390648ac044a402904f0b2e8402ad2ac73ec Mon Sep 17 00:00:00 2001 From: BoulderBadgeDad Date: Sun, 21 Jun 2026 08:59:32 -0700 Subject: [PATCH] Video automations: reliably delete the obsolete 'Scan Video Library' seed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit _fix_video_scan_default set its 'done' flag even on runs where it deleted nothing, so once the flag latched True the standalone 'Scan Video Library' system automation survived forever (the row the post-download chain replaced). Drop the flag entirely — get_system_automation_by_action already matches only the is_system-seeded row, so the cleanup is safe to run every startup and no-ops once the row is gone. --- core/automation_engine.py | 23 ++++++++-------- .../test_engine_schedule_integration.py | 27 +++++++++---------- 2 files changed, 24 insertions(+), 26 deletions(-) diff --git a/core/automation_engine.py b/core/automation_engine.py index f0b90f4a..77a95bca 100644 --- a/core/automation_engine.py +++ b/core/automation_engine.py @@ -325,20 +325,21 @@ class AutomationEngine: self._fix_video_scan_default() def _fix_video_scan_default(self): - """One-time cleanup: remove the standalone 'Scan Video Library' system - automation — it's superseded by the post-download chain (Auto-Scan Video - After Downloads → Auto-Update Video Database After Scan). Only deletes the - SYSTEM row; a user can still build their own scan automation from the action. - Flag-guarded so it runs once.""" + """Remove the obsolete standalone 'Scan Video Library' SYSTEM automation — it's + superseded by the post-download chain (Auto-Scan Video After Downloads → + Auto-Update Video Database After Scan). + + ``get_system_automation_by_action`` matches ONLY a system-seeded row + (is_system=1), so a user's own scan automation is never touched. Idempotent — + safe to run on every startup; once the row is gone the lookup returns None and + it no-ops. (No flag guard: the old one could latch True without ever deleting, + which is exactly why the row survived earlier 'cleanups'.)""" try: - from config.settings import config_manager - if config_manager.get('video_scan_cleanup_v3', False): - return auto = self.db.get_system_automation_by_action('video_scan_library') - if auto and auto.get('is_system'): + if auto: self.db.delete_automation(auto['id']) - logger.info("Removed superseded 'Scan Video Library' system automation") - config_manager.set('video_scan_cleanup_v3', True) + logger.info("Removed superseded 'Scan Video Library' system automation (id=%s)", + auto.get('id')) except Exception: logger.exception("video scan cleanup failed") diff --git a/tests/automation/test_engine_schedule_integration.py b/tests/automation/test_engine_schedule_integration.py index d742155e..7eba1f03 100644 --- a/tests/automation/test_engine_schedule_integration.py +++ b/tests/automation/test_engine_schedule_integration.py @@ -438,21 +438,18 @@ def test_ensure_system_automations_seeds_video_with_owned_by_and_mode(): assert json.loads(created['scan_library']['action_config']) == {} -def test_video_scan_library_system_automation_is_cleaned_up(monkeypatch): - """The old standalone 'Scan Video Library' system automation is deleted once - (superseded by the post-download chain). Guard flag stops it re-running.""" - class _FakeCfg: - def __init__(self): self._d = {} - def get(self, k, default=None): return self._d.get(k, default) - def set(self, k, v): self._d[k] = v - monkeypatch.setattr('config.settings.config_manager', _FakeCfg()) - +def test_video_scan_library_system_automation_is_cleaned_up(): + """The obsolete standalone 'Scan Video Library' system automation is deleted when + present (superseded by the post-download chain). No stuck flag — it just keys off + the lookup, so once the row is gone it no-ops.""" + # present → deleted (matches only the is_system-seeded row) db = MagicMock() db.get_system_automation_by_action.return_value = {'id': 99, 'is_system': 1} - engine = AutomationEngine(db) + AutomationEngine(db)._fix_video_scan_default() + db.delete_automation.assert_any_call(99) - engine._fix_video_scan_default() - db.delete_automation.assert_called_once_with(99) - - engine._fix_video_scan_default() # flag set → no second delete - db.delete_automation.assert_called_once() + # absent (already gone) → idempotent no-op, never errors or deletes + db2 = MagicMock() + db2.get_system_automation_by_action.return_value = None + AutomationEngine(db2)._fix_video_scan_default() + db2.delete_automation.assert_not_called()