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:
2026-02-21 08:56:17 +01:00
parent ac7e15e1eb
commit 0265ae2a70
15 changed files with 1923 additions and 1628 deletions

View File

@@ -660,7 +660,10 @@ Return current application configuration.
"data_dir": "data",
"scheduler": {
"enabled": true,
"interval_minutes": 60
"interval_minutes": 60,
"schedule_time": "03:00",
"schedule_days": ["mon", "tue", "wed", "thu", "fri", "sat", "sun"],
"auto_download_after_rescan": false
},
"logging": {
"level": "INFO",
@@ -691,7 +694,9 @@ Apply an update to the configuration.
{
"scheduler": {
"enabled": true,
"interval_minutes": 30
"interval_minutes": 60,
"schedule_time": "06:30",
"schedule_days": ["mon", "wed", "fri"]
},
"logging": {
"level": "DEBUG"
@@ -1177,47 +1182,21 @@ Source: [src/server/api/nfo.py](../src/server/api/nfo.py#L637-L684)
Prefix: `/api/scheduler`
Source: [src/server/api/scheduler.py](../src/server/api/scheduler.py#L1-L122)
All GET/POST config responses share the same envelope:
```json
{
"success": true,
"config": { ... },
"status": { ... }
}
```
Source: [src/server/api/scheduler.py](../src/server/api/scheduler.py)
### GET /api/scheduler/config
Get current scheduler configuration.
**Authentication:** Required
**Response (200 OK):**
```json
{
"enabled": true,
"interval_minutes": 60
}
```
Source: [src/server/api/scheduler.py](../src/server/api/scheduler.py#L22-L42)
### POST /api/scheduler/config
Update scheduler configuration.
**Authentication:** Required
**Request Body:**
```json
{
"enabled": true,
"interval_minutes": 30
}
```
**Response (200 OK):** Updated scheduler configuration
Source: [src/server/api/scheduler.py](../src/server/api/scheduler.py#L45-L75)
### POST /api/scheduler/trigger-rescan
Manually trigger a library rescan.
Get current scheduler configuration and runtime status.
**Authentication:** Required
@@ -1226,11 +1205,65 @@ Manually trigger a library rescan.
```json
{
"success": true,
"config": {
"enabled": true,
"interval_minutes": 60,
"schedule_time": "03:00",
"schedule_days": ["mon", "tue", "wed", "thu", "fri", "sat", "sun"],
"auto_download_after_rescan": false
},
"status": {
"is_running": true,
"next_run": "2025-07-15T03:00:00+00:00",
"last_run": null,
"scan_in_progress": false
}
}
```
### POST /api/scheduler/config
Update scheduler configuration and apply changes immediately.
**Authentication:** Required
**Request Body (all fields optional, uses model defaults):**
```json
{
"enabled": true,
"schedule_time": "06:30",
"schedule_days": ["mon", "wed", "fri"],
"auto_download_after_rescan": true
}
```
**Response (200 OK):** Same envelope as GET, reflecting saved values.
**Validation errors (422):**
- `schedule_time` must match `HH:MM` (00:0023:59)
- `schedule_days` entries must be one of `mon tue wed thu fri sat sun`
- `interval_minutes` must be ≥ 1
### POST /api/scheduler/trigger-rescan
Manually trigger a library rescan (and auto-download if configured).
**Authentication:** Required
**Response (200 OK):**
```json
{
"message": "Rescan started successfully"
}
```
Source: [src/server/api/scheduler.py](../src/server/api/scheduler.py#L78-L122)
**Error responses:**
- `503` — SeriesApp not yet initialised
- `500` — Rescan failed unexpectedly
---

View File

@@ -39,6 +39,23 @@ This changelog follows [Keep a Changelog](https://keepachangelog.com/) principle
## [Unreleased] - 2026-01-18
### Added
- **Cron-based Scheduler**: Replaced the asyncio sleep-loop with APScheduler's `AsyncIOScheduler + CronTrigger`
- Schedule rescans at a specific **time of day** (`HH:MM`) on selected **days of the week**
- New `SchedulerConfig` fields: `schedule_time` (default `"03:00"`), `schedule_days` (default all 7), `auto_download_after_rescan` (default `false`)
- Old `interval_minutes` field retained for backward compatibility
- **Auto-download after rescan**: When `auto_download_after_rescan` is enabled, missing episodes are automatically queued for download after each scheduled rescan
- **Day-of-week UI**: New day-of-week pill toggles (MonSun) in the Settings → Scheduler section
- **Live config reload**: POST `/api/scheduler/config` reschedules the APScheduler job without restarting the application
- **Enriched API response**: GET/POST `/api/scheduler/config` now returns `{"success", "config", "status"}` envelope including `next_run`, `last_run`, and `scan_in_progress`
### Changed
- Scheduler API response format: previously returned flat config; now returns `{"success": true, "config": {...}, "status": {...}}`
- `reload_config()` is now a synchronous method accepting a `SchedulerConfig` argument (previously async, no arguments)
- Dependencies: added `APScheduler>=3.10.4` to `requirements.txt`
### Fixed
- **Series Visibility**: Fixed issue where series added to the database weren't appearing in the API/UI

View File

@@ -114,7 +114,10 @@ Location: `data/config.json`
"data_dir": "data",
"scheduler": {
"enabled": true,
"interval_minutes": 60
"interval_minutes": 60,
"schedule_time": "03:00",
"schedule_days": ["mon", "tue", "wed", "thu", "fri", "sat", "sun"],
"auto_download_after_rescan": false
},
"logging": {
"level": "INFO",
@@ -161,12 +164,17 @@ Source: [src/server/models/config.py](../src/server/models/config.py#L62-L66)
### 4.2 Scheduler Settings
Controls automatic library rescanning.
Controls automatic cron-based library rescanning (powered by APScheduler).
| Field | Type | Default | Description |
| ---------------------------- | ---- | ------- | -------------------------------------------- |
| `scheduler.enabled` | bool | `true` | Enable/disable automatic scans. |
| `scheduler.interval_minutes` | int | `60` | Minutes between automatic scans. Minimum: 1. |
| Field | Type | Default | Description |
| -------------------------------------- | ------------ | --------------------------------------------- | -------------------------------------------------------------------- |
| `scheduler.enabled` | bool | `true` | Enable/disable automatic scans. |
| `scheduler.interval_minutes` | int | `60` | Legacy field kept for backward compatibility. Minimum: 1. |
| `scheduler.schedule_time` | string | `"03:00"` | Daily run time in 24-h `HH:MM` format. |
| `scheduler.schedule_days` | list[string] | `["mon","tue","wed","thu","fri","sat","sun"]` | Days of the week to run the scan. Empty list disables the cron job. |
| `scheduler.auto_download_after_rescan` | bool | `false` | Automatically queue missing episodes for download after each rescan. |
Valid day abbreviations: `mon`, `tue`, `wed`, `thu`, `fri`, `sat`, `sun`.
Source: [src/server/models/config.py](../src/server/models/config.py#L5-L12)

View File

@@ -19,7 +19,7 @@ The application now features a comprehensive configuration system that allows us
- Organized into logical sections with clear labels and help text
- Real-time saving with immediate feedback
- Configuration validation to prevent invalid settings
- Full control over scheduler interval, logging options, and backup settings
- Full control over cron-based scheduler (time, days of week, auto-download), logging options, and backup settings
---
@@ -35,14 +35,14 @@ The application now features a comprehensive configuration system that allows us
- General Settings: Application name and data directory configuration
- Security Settings: Master password setup with strength indicator
- Anime Directory: Primary directory path for anime storage
- Scheduler Settings: Enable/disable scheduler and configure check interval (in minutes)
- Scheduler Settings: Enable/disable scheduler, configure daily run time, select days of week, and optionally auto-download missing episodes after rescan
- Logging Settings: Configure log level, file path, file size limits, and backup count
- Backup Settings: Enable automatic backups with configurable path and retention period
- NFO Settings: TMDB API key, auto-creation options, and media file download preferences
- **Enhanced Settings/Config Modal**: Comprehensive configuration interface accessible from main page:
- General Settings: Edit application name and data directory
- Anime Directory: Modify anime storage location with browse functionality
- Scheduler Configuration: Enable/disable and configure check interval for automated operations
- Scheduler Configuration: Enable/disable, set cron run time (`HH:MM`), select active days of the week, and toggle auto-download after rescan
- Logging Configuration: Full control over logging level, file rotation, and backup count
- Backup Configuration: Configure automatic backup settings including path and retention
- NFO Settings: Complete control over TMDB integration and media file downloads