Skip to content

Mapping of AniWorld Specials/Extras/"Filme" to Sonarr#56

Merged
Zzackllack merged 14 commits intomainfrom
specials-extras
Feb 10, 2026
Merged

Mapping of AniWorld Specials/Extras/"Filme" to Sonarr#56
Zzackllack merged 14 commits intomainfrom
specials-extras

Conversation

@Zzackllack
Copy link
Owner

@Zzackllack Zzackllack commented Feb 10, 2026

Description

This pull request introduces metadata-backed mapping for "specials" episodes in the AniWorld provider, improving support for Sonarr and similar clients by enabling more accurate episode matching and search. The changes add new query parameters, implement special mapping logic for both search and episode requests, and expose configuration options for the feature. Additionally, the downloader logic is updated for improved robustness.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update

Testing

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

Screenshots (if applicable)

Additional Notes

Metadata-backed specials handling:

  • Added SpecialIds and special mapping logic to support metadata-backed specials matching, including new _handle_special_search function and integration into the main Torznab API flow. This enables more accurate mapping of requested episodes to AniWorld's specials, returning results with Sonarr-style aliases. [1] [2] [3]
  • Implemented fallback mapping for episode requests: If a direct episode probe fails and the site is AniWorld, the API attempts to resolve a special mapping and probe the mapped episode, updating release titles and magnet links accordingly. [1] [2] [3] [4] [5]

API enhancements:

  • Added support for new query parameters (tvdbid, tmdbid, imdbid, rid, tvmazeid) to the Torznab API, allowing clients to specify external IDs for improved matching. Logging and supported parameter lists were updated accordingly. [1] [2] [3]

Configuration:

  • Introduced environment variables for controlling metadata-backed specials mapping, including enable/disable, timeout, cache TTL, and confidence threshold. These are logged at startup for visibility.

Downloader robustness:

  • Improved handling of episode download hints: Ensured release_override is set only once and added checks for season and episode being non-None when generating filenames, preventing potential errors. [1] [2] [3] [4]

Summary by CodeRabbit

  • New Features

    • Torznab: added ID hints (TVDB, TMDB, IMDb, TVMaze, Rage) and movie/movie-search support.
    • AniWorld specials mapping: metadata-backed alias/source mapping for specials/extras (preserves Sonarr-style alias numbering in results and STRM links).
  • Improvements

    • Download flow: preserves title/STRM hints and applies alias-based release naming consistently.
  • Documentation

    • API and guides updated (params, movie searches, specials behavior, troubleshooting).
  • Tests

    • New tests for AniWorld specials, torznab specials mapping, and downloader episode flows.

Zzackllack and others added 6 commits February 9, 2026 23:59
- Introduced detailed documentation outlining the current state and evidence for handling AniWorld specials/extras.
- Added options comparison for different strategies to improve title-based searches.
- Provided a recommended design for integrating AniWorld specials with Sonarr's request behavior.
- Included an implementation checklist to guide future development phases.
add functionality for resolving special episode mappings from queries and requests. This includes enhancements to the API for handling specials, integrating with AniWorld's metadata, and caching mechanisms for improved performance.
implement logic to use title hint as a release name override in the download_episode function, enhancing the flexibility of the download process.
@Zzackllack Zzackllack self-assigned this Feb 10, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 10, 2026

📝 Walkthrough

Walkthrough

Adds metadata-backed AniWorld specials mapping: new provider module with caching and matching, new SPECIALS_* config flags, Torznab API ID hints and special-search integration, downloader release-override propagation, documentation updates, and tests for parsing, mapping, Torznab integration, and downloader behavior.

Changes

