Fix NFO creation 500 error for missing folders
- Auto-create series folder if it doesn't exist - Add unit and integration tests for folder creation - NFO creation now works for newly added series
This commit is contained in:
@@ -119,10 +119,32 @@ For each task completed:
|
||||
|
||||
## TODO List:
|
||||
|
||||
All tasks completed! The NFO database query issue has been resolved.
|
||||
All tasks completed! Recent issues have been resolved.
|
||||
|
||||
## Recently Fixed Issues:
|
||||
|
||||
### ✅ Fixed: NFO Creation 500 Error - Missing Folder (2026-01-18)
|
||||
|
||||
**Issue:** POST http://127.0.0.1:8000/api/nfo/blue-period/create returns 500 (Internal Server Error) with message "Series folder not found: /home/lukas/Volume/serien/Blue Period"
|
||||
|
||||
**Root Cause:** The NFO service was checking if the series folder existed before creating NFO files, and raising a FileNotFoundError if it didn't. This prevented users from creating NFO files for newly added series where no episodes had been downloaded yet.
|
||||
|
||||
**Solution:** Modified `create_tvshow_nfo()` in `nfo_service.py` to automatically create the series folder (with `mkdir(parents=True, exist_ok=True)`) if it doesn't exist, instead of raising an error.
|
||||
|
||||
**Files Modified:**
|
||||
- [src/core/services/nfo_service.py](../src/core/services/nfo_service.py)
|
||||
|
||||
**Tests Added:**
|
||||
- [tests/unit/test_nfo_service_folder_creation.py](../tests/unit/test_nfo_service_folder_creation.py) - Unit test verifying folder creation
|
||||
- [tests/integration/test_nfo_folder_creation.py](../tests/integration/test_nfo_folder_creation.py) - Integration test for end-to-end verification
|
||||
|
||||
**Verification:**
|
||||
- Unit and integration tests pass
|
||||
- NFO files can now be created for series even when the folder doesn't exist
|
||||
- Folder is automatically created when needed
|
||||
|
||||
---
|
||||
|
||||
### ✅ Fixed: NFO Service 503 Error (2026-01-18)
|
||||
|
||||
**Issue:** Failed to load resource: the server responded with a status of 503 (Service Unavailable) when creating NFO files.
|
||||
@@ -132,6 +154,7 @@ All tasks completed! The NFO database query issue has been resolved.
|
||||
**Solution:** Updated `fastapi_app.py` startup code to sync NFO configuration (including TMDB API key) from `data/config.json` to `settings` object, similar to how `anime_directory` was already being synced.
|
||||
|
||||
**Files Modified:**
|
||||
|
||||
- [src/server/fastapi_app.py](../src/server/fastapi_app.py)
|
||||
|
||||
**Verification:** NFO endpoints now return 200 OK instead of 503, and NFO creation is functional.
|
||||
|
||||
@@ -100,7 +100,8 @@ class NFOService:
|
||||
|
||||
folder_path = self.anime_directory / serie_folder
|
||||
if not folder_path.exists():
|
||||
raise FileNotFoundError(f"Series folder not found: {folder_path}")
|
||||
logger.info(f"Creating series folder: {folder_path}")
|
||||
folder_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
async with self.tmdb_client:
|
||||
# Search for TV show
|
||||
|
||||
114
tests/integration/test_nfo_folder_creation.py
Normal file
114
tests/integration/test_nfo_folder_creation.py
Normal file
@@ -0,0 +1,114 @@
|
||||
"""Integration test for NFO creation with missing folder.
|
||||
|
||||
Tests that NFO creation works when the series folder doesn't exist yet.
|
||||
"""
|
||||
from pathlib import Path
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from src.core.services.nfo_service import NFOService
|
||||
from src.core.services.tmdb_client import TMDBClient
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_nfo_creation_with_missing_folder_integration():
|
||||
"""Integration test: NFO creation creates folder if missing."""
|
||||
# Use actual temp directory for this test
|
||||
import tempfile
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
anime_dir = Path(tmpdir)
|
||||
serie_folder = "Test Anime Series"
|
||||
folder_path = anime_dir / serie_folder
|
||||
|
||||
# Verify folder doesn't exist
|
||||
assert not folder_path.exists()
|
||||
|
||||
# Create NFO service
|
||||
nfo_service = NFOService(
|
||||
tmdb_api_key="test_key",
|
||||
anime_directory=str(anime_dir),
|
||||
image_size="original"
|
||||
)
|
||||
|
||||
# Mock TMDB responses
|
||||
mock_search = {
|
||||
"results": [{
|
||||
"id": 99999,
|
||||
"name": "Test Anime Series",
|
||||
"first_air_date": "2023-01-01",
|
||||
"overview": "Test",
|
||||
"vote_average": 8.0
|
||||
}]
|
||||
}
|
||||
|
||||
mock_details = {
|
||||
"id": 99999,
|
||||
"name": "Test Anime Series",
|
||||
"first_air_date": "2023-01-01",
|
||||
"overview": "Test description",
|
||||
"vote_average": 8.0,
|
||||
"genres": [],
|
||||
"networks": [],
|
||||
"status": "Returning Series",
|
||||
"number_of_seasons": 1,
|
||||
"number_of_episodes": 10,
|
||||
"poster_path": None,
|
||||
"backdrop_path": None
|
||||
}
|
||||
|
||||
mock_ratings = {"results": []}
|
||||
|
||||
# Patch TMDB client methods
|
||||
with patch.object(
|
||||
nfo_service.tmdb_client, 'search_tv_show',
|
||||
new_callable=AsyncMock
|
||||
) as mock_search_method, \
|
||||
patch.object(
|
||||
nfo_service.tmdb_client, 'get_tv_show_details',
|
||||
new_callable=AsyncMock
|
||||
) as mock_details_method, \
|
||||
patch.object(
|
||||
nfo_service.tmdb_client, 'get_tv_show_content_ratings',
|
||||
new_callable=AsyncMock
|
||||
) as mock_ratings_method, \
|
||||
patch.object(
|
||||
nfo_service, '_download_media_files',
|
||||
new_callable=AsyncMock
|
||||
) as mock_download:
|
||||
|
||||
mock_search_method.return_value = mock_search
|
||||
mock_details_method.return_value = mock_details
|
||||
mock_ratings_method.return_value = mock_ratings
|
||||
mock_download.return_value = {
|
||||
"poster": False,
|
||||
"logo": False,
|
||||
"fanart": False
|
||||
}
|
||||
|
||||
# Create NFO - this should create the folder
|
||||
nfo_path = await nfo_service.create_tvshow_nfo(
|
||||
serie_name="Test Anime Series",
|
||||
serie_folder=serie_folder,
|
||||
year=2023,
|
||||
download_poster=False,
|
||||
download_logo=False,
|
||||
download_fanart=False
|
||||
)
|
||||
|
||||
# Verify folder was created
|
||||
assert folder_path.exists(), "Series folder should have been created"
|
||||
assert folder_path.is_dir(), "Series folder should be a directory"
|
||||
|
||||
# Verify NFO file exists
|
||||
assert nfo_path.exists(), "NFO file should exist"
|
||||
assert nfo_path.name == "tvshow.nfo", "NFO file should be named tvshow.nfo"
|
||||
assert nfo_path.parent == folder_path, "NFO should be in series folder"
|
||||
|
||||
# Verify NFO file has content
|
||||
nfo_content = nfo_path.read_text()
|
||||
assert "<tvshow>" in nfo_content, "NFO should contain tvshow tag"
|
||||
assert "<title>Test Anime Series</title>" in nfo_content, "NFO should contain title"
|
||||
|
||||
print(f"✓ Test passed: Folder created at {folder_path}")
|
||||
print(f"✓ NFO file created at {nfo_path}")
|
||||
121
tests/unit/test_nfo_service_folder_creation.py
Normal file
121
tests/unit/test_nfo_service_folder_creation.py
Normal file
@@ -0,0 +1,121 @@
|
||||
"""Unit tests for NFO service folder creation.
|
||||
|
||||
Tests that the NFO service correctly creates series folders when they don't exist.
|
||||
"""
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from src.core.services.nfo_service import NFOService
|
||||
|
||||
|
||||
class TestNFOServiceFolderCreation:
|
||||
"""Test NFO service creates folders when needed."""
|
||||
|
||||
@pytest.fixture
|
||||
def temp_anime_dir(self):
|
||||
"""Create temporary anime directory."""
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
yield Path(tmpdir)
|
||||
|
||||
@pytest.fixture
|
||||
def nfo_service(self, temp_anime_dir):
|
||||
"""Create NFO service with temporary directory."""
|
||||
return NFOService(
|
||||
tmdb_api_key="test_api_key",
|
||||
anime_directory=str(temp_anime_dir),
|
||||
image_size="original",
|
||||
auto_create=False
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_nfo_creates_missing_folder(
|
||||
self, nfo_service, temp_anime_dir
|
||||
):
|
||||
"""Test that create_tvshow_nfo creates folder if it doesn't exist."""
|
||||
serie_folder = "Test Series"
|
||||
folder_path = temp_anime_dir / serie_folder
|
||||
|
||||
# Verify folder doesn't exist initially
|
||||
assert not folder_path.exists()
|
||||
|
||||
# Mock TMDB client responses
|
||||
mock_search_results = {
|
||||
"results": [
|
||||
{
|
||||
"id": 12345,
|
||||
"name": "Test Series",
|
||||
"first_air_date": "2023-01-01",
|
||||
"overview": "Test overview",
|
||||
"vote_average": 8.5
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
mock_details = {
|
||||
"id": 12345,
|
||||
"name": "Test Series",
|
||||
"first_air_date": "2023-01-01",
|
||||
"overview": "Test overview",
|
||||
"vote_average": 8.5,
|
||||
"genres": [{"id": 16, "name": "Animation"}],
|
||||
"networks": [{"name": "Test Network"}],
|
||||
"status": "Returning Series",
|
||||
"number_of_seasons": 1,
|
||||
"number_of_episodes": 12,
|
||||
"poster_path": "/test_poster.jpg",
|
||||
"backdrop_path": "/test_backdrop.jpg"
|
||||
}
|
||||
|
||||
mock_content_ratings = {
|
||||
"results": [
|
||||
{"iso_3166_1": "DE", "rating": "12"}
|
||||
]
|
||||
}
|
||||
|
||||
with patch.object(
|
||||
nfo_service.tmdb_client, 'search_tv_show',
|
||||
new_callable=AsyncMock
|
||||
) as mock_search, \
|
||||
patch.object(
|
||||
nfo_service.tmdb_client, 'get_tv_show_details',
|
||||
new_callable=AsyncMock
|
||||
) as mock_details_call, \
|
||||
patch.object(
|
||||
nfo_service.tmdb_client, 'get_tv_show_content_ratings',
|
||||
new_callable=AsyncMock
|
||||
) as mock_ratings, \
|
||||
patch.object(
|
||||
nfo_service, '_download_media_files',
|
||||
new_callable=AsyncMock
|
||||
) as mock_download:
|
||||
|
||||
mock_search.return_value = mock_search_results
|
||||
mock_details_call.return_value = mock_details
|
||||
mock_ratings.return_value = mock_content_ratings
|
||||
mock_download.return_value = {
|
||||
"poster": False,
|
||||
"logo": False,
|
||||
"fanart": False
|
||||
}
|
||||
|
||||
# Call create_tvshow_nfo
|
||||
nfo_path = await nfo_service.create_tvshow_nfo(
|
||||
serie_name="Test Series",
|
||||
serie_folder=serie_folder,
|
||||
year=2023,
|
||||
download_poster=False,
|
||||
download_logo=False,
|
||||
download_fanart=False
|
||||
)
|
||||
|
||||
# Verify folder was created
|
||||
assert folder_path.exists()
|
||||
assert folder_path.is_dir()
|
||||
|
||||
# Verify NFO file was created
|
||||
assert nfo_path.exists()
|
||||
assert nfo_path.name == "tvshow.nfo"
|
||||
assert nfo_path.parent == folder_path
|
||||
Reference in New Issue
Block a user