StashApp/Stash-Vr Enhancements - StashInteractiveTools

@sithsam covered this (thanks for the assist). But the plugin has a next (non-stable, new hotness) and a main (stable). It’s been a while since I made a new stable release as I needed some feedback from the community. So if you want to always have the new maybe not working version use the next source URL.

Also a note if you run into an issue , check the logs and if it says “missing deps” or “can’t find import” or something like that go to the Task section in stash settings and run the install task for SIT.

1 Like

Thanks @xtc and @sithsam, updating the source yaml worked!

I Discovered that Stash’s videojs-vr player doesn’t support immersive/webXR mode

I made a feature request for it here: [Feature] Support immersive VR video from browser with webXR · Issue #6071 · stashapp/stash · GitHub
And a Stash discourse thread here: webXR/Immersive mode not Working - #2 by feederbox826 - Support - Stash

But i was wondering @xtc if this might be easy for you to integrate into a plugin to enable browser-based immersive mode for those of us who dont have VR devices that support heresphere or deoVR.

I’m not a webdev, but it seems like all that’s needed is to replace the videojs-vr video player when Stash tags the content as VR with the videojs-xr player provided by the github library GitHub - thomasdeppisch/videojs-xr: Plugin for using WebXR with videojs, based on videojs-vr

This would immediately enable me to play my stash VR videos on both my android device (google cardboard/GearVR) and Apple Vision Pro device just using the stash browser

2 Likes

Last time I tried to get this working it didn’t render correctly at all but that was using videojs-vr and a few forks like this one Adding WebXR Layers by kevleyski · Pull Request #288 · videojs/videojs-vr · GitHub. I can look into using the videojs-xr plugin.

1 Like

btw @xtc the github page for StashInteractiveTools still has

" Custom Build version of stash"

in the Requirements but it looks like that PR has been merged? can we safely use the regular stash now?

also is " Python Tools Installer" plugin still needed? im not sure what to do after I run it to get it in my docker. Do i need to install and run it first and then do the Init, tag, remap stuff?

as an aside, is there a reason StashInteractiveTools requires node <21?

Yes, the standard Stash build now works.

You don’t need to run ‘init’ I think, that’ll run automatically. (XTC will correct me if I’m wrong)

Tag can be run anytime you want to update the ‘multiple’ funscript tag. (it’ll add/remove as needed)
If you don’t care about tagging, you can skip that.

Remap replaces the standard heatmap with my custom version. You still NEED to run the normal generate heatmap FIRST to get new scenes recognized as being ‘interactive’. I might do something about that, but honestly, probably not. Remap currently runs against your ENTIRE library, and re-heatmaps (and optionally does some forms of cleanup as well). I might move the cleanup to a new function, but since remap has to do much of the same work, it’s faster to just do it all together. I couldn’t think of a way to avoid the ‘do them all again’ problem… And since we’re replacing the existing heatmap png, it’s not like I can only do ‘missing ones’ (which is what the stock generate does). Luckily it’s not slow…

