From 621c58dac50628de4ede2e624679967d4eab80a0 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 4 Mar 2026 13:27:40 -0500 Subject: [PATCH 01/12] Added additional plugin support, patched additional fields previously left uncensored. --- plugins/SFWSwitch/README.md | 1 + plugins/SFWSwitch/additional_plugins.css | 65 ++++++++++++++++++++++++ plugins/SFWSwitch/sfw.css | 22 +++++++- plugins/SFWSwitch/sfwswitch.yml | 5 +- 4 files changed, 89 insertions(+), 4 deletions(-) create mode 100644 plugins/SFWSwitch/additional_plugins.css diff --git a/plugins/SFWSwitch/README.md b/plugins/SFWSwitch/README.md index 874e2f78..67e6c363 100644 --- a/plugins/SFWSwitch/README.md +++ b/plugins/SFWSwitch/README.md @@ -9,6 +9,7 @@ https://discourse.stashapp.cc/t/sfw-switch/4658 - Gray = Blur disabled - Toggling the button blurs cover images and other content. - Hovering over an image temporarily removes the blur. +- Public Additional Plugins support. Add your plugin to the additional_plugins.css file for additional compatibility. ## Screenshots diff --git a/plugins/SFWSwitch/additional_plugins.css b/plugins/SFWSwitch/additional_plugins.css new file mode 100644 index 00000000..6ce5d6fb --- /dev/null +++ b/plugins/SFWSwitch/additional_plugins.css @@ -0,0 +1,65 @@ +/* [Global changes] Blur NSFW images and unblur on mouse over */ + +/*Credit: fl0w#9497 */ + +/* === MORE BLUR === */ + +/* Stash Battle */ +.pwr-scene-image-container, +.pwr-scene-image-container, +.pwr-hover-preview, +.pwr-scene-image-container .pwr-scene-image, + +/* HotOrNot */ +.hon-performer-image, +.hon-scene-image, +.hon-image-image-container, +.hon-image-image, + +/* O Stats */ +.custom-stats-row .stats-element img, +#on-this-day-section [style*="position: relative; height: 400px"] +{ +filter: blur(30px); +} + +/* === LESS BLUR === */ + +/* StashBattle */ +.pwr-scene-info, + +/* HotOrNot */ +.hon-performer-info.hon-scene-info, + +/* O Stats */ +.custom-stats-row .stats-element, +#on-this-day-section [style*="display: flex"][style*="cursor: pointer"] img, +#on-this-day-section [style*="display: flex"][style*="cursor: pointer"] img + div, +#on-this-day-section > div:last-child +{ +filter: blur(2px); +} + +/* StashBattle */ +.pwr-scene-image-container:hover, +.pwr-scene-image-container:hover .pwr-hover-preview, +.pwr-scene-image-container:hover .pwr-scene-image, +.pwr-scene-info:hover, + +/* HotOrNot */ +.hon-performer-image:hover, +.hon-scene-image:hover, +.hon-image-image-container:hover, +.hon-image-image:hover, +.hon-performer-info.hon-scene-info:hover, + +/* O Stats */ +.custom-stats-row .stats-element:hover, +.custom-stats-row .stats-element:hover img, +#on-this-day-section [style*="display: flex"][style*="cursor: pointer"]:hover img, +#on-this-day-section [style*="display: flex"][style*="cursor: pointer"]:hover img + div, +#on-this-day-section > div:last-child:hover, +#on-this-day-section [style*="position: relative; height: 400px"]:hover +{ +filter: blur(0px); +} diff --git a/plugins/SFWSwitch/sfw.css b/plugins/SFWSwitch/sfw.css index a8cc23e3..bf4f0a8c 100644 --- a/plugins/SFWSwitch/sfw.css +++ b/plugins/SFWSwitch/sfw.css @@ -3,6 +3,7 @@ /*Credit: fl0w#9497 */ /* === MORE BLUR === */ + /* scene */ .scene-card-preview, .vjs-poster, @@ -34,11 +35,13 @@ img.performer, /* tag */ .tag-card-image + { filter: blur(30px); } /* === LESS BLUR === */ + /* common */ .card-section-title, @@ -48,6 +51,7 @@ filter: blur(30px); h3.scene-header, .studio-logo, .image-thumbnail, +.TruncatedText.scene-card__description, /* image */ h3.image-header, @@ -57,20 +61,26 @@ h3.image-header, /* gallery */ h3.gallery-header, +.TruncatedText.gallery-card__description, /* studio */ .studio-details .logo, .studio-details > div > h2, +.studio-card__details, +.studio-parent-studios, /* tag */ .logo-container > .logo, -.logo-container > h2 +.logo-container > h2, +.TruncatedText.tag-description, +.tag-parent-tags { filter: blur(2px); } /* === UNBLUR ON HOVER === */ + /* common */ .thumbnail-section:hover *, .card:hover .card-section-title, @@ -87,6 +97,7 @@ div:hover > .scene-header, .scene-card-preview:hover, .scrubber-item:hover, .scene-image:hover, +.TruncatedText.scene-card__description:hover, /* image */ .image-image:hover, @@ -100,6 +111,7 @@ div:hover > .image-header, /* gallery */ div:hover > .gallery-header, table > tbody > tr > td:hover > a > img.w-100, +.TruncatedText.gallery-card__description:hover, /* performer */ img.performer:hover, @@ -107,10 +119,16 @@ img.performer:hover, /* studio */ .studio-details .logo:hover, .studio-details:hover > div > h2, +.studio-card__details:hover, +.studio-parent-studios:hover, + /* tag */ .logo-container > .logo:hover, -.logo-container:hover > h2 +.logo-container:hover > h2, +.TruncatedText.tag-description:hover, +.tag-parent-tags:hover + { filter: blur(0px); } diff --git a/plugins/SFWSwitch/sfwswitch.yml b/plugins/SFWSwitch/sfwswitch.yml index 9c4b7171..f8d576b1 100644 --- a/plugins/SFWSwitch/sfwswitch.yml +++ b/plugins/SFWSwitch/sfwswitch.yml @@ -1,9 +1,10 @@ name: SFW Switch description: Add a button to blur covers and images. -version: 1.3 +version: 1.4 url: https://discourse.stashapp.cc/t/sfw-switch/4658 ui: javascript: - sfw.js css: - - sfw.css \ No newline at end of file + - sfw.css + - additional_plugins.css \ No newline at end of file From 3b11e912df643c63f9dd88ab7b79c903354847ef Mon Sep 17 00:00:00 2001 From: DogmaDragon <103123951+DogmaDragon@users.noreply.github.com> Date: Thu, 5 Mar 2026 14:13:02 +0200 Subject: [PATCH 02/12] Update README Clarified instructions for adding custom selectors to additional_plugins.css. --- plugins/SFWSwitch/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/SFWSwitch/README.md b/plugins/SFWSwitch/README.md index 67e6c363..02cb3507 100644 --- a/plugins/SFWSwitch/README.md +++ b/plugins/SFWSwitch/README.md @@ -9,7 +9,8 @@ https://discourse.stashapp.cc/t/sfw-switch/4658 - Gray = Blur disabled - Toggling the button blurs cover images and other content. - Hovering over an image temporarily removes the blur. -- Public Additional Plugins support. Add your plugin to the additional_plugins.css file for additional compatibility. +- Extends the blurring functionality to some community plugins. + - Custom selectors should should be added to `additional_plugins.css` file. ## Screenshots @@ -18,4 +19,4 @@ https://discourse.stashapp.cc/t/sfw-switch/4658 ## Credit Original plugin by Belleyy [here](https://github.com/Belleyy/CommunityScripts/tree/pluginUI_SFWSwitch/plugins/SFW%20Switch). -The CSS code used is provided by fl0w#9497 [here](https://discourse.stashapp.cc/t/custom-css-snippets/4043#p-8143-blur-nsfw-images-and-unblur-on-mouse-over-41). \ No newline at end of file +The CSS code used is provided by fl0w#9497 [here](https://discourse.stashapp.cc/t/custom-css-snippets/4043#p-8143-blur-nsfw-images-and-unblur-on-mouse-over-41). From 31dda12139c2756ac2fc27fe4fd70420b99936d0 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 7 Mar 2026 08:58:48 -0500 Subject: [PATCH 03/12] SFW Switch now mutes all sound when enabled by default. --- plugins/SFWSwitch/additional_plugins.css | 8 +- plugins/SFWSwitch/sfw.js | 94 ++++++++++++++++++++++-- plugins/SFWSwitch/sfwswitch.yml | 2 +- 3 files changed, 95 insertions(+), 9 deletions(-) diff --git a/plugins/SFWSwitch/additional_plugins.css b/plugins/SFWSwitch/additional_plugins.css index 6ce5d6fb..de8c494f 100644 --- a/plugins/SFWSwitch/additional_plugins.css +++ b/plugins/SFWSwitch/additional_plugins.css @@ -18,7 +18,10 @@ /* O Stats */ .custom-stats-row .stats-element img, -#on-this-day-section [style*="position: relative; height: 400px"] +#on-this-day-section [style*="position: relative; height: 400px"], + +/* Sprite Tab */ +.sprite-cell { filter: blur(30px); } @@ -53,6 +56,9 @@ filter: blur(2px); .hon-image-image:hover, .hon-performer-info.hon-scene-info:hover, +/* Sprite Tab */ +.sprite-cell:hover, + /* O Stats */ .custom-stats-row .stats-element:hover, .custom-stats-row .stats-element:hover img, diff --git a/plugins/SFWSwitch/sfw.js b/plugins/SFWSwitch/sfw.js index 2410ade2..cc2637fa 100644 --- a/plugins/SFWSwitch/sfw.js +++ b/plugins/SFWSwitch/sfw.js @@ -1,3 +1,6 @@ +let sfw_mediaObserver = null; +let sfw_playListener = null; + function sfw_mode() { const stash_css = sfwswitch_findstashcss(); const button = document.getElementById("plugin_sfw"); @@ -6,10 +9,14 @@ function sfw_mode() { const sfwState = localStorage.getItem("sfw_mode") === "true"; - // Apply saved state to the stylesheet stash_css.disabled = !sfwState; - // Update button color + if (sfwState) { + sfw_mute_all_media(); + } else { + sfw_unmute_all_media(); + } + button.style.color = sfwState ? "#5cff00" : "#f5f8fa"; } @@ -45,6 +52,72 @@ function sfwswitch_createbutton() { setTimeout(() => clearInterval(intervalId), 10000); } +function sfw_forceMute(media) { + media.muted = true; + media.defaultMuted = true; + media.volume = 0; +} + +function sfw_mute_all_media() { + + // Mute existing media + document.querySelectorAll("audio, video").forEach(sfw_forceMute); + + // Mute media when it starts playing + if (!sfw_playListener) { + sfw_playListener = function(e) { + if (e.target.tagName === "VIDEO" || e.target.tagName === "AUDIO") { + sfw_forceMute(e.target); + } + }; + + document.addEventListener("play", sfw_playListener, true); + } + + // Watch for new media nodes + if (!sfw_mediaObserver) { + + sfw_mediaObserver = new MutationObserver(mutations => { + mutations.forEach(mutation => { + mutation.addedNodes.forEach(node => { + + if (node.tagName === "VIDEO" || node.tagName === "AUDIO") { + sfw_forceMute(node); + } + + if (node.querySelectorAll) { + node.querySelectorAll("video, audio").forEach(sfw_forceMute); + } + + }); + }); + }); + + sfw_mediaObserver.observe(document.body, { + childList: true, + subtree: true + }); + } +} + +function sfw_unmute_all_media() { + + document.querySelectorAll("audio, video").forEach(media => { + media.muted = false; + media.volume = 1; + }); + + if (sfw_mediaObserver) { + sfw_mediaObserver.disconnect(); + sfw_mediaObserver = null; + } + + if (sfw_playListener) { + document.removeEventListener("play", sfw_playListener, true); + sfw_playListener = null; + } +} + function sfwswitch_switcher() { const stash_css = sfwswitch_findstashcss(); if (!stash_css) { @@ -52,15 +125,22 @@ function sfwswitch_switcher() { return; } - // Toggle stylesheet stash_css.disabled = !stash_css.disabled; - // Save new state to localStorage - localStorage.setItem("sfw_mode", !stash_css.disabled); + const enabled = !stash_css.disabled; + + localStorage.setItem("sfw_mode", enabled); + + if (enabled) { + sfw_mute_all_media(); + } else { + sfw_unmute_all_media(); + } const button = document.getElementById("plugin_sfw"); - button.style.color = stash_css.disabled ? "#f5f8fa" : "#5cff00"; - console.log(`SFW mode ${stash_css.disabled ? "disabled" : "enabled"}`); + button.style.color = enabled ? "#5cff00" : "#f5f8fa"; + + console.log(`SFW mode ${enabled ? "enabled" : "disabled"}`); } function sfwswitch_findstashcss() { diff --git a/plugins/SFWSwitch/sfwswitch.yml b/plugins/SFWSwitch/sfwswitch.yml index f8d576b1..10e94b4d 100644 --- a/plugins/SFWSwitch/sfwswitch.yml +++ b/plugins/SFWSwitch/sfwswitch.yml @@ -1,6 +1,6 @@ name: SFW Switch description: Add a button to blur covers and images. -version: 1.4 +version: 1.5 url: https://discourse.stashapp.cc/t/sfw-switch/4658 ui: javascript: From 36e39aa9c0c0ca33cbef1018d5f9ec7981a912f2 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 9 Mar 2026 18:22:48 -0400 Subject: [PATCH 04/12] Refactored main css, all UI functions should be accouted for now. Updated plugin yaml description --- plugins/SFWSwitch/sfw.css | 180 +++++++++++--------------------- plugins/SFWSwitch/sfwswitch.yml | 2 +- 2 files changed, 62 insertions(+), 120 deletions(-) diff --git a/plugins/SFWSwitch/sfw.css b/plugins/SFWSwitch/sfw.css index bf4f0a8c..de862601 100644 --- a/plugins/SFWSwitch/sfw.css +++ b/plugins/SFWSwitch/sfw.css @@ -1,134 +1,76 @@ -/* [Global changes] Blur NSFW images and unblur on mouse over */ - -/*Credit: fl0w#9497 */ - -/* === MORE BLUR === */ - -/* scene */ -.scene-card-preview, -.vjs-poster, -video, -.scene-cover, -.scrubber-item, -.scene-image, - -/* image */ -.image-card-preview, -.image-image, -.gallery-image, - -/* group */ -.group-card-image, -.group-images, - -/* gallery */ -.gallery-card-image, -table > tbody > tr > td > a > img.w-100, - -/* performer */ -.performer-card-image, -img.performer, - -/* studio */ -.studio-card-image, - -/* tag */ -.tag-card-image - - -{ -filter: blur(30px); +/* Container*/ +.tag-card, +.scene-card, +.image-card, +.gallery-card, +.group-card, +.thumbnail-container, +.gallery-cover, +.performer-card, +.studio-card { + position: relative; + display: inline-block; } /* === LESS BLUR === */ /* common */ .card-section-title, - -/* scene */ -.scene-studio-overlay, -.scene-header > h3, -h3.scene-header, -.studio-logo, -.image-thumbnail, -.TruncatedText.scene-card__description, - -/* image */ -h3.image-header, - -/* group */ -.group-details > div > h2, - -/* gallery */ -h3.gallery-header, +.TruncatedText.tag-description, .TruncatedText.gallery-card__description, - -/* studio */ -.studio-details .logo, -.studio-details > div > h2, -.studio-card__details, +.TruncatedText.image-card__description, +.TruncatedText.scene-card__description, +.tag-name, +.detail-item-value.description, .studio-parent-studios, - -/* tag */ -.logo-container > .logo, -.logo-container > h2, -.TruncatedText.tag-description, -.tag-parent-tags - +.detail-item-value, +.studio-name, +.queue-scene-details { filter: blur(2px); } -/* === UNBLUR ON HOVER === */ - -/* common */ -.thumbnail-section:hover *, -.card:hover .card-section-title, - -/* scene */ -.card:hover .scene-studio-overlay, -.video-js:hover .vjs-poster, -video:hover, -.scene-header:hover > h3, -div:hover > .scene-header, -.studio-logo:hover, -.scene-cover:hover, -.image-thumbnail:hover, -.scene-card-preview:hover, -.scrubber-item:hover, -.scene-image:hover, -.TruncatedText.scene-card__description:hover, - -/* image */ -.image-image:hover, -div:hover > .image-header, -.gallery-image:hover, - -/* group */ -.group-images:hover, -.group-details > div > h2:hover, +/* Only the image gets blurred */ +.tag-card img, +.scene-card img, +.image-card img, +.gallery-card img, +.thumbnail-container img, +.gallery-cover img, +.group-card img, +.performer-card img, +.studio-card img, +.m-sm-auto, +.scene-cover, +.scene-player-container { + display: block; + filter: blur(30px); + transition: filter 0.25s ease; +} -/* gallery */ -div:hover > .gallery-header, -table > tbody > tr > td:hover > a > img.w-100, +/* Hover unblurs */ +.tag-card:hover img, +.scene-card:hover img, +.image-card:hover img, +.gallery-card:hover img, +.thumbnail-container:hover img, +.gallery-cover:hover img, +.group-card:hover img, +.performer-card:hover img, +.studio-card:hover img, +.m-sm-auto:hover, +.card-section-title:hover, +.TruncatedText.tag-description:hover, .TruncatedText.gallery-card__description:hover, - -/* performer */ -img.performer:hover, - -/* studio */ -.studio-details .logo:hover, -.studio-details:hover > div > h2, -.studio-card__details:hover, +.TruncatedText.image-card__description:hover, +.TruncatedText.scene-card__description:hover, +.tag-name:hover, +.detail-item-value.description:hover, .studio-parent-studios:hover, - - -/* tag */ -.logo-container > .logo:hover, -.logo-container:hover > h2, -.TruncatedText.tag-description:hover, -.tag-parent-tags:hover - -{ -filter: blur(0px); -} +.detail-item-value:hover, +.studio-name:hover, +.scene-cover:hover, +.queue-scene-details:hover, +.scene-player-container:hover { + filter: blur(0); +} \ No newline at end of file diff --git a/plugins/SFWSwitch/sfwswitch.yml b/plugins/SFWSwitch/sfwswitch.yml index 10e94b4d..867fc374 100644 --- a/plugins/SFWSwitch/sfwswitch.yml +++ b/plugins/SFWSwitch/sfwswitch.yml @@ -1,5 +1,5 @@ name: SFW Switch -description: Add a button to blur covers and images. +description: A full feature SFW plugin with additional plugin support and audio handling. version: 1.5 url: https://discourse.stashapp.cc/t/sfw-switch/4658 ui: From 8aa4de3fe71f7d3f2e5cb83110dd4d4239ef094a Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 9 Mar 2026 22:23:13 -0400 Subject: [PATCH 05/12] targeted stats-elements rather than m-sm-auto to prevent unwanted css stylings. Loosened blur --- plugins/SFWSwitch/sfw.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/SFWSwitch/sfw.css b/plugins/SFWSwitch/sfw.css index de862601..6de92410 100644 --- a/plugins/SFWSwitch/sfw.css +++ b/plugins/SFWSwitch/sfw.css @@ -25,6 +25,7 @@ .studio-parent-studios, .detail-item-value, .studio-name, +.stats-element, .queue-scene-details { filter: blur(2px); @@ -40,7 +41,6 @@ filter: blur(2px); .group-card img, .performer-card img, .studio-card img, -.m-sm-auto, .scene-cover, .scene-player-container { display: block; @@ -58,7 +58,7 @@ filter: blur(2px); .group-card:hover img, .performer-card:hover img, .studio-card:hover img, -.m-sm-auto:hover, +.stats-element:hover, .card-section-title:hover, .TruncatedText.tag-description:hover, .TruncatedText.gallery-card__description:hover, From 0872494847b8c7c6069aa7898f897fe6bbeae0d2 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 13 Mar 2026 08:35:59 -0400 Subject: [PATCH 06/12] Re-added performer card profile pic --- plugins/SFWSwitch/sfw.css | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/SFWSwitch/sfw.css b/plugins/SFWSwitch/sfw.css index 6de92410..dc99cf41 100644 --- a/plugins/SFWSwitch/sfw.css +++ b/plugins/SFWSwitch/sfw.css @@ -42,6 +42,7 @@ filter: blur(2px); .performer-card img, .studio-card img, .scene-cover, +.detail-header-image, .scene-player-container { display: block; filter: blur(30px); @@ -71,6 +72,7 @@ filter: blur(2px); .studio-name:hover, .scene-cover:hover, .queue-scene-details:hover, +.detail-header-image:hover, .scene-player-container:hover { filter: blur(0); } \ No newline at end of file From b1aeec17dc998c398e23e981f1edd0d3f20ea6b4 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 15 Mar 2026 20:21:37 -0400 Subject: [PATCH 07/12] Added audio boolean setting (disabled by default), added additional configuration for hot or not --- plugins/SFWSwitch/additional_plugins.css | 6 +- plugins/SFWSwitch/sfw.js | 90 +++++++++++++++--------- plugins/SFWSwitch/sfwswitch.yml | 11 ++- 3 files changed, 68 insertions(+), 39 deletions(-) diff --git a/plugins/SFWSwitch/additional_plugins.css b/plugins/SFWSwitch/additional_plugins.css index 6b5f40dc..51df63c4 100644 --- a/plugins/SFWSwitch/additional_plugins.css +++ b/plugins/SFWSwitch/additional_plugins.css @@ -13,16 +13,17 @@ /* HotOrNot */ .hon-performer-image, .hon-scene-image, +.hon-selection-image, .hon-image-image-container, .hon-image-image, + /* O Stats */ .custom-stats-row .stats-element img, #on-this-day-section [style*="position: relative; height: 400px"], /* Sprite Tab */ .sprite-cell -#on-this-day-section [style*="position: relative; height: 400px"] { filter: blur(30px); } @@ -33,6 +34,7 @@ filter: blur(30px); .pwr-scene-info, /* HotOrNot */ +.hon-selection-name, .hon-performer-info.hon-scene-info, /* O Stats */ @@ -56,6 +58,8 @@ filter: blur(2px); .hon-image-image-container:hover, .hon-image-image:hover, .hon-performer-info.hon-scene-info:hover, +.hon-selection-card:hover .hon-selection-image, +.hon-selection-name:hover, /* Sprite Tab */ .sprite-cell:hover, diff --git a/plugins/SFWSwitch/sfw.js b/plugins/SFWSwitch/sfw.js index cc2637fa..2ade0157 100644 --- a/plugins/SFWSwitch/sfw.js +++ b/plugins/SFWSwitch/sfw.js @@ -1,23 +1,48 @@ let sfw_mediaObserver = null; let sfw_playListener = null; - -function sfw_mode() { +let sfw_extraListeners = null; + +async function getSfwConfig() { + try { + const response = await fetch('/graphql', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + query: `{ + configuration { + plugins + } + }` + }), + }); + const result = await response.json(); + const pluginSettings = result.data.configuration.plugins.sfwswitch; + return pluginSettings?.audio_setting === true; + } catch (e) { + console.error("SFW Switch: Could not fetch config", e); + return false; + } +} +async function sfw_mode() { const stash_css = sfwswitch_findstashcss(); const button = document.getElementById("plugin_sfw"); if (!stash_css) return; const sfwState = localStorage.getItem("sfw_mode") === "true"; + const audioMuteEnabled = await getSfwConfig(); stash_css.disabled = !sfwState; - if (sfwState) { + if (sfwState && audioMuteEnabled) { sfw_mute_all_media(); } else { sfw_unmute_all_media(); } - button.style.color = sfwState ? "#5cff00" : "#f5f8fa"; + if (button) { + button.style.color = sfwState ? "#5cff00" : "#f5f8fa"; + } } function sfwswitch_createbutton() { @@ -52,18 +77,17 @@ function sfwswitch_createbutton() { setTimeout(() => clearInterval(intervalId), 10000); } +// Function to strictly handle the muted state function sfw_forceMute(media) { + if (!media) return; media.muted = true; - media.defaultMuted = true; - media.volume = 0; } function sfw_mute_all_media() { - - // Mute existing media + // Initial sweep document.querySelectorAll("audio, video").forEach(sfw_forceMute); - // Mute media when it starts playing + // Global event listener for play, seek, and volume changes if (!sfw_playListener) { sfw_playListener = function(e) { if (e.target.tagName === "VIDEO" || e.target.tagName === "AUDIO") { @@ -72,75 +96,71 @@ function sfw_mute_all_media() { }; document.addEventListener("play", sfw_playListener, true); + document.addEventListener("volumechange", sfw_playListener, true); + document.addEventListener("loadeddata", sfw_playListener, true); + document.addEventListener("seeking", sfw_playListener, true); } - // Watch for new media nodes + // MutationObserver for content loaded via AJAX/Dynamic updates if (!sfw_mediaObserver) { - sfw_mediaObserver = new MutationObserver(mutations => { - mutations.forEach(mutation => { + for (const mutation of mutations) { mutation.addedNodes.forEach(node => { - if (node.tagName === "VIDEO" || node.tagName === "AUDIO") { sfw_forceMute(node); - } - - if (node.querySelectorAll) { + } else if (node.querySelectorAll) { node.querySelectorAll("video, audio").forEach(sfw_forceMute); } - }); - }); - }); - - sfw_mediaObserver.observe(document.body, { - childList: true, - subtree: true + } }); + sfw_mediaObserver.observe(document.body, { childList: true, subtree: true }); } } function sfw_unmute_all_media() { - + // Unmute existing media document.querySelectorAll("audio, video").forEach(media => { media.muted = false; - media.volume = 1; }); + // Clean up observer if (sfw_mediaObserver) { sfw_mediaObserver.disconnect(); sfw_mediaObserver = null; } + if (sfw_playListener) { document.removeEventListener("play", sfw_playListener, true); + document.removeEventListener("volumechange", sfw_playListener, true); + document.removeEventListener("loadeddata", sfw_playListener, true); + document.removeEventListener("seeking", sfw_playListener, true); sfw_playListener = null; } } -function sfwswitch_switcher() { +async function sfwswitch_switcher() { const stash_css = sfwswitch_findstashcss(); - if (!stash_css) { - console.error("SFW stylesheet not found."); - return; - } + if (!stash_css) return; stash_css.disabled = !stash_css.disabled; - const enabled = !stash_css.disabled; localStorage.setItem("sfw_mode", enabled); - if (enabled) { + const audioMuteEnabled = await getSfwConfig(); + + if (enabled && audioMuteEnabled) { sfw_mute_all_media(); } else { sfw_unmute_all_media(); } const button = document.getElementById("plugin_sfw"); - button.style.color = enabled ? "#5cff00" : "#f5f8fa"; - - console.log(`SFW mode ${enabled ? "enabled" : "disabled"}`); + if (button) { + button.style.color = enabled ? "#5cff00" : "#f5f8fa"; + } } function sfwswitch_findstashcss() { diff --git a/plugins/SFWSwitch/sfwswitch.yml b/plugins/SFWSwitch/sfwswitch.yml index 867fc374..e5d36221 100644 --- a/plugins/SFWSwitch/sfwswitch.yml +++ b/plugins/SFWSwitch/sfwswitch.yml @@ -1,10 +1,15 @@ name: SFW Switch -description: A full feature SFW plugin with additional plugin support and audio handling. -version: 1.5 +description: Add a button to blur covers and images. +version: 1.6 url: https://discourse.stashapp.cc/t/sfw-switch/4658 ui: javascript: - sfw.js css: - sfw.css - - additional_plugins.css \ No newline at end of file + - additional_plugins.css +settings: + audio_setting: + displayName: Enable Sound Mute + description: By default the plugin does not mute sound. Enabling this feature will have sound sources included when the SFW button is enabled. + type: BOOLEAN \ No newline at end of file From 87c8c28d9255ee10428a5767749b0952f792413a Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 17 Mar 2026 11:27:01 -0400 Subject: [PATCH 08/12] adjusted img blur --- plugins/SFWSwitch/sfw.css | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/SFWSwitch/sfw.css b/plugins/SFWSwitch/sfw.css index dc99cf41..3313b4a9 100644 --- a/plugins/SFWSwitch/sfw.css +++ b/plugins/SFWSwitch/sfw.css @@ -42,6 +42,7 @@ filter: blur(2px); .performer-card img, .studio-card img, .scene-cover, +.image-container img, .detail-header-image, .scene-player-container { display: block; @@ -60,6 +61,7 @@ filter: blur(2px); .performer-card:hover img, .studio-card:hover img, .stats-element:hover, +.image-container:hover img, .card-section-title:hover, .TruncatedText.tag-description:hover, .TruncatedText.gallery-card__description:hover, From 811bfdbf4698216f5f69b51567f0a67f30e8ac6b Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 17 Mar 2026 11:28:17 -0400 Subject: [PATCH 09/12] Added transition to all unblur --- plugins/SFWSwitch/sfw.css | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/SFWSwitch/sfw.css b/plugins/SFWSwitch/sfw.css index 3313b4a9..dcf888da 100644 --- a/plugins/SFWSwitch/sfw.css +++ b/plugins/SFWSwitch/sfw.css @@ -77,4 +77,5 @@ filter: blur(2px); .detail-header-image:hover, .scene-player-container:hover { filter: blur(0); + transition: filter 0.25s ease; } \ No newline at end of file From 45335f9f824b9ddcf0bcc0840d16294e0c967aaa Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 17 Mar 2026 18:16:27 -0400 Subject: [PATCH 10/12] Major Improvement: Fixed remaining unblurred features, fixed zombie audio bug. --- plugins/SFWSwitch/README.md | 22 --- plugins/SFWSwitch/additional_plugins.css | 11 +- plugins/SFWSwitch/sfw.css | 203 ++++++++++++++++------- plugins/SFWSwitch/sfw.js | 35 ++-- 4 files changed, 176 insertions(+), 95 deletions(-) delete mode 100644 plugins/SFWSwitch/README.md diff --git a/plugins/SFWSwitch/README.md b/plugins/SFWSwitch/README.md deleted file mode 100644 index 02cb3507..00000000 --- a/plugins/SFWSwitch/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# SFW Switch - -https://discourse.stashapp.cc/t/sfw-switch/4658 - -## Features - -- Adds a SFW toggle button to the menu bar. - - Green = Blur enabled - - Gray = Blur disabled -- Toggling the button blurs cover images and other content. -- Hovering over an image temporarily removes the blur. -- Extends the blurring functionality to some community plugins. - - Custom selectors should should be added to `additional_plugins.css` file. - -## Screenshots - -![image](https://user-images.githubusercontent.com/23707269/206918500-0676e4ea-dcfb-4370-b35f-3f6887b76b54.png) - -## Credit -Original plugin by Belleyy [here](https://github.com/Belleyy/CommunityScripts/tree/pluginUI_SFWSwitch/plugins/SFW%20Switch). - -The CSS code used is provided by fl0w#9497 [here](https://discourse.stashapp.cc/t/custom-css-snippets/4043#p-8143-blur-nsfw-images-and-unblur-on-mouse-over-41). diff --git a/plugins/SFWSwitch/additional_plugins.css b/plugins/SFWSwitch/additional_plugins.css index 51df63c4..148ec158 100644 --- a/plugins/SFWSwitch/additional_plugins.css +++ b/plugins/SFWSwitch/additional_plugins.css @@ -12,6 +12,7 @@ /* HotOrNot */ .hon-performer-image, +.hon-performer-card, .hon-scene-image, .hon-selection-image, .hon-image-image-container, @@ -25,7 +26,8 @@ /* Sprite Tab */ .sprite-cell { -filter: blur(30px); + filter: blur(30px); + transition: filter 0.25s ease; } /* === LESS BLUR === */ @@ -43,7 +45,8 @@ filter: blur(30px); #on-this-day-section [style*="display: flex"][style*="cursor: pointer"] img + div, #on-this-day-section > div:last-child { -filter: blur(2px); + filter: blur(2px); + transition: filter 0.25s ease; } /* StashBattle */ @@ -54,12 +57,14 @@ filter: blur(2px); /* HotOrNot */ .hon-performer-image:hover, +.hon-performer-card:hover, .hon-scene-image:hover, .hon-image-image-container:hover, .hon-image-image:hover, .hon-performer-info.hon-scene-info:hover, -.hon-selection-card:hover .hon-selection-image, +.hon-selection-card:hover, .hon-selection-name:hover, +.hon-selection-image:hover, /* Sprite Tab */ .sprite-cell:hover, diff --git a/plugins/SFWSwitch/sfw.css b/plugins/SFWSwitch/sfw.css index dcf888da..ec6d22fb 100644 --- a/plugins/SFWSwitch/sfw.css +++ b/plugins/SFWSwitch/sfw.css @@ -1,81 +1,168 @@ -/* Container*/ -.tag-card, -.scene-card, -.image-card, -.gallery-card, + +/* === MORE BLUR === */ +/* common */ +.thumbnail-container img, +.detail-header-image, + +/* scene */ +.scene-player-container, +.scene-cover, +.scene-card-preview, +.scrubber-item, +.scene-image, +.scene-card img, +.wall-item-media, +.marker-wall, + +/* image */ +.image-card img, +.image-container img, +.image-thumbnail, +.Lightbox-carousel, + +/* group */ +.group-card-image, +.group-images, .group-card, -.thumbnail-container, -.gallery-cover, -.performer-card, -.studio-card { - position: relative; - display: inline-block; + +/* gallery */ +.gallery-image, +.gallery-card-image, +.gallery-card img, +.gallery-cover img, +.gallery, + +/* performer */ +.performer-card img, + +/* studio */ +.studio-card-image, +.studio-card img, + +/* tag */ +.tag-card img +{ + + filter: blur(30px); + transition: filter 0.25s ease; } /* === LESS BLUR === */ /* common */ .card-section-title, -.TruncatedText.tag-description, -.TruncatedText.gallery-card__description, -.TruncatedText.image-card__description, -.TruncatedText.scene-card__description, -.tag-name, .detail-item-value.description, -.studio-parent-studios, .detail-item-value, +.TruncatedText, + +/* scene */ +.scene-studio-overlay, +.scene-header > h3, +h3.scene-header, +.TruncatedText.scene-card__description, +.queue-scene-details, + +/* performer */ +.performer-name, +.card-section, +.name-data, +.aliases-data, + +/* gallery */ +.gallery-header.no-studio, +.TruncatedText.gallery-card__description, + + +/* studio */ .studio-name, -.stats-element, -.queue-scene-details +.studio-overlay a, +.studio-logo, +.studio-parent-studios, + +/* group */ +.group-details > div > h2, + +/* image */ +h3.image-header, +.Lightbox-carousel:hover, +.TruncatedText.image-card__description, + +/* tag */ +.TruncatedText.tag-description, +.tag-item.tag-link.badge.badge-secondary, +.tag-name + { filter: blur(2px); } -/* Only the image gets blurred */ -.tag-card img, -.scene-card img, -.image-card img, -.gallery-card img, -.thumbnail-container img, -.gallery-cover img, -.group-card img, -.performer-card img, -.studio-card img, -.scene-cover, -.image-container img, -.detail-header-image, -.scene-player-container { - display: block; - filter: blur(30px); - transition: filter 0.25s ease; -} -/* Hover unblurs */ -.tag-card:hover img, -.scene-card:hover img, -.image-card:hover img, -.gallery-card:hover img, +/* === UNBLUR ON HOVER === */ + +/* common */ +.detail-item-value:hover, +.scene-cover:hover, .thumbnail-container:hover img, -.gallery-cover:hover img, -.group-card:hover img, -.performer-card:hover img, -.studio-card:hover img, -.stats-element:hover, -.image-container:hover img, .card-section-title:hover, .TruncatedText.tag-description:hover, -.TruncatedText.gallery-card__description:hover, -.TruncatedText.image-card__description:hover, -.TruncatedText.scene-card__description:hover, -.tag-name:hover, .detail-item-value.description:hover, -.studio-parent-studios:hover, -.detail-item-value:hover, -.studio-name:hover, -.scene-cover:hover, +.TruncatedText:hover, + +/* scene */ +.scene-player-container:hover, +.scene-card-preview:hover, .queue-scene-details:hover, +.scene-card:hover img, +.TruncatedText.scene-card__description:hover, +.scene-player-container:hover, +.scene-header:hover > h3, +div:hover > .scene-header, +.wall-item-media:hover, +.marker-wall:hover, + +/* image */ .detail-header-image:hover, -.scene-player-container:hover { +div:hover > .image-header, +.image-card:hover img, +.image-container:hover img, +.image-thumbnail:hover, +.TruncatedText.image-card__description:hover, + + +/* group */ +.group-card:hover img, + +/* gallery */ +.gallery-header.no-studio, +.gallery-card:hover img, +.gallery-cover:hover img, +.gallery-image:hover, +.gallery-card-image:hover, +.gallery:hover, +.TruncatedText.gallery-card__description:hover, + +/* performer */ +.performer-card-image:hover, +.performer-name:hover, +.card-section:hover, +.name-data:hover, +.aliases-data:hover, + +/* studio */ +.studio-name:hover, +.studio-overlay:hover a, +.studio-card:hover img, +.studio-parent-studios:hover, +.studio-logo:hover, + + +/* tag */ +.tag-card:hover img, +.tag-item.tag-link.badge.badge-secondary:hover, +.tag-name:hover +{ filter: blur(0); transition: filter 0.25s ease; -} \ No newline at end of file +} + +/*Credit: fl0w#9497 */ \ No newline at end of file diff --git a/plugins/SFWSwitch/sfw.js b/plugins/SFWSwitch/sfw.js index 2ade0157..8e3c62ae 100644 --- a/plugins/SFWSwitch/sfw.js +++ b/plugins/SFWSwitch/sfw.js @@ -119,18 +119,7 @@ function sfw_mute_all_media() { } function sfw_unmute_all_media() { - // Unmute existing media - document.querySelectorAll("audio, video").forEach(media => { - media.muted = false; - }); - - // Clean up observer - if (sfw_mediaObserver) { - sfw_mediaObserver.disconnect(); - sfw_mediaObserver = null; - } - - + // 1. Remove listeners FIRST to prevent them from firing during the unmute loop if (sfw_playListener) { document.removeEventListener("play", sfw_playListener, true); document.removeEventListener("volumechange", sfw_playListener, true); @@ -138,12 +127,24 @@ function sfw_unmute_all_media() { document.removeEventListener("seeking", sfw_playListener, true); sfw_playListener = null; } + + if (sfw_mediaObserver) { + sfw_mediaObserver.disconnect(); + sfw_mediaObserver = null; + } + + // 2. Unmute existing media + document.querySelectorAll("audio, video").forEach(media => { + media.muted = false; + // Optional: media.volume = 1.0; // Use if volume was also forced to 0 + }); } async function sfwswitch_switcher() { const stash_css = sfwswitch_findstashcss(); if (!stash_css) return; + // Toggle the CSS stash_css.disabled = !stash_css.disabled; const enabled = !stash_css.disabled; @@ -151,10 +152,20 @@ async function sfwswitch_switcher() { const audioMuteEnabled = await getSfwConfig(); + // Logic Check: If we just disabled SFW, we MUST run unmute immediately if (enabled && audioMuteEnabled) { sfw_mute_all_media(); } else { + // This clears observers and sets muted = false sfw_unmute_all_media(); + + // CRITICAL: Force a pause/reset on any media that might be stuck in a background buffer + document.querySelectorAll("audio, video").forEach(media => { + if (media.paused && media.muted) { + // If it was supposed to be stopped, make sure it stays stopped + media.muted = false; + } + }); } const button = document.getElementById("plugin_sfw"); From b431fa92dcbc1f6077f1c18f4a0ed37b86af976d Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 17 Mar 2026 18:35:24 -0400 Subject: [PATCH 11/12] Readded readme --- plugins/SFWSwitch/README.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 plugins/SFWSwitch/README.md diff --git a/plugins/SFWSwitch/README.md b/plugins/SFWSwitch/README.md new file mode 100644 index 00000000..02cb3507 --- /dev/null +++ b/plugins/SFWSwitch/README.md @@ -0,0 +1,22 @@ +# SFW Switch + +https://discourse.stashapp.cc/t/sfw-switch/4658 + +## Features + +- Adds a SFW toggle button to the menu bar. + - Green = Blur enabled + - Gray = Blur disabled +- Toggling the button blurs cover images and other content. +- Hovering over an image temporarily removes the blur. +- Extends the blurring functionality to some community plugins. + - Custom selectors should should be added to `additional_plugins.css` file. + +## Screenshots + +![image](https://user-images.githubusercontent.com/23707269/206918500-0676e4ea-dcfb-4370-b35f-3f6887b76b54.png) + +## Credit +Original plugin by Belleyy [here](https://github.com/Belleyy/CommunityScripts/tree/pluginUI_SFWSwitch/plugins/SFW%20Switch). + +The CSS code used is provided by fl0w#9497 [here](https://discourse.stashapp.cc/t/custom-css-snippets/4043#p-8143-blur-nsfw-images-and-unblur-on-mouse-over-41). From 358a71ae9e4afb07cab50b6d4ca812af419c1bd7 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 17 Mar 2026 19:31:46 -0400 Subject: [PATCH 12/12] Improvement: Added individual wall blur functionality for walls --- plugins/SFWSwitch/sfw.css | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/plugins/SFWSwitch/sfw.css b/plugins/SFWSwitch/sfw.css index ec6d22fb..ad7e5f9f 100644 --- a/plugins/SFWSwitch/sfw.css +++ b/plugins/SFWSwitch/sfw.css @@ -3,6 +3,7 @@ /* common */ .thumbnail-container img, .detail-header-image, +.wall-item-gallery, /* scene */ .scene-player-container, @@ -12,13 +13,13 @@ .scene-image, .scene-card img, .wall-item-media, -.marker-wall, +.wall-item.show-title, /* image */ .image-card img, -.image-container img, .image-thumbnail, .Lightbox-carousel, +.react-photo-gallery--gallery img, /* group */ .group-card-image, @@ -30,7 +31,8 @@ .gallery-card-image, .gallery-card img, .gallery-cover img, -.gallery, +.GalleryWallCard.GalleryWallCard-portrait, +.GalleryWallCard.GalleryWallCard-landscape, /* performer */ .performer-card img, @@ -61,6 +63,7 @@ h3.scene-header, .TruncatedText.scene-card__description, .queue-scene-details, +.marker-wall, /* performer */ .performer-name, @@ -102,7 +105,7 @@ filter: blur(2px); /* common */ .detail-item-value:hover, .scene-cover:hover, -.thumbnail-container:hover img, + .card-section-title:hover, .TruncatedText.tag-description:hover, .detail-item-value.description:hover, @@ -119,14 +122,16 @@ filter: blur(2px); div:hover > .scene-header, .wall-item-media:hover, .marker-wall:hover, +.wall-item.show-title:hover, /* image */ .detail-header-image:hover, div:hover > .image-header, .image-card:hover img, -.image-container:hover img, +.react-photo-gallery--gallery img:hover, .image-thumbnail:hover, .TruncatedText.image-card__description:hover, +.wall-item:hover img, /* group */ @@ -138,8 +143,9 @@ div:hover > .image-header, .gallery-cover:hover img, .gallery-image:hover, .gallery-card-image:hover, -.gallery:hover, .TruncatedText.gallery-card__description:hover, +.GalleryWallCard.GalleryWallCard-portrait:hover, +.GalleryWallCard.GalleryWallCard-landscape:hover, /* performer */ .performer-card-image:hover,