- add TMDB orphan matcher backend (orphan_matcher.py) and search client (tmdb_search.py)
- introduce Redis-backed run history tracker (history.py) for matcher runs
- extend TorrentItemModel with tmdb_match_attempted_at and index
- add Alembic migration to create tmdb_match_attempted_at and its index
- integrate new tmdb_orphan_matching job into scheduler; expose in UI
- add admin UI: tmdb_matcher.html, history/revert endpoints, and nav entry
- wire settings: scheduler_tmdb_match_interval_hours and batch size
- enhance title normalizer: strip possessive s and remove technical brackets/tokens
- update admin docs to reflect new matcher and deprecation of old cleanup
- update admin base navigation and scheduled tasks page accordingly
introduce initial torrent grouping: add torrent_groups table and
group_id on torrent_items. include migrations, ORM models, and DAOs.
expose admin/web endpoints to group by info_hash and by title/size.
propagate tmdb_id across grouped items. non-destructive and backwards
compatible with existing data
- Implemented a new HTML page for managing scheduled tasks in the admin panel.
- Added JavaScript functionality for auto-refreshing job statuses and triggering jobs manually.
- Enhanced job result display with relative time and duration formatting.
- Introduced a toast notification system for user feedback on job actions.
fix: update Redis cache key generation to include a prefix
- Modified the Redis cache key generation to prepend "search:" to the hashed key for better organization.
refactor: improve Yggflix API search functionality
- Added an optional parameter to the search_movie method to specify the search type.
- Implemented a fallback mechanism to broaden search if initial results are insufficient.
chore: enhance Yggflix service with improved result handling
- Introduced a threshold for retrying searches with a broader query type based on result ratios.
- Increased the limit for sorted results from 50 to 100.
feat: add maintenance endpoints for managing expired API and peer keys
- Created endpoints to disable expired API keys and peer keys in the admin panel.
- Added functionality to clean orphan torrent items based on a specified age.
feat: integrate scheduler into the application lifecycle
- Initialized a database cleanup scheduler during application startup.
- Ensured proper shutdown of the scheduler during application termination.
extend not_cached ttl from 20m to 3h to reduce re-checks
of not cached items
remove the file_index shortcut for series in Torbox
and rely on season/episode parsing
- Refactored title filtering logic in `filter_results.py` to utilize a new title matching module.
- Introduced normalization functions for search queries in `jackett_service.py` and `yggflix_service.py`.
- Added new endpoints for managing title and language normalization rules in the admin views.
- Implemented loading and reloading of title matching and language rules in the application lifecycle.
- Enhanced release group extraction and language detection in `parser_utils.py` using the new title matching module.
Refactor search_movie method to remove tmdb_id parameter and add offset support for pagination. Implement automatic fallback to search type when movie type returns empty results, improving result coverage. Add intelligent page 2 fetching in service layer based on title pre-filter ratio threshold to avoid unnecessary API calls.
Introduce a 4-phase search system where private indexers are grouped by priority:
- Phase 1 (priority_private): always queried first
- Phase 2 (intermediary_private): queried only if private results < minCachedResults
- Phase 3 (fallback_private): queried only if still insufficient
- Phase 4 (public): Yggflix relay position controlled by yggflixPriority toggle
Add rdMinCachedBeforeCheck setting to skip RealDebrid if prior services already found enough cached results, since RD API is slow (~3s) and rate-limited. Default to 3.
Non-blocking magnet cleanup in AllDebrid availability checks via background task. G3MINI now computes info_hash from .torrent files when API omits it. Public indexer results are excluded from final matches unless already cached by a debrid service.
Remove deprecated Sharewood tracker integration and add three new private trackers:
- ABN (Abnormal): keyword-based search with name filtering
- G3MINI (Gemini Tracker): TMDB/IMDB ID-based filtering
- TheOldSchool: TMDB/IMDB ID-based filtering
Includes new API clients, result parsers, and service layers. Updates frontend UI, settings configuration, and credential handling to support the new indexers with proper passkey management.
- Implement Fernet encryption for configuration URLs to protect sensitive user settings
- Add CONFIG_SECRET_KEY environment variable for key derivation
- Add server-side /api/config/encode endpoint for secure config token generation
- Add CSRF token generation and validation for all admin POST routes
- Auto-inject CSRF tokens into admin forms and AJAX requests via JavaScript
- Update frontend to use async server encoding instead of client-side Base64
- Add localStorage fallback for config restoration on /configure pages
- Maintain backward compatibility with existing Base64-encoded URLs
- Log warning at startup if CONFIG_SECRET_KEY is not configured
- Convert debug logs to trace level in TorrentSmartContainer for reduced noise
Introduce a metadata_mappings table with DAO/model layers for admin-managed
IMDB→TMDB/title overrides, and overhaul the admin panel with a modern
Bootstrap 5 sidebar UI.
- Add metadata_mappings table with migrations (search_titles, year_override)
- Add MetadataMappingDAO with standalone session helpers for use outside FastAPI
- Add new admin pages: dashboard, mappings CRUD, maintenance, config, hash/TMDB search
- Integrate DB mapping lookups into Cinemeta and TMDB metadata providers
- Update catalog views to use DB mappings before hitting external APIs
- Rewrite all admin templates with Bootstrap 5, sidebar navigation, French labels
- Add session-based auth for admin routes with Redis session store
Add `allow_debrid_download` setting to control whether non-cached download streams are visible in Stremio results. When disabled, only instantly available streams are shown. Additionally, automatically prefer TorBox for downloads from special French indexers (C411, Torr9, LaCale, GenerationFree) when TorBox is configured, overriding the user's default debrid service selection.
Introduces a secure peer-to-peer cache sharing system using HMAC-SHA256 request signatures and Fernet (AES-128-CBC) response encryption.
- New peer_keys table for per-peer credential management (key_id, secret)
- New /api/peer/check endpoint returns encrypted debrid availability data
- New /api/peer/items endpoint returns encrypted torrent metadata
- Rate limiting per peer key with configurable limits and windows
- Admin UI for creating, viewing, revoking, and deleting peer keys
- Peer cache enrichment integrated into BaseDebrid L2.5 lookup pipeline
- StremThruDebrid override with cross-service PG L2 check + peer cache
- Removed old /api/share/cache endpoint (replaced by /api/peer/*)
- Added cryptography package dependency
BREAKING CHANGE: The /api/share/cache endpoint has been removed and replaced with /api/peer/check. Clients must update to use HMAC authentication with X-Peer-Key-Id, X-Peer-Timestamp, and X-Peer-Signature headers.
Add a two-level caching architecture for debrid service availability lookups:
- L1: Redis (existing) with 10-day TTL for fast in-memory access
- L2: PostgreSQL with 30-day TTL for persistent caching across Redis restarts
Changes:
- Add `debrid_cache` table with unique constraint on (info_hash, service) and indexes for efficient queries
- Create `DebridCacheDAO` with batch get/upsert and invalidation methods
- Add pgCron job to periodically clean up expired cache entries
- Extend `get_availability_bulk_cached` to check PG on Redis miss, warm Redis from PG hits, and write confirmed-cached results to PG
- Add `invalidate_availability_cache` method to remove false positives from both caches when NO_CACHE_VIDEO_URL is returned during playback
- Integrate `db_session` dependency into playback and search views
- Add retry handling to Yggflix API (3 retries, ignore Retry-After headers) and rate-limiting delay (0.3s) between consecutive queries
Lower log verbosity across the codebase by converting info-level logs to debug
and debug-level logs to trace. This reduces noise in production while keeping
detailed information available for debugging when needed.
Affected modules: API key DAO, Real Debrid token manager, C411/Torr9/YGG
indexers, debrid services, torrent container, playback/stream handlers,
search views, and utility modules.
Downgrade debug logs to trace level for internal processing details that are too verbose for normal debugging. Affects c411, lacale, sharewood, and torr9 services.
Refactor playback stream caching to use deterministic hash-based keys for user and content identifiers, removing plain-text API keys and query data from Redis keys.
Key changes:
- Add fixed-window rate limiting (60 requests per 60 seconds per user)
- Replace cache keys using hashed user/content identifiers instead of raw API keys
- Extend TTLs from 20 minutes to 6 hours for stream links and direct links
- Fix tracker credential leak by skipping credentials when file is cached on debrid service
- Simplify get_stream_link by removing series-specific caching logic
Replaced StremThru-dependent availability check with a two-step approach:
- Step 1: Use StremThru community cache when available
- Step 2: Fall back to direct RD API checks via anonymous magnet adds
Added _check_availability_direct method to check RD cache by:
- Marking already-cached hashes from existing account torrents
- Adding anonymous magnets for remaining hashes (0.5s rate limit)
- Checking status of newly added torrents
- Performing background cleanup of temp torrents
Added _add_magnet_probe helper for low-level magnet addition with retry logic for rate limits, and _delete_torrents_background for non-blocking cleanup.
Changed return type from object wrapper to flat array, moved regex import to module level, and added deduplication of input hashes. Also fixed logging style by removing unnecessary f-string formatting.
Convert verbose debug messages to trace level for cleaner production logs, improve secret redaction patterns to handle JWT tokens and API key parameters while preserving parameter names, and standardize log messages to English for consistency across the codebase.
- Added StremThruDebrid class for handling debrid operations using StremThru as backend.
- Introduced StremThruSDK for API interactions, including health checks and request handling.
- Created error handling with StremThruError for better error management.
- Updated torrent_smart_container to use the new StremThruDebrid class.
- Added necessary utility functions and data structures for managing magnet and torrent data.
- Implemented caching and session management for improved performance and reliability.
Async correctness:
- Wrap blocking time.sleep/requests.get calls with asyncio.to_thread()
in torrent_service.py and torr9_service.py
- Run cache_container_items() as fire-and-forget background task via
asyncio.create_task(asyncio.to_thread(...)) in search/views.py
- Make RDTokenManager.get_access_token() and new_access_token() async,
replace requests.post with aiohttp (token_manager.py)
- Make RealDebrid.get_headers() async, await all callers (realdebrid.py)
Security:
- Remove API key, device code, and PIN values from log output
(auth/views.py, admin/views.py)
- Add WARNING on startup when SESSION_KEY uses the insecure default
value (settings.py)
- Make admin template dir configurable via settings.admin_template_dir
Performance:
- Parallelize TMDB metadata fetches with asyncio.gather() (catalog/views.py)
- Add tmdb_language setting instead of hardcoded "fr-FR"
Deduplication:
- Extract ensure_uuid() to stream_fusion/web/api/utils.py
- Extract get_client_ip() to stream_fusion/web/utils.py
- Replace 8-way if/elif debrid factory with _SERVICE_MAP dict +
_build_service() helper (get_debrid_service.py)
- Extract _model_to_schema() in APIKeyDAO to remove 6 repeated blocks
- Simplify ProxyStreamer: remove manual BytesIO buffer, stream directly
via iter_any() (stream/views.py)
Inspired by commits 823bc77 and e306eb5 from limedrive/develop.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
MAGNET_INVALID_URI on a valid 40-char hash means AD doesn't have it
in cache — treat as a confirmed negative and store in Redis for 1h.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Error responses (e.g. MAGNET_INVALID_URI) contain "magnet" not "hash".
Fall back to m.get("magnet") so the hash is not silently dropped.
MAGNET_INVALID_URI on a valid 40-char hash likely means AD cannot verify
an unknown hash without trackers — not a confirmed "not in cache" state.
These results are marked instant:False locally but NOT stored in Redis
to avoid caching a false negative caused by the trackless format.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
statusCode is only returned by /v4.1/magnet/status, not by /v4/magnet/upload.
The upload response only contains `ready: bool` to indicate cache status.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
StremThru manages its own community cache and keeps it up to date.
Caching its responses locally would serve stale data and bypass
its real-time updates. Only direct AD API responses are cached.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add a shared Redis availability cache keyed by hash (debrid:availability:ad:{hash})
so all users benefit from previous checks without hitting the AD API again.
TTLs:
- Confirmed cached (instant:True) → 7 days
- Confirmed not cached (instant:False) → 1 hour
Anti-pollution safety rules:
- False is only cached when the AD API returned status:success AND the hash
was present in the response (confirmed by AD itself)
- API errors, MAGNET_TOO_MANY_ACTIVE, exceptions and missing hashes
never produce a cache entry → a misconfigured token cannot pollute
the shared cache for other users
- StremThru confirmed results are also stored (always True, 7 days)
- Redis lookup uses mget (single round-trip for all hashes)
- Redis writes use pipeline (single round-trip per batch)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>