style: apply formatter cleanup (import order, whitespace)
This commit is contained in:
@@ -54,7 +54,7 @@ This changelog follows [Keep a Changelog](https://keepachangelog.com/) principle
|
||||
- **`NfoRepairService` (`src/core/services/nfo_repair_service.py`)**: New service
|
||||
that detects incomplete `tvshow.nfo` files and triggers TMDB re-fetch.
|
||||
Provides `parse_nfo_tags()`, `find_missing_tags()`, `nfo_needs_repair()`, and
|
||||
`NfoRepairService.repair_series()`. 13 required tags are checked.
|
||||
`NfoRepairService.repair_series()`. 13 required tags are checked.
|
||||
- **`perform_nfo_repair_scan()` startup hook
|
||||
(`src/server/services/initialization_service.py`)**: New async function
|
||||
called during application startup. Iterates every series directory, checks
|
||||
|
||||
@@ -639,33 +639,33 @@ curl -X POST "http://127.0.0.1:8000/api/scheduler/config" \
|
||||
## 10. Tag Reference
|
||||
|
||||
The table below lists every XML tag written to `tvshow.nfo` and its source in
|
||||
the TMDB API response. All tags are written whenever the NFO is created or
|
||||
the TMDB API response. All tags are written whenever the NFO is created or
|
||||
updated via `create_tvshow_nfo()` / `update_tvshow_nfo()`.
|
||||
|
||||
| NFO tag | TMDB source field | Required |
|
||||
| --------------- | ---------------------------------------------- | -------- |
|
||||
| `title` | `name` | ✅ |
|
||||
| `originaltitle` | `original_name` | ✅ |
|
||||
| `showtitle` | `name` (same as `title`) | ✅ |
|
||||
| `sorttitle` | `name` (same as `title`) | ✅ |
|
||||
| `year` | First 4 chars of `first_air_date` | ✅ |
|
||||
| `plot` | `overview` | ✅ |
|
||||
| `outline` | `overview` (same as `plot`) | ✅ |
|
||||
| `tagline` | `tagline` | optional |
|
||||
| `runtime` | `episode_run_time[0]` | ✅ |
|
||||
| `premiered` | `first_air_date` | ✅ |
|
||||
| `status` | `status` | ✅ |
|
||||
| `mpaa` | US content rating from `content_ratings.results`| optional |
|
||||
| `fsk` | DE content rating (written as `mpaa` when preferred) | optional |
|
||||
| `imdbid` | `external_ids.imdb_id` | ✅ |
|
||||
| `tmdbid` | `id` | ✅ |
|
||||
| `tvdbid` | `external_ids.tvdb_id` | optional |
|
||||
| `genre` | `genres[].name` (one element per genre) | ✅ |
|
||||
| `studio` | `networks[].name` (one element per network) | ✅ |
|
||||
| `country` | `origin_country[]` or `production_countries[].name` | ✅ |
|
||||
| `actor` | `credits.cast[]` (top 10, with name/role/thumb)| ✅ |
|
||||
| `watched` | Always `false` on creation | ✅ |
|
||||
| `dateadded` | System clock at creation time (`YYYY-MM-DD HH:MM:SS`) | ✅ |
|
||||
| NFO tag | TMDB source field | Required |
|
||||
| --------------- | ----------------------------------------------------- | -------- |
|
||||
| `title` | `name` | ✅ |
|
||||
| `originaltitle` | `original_name` | ✅ |
|
||||
| `showtitle` | `name` (same as `title`) | ✅ |
|
||||
| `sorttitle` | `name` (same as `title`) | ✅ |
|
||||
| `year` | First 4 chars of `first_air_date` | ✅ |
|
||||
| `plot` | `overview` | ✅ |
|
||||
| `outline` | `overview` (same as `plot`) | ✅ |
|
||||
| `tagline` | `tagline` | optional |
|
||||
| `runtime` | `episode_run_time[0]` | ✅ |
|
||||
| `premiered` | `first_air_date` | ✅ |
|
||||
| `status` | `status` | ✅ |
|
||||
| `mpaa` | US content rating from `content_ratings.results` | optional |
|
||||
| `fsk` | DE content rating (written as `mpaa` when preferred) | optional |
|
||||
| `imdbid` | `external_ids.imdb_id` | ✅ |
|
||||
| `tmdbid` | `id` | ✅ |
|
||||
| `tvdbid` | `external_ids.tvdb_id` | optional |
|
||||
| `genre` | `genres[].name` (one element per genre) | ✅ |
|
||||
| `studio` | `networks[].name` (one element per network) | ✅ |
|
||||
| `country` | `origin_country[]` or `production_countries[].name` | ✅ |
|
||||
| `actor` | `credits.cast[]` (top 10, with name/role/thumb) | ✅ |
|
||||
| `watched` | Always `false` on creation | ✅ |
|
||||
| `dateadded` | System clock at creation time (`YYYY-MM-DD HH:MM:SS`) | ✅ |
|
||||
|
||||
The mapping logic lives in `src/core/utils/nfo_mapper.py` (`tmdb_to_nfo_model`).
|
||||
The XML serialisation lives in `src/core/utils/nfo_generator.py`
|
||||
@@ -687,36 +687,36 @@ automatically repairs any that are missing required tags.
|
||||
`src/core/services/nfo_repair_service.py` parses each `tvshow.nfo` with
|
||||
`lxml` and checks for the 13 required tags listed below.
|
||||
3. **Repair** — Series whose NFO is incomplete are queued for background reload
|
||||
via `BackgroundLoaderService.add_series_loading_task()`. The background
|
||||
via `BackgroundLoaderService.add_series_loading_task()`. The background
|
||||
loader re-fetches metadata from TMDB and rewrites the NFO with all tags
|
||||
populated.
|
||||
|
||||
### Tags Checked (13 required)
|
||||
|
||||
| XPath | Tag name |
|
||||
| -------------- | --------------- |
|
||||
| `./title` | `title` |
|
||||
| XPath | Tag name |
|
||||
| ----------------- | --------------- |
|
||||
| `./title` | `title` |
|
||||
| `./originaltitle` | `originaltitle` |
|
||||
| `./year` | `year` |
|
||||
| `./plot` | `plot` |
|
||||
| `./runtime` | `runtime` |
|
||||
| `./premiered` | `premiered` |
|
||||
| `./status` | `status` |
|
||||
| `./imdbid` | `imdbid` |
|
||||
| `./genre` | `genre` |
|
||||
| `./studio` | `studio` |
|
||||
| `./country` | `country` |
|
||||
| `./actor/name` | `actor/name` |
|
||||
| `./watched` | `watched` |
|
||||
| `./year` | `year` |
|
||||
| `./plot` | `plot` |
|
||||
| `./runtime` | `runtime` |
|
||||
| `./premiered` | `premiered` |
|
||||
| `./status` | `status` |
|
||||
| `./imdbid` | `imdbid` |
|
||||
| `./genre` | `genre` |
|
||||
| `./studio` | `studio` |
|
||||
| `./country` | `country` |
|
||||
| `./actor/name` | `actor/name` |
|
||||
| `./watched` | `watched` |
|
||||
|
||||
### Log Messages
|
||||
|
||||
| Message | Meaning |
|
||||
| --- | --- |
|
||||
| `NFO repair scan complete: 0 of N series queued for repair` | All NFOs are complete — no action needed |
|
||||
| Message | Meaning |
|
||||
| ----------------------------------------------------------- | ------------------------------------------------- |
|
||||
| `NFO repair scan complete: 0 of N series queued for repair` | All NFOs are complete — no action needed |
|
||||
| `NFO repair scan complete: X of N series queued for repair` | X series had incomplete NFOs and have been queued |
|
||||
| `NFO repair scan skipped: TMDB API key not configured` | Set `tmdb_api_key` in `data/config.json` |
|
||||
| `NFO repair scan skipped: anime directory not configured` | Set `anime_directory` in `data/config.json` |
|
||||
| `NFO repair scan skipped: TMDB API key not configured` | Set `tmdb_api_key` in `data/config.json` |
|
||||
| `NFO repair scan skipped: anime directory not configured` | Set `anime_directory` in `data/config.json` |
|
||||
|
||||
### Triggering a Manual Repair
|
||||
|
||||
@@ -731,11 +731,11 @@ This calls `NFOService.update_tvshow_nfo()` directly and overwrites the existing
|
||||
|
||||
### Source Files
|
||||
|
||||
| File | Purpose |
|
||||
| ---- | ------- |
|
||||
| `src/core/services/nfo_repair_service.py` | `REQUIRED_TAGS`, `parse_nfo_tags`, `find_missing_tags`, `nfo_needs_repair`, `NfoRepairService` |
|
||||
| `src/server/services/initialization_service.py` | `perform_nfo_repair_scan` startup hook |
|
||||
| `src/server/fastapi_app.py` | Wires `perform_nfo_repair_scan` into the lifespan |
|
||||
| File | Purpose |
|
||||
| ----------------------------------------------- | ---------------------------------------------------------------------------------------------- |
|
||||
| `src/core/services/nfo_repair_service.py` | `REQUIRED_TAGS`, `parse_nfo_tags`, `find_missing_tags`, `nfo_needs_repair`, `NfoRepairService` |
|
||||
| `src/server/services/initialization_service.py` | `perform_nfo_repair_scan` startup hook |
|
||||
| `src/server/fastapi_app.py` | Wires `perform_nfo_repair_scan` into the lifespan |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -219,8 +219,8 @@ async def lifespan(_application: FastAPI):
|
||||
from src.server.services.initialization_service import (
|
||||
perform_initial_setup,
|
||||
perform_media_scan_if_needed,
|
||||
perform_nfo_scan_if_needed,
|
||||
perform_nfo_repair_scan,
|
||||
perform_nfo_scan_if_needed,
|
||||
)
|
||||
|
||||
try:
|
||||
|
||||
@@ -4,7 +4,7 @@ These tests confirm that:
|
||||
1. The lifespan calls perform_nfo_repair_scan after perform_media_scan_if_needed.
|
||||
2. Series with incomplete NFO files are queued via the background_loader.
|
||||
"""
|
||||
from unittest.mock import AsyncMock, MagicMock, patch, call
|
||||
from unittest.mock import AsyncMock, MagicMock, call, patch
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -15,6 +15,7 @@ class TestNfoRepairScanCalledOnStartup:
|
||||
def test_perform_nfo_repair_scan_imported_in_lifespan(self):
|
||||
"""fastapi_app.py lifespan imports perform_nfo_repair_scan."""
|
||||
import importlib
|
||||
|
||||
import src.server.fastapi_app as app_module
|
||||
|
||||
source = importlib.util.find_spec("src.server.fastapi_app").origin
|
||||
|
||||
@@ -27,8 +27,8 @@ from src.server.services.initialization_service import (
|
||||
_validate_anime_directory,
|
||||
perform_initial_setup,
|
||||
perform_media_scan_if_needed,
|
||||
perform_nfo_scan_if_needed,
|
||||
perform_nfo_repair_scan,
|
||||
perform_nfo_scan_if_needed,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@ from unittest.mock import AsyncMock, MagicMock
|
||||
import pytest
|
||||
|
||||
from src.core.services.nfo_repair_service import (
|
||||
NfoRepairService,
|
||||
REQUIRED_TAGS,
|
||||
NfoRepairService,
|
||||
find_missing_tags,
|
||||
nfo_needs_repair,
|
||||
parse_nfo_tags,
|
||||
|
||||
Reference in New Issue
Block a user