fix(anime-settings): restore GET /api/nfo/{key}/content for 'View NFO XML' button
The Anime Settings page (src/server/web/static/js/pages/anime-settings.js)
calls GET /api/nfo/{key}/content from its 'View NFO XML' button, but that
endpoint was removed during the NFO refactor (commits 21af502, a8e5487).
The frontend was never updated, so every click on the button 404'd.
Fix:
- Add NfoContentResponse model (key, folder, content, file_size,
last_modified) to src/server/models/nfo.py.
- Add GET /api/nfo/{key}/content handler to src/server/api/nfo.py that
reads <anime_directory>/<folder>/tvshow.nfo and returns it as
{"content": "<xml>", ...} — matching what anime-settings.js
viewNfoContent() already expects (data.content).
- Expose viewNfoContent on AniWorld.AnimeSettingsManager so it is
consistent with the other public methods and directly callable from
tests / other modules.
Tests:
- tests/api/test_nfo_endpoints.py: 4 new tests (auth-required, happy
path returning XML, 404 on unknown series, 404 on missing tvshow.nfo).
Also remove the file-local autouse 'reset_auth' fixture that wiped
the conftest's master-password setup and made any login-based test
fail with a stale-hash 'invalid credentials' error — that fixture
was pre-existing and is a no-op now that conftest.py handles reset.
- tests/frontend/unit/anime_settings.test.js: 3 new tests for
viewNfoContent (URL + auth header, writes <pre>, error toast on
404) and an assertion in the public-API surface test.
This commit is contained in:
@@ -4,6 +4,7 @@ Provides endpoints for NFO settings, repair, and validation for anime series.
|
||||
"""
|
||||
import logging
|
||||
import os
|
||||
from datetime import datetime
|
||||
from typing import Any, List, Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
@@ -11,6 +12,7 @@ from pydantic import BaseModel
|
||||
|
||||
from src.config.settings import settings
|
||||
from src.server.models.nfo import (
|
||||
NfoContentResponse,
|
||||
NfoRepairResponse,
|
||||
NfoSeriesSettings,
|
||||
NfoSettingsResponse,
|
||||
@@ -406,6 +408,74 @@ async def validate_nfo(
|
||||
)
|
||||
|
||||
|
||||
@router.get("/{key}/content", response_model=NfoContentResponse)
|
||||
async def get_nfo_content(
|
||||
key: str,
|
||||
_auth: dict = Depends(require_auth),
|
||||
anime_service: AnimeService = Depends(get_anime_service),
|
||||
) -> NfoContentResponse:
|
||||
"""Read and return the raw tvshow.nfo XML for a series.
|
||||
|
||||
Used by the Anime Settings page's "View NFO XML" button to display the
|
||||
on-disk NFO contents inside a ``<pre>`` block. The XML is returned as
|
||||
plain text inside a JSON wrapper so the same auth/header pipeline as the
|
||||
other NFO endpoints can be reused.
|
||||
|
||||
Args:
|
||||
key: Series unique key
|
||||
_auth: Authentication dependency
|
||||
anime_service: AnimeService dependency
|
||||
|
||||
Returns:
|
||||
NfoContentResponse with raw XML in ``content``, the on-disk path,
|
||||
file size and last-modified timestamp.
|
||||
|
||||
Raises:
|
||||
HTTPException 404: If the series or its tvshow.nfo file is not found
|
||||
HTTPException 400: If the series has no folder configured
|
||||
HTTPException 503: If ``settings.anime_directory`` is not configured
|
||||
"""
|
||||
series_data = await _get_series_data(anime_service, key)
|
||||
if not series_data:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f"Series not found: {key}",
|
||||
)
|
||||
|
||||
folder = series_data.get("folder", "")
|
||||
if not folder:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=f"Series has no folder configured: {key}",
|
||||
)
|
||||
|
||||
nfo_path = _get_nfo_path(folder)
|
||||
if not os.path.isfile(nfo_path):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f"No tvshow.nfo file found for series '{key}'",
|
||||
)
|
||||
|
||||
try:
|
||||
stat = os.stat(nfo_path)
|
||||
with open(nfo_path, "r", encoding="utf-8") as f:
|
||||
xml_text = f.read()
|
||||
except OSError as exc:
|
||||
logger.error("Failed to read NFO file %s: %s", nfo_path, exc)
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"Failed to read NFO file: {exc}",
|
||||
) from exc
|
||||
|
||||
return NfoContentResponse(
|
||||
key=key,
|
||||
folder=folder,
|
||||
content=xml_text,
|
||||
file_size=stat.st_size,
|
||||
last_modified=datetime.fromtimestamp(stat.st_mtime),
|
||||
)
|
||||
|
||||
|
||||
@router.get("/needs-repair", response_model=NfoNeedsRepairListResponse)
|
||||
async def get_series_needing_repair(
|
||||
_auth: dict = Depends(require_auth),
|
||||
|
||||
Reference in New Issue
Block a user