Anikoto 2 (anikoto.cz) Resolution¶
Contributor notes for the Anikoto 2 implementation in AniPlay.
Last verified: 2026-07-27
Purpose¶
This document explains how AniPlay turns an anikoto.cz search result into an episode-specific embedded player:
- search the provider catalog;
- preserve the provider's canonical show identity;
- discover episodes and their language availability;
- load the selected episode page to initialize the provider session;
- resolve the episode's server matrix;
- exchange an opaque server token for a third-party embed URL.
It also documents the distinction between Anikoto 1 and Anikoto 2, the optional NekoStream mapper request, current playback limitations, security boundaries, and the parts of the workflow that can be reused by other projects.
Although this document describes AniPlay's implementation, it may be used as a reference by ani-cli contributors and other projects integrating anikoto.cz or another catalog-to-server-matrix provider. The endpoint shapes described here are observed private implementation details, not a stable public API. Isolate them behind a provider module and expect them to change.
For copyable PowerShell and curl commands covering every request stage, see
anikoto2-scraping-commands.md.
The implementation discussed here lives primarily in:
ani-cli-gui/electron/providers/anikoto2.tsani-cli-gui/electron/scrape.tsani-cli-gui/electron/main.tsani-cli-gui/src/catalog-types.tsani-cli-gui/src/pages/BrowsePage.tsxani-cli-gui/src/lib/watch-together-content.ts
Deterministic parser tests live in:
ani-cli-gui/electron/providers/anikoto2.test.ts
Anikoto 1 and Anikoto 2 are separate providers¶
AniPlay intentionally presents two independent Anikoto entries:
| AniPlay name | Catalog | Player workflow | Current capability |
|---|---|---|---|
| Anikoto 1 | anikotoapi.site |
Anikoto API -> MegaPlay -> native media or iframe | Native HLS, subtitles, downloads, and Watch Together where resolution succeeds |
| Anikoto 2 | anikoto.cz |
Website AJAX catalog -> episode server matrix -> third-party iframe | Embedded playback and browser fallback |
Anikoto 2 does not reuse Anikoto 1 IDs, catalog responses, MegaPlay mappings, cookies, or media-header policies. AniPlay does not silently switch between them.
The name "Anikoto 2" is an AniPlay UI distinction. It should not be interpreted as an official upstream version number.
Important terminology¶
| Layer | Current service | Purpose |
|---|---|---|
| Catalog and episode index | anikoto.cz |
Search suggestions, canonical show page, episodes, language availability, and opaque server tokens |
| Supplemental mapping | mapper.nekostream.site |
Optional additional server tokens associated with MAL and episode metadata |
| Server-token exchange | anikoto.cz/ajax/server |
Converts an opaque data-link-id token into an embed URL |
| Embed/player | VidPlay, Vidstream, VidCloud, HD, Kiwi-Stream, and others | Hosts the actual browser player |
| Media delivery | Provider-dependent third-party CDNs | Serves media used internally by an embed; not currently resolved by AniPlay |
The server label is not a stable media-provider API contract. The same visible label can change implementation, and new labels can appear without notice.
Complete resolution flow¶
Search term
|
v
GET /ajax/anime/search?keyword=...
|
+-- parse /watch/<slug> results
|
v
anikoto2:<Base64URL JSON metadata>
|
v
GET /watch/<slug>
|
+-- #watch-main[data-id]
+-- #watch-main[data-url]
|
v
GET /ajax/episode/list/<show-id>?style=grid&vrf=
|
+-- episode number and slug
+-- opaque server-list token
+-- SUB/DUB availability
+-- MAL ID and timestamp, when present
|
v
Selected episode and translation mode
|
+-- GET /watch/<slug>/ep-<episode-slug>
| initializes the persistent provider session
|
+-- GET /ajax/server/list?servers=<episode-token>
| returns SUB/H-SUB/DUB server groups
|
+-- optional mapper.nekostream.site lookup
| returns supplemental provider tokens
|
v
For each selected-language data-link-id
|
v
GET /ajax/server?get=<opaque-token>
|
v
Validated HTTPS embed StreamLink
The final result is currently an iframe URL, not a native HLS or MP4 URL.
Request context and isolated session¶
AniPlay uses an Electron persistent session partition dedicated to this provider:
This is important because the server-token exchange can depend on cookies established by loading the show and episode pages. A stateless request to /ajax/server may return Bad request even when the token is current.
The partition also prevents provider cookies from being mixed with AniPlay's default renderer session or with the Anikoto 1 integration.
Normal document requests use browser-oriented headers:
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...
Accept: text/html,application/xhtml+xml,*/*;q=0.8
Accept-Language: en-US,en;q=0.9
Referer: https://anikoto.cz/
AJAX requests additionally use:
Accept: application/json, text/javascript, */*; q=0.01
X-Requested-With: XMLHttpRequest
Referer: <the relevant show or episode page>
Requests include session credentials, follow redirects, and have a bounded timeout. HTTP 429 is surfaced as a rate-limit error and is not retried aggressively.
Do not copy cookies from this session into third-party embed requests.
1. Searching the catalog¶
Search uses the website's autocomplete request:
The response is a JSON envelope whose result contains HTML:
AniPlay parses anchors matching:
It extracts:
- the canonical provider slug from the URL;
- the displayed title from
.nameor.d-title; - a cover from
img[src]orimg[data-src], when safe.
Results are deduplicated by slug. A guessed title slug is never treated as authoritative because punctuation, aliases, seasons, and provider-specific suffixes can make it incorrect.
Search results are cached for five minutes. Failed requests are removed from the cache so a temporary provider failure does not poison later searches.
2. Creating a stable internal ID¶
AniPlay stores Anikoto 2 identity as Base64URL JSON prefixed with anikoto2::
Conceptually:
The prefix provides provider routing for history, playback, notices, and future migrations. It also prevents a numeric or slug-like ID from being mistaken for an AllAnime or Anikoto 1 identifier.
When decoding the ID, AniPlay validates that:
- the prefix is exactly
anikoto2:; - the payload is valid JSON;
- the slug contains only the expected alphanumeric and hyphen characters;
- the slug length is bounded;
- an optional cover URL passes normal URL safety checks.
The encoded title and cover are display metadata. The canonical slug remains the provider identity.
3. Loading the canonical show identity¶
Episode discovery begins with:
The page contains a root element similar to:
AniPlay reads:
data-id: the provider's numeric show ID used by the episode-list request;data-url: the provider's canonical show URL and later episode-page base.
Both values are validated. The show ID must be numeric, and the canonical URL must remain on the exact https://anikoto.cz origin.
Do not assume the encoded slug, numeric show ID, episode ID, server-list token, and server link token are interchangeable. They belong to different stages.
4. Loading and parsing episodes¶
The numeric show ID is used here:
At the time of verification, an empty vrf value is accepted. It remains part of a private website workflow and may change.
The JSON result is another HTML fragment. Episode anchors currently expose data attributes similar to:
<a
data-id="134023"
data-num="1"
data-slug="1"
data-ids="OPAQUE_SERVER_LIST_TOKEN"
data-mal="61512"
data-timestamp="..."
data-sub="1"
data-dub="1"
></a>
AniPlay records:
| Attribute | Meaning |
|---|---|
data-num |
User-visible episode number |
data-slug |
Episode path component used by the canonical page and mapper |
data-ids |
Opaque token used by /ajax/server/list |
data-id |
Internal episode ID, retained for diagnostics/future use |
data-mal |
MAL identity used only for optional mapper lookup |
data-timestamp |
Mapper input associated with the episode |
data-sub |
Whether a SUB selection is advertised |
data-dub |
Whether a DUB selection is advertised |
Episode numbers may be fractional. AniPlay accepts numeric forms such as 1, 12, and 12.5, normalizes them, removes duplicates, and sorts numerically.
Language selection is strict:
- SUB mode includes episodes with
data-sub="1"; - DUB mode includes episodes with
data-dub="1"; - AniPlay does not silently substitute SUB for a missing DUB.
The complete parsed series response is cached for five minutes.
5. Initializing the episode session¶
Before requesting server information, AniPlay loads:
This is not redundant. It initializes or refreshes the isolated provider session and establishes the episode page as the referer for subsequent AJAX requests.
Skipping this step can produce:
from the final server-token endpoint.
If recreating the workflow with a non-Electron client, use one cookie jar for the show page, episode page, server list, and server-token exchange. Do not resolve each stage with an unrelated stateless HTTP client.
6. Loading the server matrix¶
The episode's data-ids value is passed unchanged to:
The JSON result contains server groups:
<div class="servers">
<div class="type" data-type="sub">
<label>SUB</label>
<ul>
<li
data-ep-id="134023"
data-sv-id="8e4"
data-link-id="OPAQUE_LINK_TOKEN"
>VidPlay-1</li>
</ul>
</div>
</div>
A show may expose multiple groups:
SUB;H-SUBorHSUB;DUB;- supplemental download-only rows that do not contain
data-link-id.
AniPlay classifies groups using both data-type and the visible label. This matters because an H-SUB group can sometimes use data-type="sub" even though its label says H-SUB.
Current selection behavior:
- SUB mode accepts soft-sub and hard-sub groups;
- DUB mode accepts only dub groups;
- rows without
data-link-idare not treated as playable servers; - duplicate link tokens are removed.
The visible server name and data-sv-id are useful diagnostics, but data-link-id is the value used for resolution.
7. Understanding data-link-id¶
The data-link-id may look like Base64 text. It is still an opaque provider token.
Do not:
- treat its decoded bytes as a final media URL;
- invent or modify token contents;
- persist it as a stable episode identifier;
- publish real tokens in fixtures or bug reports;
- assume two visually similar tokens point to the same server.
AniPlay sends the exact token returned by the current server-list response back to anikoto.cz.
This design keeps provider-specific token handling isolated and avoids depending on the provider's internal encryption or serialization format.
8. Optional NekoStream mapper lookup¶
When data-mal, data-slug, and data-timestamp are all present, AniPlay also attempts:
This mapper can return supplemental provider entries. AniPlay currently recognizes nested sub and dub objects whose url field contains another token suitable for the normal Anikoto 2 server exchange.
Observed provider names are normalized for display:
| Mapper key | AniPlay label |
|---|---|
gogoanime |
Vidstream |
animepahe |
Kiwi-Stream |
anivibe |
Vibe-Stream |
| other keys | Original provider key |
Mapper sources are categorized as H-SUB for SUB mode or A-DUB for DUB mode, appended after the base server list, and deduplicated by token.
The mapper is supplemental:
- its failure does not fail the episode;
- it receives no Anikoto session cookies;
- it is not treated as the catalog authority;
- AniPlay does not depend on it for the canonical show or episode list.
9. Exchanging a server token¶
Each selected-language server token is resolved sequentially through:
The request uses:
- the same
persist:aniplay-anikoto2session; - credentials/cookies from earlier page loads;
- the canonical episode page as
Referer; X-Requested-With: XMLHttpRequest;- a browser User-Agent and AJAX-compatible
Accept.
A successful response resembles:
Some responses may place the URL directly in result; the parser accepts both shapes defensively.
AniPlay attempts all parsed servers so the player can expose a server selector. A failed server does not prevent another server from resolving. Final links are deduplicated by URL.
If no server resolves, AniPlay reports the first useful resolution error or a general no-playable-embeds error.
10. Validating the returned embed URL¶
The server endpoint returns a third-party URL controlled by upstream data. Treat it as untrusted input.
AniPlay accepts only URLs that:
- use HTTPS;
- contain no embedded username or password;
- do not target
localhostor a.localhostname; - do not target a literal IPv4 or IPv6 address;
- remain below the configured maximum length;
- parse as a valid absolute URL.
The validated result becomes:
{
url: 'https://THIRD_PARTY_EMBED/...',
resolution: 'Embed',
hls: false,
provider: 'Anikoto 2 · SUB · VidPlay-1',
downloadable: false,
embed: true,
}
Provider cookies, AJAX tokens, and the Anikoto referer are not forwarded automatically to the third-party host.
Literal-IP and loopback rejection reduces accidental local-network access, but it is not a complete SSRF defense against DNS rebinding. Any future main-process prefetching of returned URLs should add DNS resolution and redirect validation rather than relying only on the current string-level checks.
11. Why AniPlay currently keeps these sources embedded¶
Anikoto 2 exposes a server matrix, but the resolved result is an embed URL. Each embed provider has its own player, scripts, source endpoint, headers, cookies, token lifetime, subtitle format, and possible anti-bot behavior.
A working iframe does not prove that a stable native media API exists.
AniPlay therefore does not currently:
- scrape native HLS or MP4 URLs from Anikoto 2 embeds;
- inject Anikoto cookies into embed hosts;
- classify embed URLs as downloadable media;
- claim subtitle tracks exposed only inside the iframe;
- apply broad media-header or CORS rules to unknown hosts.
VidPlay, Vidstream, VidCloud, HD, Kiwi-Stream, and future servers should each be evaluated as separate native-source integrations.
12. Playback and server selection¶
Every resolved embed becomes an independent source entry. AniPlay's normal server selector can switch between them.
Because these are browser embeds:
- resolution is labeled
Embed; - quality selection remains inside the provider player;
- subtitles remain controlled by the provider player;
- AniPlay cannot guarantee uniform keyboard controls;
- embed-provider availability can vary by country, ISP, VPN, or content item.
If native source extraction is added later, retain the iframe as a graceful fallback whenever possible.
13. Downloads¶
Anikoto 2 links are currently marked:
AniPlay's downloader requires a validated native media URL and, when applicable, known provider-scoped request headers. Passing an iframe URL to FFmpeg does not turn it into a media source.
Do not enable downloads by merely changing the flag. A proper implementation must first:
- resolve a native HLS or direct-media URL;
- determine token lifetime and redirect behavior;
- identify required manifest, segment, key, and subtitle headers;
- scope those headers to exact trusted delivery domains;
- parse subtitle tracks;
- test cancellation and partial-file cleanup;
- retain the embed fallback independently.
14. Watch Together¶
AniPlay's Watch Together synchronization needs direct control over play, pause, seek, playback rate, buffering state, and current time.
An arbitrary third-party iframe does not provide a trusted common control protocol. Anikoto 2 is therefore considered uncontrollable while it resolves only embed sources.
Consequences:
- AniPlay does not create a synchronized room from an Anikoto 2 embed;
- an existing room cannot use the iframe as a fully synchronized source;
- chat and content identity alone would not make playback controllable;
- no resolved embed or media URL should be stored in the Worker.
If native playback is implemented later, participants should continue resolving their own current media URLs. Watch Together should share only stable content identity:
{
"provider": "anikoto2",
"showId": "anikoto2:BASE64URL_METADATA",
"animeName": "Example title",
"episode": "1",
"translationType": "sub"
}
15. Browser fallback¶
AniPlay can construct the canonical episode page from its stored slug:
This provides a browser fallback when embedded playback cannot be used inside AniPlay.
The fallback normalizes the episode number but should not be considered a substitute for a successfully parsed episode slug in every future provider revision. If data-slug diverges from the visible number, prefer the parsed canonical episode metadata.
Caching and rate limits¶
AniPlay caches successful search and series/episode results for five minutes.
The cache:
- is bounded;
- reuses in-flight promises;
- removes failed requests;
- does not aggressively retry HTTP 403 or 429;
- does not treat server link tokens as permanent media identifiers.
Provider token lifetimes are undocumented. Do not persist resolved embed URLs or tokens as durable playback history.
Respect upstream capacity. Avoid concurrently resolving a large number of episodes or putting diagnostic commands in a tight loop.
Manual diagnostics¶
These examples inspect response structure. They intentionally avoid resolving or printing real third-party embed URLs.
Use a cookie jar for the complete episode workflow:
curl -fsS \
-A 'Mozilla/5.0' \
-c anikoto2-cookies.txt \
-b anikoto2-cookies.txt \
'https://anikoto.cz/watch/SHOW_SLUG' \
| rg -o 'id="watch-main"[^>]+'
Search without printing provider tokens¶
curl -fsS \
-A 'Mozilla/5.0' \
-H 'Accept: application/json, text/javascript, */*; q=0.01' \
-H 'Referer: https://anikoto.cz/' \
-H 'X-Requested-With: XMLHttpRequest' \
'https://anikoto.cz/ajax/anime/search?keyword=black%20torch' \
| jq '{status, resultType: (.result | type), resultLength: (.result | length)}'
Inspect episode-list metadata¶
First obtain the numeric show ID from #watch-main, then:
curl -fsS \
-A 'Mozilla/5.0' \
-b anikoto2-cookies.txt \
-H 'Accept: application/json, text/javascript, */*; q=0.01' \
-H 'Referer: https://anikoto.cz/watch/SHOW_SLUG' \
-H 'X-Requested-With: XMLHttpRequest' \
'https://anikoto.cz/ajax/episode/list/SHOW_ID?style=grid&vrf=' \
| jq '{status, resultType: (.result | type), resultLength: (.result | length)}'
Initialize the selected episode session¶
curl -fsS \
-A 'Mozilla/5.0' \
-c anikoto2-cookies.txt \
-b anikoto2-cookies.txt \
'https://anikoto.cz/watch/SHOW_SLUG/ep-EPISODE_SLUG' \
-o /dev/null
Inspect the server-list envelope¶
Do not paste a real token into a public log:
curl -fsS \
-A 'Mozilla/5.0' \
-b anikoto2-cookies.txt \
-H 'Accept: application/json, text/javascript, */*; q=0.01' \
-H 'Referer: https://anikoto.cz/watch/SHOW_SLUG/ep-EPISODE_SLUG' \
-H 'X-Requested-With: XMLHttpRequest' \
'https://anikoto.cz/ajax/server/list?servers=REDACTED_EPISODE_TOKEN' \
| jq '{status, resultType: (.result | type), resultLength: (.result | length)}'
Delete the temporary cookie jar after debugging:
On PowerShell, use Remove-Item -LiteralPath .\anikoto2-cookies.txt instead.
Common failures¶
| Symptom | Likely cause | Check |
|---|---|---|
| Search returns no results | Provider autocomplete markup changed or the title is absent | Inspect the sanitized anchor classes and canonical /watch/ paths |
| Search returns invalid JSON | An error/challenge page replaced the AJAX response | Log status, content type, and final hostname without dumping the body |
| HTTP 429 | Requests are too frequent | Stop retrying, retain five-minute caches, and wait |
| Show identity is missing | #watch-main, data-id, or data-url changed |
Inspect the show page structure and canonical origin |
| Episode list is empty | AJAX attributes changed or the show has no indexed episodes | Check data-num, data-ids, data-sub, and data-dub |
| SUB exists but no server is parsed | H-SUB group labeling or server markup changed | Compare data-type, visible label, and li[data-link-id] |
| DUB request returns unavailable | The episode advertises no dub | Do not silently fall back to SUB |
/ajax/server says Bad request |
Episode page was not loaded in the same cookie session, or the token expired | Repeat show -> episode -> server list -> server exchange with one cookie jar |
| One embed server fails | Third-party host is unavailable, blocked, or its token expired | Continue trying the remaining server tokens |
| Every embed fails on one network | ISP, DNS, filtering, geography, or third-party host availability | Try the canonical browser fallback; do not claim it is an AniPlay catalog bug without testing the endpoint stages |
| Embed plays but AniPlay has no subtitles | Subtitles are internal to the iframe | Native subtitle support requires a provider-specific extractor |
| Download is unavailable | Only iframe links resolved | Do not pass embed URLs directly to FFmpeg |
| Watch Together is unavailable | Only uncontrollable iframe links resolved | Native media or a documented bidirectional player API is required |
| Mapper is down | Supplemental mapping service failed | Base anikoto.cz servers should still be attempted |
Logging safely¶
Useful diagnostics:
- workflow stage: search, show page, episode list, episode page, server list, mapper, or server exchange;
- response status and content type;
- final scheme and hostname after redirects;
- whether
#watch-mainidentity was found; - number of parsed episodes;
- advertised SUB/DUB availability;
- number of SUB, H-SUB, and DUB servers;
- visible server labels and sanitized server IDs;
- number of successfully validated embed URLs.
Do not log:
- complete
data-idsordata-link-idvalues; - resolved third-party URLs with signed paths or query strings;
- cookies;
- complete show/episode HTML in normal logs;
- full AJAX response bodies;
- mapper tokens;
- Watch Together room capability tokens.
For debug output, reduce resolved URLs to their scheme and hostname wherever possible.
Tests in AniPlay¶
electron/providers/anikoto2.test.ts covers:
- search HTML parsing and prefixed ID creation;
- encoded-ID validation;
- canonical show identity extraction;
- episode parsing and numeric ordering;
- SUB, H-SUB, and DUB server grouping;
- mapper response normalization;
- server-response URL validation;
- browser fallback construction.
Live tests should remain opt-in. Search markup, tokens, server labels, embed hosts, and availability are too volatile for deterministic CI.
Recommended additional tests when extending the provider:
- cached request expiry and failed-promise eviction;
- 403/429 behavior;
- session/cookie isolation;
- malformed JSON envelopes;
- missing
resultfields; - unsafe URL schemes and credentials;
- loopback, literal-IP, and lookalike-host rejection;
- partial server failure;
- mapper failure with working base servers;
- fractional episode selection;
- strict missing-DUB behavior.
Adding native extraction for one embed provider¶
Treat every embed host as a new provider-specific layer.
Discovery¶
- Identify whether the embed provider documents a source API.
- Record redirects, cookies, referer, origin, and player initialization.
- Determine whether source URLs are temporary, IP-bound, or signed.
- Verify whether subtitles are soft tracks or burned into the video.
Extraction¶
- Keep the extractor in its own module.
- Accept only known embed origins; reject lookalikes.
- Parse source responses defensively.
- Validate every returned media and subtitle URL.
- Preserve the iframe as fallback.
- Never forward Anikoto cookies to the media provider.
Media headers¶
- Determine exact headers required by manifests, variants, segments, keys, maps, and subtitles.
- Scope headers to exact domains and real subdomains.
- Revalidate redirected hosts.
- Never install a global Electron referer, origin, or CORS override.
Playback and quality¶
- Distinguish HLS from direct media.
- Expand HLS master variants into stable quality labels.
- Deduplicate equivalent sources.
- Retain subtitle language, label, and default status.
- Test seeking and source expiry.
Downloads¶
- Verify FFmpeg can read manifests, segments, keys, and subtitles with the scoped headers.
- Keep partial-file cleanup and cancellation.
- Do not mark a source downloadable until a native URL is proven.
Watch Together¶
- Share only stable provider/show/episode/language identity.
- Resolve media independently on every participant.
- Do not store temporary URLs, cookies, or headers in the room.
- Mark iframe-only clients unready.
- Test two clients on different networks.
Reusing this workflow in another project¶
Keep the layers separate:
Anikoto 2 search parser
!=
show and episode discovery
!=
server-matrix parser
!=
optional NekoStream mapper
!=
opaque token exchange
!=
third-party embed playback
!=
future native media extraction
A practical integration checklist:
- Use a persistent cookie jar for the complete provider flow.
- Preserve canonical slugs instead of guessing them from titles.
- Keep internal IDs prefixed by provider.
- Treat all AJAX HTML fragments as untrusted input.
- Enforce strict SUB/DUB selection.
- Classify H-SUB using both markup attributes and visible labels.
- Treat provider tokens as opaque and short-lived.
- Make mapper sources optional.
- Validate the final embed URL before opening it.
- Isolate every native embed extractor.
- Never confuse an iframe URL with a downloadable media URL.
- Keep rate-limit, parsing, mapping, and embed failures distinguishable.
Maintenance warning¶
All endpoints in this document are observed website implementation details. The request parameters, HTML classes, data attributes, token format, mapper contract, server labels, and embed hosts may change without notice.
The most important maintenance rule is to preserve the boundaries:
Do not collapse those values into one assumed identifier. That separation makes provider changes diagnosable and prevents an embed-host change from corrupting catalog or history identity.