🧩
Seanime Extensions
🧩
Seanime Extensions
  • Seanime
    • Getting started
    • Core APIs
    • Changelog
  • Content Providers
    • Write, test, share
    • Anime torrent provider
    • Manga provider
    • Online streaming provider
  • Plugins
    • Introduction
    • Write, test, share
    • APIs
      • Helpers
      • Store
      • Storage
      • Database
      • AniList
      • System
        • Permissions
        • OS
        • Filepath
        • Commands
        • Buffers, I/O
        • MIME
    • UI
      • Basics
      • User Interface
        • Tray
        • Toast
        • Screen
        • Command Palette
        • Action
        • DOM
      • Anime/Library
        • Anime
        • Playback
        • Continuity
        • Auto Downloader
        • Auto Scanner
        • Filler Manager
        • External Player Link
      • Downloading
        • Downloader
        • Torrent Client
      • Other
        • Manga
        • Discord
        • MPV
    • Hooks
    • Example
  • Frequently asked
    • Feature requests
Powered by GitBook
On this page
  • List of hooks
  • Usage
  • Best Practices
  • Editing events
  • Listening to events
  1. Plugins

Hooks

PreviousMPVNextExample

Last updated 1 month ago

Difficulty: Hard

  • Event-driven understanding required

List of hooks

Usage

Hooks should be used carefully as they can introduce undefined behavior and even slow down the app.

Some hooks, like onGetAnime , are triggered very often, so it's a good habit to start by logging the event in order to figure out its frequency.

You should also avoid expensive calculations or fetch calls in hook handlers unless you can guarantee that the hook is not triggered often.

Any error/exception that happens in a hook handler will result in a server and client error. Test your code carefully.

Example
function init() {
   // This hook is triggered before Seanime formats the library data of an anime
   // The event contains the variables that Seanime will use, and you can modify them
   $app.onAnimeEntryLibraryDataRequested((e) => {
      // Setting this to an empty array will cause Seanime to think that the anime
      // has not been downloaded.
      e.entryLocalFiles = []
      
      e.next() // Continue hook chain
   })
}

Each hook handler must call e.next() in order for the hook chain listening to that event to proceed. Not calling it will impact other plugins listening to that event.

Best Practices

Editing events

Let's say we want your plugin to change anime banner images based on what custom banner image has been set for that anime. However you want to do it without manipulating the DOM and before the page is even loaded.

We can use onGetAnimeCollection and onGetRawAnimeCollection since these are triggered when Seanime fetches the user's anime collection from AniList. Note that this will not change banner images for the same anime if it's fetched using another query (e.g. discover, search).

// Triggers the app loads the user's AniList anime collection
$app.onGetAnimeCollection((e) => {
    // 1. Get all the custom banner images
    const bannerImages = $storage.get<Record<string, string | undefined>>('bannerImages');
    if (!bannerImages) {
        e.next()
        return
    }
    if (!e.animeCollection?.mediaListCollection?.lists?.length) {
        e.next()
        return
    }
    
    // 2. Go through all anime in the collection
    for (let i = 0; i < e.animeCollection!.MediaListCollection!.lists!.length; i++) {
        for (let j = 0; j < e.animeCollection!.MediaListCollection!.lists![i]!.entries!.length; j++) {
            const mediaId = e.animeCollection!.MediaListCollection!.lists![i]!.entries![j]!.media!.id
            // 3. If this anime has a custom image, change it
            const bannerImage = bannerImages[mediaId.toString()]
            if (!!bannerImage) {
                e.animeCollection!.MediaListCollection!.lists![i]!.entries![j]!.media!.bannerImage = bannerImage
            }
        }
    }

    // 4. Continue
    e.next()
})

// Do the same with $app.onGetRawAnimeCollection

Listening to events

Let's say we want to make a plugin that stores the history of scanning durations.

// ⚠️ Not recommended: Doing unnecessary work in the hook callback
function init() {
    $app.onScanCompleted((e) => {
        const now = new Date().toISOString().replaceall(".", "_")    
        // Add the value to the history
        // NOTE: In reality this operation is very fast
        $storage.set("scan-duration-history."+now, e.duration)
        
        e.next()
    })
    
    $ui.register((ctx) => {
        
    })
}

// ✅ Good practice: Defer business logic to the UI context
function init() {
    $app.onScanCompleted((e) => {
        // Send a copy of the event
        $store.set("scan-completed", $clone(e))
        e.next()
    })
    
    // Let the UI context "listen" to the event and execute business logic
    $ui.register((ctx) => {
        // Callback is triggered anytime 'set' is called on that key
        $store.watch<number>("scan-completed", (e) => {
            const now = new Date().toISOString().replaceall(".", "_")
            
            // Add the value to the history
            $storage.set("scan-duration-history."+now, e.duration)
            
            ctx.toast.info(`Scanning took ${e.duration/1000} seconds!`)
        })
    })
}
https://seanime.rahim.app/docs/hooks