feat: cron-based scheduler with auto-download after rescan
- Replace asyncio sleep loop with APScheduler AsyncIOScheduler + CronTrigger
- Add schedule_time (HH:MM), schedule_days (days of week), auto_download_after_rescan fields to SchedulerConfig
- Add _auto_download_missing() to queue missing episodes after rescan
- Reload config live via reload_config(SchedulerConfig) without restart
- Update GET/POST /api/scheduler/config to return {success, config, status} envelope
- Add day-of-week pill toggles to Settings -> Scheduler section in UI
- Update JS loadSchedulerConfig / saveSchedulerConfig for new API shape
- Add 29 unit tests for SchedulerConfig model, 18 unit tests for SchedulerService
- Rewrite 23 endpoint tests and 36 integration tests for APScheduler behaviour
- Coverage: 96% api/scheduler, 95% scheduler_service, 90% total (>= 80% threshold)
- Update docs: API.md, CONFIGURATION.md, features.md, CHANGELOG.md
This commit is contained in:
@@ -254,17 +254,46 @@
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="config-item" id="rescan-interval-config">
|
||||
<label for="scheduled-rescan-interval" data-text="rescan-interval">Check Interval (minutes):</label>
|
||||
<input type="number" id="scheduled-rescan-interval" value="60" min="1" class="input-field">
|
||||
<small class="config-hint" data-text="rescan-interval-hint">
|
||||
How often to check for new episodes (minimum 1 minute)
|
||||
</small>
|
||||
<div class="config-item" id="rescan-time-config">
|
||||
<label for="scheduled-rescan-time" data-text="rescan-time">Run at:</label>
|
||||
<input type="time" id="scheduled-rescan-time" value="03:00" class="input-field">
|
||||
</div>
|
||||
|
||||
<div class="config-item" id="rescan-time-config">
|
||||
<label for="scheduled-rescan-time" data-text="rescan-time">Rescan Time (24h format):</label>
|
||||
<input type="time" id="scheduled-rescan-time" value="03:00" class="input-field">
|
||||
<div class="config-item" id="rescan-days-config">
|
||||
<label data-text="rescan-days">Days of week:</label>
|
||||
<div class="scheduler-days-container">
|
||||
<label class="scheduler-day-toggle-label">
|
||||
<input type="checkbox" id="scheduler-day-mon" checked class="scheduler-day-checkbox">
|
||||
<span class="scheduler-day-label" data-text="day-mon">Mon</span>
|
||||
</label>
|
||||
<label class="scheduler-day-toggle-label">
|
||||
<input type="checkbox" id="scheduler-day-tue" checked class="scheduler-day-checkbox">
|
||||
<span class="scheduler-day-label" data-text="day-tue">Tue</span>
|
||||
</label>
|
||||
<label class="scheduler-day-toggle-label">
|
||||
<input type="checkbox" id="scheduler-day-wed" checked class="scheduler-day-checkbox">
|
||||
<span class="scheduler-day-label" data-text="day-wed">Wed</span>
|
||||
</label>
|
||||
<label class="scheduler-day-toggle-label">
|
||||
<input type="checkbox" id="scheduler-day-thu" checked class="scheduler-day-checkbox">
|
||||
<span class="scheduler-day-label" data-text="day-thu">Thu</span>
|
||||
</label>
|
||||
<label class="scheduler-day-toggle-label">
|
||||
<input type="checkbox" id="scheduler-day-fri" checked class="scheduler-day-checkbox">
|
||||
<span class="scheduler-day-label" data-text="day-fri">Fri</span>
|
||||
</label>
|
||||
<label class="scheduler-day-toggle-label">
|
||||
<input type="checkbox" id="scheduler-day-sat" checked class="scheduler-day-checkbox">
|
||||
<span class="scheduler-day-label" data-text="day-sat">Sat</span>
|
||||
</label>
|
||||
<label class="scheduler-day-toggle-label">
|
||||
<input type="checkbox" id="scheduler-day-sun" checked class="scheduler-day-checkbox">
|
||||
<span class="scheduler-day-label" data-text="day-sun">Sun</span>
|
||||
</label>
|
||||
</div>
|
||||
<small class="config-hint" data-text="rescan-days-hint">
|
||||
Scheduler runs at the selected time on checked days. Uncheck all to disable scheduling.
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="config-item">
|
||||
@@ -276,11 +305,23 @@
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Advanced: legacy interval (hidden by default) -->
|
||||
<details class="config-advanced">
|
||||
<summary data-text="advanced-settings">Advanced</summary>
|
||||
<div class="config-item" id="rescan-interval-config">
|
||||
<label for="scheduled-rescan-interval" data-text="rescan-interval">Legacy Check Interval (minutes):</label>
|
||||
<input type="number" id="scheduled-rescan-interval" value="60" min="1" class="input-field">
|
||||
<small class="config-hint" data-text="rescan-interval-hint">
|
||||
Deprecated: only used if cron scheduling is not configured
|
||||
</small>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<div class="config-item scheduler-status" id="scheduler-status">
|
||||
<div class="scheduler-info">
|
||||
<div class="info-row">
|
||||
<span data-text="next-rescan">Next Scheduled Rescan:</span>
|
||||
<span id="next-rescan-time" class="info-value">-</span>
|
||||
<span id="scheduler-next-run" class="info-value">-</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span data-text="last-rescan">Last Scheduled Rescan:</span>
|
||||
|
||||
Reference in New Issue
Block a user