> For the complete documentation index, see [llms.txt](https://seanime.gitbook.io/seanime-extensions/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://seanime.gitbook.io/seanime-extensions/seanime/core-apis.md).

# 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)
```
