34 KiB
Miruro API research
Observed 2026-10-12, scoped to https://barelystarted.miruro.tv. These are reverse-engineering notes for a possible in-repository wrapper, not an official API specification. Tubular does not yet implement Miruro support.
Evidence labels:
- Verified: an actual HTTP response obtained through the research JavaScript
fetchtransport, 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 limitation: research requests returned JSON, but a local curl request to the same source-validation URL returned 403 text/html, a Cloudflare block page. Native-wrapper access is therefore unproven. Inspect HTTP status and Content-Type before decoding; stop on protection pages rather than treating them as empty results or adding bypass machinery. Successful research requests do not establish cross-client accessibility.
Endpoint map
All paths below are relative to the scoped base URL. Each row was live-verified unless explicitly labeled Frontend. Route-data responses require the SvelteKit decoder, even when Content-Type is application/json.
| Purpose | GET path and parameters | Response |
|---|---|---|
| Autocomplete | /api/search/browse?q=<query>&limit=8&type=ANIME |
Plain JSON {data, next_cursor, has_more} |
| Full search / browse | /search/__data.json?q=<query>&cursor=<returned cursor> |
Route data: {items, entityItems, nextCursor, hasMore, filters, …} |
| Detail / episode metadata | /info/<id>/<slug>/__data.json |
Streamed route data: detail, promised episodes and related metadata |
| Watch metadata + initial sources | /watch/<id>/<slug>/__data.json?ep=<number> |
Streamed route data: media, promised episodesData and sourcesData |
| Fresh episode sources | /api/sources?id=<id>&n=<number> |
Plain JSON {episodeNumber, tracks, config, providerOrder} |
| Home | /__data.json |
Streamed route data: hero, promised rails and sideLists |
| Trending | /trending/__data.json |
Route data: {items, requestedPage, year, season, years, seasons} |
| Schedule page | /schedule/__data.json |
Route data: {entries, weekStart, weekOffset, now, track} |
| Schedule events | /api/schedule?startAt=<Unix seconds>&track=all |
Plain JSON event array |
| Genre/tag references | /api/discover/refs |
Plain JSON {genres, tags} |
| Discover page | /discover/__data.json |
Route data: {lens, lensOffset, spotlights, picks, lensCount} |
| Personalized discover feed | /discover/feed |
Plain JSON {spotlights, picks}; empty arrays verified without seeds |
Sources: route map, search/layout, detail, watch, home, trending, schedule, discover, and the live URLs in the verification ledger. No plain JSON detail/episodes endpoint was identified in those bundles; none was guessed or probed. This is not proof that none exists. Other Miruro domains and third-party /api/secure/pipe wrappers are outside this contract.
IDs and the search-to-stream flow
Use Miruro's opaque catalog ID, not an external numeric ID:
Miruro id: OERe_a0YS4h-DlsBDOeXwX7HOO6316tw
AniList: 195516
MAL: 61987
Slug: the-apothecary-diaries-season-3
Verified search records expose external_ids as a map of string arrays, for example {"anilist":["195516"],"mal":["61987"]}. Unmapped provider keys may be absent. Watch data separately exposes numeric anilistId; media.id remains the opaque Miruro ID. Numeric-ID reverse lookup and route aliases are unknown.
The minimum evidenced flow is:
- Search and retain the opaque
items[].id/data[].id, title, and artwork. - Fetch detail route data and resolve its
episodeschunk, or fetch watch route data and resolveepisodesData. - Select an actual returned episode number. Detail
episode_number:1mapped toabsolute_number:49; source query uses 1, not 49 or an array index. - Fetch
/api/sources?id=<opaque id>&n=<selected number>immediately before playback. - Choose a returned track/provider/server with direct streams; preserve that server's headers and runtime configuration. Iframe-only servers are not native media URLs.
Frontend: link helpers generate /info/<id>/<slug> and /watch/<id>/<slug>?ep=<number>. The canonical title is the shorter trimmed English/Romaji title when both exist, otherwise the available English/Romaji, then native title/ID. Slugging applies Unicode NFD, removes combining accents, lowercases, removes non-ASCII-alphanumeric/non-whitespace/non-hyphen characters, trims, hyphenates whitespace, and collapses repeated hyphens. Display-language preference is separate. Missing/wrong-slug behavior is untested; preserve the deployed helper's convention rather than assuming an arbitrary slug works.
Search and discovery contracts
Plain autocomplete
Verified: this request returned eight summary records, next_cursor and has_more:true. Frontend: layout trims q, sends literal limit=8&type=ANIME, debounces input 300 ms and aborts older requests. Empty input clears local suggestions without a request.
An abbreviated observed result, with additional fields omitted:
{
"data": [{
"id": "OERe_a0YS4h-DlsBDOeXwX7HOO6316tw",
"external_ids": {"anilist": ["195516"], "mal": ["61987"]},
"title": {
"romaji": "Kusuriya no Hitorigoto 3rd Season",
"english": "The Apothecary Diaries Season 3",
"native": "薬屋のひとりごと 第3期"
},
"format": "TV",
"status": "RELEASING",
"episode_count": 12,
"episode_counts": {"raw": 2, "sub": 2, "dub": null},
"episode_duration_minutes": 24,
"season": "FALL",
"season_year": 2026,
"average_score": 85
}],
"next_cursor": "<redacted returned cursor>",
"has_more": true
}
Summary fields also observed: description, cover_url, cover_color, banner_url, background_url, logo_url, source, country_of_origin, popularity, is_adult, next_airing:{episode_number,air_at}, show_cluster_id, show_cluster_position. Across records, English title, counts, duration, season/year, score, colors/artwork, next airing, and cluster fields could be null. Null is not zero or an empty string.
Pagination trap: sending this response's next_cursor as cursor on the same otherwise-valid autocomplete request returned 400, detail:"Unsupported browse request.". /api/search/browse?limit=2&type=ANIME also returned 400, but that probe changed both limit and omission of q, so it does not isolate the cause. Do not promise plain-API pagination or infer supported limits from these probes.
Full search, filters, and pagination
Verified: decoded /search/__data.json?q=apothecary yielded:
{
mode: "anime", items: MediaSummary[], entityItems: [],
nextCursor: null, hasMore: false, limit: 24, cursor: "",
entityPage: 1, filters: { ...string-valued fields... },
context: null, error: null
}
It returned eleven summaries. An unmatched query returned items:[], entityItems:[], nextCursor:null, hasMore:false, error:null, HTTP 200. Empty results are distinct from errors.
Unfiltered /search/__data.json returned 24 summaries and a non-null nextCursor; a subsequent request with that token as cursor returned another 24 summaries with a different first ID, echoed cursor, and hasMore:true. Retain the returned token opaquely and URL encode it. Token lifetime, binding to filters/cookies and invalid-token handling are unknown. Do not reuse the autocomplete cursor.
Frontend: search page and route dependency metadata expose these page query keys:
| Keys | Representation / behavior |
|---|---|
mode |
anime, studio, staff, character |
q |
Trimmed query; entity input has a client-only 200-character limit |
cursor |
Anime pagination; reset when filters change |
page |
Numeric entity pagination, not anime pagination |
studios, staff, characters |
Numeric entity-ID context filters |
genres, format, status, tracks, season, source, country |
Single or comma-separated selected values; genre names, not reference IDs |
adult |
Empty/unset is labeled Non-adult; true is labeled Adult |
yearFrom, yearTo, minScore, maxScore |
String-valued range bounds; score slider 0–100 |
sort |
One frontend option string |
Frontend option values (source), not guaranteed server enums:
format:TV,TV_SHORT,MOVIE,OVA,ONA,SPECIAL,MUSIC.status:RELEASING,FINISHED,NOT_YET_RELEASED,HIATUS,CANCELLED.tracks:raw(Sub / Raw),dub(Dub),sub(Scheduled Subs). These discovery labels are not the playback API'sssub/subclassification.season:WINTER,SPRING,SUMMER,FALL;country:JP,KR,CN,TW.source:ORIGINAL,MANGA,LIGHT_NOVEL,WEB_NOVEL,NOVEL,VISUAL_NOVEL,VIDEO_GAME,GAME,DOUJINSHI,ANIME,COMIC,LIVE_ACTION,MULTIMEDIA_PROJECT,PICTURE_BOOK,OTHER.sort: empty (Most Popular),-score,-season_year,season_year,title,-title,-episode_count,episode_count,-duration,duration,-started_on,-ended_on,-updated_at.
The grid stores hestia-page-size in a cookie, computing 24 or 12/15/18/21 by layout. No full-search query limit was observed; 24 is a sampled response, not a guaranteed default. Entity responses/filter semantics, cookie acceptance/ranges and server validation were not exercised.
Other discovery surfaces
Verified, with frontend parameter behavior noted separately:
- Home: decoded
{hero, now, rails:Promise, sideLists:Promise}. Hero records are camelCase (externalIds,coverUrl,backdropUrl,episodeCounts, etc.), unlike search summaries.railsresolvesraw/dublatest entries withanimeId,episode,track, Unix-secondairingAt;sideListshasairing/upcomingsummary arrays. UseanimeId, not an event ID, for sources. - Trending:
{items, requestedPage, year, season, years, seasons}. Frontendyear/seasonchange the loaded collection;pageslices it locally into 18-item pages. It is not evidence of server-side cursor pagination. - Schedule page:
{entries, weekStart, weekOffset, now, track}; dependency metadata readsweekandtrack. Plain schedule returns[{id,airingAt,episode,track,media}];startAtandairingAtare Unix seconds. Usemedia.id, not eventid, for sources. Frontend fetchestrack=all, shifts weeks by7*86400, and filters all/sub/dub locally. API sub/dub variants are untested; returned entries includedtrack:"raw". - References:
/api/discover/refsreturnsgenres:[{id,name}]andtags:[{id,name,category,parent_id,parent_name}]; parent fields may be null. Search uses genre names. A tag filter query was not observed in full-search controls. - Discover page:
{lens:{id,label,tagline,recipe}, lensOffset, spotlights, picks, lensCount}. Spotlights are camelCase; picks use snake_case summaries. Dependency metadata readsseason,year,cursor,d; Frontend refresh sets a randomd. These are not a proven paginated API. - Personalized feed: unseeded
/discover/feedreturned empty arrays. Frontend sendsseeds=<comma-separated numeric list IDs>&exclude=<comma-separated IDs>&provider=<list provider>, using up to eight seeds and fallback provideranilist. These IDs belong to the external list service, not Miruro's opaque catalog. The UI gates personalization on sign-in; seeded-request authentication and schema are unknown.
Detail, episodes, and watch data
Verified: detail route initially decodes to:
{
detail: MediaDetail, id, currentSlug, tvdbBackgrounds,
characters: Promise, relations: Promise, recommendations: Promise,
artworks: Promise, show: Promise, episodes: Promise
}
detail extends summary metadata with alternate_titles:[{value,kind,language}], started_on, ended_on, broadcast, release_schedules, mal_score, mal_members, aired_season, trailer_youtube_id, genres, tags:[{name,rank,category,is_spoiler}], studios:[{id,name,is_main,is_animation_studio}], franchise, updated_at. Dates and some nested identifiers/counts can be null. Broadcast/release schedules describe airing metadata, not stream provider URLs or playback availability.
The sample title's resolved episodes contained two records despite a planned episode_count:12. Abbreviated episode with all observed keys:
{
"episode_number": 1, "absolute_number": 49, "aired_season_number": 3,
"kind": "regular", "canon_type": null, "title": "Locusts",
"synopsis": "A locust plague is looming, and mysteriously, a book on insects belonging to the Shi clan appears.",
"thumbnail_url": "https://image.tmdb.org/t/p/original/v2k4Nj11mnVwyUhqktOmYr3E4yV.jpg",
"aired_on": "2026-10-02", "duration_seconds": 1500, "finale_type": null,
"skip_times": [
{"kind":"op","start_seconds":63.313,"end_seconds":153.237},
{"kind":"ed","start_seconds":1335.252,"end_seconds":1425.384}
]
}
Do not synthesize playable episodes from planned counts. These records do not carry per-track availability flags; request actual sources for the selected episode. Null dub count does not conclusively prove no playable dub.
Watch route initially has {media, episodesData:Promise, sourcesData:Promise, anilistId, externalIds, episodeNumber, tvdbBackgrounds}. media is adapted camelCase: episodeCounts, duration (minutes), seasonYear, startDate, coverImage, nextAiringEpisode, etc.; it is not interchangeable with detail.
Resolved watch episodesData has:
{
episodes: [{id:"1",number:1,title,image,airDate,duration:1500,description,filler:false}, ...],
currentEpisode: <episode 1>, prevEpisode: null, nextEpisode: <episode 2>,
aniskip: [
{episode:1,type:"op",start:63.313,end:153.237},
{episode:1,type:"ed",start:1335.252,end:1425.384}
]
}
Watch episodes[].id is the number rendered as a string, not a provider episode ID. aniskip times are seconds; they differ in naming from detail skip_times. /api/sources does not carry those fields. Frontend: watch converts op/mixed-op to intro and ed/mixed-ed to outro, filters wrong episodes/invalid intervals and optional provider/vote/duration constraints, then recomputes skips using player-reported duration. No separate skip endpoint was found. Missing/invalid episode behavior and last-episode boundaries are untested.
Source-resolution and playback contract
Plain source response
Verified: fresh sources returned the following observed structure. Fields seen only as null have unknown non-null types; this is not an exhaustive guaranteed schema.
Sources { episodeNumber:number, tracks:Track[], config:{provider:ProviderConfig}, providerOrder:string[] }
Track { track:string, providers:Provider[] }
Provider { provider:string, servers:Server[], subtitles:Subtitle[], thumbnails:null|{vtt:string,sprite:null}, downloads:Download[] }
Server { server:string, headers:null|{header:string}, embed:null|{url:string}, streams:Stream[] }
Stream { url:string, format:string, quality:null, resolution:null, codec:null, embed:null }
Subtitle { language:string, label:string, file:string, format:string, default:boolean }
Download { url:string, quality:null, size:null, fansub:null }
- Sample tracks were
ssub(soft subtitles) andsub(hard-subtitled, UIh-sub); Frontend also supportsdub. No separateaudioproperty was observed. Discoveryraw/sub/dubcounts are a different vocabulary. - Sample direct streams used
format:"hls". Frontend recognizes HLS strings/MIME types, DASH/MPD, MP4, WebM and Ogg, and defaults a missing format to HLS. Those are consumer inputs, not verified server formats. - Quality/resolution/codec were null, yet a fetched master advertised
RESOLUTION=1920x1080; do not equate null metadata with absent HLS renditions. - An iframe-only server (
bun) hadstreams:[],headers:null,embed:{url}. An embed page is not a native stream. - Subtitle language strings were provider-specific (
en/eng,sp,po); distinct Spanish labels sharedlanguage:"sp". Preserve labels, not only language codes. VTT was verified; SRT support is frontend-only. - Subtitle/thumbnail URLs can end in
.jpegwhile returningtext/vtt. Classify by format/content, not extension. Relative thumbnail sprite references must be resolved against the VTT URL. - Download URLs can be download pages; their being raw media files is unproven.
- Source URLs may contain tokens. Fetch fresh before playing; do not persist tokens as catalog identity or log full URLs. Expiry and IP binding are unknown.
A sanitized projection (URL values deliberately redacted):
{
"episodeNumber": 1,
"tracks": [{"track":"ssub","providers":[{
"provider":"sun",
"servers":[{
"server":"icarus-2-1",
"headers":{"Origin":"https://ultracloud.cc","Referer":"https://ultracloud.cc/"},
"embed":null,
"streams":[{"url":"<fresh HLS URL>","format":"hls","quality":null,"resolution":null,"codec":null,"embed":null}]
}],
"subtitles":[{"language":"en","label":"English","file":"<fresh VTT URL>","format":"vtt","default":true}],
"thumbnails":{"vtt":"<fresh thumbnail VTT URL>","sprite":null}
}]}]
}
This projects one server/provider and omits config, other tracks/providers and downloads; it is not a complete response. Preserve the actual returned header strings without normalization.
Runtime config and selection
Verified: response config entries contain capabilities:{sub,ssub,download,skip_times,thumbnails}, visible, player (native/iframe observed), parent, proxy, variantOrder, cors, fallback, and optional hls / downloadWorker fields. proxy was false or {rotate:boolean,segments:boolean}; hls contained segments:{enabled,origin} and query:{enabled,params}. Non-null fallback shape is unknown.
Observed providerOrder: sun, bee, pewe, hop, ally, kiwi, moo, bonk, knob, nun, bun, cog, twin, telli. Config includes providers absent from the selected episode. Treat names/order as runtime data. Capability flags and cors were false even where streams/subtitles/downloads/thumbnails were returned; they are not verified availability gates.
Frontend (watch client):
- Use saved sub/dub and provider preferences. For sub, consult each provider's
variantOrderforsub/ssub, then append missing variants; iterateproviderOrderand retain only actual provider/track combinations. - Prefer exact
provider:track, then same provider, then first available. If a requested language has no entries, try the opposite language. This is existing UI behavior, not a recommendation to silently change a native user's language. - Flatten selected provider servers' nonempty streams in returned order; start at the first and advance on failure. Retain iframe embeds only for
player:"iframe". - Browser stream projection extracts Referer case-insensitively, but raw API also returns Origin. A native wrapper should retain the entire header map.
Direct resources, proxies, and browser player
Verified: using fresh Sun stream/subtitle/thumbnail URLs and the server's Origin/Referer headers, research GETs obtained an HLS master (200 application/vnd.apple.mpegurl) and two VTT bodies (200 text/vtt). No nested rendition playlist, media segment, key, sprite, download page or player was exercised. Whether either header is mandatory was not tested. This proves resource retrieval, not full playback.
Frontend: deployment HTML environment identifies https://strm.cx, proxy bases https://s1.keeply.top/ and https://s2.keeply.top/, and a public proxy obfuscation key. Watch source shows:
- Sun uses direct streams (
proxy:falsein the sample). Bee/Hop use{rotate:false,segments:true}; a bare direct URL is not proven equivalent for these providers. - Browser proxy paths encode URL/Referer and use
/pl.m3u8,/sub.vtt,/seg.jpg. This is a private deployment convention, not/api/secure/pipe. No replay of that obfuscation protocol was needed for direct-resource verification. - Provider
hlsoverrides can rewrite segment/init-resource hosts and append query parameters to playlists, including nested playlists. Browser code resolves relative URLs/URI="…"attributes, rewrites playlists intoblob:URLs, drops failed variants, and bounds recursion, size and time. Those blob URLs are browser-local, not reusable native source URLs. - The browser loads strm.cx/client.js, renders
<strmcx-embed>, and callssetSessionwith local player state/source/subtitle/skip data overstrmcx/v1messaging. This was not required for the tested source or direct-resource GETs. It is not evidence of an authenticated API session prerequisite. - strm client contains a best-effort
POST /v1/embeds; it was source-inspected only, not invoked. Iframe providers and unrelated comment/telemetry endpoints were not exercised.
For a future native player: preserve fresh query tokens, per-server headers and relevant provider config; correctly resolve nested/relative HLS URLs. Do not apply one provider's headers indiscriminately to another provider's subtitles/thumbnails. Terebi currently receives only --new-instance --url … --progress-file PATH in Tubular (existing usage); custom headers, external subtitles, HLS overrides, native seeking and full Miruro playback remain unverified.
SvelteKit route data
Sources: deployed fetch/devalue decoder, route map, and live detail/watch/home responses.
Append /__data.json to the page path and retain query parameters. The browser transport can add x-sveltekit-invalidated=<bit string> and x-sveltekit-trailing-slash=1; neither was necessary in the research requests.
An illustrative, minimal envelope:
{"type":"data","nodes":[{"type":"data","data":[{"someKey":1},"someValue"],"uses":{}},null]}
Actual responses had layout at node 0, route at node 1. Each node's data is its own devalue flattened table, with root at index 0. Object-property and ordinary-array numeric values are references into that table, not literal payload numbers. Null is a table value; undefined uses sentinel -1. Decoder also handles array holes (-2), NaN/infinities/negative zero (-3…-6), sparse arrays (-7) and tagged Date/Set/Map/BigInt/other types. A generic recursive integer dereferencer is not a complete decoder. Keep layout configuration separate from route models.
Promised fields are tagged ['Promise', <table index referencing promise ID>]. Streamed text/sveltekit-data is newline-delimited JSON, not one JSON object:
{"type":"data","nodes":[...]}
{"type":"chunk","id":<promise ID>,"data":<independent devalue table>}
Resolve by decoded promise ID; each chunk has a new table. Arrival order is not field order: sample detail chunks arrived 2,1,6,3,4,5. IDs are response-local, not constants. Consume the full body or track promises incrementally. Frontend also decodes error chunks to reject a promise; that error path was not live-exercised. Top-level redirect envelopes require separate handling.
Verified: /info/invalid/invalid/__data.json returned HTTP 200 with {"type":"redirect","location":"/"}. Status 200 therefore does not prove a successful detail response. Empty search arrays, redirect envelopes, API problem responses and transport failures must remain distinct.
Errors and caching
Verified API problem shape:
{"type":"about:blank","title":"Bad Request","status":400,"detail":"Unsupported request parameters."}
Content-Type: application/problem+json, Cache-Control: no-store.
/api/sourceswithout parameters: 400,detail:"Anime and episode are required.".- Known ID with
n=0orn=not-a-number: 400,detail:"Unsupported request parameters.". id=invalid&n=1: same 400/detail; this does not establish behavior for a syntactically valid nonexistent ID.- Unsupported tested browse requests: 400,
detail:"Unsupported browse request.". - Local curl of known ID with
n=0: Cloudflare 403 HTML, before the JSON validation behavior seen in research transport. The cause/client fingerprint/IP distinction is unknown.
Source success and source errors had no Access-Control-Allow-Origin header. The direct Sun master/subtitle responses had ACAO https://ultracloud.cc. Neither observation guarantees general browser cross-origin access. Server rate limits, stable error taxonomy, authentication guarantees, token expiry and protection behavior are unknown; no rate/protection stress tests occurred.
Observed Cache-Control values:
| Surface | Cache-Control |
|---|---|
| Plain browse success | public, max-age=300, s-maxage=900, stale-while-revalidate=3600, stale-if-error=86400 |
| Plain schedule | public, max-age=0, s-maxage=60, stale-while-revalidate=60 |
| Discover references | public, max-age=0, s-maxage=300, stale-while-revalidate=60 |
| Source success/errors and normal route data | no-store |
| Missing-detail redirect; discover feed | private, no-store |
Respect current response headers; these are sampled deployment values, not constants. Metadata cacheability does not make stream URLs durable.
Verification ledger
All research data GETs below used fetch with 20–25-second timeouts, no supplied auth/cookie/Origin/Referer unless stated. JS bundles used 15–20-second bounds. These are research-transport results, not successful local curl/native playback claims. Stream tokens and pagination cursors are intentionally not recorded.
| Request | Status / Content-Type | Observed result |
|---|---|---|
| Autocomplete | 200 / application/json | Eight summaries; next_cursor; has_more |
| Browse without q, changed limit 2 | 400 / application/problem+json | Unsupported browse request |
Valid browse + own returned cursor as cursor |
400 / application/problem+json | Unsupported browse request |
| Full query search | 200 / application/json | Eleven summaries; no nextCursor |
| Full browse + its nextCursor request | Both 200 / application/json | Two 24-item pages; different first IDs |
| Unmatched search | 200 / application/json | Empty items; error:null |
| Detail | 200 / text/sveltekit-data | Initial detail + six promised chunks; two episodes |
| Invalid detail | 200 / application/json | Redirect envelope to / |
| Watch ep=1 | 200 / text/sveltekit-data | Initial media + episodesData/sourcesData chunks |
| Source ep=1 | 200 / application/json | ssub/sub, direct + iframe providers, runtime config |
| Source missing parameters / n=0 / nonnumeric n / invalid ID | Each 400 / application/problem+json | Validation details above |
| Home | 200 / text/sveltekit-data | Hero + rail/side-list chunks |
| Trending | 200 / application/json | 97 sampled items; year/season choices |
| Schedule page / plain schedule | Both 200 / application/json | 240 sampled entries |
| References | 200 / application/json | 19 genres, 1,742 tags in sample |
| Discover | 200 / application/json | Lens, 12 spotlights, 12 picks |
| Unseeded feed | 200 / application/json | Empty spotlights/picks |
| Fresh ssub/Sun first-server master, with server Origin/Referer | 200 / application/vnd.apple.mpegurl | #EXTM3U; one 1920x1080 variant; no redirect |
| Same provider default subtitle / thumbnail VTT, same headers | Both 200 / text/vtt | WEBVTT cues; thumbnail sprite coordinates; no redirect |
| strm client | 200 / text/javascript | Source inspected, not executed |
| Local shell curl, known source ID with n=0 | 403 / text/html | Cloudflare block page |
One worker's homepage fetch without a trailing slash returned nginx 404, while the trailing-slash HTML read succeeded. This unresolved transport/URL discrepancy is not evidence of an API rule. No browser testing, playback, media-segment downloads, account mutations, protection bypass, or application tests were performed for this documentation-only change.
Bounded reproduction requests
These curl recipes show request syntax; successful execution with local curl is not established. One local curl probe was blocked. Check status/Content-Type and stop on a block page. Example IDs/dates describe the observed catalog snapshot; titles and availability can change.
BASE='https://barelystarted.miruro.tv'
ID='OERe_a0YS4h-DlsBDOeXwX7HOO6316tw'
SLUG='the-apothecary-diaries-season-3'
curl --silent --show-error --max-time 25 -i --get "$BASE/api/search/browse" \
--data-urlencode 'q=apothecary' --data-urlencode 'limit=8' --data-urlencode 'type=ANIME'
curl --silent --show-error --max-time 25 -i --get "$BASE/search/__data.json" \
--data-urlencode 'q=apothecary'
curl --silent --show-error --max-time 25 -i "$BASE/info/$ID/$SLUG/__data.json"
curl --silent --show-error --max-time 25 -i --get "$BASE/watch/$ID/$SLUG/__data.json" \
--data-urlencode 'ep=1'
curl --silent --show-error --max-time 25 -i --get "$BASE/api/sources" \
--data-urlencode "id=$ID" --data-urlencode 'n=1'
For full-search pagination, decode nextCursor from /search/__data.json, then request the same route with --data-urlencode "cursor=$CURSOR", retaining filters/cookie context. For current schedule, decode weekStart from /schedule/__data.json, then request /api/schedule with startAt=<that value>&track=all. To inspect media resources, select fresh stream/subtitle URLs from the source response and use that server's actual headers, a bounded GET and in-memory results; do not save tokenized URLs to these notes.
Equivalent plain-JSON research request (runtime/network portability is unproven):
const response = await fetch(
'https://barelystarted.miruro.tv/api/search/browse?q=apothecary&limit=8&type=ANIME',
{ signal: AbortSignal.timeout(20_000) }
);
console.log(response.status, response.headers.get('content-type'));
console.log(await response.text());
Open questions before implementation
- Can the real native HTTP client access this deployment without being blocked? Local curl failed; no workaround has been implemented or tested.
- Is there a stable plain metadata/episode API, or must the wrapper own a SvelteKit/devalue decoder tied to this deployment?
- What are server-side filter/enum constraints, multi-value semantics, cursor lifetime, page-size-cookie limits, numeric-ID lookups and slug behavior?
- What happens for unavailable episodes, empty tracks, provider failures, bad cursors, rejected promise chunks and syntactically valid missing IDs?
- Which headers/config transforms are required per provider, and can Terebi propagate them to HLS playlists/segments/keys and external subtitles?
- Are direct streams usable end-to-end, including rendition/segment access and seeking? Only master/subtitle/thumbnail retrieval was verified.
- What are seeded discovery authentication requirements, rate limits, token expiry/IP binding and content-use permissions? Reachable internal endpoints do not imply a supported third-party integration or permission to redistribute content.
Hashed JavaScript URLs below identify the inspected deployment, not stable versions. For later research, re-read the homepage's entry-script imports and route map to locate the current bundles; do not hardcode these hashes in the wrapper.