Cohort / File(s) Summary
Repository config
\.gitignore
Added .uv-cache/; simplified .codex/ to ignore the entire directory.
Specials config
app/config.py
New SPECIALS_* environment-driven constants (enabled, timeout, cache TTL, match confidence) with validation and debug logging.
AniWorld specials provider
app/providers/aniworld/specials.py
New module: models, parsing, fetch-with-cache, scoring, SkyHook/TVDB resolution, and mapping functions to resolve AniWorld specials to alias coordinates (public entrypoints: parse_filme_entries, fetch_filme_entries, resolve_special_mapping_from_query, resolve_special_mapping_from_episode_request).
Torznab API
app/api/torznab/api.py
Extended torznab_api signature with ID hints (tvdbid,tmdbid,imdbid,rid,tvmazeid); added _try_mapped_special_probe and _handle_special_search; integrated per-language special-mapping flow into tvsearch/general search, GUID/magnet/STRM adjustments, and mapped-alias metadata upserts.
Torznab utils
app/api/torznab/utils.py
Expanded SUPPORTED_PARAMS to include ID hints (q,season,ep,tvdbid,imdbid,rid,tvmazeid,tmdbid).
Downloader changes
app/core/downloader/download.py, app/core/downloader/episode.py, app/utils/naming.py
Tightened None checks for slug/season/episode (allow season 0); compute release_override from title_hint (strip " [STRM]") and propagate to final rename via new release_name_override param. Added/updated docstrings and base_hint logic.
Docs: Torznab & API render
docs/src/openapi.json, docs/src/api/torznab.md, docs/agents/api.md
Documented movie/movie-search and ID query params; described AniWorld specials alias mapping and release-title preservation; ApiOperations now hide branding.
Docs: ApiOperations branding
docs/src/api/*.md, docs/src/api/operations/[operationId].md
Added hide-branding prop to ApiOperations/OAOperation usages across multiple docs files.
Docs: Integrations & Troubleshooting
docs/src/integrations/sonarr.md, docs/src/guide/troubleshooting.md
Guidance added about preserving Sonarr alias numbering for AniWorld specials and how to verify content_path to avoid import failures.
Tests & test scaffolding
tests/test_aniworld_specials.py, tests/test_torznab_specials_mapping.py, tests/test_downloader_episode.py, tests/test_torznab_utils.py, tests/conftest.py
New unit/integration tests covering parsing, mapping resolution, Torznab special-mapping behavior, and downloader title-hint/release-override behavior; added stub_aniworld_parser fixture and various mocks.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Torznab Client
    participant TorznabAPI as Torznab API
    participant Specials as Specials Module
    participant AniWorld as Aniworld (HTTP)
    participant SkyHook as SkyHook/TVDB
    participant Cache as TTL Cache
    participant RSS as RSS Generator

    Client->>TorznabAPI: GET /torznab/api?q=...&tvdbid=...&season=0&ep=4
    TorznabAPI->>Specials: _handle_special_search(q, ids, limit)
    Specials->>Cache: check slug & show payload
    alt cache hit
        Cache-->>Specials: entries + payload
    else
        Specials->>AniWorld: fetch_filme_entries(slug)
        AniWorld-->>Specials: AniworldSpecialEntry list
        Specials->>SkyHook: resolve show/episodes (by IDs/title)
        SkyHook-->>Specials: episode payload
        Specials->>Cache: store entries/payload
    end
    Specials->>Specials: compute best SpecialEpisodeMapping
    Specials-->>TorznabAPI: mapping or None
    alt mapping found
        TorznabAPI->>RSS: build item(s) with mapped source/alias S/E, magnet/STRM, GUID metadata
    else
        TorznabAPI->>TorznabAPI: proceed with existing preview/fallback search
    end
    RSS-->>Client: RSS feed with items
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the primary change: mapping of AniWorld Specials/Extras to Sonarr, which aligns with the main feature introduced across the codebase.
Description check ✅ Passed The description covers all major aspects: specific features added, implementation details with links, configuration changes, and downloader improvements. Testing checklist is complete and documentation updates are confirmed.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch specials-extras

No actionable comments were generated in the recent review. 🎉

🧹 Recent nitpick comments
tests/test_torznab_specials_mapping.py (2)

277-317: Missing docstring on test_tvsearch_guid_alias_suffix_only_when_alias_differs.

All other test functions in this file include a docstring describing the intent and assertions. This one should too, for consistency and per PEP 257 conventions. As per coding guidelines, "Provide docstrings following PEP 257 conventions."


154-175: Docstring for _probe_quality is misleading about the fourth return element.

The docstring describes it as release_language with value "VOE", but this is actually the provider name (matching the real probe_episode_quality return signature). Not blocking since it's test-only code, but it could confuse future contributors.

app/api/torznab/api.py (3)

61-69: Consider a NamedTuple or dataclass for the 8-element return type.

An 8-element plain tuple is easy to misorder at call sites and hard to read. A lightweight NamedTuple would make destructuring self-documenting and catch field-ordering bugs at development time.

Example
from typing import NamedTuple, Optional

class MappedProbeResult(NamedTuple):
    available: bool
    height: Optional[int]
    vcodec: Optional[str]
    provider: Optional[str]
    source_season: int
    source_episode: int
    alias_season: int
    alias_episode: int

63-63: tn_module parameter lacks a type annotation.

Per coding guidelines, all parameters should include type hints. Even though this is a module reference used for duck-typing, annotating it (e.g., tn_module: types.ModuleType) would improve readability and tooling support. As per coding guidelines, "Ensure functions have descriptive names and include type hints."


301-502: Significant duplication between _handle_special_search and _handle_preview_search.

The probe → upsert → build_release → build_magnet → build_item loop is nearly identical between these two functions. The key differences are: (a) alias vs source coordinates, (b) extra metadata in upsert, and (c) the initial mapping resolution. Consider extracting the shared item-building loop into a helper to reduce the ~200 lines of near-duplicate code.

Not blocking — the differences are meaningful and the current structure is readable — but worth tracking for a future cleanup pass.

app/providers/aniworld/specials.py (2)

446-475: _skyhook_search does not cache 404 responses — may cause repeated failed lookups.

Unlike fetch_filme_entries (which caches empty results on 404), _skyhook_search and _skyhook_show call raise_for_status() on any non-success response, so a 404 from SkyHook will be re-attempted on every call for the same term until the external service returns a different status. For a search term that consistently 404s, this creates unnecessary repeated HTTP requests.

Consider caching an empty result on 404, similar to the pattern used in fetch_filme_entries at Line 362-364.

Suggested change for _skyhook_search
     response = http_get(url, timeout=timeout_seconds)
+    if response.status_code == 404:
+        _set_skyhook_cached_search(normalized_term, [])
+        return []
     response.raise_for_status()

637-679: Score can exceed 1.0 before threshold comparison in _pick_entry_for_episode.

Line 667 adds a 0.10 bonus after _title_score (which clamps to [0.0, 1.0]), so score can reach 1.10. The comment on line 665 acknowledges this. While the threshold comparison still works correctly (higher score = better match), this means the score semantics differ from _title_score's documented 0.0–1.0 range. Just noting this for awareness — not blocking.

📜 Recent review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2bb4a3c and 3234de1.

📒 Files selected for processing (5)
  • app/api/torznab/api.py
  • app/core/downloader/download.py
  • app/providers/aniworld/specials.py
  • tests/conftest.py
  • tests/test_torznab_specials_mapping.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/conftest.py
🧰 Additional context used
📓 Path-based instructions (3)
app/**

📄 CodeRabbit inference engine (.github/instructions/Next.js-Tailwind-Development-Instructions.instructions.md)

app/**: Use Next.js App Router with server and client components
Group routes by feature/domain within the app directory

Files:

  • app/api/torznab/api.py
  • app/providers/aniworld/specials.py
  • app/core/downloader/download.py
**/*.py

