From f6fb40e3926c61b1de32f0a31f41111e7b017118 Mon Sep 17 00:00:00 2001 From: nd4y <106557904+nd4y@users.noreply.github.com> Date: Mon, 10 Aug 2026 22:36:05 +0300 Subject: [PATCH] AudioPlayerLite: register the ScenePlayer patch only once The patch was registered inside the stash:location handler, so every navigation to a scene pushed another copy onto insteadFns["ScenePlayer"]. With more than one patch in the chain, runInstead() passes the next patch as an extra trailing argument, so the second copy receives the original component as its second argument and reads undefined as the third one, failing with "TypeError: originalComponent is not a function". Only the first scene opened after a page load worked; every later one errored out until the page was reloaded. Register the patch once, and render the original component through React.createElement instead of calling it with a single argument, so the patch chain keeps working if another plugin patches ScenePlayer too. Also call setStyle() instead of the undefined poster(), and guard against scenes with no files. --- plugins/AudioPlayerLite/AudioPlayerLite.js | 43 +++++++++++++-------- plugins/AudioPlayerLite/AudioPlayerLite.yml | 2 +- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/plugins/AudioPlayerLite/AudioPlayerLite.js b/plugins/AudioPlayerLite/AudioPlayerLite.js index 9e7b63ce..22e05d96 100644 --- a/plugins/AudioPlayerLite/AudioPlayerLite.js +++ b/plugins/AudioPlayerLite/AudioPlayerLite.js @@ -12,24 +12,35 @@ } } + // patch.instead() appends to a list of patches, so registering on every + // navigation stacks up copies of this patch and breaks the patch chain. + let patched = false; + PluginApi.Event.addEventListener("stash:location", async (e) => { + if (patched) return; + const path = e.detail.data.location.pathname; const idRegExp = /.*\/scenes\/(\d+)/; - if (idRegExp.test(path)) { - await PluginApi.utils.loadComponents([ - PluginApi.loadableComponents.ScenePlayer - ]); - PluginApi.patch.instead("ScenePlayer", function (props, _, originalComponent) { - const file = props.scene.files[0]; - let scene = props.scene; - if (file.video_codec === "") { - scene = { ...scene, - sceneStreams: props.scene.sceneStreams.filter((ss) => ss.label.toUpperCase() === 'HSL') - }; - poster() - } - return originalComponent({ ...props, scene }); - }); - } + if (!idRegExp.test(path)) return; + + await PluginApi.utils.loadComponents([ + PluginApi.loadableComponents.ScenePlayer + ]); + + // re-check: another navigation event may have been handled while awaiting + if (patched) return; + patched = true; + + PluginApi.patch.instead("ScenePlayer", function (props, _, originalComponent) { + const file = props.scene && props.scene.files && props.scene.files[0]; + let scene = props.scene; + if (file && file.video_codec === "") { + scene = { ...scene, + sceneStreams: props.scene.sceneStreams.filter((ss) => ss.label.toUpperCase() === 'HSL') + }; + setStyle() + } + return React.createElement(originalComponent, { ...props, scene }); + }); }); })(); diff --git a/plugins/AudioPlayerLite/AudioPlayerLite.yml b/plugins/AudioPlayerLite/AudioPlayerLite.yml index 8b2174f8..937b0394 100644 --- a/plugins/AudioPlayerLite/AudioPlayerLite.yml +++ b/plugins/AudioPlayerLite/AudioPlayerLite.yml @@ -1,6 +1,6 @@ name: AudioPlayerLite description: This plugin identifies files with no video codec and plays them as audio. -version: 0.1 +version: 0.2 url: https://discourse.stashapp.cc/t/audioplayerlite/1329 ui: javascript: