758 Commits

Author SHA1 Message Date
9a81b04b65 fix: downloaded episodes no longer appear as missing
Use the database as the authoritative source for missing-episode lists so
that episodes marked is_downloaded=True are never shown as missing, even
when the in-memory state is stale.

Key changes:
- EpisodeService.get_by_series() gains only_missing flag
- AnimeService uses DB-backed episodeDict and preserves downloaded episodes
  during sync, skipping them when adding/removing missing episodes
- DownloadService broadcasts series_updated after marking an episode downloaded
  so the frontend reflects the change immediately
- Frontend filters out series with zero missing episodes client-side and
  fixes renderSeries to respect the active filter
- Unit tests updated to assert the broadcast is sent
2026-05-25 21:30:31 +02:00
a336733ea9 fix(nfo): add year field to series and create missing NFO files
- Add missing year field when building series list in anime_service
- Add _create_missing_nfo to generate minimal NFO for series without one
- Update perform_nfo_repair_scan to detect and create missing NFOs
- Add semaphore-protected async creation with TMDB rate limiting

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-25 16:32:54 +02:00
ca93bb740a feat(providers): detect HTML encoding before parsing
Add chardet-based _decode_html_content() to aniworld_provider. Apply
to all BeautifulSoup parsing calls to prevent decoding warnings on
pages with mismatched encoding declarations. Falls back to utf-8
with errors='replace' when confidence < 0.7.

Also fix test_enhanced_provider HLS test signature and add HLS
pattern unit tests.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-25 16:30:36 +02:00
d5e955a731 Add year extraction from folder names for existing series
- New migration script: populate year from folder (YYYY) pattern
- SerieScanner: refactor year extraction logic
- anime_service: pass year when syncing from data files
2026-05-25 15:30:28 +02:00
e2a373816a feat(nfo): add minimal NFO fallback when TMDB fails
- Add create_minimal_nfo() method to NFOService for fallback when TMDB lookup fails
- Update API endpoints (single and batch) to use minimal NFO fallback on TMDBAPIError
- Document fallback behavior in NFO_GUIDE.md section 3.6
- Add unit tests for minimal NFO creation (11 tests passing)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-25 15:19:50 +02:00
a115215416 fix(providers): rotate, probe and fall back on 404
Iterate providers actually advertised on the episode page (ordered by
SUPPORTED_PROVIDERS preference) instead of always re-resolving VOE.
Each candidate is HEAD-probed before yt-dlp runs, so dead links are
skipped immediately; direct video URLs use a streaming fast path that
bypasses yt-dlp; total failure now logs the exhausted provider list.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-25 14:32:10 +02:00
c579235af0 feat(download): persist retry state and dead-letter
Retry count and queue status were in-memory only and lost on
restart, so failed downloads could not be safely resumed and
permanently-failed episodes silently blocked re-queueing via the
episode-id unique index.

- Add status + retry_count columns to DownloadQueueItem
- Replace unique(episode_id) with unique(episode_id, status) so
  permanently_failed rows do not block new pending entries
- Add PERMANENTLY_FAILED to DownloadStatus enum
- Persist retry_count on each failure; mark permanently_failed once
  max_retries reached
- QueueRepository reads status/retry_count from DB instead of
  defaulting to PENDING/0
- Stop double-incrementing retry_count in retry_failed_items;
  increment only happens in _process_download on failure

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-25 14:24:31 +02:00
0ba2587bc8 refactor(download): mark episode downloaded instead of deleting
Change _remove_episode_from_missing_list to set is_downloaded=True
and populate file_path via EpisodeService.mark_downloaded, instead of
deleting the Episode row. Preserves download history so queries can
distinguish series with downloaded episodes from completely unwatched
series.

- Pass serie_folder to construct file_path
- Look up series_id via AnimeSeriesService.get_by_key
- Update tests to mock mark_downloaded path
- Document episode lifecycle in docs/DEVELOPMENT.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-25 14:14:33 +02:00
bda1fe4445 Fix scheduler next_run_time None check; add debug logging
- Fix race condition: next_run_time only available after scheduler.start()
- Handle None gracefully in logging
- Add debug logging to _perform_rescan and _run_rescan_job
- Document scheduler troubleshooting in DEVELOPMENT.md
2026-05-25 13:48:34 +02:00
810346bc8b chore: bump version v1.1.13 2026-05-24 21:17:39 +02:00
daa937bcb7 Fix test isolation: clear logging handlers and reset propagate flags
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-23 22:44:40 +02:00
1c505bd722 Use ffmpeg for HLS streams in aniworld provider
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-23 22:26:48 +02:00
3551838887 Add startup health checks and /health/ready endpoint
- Add _run_startup_health_checks() function in fastapi_app.py
  - Check ffmpeg availability (warning)
  - Check DNS resolution for aniworld.to and api.themoviedb.org (warning)
  - Check anime_directory configuration and writability (error)
