Add 300ms minimum interval between progress broadcasts to reduce
WebSocket message volume. Broadcasts are sent immediately for
significant changes (>=1% or forced), otherwise throttled.
- Add MIN_BROADCAST_INTERVAL class constant (0.3s)
- Track last broadcast time per progress_id using time.monotonic()
- Clean up broadcast timestamps when progress completes/fails/cancels
- Implement sync_single_series_after_scan to persist scanned series to database
- Enhanced _broadcast_series_updated to include full NFO metadata (nfo_created_at, nfo_updated_at, tmdb_id, tvdb_id)
- Add immediate episode scanning in add_series endpoint when background loader isn't running
- Implement updateSingleSeries in frontend to handle series_updated WebSocket events
- Add SERIES_UPDATED event constant to WebSocket event definitions
- Update background loader to use sync_single_series_after_scan method
- Simplified background loader initialization in FastAPI app
- Add comprehensive tests for series update WebSocket payload and episode counting logic
- Import reorganization: move get_background_loader_service to dependencies module
- Created tests/unit/test_nfo_batch_operations.py
* 19 comprehensive unit tests all passing
* Test concurrent operations with max_concurrent limits
* Test partial failure handling (continues processing)
* Test skip_existing and overwrite functionality
* Test media download options
* Test result accuracy and error messages
* Test edge cases (empty, single, large, duplicates)
- Updated docs/instructions.md
* Marked NFO batch operations tests as completed
* Documented 19/19 passing tests
- Moved RuntimeError catch to encompass get_db_session() call
- Previously only caught during import, not during execution
- Now properly yields None when database not initialized
- Fixes test_add_series_endpoint_authenticated test failure
- Created src/server/utils/media.py with reusable media file functions
- Functions: check_media_files(), get_media_file_paths(), has_all_images(), count_video_files(), has_video_files()
- Defined standard filename constants: POSTER_FILENAME, LOGO_FILENAME, FANART_FILENAME, NFO_FILENAME
- Defined VIDEO_EXTENSIONS set for media player compatibility
- Refactored src/server/api/nfo.py (7 locations) to use utility functions
- Refactored src/server/services/background_loader_service.py to use utility
- Functions accept both str and Path for compatibility
- Marked Code Duplications 1, 3, 4 as RESOLVED in instructions.md
- Updated Further Considerations as RESOLVED (addressed in Issues 7, 9, 10)
- Established explicit precedence: ENV vars > config.json > defaults
- Updated fastapi_app.py to only sync config.json when ENV var not set
- Added precedence logging to show which source is used
- Documented precedence rules with examples in CONFIGURATION.md
Key principle: ENV variables always take precedence, config.json is
fallback only. This ensures deployment flexibility and clear priority.
All config tests passing (26/26)
- Added 5 new service methods for complete database coverage:
* get_series_without_nfo()
* count_all()
* count_with_nfo()
* count_with_tmdb_id()
* count_with_tvdb_id()
- Eliminated all direct database queries from business logic:
* series_manager_service.py - now uses AnimeSeriesService
* anime_service.py - now uses service layer methods
- Documented architecture decision in ARCHITECTURE.md:
* Service layer IS the repository layer
* No direct SQLAlchemy queries allowed outside service layer
- All database access must go through service methods
- 1449 tests passing, repository pattern enforced
- Add async method list_series_with_filters() to AnimeService
- Refactor list_anime to use service layer instead of direct DB access
- Convert sync database queries to async patterns
- Remove unused series_app parameter from endpoint
- Update test to skip direct unit test (covered by integration tests)
- Mark Issue 1 as resolved in documentation
- Modified BackgroundLoaderService to use multiple workers (default: 5)
- Anime additions now process in parallel without blocking
- Added comprehensive unit tests for concurrent behavior
- Updated integration tests for compatibility
- Updated architecture documentation
Critical bug fix: The filter was returning the wrong series because of
a misunderstanding of the episode table semantics.
ISSUE:
- Episodes table contains MISSING episodes (from episodeDict)
- is_downloaded=False means episode file not found in folder
- Original query logic was backwards - returned series with NO missing
episodes instead of series WITH missing episodes
SOLUTION:
- Simplified query to directly check for episodes with is_downloaded=False
- Changed from complex join with count aggregation to simple subquery
- Now correctly returns series that have at least one undownloaded episode
CHANGES:
- src/server/database/service.py: Rewrote get_series_with_no_episodes()
method with corrected logic and clearer documentation
- tests/unit/test_series_filter.py: Updated test expectations to match
corrected behavior with detailed comments explaining episode semantics
- docs/API.md: Enhanced documentation explaining filter behavior and
episode table meaning
TESTS:
All 5 unit tests pass with corrected logic
- Added get_series_with_no_episodes() method to AnimeSeriesService
- Updated list_anime endpoint to support filter='no_episodes' parameter
- Added comprehensive unit tests for the new filtering functionality
- All tests passing successfully
- Add selectinload for episode relationship in get_all()
- Prevents MissingGreenlet error during queue initialization
- Both series and episode are now eagerly loaded
1. Setup redirect flow (setup -> loading -> login):
- Add /loading to exempt paths
- Redirect setup to login after completion
- Redirect loading to login when initialization complete
2. Close pages after completion:
- Block access to /setup after setup is done
- Block access to /loading after initialization complete
- Proper redirect handling prevents re-access
3. Fix WebSocket 403 error:
- Change /ws/progress to /ws/connect (correct endpoint)
- Add /ws/connect to exempt paths
- Subscribe to 'system' room for progress updates
- Fix message data handling format
Replace non-existent emit_progress calls with proper ProgressService methods:
- start_progress for starting operations
- update_progress for progress updates
- complete_progress for successful completion
- fail_progress for failures
Convert percentage-based updates to current/total based on ProgressService API
- Create loading.html template with WebSocket-based progress updates
- Update initialization_service to emit progress events via ProgressService
- Modify setup endpoint to run initialization in background and redirect to loading page
- Add /loading route in page_controller
- Show real-time progress for series sync, NFO scan, and media scan steps
- Display completion message with button to continue to app
- Handle errors with visual feedback