NoMercy-owned libass/SubtitleOctopus wrapper. Auth, cross-origin worker, canvas geometry, and lifecycle patches built in. No UI. https://docs.nomercy.tv/nomercy-video-player/plugins/octopus
  • TypeScript 98.5%
  • JavaScript 1.5%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Stoney_Eagle f38a81ae34 style(subtitles): drop the dead font-url mapping and settle the lint
fontUrls was resolved and never used once fetchFonts started resolving each
url itself. The this-alias disable sits on the assignment it applies to;
antfu's config only reports it with CI=true, so the editor run showed a
different set of problems than the gate would.
2026-08-04 13:51:55 +02:00
.github/workflows ci: add release and npm-publish workflows 2026-05-18 12:22:35 +02:00
public feat(subtitles): our own libass worker, and SubtitlesOctopus is gone 2026-08-04 13:40:18 +02:00
src style(subtitles): drop the dead font-url mapping and settle the lint 2026-08-04 13:51:55 +02:00
.gitignore feat(nomercy-subtitle-octopus): initial package scaffold v0.1.0 2026-05-09 21:21:47 +02:00
COPYRIGHT feat(nomercy-subtitle-octopus): initial package scaffold v0.1.0 2026-05-09 21:21:47 +02:00
eslint.config.js feat(nomercy-subtitle-octopus): initial package scaffold v0.1.0 2026-05-09 21:21:47 +02:00
package-lock.json fix(deps): patch vite and js-yaml advisories 2026-06-18 21:31:14 +02:00
package.json chore(lint): make lint a real check gate and fix tab formatting 2026-07-10 06:20:25 +02:00
README.md chore(lint): make lint a real check gate and fix tab formatting 2026-07-10 06:20:25 +02:00
tsconfig.json fix(pkg): exclude test decls from dist, drop public duplication, fix worker glob 2026-05-18 12:03:35 +02:00
vite.config.ts fix(subtitle-octopus): forward availableFonts to upstream renderer 2026-05-15 12:24:09 +02:00

@nomercy-entertainment/nomercy-subtitle-octopus

NoMercy-owned TypeScript wrapper around the libass-wasm (SubtitleOctopus) WASM renderer.

This package is not a fork of upstream — it's a thin consumer-side wrapper that adds the patches NoMercy needs without modifying the WASM worker. The actual fork of libass/JavascriptSubtitlesOctopus lives at @nomercy-entertainment/libass-wasm — that package owns the C++ source, Emscripten build pipeline, and the binaries.

Patches owned here (main-thread, TypeScript)

  • Bearer token auth — pre-fetch subtitle + font files on the main thread with Authorization: Bearer <token>, hand the worker blob URLs. Worker stays unmodified, never makes auth-required calls. Custom request schemes (nmsync:, cast:, data:, blob:, file:, etc.) bypass the same-origin guard so the token never leaks onto requests that can't carry it.
  • Cross-origin worker instantiation — Blob URL + importScripts() shim so workers load from a different origin (CDN deployments).
  • Canvas geometry syncResizeObserver against a configurable geometrySource element; copies six CSS properties to canvasParent so the libass canvas tracks the player's overlay through fullscreen / theater / float transitions.
  • Lifecycle race guardscurrentLoadedUrl + race-token pattern, orphan canvas sweep, idempotent dispose.
  • URL resolutionbasePath prepend with proper RFC-3986 absolute-URL detection (any scheme, plus protocol-relative //host/path).

Binaries

public/subtitles-octopus-worker.{js,wasm} and public/default.ttf are vendored from upstream libass-wasm@4.1.0 pending a first NoMercy build out of the sister fork (@nomercy-entertainment/libass-wasm). License chain reproduced in COPYRIGHT.

Worker files — copy to public

The libass WASM worker files ship inside this package's dist/ directory. Your build tool must copy them into the public/static directory so the browser can load them:

// vite.config.ts
import { viteStaticCopy } from 'vite-plugin-static-copy';

export default {
	plugins: [
		viteStaticCopy({
			targets: [
				{
					src: 'node_modules/@nomercy-entertainment/nomercy-subtitle-octopus/dist/subtitles-octopus-worker*.{js,wasm,data}',
					dest: 'static',
				},
			],
		}),
	],
};

Then pass the copied paths when registering the plugin:

player.addPlugin(OctopusPlugin, {
	workerUrl: '/static/subtitles-octopus-worker.js',
	legacyWorkerUrl: '/static/subtitles-octopus-worker-legacy.js',
});

Usage

import { NMSubtitleOctopus } from '@nomercy-entertainment/nomercy-subtitle-octopus';

// Pre-fetch subtitle bytes and font binaries with whatever auth pipeline you use.
const trackContent = await fetchAuthedText('/episode-1.ass');
const availableFonts = await fetchAuthedFonts('/fonts/');

const octopus = new NMSubtitleOctopus({
	video: videoElement,
	trackContent,
	availableFonts,
	geometrySource: playerContainer,
});

octopus.on('rendererReady', ({ url }) => console.log('renderer ready', url));

The renderer never performs authenticated network I/O — your consumer (or the NoMercy player kit) pre-fetches every byte the worker needs and passes it as trackContent plus a name → blob:URL font map.

License

MIT — TypeScript wrapper code. The vendored WASM artefact carries the upstream license chain (LGPL-2.1 / FTL / GPL-2.0 / MIT / ISC / NTP / Zlib / BSL-1.0) reproduced in COPYRIGHT.