- Store startup checks in app.state for health endpoint access
- Add /health/ready endpoint for container orchestrators
  - Returns not_ready with 503 when critical failures present
  - Includes critical_failures list for debugging
- Update /health endpoint to include startup check results
  - Status reflects worst check (error > warning > ok)
- Document health check endpoints in DEVELOPMENT.md
- Add unit tests for startup health checks
- Add unit tests for /health/ready endpoint

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-23 22:12:03 +02:00
9a20541598 feat(NFO): add TMDB search fallback with alt_titles support
- New _search_with_fallback() method tries multiple strategies:
  1. Primary query with year filter (de-DE locale)
  2. Alternative titles with ja-JP / en-US locales
  3. English search (en-US)
  4. Search without year constraint
  5. Punctuation-normalized query
- create_nfo() accepts new alt_titles param for Japanese/title fallback
- Better match rate for anime with non-English titles

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-23 21:57:00 +02:00
3f7651404d fix(tmdb): harden aiohttp session lifecycle
- Add async context manager to NFOService wrapping TMDBClient + ImageDownloader
- Add TMDBClient.__del__ warning when session leaks
- Log exc_info on session recreation for traceback visibility
- Document async-with usage in docs/DEVELOPMENT.md and docs/TESTING.md
- Add unit tests covering leak detection, context-manager cleanup, and connector-closed warning

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-23 21:34:26 +02:00
bee24406e6 Add runner.csx script 2026-05-23 21:28:54 +02:00
31eb0026cf Add queue deduplication to prevent duplicate entries
- In-memory dedup in add_to_queue() using _pending_by_episode dict
- Batch-local dedup via seen_in_batch set (handles duplicates within single call)
- Database unique index on episode_id via __table_args__
- 5-minute cooldown in _auto_download_missing() to prevent rapid re-triggers
- Updated _add_to_pending_queue() and _remove_from_pending_queue() to track episode keys
- Added TestQueueDeduplication with 4 test cases
- Updated DEVELOPMENT.md and TESTING.md with queue dedup docs

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-23 21:27:41 +02:00
24ea12bbaf Add Docs/runner.csx 2026-05-23 21:19:15 +02:00
e74b602f60 Add ffmpeg for HLS stream download support
- Add ffmpeg to Dockerfile.app for container HLS support
- Configure yt-dlp with --downloader ffmpeg --hls-use-mpegts
- Add startup health check warns if ffmpeg missing
- Update DEVELOPMENT.md with ffmpeg prerequisites and troubleshooting
- Add tests for ffmpeg HLS options and health check
2026-05-23 21:18:39 +02:00
db65e28854 backup 2026-05-21 22:10:07 +02:00
11e231a4ab chore: bump version v1.1.12 2026-05-21 21:42:13 +02:00
a11f8c4fa0 fix(vpn): add explicit host route for health-check target
Without a /32 route in the main table, CHECK_HOST (1.1.1.1) fell through
to the VPN default route where source-address selection was defeated by
the priority-100 'from ETH0_IP' policy rule, causing pings to bypass
wg0 and be dropped by the kill switch.

Also add secondary google.com ping to distinguish IP vs DNS failures.
2026-05-21 21:41:51 +02:00
cf5a06af11 chore: bump version v1.1.11 2026-05-21 21:22:08 +02:00
e07f75432e backup 2026-05-21 21:21:13 +02:00
1696d5c65b chore: bump version v1.1.9 2026-05-21 21:04:51 +02:00
c8b386f47a chore: bump version v1.1.8 2026-05-20 20:00:45 +02:00
3888da352a feat(tmdb): improve rate limiting and retry resilience
- Increase max_retries from 3 to 5 with exponential backoff capped at 30s
- Add per-second rate limiter (~35 req/s) to stay under TMDB's ~40/s limit
- Replace small semaphore (4) with larger one (30) + token-bucket throttle
- Abort retries immediately on DNS/name-resolution failures
- Increase rate-limit fallback wait from default to max(delay*2, 10)s
2026-05-20 20:00:11 +02:00
06e104db42 chore: bump version v1.1.7 2026-05-20 19:41:58 +02:00
d4594bd1d9 chore: bump version 2026-05-20 19:40:17 +02:00
d866e836f6 backup 2026-05-20 19:39:08 +02:00
195dae13cb test: add integration tests for NFO content and repair
- test_add_anime_nfo_content.py: verify required NFO tags after anime add
- test_sacrificial_princess_nfo.py: test full NFO generation and repair path
2026-05-20 19:38:43 +02:00
51be777e7d fix: strip all trailing year suffixes to prevent duplication
- series.py: use regex to remove all trailing (YYYY) before appending year
- nfo_service.py: _extract_year_from_name strips all trailing year suffixes
- nfo_repair_service.py: add _read_tmdb_id() helper to extract TMDB ID from NFO
2026-05-20 19:38:37 +02:00
7930e49701 fix: prevent duplicate year suffixes in series name and folder creation
Apply the same duplicate-year prevention logic to additional code paths:

