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:
115
docs/API.md
115
docs/API.md
@@ -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:00–23: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
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -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 (Mon–Sun) 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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user