diff --git a/plugins/SecondaryPerformerImage/README.md b/plugins/SecondaryPerformerImage/README.md new file mode 100644 index 00000000..acecdf4d --- /dev/null +++ b/plugins/SecondaryPerformerImage/README.md @@ -0,0 +1,12 @@ +# Secondary Performer Images + +This plugin adds support for a secondary performer image on the performer details page, enabling more customization. A new `Set secondary image...` button will be available on the performer edit page to provide this new image. Performers without a secondary image will continue to behave as they've always done. + +## Modes +The plugin offers three different display modes that can be set for the use of the secondary image. The modes range from 0-2, with 0 being the default mode. Any values that fall outside this range will be processed as the default mode. + +0 - Specify 0 to enable the secondary image to be used in the expanded view. You might consider using this mode if your performer image currently consists of headshots and you want to provide fuller body images for the expanded view. + +1 - Specify 1 to enable the secondary image to be used in the collapsed view. You might consider using this mode if your performer image currently consists of body shots and you want to provide headshots for the collapsed view. + +2 - Specify 2 to enable a button to be used to flip between the primary and secondary image. This mode gives you the option to provide whatever combination of images you want to use together. Some might consider using this option to provide front and back images of the performer. diff --git a/plugins/SecondaryPerformerImage/SecondaryPerformerImage.css b/plugins/SecondaryPerformerImage/SecondaryPerformerImage.css new file mode 100644 index 00000000..28348c20 --- /dev/null +++ b/plugins/SecondaryPerformerImage/SecondaryPerformerImage.css @@ -0,0 +1 @@ +#performer-page .detail-header:not(.edit) .perf-images{position:relative;z-index:1}#performer-page .detail-header:not(.edit) .perf-images button.btn.btn-link{padding:0;position:relative;transition:all .3s;z-index:1}#performer-page .detail-header:not(.edit) .perf-images .active button{z-index:2}#performer-page .detail-header:not(.edit) .perf-images .inactive img{display:none}#performer-page .detail-header:not(.edit) .perf-images .inactive .detail-header-image{padding:0}#performer-page .detail-header:not(.edit) .perf-images .inactive button{opacity:.5;padding:0;transform:rotateY(180deg)}#performer-page .detail-header:not(.edit) .perf-images button.flip{align-items:center;border-radius:50%;bottom:-5px;display:flex;font-size:20px;height:40px;justify-content:center;padding:0;position:absolute;right:-5px;width:40px;z-index:2}#performer-page:has(button.flip) .perf-images{display:flex;float:left;height:auto;justify-content:center}.edit .secondary-image{display:none}.secondary-image-popover{padding:5px}.secondary-image-popover .secondary-image-thumbnail{max-width:22rem;object-fit:cover;object-position:top}.performer-head .custom-fields .detail-item.alt-image{display:none} diff --git a/plugins/SecondaryPerformerImage/SecondaryPerformerImage.js b/plugins/SecondaryPerformerImage/SecondaryPerformerImage.js new file mode 100644 index 00000000..58e4cf26 --- /dev/null +++ b/plugins/SecondaryPerformerImage/SecondaryPerformerImage.js @@ -0,0 +1 @@ +(()=>{"use strict";var e={264:(e,t,a)=>{a.r(t)},577:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0});const{PluginApi:a}=window,{React:n}=a,r=(e,t)=>{const a=new FileReader;a.onloadend=()=>{a.error||t(a.result)},a.readAsDataURL(e)},o={onImageChange:(e,t)=>{var a,n;const o=null===(n=null===(a=null==e?void 0:e.currentTarget)||void 0===a?void 0:a.files)||void 0===n?void 0:n[0];o&&r(o,t)},usePasteImage:(e,t=!0)=>{const a=n.useCallback((t=>{e(t)}),[e]);return n.useEffect((()=>{const e=e=>((e,t)=>{var a;const n=null===(a=null==e?void 0:e.clipboardData)||void 0===a?void 0:a.files;if(!(null==n?void 0:n.length))return;const o=n[0];r(o,t)})(e,a);return t&&document.addEventListener("paste",e),()=>document.removeEventListener("paste",e)}),[t,a]),!1},imageToDataURL:async e=>{const t=await fetch(e),a=await t.blob();return new Promise(((e,t)=>{const n=new FileReader;n.onloadend=()=>{e(n.result)},n.onerror=t,n.readAsDataURL(a)}))}};t.default=o},604:function(e,t,a){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.handlePerformerHeaderImagePatch=function(){o.patch.instead("PerformerHeaderImage",(function(e,t,a){var n,r;const{encodingImage:s,collapsed:u,activeImage:m,lightboxImages:d,performer:g}=e,{Button:v}=o.libraries.Bootstrap,{Icon:f}=o.components,{faRefresh:p}=o.libraries.FontAwesomeSolid,[y,h]=l.useState(!0),E=i.useConfigurationQuery(),[b]=c.usePerformerUpdate();void 0===(null===(n=g.custom_fields)||void 0===n?void 0:n.alt_image)&&b({variables:{input:{id:g.id,custom_fields:{full:{alt_image:""}}}}});const I=a({...e});return!E.loading&&(null===(r=g.custom_fields)||void 0===r?void 0:r.alt_image)?l.createElement(l.Fragment,null,function(){var e,t;const n=a({encodingImage:s,activeImage:null===(e=g.custom_fields)||void 0===e?void 0:e.alt_image,lightboxImages:d,performer:g}),r=E.data.configuration.plugins.SecondaryPerformerImage;let o=null!==(t=null==r?void 0:r.imageMode)&&void 0!==t?t:0;if((o<0||o>2)&&(o=0),2==o)return l.createElement("div",{className:"perf-images"},l.createElement("div",{className:"primary-image "+(y?"active":"inactive")},I),l.createElement("div",{className:"secondary-image "+(y?"inactive":"active")},n),l.createElement(v,{className:"flip",onClick:()=>h(!y)},l.createElement(f,{icon:p})));{let e=0==o?u:!u;return l.createElement("div",{className:"perf-images"},l.createElement("div",{className:"primary-image "+(e?"active":"inactive")},I),l.createElement("div",{className:"secondary-image "+(e?"inactive":"active")},n))}}()):l.createElement(l.Fragment,null,I)})),o.patch.instead("ImageInput",(function(e,t,a){var n;const{isEditing:o,text:i,onImageChange:c,onImageURL:s,acceptSVG:u}=e,m=document.querySelector("#performer-page"),d=null===(n=Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype,"value"))||void 0===n?void 0:n.set,g=document.querySelector("div.custom-fields-input > button"),v=document.querySelector("div.custom-fields-input > div"),f=document.querySelector('input[placeholder="alt_image"]');async function p(e){if(!e||!f||!d)return;var t;"collapse"==v.getAttribute("class")&&(g.focus(),g.click(),await(t=200,new Promise((e=>setTimeout(e,t)))),f.focus()),null==d||d.call(f,e);const a=new Event("change",{bubbles:!0});f.dispatchEvent(a),f.focus()}const y=a({...e}),h=a({isEditing:o,text:"Set secondary image...",onImageChange:function(e){r.default.onImageChange(e,p)},onImageURL:p,acceptSVG:u});return m?l.createElement(l.Fragment,null,y,h):l.createElement(l.Fragment,null,y)})),o.patch.instead("CustomFieldInput",(function(e,t,a){const{field:n,value:r,onChange:i,isNew:c=!1,error:s}=e,{HoverPopover:u}=o.components,m=l.useMemo((()=>l.createElement("div",{className:"secondary-image-popover"},l.createElement("img",{className:"secondary-image-thumbnail",alt:n,src:r}))),[r]),d=a({...e});return"alt_image"===n&&r?l.createElement(u,{className:"scene-card__performer",placement:"top",content:m,leaveDelay:100},d):l.createElement(l.Fragment,null,d)})),o.patch.instead("CustomFields",(function(e,t,a){const{values:n}=e;if(Object.keys(n).length<=1)return l.createElement(l.Fragment,null);const r=a({...e});return l.createElement(l.Fragment,null,r)}))};const r=n(a(577)),{PluginApi:o}=window,{GQL:i,React:l}=o,{StashService:c}=window.PluginApi.utils}},t={};function a(n){var r=t[n];if(void 0!==r)return r.exports;var o=t[n]={exports:{}};return e[n].call(o.exports,o,o.exports,a),o.exports}a.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};(()=>{const e=a(604);a(264),(0,e.handlePerformerHeaderImagePatch)()})()})(); \ No newline at end of file diff --git a/plugins/SecondaryPerformerImage/SecondaryPerformerImage.yml b/plugins/SecondaryPerformerImage/SecondaryPerformerImage.yml new file mode 100644 index 00000000..b6ddc7dd --- /dev/null +++ b/plugins/SecondaryPerformerImage/SecondaryPerformerImage.yml @@ -0,0 +1,16 @@ +name: Add secondary perfomrer image. +description: Adds support for a secondary perfomrer image on the perform details page. +url: https://github.com/stashapp/CommunityScripts +version: 1.0 +settings: + imageMode: + displayName: Image Mode + description: Mode at which to display the performer image. There are only 3 valid values. Specify 0 to enable the secondary image to be used in the expanded view. Specify 1 to enable the secondary image to be used in the collapsed view. Specify 2 to enable a button to be used to flip between the primary and secondary image. + type: NUMBER +ui: + javascript: + - SecondaryPerformerImage.js + css: + - SecondaryPerformerImage.css + assets: + /: .