mirror of
https://github.com/stashapp/CommunityScripts.git
synced 2026-04-28 00:50:29 -05:00
[SFW Switch] Audio handling + Additional plugin support (#679)
* Added additional plugin support, patched additional fields previously left uncensored. * Update README Clarified instructions for adding custom selectors to additional_plugins.css. * SFW Switch now mutes all sound when enabled by default. * Refactored main css, all UI functions should be accouted for now. Updated plugin yaml description * targeted stats-elements rather than m-sm-auto to prevent unwanted css stylings. Loosened blur * Re-added performer card profile pic * Added audio boolean setting (disabled by default), added additional configuration for hot or not * adjusted img blur * Added transition to all unblur * Major Improvement: Fixed remaining unblurred features, fixed zombie audio bug. * Readded readme * Improvement: Added individual wall blur functionality for walls * Fix double blur for groups, fix unblur in performer tagger --------- Co-authored-by: DogmaDragon <103123951+DogmaDragon@users.noreply.github.com>
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
/* performer */
|
||||
.performer-name,
|
||||
.card-section,
|
||||
.name-data,
|
||||
.aliases-data,
|
||||
|
||||
/* gallery */
|
||||
.gallery-header.no-studio,
|
||||
.TruncatedText.gallery-card__description,
|
||||
|
||||
|
||||
/* studio */
|
||||
.studio-name,
|
||||
.studio-overlay a,
|
||||
.studio-logo,
|
||||
.studio-parent-studios,
|
||||
|
||||
/* group */
|
||||
.group-details > div > h2,
|
||||
|
||||
/* gallery */
|
||||
h3.gallery-header,
|
||||
.TruncatedText.gallery-card__description,
|
||||
|
||||
/* studio */
|
||||
.studio-details .logo,
|
||||
.studio-details > div > h2,
|
||||
.studio-card__details,
|
||||
.studio-parent-studios,
|
||||
/* 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-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,
|
||||
.studio-logo:hover,
|
||||
.scene-cover:hover,
|
||||
.image-thumbnail:hover,
|
||||
.scene-card-preview:hover,
|
||||
.scrubber-item:hover,
|
||||
.scene-image:hover,
|
||||
.TruncatedText.scene-card__description:hover,
|
||||
.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 */
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
- 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
|
||||
Reference in New Issue
Block a user