Replaces the read-only 'NFO Diagnostics' page with a full per-anime
Settings page reached from the right-click context menu on series cards.
Users can now view and edit key, name, folder, tmdb_id, tvdb_id and site
for each anime; changes are persisted to the DB and optionally written
back to the NFO file or used to regenerate it.
Backend
- Rename NfoDiagnosticsResponse -> NfoSettingsResponse,
NfoSeriesDiagnostics -> NfoSeriesSettings
- Rename get_nfo_diagnostics -> get_nfo_settings,
repair_nfo -> repair_nfo_settings
- Fix nfo.py bug: repair was calling non-existent
update_series_nfo_status(); now uses update_nfo_status() and an
explicit AnimeSeriesService.update(nfo_path=...)
- New endpoints on /api/anime/{key}:
GET /settings -> AnimeSettingsResponse
PUT /settings -> AnimeSettingsResponse
(body: name/folder/tmdb_id/tvdb_id/site,
options: apply_to_nfo, rename_disk)
POST /regenerate-nfo -> AnimeSettingsRegenerateNfoResponse
- New Pydantic models: AnimeSettingsResponse,
AnimeSettingsUpdateRequest, AnimeSettingsRegenerateNfoResponse
- /anime/settings page route; /settings/nfo now 301-redirects to it
Frontend
- New AniWorld.AnimeSettingsManager JS module (single-page form,
no tabs) with public API init/loadSeries/saveSettings/regenerateNfo/
validateField/populateForm/showSaveSuccess/showError
- New anime-settings.html template + anime-settings.css
- Right-click menu: data-action 'nfo-diagnostics' replaced by
'anime-settings' (label 'Anime Settings'), navigates to
/anime/settings?key=...
- Library 'Open NFO Diagnostics' link renamed to 'Open Anime Settings'
Bug fix
- context-menu click handler was calling hide() BEFORE building the
navigation URL, which cleared currentSeriesKey to null and produced
/anime/settings?key=null. Captures the key into a local const first.
Regression-locked by tests/frontend/unit/context_menu.test.js.
Tests
- 21 new pytest tests in tests/api/test_anime_settings_endpoints.py
(GET/PUT/regenerate-nfo, auth, validation, nfo-repair bug regression)
- tests/api/test_nfo_endpoints.py trimmed to 6 focused tests
- 31 new Vitest unit tests for AnimeSettingsManager
- 5 new Vitest unit tests for ContextMenu (incl. source-invariant
regression guard for the hide()-before-key bug)
- 5 new Playwright E2E tests covering right-click, direct nav,
legacy /settings/nfo redirect, and context-menu labels
- New vitest.config.js (environment: happy-dom)
Docs
- Docs/API.md: new section 'Anime Settings Endpoints'
- Docs/CHANGELOG.md: documents the rename and the context-menu bug fix
Verified
- pytest: 27/27 (21 new + 6 trimmed nfo)
- vitest: 36/36 (31 anime-settings + 5 context-menu)
- playwright e2e: 5/5
99 lines
2.7 KiB
Python
99 lines
2.7 KiB
Python
"""
|
|
Page controller for serving HTML templates.
|
|
|
|
This module provides endpoints for serving HTML pages using Jinja2 templates.
|
|
"""
|
|
from fastapi import APIRouter, Request
|
|
from fastapi.responses import HTMLResponse
|
|
|
|
from src.server.utils.template_helpers import render_template
|
|
|
|
router = APIRouter(tags=["pages"])
|
|
|
|
|
|
@router.get("/", response_class=HTMLResponse)
|
|
async def root(request: Request):
|
|
"""Serve the main application page."""
|
|
return render_template(
|
|
"index.html",
|
|
request,
|
|
title="Aniworld Download Manager"
|
|
)
|
|
|
|
|
|
@router.get("/setup", response_class=HTMLResponse)
|
|
async def setup_page(request: Request):
|
|
"""Serve the setup page."""
|
|
return render_template(
|
|
"setup.html",
|
|
request,
|
|
title="Setup - Aniworld"
|
|
)
|
|
|
|
|
|
@router.get("/login", response_class=HTMLResponse)
|
|
async def login_page(request: Request):
|
|
"""Serve the login page."""
|
|
return render_template(
|
|
"login.html",
|
|
request,
|
|
title="Login - Aniworld"
|
|
)
|
|
|
|
|
|
@router.get("/queue", response_class=HTMLResponse)
|
|
async def queue_page(request: Request):
|
|
"""Serve the download queue page."""
|
|
return render_template(
|
|
"queue.html",
|
|
request,
|
|
title="Download Queue - Aniworld"
|
|
)
|
|
|
|
|
|
@router.get("/loading", response_class=HTMLResponse)
|
|
async def loading_page(request: Request):
|
|
"""Serve the initialization loading page."""
|
|
return render_template(
|
|
"loading.html",
|
|
request,
|
|
title="Initializing - Aniworld"
|
|
)
|
|
|
|
|
|
@router.get("/setup/unresolved", response_class=HTMLResponse)
|
|
async def unresolved_page(request: Request):
|
|
"""Serve the unresolved folders resolution page."""
|
|
return render_template(
|
|
"unresolved.html",
|
|
request,
|
|
title="Resolve Series - Aniworld"
|
|
)
|
|
|
|
|
|
@router.get("/settings/nfo", response_class=HTMLResponse)
|
|
async def nfo_settings_page_redirect():
|
|
"""Backwards-compatible redirect from the old NFO settings URL.
|
|
|
|
Older bookmarks and open tabs may still point at /settings/nfo —
|
|
redirect them to the new per-anime Anime Settings page.
|
|
"""
|
|
from fastapi.responses import RedirectResponse
|
|
|
|
return RedirectResponse(url="/anime/settings", status_code=301)
|
|
|
|
|
|
@router.get("/anime/settings", response_class=HTMLResponse)
|
|
async def anime_settings_page(request: Request):
|
|
"""Serve the per-anime Anime Settings page.
|
|
|
|
Replaces the old NFO Diagnostics page. The same template is used
|
|
for all series — the active series key is passed via the
|
|
``?key=...`` query parameter and consumed by the page's JS.
|
|
"""
|
|
return render_template(
|
|
"anime-settings.html",
|
|
request,
|
|
title="Anime Settings - Aniworld"
|
|
)
|