ASS Subtitles

:placard: Summary Renders embedded ASS/SSA subtitles during video playback using JASSUB (WebAssembly libass).
:link: Repository https://github.com/hisoka343/stashapp-subtitle-plugin-support/tree/main/plugins
:information_source: Source URL https://hisoka343.github.io/stashapp-subtitle-plugin-support/main/index.yml
:open_book: Install How to install a plugin?

Made a plugin that renders embedded ASS/SSA subtitles in Stash’s video player.

What it does

  • Detects and extracts embedded ASS/SSA subtitle tracks from your videos using ffmpeg
  • Renders them pixel-perfectly using JASSUB — a WebAssembly port of libass (same engine as mpv/VLC)
  • CC button in the player toolbar to toggle subs
  • Works in fullscreen
  • Supports pretty much all ASS features: styled text, positioning, colors, outlines, karaoke, animations, fansub typesetting, etc.

Why

Stash doesn’t support ASS/SSA subs natively. If you have anime or anything with embedded styled subtitles, they just don’t show. This fixes that.

How it works

  • Python backend extracts subtitle data via ffmpeg on-the-fly (or batch pre-extract for speed)
  • JS frontend loads the JASSUB WASM renderer and draws subs on a canvas overlay
  • Canvas lives outside React’s DOM so it doesn’t get nuked by re-renders, repositions every frame to track the video through resize and fullscreen

Heads up — this was made entirely by AI for my own use. I’m just sharing it in case someone else finds it useful or wants to improve on it.

3 Likes

Thanks for this!! Been hoping Stash would add subtitle support but this works perfectly

Hi, Ive been using stash for a while and everything was running fine before.

I now have issues with multiple plugins using graphql returning http error 401.

Here is a example log of one plugin:

2026-04-25 21:14:08
Error   
Plugin returned error: exit status 1
2026-04-25 21:14:08
Error   
[Plugin / ASS Subtitles] urllib.error.HTTPError: HTTP Error 401: Unauthorized
2026-04-25 21:14:08
Error   
[Plugin / ASS Subtitles]     raise HTTPError(req.full_url, code, msg, hdrs, fp)
2026-04-25 21:14:08
Error   
[Plugin / ASS Subtitles]   File "C:\Users\svand\AppData\Local\Python\pythoncore-3.14-64\Lib\urllib\request.py", line 611, in http_error_default
2026-04-25 21:14:08
Error   
[Plugin / ASS Subtitles]     result = func(*args)
2026-04-25 21:14:08
Error   
[Plugin / ASS Subtitles]   File "C:\Users\svand\AppData\Local\Python\pythoncore-3.14-64\Lib\urllib\request.py", line 464, in _call_chain
2026-04-25 21:14:08
Error   
[Plugin / ASS Subtitles]            ~~~~~~~~~~~~~~~~^^^^^^^
2026-04-25 21:14:08
Error   
[Plugin / ASS Subtitles]     return self._call_chain(*args)
2026-04-25 21:14:08
Error   
[Plugin / ASS Subtitles]   File "C:\Users\svand\AppData\Local\Python\pythoncore-3.14-64\Lib\urllib\request.py", line 531, in error
2026-04-25 21:14:08
Error   
[Plugin / ASS Subtitles]         'http', request, response, code, msg, hdrs)
2026-04-25 21:14:08
Error   
[Plugin / ASS Subtitles]     response = self.parent.error(
2026-04-25 21:14:08
Error   
[Plugin / ASS Subtitles]   File "C:\Users\svand\AppData\Local\Python\pythoncore-3.14-64\Lib\urllib\request.py", line 602, in http_response
2026-04-25 21:14:08
Error   
[Plugin / ASS Subtitles]     response = meth(req, response)
2026-04-25 21:14:08
Error   
[Plugin / ASS Subtitles]   File "C:\Users\svand\AppData\Local\Python\pythoncore-3.14-64\Lib\urllib\request.py", line 493, in open
2026-04-25 21:14:08
Error   
[Plugin / ASS Subtitles]            ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^
2026-04-25 21:14:08
Error   
[Plugin / ASS Subtitles]     return opener.open(url, data, timeout)
2026-04-25 21:14:08
Error   
[Plugin / ASS Subtitles]   File "C:\Users\svand\AppData\Local\Python\pythoncore-3.14-64\Lib\urllib\request.py", line 187, in urlopen
2026-04-25 21:14:08
Error   
[Plugin / ASS Subtitles]          ~~~~~~~~~~~~~~~~~~~~~~^^^^^
2026-04-25 21:14:08
Error   
[Plugin / ASS Subtitles]     with urllib.request.urlopen(req) as resp:
2026-04-25 21:14:08
Error   
[Plugin / ASS Subtitles]   File "F:\data\stash\plugins\ass-subtitles/extract_subtitles.py", line 30, in graphql_request
2026-04-25 21:14:08
Error   
[Plugin / ASS Subtitles]     count_result = graphql_request(count_query)
2026-04-25 21:14:08
Error   
[Plugin / ASS Subtitles]   File "F:\data\stash\plugins\ass-subtitles/extract_subtitles.py", line 209, in process_all_scenes
2026-04-25 21:14:08
Error   
[Plugin / ASS Subtitles]     ~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^
2026-04-25 21:14:08
Error   
[Plugin / ASS Subtitles]     process_all_scenes(stash_settings)
2026-04-25 21:14:08
Error   
[Plugin / ASS Subtitles]   File "F:\data\stash\plugins\ass-subtitles/extract_subtitles.py", line 383, in main
2026-04-25 21:14:08
Error   
[Plugin / ASS Subtitles]     ~~~~^^
2026-04-25 21:14:08
Error   
[Plugin / ASS Subtitles]     main()
2026-04-25 21:14:08
Error   
[Plugin / ASS Subtitles]   File "F:\data\stash\plugins\ass-subtitles/extract_subtitles.py", line 387, in <module>
2026-04-25 21:14:08
Error   
[Plugin / ASS Subtitles] Traceback (most recent call last):

I did not change anything about stash or network related stuff. Running on Windows 11 without VPN.
I can test my api credentials with porndb and that works fine, but stashdb also seems not to respond.

can anyone help me please?
If you need more information please ask!

updated the plugin to use stashapp-tools for authenitcation. think that should solve it

No not working. I think it has nothing to do with this specific plugin, thats why i created a seperate thread here but it was merged. I don’t understand what’s happening. Its multiple plugins and something to do with http error 401 and tls timeout

https://discourse.stashapp.cc/t/need-help-with-http-error-401/6945

eventually solved the problem with the help of chatgpt. it turned out to be a ipv6 routing issue. This powershell command fixed it:

Disable-NetAdapterBinding -Name "*" -ComponentID ms_tcpip6

How does the “Extract Subtitles for Scene” function work?

It says “Extracts subtitles for a specific scene (pass scene_id in args).”
However when I press the button I get no popup or any field in which to enter the scene_id that I want to extract the subs on.

Running on Linux (Ubuntu), in a Docker container (Hotio’s image).

The plugin task button in Stash’s UI has no way to prompt for a scene_id — it’s intended for API/GraphQL use only, not end users. The fix is to have the plugin inject its own :down_arrow:CC button directly into the video player toolbar when you’re on a scene page that has no subtitles yet. Since you’re already on that scene’s URL, the plugin knows the ID automatically — no input needed. Click it, it extracts, then switches to the normal CC toggle when done. No popup, no manual ID entry.