SDK API Reference (updated for miru_core 5ff71a8)
Updated for
miru_corecommit5ff71a8(2026-08-24) withpkg/extension/golang/sdk/sdk.goandruntime/*. Covers: model types,Fetch, filter builders, cache, settings, cookies, content type constants, and fullMirror()/Watch()/Search()examples.
Extension Template: Quick start by cloning the official template:
Terminal window git clone https://github.com/appdevelpo/miru_extension_template.git
Note (V1 vs V2): The official template (
miru_extension_template/extension.go) uses V1-stylefilter string. For V2 (@apiVersion 2or current), usefilter sdk.Filter.
Overview & Import
Section titled “Overview & Import”SDK Import
Section titled “SDK Import”import sdk "github.com/miru-project/miru-core/pkg/extension/golang/sdk"sdk is a curated re-export of the internal runtime package (avoiding the name clash with Go’s standard runtime). All types are aliases of host runtime types for clean Scriggo/host boundary crossing.
Model Types
Section titled “Model Types”| Type | Fields | Usage |
|---|---|---|
sdk.ExtensionListItem |
Title / URL / Cover / Update / Image / Type / Headers |
Search() / Latest() result row |
sdk.ExtensionDetail |
Title / URL / Cover / Image / Type / Desc / Description / Chapters []ExtensionEpisodeGroup / Headers |
Detail() full page (Desc preferred; Description ignored if both set) |
sdk.ExtensionEpisodeGroup |
Title / URLs []string |
Detail.Chapters group (URLs may be bare strings or {Name, URL} structs; host handles both) |
sdk.ExtensionWatch |
Title / URL / Type / Pages []string / Groups []ExtensionMirrorGroup |
Watch() return (@type not all/bangumi/manga/fikushon) |
sdk.ExtensionMirrorGroup |
Title / Mirrors []ExtensionMirror |
Watch.Groups mirror group |
sdk.ExtensionMirror |
Name / URL / Headers |
Single mirror option |
sdk.ExtensionAllMirror |
Manga / Fikushon / Bangumi |
@type all Watch() return |
sdk.ExtensionMangaWatchMirror |
URLs []string / Headers |
@type manga Watch() / Mirror() |
sdk.ExtensionFikushonWatchMirror |
Content []string / Title / Subtitle |
@type fikushon Watch() / Mirror() |
sdk.ExtensionBangumiWatchMirror |
Type (BangumiWatchType) / URL / Subtitles / Headers / AudioTrack / TLSConfig |
@type bangumi Watch() / Mirror() |
sdk.ExtensionBangumiWatchMirrorSubtitle |
Language / Title / URL |
Subtitle track |
Content Type Constants (BangumiWatchType)
Section titled “Content Type Constants (BangumiWatchType)”var ( HLS = sdk.HLS // "hls" MP4 = sdk.MP4 // "mp4" Magnet = sdk.Magnet // "magnet" Torrent = sdk.Torrent // "torrent" — EXPORTED from sdk package (same alias as HLS/MP4/Magnet))
TorrentandMagnetare for raw.torrentlinks ormagnet:URIs. These mirrors must NOT setTLSConfig(torrent resolution is handled separately, not by mirror proxy).
Network & Proxy (Fetch + TLSConfig + ProxyURL)
Section titled “Network & Proxy (Fetch + TLSConfig + ProxyURL)”Basic Fetch
Section titled “Basic Fetch”body, status, errStr := sdk.Fetch( url, // URL (required) "GET", // method ("GET" / "POST" / ...) map[string]string{...}, // headers (nil allowed) "", // request body (empty for GET) nil, // TLSConfig (nil = default; non-nil = tls-client fingerprint))// Returns: response body string / HTTP status / error message (empty on success)Using TLSConfig (Cloudflare / TLS fingerprint bypass)
Section titled “Using TLSConfig (Cloudflare / TLS fingerprint bypass)”tls := &sdk.TLSConfig{ Profile: "chrome_133", UserAgent: "Mozilla/5.0 ...", DisableRedirect: false, InsecureSkipVerify: true,}body, status, errStr := sdk.Fetch(url, "GET", headers, "", tls)
Fetchis generic and site-agnostic. All site-specific logic (domain, signing, parsing) lives inside the extension that calls it. The host provides no built-in parser.
Mirror & Watch + Content Types (Mirror / Watch / BangumiWatchType)
Section titled “Mirror & Watch + Content Types (Mirror / Watch / BangumiWatchType)”Mirror-level TLSConfig (recommended)
Section titled “Mirror-level TLSConfig (recommended)”For HLS/MP4 streams behind TLS-fingerprinting CDNs, set TLSConfig directly on the mirror struct:
func Mirror(pkg, url string) (*sdk.ExtensionBangumiWatchMirror, error) { return &sdk.ExtensionBangumiWatchMirror{ Type: sdk.HLS, URL: "https://cdn.example.com/video.m3u8", Headers: map[string]string{"Referer": "https://example.com/"}, TLSConfig: &sdk.TLSConfig{Profile: "chrome_133"}, Subtitles: []sdk.ExtensionBangumiWatchMirrorSubtitle{ {Language: "en", Title: "English", URL: "https://cdn.example.com/subs/en.vtt"}, }, }, nil}The host rewrites all mirror URLs (main + subtitles) to host-relative /proxy/... paths. The Flutter player communicates only with localhost; TLS fingerprinting and upstream fetch are handled server-side. Do NOT call sdk.ProxyURL manually on mirror URLs.
ProxyURL (manual only)
Section titled “ProxyURL (manual only)”proxy := sdk.ProxyURL( targetURL, map[string]string{"Referer":"https://example.com/"}, "chrome_133",)// Returns: host-relative proxy URL string (e.g. http://<host>/proxy/...?__u=...)
ProxyURLis for rare cases requiring manual proxy URL construction. Standard video playback: setTLSConfigon mirror and let the host handle it.
Filter System (FilterDefinition + Filter + Builders)
Section titled “Filter System (FilterDefinition + Filter + Builders)”CreateFilter() returns FilterDefinition (typed union: exactly one of Select/MultiSelect/Range set). Search() receives Filter (the selected values mapping).
FilterDefinition & Builders
Section titled “FilterDefinition & Builders”// Select (single-value)sdk.NewSelect("Type", "all"). Option("all", "All"). Option("manga", "Manga"). Option("bangumi", "Anime"). Build()
// MultiSelect (multi-value, Min/Max bounds, constructor variadic defaults)sdk.NewMultiSelect("Tags", 1, 3, "ja"). Option("ja", "Japanese"). Option("zh", "Chinese"). Build()
// Range (numeric bounds)sdk.NewRange("Year", 1990, 2025, 2000, 2025).Build()
NewSelect/NewMultiSelect/NewRangereturn*Builder. Chain.Option();.Build()returnsFilterDefinition(forCreateFilter()). Note:NewMultiSelecttakes variadicdefaults ...string(constructor-level default);SelectFilteruses constructordefparameter.MultiSelectBuilderdoes NOT have.Default().
Read Filter (in Search() / CreateFilter())
Section titled “Read Filter (in Search() / CreateFilter())”func Search(pkg, kw string, page int, filter sdk.Filter) ([]sdk.ExtensionListItem, error) { // Single-select value typ := sdk.FirstSelection(filter, "Type") // Multi-select values (slice) tags := sdk.SelectionsOf(filter, "Tags") // Has selection? if sdk.HasSelection(filter, "YearRange") { ... }}Cross-Function Cache (SaveCache / GetCache / DeleteCache)
Section titled “Cross-Function Cache (SaveCache / GetCache / DeleteCache)”Every Search() / Detail() / Watch() / Mirror() call runs a fresh ScriggoVM (see invoke.go). Function-local variables do not persist across calls. Use cache for cross-call state:
// Write (key is string; value: string/number/slice/map/struct. AVOID: function values, channels, goroutine-bound references — these break the Scriggo VM boundary.)sdk.SaveCache("token", authToken)
// Readv, ok := sdk.GetCache("token")if ok { token := v.(string)}Cache is isolated per extension package (
pkg). To delete:runtime.DeleteCache(pkg). Note lazyensureCrashFilefallback for pre-init panics (pkg/logger/log.go).
Extension Settings (RegisterSetting / GetSetting / SetSetting)
Section titled “Extension Settings (RegisterSetting / GetSetting / SetSetting)”// Register (run once in Load() or first call)sdk.RegisterSetting(sdk.ExtensionSetting{ Key: "quality", Title: "Quality", Type: sdk.SettingRadio, Value: "1080p", DefaultValue: "720p", Description: "Default download quality", Options: []string{"720p", "1080p", "4k"},}, pkg)
// Readval, err := sdk.GetSetting(pkg, "quality") // empty string = unset
// Writeerr := sdk.SetSetting(pkg, "quality", "4k")Cookie Management (GetCookies / SetCookies)
Section titled “Cookie Management (GetCookies / SetCookies)”cookies, err := sdk.GetCookies("https://example.com/")// cookies: []string (each "name=value")
err = sdk.SetCookies("https://example.com/", []string{ "session_id=abc; Path=/", "auth_token=xyz; HttpOnly",})Full Entry-Point Example (Load / Search / Detail / Watch / Mirror)
Section titled “Full Entry-Point Example (Load / Search / Detail / Watch / Mirror)”package example
import sdk "github.com/miru-project/miru-core/pkg/extension/golang/sdk"
func Load() { sdk.RegisterSetting(sdk.ExtensionSetting{ Key: "proxy", Title: "Proxy", Type: sdk.SettingToggle, Value: "false", DefaultValue: "false", }, "example")}
func Search(pkg, kw string, page int, filter sdk.Filter) ([]sdk.ExtensionListItem, error) { token, ok := sdk.GetCache("token") if !ok { body, _, errStr := sdk.Fetch("https://example.com/login", "POST", map[string]string{"Content-Type":"application/x-www-form-urlencoded"}, "username=user&password=pass", nil) if errStr != "" { return nil, fmt.Errorf("login failed: %s", errStr) } token = body sdk.SaveCache("token", token) } headers := map[string]string{"Authorization": token.(string), "User-Agent":"Miru/1.0"} body, _, errStr := sdk.Fetch("https://example.com/search?q="+kw, "GET", headers, "", nil) if errStr != "" { return nil, fmt.Errorf("search failed: %s", errStr) } return []sdk.ExtensionListItem{ {Title: "Result 1", URL: "https://example.com/1", Cover: "https://example.com/1.jpg", Type: "manga"}, }, nil}
func Detail(pkg, url string) (*sdk.ExtensionDetail, error) { body, _, errStr := sdk.Fetch(url, "GET", nil, "", nil) if errStr != "" { return nil, fmt.Errorf("detail failed: %s", errStr) } return &sdk.ExtensionDetail{ Title: "Detail", Desc: body, URL: url, Chapters: []sdk.ExtensionEpisodeGroup{ {Title: "Chapter 1", URLs: []string{"https://cdn.example.com/1.jpg"}}, }, }, nil}
func Watch(pkg, url string) (*sdk.ExtensionWatch, error) { return &sdk.ExtensionWatch{ Title: "Playback", URL: url, Type: "manga", Groups: []sdk.ExtensionMirrorGroup{ {Title: "Source 1", Mirrors: []sdk.ExtensionMirror{ {Name: "Raw", URL: url}, }}, }, }, nil}
func Mirror(pkg, url string) (*sdk.ExtensionBangumiWatchMirror, error) { return &sdk.ExtensionBangumiWatchMirror{ Type: sdk.HLS, URL: url, TLSConfig: &sdk.TLSConfig{Profile: "chrome_133"}, }, nil}Note (
Mirror()return): Depends on extension’s@typetag:
@type bangumi->*ExtensionBangumiWatchMirror@type manga->*ExtensionMangaWatchMirror(URLs []string, noTLSConfig)@type fikushon->*ExtensionFikushonWatchMirror(Content []string)@type all->*ExtensionAllMirror(Manga/Fikushon/Bangumimembers) Example above isbangumi; adjust return type accordingly.
Source References
Section titled “Source References”| Concept | Source Path |
|---|---|
| SDK alias/re-export | pkg/extension/golang/sdk/sdk.go |
| Model types (watch/mirror shapes) | pkg/extension/golang/runtime/model.go |
Fetch / TLSConfig / ProxyURL |
pkg/extension/golang/runtime/model.go |
Filter builders (NewSelect etc) and Filter reading |
pkg/extension/golang/runtime/filters.go, runtime/filter.go |
Cache (SaveCache / GetCache / DeleteCache) |
pkg/extension/golang/runtime/cache.go |
Settings (RegisterSetting / GetSetting / SetSetting) |
pkg/extension/golang/runtime/settings.go |
Cookies (GetCookies / SetCookies) |
pkg/extension/golang/runtime/settings.go |
Extension Dev Audit Notes
Section titled “Extension Dev Audit Notes”Filter Concepts: Three separate layers —
FilterDefinition(whatCreateFilter()returns:Select/MultiSelect/Rangeunion);Filter(frontend selection passed toSearch());FilterSelectionBuilder(.Select()/.SelectMany()/.Build()). Do not mix.CreateFilter Return: Must return
map[string]sdk.FilterDefinition, not a single value. The host converts each entry viareflect.Map->runtime.FilterDefinitionToProto(filter_bridge.go).Episode URLs:
URLselements may be bare URL strings or{Name, URL}structs. The host (convert_list.go) handles both automatically.Execution Environment:
Load()is optional (lazy compile on first request). Each entry point call creates a freshScriggoVM(invoke.go) with isolated native package mapping (packagesForPkg()indevlog.go).Fetch()errors return strings (errStr), not Goerrorinterfaces, due to Scriggo boundary limits (runtime/model.go).
Scriggo Limitations & Importable Packages (Supplement)
Section titled “Scriggo Limitations & Importable Packages (Supplement)”Full Scriggo limitations and practical advice: see
developer/go/2-detail-usage.mdx. Key verified points from source (packages.go,runtime/model.go,vm.go,convert_reflect.go):
- Method / interface declarations: Not supported (models are plain structs in
runtime/model.go). unsafe/runtimeimports: Not inpackages.go;Compile()rejects them.reflectlimitations:convert_reflect.goreads fields case-insensitively (URL->Url);fmt.Printf("%T", v)shows underlying host wrapper type, not user-defined type.- Native package isolation:
devlog.go’spackagesForPkg()clones the 153-entrypackagesmap per extension (packages.goinit); only these 153 packages can be imported. - Performance limits: Per
packages.gocomments (register limits: 127 int/float/string/generic, 256 types/predefined functions, 16384 int/float constants, 256 string constants, 256 generic values); enforced byscriggo.Build()(vm.go). - Cache isolation:
runtime/cache.go(sync.Map);load.go(HandleReload) drops both cache andpkgPackageson.gofile change.