From a52ba76c6ba25468351309d6421f58a8bfe33992 Mon Sep 17 00:00:00 2001 From: HandyRandyx Date: Thu, 4 Jul 2024 04:55:14 -0300 Subject: [PATCH 1/5] [Hot Cards] Customizable Hot Cards (#355) * Fix inconsistent height of hot cards in the home page * Refactor CSS * Rename classes for clarity - hot-border -> hot-card - hot-card -> hot-border * Add support for 0-5 rating threshold alongside 0-100 range * Improve configuration strings * Add missed fallback * Add customizable Hot Cards - Allow users to specify Hot Cards to use tag-based or rating-based for each card type. - Introduce a new custom string format for more flexible and detailed customization. - Update README with detailed instructions, and examples. --- plugins/hotCards/README.md | 120 ++++++++++- plugins/hotCards/hotCards.css | 98 ++++----- plugins/hotCards/hotCards.js | 380 ++++++++++++++++++++++++++++------ plugins/hotCards/hotCards.yml | 48 ++--- 4 files changed, 498 insertions(+), 148 deletions(-) diff --git a/plugins/hotCards/README.md b/plugins/hotCards/README.md index 8f737c8..d3ed3d2 100644 --- a/plugins/hotCards/README.md +++ b/plugins/hotCards/README.md @@ -1,19 +1,125 @@ # Hot Cards -Hot Cards is a Stash CommunityScript plugin that offers a visual aid by applying custom CSS to card elements based on a tag ID or a rating threshold. You can use this plugin to remind yourself of certain performers, scenes, studios, movies, images or galleries. +Hot Cards is a Stash CommunityScript plugin designed to enhance your visual experience by applying custom styling to card elements based on a Tag ID or a Rating Threshold. This plugin is perfect for highlighting certain performers, scenes, studios, movies, images, or galleries. ## Features -- Adds custom CSS to card elements that match a specified tag ID or rating threshold. +- Custom styling to card elements that match a specified Tag ID or Rating Threshold. - Enable or disable Hot Cards on various sections like home, scenes, images, movies, galleries, performers, and studios. +- Specify Hot Cards to be tag-based or rating-based for each card type, as desired. +- Customizable Hot Cards. ## Installation -1. Go to Settings > Plugins. -2. Under Available Plugins expand the Community (stable) option. -3. Search for Hot Cards. -4. Select the plugin and click Install. +1. Go to **Settings** > **Plugins**. +2. Under **Available Plugins** expand the **Community (stable)** option. +3. Search for **Hot Cards**. +4. Select the plugin and click **Install**. ## Usage -Once installed, you can configure the plugin. Set a desire tag ID or a rating threshold and enable the sections where you want the hot cards to be displayed. +After installation, you can configure the plugin to suit your needs. Set a desired Tag ID or Rating Threshold and enable Hot Cards for the card types you want. Customize the appearance of Hot Cards for each type of card (scene, image, movie, gallery, performer, studio) using the format provided or leave the fields empty to apply the default style. + +### Custom String Format: + +_[criterion]\_[value]\_[style]\_[gradient-opts]\_[border-opts]_ + +**Important**: If you have previously installed the plugin, after updating to `1.1.0`, be sure to update your settings from the old boolean format to the new string format. Refresh the page for the changes to take effect. + +1. **criterion**: `` + + `t` for tag-based, `r` for rating-based,`d` for disabled. You can also leave it `` and it will grab the global value of the _Tag ID_ or _Rating Threshold_ as configured. + +2. **value**: `` + + Specific value for Tag ID or Rating Threshold. + +3. **style**: `` (comma separated) + + - Fixed color: **#5ff2a2** + - Style preset: **hot** + - Gradient: **#ef1313,#3bd612,...** (Hex color codes, color names) + +4. **gradient_opts**: `,,` (comma separated) + + Example: **linear,35deg,4s alternate infinite** + +5. **border_opts**: `,` (comma separated) + + Example: **#ff0000,2s ease infinite** + +
+ +**Note**: _It is recommended to refresh the page once you are done configuring for the changes to take effect and the previous style to be rewritten._ + +## Examples + +**Style Preset**: + +t_123_gold + +| Segment | Value | Meaning | +| ------------- | :---: | :---------------: | +| criterion | t | tag-based | +| value | 123 | use 123 as Tag ID | +| style | gold | use gold preset | +| gradient_opts | | N/A | +| border_opts | | N/A | + +--- + +**Fixed Color** + +r\_\_#2673b8 + +| Segment | Value | Meaning | +| ------------- | :-----: | :---------------------------: | +| criterion | r | rating-based | +| value | | use rating-based global value | +| style | #2673b8 | use fixed color | +| gradient_opts | | N/A | +| border_opts | | N/A | + +--- + +**Fixed Color with Border** + +\_\_#5ff2a2\_\_#5ff1a1 + +| Segment | Value | Meaning | +| ------------- | :-----: | :------------------------------------------: | +| criterion | | use tag-based or rating-based global enabled | +| value | | use tag-based or rating-based global value | +| style | #5ff2a2 | use fixed color | +| gradient_opts | | N/A | +| border_opts | #5ff1a1 | use border color when hovering cards | + +--- + +**Gradient with Border** + +\_67_pink,red,yellow,green,red,blue\_,30deg,5s ease infinite_red,1s ease-in-out infinite + +| Segment | Value | Meaning | +| ------------- | :----------------------------: | :------------------------------------------: | +| criterion | | use tag-based or rating-based global enabled | +| value | 67 | use 67 as Tag ID or Rating Threshold | +| style | pink,red,yellow,green,red,blue | use gradient | +| gradient_opts | ,30deg,5s ease infinite | type not provided, use linear per default | +| border_opts | red,1s ease-in-out infinite | + +## Style Presets + +These presets provide predefined styles for quick and easy customization. + +### default + +You can specify '\_\_default' for the card type you want the **default** preset to be applied and it will use the globally configured Tag ID or Rating Threshold. You can also leave the field empty and the default style will be applied anyway. + +### hot + +You can specify '\_\_hot' for the card type you want the **hot** preset to be applied and it will use the globally configured Tag ID or Rating Threshold. + +### gold + +You can specify '\_\_gold' for the card type you want the **gold** preset to be applied and it will use the globally configured Tag ID or Rating Threshold. diff --git a/plugins/hotCards/hotCards.css b/plugins/hotCards/hotCards.css index 93b26e7..65fbd3b 100644 --- a/plugins/hotCards/hotCards.css +++ b/plugins/hotCards/hotCards.css @@ -1,69 +1,57 @@ -.hot-border > .card { - background-color: rgba(0, 0, 0, 0.2); -} - .hot-card { - animation: pulse 3s ease-in-out infinite; -} - -@-webkit-keyframes pulse { - 0% { - -webkit-box-shadow: 0 0 0 0 rgba(255, 36, 9, 0); - } - 70% { - -webkit-box-shadow: - 0px 0px 1em #ff2409, - 0px 0px 2em #ff2409; - } - 100% { - -webkit-box-shadow: 0 0 0 0 rgba(255, 36, 9, 0); - } -} - -.hot-card:hover { - box-shadow: - 0px 0px 1em #ff2409, - 0px 0px 2em #ff2409; - animation: none; - animate: glow; -} - -.hot-border { --border-width: 0.2rem; - display: flex; position: relative; border-radius: var(--border-width); +} - &::after { - position: absolute; - content: ""; - top: calc(0.8 * var(--border-width)); - left: calc(0.8 * var(--border-width)); - z-index: -1; - width: calc(100% + var(--border-width) * -1.5); - height: calc(100% + var(--border-width) * -1.5); - background: linear-gradient( - 60deg, - hsl(224, 85%, 66%), - hsl(269, 85%, 66%), - hsl(314, 85%, 66%), - hsl(359, 85%, 66%), - hsl(44, 85%, 66%), - hsl(357.2, 87.7%, 52.4%), - hsl(301, 70.2%, 50%), - hsl(179, 85%, 66%) - ); +.hot-card > .card { + background-color: rgba(0, 0, 0, 0.2); + height: auto; +} - background-size: 300% 300%; - background-position: 0 50%; - border-radius: calc(2 * var(--border-width)); - animation: moveGradient 4s alternate infinite; +.hot-card::after { + z-index: -1; +} + +.hot-border:hover { + box-shadow: + 0px 0px 1em var(--border-color), + 0px 0px 2em var(--border-color); + animation: none !important; +} + +@keyframes pulse { + 0% { + box-shadow: 0 0 0 0 rgba(255, 36, 9, 0); + } + 70% { + box-shadow: + 0px 0px 1em var(--border-color), + 0px 0px 2em var(--border-color); + } + 100% { + box-shadow: 0 0 0 0 rgba(255, 36, 9, 0); } } -@keyframes moveGradient { +@keyframes move { + 0% { + background-position: 0% 50%; + } 50% { background-position: 100% 50%; } + 100% { + background-position: 0% 50%; + } +} + +@media (prefers-reduced-motion: reduce) { + .hot-card { + animation: none; + } + .hot-card:hover { + animation: none; + } } diff --git a/plugins/hotCards/hotCards.js b/plugins/hotCards/hotCards.js index 9f4d6a8..b97632e 100644 --- a/plugins/hotCards/hotCards.js +++ b/plugins/hotCards/hotCards.js @@ -2,63 +2,115 @@ "use strict"; const userSettings = await csLib.getConfiguration("hotCards", {}); - const TAG_ID = userSettings?.tagId; - const RATING_THRESHOLD = parseInt(userSettings?.threshold ?? 0); - const CARDS = { - gallery: { - class: "gallery-card", - data: stash.galleries, - enabled: userSettings?.galleries, + const SEPARATOR = "_"; + const INNER_SEPARATOR = ","; + const DEFAULTS = { + criterion: "", + value: "", + style: "default", + gradient_opts: { + type: "linear", + angle: "0deg", + animation: "", }, - image: { - class: "image-card", - data: stash.images, - enabled: userSettings?.images, - }, - movie: { - class: "movie-card", - data: stash.movies, - enabled: userSettings?.movies, - }, - performer: { - class: "performer-card", - data: stash.performers, - enabled: userSettings?.performers, - }, - scene: { - class: "scene-card", - data: stash.scenes, - enabled: userSettings?.scenes, - }, - studio: { - class: "studio-card", - data: stash.studios, - enabled: userSettings?.studios, + border_opts: { + color: "transparent", + animation: "", }, }; + const CRITERIA = { tag: "t", rating: "r", disabled: "d" }; + const SETTINGS = parseSettings(userSettings ?? ""); + const TAG_ID = SETTINGS.tagId; + const RATING_THRESHOLD = parseInt(SETTINGS.threshold ?? 0); + const CARD_KEYS = { + galleries: "gallery", + images: "image", + movies: "movie", + performers: "performer", + scenes: "scene", + studios: "studio", + }; + + const CARDS = Object.entries(CARD_KEYS).reduce((acc, [plural, singular]) => { + acc[singular] = { + class: `${singular}-card`, + config: SETTINGS[plural] || "", + data: stash[plural], + enabled: SETTINGS[plural]?.criterion !== CRITERIA.disabled, + }; + return acc; + }, {}); + + /** + * Custom CSS style presets for hot cards. + */ + const STYLES = { + default: getDefaultStylePreset(), + hot: getHotStylePreset(), + gold: getGoldStylePreset(), + }; + const isTagBased = TAG_ID?.length; const isRatingBased = RATING_THRESHOLD !== 0; + const isTagOrRatingBased = isTagBased || isRatingBased; + const isStarsRatingSystem = RATING_THRESHOLD <= 5; + let backupCards = []; let hotCards = []; + function parseSettings(settings) { + return Object.keys(settings).reduce((acc, key) => { + if (key === "threshold" || key === "tagId" || key === "home") { + acc[key] = settings[key]; + } else { + acc[key] = parseCustomFormat(settings[key]); + } + return acc; + }, {}); + } + + function parseCustomFormat(custom) { + const segments = custom.toString().split(SEPARATOR); + + return { + criterion: segments[0] || DEFAULTS.criterion, + value: segments[1] || DEFAULTS.value, + style: segments[2] || DEFAULTS.style, + gradient_opts: parseSegment(segments[3], DEFAULTS.gradient_opts, [ + "type", + "angle", + "animation", + ]), + border_opts: parseSegment(segments[4], DEFAULTS.border_opts, [ + "color", + "animation", + ]), + }; + } + + function parseSegment(segment, defaults, keys) { + const values = segment ? segment.split(INNER_SEPARATOR) : []; + return keys.reduce((acc, key, index) => { + acc[key] = values[index] || defaults[key]; + return acc; + }, {}); + } + // Mapping of configuration keys to functions const hotCardsHandlers = { - home: handleHomeHotCards, - galleries: handleGalleriesHotCards, - images: handleImagesHotCards, - movies: handleMoviesHotCards, - performers: handlePerformersHotCards, - scenes: handleScenesHotCards, - studios: handleStudiosHotCards, + gallery: handleGalleriesHotCards, + image: handleImagesHotCards, + movie: handleMoviesHotCards, + performer: handlePerformersHotCards, + scene: handleScenesHotCards, + studio: handleStudiosHotCards, }; - // Iterate over the corresponding config to call the appropriate functions - for (const [key, value] of Object.entries(userSettings)) { - if ( - value === true && - hotCardsHandlers[key] && - (isTagBased || isRatingBased) - ) { + // Handle home hot cards separately + if (SETTINGS.home && isTagOrRatingBased) handleHomeHotCards(); + + for (const [key, card] of Object.entries(CARDS)) { + if (card.enabled && hotCardsHandlers[key] && isTagOrRatingBased) { hotCardsHandlers[key](); } } @@ -184,30 +236,41 @@ function handleHotCards(card, isHome = false) { waitForClass(card.class, () => { - createAndInsertHotCards(card.data, card.class, isHome); + createAndInsertHotCards(card.data, card.class, card.config, isHome); + setHotCardStyling(card); }); } /** * Wraps cards in "hot" elements based on specific conditions (tag or rating). * - * On the home page, this function may be triggered by multiple intercepted GraphQL requests, - * each corresponding to a user-customized carousel. + * On the home page, multiple GraphQL requests are intercepted, + * each corresponding to a premade filter / user saved filter. * - * The user is able to customize the home page as desired and add - * several carousels of the same type of resource with different filters saved. - * As a result, several graphql request are intercepted and this function runs - * as many times as the user configured carousels. + * This function is called for each card type enabled to add hot elements. * * The first time it runs, the hotCards array is populated, * so we need an additional flag to differentiate that we are on the home page. * * @param {Object} stashData - Data fetched from the GraphQL interceptor. e.g. stash.performers. * @param {string} cardClass - CSS class used to identify cards in the DOM. e.g. 'performer-card'. + * @param {Object} config - User settings for the current card type. * @param {boolean} isHome - Flag indicating if the current page is the homepage. */ - function createAndInsertHotCards(stashData, cardClass, isHome) { - // To avoid DOM exceptions, it only runs when `hotCards` is empty and we are not in the home page. + function createAndInsertHotCards(stashData, cardClass, config, isHome) { + const isCriterionTag = config.criterion === CRITERIA.tag; + const isCriterionRating = config.criterion === CRITERIA.rating; + const isCriterionEmpty = config.criterion.length === 0; + const valueNotSet = config.value.length === 0; + const isCriterionTagOrEmpty = + isTagBased && (isCriterionTag || isCriterionEmpty); + const isCriterionRatingOrEmpty = + isRatingBased && (isCriterionRating || isCriterionEmpty); + + /** + * To avoid DOM exceptions, it runs if `hotCards` is empty and we are not in the home page + * or if we are in the home page. + */ if (hotCards.length === 0 || isHome) { const cards = document.querySelectorAll(`.${cardClass}`); @@ -216,25 +279,36 @@ const id = new URL(link.href).pathname.split("/").pop(); const data = stashData[id]; - if (isTagBased) { + if (isCriterionTagOrEmpty) { if (data?.tags?.length) { + // If the tag ID for this card type is not set, use the default tag ID. + const tagId = valueNotSet ? TAG_ID : config.value; data.tags.forEach((tag) => { - if (tag.id === TAG_ID) createHotElementAndAttachToDOM(card); + if (tag.id === tagId) + createHotElementAndAttachToDOM(card, cardClass, isHome); }); } - } else if (isRatingBased && data?.rating100 !== null) { - if (data.rating100 >= RATING_THRESHOLD) - createHotElementAndAttachToDOM(card); + } else if (isCriterionRatingOrEmpty && data?.rating100 !== null) { + const rating = isStarsRatingSystem + ? data?.rating100 / 20 + : data.rating100; + // If the rating threshold for this card type is not set, use the default threshold. + const ratingThreshold = valueNotSet ? RATING_THRESHOLD : config.value; + if (rating >= ratingThreshold) + createHotElementAndAttachToDOM(card, cardClass, isHome); } }); } } - function createHotElementAndAttachToDOM(cardElement) { - const hotElement = createElementFromHTML(`
`); + function createHotElementAndAttachToDOM(cardElement, cardClass, isHome) { + const hotElement = createElementFromHTML( + `
` + ); + if (isHome) hotElement.style.height = "100%"; backupCards.push(cardElement); - cardElement.classList.add("hot-card"); + cardElement.classList.add("hot-border"); cardElement.before(hotElement); hotElement.append(cardElement); hotCards.push(hotElement); @@ -246,6 +320,188 @@ return div.firstChild; } + /** + * Sets the style of the hot card based on the user's configuration. + */ + function setHotCardStyling(card) { + const { style, gradient_opts, border_opts } = card.config; + const colors = style.split(INNER_SEPARATOR).map((color) => color.trim()); + const styleElement = document.createElement("style"); + + const pseudoElementStyle = + colors.length === 1 + ? applySingleColorStyle(card, colors[0], border_opts) + : applyCustomGradientStyle(card, colors, gradient_opts, border_opts); + + styleElement.innerHTML = pseudoElementStyle; + document.head.appendChild(styleElement); + } + + /** + * If there's only one color, it can be a style preset or a fixed color. + */ + function applySingleColorStyle(card, color, border_opts) { + if (STYLES[color]) { + const { border, gradient, filter } = STYLES[color]; + setBorder(card, border.color, border.animation); + return getHotCardPseudoElementString( + card, + gradient.generated, + gradient.animation, + filter + ); + } else { + setBorder(card, border_opts.color, border_opts.animation); + return getHotCardPseudoElementString(card, color); + } + } + + /** + * If there are more than one color, it's a custom gradient. + */ + function applyCustomGradientStyle(card, colors, gradient_opts, border_opts) { + const { type, angle, animation } = gradient_opts; + const gradient = getGradient(type, angle, colors); + setBorder(card, border_opts.color, border_opts.animation); + return getHotCardPseudoElementString(card, gradient, animation); + } + + function setBorder(card, color, animation = "") { + animation = animation ? `pulse ${animation}` : ""; + document + .querySelectorAll(`.hot-${card.class} > .hot-border`) + .forEach((card) => { + card.style.setProperty("--border-color", color); + card.style.animation = animation; + }); + } + + function getGradient(type, positionAngle = "", colors) { + const positionAngleStr = positionAngle ? `${positionAngle},` : ""; + return `${type}-gradient(${positionAngleStr} ${colors.join(", ")})`; + } + + function getHotCardPseudoElementString( + card, + background, + gradientAnimation = "", + filter = "" + ) { + const gradientAnimationStr = gradientAnimation + ? `animation: move ${gradientAnimation};` + : ""; + const filterStr = filter ? `filter: ${filter};` : ""; + const hotCardClass = `.hot-${card.class}`; + + return `${hotCardClass}::before, + ${hotCardClass}::after { + content: ""; + position: absolute; + top: calc(0.8 * var(--border-width)); + left: calc(0.8 * var(--border-width)); + width: calc(100% + var(--border-width) * -1.5); + height: calc(100% + var(--border-width) * -1.5); + border-radius: calc(2 * var(--border-width)); + background: ${background}; + background-size: 300% 300%; + background-position: 0 50%; + ${gradientAnimationStr} + } + ${hotCardClass}::after { + ${filterStr} + }`; + } + + function createCardStyle( + borderColor, + borderAnimation, + gradientType, + gradientAngle, + gradientColors, + gradientAnimation, + filter + ) { + return { + border: { + color: borderColor, + animation: borderAnimation, + }, + gradient: { + type: gradientType, + angle: gradientAngle, + colors: gradientColors, + animation: gradientAnimation, + generated: getGradient(gradientType, gradientAngle, gradientColors), + }, + filter, + }; + } + + function getDefaultStylePreset() { + return createCardStyle( + "#ff2409", + "3s ease-in-out infinite", + "linear", + "60deg", + [ + "hsl(224, 85%, 66%)", + "hsl(269, 85%, 66%)", + "hsl(314, 85%, 66%)", + "hsl(359, 85%, 66%)", + "hsl(44, 85%, 66%)", + "hsl(357.2, 87.7%, 52.4%)", + "hsl(301, 70.2%, 50%)", + "hsl(179, 85%, 66%)", + ], + "4s alternate infinite" + ); + } + + function getHotStylePreset() { + return createCardStyle( + "#a41111", + "4s ease-in-out infinite", + "radial", + "", + [ + "hsl(351.7, 86.5%, 62.4%)", + "hsl(351.7, 86.4%, 46.1%)", + "hsl(357, 86.6%, 49.6%)", + "hsl(343.3, 73.1%, 39.4%)", + "hsl(0, 84.9%, 36.5%)", + "hsl(354.4, 72.9%, 40.6%)", + "hsl(348.8, 92.9%, 44.1%)", + "hsl(345, 80%, 49%)", + "hsl(354.5, 83.1%, 46.5%)", + "hsl(357, 86.6%, 49.6%)", + "hsl(328.2, 73.9%, 22.5%)", + "hsl(345, 81%, 49.4%)", + "hsl(0, 70%, 31.4%)", + ], + "20s linear infinite" + ); // 'blur(2.0rem)' + } + + function getGoldStylePreset() { + return createCardStyle( + "#d4af37", + "6s ease-in-out infinite", + "linear", + "45deg", + [ + "hsl(19.9, 62.7%, 52.7%)", + "hsl(45, 90.4%, 40.8%)", + "hsl(40.2, 56.5%, 37.8%)", + "hsl(42.1, 96.5%, 55.1%)", + "hsl(30.4, 100%, 27.1%)", + "hsl(30.8, 49.4%, 45.7%)", + "hsl(20, 85%, 60%)", + "hsl(14.9, 75.8%, 32.4%)", + ], + "8s ease-in-out infinite" + ); + } + /** * Since it was necessary to insert a div before the card for * the border design to be visible (otherwise the overflow:hidden; property of diff --git a/plugins/hotCards/hotCards.yml b/plugins/hotCards/hotCards.yml index 460bc3c..233b5d6 100644 --- a/plugins/hotCards/hotCards.yml +++ b/plugins/hotCards/hotCards.yml @@ -1,6 +1,6 @@ name: Hot Cards -description: Adds custom css to card elements that match a tag id or a rating threshold. -version: 1.0.0 +description: Adds custom styling to card elements that match a tag ID or a rating threshold. +version: 1.1.0 url: https://github.com/stashapp/CommunityScripts/tree/main/plugins/hotCards # requires: CommunityScriptsUILibrary ui: @@ -17,37 +17,37 @@ ui: settings: tagId: displayName: Tag ID - description: The tag ID to match against. Leave blank to disable tag-based hot cards. + description: Tag ID to match against. Leave blank to disable tag-based hot cards. type: STRING threshold: displayName: Rating Threshold - description: The rating threshold (0-100) to match against. Set to 0 to disable rating-based hot cards. + description: Rating threshold (0-5). Use 6-100 for tenths. Set to 0 to disable rating-based hot cards. type: NUMBER home: - displayName: Enable on home - description: Enable hot cards on the home page. + displayName: Enable for the homepage + description: Enable Hot Cards on the home page. type: BOOLEAN scenes: - displayName: Enable on scenes - description: Enable hot cards on scene cards. - type: BOOLEAN + displayName: Enable for scenes + description: "Empty to enable, type 'd' to disable. Customize: [criterion]_[value]_[style]_[gradient-opts]_[border-opts]. See docs." + type: STRING images: - displayName: Enable on images - description: Enable hot cards on image cards. - type: BOOLEAN + displayName: Enable for images + description: "Empty to enable, type 'd' to disable. Customize: [criterion]_[value]_[style]_[gradient-opts]_[border-opts]. See docs." + type: STRING movies: - displayName: Enable on movies - description: Enable hot cards on movie cards. - type: BOOLEAN + displayName: Enable for movies + description: "Empty to enable, type 'd' to disable. Customize: [criterion]_[value]_[style]_[gradient-opts]_[border-opts]. See docs." + type: STRING galleries: - displayName: Enable on galleries - description: Enable hot cards on gallery cards. - type: BOOLEAN + displayName: Enable for galleries + description: "Empty to enable, type 'd' to disable. Customize: [criterion]_[value]_[style]_[gradient-opts]_[border-opts]. See docs." + type: STRING performers: - displayName: Enable on performers - description: Enable hot cards on performer cards. - type: BOOLEAN + displayName: Enable for performers + description: "Empty to enable, type 'd' to disable. Customize: [criterion]_[value]_[style]_[gradient-opts]_[border-opts]. See docs." + type: STRING studios: - displayName: Enable on studios - description: Enable hot cards on studio cards. - type: BOOLEAN + displayName: Enable for studios + description: "Empty to enable, type 'd' to disable. Customize: [criterion]_[value]_[style]_[gradient-opts]_[border-opts]. See docs." + type: STRING From 263f6fd41f8979218649fd79c04255842df41344 Mon Sep 17 00:00:00 2001 From: skier233 <39396856+skier233@users.noreply.github.com> Date: Thu, 4 Jul 2024 04:02:30 -0400 Subject: [PATCH 2/5] [AI Tagger] Add path mutation (and by extension docker) support (#353) * add path mutation (and by extension docker) support * update version --- plugins/AITagger/ai_tagger.py | 11 +++++++++++ plugins/AITagger/ai_tagger.yml | 2 +- plugins/AITagger/config.py | 6 +++++- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/plugins/AITagger/ai_tagger.py b/plugins/AITagger/ai_tagger.py index 8d06e60..e8d966b 100644 --- a/plugins/AITagger/ai_tagger.py +++ b/plugins/AITagger/ai_tagger.py @@ -129,6 +129,13 @@ async def tag_scenes(): async def __tag_images(images): async with semaphore: imagePaths, imageIds, temp_files = media_handler.get_image_paths_and_ids(images) + mutated_image_paths = [] + for path in imagePaths: + mutated_path = path + for key, value in config.path_mutation.items(): + mutated_path = mutated_path.replace(key, value) + mutated_image_paths.append(mutated_path) + imagePaths = mutated_image_paths try: server_result = await ai_server.process_images_async(imagePaths) if server_result is None: @@ -175,6 +182,10 @@ async def __tag_images(images): async def __tag_scene(scene): async with semaphore: scenePath = scene['files'][0]['path'] + mutated_path = scenePath + for key, value in config.path_mutation.items(): + mutated_path = mutated_path.replace(key, value) + scenePath = mutated_path sceneId = scene['id'] log.debug("files result:" + str(scene['files'][0])) phash = scene['files'][0].get('fingerprint', None) diff --git a/plugins/AITagger/ai_tagger.yml b/plugins/AITagger/ai_tagger.yml index d422660..84bc850 100644 --- a/plugins/AITagger/ai_tagger.yml +++ b/plugins/AITagger/ai_tagger.yml @@ -1,6 +1,6 @@ name: AI Tagger description: Tag videos and Images with Locally hosted AI using Skier's Patreon AI models -version: 1.3 +version: 1.4 url: https://github.com/stashapp/CommunityScripts/tree/main/plugins/AITagger exec: - python diff --git a/plugins/AITagger/config.py b/plugins/AITagger/config.py index 1cd4d1c..3f504e4 100644 --- a/plugins/AITagger/config.py +++ b/plugins/AITagger/config.py @@ -13,4 +13,8 @@ ai_base_tag_name = "AI" tagme_tag_name = "AI_TagMe" updateme_tag_name = "AI_UpdateMe" aitagged_tag_name = "AI_Tagged" -aierrored_tag_name = "AI_Errored" \ No newline at end of file +aierrored_tag_name = "AI_Errored" + +# Example for mutating paths +# path_mutation = {"E:": "F:", "G:": "D:"} +path_mutation = {} From 9e3f6ae052f2c9ac24c0d4232579e68f83c4b04a Mon Sep 17 00:00:00 2001 From: HandyRandyx Date: Fri, 5 Jul 2024 08:19:27 -0300 Subject: [PATCH 3/5] [Hot Cards] Improve README for clearer text and some code enhancements (#356) * Fix data is undefined error The error wasn't breaking anything, but it was unnecesary for it to happen. * Add flexibility to also modify style presets The gradient angle and animation, as well as the border color and border animation of the style presets can now be modified using the field format. * Update README with images and enhancements * Update version * Update README --- plugins/hotCards/README.md | 132 +++++++++++++++------------- plugins/hotCards/assets/default.png | Bin 0 -> 151697 bytes plugins/hotCards/assets/gold.png | Bin 0 -> 127171 bytes plugins/hotCards/assets/hot.png | Bin 0 -> 152953 bytes plugins/hotCards/hotCards.js | 64 ++++++++++---- plugins/hotCards/hotCards.yml | 2 +- 6 files changed, 117 insertions(+), 81 deletions(-) create mode 100644 plugins/hotCards/assets/default.png create mode 100644 plugins/hotCards/assets/gold.png create mode 100644 plugins/hotCards/assets/hot.png diff --git a/plugins/hotCards/README.md b/plugins/hotCards/README.md index d3ed3d2..0db47da 100644 --- a/plugins/hotCards/README.md +++ b/plugins/hotCards/README.md @@ -1,6 +1,6 @@ # Hot Cards -Hot Cards is a Stash CommunityScript plugin designed to enhance your visual experience by applying custom styling to card elements based on a Tag ID or a Rating Threshold. This plugin is perfect for highlighting certain performers, scenes, studios, movies, images, or galleries. +Hot Cards is a Stash CommunityScript plugin designed to enhance your visual experience by applying custom styling to card elements based on a Tag ID or a Rating Threshold. This plugin is perfect for highlighting certain performers or scenes and making sure you don't forget them! ## Features @@ -20,106 +20,114 @@ Hot Cards is a Stash CommunityScript plugin designed to enhance your visual expe After installation, you can configure the plugin to suit your needs. Set a desired Tag ID or Rating Threshold and enable Hot Cards for the card types you want. Customize the appearance of Hot Cards for each type of card (scene, image, movie, gallery, performer, studio) using the format provided or leave the fields empty to apply the default style. -### Custom String Format: +### Configure the field format: _[criterion]\_[value]\_[style]\_[gradient-opts]\_[border-opts]_ **Important**: If you have previously installed the plugin, after updating to `1.1.0`, be sure to update your settings from the old boolean format to the new string format. Refresh the page for the changes to take effect. -1. **criterion**: `` - - `t` for tag-based, `r` for rating-based,`d` for disabled. You can also leave it `` and it will grab the global value of the _Tag ID_ or _Rating Threshold_ as configured. - -2. **value**: `` - - Specific value for Tag ID or Rating Threshold. - -3. **style**: `` (comma separated) - - - Fixed color: **#5ff2a2** - - Style preset: **hot** - - Gradient: **#ef1313,#3bd612,...** (Hex color codes, color names) - -4. **gradient_opts**: `,,` (comma separated) - - Example: **linear,35deg,4s alternate infinite** - -5. **border_opts**: `,` (comma separated) - - Example: **#ff0000,2s ease infinite** +| Parameter | Description | +| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `` | Defines the basis for applying styles. Use `t` for tag-based criteria, `r` for rating-based criteria, or `d` to disable. If left empty, it will default to the global Tag ID or Rating Threshold configuration. If both options are enabled and unspecified, the Tag ID will be used by default. | +| `` | Specifies the exact value for the Tag ID or Rating Threshold to be used. | +| `