I just (mostly) finished a ‘I have 2+ scene files, and they don’t have the funscripts the other has, fix that’ solution. (so if you have your funscript on a 4k scene, and now you got the 8k scene… and are tired of manually copying funscripts around…). It’s working code, and once I am sure it’s useful and bugfree, I’ll submit a patch to XTC. I’m using it to consolidate lots of acquired content.
It makes ‘merging’ quite useful. merge 4k to 8k file, it gets all of the metadata… (then you can delete the 4k, and the 8k has it’s own funscript(s), so you’re all set. Not quite perfect yet, though. Spent a lot of time debugging and finetuning how to handle edge cases.

1 Like

Hi @xtc and @Scruffynerf
Wondering if you’re interested in helping me merge this:

I took the work @Spunkle did with the browser script and forked StashInteractiveTools, then had Gemini 2.5 Pro take a look at the repository and the browser script and figure out how to combine them. It took a bit of back-and-forth but I got it in a working state today. I’m not a webdev and i’ve only toyed with React very briefly back in 2017, but i am a software engineer so I was able to work through the issues with Gemini’s help.

The patch adds a plugin setting for the address of the TCodeESP32 device running Khrull’s firmware

When you load a scene with funscripts it tries to connect to the TcodeESP32 device at the address you entered and then finds all of them for L0, L1, L2, R0, R1, R2 and then sets them to enabled

It’s working as expected and I was able to use it to wireless control my SR6 while browsing my local stash server on my Apple Vision Pro (Immersive VR is not working in stash natively so i just watched 2D videos, hence my request to XTC to look into adding support for videojs-xr to get webXR working on stash natively).

But I was able to successfully browse different single and multi-script videos, it connects each time and sync properly with the video, though sometimes i find it can be very jumpy. Right now i think it’s just finding the TCode commands on a 10millisecond loop, not sure if there is a smarter way to do this but the TCodeESP32 firmware only accepts the raw tcode commands, you can’t send it the files and and a sync time (maybe that would be a cool extension to the device firmware).

Anyway, huge thank you to @Spunkle for working out the way to send the commands, i couldn’t havre done it without his work as a springboard.

This patch has a ton of clean up work needed but since i’m not really a web dev it’s probably better if one of you take over from here if you’re willing to help.

In particular Gemini totally ripped up src/hooks/useInteractiveTools.tsx to make this work. I spent a lot of time going back and forth with it on this file to make it function properly, it really had no idea how to access the funscripts that stash-interactive-tools provides. Eventually I was able to eek it out but it probably is not the best implementation. Also I have no clue whether this has fucked up the Handy integration or IVDB stuff. I don’t have a Handy to test with and I honestly wasnt sure what the IVDB specific stuff was doing, all my funscripts are locally stored on my NAS that runs stash.

Right now i think the client device is sending the TCode commands every 10ms, but i was wondering if there is a way to actually plug in to the server-side code so the server is sending the commands. My reasoning is that it’s likely that the server has a much more stable and faster connection so you can cut one half of the equation (Sending the commands) out of the equation, then it becomes an issue of the ESP32 device receiving them wirelessly

1 Like

I dont have a device that supports the tcode protocol to test but if @Scruffynerf can test it out and sign off I have no problem including it. Overall the code looks pretty damn straight forward . Crazy what AI can do lol.

Actually I sorta want to find a way to allow this to be a plugin vs embed into the core, mostly due to me not being able to test it and ensure I’m not breaking anything lol. I’ll have to think about how to build a plugin framework that can hook into the lifecycle.

Me either… i wish.

Version 1.1.0-next.5 Has been released.
It now tries to detect handy tokens that are loaded from the file system and apply the fix so that they work in stash.
image

Technically all you need if you want to verify it is an ESP32 device with the firmware loaded, you don’t need the whole OSR2/SR6. You can just check the device for logs of the commands its receiving. You can get them pretty cheap on eBay or AliExpress. If you need help setting it up I could lend a hand

This is pretty great!
The only gripe I have is that I keep my funscripts separate from my media.
Might tinker around in the code and add a way to scan those files from a root directory.

Also t-code wise, it’s just serial data. You can use com0com or another virtual comm port to dump traffic. I might take a look at that as well, though I have basically zero free time so don’t quote me on that.

If you want I don’t mind giving this a test. Need to get it all set up first though.

Nah have at it, willing to add it in I just can’t confirm its not breaking anything etc. If it does work I may refactor it a bit so its pretty isolated from the rest of the code.

Also think I’m going to start plugging into the stash patchable system for some of these elements so other ppl can add their own features if I don’t have time. Just a random thought but I’ve been wanting to solve the issue that @plasmarobo brought up with a true script mapper/editor but soo much work and lack of time :frowning: . This isn’t the first time someone brought this up

1 Like

would there be any way to make stash serve the selected funscript ? I’m using multifunplayer, not a handy

1 Like

That’s what my script does Stash-TCode

Or the edit that TaakoMagnusen made of this plugin integrating some parts of my script.

I recently updated to the latest version of Stash and now my scripts don’t even play through stash anymore. Is anyone else having this problem?

I’m not personally, does it go away after downgrading?

Any Mac users in here? I’m running into python errors simply running the install after adding the plug-in to Stash

macOS 26.1
Python 3.14.2

Silly me, I didn’t catch the error way at the bottom. Sorry about that. But to fix you need to install the stashapp-tools dependency. You can do this from the install task for the SIT plugin. I’m not at my desk ATM but I think it’s under the Tasks section in task settings.. all the way at the bottom you should see Stash Interactive Tools

It should look similar to this (this is an old screenshot before the install task was added but still same look)

Click on the install button and hopefully that error is no longer there and plugin works correctly when viewing a scene.

This is great! One thing I have been looking for for quite a while is a feature that handycontrol had at some point (it broke and is no longer supported), which is the ability to change to manual mode or a predefined pattern/preset during video playback through the push of a button and then changing back to the script at any point. Is that something that could be implemented into the stash plugin theoretically?

edit:
Unfortunately I’m having trouble setting this up. I installed it via the plugin path which worked fine. I have python 3.10 installed and the python tools installer plugin.

Handy worked with stash before I tried installing the plugins. When I try to start a video, it says “something went wrong” with the following details:

TypeError: Cannot read properties of undefined (reading ‘alwaysDefaultToStashSyncOffset’)
at Bn (http://localhost:9999/plugin/StashInteractiveTools/javascript:11:44829)
at qn
at Jn (http://localhost:9999/plugin/StashInteractiveTools/javascript:11:48314)
at dl
at Object.apply (http://localhost:9999/assets/index-CcPORYqs.js:53:62972)
at div
at t (http://localhost:9999/assets/index-CcPORYqs.js:49:3585)
at http://localhost:9999/assets/index-CcPORYqs.js:49:11796
at http://localhost:9999/assets/index-CcPORYqs.js:49:106808
at Object.apply (http://localhost:9999/assets/index-CcPORYqs.js:53:62972)
at div
at http://localhost:9999/assets/index-CcPORYqs.js:49:105926
at TR (http://localhost:9999/assets/index-CcPORYqs.js:49:105310)
at div
at Object.apply (http://localhost:9999/assets/index-CcPORYqs.js:53:62972)
at div
at Wn (http://localhost:9999/assets/Scene-Ccu55IXj.js:2:11446)
at t (http://localhost:9999/assets/index-CcPORYqs.js:41:9242)
at t (http://localhost:9999/assets/index-CcPORYqs.js:41:11195)
at E (http://localhost:9999/assets/Scenes-Bi-VfrAX.js:2:1148)
at t (http://localhost:9999/assets/index-CcPORYqs.js:41:9242)
at t (http://localhost:9999/assets/index-CcPORYqs.js:41:11195)
at Suspense
at DZ (http://localhost:9999/assets/index-CcPORYqs.js:53:364413)
at div
at _1e (http://localhost:9999/assets/index-CcPORYqs.js:237:4751)
at h$1 (http://localhost:9999/assets/index-CcPORYqs.js:81:491547)
at mP1 (http://localhost:9999/assets/index-CcPORYqs.js:58:4146)
at ig1 (http://localhost:9999/assets/index-CcPORYqs.js:53:66639)
at Suspense
at dP1 (http://localhost:9999/assets/index-CcPORYqs.js:58:3948)
at Object.apply (http://localhost:9999/assets/index-CcPORYqs.js:53:62972)
at ttt (http://localhost:9999/assets/index-CcPORYqs.js:91:36934)
at ng1 (http://localhost:9999/assets/index-CcPORYqs.js:53:64630)
at t (http://localhost:9999/assets/index-CcPORYqs.js:41:73962)
at DZ (http://localhost:9999/assets/index-CcPORYqs.js:53:364413)
at H0t (http://localhost:9999/assets/index-CcPORYqs.js:237:5052)
at a3e (http://localhost:9999/assets/index-CcPORYqs.js:20:203318)
at t (http://localhost:9999/assets/index-CcPORYqs.js:41:5754)
at t (http://localhost:9999/assets/index-CcPORYqs.js:41:12270)

and the logs say this:

025-12-29 12:01:55
Error
runPluginOperation: input: runPluginOperation exit status 1
2025-12-29 12:01:55
Error
[Plugin / Stash Interactive Tools] TypeError: run() missing 1 required positional argument: ‘c’
2025-12-29 12:01:55
Error
[Plugin / Stash Interactive Tools] run()
2025-12-29 12:01:55
Error
[Plugin / Stash Interactive Tools] File “C:\Users\Z.stash\plugins\StashInteractiveTools\config.py”, line 61, in get_stash_client
2025-12-29 12:01:55
Error
[Plugin / Stash Interactive Tools] stash = get_stash_client()
2025-12-29 12:01:55
Error
[Plugin / Stash Interactive Tools] File “C:\Users\Z.stash\plugins\StashInteractiveTools\config.py”, line 88, in get_config
2025-12-29 12:01:55
Error
[Plugin / Stash Interactive Tools] config = get_config()
2025-12-29 12:01:55
Error
[Plugin / Stash Interactive Tools] File “C:\Users\Z.stash\plugins\StashInteractiveTools\StashInteractiveTools.py”, line 10, in main
2025-12-29 12:01:55
Error
[Plugin / Stash Interactive Tools] main()
2025-12-29 12:01:55
Error
[Plugin / Stash Interactive Tools] File “C:\Users\Z.stash\plugins\StashInteractiveTools\StashInteractiveTools.py”, line 30, in
2025-12-29 12:01:55
Error
[Plugin / Stash Interactive Tools] Traceback (most recent call last):
2025-12-29 12:01:55
Error
[Plugin / Stash Interactive Tools] During handling of the above exception, another exception occurred:
2025-12-29 12:01:55
Error
[Plugin / Stash Interactive Tools] ModuleNotFoundError: No module named ‘stashapi’
2025-12-29 12:01:55
Error
[Plugin / Stash Interactive Tools] import stashapi.log as log
2025-12-29 12:01:55
Error
[Plugin / Stash Interactive Tools] File “C:\Users\Z.stash\plugins\StashInteractiveTools\config.py”, line 57, in get_stash_client
2025-12-29 12:01:55
Error
[Plugin / Stash Interactive Tools] Traceback (most recent call last):

Any ideas?