# Core APIs

## Types

Create a `core.d.ts` file containing the following content:

{% embed url="<https://raw.githubusercontent.com/5rahim/seanime/refs/heads/main/internal/extension_repo/goja_plugin_types/core.d.ts>" %}
core.d.ts
{% endembed %}

## Examples

### User preference

Returns the user preference value from [Write, test, share](/seanime-extensions/content-providers/write-test-share.md#add-user-configuration-optional)

```typescript
$getUserPreference("apiToken")
```

### Console

```typescript
console.log()
console.warn()
console.error()
```

### Fetch

{% code title="Example" %}

```typescript
/// <reference path="./core.d.ts" />

const res = await fetch("https://jsonplaceholder.typicode.com/todos/1")
const data = res.json()
```

{% endcode %}

### Doc

Parse HTML

```typescript
const $ = LoadDoc(`
<section id="content">
    <article class="post" data-id="1">
        <h2>First Post</h2>
        <p>This is the first post.</p>
        <a href="https://example.com/first-post" class="read-more">Read more</a>
    </article>
    <article class="post" data-id="2">
        <h2>Second Post</h2>
        <p>This is the second post.</p>
        <a href="https://example.com/second-post" class="read-more">Read more</a>
    </article>
    <article class="post" data-id="3">
        <h2>Third Post</h2>
        <p>This is the third post.</p>
        <a href="https://example.com/third-post" class="read-more">Read more</a>
    </article>
</section>
`);

const titles = $("section")
    .children("article.post")
    .filter((i, e) => e.attr("data-id") !== "1")
    .map((i, e) => e.children("h2").text())

console.log(titles) // [Second Post, Third Post] 
```

### ChromeDP

Headless browser powered by the user's installed Chrome/Chromium binary.

{% hint style="warning" %}
ChromeDP is experimental and is not guaranteed to work.

Always call `close()` to avoid leaking resources.
{% endhint %}

```typescript
// -------- Content Providers ----------

const browser = await ChromeDP.newBrowser();
await browser.navigate("https://example.com");

// Wait for the dynamic item to appear
await browser.waitVisible("#dynamic-item");

// Get the text of the dynamic item
const itemText = await browser.text("#dynamic-item");

await browser.close();

// ------------- Plugins ---------------
function init() {
    $ui.register((ctx: $ui.Context) => {
        async function doSomething() {
            const browser = await ctx.chromeDP.newBrowser()
            // ...
            await browser.close()
        }
    })
}
```

### CryptoJS

```typescript
let message = "seanime";
let key = CryptoJS.enc.Utf8.parse("secret key");

console.log("Message:", message);

let encrypted = CryptoJS.AES.encrypt(message, key);
console.log("Encrypted:", encrypted); // map[iv toString]
console.log("Encrypted.toString():", encrypted.toString()); // AoHrnhJfbRht2idLHM82WdkIEpRbXufnA6+ozty9fbk=
console.log("Encrypted.toString(CryptoJS.enc.Base64):", encrypted.toString(CryptoJS.enc.Base64)); // AoHrnhJfbRht2idLHM82WdkIEpRbXufnA6+ozty9fbk=

let decrypted = CryptoJS.AES.decrypt(encrypted, key);
console.log("Decrypted:", decrypted.toString(CryptoJS.enc.Utf8));

let iv = CryptoJS.enc.Utf8.parse("3134003223491201");
encrypted = CryptoJS.AES.encrypt(message, key, { iv: iv });
console.log("Encrypted:", encrypted); // map[iv toString]

decrypted = CryptoJS.AES.decrypt(encrypted, key);
console.log("Decrypted without IV:", decrypted.toString(CryptoJS.enc.Utf8)); // "" <- Nothing

decrypted = CryptoJS.AES.decrypt(encrypted, key, { iv: iv });
console.log("Decrypted with IV:", decrypted.toString(CryptoJS.enc.Utf8)); // seanime


let a = CryptoJS.enc.Utf8.parse("Hello, World!");
console.log(a); // // Uint8Array [72 101 108 108 ...]

let b = CryptoJS.enc.Base64.stringify(a);
console.log(b); // SGVsbG8sIFdvcmxkIQ==

let c = CryptoJS.enc.Base64.parse(b);
console.log(c); // Uint8Array [72 101 108 108 ...]

let d = CryptoJS.enc.Utf8.stringify(c);
console.log(d); // Hello, World!
```

### $habari

Filename parser

```typescript
data := $habari.parse("Hyouka (2012) S1-2 [BD 1080p HEVC OPUS] [Dual-Audio]")
console.log(data.title)            // Hyouka
console.log(data.formatted_title)  // Hyouka (2012)
console.log(data.year)             // 2012
console.log(data.season_number)    // ["1", "2"]
console.log(data.video_resolution) // 1080p
```

### $clone

Useful for safely handling events.

```typescript
function init() {
    $app.onPreUpdateEntryEvent(e => {
        $store.set("onPreUpdateEntry", $clone(e))
    })
}
```

### $replace

The `$replace` function is used to overwrite properties of an object within an event.

{% hint style="warning" %}
This only works on values that are not undefined and on values that are references under the hood.
{% endhint %}

{% tabs %}
{% tab title="How it works" %}
{% code overflow="wrap" %}

```typescript
$app.onGetAnime((e) => {
    if(e.anime.id === 130003) {
        console.log(e.anime.title)
        // { 
        //    "english": "Bocchi the Rock!",
        //    "romaji": "Bocchi the Rock!", 
        //    "userPreferred": "Bocchi the Rock!"
        // }
    
        e.anime.title = { "english": "The One Piece is Real" }
        console.log(e.anime.title) 
        // { 
        //    "english": "The One Piece is Real",
        //    "romaji": "Bocchi the Rock!", 
        //    "userPreferred": "Bocchi the Rock!"
        // }
    
        // ✅ Overwrite the entire 'title' object
        $replace(e.anime.title, { "english": "The One Piece is Real" })
        console.log(e.anime.title)
        // { 
        //    "english": "The One Piece is Real",
        //    "romaji": undefined, 
        //    "userPreferred": undefined
        // }
        
        e.anime.synonyms[0] = "The One Piece" // ✅ Works
        $replace(e.anime.synonyms[0], "The One Piece") // ✅ Works
        
        // ⛔️ Doesn't work because 'id' is not a reference under the hood
        $replace(e.anime.id, 22)
        // ⛔️ Doesn't work if 'bannerImage' is undefined
        $replace(e.anime.bannerImage, "abc")
    }
    
    e.next();
})
```

{% endcode %}
{% endtab %}
{% endtabs %}

### $torrentUtils

```typescript
// Get a magnet link from a torrent file content
const res = await fetch("http://[...].torrent")
const content = res.text()
$torrentUtils.getMagnetLinkFromTorrentData(content)
```

### $toString

Converts binary data to string.

```typescript
const uint8Array = new Uint8Array(new ArrayBuffer(5));
uint8Array[0] = 104;
uint8Array[1] = 101;
uint8Array[2] = 108;
uint8Array[3] = 108;
uint8Array[4] = 111;

console.log($toString(uint8Array)); // hello
```

### $toBytes

Similar to the Web API `TextEncoder.encode`

```typescript
const b = $toBytes("hello")
console.log(b); // Uint8Array [104, 101, 108, 108, 111]

console($toString(b)); // hello
```

### $sleep

{% hint style="warning" %}
Use carefully
{% endhint %}

```typescript
// sleeps for 1s
$sleep(1000)
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://seanime.gitbook.io/seanime-extensions/seanime/core-apis.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