- Serie.name_with_year property: skip adding year suffix if name already ends with it
- add_series API endpoint: avoid duplicating year in folder_name_with_year
- Add integration test for Serie.name_with_year idempotency
- Add API test for add_series endpoint year deduplication

Complements the folder_rename_service fix for comprehensive coverage.
2026-05-19 21:25:21 +02:00
75c22fe296 fix(folder-rename): prevent duplicate year suffixes in series folder names
Use regex to strip all trailing year suffixes before adding the canonical
one, preventing duplication like 'Show (2021) (2021) (2021)'.

- Add regex pattern (\s*\(\d{4}\))+\s*$ to remove all existing year suffixes
- Ensure idempotent behavior across multiple folder rename runs
- Add 7 unit tests covering the bug cases and edge scenarios

Fixes: 86 Eighty Six (2021) (2021)..., Alma-chan (2025) (2025)...
2026-05-19 21:24:07 +02:00
7bcd0600d5 chore: bump version v1.1.5 2026-05-18 09:57:51 +02:00
a333329ae2 backup 2026-05-18 09:56:59 +02:00
363f7899f8 refactor(logging): reduce download log spam and set INFO level
- Pass app logger to yt-dlp so internal [download] progress lines
  are routed through the INFO-level logger instead of stdout.
- Throttle download_progress_handler debug logging to avoid
  flooding logs on every fragment tick.
- Switch key provider lifecycle messages to INFO (start/complete)
  while keeping verbose details at DEBUG.
- Set debug_enabled=False in development config so dev mode
  does not emit extra debug noise.
- Update config docstring example from DEBUG to INFO.
2026-05-18 09:56:19 +02:00
a08a8f7408 backup 2026-05-17 18:57:12 +02:00
54ac5e9ab7 chore: release v1.1.4 v1.1.4 2026-05-17 18:51:09 +02:00
c93ac3e7b8 chore: release v1.1.3 v1.1.3 2026-05-17 18:40:37 +02:00
68c4335348 feat(vpn): add startup connectivity checks and PersistentKeepalive
Add check_vpn_connectivity() that runs once after wg0 comes up:
- Waits for handshake (up to 15s) and prints public key if missing
- Measures RX bytes before/after curl to detect server-side routing issues
- Tests DNS resolution and dumps resolv.conf on failure
- On failure prints exact server-side commands to fix (sysctl, iptables, wg)

Add PersistentKeepalive=25 to wg0.conf to keep NAT mappings alive.
2026-05-17 18:40:24 +02:00
be87f2e230 chore: release v1.1.2 v1.1.2 2026-05-17 18:31:59 +02:00
c56e0f507d fix(vpn): fix DNS iptables rules and add NET_RAW cap
DNS OUTPUT was restricted to -o wg0, but routing decision happens
after iptables OUTPUT — so DNS to VPN-internal addresses (198.18.0.x)
was blocked before the kernel selected the outgoing interface.
Allow DNS unconditionally; routing still sends it through wg0.

Add NET_RAW capability so ping works inside the container.
2026-05-17 18:31:38 +02:00
cb0a36ccc2 chore: release v1.1.1 v1.1.1 2026-05-16 21:47:05 +02:00
3644b16447 feat(vpn): add version logging from VERSION file
- Read version from /etc/wireguard/VERSION instead of hardcoding
- Copy VERSION file into container image during build
- Update VERSION to v1.1.0
2026-05-16 21:46:19 +02:00
d5116e378e chore: release v0.1.0 v0.1.0 2026-05-16 21:41:40 +02:00
50a7083ce5 fix(vpn): support AllowedIPs=0.0.0.0/0 and multi-DNS configs
- Parse AllowedIPs dynamically from WireGuard config instead of hardcoding routes
- Remove auto-created default route by wg setconf to prevent breaking endpoint connection
- Fix DNS parsing: write comma-separated DNS servers as separate nameserver lines
- Add test for AllowedIPs route verification and DNS configuration
- Update test to skip container runtime tests when not running as root
2026-05-16 21:41:27 +02:00
52c0ff2337 chore(docs): remove temporary planning file docs/bla 2026-05-16 21:22:44 +02:00
a5fd88e224 chore(vpn): update WireGuard endpoint and credentials
- Rotate to new VPN endpoint (91.148.236.64)
- Update private/public keys and client address
- Switch DNS to 198.18.0.1/0.2
- Add local network route preservation via PostUp/PostDown
- Align nl.conf and wg0.conf configurations
2026-05-16 21:22:04 +02:00
98d4edad14 feat(vpn): dynamic AllowedIPs routing and improved test coverage
- Parse AllowedIPs from WireGuard config in entrypoint.sh
- Add/remove routes dynamically instead of hardcoded 0.0.0.0/1 split
- Handle both 0.0.0.0/0 and custom AllowedIPs
- Add route cleanup on VPN stop (endpoint + AllowedIPs)
- Update test_vpn.py with AllowedIPs route verification
- Allow non-root build-only tests with automatic runtime skip
2026-05-16 21:21:56 +02:00