# Miru Extension API V2 (Recommended) Miru API V2 uses a modern, global function-based approach for a cleaner syntax and native `async`/`await` support. ## JS Environment Restrictions > [!WARNING] > **Global Scope**: Do **not** use `const` or `let` at the top-level global scope. The JS engine (Goja) may fail to evaluate the script correctly during re-loads. Use `var` for global variables or keep logic inside functions. ## Filter Lifecycle To provide a dynamic search experience, the filter system follows this flow: 1. **Selection**: When a user clicks an option in the filter UI, Miru calls `createFilter(filter)` with the currently selected values. This allows you to dynamically change the *next* set of filter options based on previous choices (e.g., selecting a Category might fetch then update the Tags list). 2. **Execution**: When the user finally presses the **Search** button, your `search(kw, page, filter)` function is called. The `filter` parameter will contain all the selections currently active in the UI. ## Watch & Mirror Lifecycle For **API V2**, the process of playing a video or viewing content follows a two-step resolution: 1. **Initial Resolution**: When a user clicks to watch an episode or chapter, Miru calls `watch(url)`. This function can return a direct stream/content URL or a structured list of `mirrors`. 2. **Final Resolution**: Miru then calls `mirror(url)` to get the final playable stream link. - **Default Behavior**: If you do not override the `mirror()` function, the built-in runtime simply returns the `url` passed to it. - **Custom Logic**: If your `watch()` function returns mirrors, the `mirror()` function is your chance to resolve those mirrors into a final stream (e.g., executing a second scraping step or solving a challenge). ## Core Interface To implement a V2 extension, you define the following global async functions in your `.js` file. ### 1. `load()` Called when the extension is initialized. Use this for setup and registering settings. ```javascript async function load() { // Register settings or init data } ``` ### 2. `latest(page)` Fetch the latest updates or items. - **page**: `int` - The requested page number. - **Returns**: `[ExtensionListItem]` **Example JSON Response:** ```json [ { "title": "Anime Title", "url": "/anime/123", "cover": "https://example.com/cover.jpg", "update": "Ep 12", "headers": { "User-Agent": "Miru" } } ] ``` ### 3. `search(kw, page, filter)` Search for items based on a keyword and filters. - **kw**: `string` - Search keyword. - **page**: `int` - Page number. - **filter**: `object` - The current filter values `{ id: [values] }`. This is the same object structure you define in `createFilter`. - **Returns**: `[ExtensionListItem]` **Example JSON Response:** ```json [ { "title": "Search Result", "url": "/anime/456", "cover": "https://example.com/cover2.jpg" } ] ``` ### 4. `createFilter(filter)` Generate filter options for the search UI. - **filter**: `object` - Currently active selections. **Action**: Every time a user interacts with a filter option in the UI, this function is re-called, allowing you to update available filters dynamically. - **Returns**: `map` **Example JSON Response:** ```json { "genre": { "title": "Genres", "min": 0, "max": 1, "default": "action", "options": { "action": "Action", "romance": "Romance", "horror": "Horror" } }, "year": { "title": "Year", "min": 0, "max": 1, "options": { "2023": "2023", "2022": "2022" } } } ``` ### 5. `detail(url)` Fetch item details and episodes/chapters. - **url**: `string` - The item URL. - **Returns**: `ExtensionDetail` **Example JSON Response:** ```json { "title": "Steins;Gate", "cover": "https://...", "desc": "A mad scientist...", "episodes": [ { "title": "TV Series", "urls": [ { "name": "Episode 1", "url": "/watch/1", "update": "2023-10-01", "description": "The first encounter." }, { "name": "Episode 2", "url": "/watch/2", "update": "2023-10-08" } ] } ], "headers": { "Referer": "https://mystream.site" } } ``` ### 6. `watch(url)` Fetches the content for a specific episode or chapter. - **url**: `string` - The episode/chapter URL. - **Returns**: `ExtensionBangumiWatch` | `ExtensionMangaWatch` | `ExtensionFikushonWatch` #### **Bangumi (Video) Example:** ```json { "type": "hls", "url": "https://example.com/master.m3u8", "subtitles": [ { "title": "English", "url": "https://...", "language": "en" } ], "headers": { "User-Agent": "Miru" } } ``` #### **Manga (Images) Example:** ```json { "urls": [ "https://example.com/1.jpg", "https://example.com/2.jpg" ], "headers": { "Referer": "https://manga.site" } } ``` #### **Fikushon (Text/Novel) Example:** ```json { "title": "Chapter 1", "subtitle": "The Start", "content": [ "First paragraph text...", "Second paragraph text..." ] } ``` #### **Returning Mirrors (API V2 Exclusive)** Instead of a direct URL, you can return mirrors to let the user select the source. ```json { "mirrors": [ { "title": "Group A", "mirrors": [ { "name": "Mirror 1", "url": "https://site-a.com/link1" }, { "name": "Mirror 2", "url": "https://site-b.com/link2" } ] } ], "default_group": "Group A", "default_index": 0 } ``` > [!Note] > For the relationship of `EpisodeList` and `Episode` in `watch` can be view as different video source with list of episode, while `mirror` can be view as video resolution list after obtain the video link. ### 7. `mirror(url)` Resolves the final playable URL from a mirror link selected by the user. - **url**: `string` - The URL of the selected mirror. - **Returns**: `string` --- ## Load & Settings Use the `load()` function to initialize settings that will appear in the Miru UI. ### `registerSetting(setting)` ```javascript async function load() { // Input setting await registerSetting({ title: "Site Domain", key: "domain", type: "input", description: "Homepage URL used for scraping", defaultValue: "https://example.com" }); // Radio setting await registerSetting({ title: "Quality Preference", key: "quality", type: "radio", defaultValue: "1080p", options: { "1080p": "High (1080p)", "720p": "Medium (720p)", "480p": "Low (480p)" } }); } ``` **Setting Types:** - `input` (0): Text field. - `radio` (1): Selection with `options` (Key-Value map). - `toggle` (2): Checkbox. ### `getSetting(key)` Retrieves the current value of a setting. ```javascript const domain = await getSetting("domain"); ``` ## Networking Use the standard `fetch()` API for all network requests. ```javascript const res = await fetch("..."); const data = await res.json(); ``` ## Utilities Standard Node-like modules are available via `require()`: - `linkedom`: DOM Parser - `crypto-js`: Cryptography - `md5`, `jsencrypt`, `url`