feat: Add NFO integration test script
- Created scripts/test_nfo_integration.py for manual testing - Tests TMDB client, NFO generation, and complete workflow - Requires real TMDB API key (not for CI) - Downloads real data and creates sample files in test_output/ - Provides Kodi compatibility verification - Updated task3_status.md with testing challenges and approach
This commit is contained in:
@@ -1,12 +1,15 @@
|
|||||||
# Task 3: NFO Metadata Integration - Status Report
|
# Task 3: NFO Metadata Integration - Status Report
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|
||||||
Task 3 focuses on creating tvshow.nfo files and downloading media (poster/logo/fanart) using TMDB API, adapted from the scraper repository.
|
Task 3 focuses on creating tvshow.nfo files and downloading media (poster/logo/fanart) using TMDB API, adapted from the scraper repository.
|
||||||
|
|
||||||
## ✅ Completed (80%)
|
## ✅ Completed (80%)
|
||||||
|
|
||||||
### 1. Core Infrastructure (100%)
|
### 1. Core Infrastructure (100%)
|
||||||
|
|
||||||
- ✅ **TMDB API Client** (`src/core/services/tmdb_client.py` - 270 lines)
|
- ✅ **TMDB API Client** (`src/core/services/tmdb_client.py` - 270 lines)
|
||||||
|
|
||||||
- Async HTTP client using aiohttp
|
- Async HTTP client using aiohttp
|
||||||
- Search TV shows by name and year
|
- Search TV shows by name and year
|
||||||
- Get detailed show information with external IDs
|
- Get detailed show information with external IDs
|
||||||
@@ -19,6 +22,7 @@ Task 3 focuses on creating tvshow.nfo files and downloading media (poster/logo/f
|
|||||||
- Context manager support
|
- Context manager support
|
||||||
|
|
||||||
- ✅ **NFO XML Generator** (`src/core/utils/nfo_generator.py` - 180 lines)
|
- ✅ **NFO XML Generator** (`src/core/utils/nfo_generator.py` - 180 lines)
|
||||||
|
|
||||||
- Generate Kodi/XBMC XML from TVShowNFO models
|
- Generate Kodi/XBMC XML from TVShowNFO models
|
||||||
- Handle all standard Kodi fields
|
- Handle all standard Kodi fields
|
||||||
- Support ratings, actors, images, unique IDs
|
- Support ratings, actors, images, unique IDs
|
||||||
@@ -27,6 +31,7 @@ Task 3 focuses on creating tvshow.nfo files and downloading media (poster/logo/f
|
|||||||
- Handle special characters and Unicode
|
- Handle special characters and Unicode
|
||||||
|
|
||||||
- ✅ **Image Downloader** (`src/core/utils/image_downloader.py` - 296 lines)
|
- ✅ **Image Downloader** (`src/core/utils/image_downloader.py` - 296 lines)
|
||||||
|
|
||||||
- Download images from URLs
|
- Download images from URLs
|
||||||
- Validate images using PIL (format, size)
|
- Validate images using PIL (format, size)
|
||||||
- Retry logic with exponential backoff
|
- Retry logic with exponential backoff
|
||||||
@@ -40,13 +45,14 @@ Task 3 focuses on creating tvshow.nfo files and downloading media (poster/logo/f
|
|||||||
- Orchestrates TMDB client, NFO generator, and image downloader
|
- Orchestrates TMDB client, NFO generator, and image downloader
|
||||||
- check_nfo_exists() - Check if tvshow.nfo exists
|
- check_nfo_exists() - Check if tvshow.nfo exists
|
||||||
- create_tvshow_nfo() - Create NFO by scraping TMDB
|
- create_tvshow_nfo() - Create NFO by scraping TMDB
|
||||||
- _find_best_match() - Match search results with year filter
|
- \_find_best_match() - Match search results with year filter
|
||||||
- _tmdb_to_nfo_model() - Convert TMDB data to TVShowNFO model
|
- \_tmdb_to_nfo_model() - Convert TMDB data to TVShowNFO model
|
||||||
- _download_media_files() - Download poster/logo/fanart
|
- \_download_media_files() - Download poster/logo/fanart
|
||||||
- Handle search ambiguity
|
- Handle search ambiguity
|
||||||
- Proper error handling and logging
|
- Proper error handling and logging
|
||||||
|
|
||||||
### 2. Configuration (100%)
|
### 2. Configuration (100%)
|
||||||
|
|
||||||
- ✅ Added NFO settings to `src/config/settings.py`:
|
- ✅ Added NFO settings to `src/config/settings.py`:
|
||||||
- TMDB_API_KEY: API key for TMDB access
|
- TMDB_API_KEY: API key for TMDB access
|
||||||
- NFO_AUTO_CREATE: Auto-create NFOs when scanning (default: False)
|
- NFO_AUTO_CREATE: Auto-create NFOs when scanning (default: False)
|
||||||
@@ -57,6 +63,7 @@ Task 3 focuses on creating tvshow.nfo files and downloading media (poster/logo/f
|
|||||||
- NFO_IMAGE_SIZE: Image size to download (default: "original")
|
- NFO_IMAGE_SIZE: Image size to download (default: "original")
|
||||||
|
|
||||||
### 3. Dependencies (100%)
|
### 3. Dependencies (100%)
|
||||||
|
|
||||||
- ✅ Updated `requirements.txt`:
|
- ✅ Updated `requirements.txt`:
|
||||||
- aiohttp>=3.9.0 (async HTTP client)
|
- aiohttp>=3.9.0 (async HTTP client)
|
||||||
- lxml>=5.0.0 (XML generation/validation)
|
- lxml>=5.0.0 (XML generation/validation)
|
||||||
@@ -68,6 +75,7 @@ Task 3 focuses on creating tvshow.nfo files and downloading media (poster/logo/f
|
|||||||
### 1. Unit Tests (40% complete, needs major updates)
|
### 1. Unit Tests (40% complete, needs major updates)
|
||||||
|
|
||||||
**Current Status:**
|
**Current Status:**
|
||||||
|
|
||||||
- ✅ Test files created for all modules:
|
- ✅ Test files created for all modules:
|
||||||
- `tests/unit/test_tmdb_client.py` (16 tests, all failing)
|
- `tests/unit/test_tmdb_client.py` (16 tests, all failing)
|
||||||
- `tests/unit/test_nfo_generator.py` (21 tests, 18 passing, 3 failing)
|
- `tests/unit/test_nfo_generator.py` (21 tests, 18 passing, 3 failing)
|
||||||
@@ -77,6 +85,7 @@ Task 3 focuses on creating tvshow.nfo files and downloading media (poster/logo/f
|
|||||||
The tests were written based on assumptions about the API that don't match the actual implementation:
|
The tests were written based on assumptions about the API that don't match the actual implementation:
|
||||||
|
|
||||||
1. **ImageDownloader Issues:**
|
1. **ImageDownloader Issues:**
|
||||||
|
|
||||||
- Tests assume context manager (`__aenter__`), but not implemented
|
- Tests assume context manager (`__aenter__`), but not implemented
|
||||||
- Tests assume `_validate_image()` method, actual is `validate_image()` (no underscore)
|
- Tests assume `_validate_image()` method, actual is `validate_image()` (no underscore)
|
||||||
- Tests assume `session` attribute, but ImageDownloader creates sessions internally
|
- Tests assume `session` attribute, but ImageDownloader creates sessions internally
|
||||||
@@ -84,6 +93,7 @@ The tests were written based on assumptions about the API that don't match the a
|
|||||||
- Tests assume `ImageValidationError` exception, but only `ImageDownloadError` exists
|
- Tests assume `ImageValidationError` exception, but only `ImageDownloadError` exists
|
||||||
|
|
||||||
2. **NFO Generator Issues:**
|
2. **NFO Generator Issues:**
|
||||||
|
|
||||||
- 3 tests failing due to XML validation logic differences
|
- 3 tests failing due to XML validation logic differences
|
||||||
- Need to review actual lxml etree behavior
|
- Need to review actual lxml etree behavior
|
||||||
|
|
||||||
@@ -92,15 +102,20 @@ The tests were written based on assumptions about the API that don't match the a
|
|||||||
- Tests assume `_make_request()` method, need to verify API
|
- Tests assume `_make_request()` method, need to verify API
|
||||||
|
|
||||||
**Refactoring Needed:**
|
**Refactoring Needed:**
|
||||||
- Review actual implementation APIs
|
|
||||||
- Update test mocks to match implementation
|
- ❗ **Critical Challenge**: aiohttp mocking is complex due to nested async context managers
|
||||||
- Consider adding context manager support to ImageDownloader
|
- **Alternative Approach Recommended**:
|
||||||
- Simplify test approach - use @patch on aiohttp.ClientSession instead of internal mocking
|
1. Create integration test script with real API calls (see below)
|
||||||
|
2. Focus unit tests on business logic (NFO conversion, file operations)
|
||||||
|
3. Mock at higher level (mock `download_image` method, not aiohttp internals)
|
||||||
|
4. Consider adding dependency injection to make testing easier
|
||||||
|
- Or: Simplify implementation to use requests library (sync) for easier testing
|
||||||
- Add integration tests with real API calls (optional, for manual verification)
|
- Add integration tests with real API calls (optional, for manual verification)
|
||||||
|
|
||||||
### 2. Integration with SerieList (Not started)
|
### 2. Integration with SerieList (Not started)
|
||||||
|
|
||||||
**Needs Implementation:**
|
**Needs Implementation:**
|
||||||
|
|
||||||
- Integrate NFOService into SerieList scan process
|
- Integrate NFOService into SerieList scan process
|
||||||
- Add auto-create logic based on NFO_AUTO_CREATE setting
|
- Add auto-create logic based on NFO_AUTO_CREATE setting
|
||||||
- Add update logic based on NFO_UPDATE_ON_SCAN setting
|
- Add update logic based on NFO_UPDATE_ON_SCAN setting
|
||||||
@@ -110,6 +125,7 @@ The tests were written based on assumptions about the API that don't match the a
|
|||||||
|
|
||||||
**Optional Enhancement:**
|
**Optional Enhancement:**
|
||||||
Add CLI commands for NFO management:
|
Add CLI commands for NFO management:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Create NFO for specific series
|
# Create NFO for specific series
|
||||||
python src/cli/Main.py nfo create "Attack on Titan" --year 2013
|
python src/cli/Main.py nfo create "Attack on Titan" --year 2013
|
||||||
@@ -127,18 +143,22 @@ python src/cli/Main.py nfo status
|
|||||||
## 📊 Coverage Status
|
## 📊 Coverage Status
|
||||||
|
|
||||||
**Current:**
|
**Current:**
|
||||||
|
|
||||||
- `src/core/services/tmdb_client.py`: 0% (tests failing)
|
- `src/core/services/tmdb_client.py`: 0% (tests failing)
|
||||||
- `src/core/utils/nfo_generator.py`: 0% (tests failing)
|
- `src/core/utils/nfo_generator.py`: 0% (tests failing)
|
||||||
- `src/core/utils/image_downloader.py`: 0% (tests failing)
|
- `src/core/utils/image_downloader.py`: 0% (tests failing)
|
||||||
- `src/core/services/nfo_service.py`: Not tested yet
|
- `src/core/services/nfo_service.py`: Not tested yet
|
||||||
|
|
||||||
**Target:**
|
**Target:**
|
||||||
|
|
||||||
- All modules: > 85% coverage
|
- All modules: > 85% coverage
|
||||||
|
|
||||||
## 🔧 Next Steps (Priority Order)
|
## 🔧 Next Steps (Priority Order)
|
||||||
|
|
||||||
### High Priority
|
### High Priority
|
||||||
|
|
||||||
1. **Fix Unit Tests** (2-3 hours)
|
1. **Fix Unit Tests** (2-3 hours)
|
||||||
|
|
||||||
- Update test_image_downloader.py to match actual API
|
- Update test_image_downloader.py to match actual API
|
||||||
- Fix test_nfo_generator.py validation tests
|
- Fix test_nfo_generator.py validation tests
|
||||||
- Update test_tmdb_client.py mocking strategy
|
- Update test_tmdb_client.py mocking strategy
|
||||||
@@ -152,7 +172,9 @@ python src/cli/Main.py nfo status
|
|||||||
- Verify generated NFO is valid Kodi format
|
- Verify generated NFO is valid Kodi format
|
||||||
|
|
||||||
### Medium Priority
|
### Medium Priority
|
||||||
|
|
||||||
3. **Integrate with SerieList** (1-2 hours)
|
3. **Integrate with SerieList** (1-2 hours)
|
||||||
|
|
||||||
- Add NFOService to SerieList.load_series()
|
- Add NFOService to SerieList.load_series()
|
||||||
- Implement auto-create logic
|
- Implement auto-create logic
|
||||||
- Implement update logic
|
- Implement update logic
|
||||||
@@ -166,7 +188,9 @@ python src/cli/Main.py nfo status
|
|||||||
- Test commands
|
- Test commands
|
||||||
|
|
||||||
### Low Priority
|
### Low Priority
|
||||||
|
|
||||||
5. **Documentation** (30 minutes)
|
5. **Documentation** (30 minutes)
|
||||||
|
|
||||||
- Document TMDB API setup (getting API key)
|
- Document TMDB API setup (getting API key)
|
||||||
- Document NFO file format and Kodi compatibility
|
- Document NFO file format and Kodi compatibility
|
||||||
- Add examples to README
|
- Add examples to README
|
||||||
@@ -180,6 +204,7 @@ python src/cli/Main.py nfo status
|
|||||||
## 🐛 Known Issues
|
## 🐛 Known Issues
|
||||||
|
|
||||||
1. **NFOService.update_tvshow_nfo()** - Not implemented
|
1. **NFOService.update_tvshow_nfo()** - Not implemented
|
||||||
|
|
||||||
- Marked with `raise NotImplementedError`
|
- Marked with `raise NotImplementedError`
|
||||||
- Need to parse existing NFO to extract TMDB ID
|
- Need to parse existing NFO to extract TMDB ID
|
||||||
- Then refetch and regenerate
|
- Then refetch and regenerate
|
||||||
@@ -215,16 +240,19 @@ Once tests are fixed, verify:
|
|||||||
## 💡 Recommendations
|
## 💡 Recommendations
|
||||||
|
|
||||||
### Immediate Actions
|
### Immediate Actions
|
||||||
|
|
||||||
1. Invest time in fixing tests - they provide essential validation
|
1. Invest time in fixing tests - they provide essential validation
|
||||||
2. Add simple integration test script for manual verification
|
2. Add simple integration test script for manual verification
|
||||||
3. Test with a few real anime series to validate Kodi compatibility
|
3. Test with a few real anime series to validate Kodi compatibility
|
||||||
|
|
||||||
### Architecture Improvements
|
### Architecture Improvements
|
||||||
|
|
||||||
1. Consider adding context manager to ImageDownloader for consistency
|
1. Consider adding context manager to ImageDownloader for consistency
|
||||||
2. Add more detailed logging in NFOService for debugging
|
2. Add more detailed logging in NFOService for debugging
|
||||||
3. Consider caching TMDB results more aggressively
|
3. Consider caching TMDB results more aggressively
|
||||||
|
|
||||||
### Future Enhancements
|
### Future Enhancements
|
||||||
|
|
||||||
1. Support for episode-level NFO files (episodedetails)
|
1. Support for episode-level NFO files (episodedetails)
|
||||||
2. Support for season-level NFO files
|
2. Support for season-level NFO files
|
||||||
3. Background task for bulk NFO creation
|
3. Background task for bulk NFO creation
|
||||||
@@ -235,6 +263,7 @@ Once tests are fixed, verify:
|
|||||||
## 🎯 Completion Criteria
|
## 🎯 Completion Criteria
|
||||||
|
|
||||||
Task 3 will be considered complete when:
|
Task 3 will be considered complete when:
|
||||||
|
|
||||||
- ✅ All core components implemented (DONE)
|
- ✅ All core components implemented (DONE)
|
||||||
- ✅ Configuration added (DONE)
|
- ✅ Configuration added (DONE)
|
||||||
- ✅ Dependencies installed (DONE)
|
- ✅ Dependencies installed (DONE)
|
||||||
@@ -244,6 +273,7 @@ Task 3 will be considered complete when:
|
|||||||
- ⚠️ Documentation updated (PENDING)
|
- ⚠️ Documentation updated (PENDING)
|
||||||
|
|
||||||
## ⏱️ Estimated Time to Complete
|
## ⏱️ Estimated Time to Complete
|
||||||
|
|
||||||
- Fix tests: 2-3 hours
|
- Fix tests: 2-3 hours
|
||||||
- Integration: 1-2 hours
|
- Integration: 1-2 hours
|
||||||
- Documentation: 30 minutes
|
- Documentation: 30 minutes
|
||||||
|
|||||||
289
scripts/test_nfo_integration.py
Normal file
289
scripts/test_nfo_integration.py
Normal file
@@ -0,0 +1,289 @@
|
|||||||
|
"""Manual integration test for NFO functionality.
|
||||||
|
|
||||||
|
This script tests the complete NFO generation workflow with real TMDB API calls.
|
||||||
|
It's intended for manual verification, not automated testing.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
1. Set TMDB_API_KEY environment variable
|
||||||
|
2. Run: python scripts/test_nfo_integration.py
|
||||||
|
3. Check output in test_output/ directory
|
||||||
|
|
||||||
|
Requirements:
|
||||||
|
- Valid TMDB API key (get from https://www.themoviedb.org/settings/api)
|
||||||
|
- Internet connection
|
||||||
|
- Write permissions for test_output/ directory
|
||||||
|
"""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
# Add src to path
|
||||||
|
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||||
|
|
||||||
|
from src.core.services.tmdb_client import TMDBClient, TMDBAPIError
|
||||||
|
from src.core.services.nfo_service import NFOService
|
||||||
|
from src.core.entities.nfo_models import TVShowNFO
|
||||||
|
from src.core.utils.nfo_generator import generate_tvshow_nfo, validate_nfo_xml
|
||||||
|
|
||||||
|
|
||||||
|
async def test_tmdb_client():
|
||||||
|
"""Test TMDB client basic functionality."""
|
||||||
|
print("\n=== Testing TMDB Client ===")
|
||||||
|
|
||||||
|
api_key = os.getenv("TMDB_API_KEY")
|
||||||
|
if not api_key:
|
||||||
|
print("❌ TMDB_API_KEY environment variable not set")
|
||||||
|
print(" Get your API key from: https://www.themoviedb.org/settings/api")
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
async with TMDBClient(api_key=api_key) as client:
|
||||||
|
# Test 1: Search for a show
|
||||||
|
print("\n1. Searching for 'Attack on Titan'...")
|
||||||
|
results = await client.search_tv_show("Attack on Titan")
|
||||||
|
|
||||||
|
if results and results.get("results"):
|
||||||
|
show = results["results"][0]
|
||||||
|
print(f" ✅ Found: {show['name']} (ID: {show['id']})")
|
||||||
|
show_id = show["id"]
|
||||||
|
else:
|
||||||
|
print(" ❌ No results found")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Test 2: Get show details
|
||||||
|
print(f"\n2. Getting details for show ID {show_id}...")
|
||||||
|
details = await client.get_tv_show_details(
|
||||||
|
show_id,
|
||||||
|
append_to_response="credits,external_ids,images"
|
||||||
|
)
|
||||||
|
print(f" ✅ Title: {details['name']}")
|
||||||
|
print(f" ✅ First Air Date: {details.get('first_air_date', 'N/A')}")
|
||||||
|
print(f" ✅ Rating: {details.get('vote_average', 'N/A')}/10")
|
||||||
|
|
||||||
|
# Test 3: Get external IDs
|
||||||
|
if "external_ids" in details:
|
||||||
|
ext_ids = details["external_ids"]
|
||||||
|
print(f" ✅ IMDB ID: {ext_ids.get('imdb_id', 'N/A')}")
|
||||||
|
print(f" ✅ TVDB ID: {ext_ids.get('tvdb_id', 'N/A')}")
|
||||||
|
|
||||||
|
# Test 4: Get images
|
||||||
|
if "images" in details:
|
||||||
|
images = details["images"]
|
||||||
|
print(f" ✅ Posters: {len(images.get('posters', []))}")
|
||||||
|
print(f" ✅ Backdrops: {len(images.get('backdrops', []))}")
|
||||||
|
print(f" ✅ Logos: {len(images.get('logos', []))}")
|
||||||
|
|
||||||
|
# Test 5: Get image URL
|
||||||
|
if details.get("poster_path"):
|
||||||
|
url = client.get_image_url(details["poster_path"], "w500")
|
||||||
|
print(f" ✅ Poster URL: {url[:60]}...")
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
except TMDBAPIError as e:
|
||||||
|
print(f" ❌ TMDB API Error: {e}")
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
print(f" ❌ Unexpected Error: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
async def test_nfo_generation():
|
||||||
|
"""Test NFO XML generation."""
|
||||||
|
print("\n=== Testing NFO Generation ===")
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Create a sample NFO model
|
||||||
|
print("\n1. Creating sample TVShowNFO model...")
|
||||||
|
from src.core.entities.nfo_models import RatingInfo, ActorInfo, ImageInfo, UniqueID
|
||||||
|
|
||||||
|
nfo = TVShowNFO(
|
||||||
|
title="Test Show",
|
||||||
|
originaltitle="Test Show Original",
|
||||||
|
year=2020,
|
||||||
|
plot="This is a test show for NFO generation validation.",
|
||||||
|
runtime=45,
|
||||||
|
premiered="2020-01-15",
|
||||||
|
status="Continuing",
|
||||||
|
genre=["Action", "Drama", "Animation"],
|
||||||
|
studio=["Test Studio"],
|
||||||
|
country=["Japan"],
|
||||||
|
ratings=[RatingInfo(
|
||||||
|
name="themoviedb",
|
||||||
|
value=8.5,
|
||||||
|
votes=1000,
|
||||||
|
max_rating=10,
|
||||||
|
default=True
|
||||||
|
)],
|
||||||
|
actors=[
|
||||||
|
ActorInfo(name="Test Actor 1", role="Main Character"),
|
||||||
|
ActorInfo(name="Test Actor 2", role="Villain")
|
||||||
|
],
|
||||||
|
thumb=[ImageInfo(url="https://image.tmdb.org/t/p/w500/poster.jpg")],
|
||||||
|
fanart=[ImageInfo(url="https://image.tmdb.org/t/p/original/fanart.jpg")],
|
||||||
|
uniqueid=[
|
||||||
|
UniqueID(type="tmdb", value="12345", default=False),
|
||||||
|
UniqueID(type="tvdb", value="67890", default=True)
|
||||||
|
],
|
||||||
|
tmdbid=12345,
|
||||||
|
tvdbid=67890,
|
||||||
|
imdbid="tt1234567"
|
||||||
|
)
|
||||||
|
print(" ✅ TVShowNFO model created")
|
||||||
|
|
||||||
|
# Test 2: Generate XML
|
||||||
|
print("\n2. Generating XML...")
|
||||||
|
xml_string = generate_tvshow_nfo(nfo)
|
||||||
|
print(f" ✅ Generated {len(xml_string)} characters")
|
||||||
|
|
||||||
|
# Test 3: Validate XML
|
||||||
|
print("\n3. Validating XML...")
|
||||||
|
validate_nfo_xml(xml_string)
|
||||||
|
print(" ✅ XML is valid")
|
||||||
|
|
||||||
|
# Test 4: Save to file
|
||||||
|
output_dir = Path("test_output")
|
||||||
|
output_dir.mkdir(exist_ok=True)
|
||||||
|
nfo_path = output_dir / "test_tvshow.nfo"
|
||||||
|
nfo_path.write_text(xml_string, encoding="utf-8")
|
||||||
|
print(f" ✅ Saved to: {nfo_path}")
|
||||||
|
|
||||||
|
# Test 5: Show sample
|
||||||
|
print("\n4. Sample XML (first 500 chars):")
|
||||||
|
print(" " + xml_string[:500].replace("\n", "\n "))
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f" ❌ Error: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
async def test_nfo_service():
|
||||||
|
"""Test complete NFO service workflow."""
|
||||||
|
print("\n=== Testing NFO Service ===")
|
||||||
|
|
||||||
|
api_key = os.getenv("TMDB_API_KEY")
|
||||||
|
if not api_key:
|
||||||
|
print("❌ TMDB_API_KEY environment variable not set")
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Create test output directory
|
||||||
|
output_dir = Path("test_output")
|
||||||
|
output_dir.mkdir(exist_ok=True)
|
||||||
|
|
||||||
|
# Create a test series folder
|
||||||
|
test_series = output_dir / "Attack_on_Titan"
|
||||||
|
test_series.mkdir(exist_ok=True)
|
||||||
|
|
||||||
|
print(f"\n1. Creating NFO for 'Attack on Titan'...")
|
||||||
|
print(f" Output directory: {test_series}")
|
||||||
|
|
||||||
|
# Initialize NFO service
|
||||||
|
nfo_service = NFOService(
|
||||||
|
tmdb_api_key=api_key,
|
||||||
|
anime_directory=str(output_dir),
|
||||||
|
image_size="w500"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create NFO
|
||||||
|
nfo_path = await nfo_service.create_tvshow_nfo(
|
||||||
|
serie_name="Attack on Titan",
|
||||||
|
serie_folder="Attack_on_Titan",
|
||||||
|
year=2013,
|
||||||
|
download_poster=True,
|
||||||
|
download_logo=True,
|
||||||
|
download_fanart=True
|
||||||
|
)
|
||||||
|
|
||||||
|
print(f" ✅ NFO created: {nfo_path}")
|
||||||
|
|
||||||
|
# Check if files were created
|
||||||
|
print("\n2. Checking created files...")
|
||||||
|
files_created = {
|
||||||
|
"tvshow.nfo": (test_series / "tvshow.nfo").exists(),
|
||||||
|
"poster.jpg": (test_series / "poster.jpg").exists(),
|
||||||
|
"logo.png": (test_series / "logo.png").exists(),
|
||||||
|
"fanart.jpg": (test_series / "fanart.jpg").exists(),
|
||||||
|
}
|
||||||
|
|
||||||
|
for filename, exists in files_created.items():
|
||||||
|
status = "✅" if exists else "❌"
|
||||||
|
size = ""
|
||||||
|
if exists:
|
||||||
|
file_path = test_series / filename
|
||||||
|
size = f" ({file_path.stat().st_size:,} bytes)"
|
||||||
|
print(f" {status} {filename}{size}")
|
||||||
|
|
||||||
|
# Read and validate NFO
|
||||||
|
if files_created["tvshow.nfo"]:
|
||||||
|
print("\n3. Validating generated NFO...")
|
||||||
|
nfo_content = nfo_path.read_text(encoding="utf-8")
|
||||||
|
validate_nfo_xml(nfo_content)
|
||||||
|
print(" ✅ NFO is valid XML")
|
||||||
|
|
||||||
|
# Show sample
|
||||||
|
print("\n4. NFO Content (first 800 chars):")
|
||||||
|
print(" " + nfo_content[:800].replace("\n", "\n "))
|
||||||
|
|
||||||
|
return all(files_created.values())
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f" ❌ Error: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
"""Run all integration tests."""
|
||||||
|
print("=" * 70)
|
||||||
|
print("NFO Functionality Integration Tests")
|
||||||
|
print("=" * 70)
|
||||||
|
print("\nNOTE: This requires a valid TMDB API key set as environment variable.")
|
||||||
|
print("Get your API key from: https://www.themoviedb.org/settings/api")
|
||||||
|
print("Set it with: export TMDB_API_KEY='your_api_key_here'")
|
||||||
|
|
||||||
|
results = []
|
||||||
|
|
||||||
|
# Test 1: TMDB Client
|
||||||
|
results.append(("TMDB Client", await test_tmdb_client()))
|
||||||
|
|
||||||
|
# Test 2: NFO Generation
|
||||||
|
results.append(("NFO Generation", await test_nfo_generation()))
|
||||||
|
|
||||||
|
# Test 3: NFO Service (full workflow)
|
||||||
|
results.append(("NFO Service", await test_nfo_service()))
|
||||||
|
|
||||||
|
# Summary
|
||||||
|
print("\n" + "=" * 70)
|
||||||
|
print("SUMMARY")
|
||||||
|
print("=" * 70)
|
||||||
|
|
||||||
|
for test_name, passed in results:
|
||||||
|
status = "✅ PASSED" if passed else "❌ FAILED"
|
||||||
|
print(f"{test_name:.<50} {status}")
|
||||||
|
|
||||||
|
all_passed = all(result for _, result in results)
|
||||||
|
|
||||||
|
if all_passed:
|
||||||
|
print("\n🎉 All tests passed!")
|
||||||
|
print("\nGenerated files are in the 'test_output/' directory.")
|
||||||
|
print("You can import tvshow.nfo into Kodi/Plex/Jellyfin to verify compatibility.")
|
||||||
|
else:
|
||||||
|
print("\n⚠️ Some tests failed. Check the output above for details.")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
exit_code = asyncio.run(main())
|
||||||
|
sys.exit(exit_code)
|
||||||
Reference in New Issue
Block a user