diff --git a/plugins/SFWSwitch/additional_plugins.css b/plugins/SFWSwitch/additional_plugins.css index 6ce5d6fb..148ec158 100644 --- a/plugins/SFWSwitch/additional_plugins.css +++ b/plugins/SFWSwitch/additional_plugins.css @@ -12,15 +12,22 @@ /* HotOrNot */ .hon-performer-image, +.hon-performer-card, .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"] +#on-this-day-section [style*="position: relative; height: 400px"], + +/* Sprite Tab */ +.sprite-cell { -filter: blur(30px); + filter: blur(30px); + transition: filter 0.25s ease; } /* === LESS BLUR === */ @@ -29,6 +36,7 @@ filter: blur(30px); .pwr-scene-info, /* HotOrNot */ +.hon-selection-name, .hon-performer-info.hon-scene-info, /* O Stats */ @@ -37,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 */ @@ -48,10 +57,17 @@ 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-name:hover, +.hon-selection-image:hover, + +/* Sprite Tab */ +.sprite-cell:hover, /* O Stats */ .custom-stats-row .stats-element:hover, diff --git a/plugins/SFWSwitch/sfw.css b/plugins/SFWSwitch/sfw.css index bf4f0a8c..6ba1a1fb 100644 --- a/plugins/SFWSwitch/sfw.css +++ b/plugins/SFWSwitch/sfw.css @@ -1,134 +1,173 @@ -/* [Global changes] Blur NSFW images and unblur on mouse over */ - -/*Credit: fl0w#9497 */ /* === MORE BLUR === */ +/* common */ +.thumbnail-container img, +.detail-header-image, +.wall-item-gallery, /* scene */ -.scene-card-preview, -.vjs-poster, -video, +.scene-player-container, .scene-cover, +.scene-card-preview, .scrubber-item, .scene-image, +.scene-card img, +.wall-item-media, +.wall-item.show-title, /* image */ -.image-card-preview, -.image-image, -.gallery-image, +.image-card img, +.image-thumbnail, +.Lightbox-carousel, +.react-photo-gallery--gallery img, /* group */ .group-card-image, -.group-images, /* gallery */ +.gallery-image, .gallery-card-image, -table > tbody > tr > td > a > img.w-100, +.gallery-card img, +.gallery-cover img, +.GalleryWallCard.GalleryWallCard-portrait, +.GalleryWallCard.GalleryWallCard-landscape, /* performer */ -.performer-card-image, -img.performer, +.performer-card img, /* studio */ .studio-card-image, +.studio-card img, /* tag */ -.tag-card-image - - +.tag-card img { -filter: blur(30px); + + filter: blur(30px); + transition: filter 0.25s ease; } /* === LESS BLUR === */ /* common */ .card-section-title, +.detail-item-value.description, +.detail-item-value, +.TruncatedText, /* scene */ .scene-studio-overlay, .scene-header > h3, h3.scene-header, -.studio-logo, -.image-thumbnail, .TruncatedText.scene-card__description, +.queue-scene-details, +.marker-wall, -/* image */ -h3.image-header, - -/* group */ -.group-details > div > h2, +/* performer */ +.performer-name, +.card-section, +.name-data, +.aliases-data, /* gallery */ -h3.gallery-header, +.gallery-header.no-studio, .TruncatedText.gallery-card__description, + /* studio */ -.studio-details .logo, -.studio-details > div > h2, -.studio-card__details, +.studio-name, +.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 */ -.logo-container > .logo, -.logo-container > h2, .TruncatedText.tag-description, -.tag-parent-tags +.tag-item.tag-link.badge.badge-secondary, +.tag-name { filter: blur(2px); } + /* === UNBLUR ON HOVER === */ /* common */ -.thumbnail-section:hover *, -.card:hover .card-section-title, +.detail-item-value:hover, +.scene-cover:hover, + +.card-section-title:hover, +.TruncatedText.tag-description:hover, +.detail-item-value.description:hover, +.TruncatedText:hover, /* 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-player-container:hover, .scene-card-preview:hover, -.scrubber-item:hover, -.scene-image: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, +.wall-item.show-title:hover, /* image */ -.image-image:hover, +.detail-header-image:hover, div:hover > .image-header, -.gallery-image:hover, +.image-card:hover img, +.react-photo-gallery--gallery img:hover, +.image-thumbnail:hover, +.TruncatedText.image-card__description:hover, +.wall-item:hover img, + /* group */ -.group-images:hover, -.group-details > div > h2:hover, +.group-card:hover img, /* gallery */ -div:hover > .gallery-header, -table > tbody > tr > td:hover > a > img.w-100, +.gallery-header.no-studio, +.gallery-card:hover img, +.gallery-cover:hover img, +.gallery-image:hover, +.gallery-card-image:hover, .TruncatedText.gallery-card__description:hover, +.GalleryWallCard.GalleryWallCard-portrait:hover, +.GalleryWallCard.GalleryWallCard-landscape:hover, /* performer */ -img.performer:hover, +.performer-card-image:hover, +.performer-name:hover, +.card-section:hover, +.name-data:hover, +.aliases-data:hover, +.performer-card img:hover, /* studio */ -.studio-details .logo:hover, -.studio-details:hover > div > h2, -.studio-card__details:hover, +.studio-name:hover, +.studio-overlay:hover a, +.studio-card:hover img, .studio-parent-studios:hover, +.studio-logo:hover, /* tag */ -.logo-container > .logo:hover, -.logo-container:hover > h2, -.TruncatedText.tag-description:hover, -.tag-parent-tags:hover - +.tag-card:hover img, +.tag-item.tag-link.badge.badge-secondary:hover, +.tag-name:hover { -filter: blur(0px); + filter: blur(0); + transition: filter 0.25s ease; } + +/*Credit: fl0w#9497 */ diff --git a/plugins/SFWSwitch/sfw.js b/plugins/SFWSwitch/sfw.js index 2410ade2..8e3c62ae 100644 --- a/plugins/SFWSwitch/sfw.js +++ b/plugins/SFWSwitch/sfw.js @@ -1,16 +1,48 @@ -function sfw_mode() { +let sfw_mediaObserver = null; +let sfw_playListener = null; +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(); - // Apply saved state to the stylesheet stash_css.disabled = !sfwState; - // Update button color - button.style.color = sfwState ? "#5cff00" : "#f5f8fa"; + if (sfwState && audioMuteEnabled) { + sfw_mute_all_media(); + } else { + sfw_unmute_all_media(); + } + + if (button) { + button.style.color = sfwState ? "#5cff00" : "#f5f8fa"; + } } function sfwswitch_createbutton() { @@ -45,22 +77,101 @@ function sfwswitch_createbutton() { setTimeout(() => clearInterval(intervalId), 10000); } -function sfwswitch_switcher() { - const stash_css = sfwswitch_findstashcss(); - if (!stash_css) { - console.error("SFW stylesheet not found."); - return; +// Function to strictly handle the muted state +function sfw_forceMute(media) { + if (!media) return; + media.muted = true; +} + +function sfw_mute_all_media() { + // Initial sweep + document.querySelectorAll("audio, video").forEach(sfw_forceMute); + + // 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") { + sfw_forceMute(e.target); + } + }; + + document.addEventListener("play", sfw_playListener, true); + document.addEventListener("volumechange", sfw_playListener, true); + document.addEventListener("loadeddata", sfw_playListener, true); + document.addEventListener("seeking", sfw_playListener, true); } - // Toggle stylesheet - stash_css.disabled = !stash_css.disabled; + // MutationObserver for content loaded via AJAX/Dynamic updates + if (!sfw_mediaObserver) { + sfw_mediaObserver = new MutationObserver(mutations => { + for (const mutation of mutations) { + mutation.addedNodes.forEach(node => { + if (node.tagName === "VIDEO" || node.tagName === "AUDIO") { + sfw_forceMute(node); + } else if (node.querySelectorAll) { + node.querySelectorAll("video, audio").forEach(sfw_forceMute); + } + }); + } + }); + sfw_mediaObserver.observe(document.body, { childList: true, subtree: true }); + } +} - // Save new state to localStorage - localStorage.setItem("sfw_mode", !stash_css.disabled); +function sfw_unmute_all_media() { + // 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); + document.removeEventListener("loadeddata", sfw_playListener, true); + 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; + + localStorage.setItem("sfw_mode", enabled); + + 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"); - button.style.color = stash_css.disabled ? "#f5f8fa" : "#5cff00"; - console.log(`SFW mode ${stash_css.disabled ? "disabled" : "enabled"}`); + if (button) { + button.style.color = enabled ? "#5cff00" : "#f5f8fa"; + } } function sfwswitch_findstashcss() { diff --git a/plugins/SFWSwitch/sfwswitch.yml b/plugins/SFWSwitch/sfwswitch.yml index f8d576b1..e5d36221 100644 --- a/plugins/SFWSwitch/sfwswitch.yml +++ b/plugins/SFWSwitch/sfwswitch.yml @@ -1,10 +1,15 @@ name: SFW Switch description: Add a button to blur covers and images. -version: 1.4 +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