video automations: remove the standalone 'Scan Video Library' system automation
Superseded by the post-download chain (Auto-Scan Video After Downloads → Auto-Update Video Database After Scan), which keeps the library fresh without a separate scheduled scan. Two small changes: drop it from SYSTEM_AUTOMATIONS (no longer seeded), and a one-time flag-guarded cleanup (_fix_video_scan_default, v3) that DELETES the existing system row — so it actually stops running, not just hidden. The video_scan_library action/handler/block stay, so a custom scan automation can still be built later. Engine seed test updated. 66 automation tests pass.
This commit is contained in:
parent
91eae710b4
commit
bdcd929558
2 changed files with 41 additions and 38 deletions
|
|
@ -150,17 +150,8 @@ SYSTEM_AUTOMATIONS = [
|
||||||
# them out) and ON the video Automations page (it shows only these).
|
# them out) and ON the video Automations page (it shows only these).
|
||||||
# Schedule-based for now; a video-download-complete event trigger can
|
# Schedule-based for now; a video-download-complete event trigger can
|
||||||
# replace the schedule once that event is wired into the engine.
|
# replace the schedule once that event is wired into the engine.
|
||||||
{
|
# (No standalone scheduled scan — the post-download chain below keeps the library
|
||||||
'name': 'Scan Video Library',
|
# fresh. The 'video_scan_library' action/block still exist for a custom automation.)
|
||||||
'trigger_type': 'schedule',
|
|
||||||
'trigger_config': {'interval': 6, 'unit': 'hours'},
|
|
||||||
'action_type': 'video_scan_library',
|
|
||||||
'action_config': {'mode': 'incremental'}, # recurring = light (newest-only, no prune)
|
|
||||||
'owned_by': 'video',
|
|
||||||
# First run a full interval out — a scheduled scan should NOT fire right after
|
|
||||||
# app startup; post-download freshness is handled by the event chain instead.
|
|
||||||
'initial_delay': 6 * 60 * 60,
|
|
||||||
},
|
|
||||||
# Post-download chain (video twin of music's batch_complete → scan_library →
|
# Post-download chain (video twin of music's batch_complete → scan_library →
|
||||||
# library_scan_completed → start_database_update). Event-based, so a finished
|
# library_scan_completed → start_database_update). Event-based, so a finished
|
||||||
# video download refreshes the server then pulls the new media into video.db.
|
# video download refreshes the server then pulls the new media into video.db.
|
||||||
|
|
@ -325,29 +316,22 @@ 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 correction: the first 'Scan Video Library' seed defaulted to a
|
"""One-time cleanup: remove the standalone 'Scan Video Library' system
|
||||||
FULL scan that fired ~5 min after startup. Switch existing rows that still
|
automation — it's superseded by the post-download chain (Auto-Scan Video
|
||||||
carry that old default to an INCREMENTAL scan and push the next run a full
|
After Downloads → Auto-Update Video Database After Scan). Only deletes the
|
||||||
interval out (no startup-proximate scan). Guarded by a flag + only touches
|
SYSTEM row; a user can still build their own scan automation from the action.
|
||||||
the unchanged default, so a user's deliberate choice is never overridden."""
|
Flag-guarded so it runs once."""
|
||||||
try:
|
try:
|
||||||
from config.settings import config_manager
|
from config.settings import config_manager
|
||||||
if config_manager.get('video_scan_default_v2', False):
|
if config_manager.get('video_scan_cleanup_v3', False):
|
||||||
return
|
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:
|
if auto and auto.get('is_system'):
|
||||||
try:
|
self.db.delete_automation(auto['id'])
|
||||||
cfg = json.loads(auto.get('action_config') or '{}')
|
logger.info("Removed superseded 'Scan Video Library' system automation")
|
||||||
except (ValueError, TypeError):
|
config_manager.set('video_scan_cleanup_v3', True)
|
||||||
cfg = {}
|
|
||||||
if cfg.get('mode', 'full') == 'full': # still the old default → correct it
|
|
||||||
self.db.update_automation(
|
|
||||||
auto['id'], action_config=json.dumps({'mode': 'incremental'}),
|
|
||||||
next_run=_utc_after(6 * 60 * 60))
|
|
||||||
logger.info("Migrated 'Scan Video Library' to incremental + schedule-only")
|
|
||||||
config_manager.set('video_scan_default_v2', True)
|
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception("video scan default migration failed")
|
logger.exception("video scan cleanup failed")
|
||||||
|
|
||||||
def get_system_automation_next_run_seconds(self, action_type):
|
def get_system_automation_next_run_seconds(self, action_type):
|
||||||
"""Get seconds until next run for a system automation. Returns 0 if not found or disabled."""
|
"""Get seconds until next run for a system automation. Returns 0 if not found or disabled."""
|
||||||
|
|
|
||||||
|
|
@ -411,10 +411,9 @@ def test_end_to_end_monthly_schedule_produces_valid_db_string(engine_with_db):
|
||||||
|
|
||||||
|
|
||||||
def test_ensure_system_automations_seeds_video_with_owned_by_and_mode():
|
def test_ensure_system_automations_seeds_video_with_owned_by_and_mode():
|
||||||
"""The video twin ('Scan Video Library') must seed with owned_by='video'
|
"""The video post-download chain twins must seed with owned_by='video' so they
|
||||||
so it stays off the music page, and carry its action_config (mode). Music
|
stay off the music page; the standalone 'Scan Video Library' is NO LONGER seeded
|
||||||
system automations keep owned_by=None. Regression guard for the seeding
|
(superseded by the chain). Music system automations keep owned_by=None."""
|
||||||
seam in ensure_system_automations()."""
|
|
||||||
db = MagicMock()
|
db = MagicMock()
|
||||||
db.get_system_automation_by_action.return_value = None # nothing seeded yet
|
db.get_system_automation_by_action.return_value = None # nothing seeded yet
|
||||||
created = {}
|
created = {}
|
||||||
|
|
@ -428,12 +427,32 @@ def test_ensure_system_automations_seeds_video_with_owned_by_and_mode():
|
||||||
engine = AutomationEngine(db)
|
engine = AutomationEngine(db)
|
||||||
engine.ensure_system_automations()
|
engine.ensure_system_automations()
|
||||||
|
|
||||||
# Video twin seeded, tagged for the video side, with its mode config.
|
# The standalone scheduled scan is gone; the chain twins are seeded, video-tagged.
|
||||||
assert 'video_scan_library' in created, 'video twin not seeded'
|
assert 'video_scan_library' not in created
|
||||||
video = created['video_scan_library']
|
assert created['video_scan_server']['owned_by'] == 'video'
|
||||||
assert video['owned_by'] == 'video'
|
assert created['video_update_database']['owned_by'] == 'video'
|
||||||
assert json.loads(video['action_config']) == {'mode': 'incremental'}
|
assert json.loads(created['video_update_database']['action_config']) == {'mode': 'incremental'}
|
||||||
|
|
||||||
# Music automations stay owned_by=None (shown on the music page).
|
# Music automations stay owned_by=None (shown on the music page).
|
||||||
assert created['scan_library']['owned_by'] is None
|
assert created['scan_library']['owned_by'] is None
|
||||||
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):
|
||||||
|
"""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())
|
||||||
|
|
||||||
|
db = MagicMock()
|
||||||
|
db.get_system_automation_by_action.return_value = {'id': 99, 'is_system': 1}
|
||||||
|
engine = AutomationEngine(db)
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue