diff --git a/locale/themerr.po b/locale/themerr.po index aace9039..c1ad212f 100644 --- a/locale/themerr.po +++ b/locale/themerr.po @@ -210,7 +210,7 @@ msgid "" "Servers page." msgstr "" -#: src/common/config.py:260 src/common/config.py:361 +#: src/common/config.py:260 src/common/config.py:368 msgid "Movie support" msgstr "" @@ -218,7 +218,7 @@ msgstr "" msgid "Add themes to Jellyfin movies." msgstr "" -#: src/common/config.py:264 src/common/config.py:367 +#: src/common/config.py:264 src/common/config.py:374 msgid "Series support" msgstr "" @@ -226,7 +226,7 @@ msgstr "" msgid "Add themes to Jellyfin series." msgstr "" -#: src/common/config.py:268 src/common/config.py:355 web/templates/home.html:31 +#: src/common/config.py:268 src/common/config.py:362 web/templates/home.html:31 msgid "Collections" msgstr "" @@ -359,144 +359,154 @@ msgid "Themerr settings." msgstr "" #: src/common/config.py:349 -msgid "Themerr Enabled" +msgid "Scan on startup" msgstr "" #: src/common/config.py:350 -msgid "When enabled, Themerr will attempt to update themes." +msgid "" +"Run the theme scan when Themerr starts. Scheduled scans still run when " +"this is disabled. Changes apply the next time Themerr starts." msgstr "" #: src/common/config.py:356 +msgid "Themerr Enabled" +msgstr "" + +#: src/common/config.py:357 +msgid "When enabled, Themerr will attempt to update themes." +msgstr "" + +#: src/common/config.py:363 msgid "Add themes to collections." msgstr "" -#: src/common/config.py:362 +#: src/common/config.py:369 msgid "Add themes to movies using the Plex Movie agent." msgstr "" -#: src/common/config.py:368 +#: src/common/config.py:375 msgid "Add themes to series using the Plex Series agent." msgstr "" -#: src/common/config.py:373 +#: src/common/config.py:380 msgid "Overwrite Plex themes" msgstr "" -#: src/common/config.py:374 +#: src/common/config.py:381 msgid "When enabled, Themerr will overwrite themes provided by Plex." msgstr "" -#: src/common/config.py:379 +#: src/common/config.py:386 msgid "Prefer MP4A AAC Codec" msgstr "" -#: src/common/config.py:380 +#: src/common/config.py:387 msgid "This can improve theme compatibility with Apple devices." msgstr "" -#: src/common/config.py:385 +#: src/common/config.py:392 msgid "Remove unused themes" msgstr "" -#: src/common/config.py:386 +#: src/common/config.py:393 msgid "" "Frees up space in your Plex metadata directory. This option requires that" " the \"Plex data directory\" path is set" msgstr "" -#: src/common/config.py:392 +#: src/common/config.py:399 msgid "Remove unused art" msgstr "" -#: src/common/config.py:393 src/common/config.py:401 +#: src/common/config.py:400 src/common/config.py:408 msgid "" "Applies to collection. Frees up space in your Plex metadata directory. " "This option requires that the \"Plex data directory\" path is set" msgstr "" -#: src/common/config.py:400 +#: src/common/config.py:407 msgid "Remove unused posters" msgstr "" -#: src/common/config.py:408 +#: src/common/config.py:415 msgid "Collection metadata" msgstr "" -#: src/common/config.py:409 +#: src/common/config.py:416 msgid "" "Update collection metadata (poster, art, and summary) during scheduled " "update." msgstr "" -#: src/common/config.py:414 +#: src/common/config.py:421 msgid "Ignore locked fields." msgstr "" -#: src/common/config.py:415 +#: src/common/config.py:422 msgid "" "If you used Themerr v2024.813.13709 or lower, you may need to enable this" " to update items." msgstr "" -#: src/common/config.py:421 +#: src/common/config.py:428 msgid "Ignored library IDs" msgstr "" -#: src/common/config.py:422 +#: src/common/config.py:429 msgid "" "Comma-separated library IDs to exclude from scheduled updates. IDs appear" " on Home." msgstr "" -#: src/common/config.py:428 +#: src/common/config.py:435 msgid "Update themes interval" msgstr "" -#: src/common/config.py:429 +#: src/common/config.py:436 msgid "Interval for automatic update task, in minutes (min: 15)." msgstr "" -#: src/common/config.py:436 +#: src/common/config.py:443 msgid "Update database cache interval" msgstr "" -#: src/common/config.py:437 +#: src/common/config.py:444 msgid "Interval for database cache update task, in minutes (min: 15)." msgstr "" -#: src/common/config.py:444 +#: src/common/config.py:451 msgid "PlexAPI timeout" msgstr "" -#: src/common/config.py:445 +#: src/common/config.py:452 msgid "" "Increase this slightly if you experience timeouts when adding themes. " "(min: 1)" msgstr "" -#: src/common/config.py:452 +#: src/common/config.py:459 msgid "Theme upload retries" msgstr "" -#: src/common/config.py:453 +#: src/common/config.py:460 msgid "If uploading themes fail, retry this many times. (min: 0)" msgstr "" -#: src/common/config.py:460 +#: src/common/config.py:467 msgid "Multiprocessing thread count" msgstr "" -#: src/common/config.py:461 +#: src/common/config.py:468 msgid "" "The number of threads to use when adding themes. Restart required. (min: " "1)" msgstr "" -#: src/common/config.py:469 +#: src/common/config.py:476 msgid "YouTube Cookies" msgstr "" -#: src/common/config.py:471 +#: src/common/config.py:478 msgid "" "Optional: export youtube.com cookies with Get cookies.txt LOCALLY using " "its JSON format, then paste the entire JSON array here and save. Saved " @@ -505,7 +515,7 @@ msgid "" "them." msgstr "" -#: src/common/config.py:478 +#: src/common/config.py:485 msgid "Cookie export instructions" msgstr "" diff --git a/src/common/config.py b/src/common/config.py index a2ee2f1e..467a44ba 100644 --- a/src/common/config.py +++ b/src/common/config.py @@ -344,6 +344,13 @@ def on_change_plex_url() -> None: 'name': _('Themerr'), 'description': _('Themerr settings.'), 'icon': 'music', + 'BOOL_SCAN_ON_BOOT': { + 'type': 'boolean', + 'name': _('Scan on startup'), + 'description': _('Run the theme scan when Themerr starts. Scheduled scans still run when this is ' + 'disabled. Changes apply the next time Themerr starts.'), + 'default': True, + }, 'BOOL_THEMERR_ENABLED': { 'type': 'boolean', 'name': _('Themerr Enabled'), diff --git a/src/themerr/scheduled_tasks.py b/src/themerr/scheduled_tasks.py index 1e0971ca..0f763aaa 100644 --- a/src/themerr/scheduled_tasks.py +++ b/src/themerr/scheduled_tasks.py @@ -20,6 +20,7 @@ _job_lock = threading.RLock() _jobs = deque(maxlen=30) _running = {} +_SCAN_TAG = 'theme-scan' def run_threaded( @@ -108,7 +109,14 @@ def schedule_loop() -> None: ... """ log.info('Scheduler started; dispatching initial jobs') - schedule.run_all() # run all jobs once + if config.CONFIG['Themerr']['BOOL_SCAN_ON_BOOT']: + schedule.run_all() # run all jobs once + else: + log.info('Skipping the startup theme scan because Scan on startup is disabled') + skipped = schedule.get_jobs(_SCAN_TAG) + for job in schedule.jobs[:]: + if job not in skipped: + job.run() while True: schedule.run_pending() @@ -124,7 +132,7 @@ def configure_jobs() -> None: target=scheduled_update, daemon=True, task_name='Theme scan and queue', - ).tag('themerr') + ).tag('themerr', _SCAN_TAG) schedule.every(max(15, int(config.CONFIG['Themerr']['INT_UPDATE_DATABASE_CACHE_INTERVAL']))).minutes.do( job_func=run_threaded, diff --git a/tests/unit/themerr/test_scheduled_tasks.py b/tests/unit/themerr/test_scheduled_tasks.py index 71643845..c590c719 100644 --- a/tests/unit/themerr/test_scheduled_tasks.py +++ b/tests/unit/themerr/test_scheduled_tasks.py @@ -4,6 +4,9 @@ from threading import Event from unittest.mock import Mock +# lib imports +import pytest + # local imports from themerr import scheduled_tasks @@ -39,7 +42,7 @@ def test_scheduled_task_failure_is_logged_and_thread_exits(monkeypatch): assert error.call_args.args[:2] == ('Scheduled task failed: %s (%.1f seconds)', 'Theme scan and queue') -def test_schedule_loop(monkeypatch): +def test_schedule_loop(configured, monkeypatch): run_all = Mock() pending = Mock() @@ -127,3 +130,40 @@ def test_reconfigure_changes_intervals_without_duplicating_or_removing_other_job assert scheduler.get_jobs('other') == [unrelated] finally: scheduler.clear() + + +def test_scan_on_boot_defaults_to_enabled(configured): + assert configured['Themerr']['BOOL_SCAN_ON_BOOT'] is True + + +def test_disabled_scan_on_boot_survives_a_config_reload(configured): + from common import config + configured['Themerr']['BOOL_SCAN_ON_BOOT'] = False + assert config.save_config(configured) + reloaded = config.create_config(configured.filename) + assert reloaded['Themerr']['BOOL_SCAN_ON_BOOT'] is False + + +def test_schedule_loop_skips_only_the_startup_scan_when_disabled(configured, monkeypatch): + scheduler = scheduled_tasks.schedule + scheduler.clear() + dispatched = [] + monkeypatch.setattr(scheduled_tasks, 'run_threaded', lambda **kwargs: dispatched.append(kwargs['task_name'])) + + def stop(seconds): + raise RuntimeError('stop') + + monkeypatch.setattr(scheduled_tasks.time, 'sleep', stop) + try: + configured['Themerr']['BOOL_THEMERR_ENABLED'] = True + configured['Themerr']['BOOL_SCAN_ON_BOOT'] = False + scheduled_tasks.configure_jobs() + with pytest.raises(RuntimeError): + scheduled_tasks.schedule_loop() + assert 'Theme scan and queue' not in dispatched + assert 'Dashboard refresh' in dispatched + scan_jobs = scheduler.get_jobs('theme-scan') + assert len(scan_jobs) == 1 + assert scan_jobs[0].next_run is not None + finally: + scheduler.clear()