Files
tubular/docs/anilist-direct-provider-feasibility.md
T

140 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# AniList catalog and direct streaming feasibility
Observed **2026-10-25**. Research only: no application/player implementation, browser automation, GUI playback, protection bypass, remote mutation, or persisted stream URLs. This investigation follows the proposed AniList catalog → independently resolved episode path, not another Miruro mirror change.
## Decision
**The catalog-to-native-media path works for a sample without Miruro.** AniList's native GraphQL interface returned catalog metadata, and ZokoAnime accepted both the returned MAL ID and its working AniList-ID route. Fresh HLS and English subtitles were retrieved; FFmpeg decoded a video frame at the start and after seeking to ten minutes.
**This is not yet a drop-in Terebi integration.** The sampled playlist crosses CDN origins that Terebi deliberately rejects, and no authoritative ZokoAnime episode-inventory interface was found in the inspected documentation/selector. Do not replace the browsing UI, synthesize available episodes from AniList totals, or disable the player's origin checks on the strength of this sample.
Evidence labels: **Observed** = executed bounded native requests/media probes; **Source** = inspected first-party documentation/JavaScript or maintained extractor code; **[INFERENCE]** = recommendation or implication not exercised in Tubular/Terebi.
## Discovery and exact identity mapping
**Source:** current [ani-cli master][ani-cli] identifies itself as version 5.1.6 and uses `hianime.at`, not the historical AllAnime interface. Its search/episode/server flow selects a ZokoAnime embed and reads its static source configuration. This is extractor-source evidence, not a recommendation to execute ani-cli or use its browser User-Agent/cipher configuration.
**Observed:** default native `ureq` search for Cowboy Bebop returned separate series, movie and special entries. Selecting the exact TV-series entry yielded 26 actual episode links. Episode 1's declared sub server pointed to:
```text
https://zokoanime.video/stream/mal/1/1/sub
```
The [ZokoAnime site][zoko] and [documentation][zoko-docs] advertise MAL/AniList-ID embeds. Their deployed [selector implementation][selector] emits source values `mal` and **`ani`**, whereas the [documentation implementation][docs-js] describes `anilist`.
**Observed discrepancy:** `/stream/anilist/1/1/sub` returned HTTP 404 JSON, `Route GET:/stream/anilist/1/1/sub not found`. `/stream/ani/1/1/sub` returned a playable configuration. Use the demonstrated `ani` route or AniList's `idMal`, not the prose's broken `anilist` route.
AniList's [media interface][media] exposes both identifiers. The exact first catalog query was:
```graphql
query {
Media(id: 1, type: ANIME) {
id
idMal
title { romaji english }
format
status
episodes
}
}
```
**Observed:** unauthenticated POST `https://graphql.anilist.co` returned HTTP 200 `application/json`, with AniList ID 1, MAL ID 1, title Cowboy Bebop, format TV, status FINISHED and total 26. After this query, the direct ZokoAnime lookup used only the returned identifier; no Miruro or HiAnime request participated in that lookup.
A second native GraphQL query established the split-season and movie identifiers below. Route success means a source configuration was returned, not that every episode or the semantic content was visually verified.
| Catalog entry | AniList ID | MAL ID | Catalog total | Direct lookup observed |
| --- | ---: | ---: | ---: | --- |
| Cowboy Bebop | 1 | 1 | 26 | `ani` and `mal`, episode 1: source + English subtitle; `ani`, episode 26: configuration present |
| Attack on Titan Season 3 Part 2 | 104578 | 38524 | 10 | `ani`, episode 1: source + English/Spanish/Portuguese subtitles |
| Cowboy Bebop: The Movie | 5 | 5 | 1 | `ani`, episode 1: configuration present |
**[INFERENCE]:** no external mapping dataset or title matching is needed for these routes. [Fribb/anime-lists][fribb] offers catalog-ID and TVDB/TMDB season/offset mappings, but it is unnecessary for this demonstrated direct-ID path and does not establish ZokoAnime's episode availability. No dataset was downloaded/adopted or its license relied on.
## Native request and media evidence
The temporary Rust probe used the project's existing `ureq 3.4.2`/rustls transport, normal certificate verification, default User-Agent, a 20-second global timeout, zero automatic redirects and a 2 MiB text-body limit. No cookies, authentication, browser impersonation, cipher tuning, challenge solution or proxy was supplied. Resource requests used `Referer: https://zokoanime.video/`; whether that header is mandatory was not tested.
Bodies were captured in memory; only metadata and redacted summaries were surfaced. Player JavaScript was inspected, never executed. The embed's `window.__P` configuration uses the Base64/XOR JSON transform explicitly implemented in [its own decoder][obfuscate]; no DRM, challenge or ad-verification protocol was implemented. Advertising scripts and view/telemetry actions were not requested.
| Resource | HTTP | Content-Type | Body bytes / result |
| --- | ---: | --- | --- |
| [HiAnime title search][search] | 200 | text/html | 250238 bytes; series/movie/special candidates |
| [Selected series][series] | 200 | text/html | 186932 bytes |
| [Actual episode list][episodes] | 200 | application/json | 25926 bytes; 26 links; episode 1 ID 21418, title Asteroid Blues |
| [Episode 1 servers][servers] | 200 | application/json | 1936 bytes; ZokoAnime and HD-1 for sub/dub |
| [Direct MAL episode embed][mal-embed] | 200 | text/html | 1832 bytes; source + English subtitle configuration |
| [Direct AniList episode embed][ani-embed] | 200 | text/html | 1832 bytes; source + English subtitle configuration |
| Fresh HLS master, URL redacted | 200 | application/vnd.apple.mpegurl | 98 bytes; one advertised variant |
| HLS variant, URL redacted | 200 | application/vnd.apple.mpegurl | 38193 bytes; 353 segments; ENDLIST; no EXT-X-KEY tags |
| English subtitle, URL redacted | 200 | text/vtt | 14466 bytes; WEBVTT; 217 cue arrows |
**Observed native media:** `ffprobe 9.0.2` exited 0 and reported H.264 video at 1448×1080, AAC audio at 48000 Hz, and duration 1483.145982 seconds. The manifest advertised 1920×1080; the decoded stream dimensions are the stronger evidence. No audio track/language equivalence across other items was verified.
Two bounded `ffmpeg` runs, each capped at 30 seconds, requested one video frame with input seeks of 0 and 600 seconds. Both exited 0 with `frame=1` and `progress=end`. Final `ffprobe` and decode checks explicitly set `tls_verify=1` and `max_redirects=0`, used `http,https,tcp,tls` protocols only, five-second socket timeouts, 1 MiB probe size, two-second analysis duration, and `extension_picky=0`. The latter permits segment-content probing rather than trusting CDN filename extensions; Terebi already uses the same setting. No whole-episode download or GUI playback occurred.
### Missing episodes are not HTTP errors
**Observed:** [episode 27][missing] of the finished 26-episode sample returned **HTTP 200 `text/html`**, but no `window.__P` configuration. Its visible page said “Error 404 / Not found” and that there was nothing to play. Status/MIME alone cannot establish availability. The resolver must recognize missing configuration as unavailable, not a successful empty source.
**Source:** AniList's `episodes` field is the amount when complete; its [streamingEpisodes][streaming-episodes] links are legal streaming-site links, not a general native-stream inventory. The inspected ZokoAnime documentation exposes a numbered embed lookup, and the player selector takes a manually entered episode number. No authoritative episode-list interface was established. The HiAnime listing proves the sample's inventory but would add another aggregation dependency if used in production.
**[INFERENCE]:** episodic browsing needs a separate availability decision: obtain a supported inventory, or explicitly distinguish catalog episode numbers from provider-confirmed playable episodes and resolve on demand. This report does not choose or silently change Tubular's current available-episodes-only behavior. Split seasons, movies and specials still need explicit mapping semantics; the sampled routes are not universal proof.
## Terebi compatibility and security
**Observed:** the master and variant were on `hls.dramahot.top`, while their segment URLs named `hls1.drama1.cfd`. URLs/query tokens are deliberately not retained here.
**Source:** current [`resolvedIOOpen`][player-policy] rejects any resource whose origin differs from the initial source's origin, and rejects redirects before forwarding custom headers. [`StreamURL.parse`][url-parse] has a dedicated Miruro case only; other hosts enter the YouTube path, not a ZokoAnime resolver. Existing Miruro identity/progress/state must not be reused to disguise this provider.
**[INFERENCE]:** the sampled manifest would fail the current protected HLS path despite generic FFmpeg being able to decode it. Native FFmpeg success is not Terebi acceptance, subtitle rendering, resume/progress, or complete playback proof. No Terebi source or security policy was changed or exercised with this provider.
A future integration needs a separately reviewed, origin-scoped resource/header policy for the concrete CDN relationships, or a demonstrably compatible source. Do not simply remove the origin comparison, globally allow cross-origin redirects, or forward headers to arbitrary manifest hosts.
## Recommendation and remaining prerequisites
- **Go for a focused implementation design:** AniList catalog → exact AniList/MAL ID → ZokoAnime native resolver is demonstrated independently of Miruro. Keep catalog/selection in Tubular and fresh source/subtitle resolution in Terebi. Do not add a general multi-provider framework or mapping database for this one path.
- **Before replacing browsing:** settle authoritative episode availability versus explicitly labeled on-demand resolution. Keep unknown/missing distinct from zero and from network/protection errors.
- **Before native player integration:** decide the narrow cross-origin policy and provider-correct identity/progress contract; map or explicitly retain existing Miruro-keyed collections rather than reinterpreting them.
- **Before distribution:** review [AniList's terms][terms]. They prohibit mass collection, API use as backup/storage, and competing non-complementary list/tracker services. Tubular's local watched-state makes applicability worth clarifying; this investigation does not establish permission. ZokoAnime advertises iframe integration, but no stable documented raw-media/native interface or distribution-rights guarantee was established.
- **Known limits:** small point-in-time sample; no ongoing-series inventory, dub media decode, special-numbering coverage, full episode playback, source expiry/IP-binding, uptime, geographical availability or rights verification. ZokoAnime may itself have upstream dependencies; only independence from Miruro on the exercised path is proven.
### Reproduction record
The executed temporary harness was built with `cargo build --locked --example direct_provider_probe` and called as `target/debug/examples/direct_provider_probe URL`; AniList POST payloads and resource Referer values were passed through environment variables. A Python subprocess driver kept response bodies and fresh URLs in memory, applied the first-party static config decoder and invoked `ffprobe`/`ffmpeg` directly. The temporary Rust source, binary and driver state were removed after this report; that Cargo example is **not a retained runnable command**. No permanent tests were added for research-only work.
For a fresh ordinary-HTTPS metadata/status check, the following requests reproduce the public request shapes; the native results above were obtained with ureq, not these curl commands:
```sh
curl --silent --show-error --max-time 20 \
-H 'Content-Type: application/json' \
--data '{"query":"query { Media(id: 1, type: ANIME) { id idMal title { romaji } episodes } }"}' \
https://graphql.anilist.co
curl --silent --show-error --max-time 20 --output /dev/null \
--write-out 'HTTP %{http_code}; %{content_type}\n' \
https://zokoanime.video/stream/ani/1/1/sub
```
Re-run source resolution before any media check; never save or publish resolved tokenized URLs. The first-party configuration decoder and native media flags above document the exercised method without retaining credentials or stale media URLs.
[ani-cli]: https://raw.githubusercontent.com/pystardust/ani-cli/master/ani-cli
[zoko]: https://zokoanime.video/
[zoko-docs]: https://zokoanime.video/docs
[selector]: https://zokoanime.video/assets/player-CiFNLeyH.js
[docs-js]: https://zokoanime.video/assets/docs-sIsMGsz7.js
[obfuscate]: https://zokoanime.video/core/obfuscate.js?v=23
[media]: https://docs.anilist.co/reference/object/media
[terms]: https://docs.anilist.co/guide/terms-of-use
[streaming-episodes]: https://docs.anilist.co/reference/object/mediastreamingepisode
[fribb]: https://github.com/Fribb/anime-lists
[search]: https://hianime.at/search?keyword=Cowboy+Bebop
[series]: https://hianime.at/cowboy-bebop-1281
[episodes]: https://hianime.at/api/theme/episode/list/1281
[servers]: https://hianime.at/api/theme/episode/servers?episodeId=21418
[mal-embed]: https://zokoanime.video/stream/mal/1/1/sub
[ani-embed]: https://zokoanime.video/stream/ani/1/1/sub
[missing]: https://zokoanime.video/stream/ani/1/27/sub
[player-policy]: ../../terebi/Sources/Core/player.cpp#L111-L126
[url-parse]: ../../terebi/Sources/App/StreamResolver.swift#L8-L35