Implement async series data loading with background processing

- Add loading status fields to AnimeSeries model
- Create BackgroundLoaderService for async task processing
- Update POST /api/anime/add to return 202 Accepted immediately
- Add GET /api/anime/{key}/loading-status endpoint
- Integrate background loader with startup/shutdown lifecycle
- Create database migration script for loading status fields
- Add unit tests for BackgroundLoaderService (10 tests, all passing)
- Update AnimeSeriesService.create() to accept loading status fields

Architecture follows clean separation with no code duplication:
- BackgroundLoader orchestrates, doesn't reimplement
- Reuses existing AnimeService, NFOService, WebSocket patterns
- Database-backed status survives restarts
This commit is contained in:
2026-01-19 07:14:55 +01:00
parent df19f8ad95
commit f18c31a035
12 changed files with 3463 additions and 141 deletions

View File

@@ -10,9 +10,9 @@ This document describes the database schema, models, and data layer of the Aniwo
### Technology
- **Database Engine**: SQLite 3 (default), PostgreSQL supported
- **ORM**: SQLAlchemy 2.0 with async support (aiosqlite)
- **Location**: `data/aniworld.db` (configurable via `DATABASE_URL`)
- **Database Engine**: SQLite 3 (default), PostgreSQL supported
- **ORM**: SQLAlchemy 2.0 with async support (aiosqlite)
- **Location**: `data/aniworld.db` (configurable via `DATABASE_URL`)
Source: [src/config/settings.py](../src/config/settings.py#L53-L55)
@@ -73,15 +73,16 @@ Stores anime series metadata.
**Identifier Convention:**
- `key` is the **primary identifier** for all operations (e.g., `"attack-on-titan"`)
- `folder` is **metadata only** for filesystem operations (e.g., `"Attack on Titan (2013)"`)
- `id` is used only for database relationships
- `key` is the **primary identifier** for all operations (e.g., `"attack-on-titan"`)
- `folder` is **metadata only** for filesystem operations (e.g., `"Attack on Titan (2013)"`)
- `id` is used only for database relationships
Source: [src/server/database/models.py](../src/server/database/models.py#L23-L87)
### 3.2 episodes
Stores **missing episodes** that need to be downloaded. Episodes are automatically managed during scans:
- New missing episodes are added to the database
- Episodes that are no longer missing (files now exist) are removed from the database
- When an episode is downloaded, it can be marked with `is_downloaded=True` or removed from tracking
@@ -100,7 +101,7 @@ Stores **missing episodes** that need to be downloaded. Episodes are automatical
**Foreign Key:**
- `series_id` -> `anime_series.id` (ON DELETE CASCADE)
- `series_id` -> `anime_series.id` (ON DELETE CASCADE)
Source: [src/server/database/models.py](../src/server/database/models.py#L122-L181)
@@ -132,7 +133,7 @@ Stores download queue items with status tracking.
**Foreign Key:**
- `series_id` -> `anime_series.id` (ON DELETE CASCADE)
- `series_id` -> `anime_series.id` (ON DELETE CASCADE)
Source: [src/server/database/models.py](../src/server/database/models.py#L200-L300)
@@ -363,7 +364,7 @@ Source: [src/server/database/models.py](../src/server/database/models.py#L89-L11
### Cascade Rules
- Deleting `anime_series` deletes all related `episodes` and `download_queue_item`
- Deleting `anime_series` deletes all related `episodes` and `download_queue_item`
---