Octopus (ASS/SSA)
OctopusPlugin is the plugin to reach for when your media library includes anime or any content with styled .ass or .ssa subtitle tracks.
It wraps @nomercy-entertainment/nomercy-subtitle-octopus (the NoMercy fork of libass-wasm) and handles the lifecycle, font loading, and auth-gating so the WASM worker never touches the network directly.
For plain .vtt subtitle tracks, use SubtitleOverlayPlugin instead.
@nomercy-entertainment/nomercy-subtitle-octopus is a plain dependency bundled with the package.
The plugin imports it dynamically at runtime as a safety net.
When the import fails the plugin logs a warning and marks itself as degraded; playback continues normally and ASS/SSA subtitles will not render.
import { OctopusPlugin } from '@nomercy-entertainment/nomercy-video-player/plugins';
import type { OctopusOptions } from '@nomercy-entertainment/nomercy-video-player/plugins';
Plugin id: 'octopus'
What it does
The plugin listens to the player’s 'subtitle' event.
When a track is selected it resolves the URL by calling player.subtitles?.() and indexing into that list by the zero-based index the event carries.
If the resolved extension is not .ass or .ssa the renderer is torn down and the track is left to the native text-track pipeline or SubtitleOverlayPlugin.
For ASS/SSA files it pre-fetches the subtitle body via this.fetch (the player core’s auth pipeline), resolves fonts into blob URLs the same way, and instantiates the libass WASM renderer.
On the 'current' event the renderer is torn down and the per-item font cache is cleared so the new item starts clean.
All network I/O goes through the player core auth pipeline. The WASM worker receives pre-fetched content as blob URLs and inline strings, it never performs authenticated XHR itself.
Options
| Option | Type | Default | Description |
|---|---|---|---|
workerUrl | string | bundled path | URL for the modern WASM worker script. |
legacyWorkerUrl | string | none | URL for the legacy non-WASM worker, for browsers without WebAssembly. |
fallbackFont | string | none | Font URL used when a requested font is not in the item’s fonts list. |
fonts | string[] | [] | Static font URLs preloaded for every item, supplementing per-item fonts. |
targetFps | number | library default | Renderer target FPS. Leave unset to let the library choose. |
renderMode | 'wasm-blend' | 'js-blend' | 'lossy' | 'wasm-blend' | Render quality vs. performance trade-off. |
lazyFileLoading | boolean | false | Fetch subtitle chunks lazily, useful for very large ASS files. |
prescaleFactor | number | library default | Internal scaler ratio. Leave unset for default scaling. |
renderAhead | number | 10 | Frames pre-computed ahead of currentTime. Higher values smooth playback through heavy ASS effects at the cost of memory. |
debug | boolean | false | Enable debug logging in the libass WASM worker. |
Events
Events fire on the plugin channel. Subscribe from outside the plugin using the string form.
| Event | Payload | Description |
|---|---|---|
renderer:ready | { url: string } | Fired after the libass renderer initialises and the first frame is ready for the given subtitle URL. |
player.on('plugin:octopus:renderer:ready', ({ url }) => {
console.log('libass renderer ready for', url);
});
Methods
subtitle
plugin.subtitle(): string | null
plugin.subtitle(url: string | null): Promise<void>
Read or set the active subtitle URL directly, bypassing the player core’s track list.
Use this for ASS files the consumer supplies outside the subtitles array.
import { OctopusPlugin } from '@nomercy-entertainment/nomercy-video-player/plugins';
const plugin = player.getPlugin(OctopusPlugin);
await plugin?.subtitle(
'https://raw.githubusercontent.com/NoMercy-Entertainment/nomercy-media/master/Anime/Rail.Wars%21.(2014)/Rail.Wars%21.S00E00/subtitles/Rail.Wars%21.(2014).S00E00.NoMercy.eng.full.ass',
);
await plugin?.subtitle(null); // clear
fonts
plugin.fonts(): readonly string[]
plugin.fonts(urls: string[]): Promise<void>
Read or replace the plugin-level static font list.
The getter returns blob: URLs when fonts have already been fetched and loaded for the active item, or the original plugin-level URL strings before the first load. Calling it with new URLs clears the per-item font cache and re-loads the active subtitle so the new fonts apply immediately.
renderer
plugin.renderer(): object | null
Returns the raw libass renderer handle for advanced consumers.
The returned value is an opaque object; its type is not part of the public API.
The plugin retains lifecycle ownership, do not call dispose() on the returned instance directly.
Font resolution
Fonts are resolved in this order for each item:
- The
fontsarray on the activeVideoPlaylistItem. Each entry is aFontTrackRefobject ({ file: string; label?: string }) whosefileproperty is afonts.jsonmanifest URL or a direct font file URL. OctopusOptions.fonts, static URLs registered at plugin level, applied to all items.OctopusOptions.fallbackFont, a single URL used when ASS requests a font not found above.
All font fetches go through the player core’s auth pipeline. Each binary is converted to a blob URL and passed to the WASM worker. Blob URLs are revoked when the renderer is torn down to avoid memory leaks.
Registration
import nmplayer from '@nomercy-entertainment/nomercy-video-player';
import { OctopusPlugin } from '@nomercy-entertainment/nomercy-video-player/plugins';
const player = nmplayer('player')
.addPlugin(OctopusPlugin, {
workerUrl: '/subtitles-octopus-worker.js',
fallbackFont: '/fonts/NotoSans-Regular.ttf',
renderAhead: 10,
})
.setup({
baseUrl: 'https://raw.githubusercontent.com/NoMercy-Entertainment/nomercy-media/master/Anime',
playlist: [
{
id: 'rail-wars-s00e00',
title: 'Rail Wars! S00E00',
url: '/Rail.Wars!.(2014)/Rail.Wars!.S00E00/Rail.Wars!.(2014).S00E00.NoMercy.m3u8',
subtitles: [
{
id: '1',
kind: 'subtitles',
label: 'English (ASS)',
language: 'en',
url: '/Rail.Wars!.(2014)/Rail.Wars!.S00E00/subtitles/Rail.Wars!.(2014).S00E00.NoMercy.eng.full.ass',
},
],
fonts: [
{
file: '/Rail.Wars!.(2014)/Rail.Wars!.S00E00/fonts.json',
},
],
},
],
});