Video automations: reliably delete the obsolete 'Scan Video Library' seed

_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.
This commit is contained in:
BoulderBadgeDad 2026-06-21 08:59:32 -07:00
parent 5711963a6f
commit fd66390648
2 changed files with 24 additions and 26 deletions

View file

@ -325,20 +325,21 @@ class AutomationEngine:
self._fix_video_scan_default() self._fix_video_scan_default()
def _fix_video_scan_default(self): def _fix_video_scan_default(self):
"""One-time cleanup: remove the standalone 'Scan Video Library' system """Remove the obsolete standalone 'Scan Video Library' SYSTEM automation — it's
automation it's superseded by the post-download chain (Auto-Scan Video superseded by the post-download chain (Auto-Scan Video After Downloads
After Downloads Auto-Update Video Database After Scan). Only deletes the Auto-Update Video Database After Scan).
SYSTEM row; a user can still build their own scan automation from the action.
Flag-guarded so it runs once.""" ``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: 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') 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']) self.db.delete_automation(auto['id'])
logger.info("Removed superseded 'Scan Video Library' system automation") logger.info("Removed superseded 'Scan Video Library' system automation (id=%s)",
config_manager.set('video_scan_cleanup_v3', True) auto.get('id'))
except Exception: except Exception:
logger.exception("video scan cleanup failed") logger.exception("video scan cleanup failed")

View file

@ -438,21 +438,18 @@ def test_ensure_system_automations_seeds_video_with_owned_by_and_mode():
assert json.loads(created['scan_library']['action_config']) == {} assert json.loads(created['scan_library']['action_config']) == {}
def test_video_scan_library_system_automation_is_cleaned_up(monkeypatch): def test_video_scan_library_system_automation_is_cleaned_up():
"""The old standalone 'Scan Video Library' system automation is deleted once """The obsolete standalone 'Scan Video Library' system automation is deleted when
(superseded by the post-download chain). Guard flag stops it re-running.""" present (superseded by the post-download chain). No stuck flag it just keys off
class _FakeCfg: the lookup, so once the row is gone it no-ops."""
def __init__(self): self._d = {} # present → deleted (matches only the is_system-seeded row)
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())
db = MagicMock() db = MagicMock()
db.get_system_automation_by_action.return_value = {'id': 99, 'is_system': 1} 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() # absent (already gone) → idempotent no-op, never errors or deletes
db.delete_automation.assert_called_once_with(99) db2 = MagicMock()
db2.get_system_automation_by_action.return_value = None
engine._fix_video_scan_default() # flag set → no second delete AutomationEngine(db2)._fix_video_scan_default()
db.delete_automation.assert_called_once() db2.delete_automation.assert_not_called()