- Parse existing NFO to extract TMDB ID from uniqueid or tmdbid element
- Fetch fresh metadata from TMDB API
- Regenerate NFO with updated data
- Optionally re-download media files
- Add comprehensive error handling (missing NFO, no TMDB ID, invalid XML)
- Add unit tests for XML parsing logic (4 tests, all passing)
- Add integration test script (requires TMDB API key)
- Created TMDB API client with async requests, caching, and retry logic
- Implemented NFO XML generator for Kodi/XBMC format
- Created image downloader for poster/logo/fanart with validation
- Added NFO service to orchestrate metadata creation
- Added NFO-related configuration settings
- Updated requirements.txt with aiohttp, lxml, pillow
- Created unit tests (need refinement due to implementation mismatch)
Components created:
- src/core/services/tmdb_client.py (270 lines)
- src/core/services/nfo_service.py (390 lines)
- src/core/utils/nfo_generator.py (180 lines)
- src/core/utils/image_downloader.py (296 lines)
- tests/unit/test_tmdb_client.py
- tests/unit/test_nfo_generator.py
- tests/unit/test_image_downloader.py
Note: Tests need to be updated to match actual implementation APIs.
Dependencies installed: aiohttp, lxml, pillow
- Create TVShowNFO, ActorInfo, RatingInfo, ImageInfo models
- Add validation for dates (YYYY-MM-DD), URLs, IMDB IDs
- Support all Kodi/XBMC standard fields
- Include nested models for ratings, actors, images
- Comprehensive unit tests with 61 tests
- Test coverage: 95.16% (exceeds 95% requirement)
- All tests passing
- Add nfo_path property to Serie class
- Add has_nfo(), has_poster(), has_logo(), has_fanart() methods
- Update to_dict()/from_dict() to include nfo metadata
- Modify SerieList.load_series() to detect NFO and media files
- Add logging for missing NFO and media files with statistics
- Comprehensive unit tests with 100% coverage
- All 67 tests passing
- Change 'scanner' to 'serie_scanner' attribute name
- Update tests to match SeriesApp attribute naming
- Scanner now properly detected and called on add
- All add_series tests passing (9/9)
- Remove folder creation from add_series endpoint
- Add folder creation to download() method in SeriesApp
- Maintain database persistence and targeted scanning
- Update tests to use tmp_path fixtures
- All add_series and download tests passing (13/13)
- Replace callback system with events library in SerieScanner
- Update SeriesApp to subscribe to loader and scanner events
- Refactor ScanService to use Events instead of CallbackManager
- Remove CallbackManager imports and callback classes
- Add safe event calling with error handling in SerieScanner
- Update AniworldLoader to use Events for download progress
- Remove progress_callback parameter from download methods
- Update all affected tests for Events pattern
- Fix test_series_app.py for new event subscription model
- Comment out obsolete callback tests in test_scan_service.py
All core tests passing. Events provide cleaner event-driven architecture.
- Add sanitize_folder_name utility for filesystem-safe folder names
- Add sanitized_folder property to Serie entity
- Update SerieList.add() to use sanitized display names for folders
- Add scan_single_series() method for targeted episode scanning
- Enhance add_series endpoint: DB save -> folder create -> targeted scan
- Update response to include missing_episodes and total_missing
- Add comprehensive unit tests for new functionality
- Update API tests with proper mock support
- Add total_items parameter to broadcast_scan_started and broadcast_scan_progress
- Pass total from SeriesApp to WebSocket broadcasts in AnimeService
- Update JS overlay to show progress bar and current/total count
- Add CSS for progress bar styling
- Add unit tests for new total_items parameter
- All 1024 tests passing
- Add broadcast_scan_started, broadcast_scan_progress, broadcast_scan_completed to WebSocketService
- Inject WebSocketService into AnimeService for real-time scan progress broadcasts
- Add CSS styles for scan progress overlay with spinner, stats, and completion state
- Update app.js to handle scan events and display progress overlay
- Add unit tests for new WebSocket broadcast methods
- All 1022 tests passing
- Fixed room name mismatch: ProgressService was broadcasting to
'download_progress' but JS clients join 'downloads' room
- Added _get_room_for_progress_type() mapping function
- Updated all progress methods to use correct room names
- Added 13 new tests for room name mapping and broadcast verification
- Updated existing tests to expect correct room names
- Fixed JS clients to join valid rooms (downloads, queue, scan)
- Update _update_series_in_db to sync missing episodes bidirectionally
- Add delete_by_series_and_episode method to EpisodeService
- Remove downloaded episodes from DB after successful download
- Clear anime service cache when episodes are removed
- Fix tests to use 'message' instead of 'detail' in API responses
- Mock DB operations in rescan tests
- Remove db_session parameter from SeriesApp, SerieList, SerieScanner
- Move all database operations to AnimeService (service layer)
- Add add_series_to_db, contains_in_db methods to AnimeService
- Update sync_series_from_data_files to use inline DB operations
- Remove obsolete test classes for removed DB methods
- Fix pylint issues: add broad-except comments, fix line lengths
- Core layer (src/core/) now has zero database imports
722 unit tests pass
- Add documentation warnings for in-memory rate limiting and failed login attempts
- Consolidate duplicate health endpoints into api/health.py
- Fix CLI to use correct async rescan method names
- Update download.py and anime.py to use custom exception classes
- Add WebSocket room validation and rate limiting
- Update SeriesApp.rescan() to use database storage by default (use_database=True)
- Use SerieScanner.scan_async() for database mode, which saves directly to DB
- Fall back to legacy file-based scan() when use_database=False (for CLI compatibility)
- Reinitialize SerieList from database after scan when in database mode
- Update unit tests to use use_database=False for mocked tests
- Add parameter to control storage mode for backward compatibility
- Fix return type annotation in SetupRedirectMiddleware.dispatch() to use Response instead of RedirectResponse
- Replace broad 'except Exception' with specific exception types (FileNotFoundError, ValueError, OSError, etc.)
- Rename AppConfig.validate() to validate_config() to avoid shadowing BaseModel.validate()
- Fix ValidationResult.errors field to use List[str] with default_factory
- Add pylint disable comments for intentional broad exception catches during shutdown
- Rename lifespan parameter to _application to indicate unused variable
- Update all callers to use new validate_config() method name
- Moved _sync_series_to_database from fastapi_app.py to anime_service.py
- Renamed to sync_series_from_data_files for better clarity
- Updated all imports and test references
- Removed completed TODO tasks from instructions.md
- Add get_all_series_from_data_files() to SeriesApp
- Sync series from data files to DB on startup
- Add unit tests for new SeriesApp method
- Add integration tests for sync functionality
- Update documentation
- Updated infrastructure.md with queue database schema and storage details
- Updated instructions.md to mark migration task as completed
- No deprecated JSON code remains in codebase
- Created QueueRepository adapter in src/server/services/queue_repository.py
- Refactored DownloadService to use repository pattern instead of JSON
- Updated application startup to initialize download service from database
- Updated all test fixtures to use MockQueueRepository
- All 1104 tests passing
Task 9: Clean up legacy code
- Added deprecation warnings to Serie.save_to_file() and load_from_file()
- Updated infrastructure.md with Data Storage section documenting:
- SQLite database as primary storage
- Legacy file storage as deprecated
- Data migration process
- Added deprecation warning tests for Serie class
- Updated existing tests to handle new warnings
- All 1012 tests pass (872 unit + 55 API + 85 integration)
Task 8: Write integration tests for data file migration
- Added test_migration_on_fresh_start_no_data_files test
- Added test_add_series_saves_to_database test
- Added test_scan_async_saves_to_database test
- Added test_load_series_from_db test
- Added test_search_and_add_workflow test
- All 11 migration integration tests pass
- All 870 tests pass (815 unit + 55 API)
- Added db_session parameter to SeriesApp.__init__()
- Added db_session property and set_db_session() method
- Added init_from_db_async() for async database initialization
- Pass db_session to SerieList and SerieScanner during construction
- Added get_series_app_with_db() dependency for FastAPI endpoints
- All 815 unit tests and 55 API tests pass
- Task 7.1: Update All Test Fixtures to Use Key
- Updated FakeSerie/FakeSeriesApp with realistic keys in test_anime_endpoints.py
- Updated 6+ fixtures in test_websocket_integration.py
- Updated 5 fixtures in test_download_progress_integration.py
- Updated 9 fixtures in test_download_progress_websocket.py
- Updated 10+ fixtures in test_download_models.py
- All fixtures now use URL-safe, lowercase, hyphenated key format
- Task 7.2: Add Integration Tests for Identifier Consistency
- Created tests/integration/test_identifier_consistency.py with 10 tests
- TestAPIIdentifierConsistency: API response validation
- TestServiceIdentifierConsistency: Download service key usage
- TestWebSocketIdentifierConsistency: WebSocket events
- TestIdentifierValidation: Model validation
- TestEndToEndIdentifierFlow: Full flow verification
- Tests use UUID suffixes for isolation
All 1006 tests passing.
- Updated app.js to use 'key' as primary series identifier
- selectedSeries Set now uses key instead of folder
- createSerieCard() uses data-key attribute for identification
- toggleSerieSelection() uses key for lookups
- downloadSelected() iterates with key values
- updateSelectionUI() and toggleSelectAll() use key
- Updated WebSocket service tests
- Tests now include key and folder in broadcast data
- Verified both fields are included in messages
- No changes needed for queue.js and other JS files
- They use download item IDs correctly, not series identifiers
- No template changes needed
- Series cards rendered dynamically in app.js
All 996 tests passing
- Add series context helpers: prepare_series_context, get_series_by_key, filter_series_by_missing_episodes
- Update module docstring with identifier convention documentation
- Add unit tests for new series context helper functions
- Update infrastructure.md with template helpers documentation
- Mark Phase 4 (API Layer) as complete
- Add validate_series_key() function that validates URL-safe, lowercase,
hyphen-separated series keys (e.g., 'attack-on-titan')
- Add validate_series_key_or_folder() for backward compatibility during
transition from folder-based to key-based identification
- Create comprehensive test suite with 99 test cases for all validators
- Update infrastructure.md with validation utilities documentation
- Mark Task 4.6 as complete in instructions.md
Test: conda run -n AniWorld python -m pytest tests/unit/test_validators.py -v
All 99 validator tests pass, 718 total unit tests pass
- Updated AnimeSeriesResponse and SearchResult models in anime.py:
- Changed 'id' field to 'key' as the primary series identifier
- Added 'folder' as optional metadata field
- Added field validator to normalize key to lowercase and strip whitespace
- Added comprehensive docstrings explaining identifier usage
- Updated DownloadItem and DownloadRequest models in download.py:
- Added field validator for serie_id normalization (lowercase, stripped)
- Improved documentation for serie_id (primary identifier) vs serie_folder (metadata)
- Updated test_anime_models.py with comprehensive tests:
- Tests for key normalization and whitespace stripping
- Tests for folder as optional metadata
- Reorganized tests into proper class structure
- Updated test_download_models.py with validator tests:
- Tests for serie_id normalization in DownloadItem
- Tests for serie_id normalization in DownloadRequest
All 885 tests pass.
- Verified queue API endpoints already use 'serie_id' (key) as primary identifier
- Updated test fixtures to use explicit key values (e.g., 'test-series-key')
- Added test to verify queue items include serie_id (key) and serie_folder (metadata)
- Fixed test_queue_items_have_required_fields to find correct item by ID
- Added test_queue_item_uses_key_as_identifier for explicit key verification
- Updated instructions.md to mark Task 4.3 as complete
All 870 tests pass.
- Updated DownloadRequest and DownloadItem models with comprehensive
docstrings explaining serie_id (key as primary identifier) vs
serie_folder (filesystem metadata)
- Updated add_to_queue() endpoint docstring to document request parameters
- Updated all test files to include required serie_folder field:
- tests/api/test_download_endpoints.py
- tests/api/test_queue_features.py
- tests/frontend/test_existing_ui_integration.py
- tests/integration/test_download_flow.py
- Updated infrastructure.md with Download Queue request/response models
- All 869 tests pass
This is part of the Series Identifier Standardization effort (Phase 4.2)
to ensure key is used as the primary identifier throughout the codebase.
- Create ScanService class (src/server/services/scan_service.py)
- Use 'key' as primary series identifier throughout
- Include 'folder' as metadata only for display purposes
- Implement scan progress tracking via ProgressService
- Add callback classes for progress, error, and completion
- Support scan event subscription and broadcasting
- Maintain scan history with configurable limit
- Provide cancellation support for in-progress scans
- Create comprehensive unit tests (tests/unit/test_scan_service.py)
- 38 tests covering all functionality
- Test ScanProgress dataclass serialization
- Test callback classes (progress, error, completion)
- Test service lifecycle (start, cancel, status)
- Test event subscription and broadcasting
- Test key-based identification throughout
- Test singleton pattern
- Update infrastructure.md with ScanService documentation
- Document service overview and key features
- Document components and event types
- Document integration points
- Include usage example
- Update instructions.md
- Mark Task 3.4 as complete
- Mark Phase 3 as fully complete
- Remove finished task definition
Task: Phase 3, Task 3.4 - Update ScanService to Use Key
Completion Date: November 27, 2025
- Added optional 'key' and 'folder' fields to ProgressUpdate dataclass
- key: Primary series identifier (provider key, e.g., 'attack-on-titan')
- folder: Optional series folder name for display (e.g., 'Attack on Titan (2013)')
- Updated start_progress() and update_progress() methods to accept key/folder parameters
- Enhanced to_dict() serialization to include key/folder when present
- Updated all docstrings to clarify identifier usage
- Added 5 new comprehensive unit tests for key/folder functionality
- All 25 ProgressService tests passing
- Updated infrastructure.md with series identifier documentation
- Maintains backward compatibility - fields are optional
- Completed Phase 3, Task 3.3 of identifier standardization initiative
- Updated DownloadService to use 'serie_id' (provider key) for identification
- Changed 'serie_folder' from Optional to required in models (DownloadItem, DownloadRequest)
- Removed incorrect fallback logic that used serie_id as folder name
- Enhanced docstrings to clarify purpose of each identifier field:
* serie_id: Provider key (e.g., 'attack-on-titan') for lookups
* serie_folder: Filesystem folder name (e.g., 'Attack on Titan (2013)') for file operations
- Updated logging to reference 'serie_key' for clarity
- Fixed all unit tests to include required serie_folder field
- All 25 download service tests passing
- All 47 download model tests passing
- Updated infrastructure.md with detailed documentation
- Marked Task 3.1 as completed in instructions.md
Benefits:
- Clear separation between provider identifier and filesystem path
- Prevents confusion from mixing different identifier types
- Consistent with broader series identifier standardization effort
- Better error messages when required fields are missing
Task 2.1 - Update SeriesApp to Use Key for All Operations
Changes:
- Added 'key' field to DownloadStatusEventArgs and ScanStatusEventArgs
- Updated download() method docstrings to clarify key vs folder usage
- Implemented _get_serie_by_key() helper method for series lookups
- Updated all event emissions to include both key (identifier) and folder (metadata)
- Enhanced logging to show both key and folder for better debugging
- Fixed test mocks to include new key and item_id fields
Benefits:
- Consistent series identification throughout core application layer
- Clear separation between identifier (key) and metadata (folder)
- Better debugging with comprehensive log messages
- Type-safe lookups with Optional[Serie] return types
- Single source of truth for series lookups
Test Results:
- All 16 SeriesApp tests pass
- All 562 unit tests pass with no regressions
- No breaking changes to existing functionality
Follows:
- PEP 8 style guidelines (max 79 chars per line)
- PEP 257 docstring standards
- Project coding standards (type hints, error handling, logging)
- Renamed folderDict to keyDict for clarity
- Updated internal storage to use serie.key instead of serie.folder
- Optimized contains() from O(n) to O(1) with direct key lookup
- Added get_by_key() as primary lookup method
- Added get_by_folder() for backward compatibility
- Enhanced docstrings to clarify key vs folder usage
- Created comprehensive test suite (12 tests, all passing)
- Verified no breaking changes (16 SeriesApp tests pass)
This establishes key as the single source of truth for series
identification while maintaining folder as metadata for filesystem
operations only.