fix: support current official Miruro mirrors
This commit is contained in:
+6
-2
@@ -2,13 +2,15 @@
|
||||
|
||||
Observed **2026-10-12**, scoped to **https://barelystarted.miruro.tv**; ownership split updated **2026-10-22**. These are reverse-engineering notes, not an official API specification. Tubular now owns catalog/detail retrieval and stable identity; Terebi resolves episode URLs. Source/CDN investigations below are historical evidence, not the current launch path.
|
||||
|
||||
**Mirror update (2026-10-25):** the [official directory](https://www.miruro.com/#domains) links Miruro 2.0 at `https://miruro.cx`, `https://miruro.pw`, `https://miruro.ws`, `https://miruro.to`, `https://miruro.tv`, and `https://miruro.bz`; `www.` hosts are labeled Legacy. Tubular now uses these six explicit origins for catalog/detail requests. Local native `ureq` and curl probes received **403 `text/html`** from all six autocomplete endpoints; curl returned Cloudflare block pages. Thus live schema/ID compatibility and successful browsing on these mirrors remain unverified. The original `cargo run --locked --example miruro_probe` now exercises all six and reports `Miruro request failed on 6/6 mirrors`, rather than stopping at the obsolete browsing origin. Endpoint/schema evidence below remains the historical snapshot, not a claim of current mirror access.
|
||||
|
||||
Evidence labels:
|
||||
|
||||
- **Verified:** an actual HTTP response obtained through the research JavaScript `fetch` transport, or the explicitly identified local Rust probe below, with bounded timeouts and no supplied authentication, cookies, or protection bypass.
|
||||
- **Frontend:** behavior read from deployed first-party JavaScript; not necessarily a server-side guarantee.
|
||||
- **Unknown:** not established by those sources or requests.
|
||||
|
||||
**Transport distinction:** research requests returned JSON, while a local `curl` source-validation request returned **403 `text/html`**, a Cloudflare block page. A subsequent local Rust `ureq` probe passed the search → detail/episodes → sources → HLS master/subtitle path without bypass machinery. Native access is proven for that client and sample, not every transport, network, provider or future request. Inspect HTTP status and Content-Type before decoding; stop on protection pages rather than treating them as empty results.
|
||||
**Transport distinction (historical):** research requests returned JSON, while a local `curl` source-validation request returned **403 `text/html`**, a Cloudflare block page. A subsequent local Rust `ureq` probe passed the search → detail/episodes → sources → HLS master/subtitle path without bypass machinery. Native access was proven for that client and sample, not every transport, network, provider or future request. Inspect HTTP status and Content-Type before decoding; never treat protection pages as empty results. The current client can try the next explicitly listed official mirror, but cannot restore access when every mirror blocks it.
|
||||
|
||||
## Endpoint map
|
||||
|
||||
@@ -395,7 +397,7 @@ Current library interface:
|
||||
|
||||
| Call | Result / contract |
|
||||
| --- | --- |
|
||||
| `Client::default()` | Reusable blocking HTTP agent; 25-second global request timeout; no automatic redirects |
|
||||
| `Client::default()` | Reusable blocking HTTP agent; six official 2.0 mirrors; sticky successful origin; 25-second total request budget, at most five seconds per attempt; no automatic redirects |
|
||||
| `client.search(query)` | `io::Result<Vec<serde_json::Value>>`; trimmed, nonempty autocomplete query; no cursor pagination |
|
||||
| `client.detail(id, slug)` | `io::Result<Detail>`; decoded `media` JSON and `episodes: Vec<Value>`; validates returned media ID |
|
||||
| `Identity::new(id, episode)`, `Identity::from_key(key)` | Validated catalog namespace/episode identity; `key()` is `miruro:<opaque ID>:<episode>` |
|
||||
@@ -403,6 +405,8 @@ Current library interface:
|
||||
|
||||
Raw catalog methods preserve JSON metadata without a second deployment-specific schema. IDs/slugs are validated path segments; HTTP/HTML/Content-Type failures remain errors, not empty results. Successful bodies are limited to 2 MiB. Source selection, resource headers and subtitles belong to Terebi.
|
||||
|
||||
The client starts at `miruro.cx` and remembers the last origin whose response passed HTTP, Content-Type and bounded-body checks. On transport/body failures, unexpected Content-Type, HTTP 301–308, 403, 404, 408 or 5xx, it tries the remaining origins once in directory order. Other HTTP errors, including 400, 401 and 429, stop immediately; rotating mirrors must not circumvent rate limits. Exhaustion reports the attempted/total mirror count and last origin/error. JSON/route decoding and detail-ID validation remain mandatory and do not trigger additional fallback. The directory is not scraped at runtime, and requests are not randomized. Playback URLs deliberately retain `barelystarted.miruro.tv`: current Terebi admits and resolves only that hostname, so playback failover requires a separate player change.
|
||||
|
||||
Its private route decoder resolves only the needed detail value and episode promise, using separate value tables and promise IDs. Unrelated sidebar promises are not returned. It supports the JSON-shaped tables verified on this path, **not arbitrary devalue**: tagged special types and negative sentinels are explicit errors, never silently coerced to null. Out-of-range references, rejected/missing/duplicate episode chunks, cycles/excessive depth and decoded-size amplification are rejected. Depth is bounded to 64 and expanded values share a 2 MiB size budget. These are wrapper safety limits, not Miruro server limits.
|
||||
|
||||
The retained `cargo run --locked --example miruro_probe` smoke now covers search → detail/episodes only, not sources or playback.
|
||||
|
||||
Reference in New Issue
Block a user