📄 CodeRabbit inference engine (.github/instructions/Performance-Optimization-Best-Practices.instructions.md)

**/*.py: Use functools.lru_cache for memoization of expensive pure functions
Use asyncio or multiprocessing for I/O-bound and CPU-bound tasks respectively; avoid GIL bottlenecks in CPU-bound code

**/*.py: Write clear and concise comments for each function
Ensure functions have descriptive names and include type hints
Provide docstrings following PEP 257 conventions
Use the typing module for type annotations (e.g., List[str], Dict[str, int])
Break down complex functions into smaller, more manageable functions
Always prioritize readability and clarity
For algorithm-related code, include explanations of the approach used
Write code with good maintainability practices, including comments on why certain design decisions were made
Handle edge cases and write clear exception handling
For libraries or external dependencies, mention their usage and purpose in comments
Use consistent naming conventions and follow language-specific best practices
Write concise, efficient, and idiomatic code that is also easily understandable
Follow the PEP 8 style guide for Python
Maintain proper indentation (use 4 spaces for each level of indentation)
Ensure lines do not exceed 79 characters
Place function and class docstrings immediately after the def or class keyword
Use blank lines to separate functions, classes, and code blocks where appropriate
Account for common edge cases like empty inputs, invalid data types, and large datasets
Include comments for edge cases and the expected behavior in those cases

**/*.py: Python runtime baseline is 3.12
Run tests using pytest
Format code using ruff format app

Files:

  • app/api/torznab/api.py
  • app/providers/aniworld/specials.py
  • tests/test_torznab_specials_mapping.py
  • app/core/downloader/download.py
**/*.{js,jsx,ts,tsx,py,java,cs}

📄 CodeRabbit inference engine (.github/instructions/Performance-Optimization-Best-Practices.instructions.md)

Minimize logging in hot paths and prefer structured logging

Files:

  • app/api/torznab/api.py
  • app/providers/aniworld/specials.py
  • tests/test_torznab_specials_mapping.py
  • app/core/downloader/download.py
🧠 Learnings (1)
📚 Learning: 2025-10-10T20:57:55.343Z
Learnt from: CR
Repo: Zzackllack/AniBridge PR: 0
File: .github/instructions/Python-Coding-Conventions.instructions.md:0-0
Timestamp: 2025-10-10T20:57:55.343Z
Learning: Applies to **/*.py : Provide docstrings following PEP 257 conventions

Applied to files:

  • app/providers/aniworld/specials.py
🧬 Code graph analysis (2)
app/providers/aniworld/specials.py (4)
app/utils/logger.py (1)
  • config (21-90)
app/providers/megakino/client.py (1)
  • search (95-133)
tests/test_aniworld_specials.py (1)
  • raise_for_status (134-135)
tests/test_title_resolver_sto.py (1)
  • json (15-16)
tests/test_torznab_specials_mapping.py (3)
tests/conftest.py (1)
  • client (46-131)
app/api/torznab/api.py (1)
  • torznab_api (506-1023)
app/providers/aniworld/specials.py (1)
  • SpecialEpisodeMapping (113-121)
🔇 Additional comments (13)
app/core/downloader/download.py (3)

61-64: LGTM — empty-string-to-None guard correctly implemented.

The cleaned if cleaned else None pattern properly addresses the edge case where title_hint could yield an empty string after stripping.


100-116: Good: Megakino flow guards None season/episode and propagates release_override.

The season is not None and episode is not None checks correctly prevent f"{season:02d}" from raising TypeError on None, and release_name_override is now consistently threaded through to rename_to_release.

Also applies to: 135-138, 148-157


217-218: Non-Megakino path: consistent with Megakino changes.

Same None-safe guard for base_hint generation and release_name_override propagation.

Also applies to: 302-310

tests/test_torznab_specials_mapping.py (2)

64-121: Good: test_search_uses_special_mapping_alias_in_title verifies alias coordinates flow through to title and enclosure.

The test correctly validates that the alias episode (S00E05) appears in the title while the source coordinates (aw_s=0, aw_e=4) appear in the magnet/enclosure URL.


255-274: Good: Cross-language reuse test validates that special mapping is resolved once but probed per language.

The assertion that the requested episode (S0E5) is probed only once while the mapped episode (S0E4) is probed twice (once per language) confirms efficient mapping reuse.

app/api/torznab/api.py (3)

976-978: GUID alias suffix is now conditional — addresses the previous review concern.

The suffix :alias-s{alias_season}e{alias_episode} is only appended when the alias coordinates differ from the source, preventing GUID format changes for non-special episodes.


660-685: Good: Special search is attempted first and falls back to preview search.

When SPECIALS_METADATA_ENABLED is true and the special search produces results, the preview search is skipped; otherwise it falls back cleanly. The SpecialIds are correctly forwarded.


816-829: special_map_attempted guard correctly limits resolution to one attempt per request.

The mapping is resolved once on the first unavailable probe, then reused for subsequent languages. On subsequent languages, Lines 825-829 apply the already-resolved coordinates before checking cache/probing, which is the correct behavior.

Also applies to: 877-914

app/providers/aniworld/specials.py (5)

51-68: Cache pruning is clean and correctly bounded.

The two-pass approach (TTL expiry then overflow eviction by oldest timestamp) is simple and effective for the 512-entry limit. The _unlocked naming convention clearly communicates that the caller must hold _CACHE_LOCK.


186-232: Title scoring is well-designed with appropriate clamping.

The weighted combination of overlap, Jaccard, and containment with part-number bonus/penalty is a pragmatic approach. The final max(0.0, min(1.0, score)) clamp ensures the return value stays in bounds regardless of bonus/penalty effects.


335-368: Good: 404 handling now returns an empty list instead of raising.

This correctly addresses the previous review comment — a 404 for a slug with no specials page is a normal case, not an error.


71-79: Data models are clean and well-structured.

frozen=True dataclasses are a good choice for these immutable value types. The combined_title property on AniworldSpecialEntry is a nice convenience that avoids scattered string concatenation.

Also applies to: 82-103, 105-109, 112-121


756-814: Resolution pipeline is well-layered with clear separation of concerns.

resolve_special_mapping_from_query (title-based) and resolve_special_mapping_from_episode_request (coordinate-based) share context resolution via _resolve_special_context but differ appropriately in how they select the metadata episode. Both return None on any resolution failure, ensuring safe fallback.

Also applies to: 817-881

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Add support for movie queries in the Torznab API, including new query parameters for optional ID hints. Update documentation to reflect changes in specials/extras handling for AniWorld, ensuring consistency in Sonarr alias numbering.
This change updates multiple API documentation files to include the
hide-branding attribute for the ApiOperations component, ensuring a
consistent appearance across the documentation.
@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Feb 10, 2026

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
anibridge-docs 2bb4a3c Feb 10 2026, 07:48 PM

@Zzackllack Zzackllack marked this pull request as ready for review February 10, 2026 18:58
Copilot AI review requested due to automatic review settings February 10, 2026 18:58
@coderabbitai coderabbitai bot added documentation Improvements or additions to documentation enhancement New feature or request labels Feb 10, 2026
coderabbitai[bot]

This comment was marked as resolved.

This comment was marked as resolved.

@coderabbitai

This comment was marked as resolved.

Docstrings generation was requested by @Zzackllack.

* #56 (comment)

The following files were modified:

* `app/api/torznab/api.py`
* `app/core/downloader/download.py`
* `app/core/downloader/episode.py`
* `app/providers/aniworld/specials.py`
* `tests/test_downloader_episode.py`
* `tests/test_torznab_specials_mapping.py`
* `tests/test_torznab_utils.py`
coderabbitai[bot]

This comment was marked as resolved.

…probes

Reuse resolved specials mapping across tvsearch language iterations to avoid
re-probing the originally requested episode for every language. Keep GUIDs
stable by appending alias suffixes only when alias and source coordinates
differ, and skip special-search resolution work when metadata mapping is
disabled.

Normalize downloader release override handling so empty STRM-only title hints
produce `None` instead of an empty string. Handle AniWorld `/filme` 404 as a
valid "no specials" case and cache the empty result.

Refactor AniWorld specials matching internals by extracting shared resolver
context, adding named scoring/threshold constants, documenting public entry
points/dataclasses, and adding bounded TTL cache pruning under lock.

Update tests to use a shared `aniworld.parser` fixture with monkeypatch-based
`sys.modules` isolation, add regressions for GUID suffix behavior, multi-lang
probe reuse, downloader override normalization, and 404 specials handling.

Update docs for Sonarr/Torznab troubleshooting and API guidance, including
line-wrap fixes to follow markdown readability guidelines.
coderabbitai[bot]

This comment was marked as resolved.

Introduces a new function to probe mapped AniWorld special coordinates,
utilizing cache first and then performing a live probe if necessary.
This improves the reliability of availability checks for specials.

Also updates the existing code to utilize this new function, reducing
redundancy and improving clarity.
@Zzackllack Zzackllack merged commit 10e1626 into main Feb 10, 2026
12 checks passed
@Zzackllack Zzackllack deleted the specials-extras branch February 10, 2026 20:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants