├── plugins ├── DupFileManager │ ├── .gitignore │ ├── requirements.txt │ ├── DupFileManager.css.map │ └── DupFileManager.css ├── dupeMarker │ ├── requirements.txt │ ├── dupeMarker.yml │ ├── README.md │ └── dupeMarker.py ├── titleFromFilename │ ├── requirements.txt │ ├── config.py │ ├── titleFromFilename.yml │ ├── README.md │ ├── log.py │ └── titleFromFilename.py ├── LocalVisage │ ├── stashface │ │ ├── utils │ │ │ ├── __init__.py │ │ │ └── vtt_parser.py │ │ ├── web │ │ │ └── __init__.py │ │ ├── models │ │ │ └── __init__.py │ │ └── app.py │ ├── docker-compose.yaml │ ├── Dockerfile │ └── LocalVisage.yml ├── TPDBMarkers │ ├── requirements.txt │ ├── README.md │ └── TPDBMarkers.yml ├── tagGalleriesFromImages │ ├── requirements.txt │ └── tagGalleriesFromImages.yml ├── tagImagesWithPerfTags │ ├── requirements.txt │ ├── README.md │ └── tagImagesWithPerfTags.yml ├── tagScenesWithPerfTags │ ├── requirements.txt │ ├── README.md │ └── tagScenesWithPerfTags.yml ├── videoChapterMarkers │ ├── requirements.txt │ ├── README.md │ └── videoChapterMarkers.yml ├── AdulttimeInteractiveDL │ ├── requirements.txt │ ├── README.md │ └── AdulttimeInteractiveDL.yml ├── PythonToolsInstaller │ ├── packages │ │ └── stashtools.txt │ ├── PythonToolsInstaller.yml │ └── PythonToolsInstaller.py ├── additionalFilesDeleter │ ├── requirements.txt │ ├── README.md │ └── deleter.yml ├── comicInfoExtractor │ ├── requirements.txt │ ├── config.yml │ ├── comicInfoExtractor.yml │ └── README.md ├── timestampTrade │ ├── requirements.txt │ └── README.md ├── starIdentifier │ ├── requirements.txt │ ├── star_identifier_config.py │ ├── star_identifier.yml │ └── log.py ├── DateParser │ ├── requirements.txt │ ├── README.md │ └── date_parser.yml ├── PlexSync │ ├── requirements.txt │ ├── TODO.md │ ├── README.md │ └── PlexSync.yml ├── stashdb-performer-gallery │ ├── requirements.txt │ ├── README.md │ └── stashdb-performer-gallery.yml ├── RenameFile │ ├── requirements.txt │ ├── renamefile.css │ └── version_history │ │ └── README.md ├── miscTags │ └── README.md ├── stashAI │ ├── README.md │ └── stashai.yml ├── AudioPlayer │ ├── README.md │ ├── AudioPlayer.yml │ └── AudioPlayer.css ├── VideoBanner │ ├── README.md │ └── VideoBanner.yml ├── stashNotes │ ├── README.md │ └── stashNotes.yml ├── stats │ ├── README.md │ └── stats.yml ├── AITagger │ ├── requirements.txt │ ├── README.md │ ├── utility.py │ ├── config.py │ └── ai_tagger.yml ├── PythonDepManager │ ├── __init__.py │ ├── PythonDepManager.yml │ ├── flush.py │ └── log.py ├── AudioPlayerLite │ ├── README.md │ ├── AudioPlayerLite.yml │ ├── AudioPlayerLite.css │ └── AudioPlayerLite.js ├── FileMonitor │ ├── requirements.txt │ └── version_history │ │ └── README.md ├── filenameParser │ ├── README.md │ └── filenameParser.yml ├── ThumbPreviews │ ├── README.md │ └── ThumbPreviews.yml ├── VideoScrollWheel │ ├── README.md │ └── VideoScrollWheel.yml ├── audio-transcodes │ ├── README.md │ └── audio-transcodes.yml ├── funscriptMarkers │ ├── README.md │ └── funscriptMarkers.yml ├── setSceneCoverFromFile │ ├── README.md │ ├── set_scene_cover.yml │ └── set_cover.py ├── hotCards │ ├── assets │ │ ├── hot.png │ │ ├── gold.png │ │ ├── holo.png │ │ ├── default.png │ │ └── illusion.webp │ ├── utils │ │ ├── fetchInterceptor.js │ │ └── stashHandler.js │ └── hotCards.yml ├── sceneCoverCropper │ ├── README.md │ └── sceneCoverCropper.yml ├── ExtraPerformerInfo │ ├── README.md │ └── extraPerformerInfo.yml ├── markerDeleteButton │ ├── README.md │ ├── markerDeleteButton.css │ └── markerDeleteButton.yml ├── bulkImageScrape │ ├── res │ │ ├── task.png │ │ ├── running.png │ │ ├── settings.png │ │ ├── valid_scraper_id.png │ │ └── valid_image_scraper.png │ ├── requirements.txt │ └── BulkImageScrape.yml ├── markerTagToScene │ ├── README.md │ └── markerTagToScene.yml ├── image_date_from_metadata │ ├── README.md │ └── image_date_from_metadata.yml ├── themeSwitch │ ├── assets │ │ ├── themes │ │ │ └── materialize │ │ │ │ └── source.txt │ │ └── snippets │ │ │ ├── scenes │ │ │ ├── show-extra-info.css │ │ │ ├── hide-studio-logo.css │ │ │ ├── tags-list-sess-width.css │ │ │ ├── hide-scene-Scrubber.css │ │ │ ├── hide-scene-specs.css │ │ │ ├── disable-zoom-wall-mode.css │ │ │ ├── hide-truncated-text.css │ │ │ ├── adjust-mouse-wall-mode.css │ │ │ ├── longer-string-studio.css │ │ │ └── swap-studio-res-duration.css │ │ │ ├── global │ │ │ ├── hide-o-count-badges.css │ │ │ ├── hide-donate-button.css │ │ │ ├── sticky-tool-bar.css │ │ │ ├── blur-nsfw-images.css │ │ │ └── unblur-nsfw-images-on-mouse-hover.css │ │ │ ├── images │ │ │ ├── disable-lightbox-annimation.css │ │ │ └── dont-crop-preview-thumbnails.css │ │ │ ├── performers │ │ │ ├── show-entire-performer-image.css │ │ │ ├── move-tag-field-to-top.css │ │ │ └── performer-image-as-backdrop.css │ │ │ ├── studios │ │ │ ├── more-studios-row.css │ │ │ └── different-studio-cards-layout.css │ │ │ ├── movies │ │ │ ├── desktop-better-layout-regular.css │ │ │ └── desktop-better-layout-larger.css │ │ │ ├── galleries │ │ │ └── galleries-grid-view.css │ │ │ └── tags │ │ │ ├── hide-tag-images.css │ │ │ ├── different-tag-cards.css │ │ │ └── alternative-tag-layout.css │ └── themeSwitch.yml ├── performerStashboxUrlToID │ ├── README.md │ └── performerStashboxUrlToID.yml ├── stashAppAndroidTvCompanion │ ├── README.md │ ├── stashAppAndroidTvCompanion.yml │ └── stashAppAndroidTvCompanion.py ├── chooseYourAdventurePlayer │ ├── Sample │ │ └── 1234 │ │ │ ├── images │ │ │ ├── bad.jpg │ │ │ ├── good.jpg │ │ │ ├── poster-1.png │ │ │ ├── poster-2.png │ │ │ └── end-screen.png │ │ │ ├── videos │ │ │ ├── Scene0.mp4 │ │ │ ├── Scene1a.mp4 │ │ │ ├── Scene1b.mp4 │ │ │ ├── Scene2A-1.mp4 │ │ │ ├── Scene2A-2.mp4 │ │ │ ├── Scene2A.mp4 │ │ │ └── Scene2B.mp4 │ │ │ └── choices │ │ │ ├── 2-e.json │ │ │ ├── 1b-e.json │ │ │ ├── 1a.json │ │ │ ├── 1b.json │ │ │ ├── 0.json │ │ │ └── 2.json │ └── chooseYourAdventurePlayer.yml ├── stashNotifications │ ├── README.md │ ├── stashNotifications.yml │ └── stashNotifications.css ├── untagRedundantTags │ ├── requirements.txt │ └── README.md ├── performer-poster-backdrop │ ├── assets │ │ └── performer-poster-backdrop-example.png │ ├── performer-poster-backdrop.yml │ └── performer-poster-backdrop.css ├── CommunityScriptsUILibrary │ └── CommunityScriptsUILibrary.yml ├── e621_tagger │ ├── requirements.txt │ ├── README.md │ └── e621_tagger.yml ├── tagCopyPaste │ ├── tagCopyPaste.css │ ├── tagCopyPaste.yml │ └── README.md ├── SFWSwitch │ ├── sfwswitch.yml │ └── README.md ├── externalLinksEnhanced │ ├── CHANGELOG.md │ ├── custom │ │ ├── custom.json │ │ ├── bluesky.svg │ │ └── onlyfans.svg │ ├── externalLinksEnhanced.yml │ └── externalLinksEnhanced.css ├── AIOverhaul │ ├── README.md │ ├── AIOverhaul.yml │ └── css │ │ └── SimilarScenes.css ├── StashRandomButton │ ├── random_button.css │ ├── random_button.yml │ └── LICENSE.md ├── scenePageRememberStates │ ├── README.md │ ├── scenePageRememberStates.yml │ └── scenePageRememberStates.js ├── defaultDataForPath │ └── defaultDataForPath.yml ├── nfoSceneParser │ ├── nfoSceneParser.yml │ ├── abstractParser.py │ └── log.py ├── imageGalleryNavigation │ ├── imageGalleryNavigation.yml │ ├── imageGalleryNavigation.css │ └── README.md ├── SecondaryPerformerImage │ ├── SecondaryPerformerImage.yml │ ├── SecondaryPerformerImage.css │ └── README.md ├── setPerformersFromTags │ └── setPerformersFromTags.yml ├── pathParser │ └── pathParser.yml └── cjCardTweaks │ ├── cjCardTweaks.yml │ └── README.md ├── .gitignore ├── archive ├── renamerOnUpdate │ ├── .gitignore │ ├── renamerOnUpdate.yml │ └── log.py ├── README.md ├── visage │ └── visage.yml └── stashRealbooru │ ├── stash-realbooru.yml │ └── stash-realbooru.css ├── scripts ├── Sqlite_Renamer │ └── requirements.txt ├── stash-watcher │ ├── requirements.txt │ ├── Dockerfile │ ├── config.toml │ └── defaults.toml └── kodi-helper │ └── config.py ├── themes ├── Theme-Night │ ├── README.md │ └── Theme-Night.yml ├── Theme-PornHub │ ├── README.md │ └── Theme-PornHub.yml ├── Theme-Pulsar │ ├── README.md │ └── Theme-Pulsar.yml ├── Theme-NeonDark │ ├── README.md │ └── Theme-NeonDark.yml ├── Theme-BlackHole │ ├── README.md │ ├── Theme-BlackHole.yml │ └── Theme-BlackHole.css ├── Theme-ModernDark │ ├── README.md │ └── Theme-ModernDark.yml ├── Theme-ColorPalette │ ├── README.md │ ├── Theme-ColorPalette.yml │ └── Theme-ColorPalette.js ├── Theme-PulsarLight │ ├── README.md │ └── Theme-PulsarLight.yml ├── Theme-Minimal │ ├── assets │ │ └── Outfit-VariableFont_wght.woff2 │ ├── skeleton.css │ ├── modal.css │ ├── scene-tagger.css │ ├── popover.css │ ├── studio-tagger.css │ ├── stats.css │ ├── Theme-Minimal.yml │ ├── reset-scrollbars.js │ ├── _scrollbars.css │ ├── nav-bar.css │ ├── studio-performer-tagger.css │ ├── settings.css │ ├── markers-wall.css │ └── README.md ├── Theme-Plex │ ├── Theme-Plex.yml │ └── README.md ├── Theme-RoundedYellow │ ├── Theme-RoundedYellow.yml │ └── README.md └── README.md ├── validate.js ├── .vscode ├── extensions.json └── settings.json ├── package.json ├── .editorconfig ├── validator └── package.json ├── .github └── workflows │ ├── validate.yml │ └── deploy.yml ├── userscripts ├── StashDB_Submission_Helper │ └── README.md └── FansDB_Submission_Helper │ └── README.md └── README.md /plugins/DupFileManager/.gitignore: -------------------------------------------------------------------------------- 1 | *.log -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /_site 2 | node_modules 3 | dist -------------------------------------------------------------------------------- /archive/renamerOnUpdate/.gitignore: -------------------------------------------------------------------------------- 1 | config.py -------------------------------------------------------------------------------- /plugins/dupeMarker/requirements.txt: -------------------------------------------------------------------------------- 1 | stashapp-tools -------------------------------------------------------------------------------- /plugins/titleFromFilename/requirements.txt: -------------------------------------------------------------------------------- 1 | requests 2 | -------------------------------------------------------------------------------- /plugins/LocalVisage/stashface/utils/__init__.py: -------------------------------------------------------------------------------- 1 | # utils package -------------------------------------------------------------------------------- /plugins/LocalVisage/stashface/web/__init__.py: -------------------------------------------------------------------------------- 1 | # web package -------------------------------------------------------------------------------- /scripts/Sqlite_Renamer/requirements.txt: -------------------------------------------------------------------------------- 1 | progressbar2~=4.4.2 2 | -------------------------------------------------------------------------------- /plugins/LocalVisage/stashface/models/__init__.py: -------------------------------------------------------------------------------- 1 | # models package -------------------------------------------------------------------------------- /plugins/TPDBMarkers/requirements.txt: -------------------------------------------------------------------------------- 1 | requests 2 | stashapp-tools 3 | -------------------------------------------------------------------------------- /plugins/tagGalleriesFromImages/requirements.txt: -------------------------------------------------------------------------------- 1 | stashapp-tools 2 | -------------------------------------------------------------------------------- /plugins/tagImagesWithPerfTags/requirements.txt: -------------------------------------------------------------------------------- 1 | stashapp-tools 2 | -------------------------------------------------------------------------------- /plugins/tagScenesWithPerfTags/requirements.txt: -------------------------------------------------------------------------------- 1 | stashapp-tools 2 | -------------------------------------------------------------------------------- /plugins/videoChapterMarkers/requirements.txt: -------------------------------------------------------------------------------- 1 | stashapp-tools>=0.2.58 -------------------------------------------------------------------------------- /plugins/AdulttimeInteractiveDL/requirements.txt: -------------------------------------------------------------------------------- 1 | stashapp-tools>=0.2.33 -------------------------------------------------------------------------------- /plugins/PythonToolsInstaller/packages/stashtools.txt: -------------------------------------------------------------------------------- 1 | stashapp-tools 2 | -------------------------------------------------------------------------------- /plugins/additionalFilesDeleter/requirements.txt: -------------------------------------------------------------------------------- 1 | stashapp-tools==0.2.40 -------------------------------------------------------------------------------- /plugins/comicInfoExtractor/requirements.txt: -------------------------------------------------------------------------------- 1 | stashapp-tools 2 | pyyaml 3 | -------------------------------------------------------------------------------- /plugins/timestampTrade/requirements.txt: -------------------------------------------------------------------------------- 1 | requests 2 | stashapp-tools 3 | -------------------------------------------------------------------------------- /plugins/starIdentifier/requirements.txt: -------------------------------------------------------------------------------- 1 | face_recognition 2 | numpy 3 | requests -------------------------------------------------------------------------------- /plugins/DateParser/requirements.txt: -------------------------------------------------------------------------------- 1 | dateparser>=1.1.3 2 | stashapp-tools>=0.2.17 -------------------------------------------------------------------------------- /plugins/PlexSync/requirements.txt: -------------------------------------------------------------------------------- 1 | stashapp-tools 2 | unidecode 3 | requests 4 | -------------------------------------------------------------------------------- /plugins/stashdb-performer-gallery/requirements.txt: -------------------------------------------------------------------------------- 1 | requests 2 | stashapp-tools 3 | -------------------------------------------------------------------------------- /plugins/RenameFile/requirements.txt: -------------------------------------------------------------------------------- 1 | stashapp-tools >= 0.2.50 2 | requests 3 | psutil -------------------------------------------------------------------------------- /themes/Theme-Night/README.md: -------------------------------------------------------------------------------- 1 | # Night 2 | 3 | https://discourse.stashapp.cc/t/night/1425 -------------------------------------------------------------------------------- /validate.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict'; 3 | require('./validator/index.js')(); -------------------------------------------------------------------------------- /plugins/DupFileManager/requirements.txt: -------------------------------------------------------------------------------- 1 | stashapp-tools >= 0.2.50 2 | requests 3 | Send2Trash -------------------------------------------------------------------------------- /plugins/miscTags/README.md: -------------------------------------------------------------------------------- 1 | # Misc Tags 2 | 3 | https://discourse.stashapp.cc/t/misc-tags/1383 -------------------------------------------------------------------------------- /plugins/stashAI/README.md: -------------------------------------------------------------------------------- 1 | # Stash AI 2 | 3 | https://discourse.stashapp.cc/t/stash-ai/1392 -------------------------------------------------------------------------------- /scripts/stash-watcher/requirements.txt: -------------------------------------------------------------------------------- 1 | argparse 2 | stashapp-tools 3 | watchdog<=4.0.2 4 | -------------------------------------------------------------------------------- /themes/Theme-PornHub/README.md: -------------------------------------------------------------------------------- 1 | # Pornhub 2 | 3 | https://discourse.stashapp.cc/t/pornhub/1428 -------------------------------------------------------------------------------- /themes/Theme-Pulsar/README.md: -------------------------------------------------------------------------------- 1 | # Pulsar 2 | 3 | https://discourse.stashapp.cc/t/pulsar/1429 -------------------------------------------------------------------------------- /plugins/titleFromFilename/config.py: -------------------------------------------------------------------------------- 1 | # strip file extension from title 2 | STRIP_EXT = True 3 | -------------------------------------------------------------------------------- /scripts/kodi-helper/config.py: -------------------------------------------------------------------------------- 1 | api_key = "" 2 | server_url = "http://localhost:9999/graphql" 3 | -------------------------------------------------------------------------------- /themes/Theme-NeonDark/README.md: -------------------------------------------------------------------------------- 1 | # NeonDark 2 | 3 | https://discourse.stashapp.cc/t/neondark/1424 -------------------------------------------------------------------------------- /plugins/AudioPlayer/README.md: -------------------------------------------------------------------------------- 1 | # AudioPlayer 2 | 3 | https://discourse.stashapp.cc/t/audioplayer/1328 -------------------------------------------------------------------------------- /plugins/VideoBanner/README.md: -------------------------------------------------------------------------------- 1 | # Video Banner 2 | 3 | https://discourse.stashapp.cc/t/video-banner/1927 -------------------------------------------------------------------------------- /plugins/stashNotes/README.md: -------------------------------------------------------------------------------- 1 | # Stash Notes 2 | 3 | https://discourse.stashapp.cc/t/stash-notes/1410 -------------------------------------------------------------------------------- /plugins/stats/README.md: -------------------------------------------------------------------------------- 1 | # Extended Stats 2 | 3 | https://discourse.stashapp.cc/t/extended-stats/1412 -------------------------------------------------------------------------------- /themes/Theme-BlackHole/README.md: -------------------------------------------------------------------------------- 1 | # BlackHole 2 | 3 | https://discourse.stashapp.cc/t/blackhole/1419 -------------------------------------------------------------------------------- /themes/Theme-ModernDark/README.md: -------------------------------------------------------------------------------- 1 | # ModernDark 2 | 3 | https://discourse.stashapp.cc/t/moderndark/1423 -------------------------------------------------------------------------------- /plugins/AITagger/requirements.txt: -------------------------------------------------------------------------------- 1 | stashapp-tools 2 | aiohttp 3 | asyncio 4 | pydantic 5 | imageio[ffmpeg] -------------------------------------------------------------------------------- /plugins/PythonDepManager/__init__.py: -------------------------------------------------------------------------------- 1 | from .deps import ensure_import 2 | 3 | __all__ = ["ensure_import"] -------------------------------------------------------------------------------- /themes/Theme-ColorPalette/README.md: -------------------------------------------------------------------------------- 1 | # colorPalette 2 | 3 | https://discourse.stashapp.cc/t/colorpalette/1420 -------------------------------------------------------------------------------- /themes/Theme-PulsarLight/README.md: -------------------------------------------------------------------------------- 1 | # PulsarLight 2 | 3 | https://discourse.stashapp.cc/t/pulsarlight/1430 -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["redhat.vscode-yaml", "esbenp.prettier-vscode"] 3 | } 4 | -------------------------------------------------------------------------------- /plugins/AudioPlayerLite/README.md: -------------------------------------------------------------------------------- 1 | # AudioPlayerLite 2 | 3 | https://discourse.stashapp.cc/t/audioplayerlite/1329 -------------------------------------------------------------------------------- /plugins/FileMonitor/requirements.txt: -------------------------------------------------------------------------------- 1 | stashapp-tools >= 0.2.50 2 | requests 3 | watchdog 4 | schedule 5 | pyyaml -------------------------------------------------------------------------------- /plugins/filenameParser/README.md: -------------------------------------------------------------------------------- 1 | # Filename parser 2 | 3 | https://discourse.stashapp.cc/t/filename-parser/1378 -------------------------------------------------------------------------------- /plugins/DateParser/README.md: -------------------------------------------------------------------------------- 1 | # Gallery Date Parser 2 | 3 | https://discourse.stashapp.cc/t/gallery-date-parser/1330 -------------------------------------------------------------------------------- /plugins/TPDBMarkers/README.md: -------------------------------------------------------------------------------- 1 | # The Porn DB Markers 2 | 3 | https://discourse.stashapp.cc/t/the-porn-db-markers/1335 -------------------------------------------------------------------------------- /plugins/ThumbPreviews/README.md: -------------------------------------------------------------------------------- 1 | # Thumbnail Previews 2 | 3 | https://discourse.stashapp.cc/t/thumbnail-previews/1947 -------------------------------------------------------------------------------- /plugins/VideoScrollWheel/README.md: -------------------------------------------------------------------------------- 1 | # VideoScrollWheel 2 | 3 | https://discourse.stashapp.cc/t/videoscrollwheel/1336 -------------------------------------------------------------------------------- /plugins/audio-transcodes/README.md: -------------------------------------------------------------------------------- 1 | # audio-transcodes 2 | 3 | https://discourse.stashapp.cc/t/audio-transcodes/1338 -------------------------------------------------------------------------------- /plugins/funscriptMarkers/README.md: -------------------------------------------------------------------------------- 1 | # Funscript Markers 2 | 3 | https://discourse.stashapp.cc/t/funscript-markers/1379 -------------------------------------------------------------------------------- /plugins/setSceneCoverFromFile/README.md: -------------------------------------------------------------------------------- 1 | # Set Scene Cover 2 | 3 | https://discourse.stashapp.cc/t/set-scene-cover/1391 -------------------------------------------------------------------------------- /plugins/hotCards/assets/hot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stashapp/CommunityScripts/HEAD/plugins/hotCards/assets/hot.png -------------------------------------------------------------------------------- /plugins/sceneCoverCropper/README.md: -------------------------------------------------------------------------------- 1 | # Scene Cover Cropper 2 | 3 | https://discourse.stashapp.cc/t/scene-cover-cropper/1388 -------------------------------------------------------------------------------- /plugins/ExtraPerformerInfo/README.md: -------------------------------------------------------------------------------- 1 | # Extra Performer Info 2 | 3 | https://discourse.stashapp.cc/t/extra-performer-info/1332 -------------------------------------------------------------------------------- /plugins/PlexSync/TODO.md: -------------------------------------------------------------------------------- 1 | - Automatic support for getting/creating tag IDs. And/or have them more dynamically name-based. 2 | -------------------------------------------------------------------------------- /plugins/hotCards/assets/gold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stashapp/CommunityScripts/HEAD/plugins/hotCards/assets/gold.png -------------------------------------------------------------------------------- /plugins/hotCards/assets/holo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stashapp/CommunityScripts/HEAD/plugins/hotCards/assets/holo.png -------------------------------------------------------------------------------- /plugins/markerDeleteButton/README.md: -------------------------------------------------------------------------------- 1 | # Marker Delete Button 2 | 3 | https://discourse.stashapp.cc/t/marker-delete-button/1381 -------------------------------------------------------------------------------- /plugins/bulkImageScrape/res/task.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stashapp/CommunityScripts/HEAD/plugins/bulkImageScrape/res/task.png -------------------------------------------------------------------------------- /plugins/hotCards/assets/default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stashapp/CommunityScripts/HEAD/plugins/hotCards/assets/default.png -------------------------------------------------------------------------------- /plugins/hotCards/assets/illusion.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stashapp/CommunityScripts/HEAD/plugins/hotCards/assets/illusion.webp -------------------------------------------------------------------------------- /plugins/markerTagToScene/README.md: -------------------------------------------------------------------------------- 1 | # Scene Marker Tags to Scene 2 | 3 | https://discourse.stashapp.cc/t/scene-marker-tags-to-scene/1382 -------------------------------------------------------------------------------- /plugins/bulkImageScrape/res/running.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stashapp/CommunityScripts/HEAD/plugins/bulkImageScrape/res/running.png -------------------------------------------------------------------------------- /plugins/bulkImageScrape/res/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stashapp/CommunityScripts/HEAD/plugins/bulkImageScrape/res/settings.png -------------------------------------------------------------------------------- /plugins/image_date_from_metadata/README.md: -------------------------------------------------------------------------------- 1 | # Image Date From Metadata 2 | 3 | https://discourse.stashapp.cc/t/image-date-from-metadata/2225 -------------------------------------------------------------------------------- /plugins/stashdb-performer-gallery/README.md: -------------------------------------------------------------------------------- 1 | # stashdb performer gallery 2 | 3 | https://discourse.stashapp.cc/t/stashdb-performer-gallery/1411 -------------------------------------------------------------------------------- /plugins/themeSwitch/assets/themes/materialize/source.txt: -------------------------------------------------------------------------------- 1 | https://github.com/killhellokitty/stash-material-ize-theme/blob/main/stash-theme.css -------------------------------------------------------------------------------- /plugins/performerStashboxUrlToID/README.md: -------------------------------------------------------------------------------- 1 | # Performer Stashbox Url to ID 2 | 3 | https://discourse.stashapp.cc/t/performer-stashbox-url-to-id/2629 -------------------------------------------------------------------------------- /plugins/stashAppAndroidTvCompanion/README.md: -------------------------------------------------------------------------------- 1 | # StashAppAndroidTV Companion 2 | 3 | https://discourse.stashapp.cc/t/stashappandroidtv-companion/1409 -------------------------------------------------------------------------------- /plugins/tagImagesWithPerfTags/README.md: -------------------------------------------------------------------------------- 1 | # Tag Images From Performer Tags 2 | 3 | https://discourse.stashapp.cc/t/tag-images-from-performer-tags/2059 -------------------------------------------------------------------------------- /plugins/tagScenesWithPerfTags/README.md: -------------------------------------------------------------------------------- 1 | # Tag Scenes From Performer Tags 2 | 3 | https://discourse.stashapp.cc/t/tag-scenes-from-performer-tags/1413 -------------------------------------------------------------------------------- /plugins/AdulttimeInteractiveDL/README.md: -------------------------------------------------------------------------------- 1 | # Adulttime Interactive Downloader 2 | 3 | https://discourse.stashapp.cc/t/adulttime-interactive-downloader/1327 -------------------------------------------------------------------------------- /plugins/bulkImageScrape/res/valid_scraper_id.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stashapp/CommunityScripts/HEAD/plugins/bulkImageScrape/res/valid_scraper_id.png -------------------------------------------------------------------------------- /plugins/bulkImageScrape/res/valid_image_scraper.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stashapp/CommunityScripts/HEAD/plugins/bulkImageScrape/res/valid_image_scraper.png -------------------------------------------------------------------------------- /plugins/themeSwitch/assets/snippets/scenes/show-extra-info.css: -------------------------------------------------------------------------------- 1 | .extra-scene-info { 2 | display: inline; 3 | } 4 | .file-path { 5 | display: block; 6 | } 7 | -------------------------------------------------------------------------------- /plugins/themeSwitch/assets/snippets/global/hide-o-count-badges.css: -------------------------------------------------------------------------------- 1 | /* [Global changes] Hide 0 count badges */ 2 | span.badge[data-value="0"] { 3 | display: none; 4 | } 5 | -------------------------------------------------------------------------------- /themes/Theme-Minimal/assets/Outfit-VariableFont_wght.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stashapp/CommunityScripts/HEAD/themes/Theme-Minimal/assets/Outfit-VariableFont_wght.woff2 -------------------------------------------------------------------------------- /plugins/chooseYourAdventurePlayer/Sample/1234/images/bad.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stashapp/CommunityScripts/HEAD/plugins/chooseYourAdventurePlayer/Sample/1234/images/bad.jpg -------------------------------------------------------------------------------- /plugins/chooseYourAdventurePlayer/Sample/1234/images/good.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stashapp/CommunityScripts/HEAD/plugins/chooseYourAdventurePlayer/Sample/1234/images/good.jpg -------------------------------------------------------------------------------- /plugins/chooseYourAdventurePlayer/Sample/1234/videos/Scene0.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stashapp/CommunityScripts/HEAD/plugins/chooseYourAdventurePlayer/Sample/1234/videos/Scene0.mp4 -------------------------------------------------------------------------------- /plugins/stashNotifications/README.md: -------------------------------------------------------------------------------- 1 | # Stash Notifications 2 | 3 | https://discourse.stashapp.cc/t/stash-notifications/870 4 | 5 | Receive notifications about plugin and scraper changes. -------------------------------------------------------------------------------- /plugins/themeSwitch/assets/snippets/global/hide-donate-button.css: -------------------------------------------------------------------------------- 1 | /* [Global changes] Hide the Donate button */ 2 | 3 | .btn-primary.btn.donate.minimal { 4 | display: none; 5 | } 6 | -------------------------------------------------------------------------------- /plugins/themeSwitch/assets/snippets/images/disable-lightbox-annimation.css: -------------------------------------------------------------------------------- 1 | /* [Images tab] Disable lightbox animation */ 2 | 3 | .Lightbox-carousel { 4 | transition: none; 5 | } 6 | -------------------------------------------------------------------------------- /plugins/themeSwitch/assets/snippets/scenes/hide-studio-logo.css: -------------------------------------------------------------------------------- 1 | /* [Scenes tab] Hide studio logo/text from scene card */ 2 | 3 | .scene-studio-overlay { 4 | display: none; 5 | } 6 | -------------------------------------------------------------------------------- /plugins/RenameFile/renamefile.css: -------------------------------------------------------------------------------- 1 | .renamefile { 2 | color: unset; 3 | &:hover { 4 | text-decoration: unset; 5 | } 6 | &:active { 7 | color: white; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /plugins/chooseYourAdventurePlayer/Sample/1234/images/poster-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stashapp/CommunityScripts/HEAD/plugins/chooseYourAdventurePlayer/Sample/1234/images/poster-1.png -------------------------------------------------------------------------------- /plugins/chooseYourAdventurePlayer/Sample/1234/images/poster-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stashapp/CommunityScripts/HEAD/plugins/chooseYourAdventurePlayer/Sample/1234/images/poster-2.png -------------------------------------------------------------------------------- /plugins/chooseYourAdventurePlayer/Sample/1234/videos/Scene1a.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stashapp/CommunityScripts/HEAD/plugins/chooseYourAdventurePlayer/Sample/1234/videos/Scene1a.mp4 -------------------------------------------------------------------------------- /plugins/chooseYourAdventurePlayer/Sample/1234/videos/Scene1b.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stashapp/CommunityScripts/HEAD/plugins/chooseYourAdventurePlayer/Sample/1234/videos/Scene1b.mp4 -------------------------------------------------------------------------------- /plugins/chooseYourAdventurePlayer/Sample/1234/videos/Scene2A-1.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stashapp/CommunityScripts/HEAD/plugins/chooseYourAdventurePlayer/Sample/1234/videos/Scene2A-1.mp4 -------------------------------------------------------------------------------- /plugins/chooseYourAdventurePlayer/Sample/1234/videos/Scene2A-2.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stashapp/CommunityScripts/HEAD/plugins/chooseYourAdventurePlayer/Sample/1234/videos/Scene2A-2.mp4 -------------------------------------------------------------------------------- /plugins/chooseYourAdventurePlayer/Sample/1234/videos/Scene2A.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stashapp/CommunityScripts/HEAD/plugins/chooseYourAdventurePlayer/Sample/1234/videos/Scene2A.mp4 -------------------------------------------------------------------------------- /plugins/chooseYourAdventurePlayer/Sample/1234/videos/Scene2B.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stashapp/CommunityScripts/HEAD/plugins/chooseYourAdventurePlayer/Sample/1234/videos/Scene2B.mp4 -------------------------------------------------------------------------------- /plugins/themeSwitch/assets/snippets/scenes/tags-list-sess-width.css: -------------------------------------------------------------------------------- 1 | /* [Scenes tab] Make the list of tags take up less width */ 2 | 3 | .bs-popover-bottom { 4 | max-width: 500px; 5 | } 6 | -------------------------------------------------------------------------------- /plugins/chooseYourAdventurePlayer/Sample/1234/images/end-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stashapp/CommunityScripts/HEAD/plugins/chooseYourAdventurePlayer/Sample/1234/images/end-screen.png -------------------------------------------------------------------------------- /plugins/themeSwitch/assets/snippets/scenes/hide-scene-Scrubber.css: -------------------------------------------------------------------------------- 1 | /* [Scenes tab] Hide the scene scrubber and max out the player's height */ 2 | 3 | .scrubber-wrapper { 4 | display: none; 5 | } 6 | -------------------------------------------------------------------------------- /plugins/themeSwitch/assets/snippets/scenes/hide-scene-specs.css: -------------------------------------------------------------------------------- 1 | /* [Scenes tab] Hide scene specs (resolution, duration) from scene card */ 2 | 3 | .scene-specs-overlay { 4 | display: none; 5 | } 6 | -------------------------------------------------------------------------------- /plugins/untagRedundantTags/requirements.txt: -------------------------------------------------------------------------------- 1 | # stashapi has to be installed from source until stashapp-tools is updated to include the latest version 2 | stashapi @ git+https://github.com/stg-annon/stashapi.git -------------------------------------------------------------------------------- /plugins/bulkImageScrape/requirements.txt: -------------------------------------------------------------------------------- 1 | requests 2 | # stashapi has to be installed from source until stashapp-tools is updated to include the latest version 3 | stashapi @ git+https://github.com/stg-annon/stashapi.git -------------------------------------------------------------------------------- /plugins/performer-poster-backdrop/assets/performer-poster-backdrop-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stashapp/CommunityScripts/HEAD/plugins/performer-poster-backdrop/assets/performer-poster-backdrop-example.png -------------------------------------------------------------------------------- /plugins/CommunityScriptsUILibrary/CommunityScriptsUILibrary.yml: -------------------------------------------------------------------------------- 1 | name: CommunityScriptsUILibrary 2 | description: CommunityScripts UI helper library 3 | version: 1.0.0 4 | ui: 5 | javascript: 6 | - cs-ui-lib.js 7 | -------------------------------------------------------------------------------- /plugins/e621_tagger/requirements.txt: -------------------------------------------------------------------------------- 1 | requests 2 | # stashapi has to be installed from source until stashapp-tools is updated to include the latest version 3 | stashapi @ git+https://github.com/stg-annon/stashapi.git 4 | -------------------------------------------------------------------------------- /themes/Theme-Plex/Theme-Plex.yml: -------------------------------------------------------------------------------- 1 | name: Theme - Plex 2 | description: Plex Theme for Stash by Fidelio 2020 3 | version: 1.0.5 4 | url: https://discourse.stashapp.cc/t/plex/1426 5 | ui: 6 | css: 7 | - Theme-Plex.css 8 | -------------------------------------------------------------------------------- /plugins/themeSwitch/assets/snippets/performers/show-entire-performer-image.css: -------------------------------------------------------------------------------- 1 | /* [Performers tab] Show entire performer image in performer card */ 2 | 3 | .performer.image { 4 | background-size: contain !important; 5 | } 6 | -------------------------------------------------------------------------------- /themes/Theme-Night/Theme-Night.yml: -------------------------------------------------------------------------------- 1 | name: Theme - Night 2 | description: Night Theme for Stash (unknown author) 3 | version: 0.1 4 | url: https://discourse.stashapp.cc/t/night/1425 5 | ui: 6 | css: 7 | - Theme-Night.css 8 | -------------------------------------------------------------------------------- /plugins/tagCopyPaste/tagCopyPaste.css: -------------------------------------------------------------------------------- 1 | button.imageGalleryNav-copyButton, 2 | button.imageGalleryNav-pasteButton { 3 | float: right; 4 | height: 21px; 5 | line-height: 20px; 6 | padding: 0 10px; 7 | margin-right: 15px; 8 | } 9 | -------------------------------------------------------------------------------- /themes/Theme-Pulsar/Theme-Pulsar.yml: -------------------------------------------------------------------------------- 1 | name: Theme - Pulsar 2 | description: Plex Theme for Stash by Fonzie 2020-21 3 | version: 1.8.1 4 | url: https://discourse.stashapp.cc/t/pulsar/1429 5 | ui: 6 | css: 7 | - Theme-Pulsar.css 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "community-scripts", 3 | "description": "This repository contains plugin and utility scripts created by the Stash community and hosted on the official GitHub repo.", 4 | "license": "AGPL-3.0-only" 5 | } 6 | -------------------------------------------------------------------------------- /plugins/themeSwitch/assets/snippets/images/dont-crop-preview-thumbnails.css: -------------------------------------------------------------------------------- 1 | /* [Images tab] Don't crop preview thumbnails */ 2 | 3 | .flexbin > * > img { 4 | object-fit: inherit; 5 | max-width: none; 6 | min-width: initial; 7 | } 8 | -------------------------------------------------------------------------------- /themes/Theme-NeonDark/Theme-NeonDark.yml: -------------------------------------------------------------------------------- 1 | name: Theme - NeonDark 2 | description: NeonDark Theme for Stash by Dankonite 3 | version: 1.0 4 | url: https://discourse.stashapp.cc/t/neondark/1424 5 | ui: 6 | css: 7 | - Theme-NeonDark.css 8 | -------------------------------------------------------------------------------- /plugins/SFWSwitch/sfwswitch.yml: -------------------------------------------------------------------------------- 1 | name: SFW Switch 2 | description: Add a button to blur covers and images. 3 | version: 1.2 4 | url: https://discourse.stashapp.cc/t/sfw-switch/4658 5 | ui: 6 | javascript: 7 | - sfw.js 8 | css: 9 | - sfw.css -------------------------------------------------------------------------------- /themes/Theme-Minimal/skeleton.css: -------------------------------------------------------------------------------- 1 | .skeleton-card { 2 | background-color: var(--primary-3); 3 | } 4 | 5 | .scene-skeleton { 6 | max-height: unset; 7 | min-height: unset; 8 | height: 16rem; 9 | border-radius: 1rem; 10 | } 11 | -------------------------------------------------------------------------------- /plugins/chooseYourAdventurePlayer/Sample/1234/choices/2-e.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "2-e", 3 | "type": "end", 4 | "title": "", 5 | "skipto": null, 6 | "choices": null, 7 | "resource": { 8 | "resolved_content": null 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /themes/Theme-BlackHole/Theme-BlackHole.yml: -------------------------------------------------------------------------------- 1 | name: Theme - BlackHole 2 | description: BlackHole Theme for Stash by BViking78 3 | version: 2.0.0 4 | url: https://discourse.stashapp.cc/t/blackhole/1419 5 | ui: 6 | css: 7 | - Theme-BlackHole.css 8 | -------------------------------------------------------------------------------- /themes/Theme-ModernDark/Theme-ModernDark.yml: -------------------------------------------------------------------------------- 1 | name: Theme - ModernDark 2 | description: ModernDark Theme for Stash by cj13 3 | version: 1.3 4 | url: https://discourse.stashapp.cc/t/moderndark/1423 5 | ui: 6 | css: 7 | - Theme-ModernDark.css 8 | -------------------------------------------------------------------------------- /themes/Theme-PulsarLight/Theme-PulsarLight.yml: -------------------------------------------------------------------------------- 1 | name: Theme - PulsarLight 2 | description: Plex Theme for Stash by Fonzie 2021 3 | version: 0.3.1 4 | url: https://discourse.stashapp.cc/t/pulsarlight/1430 5 | ui: 6 | css: 7 | - Theme-PulsarLight.css 8 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /plugins/chooseYourAdventurePlayer/Sample/1234/choices/1b-e.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "1b-e", 3 | "type": "exit", 4 | "title": "Bad Choice", 5 | "skipto": null, 6 | "choices": null, 7 | "resource": { 8 | "resolved_content": null 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /plugins/externalLinksEnhanced/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # v1.2 (2025-04-21) 2 | 3 | ## New icons 4 | 5 | * Bluesky (bsky.app) ~~ as custom icon 6 | * IMDB (imdb.com) 7 | * Wordpress (wordpress.com) 8 | 9 | ## Fixes 10 | 11 | * Fixed caching issue (thanks feederbox!) -------------------------------------------------------------------------------- /plugins/themeSwitch/assets/snippets/scenes/disable-zoom-wall-mode.css: -------------------------------------------------------------------------------- 1 | /* [Scenes tab] Disable zoom on hover in wall mode */ 2 | 3 | .wall-item:hover .wall-item-container { 4 | transform: none; 5 | } 6 | .wall-item:before { 7 | opacity: 0 !important; 8 | } 9 | -------------------------------------------------------------------------------- /plugins/AITagger/README.md: -------------------------------------------------------------------------------- 1 | # AI Tagger 2 | 3 | https://discourse.stashapp.cc/t/ai-tagger/587 4 | 5 | # For details around this plugin and using and configuring it, see the official documentation here: 6 | https://github.com/skier233/nsfw_ai_model_server/wiki/Stash-Plugin-Guide -------------------------------------------------------------------------------- /themes/Theme-RoundedYellow/Theme-RoundedYellow.yml: -------------------------------------------------------------------------------- 1 | name: Theme - Rounded Yellow 2 | description: Theme with rounded corners and yellow accents 3 | version: 1.0 4 | url: https://discourse.stashapp.cc/t/rounded-yellow/1431 5 | ui: 6 | css: 7 | - Theme-RoundedYellow.css 8 | -------------------------------------------------------------------------------- /plugins/themeSwitch/assets/snippets/scenes/hide-truncated-text.css: -------------------------------------------------------------------------------- 1 | /*This will hide the truncated text that appears under the tile and date. */ 2 | /* [Scenes Tab] - Hide the truncated text on scene card */ 3 | 4 | .TruncatedText.scene-card__description { 5 | display: none; 6 | } 7 | -------------------------------------------------------------------------------- /plugins/DupFileManager/DupFileManager.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sourceRoot":"","sources":["../src/DupFileManager.scss"],"names":[],"mappings":"AAAA;EACE;EACA;;;AAGF;EACE;EACA;EACA;;AAEA;EACE;;;AAIJ;AAAA;EAEE;EACA;EACA;EACA;;AAEA;AAAA;EACE;EACA;;;AAIJ;EACE;;;AAGF;EACE","file":"DupFileManager.css"} -------------------------------------------------------------------------------- /plugins/themeSwitch/assets/snippets/global/sticky-tool-bar.css: -------------------------------------------------------------------------------- 1 | /* [Global changes] Make the Toolbar Sticky v 0.1*/ 2 | 3 | .justify-content-center.btn-toolbar { 4 | position: sticky; 5 | top: 40px; 6 | z-index: 100; 7 | padding: 12px; 8 | padding-bottom: 1px; 9 | } 10 | -------------------------------------------------------------------------------- /plugins/AIOverhaul/README.md: -------------------------------------------------------------------------------- 1 | # AI Overhaul 2 | 3 | https://discourse.stashapp.cc/t/aioverhaul/4847 4 | 5 | # For details around this plugin and using and configuring it, see the official documentation here: 6 | https://github.com/skier233/Stash-AIServer/wiki/AI-Overhaul-Installation-Instructions 7 | -------------------------------------------------------------------------------- /plugins/StashRandomButton/random_button.css: -------------------------------------------------------------------------------- 1 | .random-btn { 2 | background-color: #ff0052; 3 | border-color: #ff0052; 4 | color: #fff; 5 | margin-left: 10px; 6 | } 7 | .random-btn:hover { 8 | background-color: #a10134; 9 | border-color: #a10134; 10 | color: #fff; 11 | } -------------------------------------------------------------------------------- /plugins/ThumbPreviews/ThumbPreviews.yml: -------------------------------------------------------------------------------- 1 | name: Thumbnail Previews 2 | description: Displays preview videos on thumbnail cards. 3 | version: 0.1 4 | url: https://discourse.stashapp.cc/t/thumbnail-previews/1947 5 | ui: 6 | javascript: 7 | - ThumbPreviews.js 8 | requires: 9 | - CommunityScriptsUILibrary -------------------------------------------------------------------------------- /plugins/stashNotes/stashNotes.yml: -------------------------------------------------------------------------------- 1 | name: Stash Notes 2 | description: Adds a button to the navigation bar which opens a small window for writing notes to your browser's local storage. 3 | version: 1.0 4 | url: https://discourse.stashapp.cc/t/stash-notes/1410 5 | ui: 6 | javascript: 7 | - stashNotes.js 8 | -------------------------------------------------------------------------------- /themes/Theme-Minimal/modal.css: -------------------------------------------------------------------------------- 1 | .modal-header { 2 | background-color: var(--primary-2); 3 | border-bottom: unset; 4 | } 5 | 6 | .modal-body { 7 | background-color: var(--primary-2); 8 | } 9 | 10 | .modal-footer { 11 | background-color: var(--primary-2); 12 | border-top: unset; 13 | } 14 | -------------------------------------------------------------------------------- /plugins/stats/stats.yml: -------------------------------------------------------------------------------- 1 | name: Extended Stats 2 | # requires: CommunityScriptsUILibrary 3 | description: Adds new stats to the stats page 4 | version: 1.1 5 | url: https://discourse.stashapp.cc/t/extended-stats/1412 6 | ui: 7 | requires: 8 | - CommunityScriptsUILibrary 9 | javascript: 10 | - stats.js 11 | -------------------------------------------------------------------------------- /plugins/AudioPlayerLite/AudioPlayerLite.yml: -------------------------------------------------------------------------------- 1 | name: AudioPlayerLite 2 | description: This plugin identifies files with no video codec and plays them as audio. 3 | version: 0.1 4 | url: https://discourse.stashapp.cc/t/audioplayerlite/1329 5 | ui: 6 | javascript: 7 | - AudioPlayerLite.js 8 | css: 9 | - AudioPlayerLite.css -------------------------------------------------------------------------------- /themes/Theme-Minimal/scene-tagger.css: -------------------------------------------------------------------------------- 1 | .tagger-container { 2 | .search-item { 3 | background-color: unset; 4 | 5 | .search-result { 6 | padding: 1.25rem; 7 | border-radius: 1rem; 8 | } 9 | 10 | .selected-result { 11 | background-color: var(--primary-2); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /plugins/themeSwitch/assets/snippets/scenes/adjust-mouse-wall-mode.css: -------------------------------------------------------------------------------- 1 | /* [Scenes tab] Adjust the mouse over behaviour in wall mode */ 2 | 3 | @media (min-width: 576px) { 4 | .wall-item:hover::before { 5 | opacity: 0; 6 | } 7 | 8 | .wall-item:hover .wall-item-container { 9 | transform: scale(1.5); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /plugins/themeSwitch/assets/snippets/scenes/longer-string-studio.css: -------------------------------------------------------------------------------- 1 | /* [Scenes tab] Allow for longer string when displaying "Studio as Text" on scene thumbnails */ 2 | 3 | .scene-studio-overlay { 4 | font-weight: 600 !important; 5 | opacity: 1 !important; 6 | width: 60% !important; 7 | text-overflow: ellipsis !important; 8 | } 9 | -------------------------------------------------------------------------------- /themes/Theme-PornHub/Theme-PornHub.yml: -------------------------------------------------------------------------------- 1 | name: Theme - Pornhub 2 | description: PornHub Theme for Stash by neurokinin 3 | version: 1.0.1 4 | url: https://discourse.stashapp.cc/t/pornhub/1428 5 | ui: 6 | css: 7 | - Theme-PornHub.css 8 | - https://raw.githubusercontent.com/stashapp/CommunityScripts/main/themes/Theme-Plex/Theme-Plex.css 9 | -------------------------------------------------------------------------------- /themes/Theme-Minimal/popover.css: -------------------------------------------------------------------------------- 1 | .popover { 2 | background-color: var(--primary-2); 3 | border-radius: 12px; 4 | 5 | .image-thumbnail { 6 | border-radius: 4px; 7 | } 8 | 9 | .arrow::after, 10 | .arrow::before { 11 | border-bottom-color: var(--primary-2); 12 | border-top-color: var(--primary-2); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /archive/README.md: -------------------------------------------------------------------------------- 1 | # Deprecation history 2 | 3 | 1. [renamerOnUpdate](./renamerOnUpdate) - issue [#483](https://github.com/stashapp/CommunityScripts/issues/483) 4 | 2. [visage](./visage/) - issue [#532](https://github.com/stashapp/CommunityScripts/issues/532) 5 | 3. [stashRealBooru](./stashRealbooru/) - issue [#540](https://github.com/stashapp/CommunityScripts/issues/540) -------------------------------------------------------------------------------- /plugins/externalLinksEnhanced/custom/custom.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "bluesky", 4 | "icon": "bluesky.svg", 5 | "addresses": ["bsky.app"], 6 | "regex": "^https?:\/\/(?:www\\.)?bsky.app\/profile\/(.*?)\/" 7 | }, 8 | { 9 | "name": "onlyfans", 10 | "icon": "onlyfans.svg", 11 | "addresses": ["onlyfans.com"] 12 | } 13 | ] -------------------------------------------------------------------------------- /plugins/markerDeleteButton/markerDeleteButton.css: -------------------------------------------------------------------------------- 1 | .wall-item-container:hover .marker-delete-button { 2 | display: block; 3 | } 4 | 5 | .marker-delete-button { 6 | display: none; 7 | opacity: 0.25; 8 | position: absolute; 9 | top: 5px; 10 | right: 5px; 11 | z-index: 999; 12 | } 13 | 14 | .marker-delete-button:hover { 15 | opacity: 0.75; 16 | } 17 | -------------------------------------------------------------------------------- /plugins/themeSwitch/assets/snippets/scenes/swap-studio-res-duration.css: -------------------------------------------------------------------------------- 1 | /* [Scenes tab] Swap studio and resolution/duration positions */ 2 | 3 | .scene-studio-overlay { 4 | bottom: 1rem; 5 | right: 0.7rem; 6 | height: inherit; 7 | top: inherit; 8 | } 9 | 10 | .scene-specs-overlay { 11 | right: 0.7rem; 12 | top: 0.7rem; 13 | bottom: inherit; 14 | } 15 | -------------------------------------------------------------------------------- /archive/visage/visage.yml: -------------------------------------------------------------------------------- 1 | name: Visage 2 | # requires: CommunityScriptsUILibrary 3 | description: Use facial Recognition To Lookup Performers. 4 | version: 1.0.2 5 | ui: 6 | requires: 7 | - CommunityScriptsUILibrary 8 | javascript: 9 | - visage.js 10 | css: 11 | - visage.css 12 | csp: 13 | connect-src: 14 | - "https://cc1234-stashface.hf.space" 15 | -------------------------------------------------------------------------------- /plugins/stashNotifications/stashNotifications.yml: -------------------------------------------------------------------------------- 1 | name: StashNotifications 2 | description: Receive notifications about plugin and scraper changes. 3 | version: 1.1 4 | url: https://discourse.stashapp.cc/t/stashnotifications/870 5 | ui: 6 | javascript: 7 | - stashNotifications.js 8 | css: 9 | - stashNotifications.css 10 | assets: 11 | /: . 12 | -------------------------------------------------------------------------------- /plugins/LocalVisage/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | stash: 3 | image: stashapp/stash:LocalVisage 4 | container_name: stash 5 | build: 6 | context: . 7 | dockerfile: Dockerfile 8 | restart: unless-stopped 9 | ports: 10 | - "7860:7860" 11 | - "9999:9999" 12 | volumes: 13 | - "./config:/appdata" 14 | - "./data:/data" 15 | -------------------------------------------------------------------------------- /plugins/chooseYourAdventurePlayer/Sample/1234/choices/1a.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "1a", 3 | "type": null, 4 | "title": "Good Choice", 5 | "skipto": "2", 6 | "choices": [ 7 | { 8 | "description": null, 9 | "id": "2", 10 | "type": null, 11 | "photo": "TBD" 12 | } 13 | ], 14 | "resource": { 15 | "resolved_content": "Scene1a.mp4" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /plugins/externalLinksEnhanced/externalLinksEnhanced.yml: -------------------------------------------------------------------------------- 1 | name: External Links Enhanced 2 | description: Adds additional icons for external links. 3 | version: 1.2 4 | url: https://discourse.stashapp.cc/t/external-links-enhanced/584 5 | ui: 6 | javascript: 7 | - externalLinksEnhanced.js 8 | css: 9 | - externalLinksEnhanced.css 10 | assets: 11 | /: . 12 | -------------------------------------------------------------------------------- /plugins/chooseYourAdventurePlayer/Sample/1234/choices/1b.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "1b", 3 | "type": null, 4 | "title": "Bad Choice", 5 | "skipto": null, 6 | "choices": [ 7 | { 8 | "description": null, 9 | "id": "1b-e", 10 | "type": "exit", 11 | "photo": "TBD" 12 | } 13 | ], 14 | "resource": { 15 | "resolved_content": "Scene1b.mp4" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /plugins/themeSwitch/assets/snippets/studios/more-studios-row.css: -------------------------------------------------------------------------------- 1 | /* [Studios tab] Show more item per row - Author hijack_hornet */ 2 | :not(.recommendation-row .studio-card).studio-card { 3 | width: 15%; 4 | } 5 | :not(.recommendation-row .studio-card-image).studio-card-image { 6 | width: 100%; 7 | } 8 | .studio-card h5 { 9 | text-align: center !important; 10 | display: block; 11 | } 12 | -------------------------------------------------------------------------------- /plugins/PythonDepManager/PythonDepManager.yml: -------------------------------------------------------------------------------- 1 | name: PythonDepManager 2 | description: Manage Python dependencies for CommunityScripts 3 | version: 0.1.0 4 | url: https://discourse.stashapp.cc/t/pythondepmanager/1801 5 | exec: 6 | - python 7 | - "{pluginDir}/flush.py" 8 | interface: raw 9 | 10 | tasks: 11 | - name: "Flush Dependencies" 12 | description: Flush all cached dependencies 13 | -------------------------------------------------------------------------------- /plugins/themeSwitch/assets/snippets/movies/desktop-better-layout-regular.css: -------------------------------------------------------------------------------- 1 | /* [Movies tab] Better Movie layout for desktops: Regular size poster */ 2 | 3 | .movie-details.mb-3.col.col-xl-4.col-lg-6 { 4 | flex-basis: 70%; 5 | } 6 | .col-xl-8.col-lg-6 { 7 | flex-basis: 30%; 8 | } 9 | .movie-images { 10 | flex-wrap: wrap; 11 | } 12 | .movie-image-container { 13 | flex: 0 0 500px; 14 | } 15 | -------------------------------------------------------------------------------- /plugins/themeSwitch/assets/snippets/performers/move-tag-field-to-top.css: -------------------------------------------------------------------------------- 1 | /* [Performers tab] Move the tags row in the Performer's edit panel to the second position (just after name). */ 2 | 3 | form#performer-edit { 4 | display: flex; 5 | flex-direction: column; 6 | } 7 | #performer-edit > .row:nth-child(24) { 8 | order: -1; 9 | } 10 | #performer-edit > .row:first-child { 11 | order: -2; 12 | } 13 | -------------------------------------------------------------------------------- /validator/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "stash-script-validator", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "scripts": { 7 | "main": "node index.js" 8 | }, 9 | "dependencies": { 10 | "ajv": "7", 11 | "ajv-formats": "^3.0.1", 12 | "better-ajv-errors": "^1.2.0", 13 | "chalk": "4", 14 | "yaml": "^2.5.1" 15 | } 16 | } -------------------------------------------------------------------------------- /plugins/StashRandomButton/random_button.yml: -------------------------------------------------------------------------------- 1 | name: RandomButton 2 | description: Adds a button to quickly jump to a random scene, image, performer, studio, group, tag, or gallery, both on overview and internal entity pages. 3 | version: 2.0.1 4 | url: https://discourse.stashapp.cc/t/randombutton/1809 5 | ui: 6 | requires: [] 7 | javascript: 8 | - random_button.js 9 | css: 10 | - random_button.css 11 | -------------------------------------------------------------------------------- /plugins/themeSwitch/assets/snippets/movies/desktop-better-layout-larger.css: -------------------------------------------------------------------------------- 1 | /* [Movies tab] Better Movie layout for desktops: Larger size poster */ 2 | 3 | .movie-details.mb-3.col.col-xl-4.col-lg-6 { 4 | flex-basis: 70%; 5 | } 6 | .col-xl-8.col-lg-6 { 7 | flex-basis: 30%; 8 | } 9 | .movie-images { 10 | flex-direction: column; 11 | flex-wrap: wrap; 12 | } 13 | .movie-image-container { 14 | flex: 1 1 700px; 15 | } 16 | -------------------------------------------------------------------------------- /plugins/stashAI/stashai.yml: -------------------------------------------------------------------------------- 1 | name: Stash AI 2 | # requires: CommunityScriptsUILibrary 3 | description: Add Tags or Markers to a video using AI 4 | version: 1.0.2 5 | url: https://discourse.stashapp.cc/t/stash-ai/1392 6 | ui: 7 | requires: 8 | - CommunityScriptsUILibrary 9 | javascript: 10 | - stashai.js 11 | css: 12 | - stashai.css 13 | csp: 14 | connect-src: 15 | - "https://cc1234-stashtag.hf.space" 16 | -------------------------------------------------------------------------------- /plugins/filenameParser/filenameParser.yml: -------------------------------------------------------------------------------- 1 | name: Filename parser 2 | description: Parses filename into studio, date, performers and title 3 | version: 0.1 4 | url: https://discourse.stashapp.cc/t/filename-parser/1378 5 | exec: 6 | - filenameParser.js 7 | interface: js 8 | hooks: 9 | - name: Prepopulates data based on filename 10 | description: 11 | triggeredBy: 12 | - Scene.Create.Post 13 | - Gallery.Create.Post 14 | -------------------------------------------------------------------------------- /archive/stashRealbooru/stash-realbooru.yml: -------------------------------------------------------------------------------- 1 | name: Stash Realbooru 2 | # requires: CommunityScriptsUILibrary 3 | description: Add tags based on the realbooru model, This works on individual images. 4 | version: 1.0.1 5 | ui: 6 | requires: 7 | - CommunityScriptsUILibrary 8 | javascript: 9 | - stash-realbooru.js 10 | css: 11 | - stash-realbooru.css 12 | csp: 13 | connect-src: 14 | - "https://cc1234-deepdanbooru.hf.space" 15 | -------------------------------------------------------------------------------- /plugins/markerDeleteButton/markerDeleteButton.yml: -------------------------------------------------------------------------------- 1 | name: Marker Delete Button 2 | # requires: CommunityScriptsUILibrary 3 | description: Adds a delete button to entries on the Markers page and on the Scene page. 4 | version: 0.2 5 | url: https://discourse.stashapp.cc/t/marker-delete-button/1381 6 | ui: 7 | requires: 8 | - CommunityScriptsUILibrary 9 | javascript: 10 | - markerDeleteButton.js 11 | css: 12 | - markerDeleteButton.css 13 | -------------------------------------------------------------------------------- /plugins/PythonDepManager/flush.py: -------------------------------------------------------------------------------- 1 | import log 2 | import shutil 3 | from deps import get_base_folder 4 | 5 | 6 | def flush_dependencies() -> None: 7 | """Delete all dependencies in the base folder""" 8 | # get working directory 9 | plugin_folder = get_base_folder() 10 | log.info(f"Flushing dependencies from {plugin_folder}") 11 | shutil.rmtree(plugin_folder) 12 | 13 | 14 | if __name__ == "__main__": 15 | flush_dependencies() 16 | -------------------------------------------------------------------------------- /plugins/scenePageRememberStates/README.md: -------------------------------------------------------------------------------- 1 | # Scene Page Remember States 2 | 3 | https://discourse.stashapp.cc/t/scene-page-remember-states/1389 4 | 5 | This plugin uses local storage to rememebr what is the current active nav tab of the scenes' detail panel, and upon any page load activate the last remembered active nav tab. 6 | 7 | It also rembers the active collapsed state of the divider button and upon page load if it's true, it will automatically collapse the divider. -------------------------------------------------------------------------------- /themes/README.md: -------------------------------------------------------------------------------- 1 | # Adding Theme plugins as part of Theme Switch plugin 2 | 3 | On developing theme plugins, consider adding your theme to plugins/themeSwitch. While some users may prefer to have individual themes installed requiring to so to the settings > plugin page each time to change the theme, other users may prefer a Theme Switch manager always available on the nav bar. 4 | 5 | There is instructions on how to your theme to the plugin in the folders README.md 6 | -------------------------------------------------------------------------------- /themes/Theme-Minimal/studio-tagger.css: -------------------------------------------------------------------------------- 1 | .StudioTagger { 2 | .StudioTagger-studio { 3 | background-color: unset; 4 | .studio-card { 5 | border-radius: 1rem; 6 | img { 7 | background-color: unset; 8 | } 9 | } 10 | .StudioTagger-details { 11 | margin-top: 1rem; 12 | 13 | .StudioTagger-header { 14 | h2 { 15 | font-size: var(--text-xl); 16 | } 17 | } 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /plugins/scenePageRememberStates/scenePageRememberStates.yml: -------------------------------------------------------------------------------- 1 | name: Scene Page Remember States 2 | description: Uses local storage to remember the state of the scene page detail panel nav bar and activate it on page load. Remembers collapse state of the divider. 3 | version: 0.2 4 | url: https://discourse.stashapp.cc/t/scene-page-remember-states/1389 5 | ui: 6 | requires: 7 | - CommunityScriptsUILibrary 8 | javascript: 9 | - scenePageRememberStates.js 10 | -------------------------------------------------------------------------------- /plugins/themeSwitch/assets/snippets/galleries/galleries-grid-view.css: -------------------------------------------------------------------------------- 1 | /* [Galleries tab] Grid view for galleries */ 2 | 3 | .col.col-sm-6.mx-auto.table .d-none.d-sm-block { 4 | display: none !important; 5 | } 6 | .col.col-sm-6.mx-auto.table .w-100.w-sm-auto { 7 | width: 175px !important; 8 | background-color: rgba(0, 0, 0, 0.45); 9 | box-shadow: 0 0 2px rgba(0, 0, 0, 0.35); 10 | } 11 | .col.col-sm-6.mx-auto.table tr { 12 | display: inline-table; 13 | } 14 | -------------------------------------------------------------------------------- /plugins/themeSwitch/assets/snippets/tags/hide-tag-images.css: -------------------------------------------------------------------------------- 1 | /* Author: Echoman */ 2 | /* [Tags Images] Hides The tag images in Tags view and HR */ 3 | .tag-card-image { 4 | display: none !important; 5 | } 6 | 7 | .tag-card > hr { 8 | display: none !important; 9 | } 10 | 11 | .card { 12 | padding: 10px !important; 13 | } 14 | 15 | .card-popovers { 16 | margin-bottom: 0px !important; 17 | } 18 | 19 | .tag-card { 20 | padding: 0px !important; 21 | } 22 | -------------------------------------------------------------------------------- /plugins/titleFromFilename/titleFromFilename.yml: -------------------------------------------------------------------------------- 1 | name: titleFromFilename 2 | description: Set a scene's title from it's filename 3 | version: 1.2 4 | url: https://discourse.stashapp.cc/t/titlefromfilename/1519 5 | exec: 6 | - python 7 | - "{pluginDir}/titleFromFilename.py" 8 | interface: raw 9 | hooks: 10 | - name: hook_set_title_from_filename 11 | description: Set the title of the scene to it's filename 12 | triggeredBy: 13 | - Scene.Create.Post 14 | -------------------------------------------------------------------------------- /themes/Theme-RoundedYellow/README.md: -------------------------------------------------------------------------------- 1 | # Rounded Yellow 2 | 3 | https://discourse.stashapp.cc/t/rounded-yellow/1431 4 | 5 | A theme with yellow accent colour and rounded corners. 6 | 7 | ## Preview 8 | ![2024-01-03_14-37-11_blur](https://github.com/Written2001/CommunityScripts/assets/121555133/96932001-4b46-4e97-b4de-5e7cb61608fb) 9 | ![2024-01-03_14-37-01_blur](https://github.com/Written2001/CommunityScripts/assets/121555133/e999ae6b-5e53-418c-a561-00c658afba3e) 10 | -------------------------------------------------------------------------------- /plugins/themeSwitch/themeSwitch.yml: -------------------------------------------------------------------------------- 1 | name: Theme Switch 2 | # requires: CommunityScriptsUILibrary 3 | description: Theme and CSS script manager located in main menu bar top right. 4 | version: 2.1.2 5 | url: https://discourse.stashapp.cc/t/theme-switch/1414 6 | ui: 7 | requires: 8 | - CommunityScriptsUILibrary 9 | javascript: 10 | - themeSwitchCSS.js 11 | - themeSwitchMain.js 12 | css: 13 | - themeSwtichDefault.css 14 | assets: 15 | /: assets 16 | -------------------------------------------------------------------------------- /scripts/stash-watcher/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.11.5-alpine3.18 2 | 3 | WORKDIR /usr/src/app 4 | 5 | COPY requirements.txt ./ 6 | RUN pip install --no-cache-dir -r requirements.txt 7 | 8 | COPY . . 9 | 10 | #Create an empty config file so that we can just use the defaults. This file can be mounted if it needs to be 11 | #modified 12 | RUN touch /config.toml 13 | 14 | #Apparently using -u causes the logs to output immediately 15 | CMD [ "python", "-u", "./watcher.py", "/config.toml" ] 16 | -------------------------------------------------------------------------------- /plugins/PythonToolsInstaller/PythonToolsInstaller.yml: -------------------------------------------------------------------------------- 1 | name: "Python Tools Installer" 2 | description: Download stashapp-tools for DockerEnv 3 | version: 0.1.2 4 | url: https://github.com/stashapp/CommunityScripts/tree/main/plugins/PythonToolsInstaller 5 | exec: 6 | - python 7 | - "{pluginDir}/PythonToolsInstaller.py" 8 | interface: raw 9 | tasks: 10 | - name: "Install" 11 | description: Install Python Module stashapi-tools 12 | defaultArgs: 13 | mode: process_py_stashapi_tools 14 | -------------------------------------------------------------------------------- /plugins/videoChapterMarkers/README.md: -------------------------------------------------------------------------------- 1 | # Video Chapter Markers 2 | 3 | https://discourse.stashapp.cc/t/video-chapter-markers/1416 4 | 5 | This plugin extracts chapter information embedded in video files using `ffprobe` and creates markers in Stash. It processes scenes by checking if no markers are present and then adds chapter markers based on the video file's chapter data. This tool is especially useful for automating the import of chapter markers when a scene is updated or processing multiple scenes at once. -------------------------------------------------------------------------------- /plugins/sceneCoverCropper/sceneCoverCropper.yml: -------------------------------------------------------------------------------- 1 | name: Scene Cover Cropper 2 | # requires: CommunityScriptsUILibrary 3 | description: Crop Scene Cover Images 4 | version: 1.0 5 | url: https://discourse.stashapp.cc/t/scene-cover-cropper/1388 6 | ui: 7 | requires: 8 | - CommunityScriptsUILibrary 9 | css: 10 | - https://cdn.jsdelivr.net/npm/cropperjs@1.6.1/dist/cropper.min.css 11 | javascript: 12 | - https://cdn.jsdelivr.net/npm/cropperjs@1.6.1/dist/cropper.min.js 13 | - sceneCoverCropper.js 14 | -------------------------------------------------------------------------------- /plugins/VideoBanner/VideoBanner.yml: -------------------------------------------------------------------------------- 1 | name: Video Banner 2 | description: Displays random generated preview videos in the detail page banner. 3 | version: 0.1.1 4 | url: https://discourse.stashapp.cc/t/video-banner/1927 5 | 6 | ui: 7 | requires: 8 | - CommunityScriptsUILibrary 9 | javascript: 10 | - VideoBanner.js 11 | 12 | settings: 13 | videoBrightness: 14 | displayName: Video Brightness (%) 15 | description: Adjust the brightness of the background video (e.g., 50 for 50%). 16 | type: NUMBER -------------------------------------------------------------------------------- /plugins/dupeMarker/dupeMarker.yml: -------------------------------------------------------------------------------- 1 | name: Dupe Marker Detector 2 | description: Finds and marks duplicate markers 3 | version: 0.2 4 | url: https://discourse.stashapp.cc/t/dupe-marker-detector/1375 5 | exec: 6 | - python 7 | - "{pluginDir}/dupeMarker.py" 8 | interface: raw 9 | tasks: 10 | - name: "Mark" 11 | description: Mark duplicate markers 12 | defaultArgs: 13 | mode: mark 14 | - name: "Delete" 15 | description: "Delete duplicate markers" 16 | defaultArgs: 17 | mode: delete 18 | -------------------------------------------------------------------------------- /plugins/funscriptMarkers/funscriptMarkers.yml: -------------------------------------------------------------------------------- 1 | name: Funscript Markers 2 | description: Create markers if there is a funscript with "chapters" included in the metadata of the script 3 | version: 0.1 4 | url: https://discourse.stashapp.cc/t/funscript-markers/1379 5 | exec: 6 | - python 7 | - "{pluginDir}/funscriptMarkers.py" 8 | interface: raw 9 | tasks: 10 | - name: "Re-process All" 11 | description: Look at funscript files and see if there are chapters in the metadata 12 | defaultArgs: 13 | mode: processAll 14 | -------------------------------------------------------------------------------- /themes/Theme-Minimal/stats.css: -------------------------------------------------------------------------------- 1 | .stats { 2 | margin: 0 auto !important; 3 | flex-direction: column; 4 | max-width: 40ch; 5 | margin-bottom: 32px !important; 6 | 7 | .stats-element { 8 | display: flex; 9 | flex-direction: row-reverse; 10 | justify-content: space-between; 11 | align-items: baseline; 12 | 13 | .title { 14 | font-size: var(--text-base); 15 | } 16 | 17 | .heading { 18 | font-size: var(--text-base); 19 | text-transform: capitalize; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /plugins/dupeMarker/README.md: -------------------------------------------------------------------------------- 1 | # Dupe Marker Detector 2 | 3 | https://discourse.stashapp.cc/t/dupe-marker-detector/1375 4 | 5 | Marks duplicate markers with a tag: `[Marker: Duplicate]` 6 | 7 | Tasks -> Search for duplicate markers 8 | 9 | It will add the tag to any markers that have an **exact** match for title, time **and** primary tag. 10 | It will only add to existing markers, it is up to the user to go to the tag and navigate to the scene where the duplicates will be highlighted with the tag. 11 | 12 | (it's technically a Dupe Marker Marker) -------------------------------------------------------------------------------- /scripts/stash-watcher/config.toml: -------------------------------------------------------------------------------- 1 | #This is the information about your stash instance 2 | [Host] 3 | #The scheme (either http or https) 4 | Scheme = http 5 | #The full hostname for your stash instance. If you're running in docker you might want the 6 | #service name and not localhost here. 7 | Host = localhost 8 | #The port number for your stash instance 9 | Port = 9999 10 | #The api key, if your stash instance is password protected 11 | ApiKey = 12 | 13 | #Configuration for the listener itself 14 | [Config] 15 | #A comma separated list of paths to watch. 16 | Paths = /data 17 | -------------------------------------------------------------------------------- /.github/workflows/validate.yml: -------------------------------------------------------------------------------- 1 | name: Validate Plugins 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | paths: 7 | - 'plugins/**' 8 | - 'themes/**' 9 | pull_request: 10 | branches: [main] 11 | paths: 12 | - 'plugins/**' 13 | - 'themes/**' 14 | 15 | jobs: 16 | validate: 17 | runs-on: ubuntu-22.04 18 | steps: 19 | - uses: actions/checkout@v4 20 | - uses: actions/setup-node@v4 21 | with: 22 | node-version: '20.x' 23 | - run: cd ./validator && yarn install --frozen-lockfile 24 | - run: node ./validate.js --ci -------------------------------------------------------------------------------- /plugins/comicInfoExtractor/config.yml: -------------------------------------------------------------------------------- 1 | #pkgignore 2 | #ImportList is a dictionary 3 | #that matches an xml Attribute from ComicInfo.xml to the according value in stash (using the graphql naming) 4 | #Fields that refer to different types of media are resolved by name and created if necessary (tags, studio, performers) 5 | #Fields that can contain multiple values (tags, performers) will be expected as a comma separated string, like 6 | #Outdoor, Blonde 7 | ImportList: 8 | Genre: tags 9 | Title: title 10 | Writer: studio 11 | Year: date 12 | Summary: details 13 | -------------------------------------------------------------------------------- /plugins/defaultDataForPath/defaultDataForPath.yml: -------------------------------------------------------------------------------- 1 | name: Default Data For Path 2 | description: Adds configured Tags, Performers and/or Studio to all newly scanned Scenes, Images and Galleries. 3 | version: 1.1 4 | url: https://discourse.stashapp.cc/t/default-data-for-path/1373 5 | exec: 6 | - defaultDataForPath.js 7 | interface: js 8 | hooks: 9 | - name: Add Configured Data on Scan 10 | description: Adds configured tags/performers/studio on Task->Scan creation. 11 | triggeredBy: 12 | - Scene.Create.Post 13 | - Gallery.Create.Post 14 | - Image.Create.Post 15 | -------------------------------------------------------------------------------- /plugins/audio-transcodes/audio-transcodes.yml: -------------------------------------------------------------------------------- 1 | name: audio-transcodes 2 | description: Generate a transcode video from an audio file 3 | version: 0.2 4 | url: https://discourse.stashapp.cc/t/audio-transcodes/1338 5 | exec: 6 | - python 7 | - "{pluginDir}/audio-transcodes.py" 8 | interface: raw 9 | hooks: 10 | - name: process audio 11 | description: transcode audio files on scan 12 | triggeredBy: 13 | - Scene.Create.Post 14 | tasks: 15 | - name: "Process All" 16 | description: transcode audio to video on all audio files 17 | defaultArgs: 18 | mode: processScenes 19 | -------------------------------------------------------------------------------- /plugins/stashAppAndroidTvCompanion/stashAppAndroidTvCompanion.yml: -------------------------------------------------------------------------------- 1 | name: StashAppAndroidTV Companion 2 | description: A companion plugin for StashAppAndroidTV 3 | version: 0.0.1 4 | url: https://discourse.stashapp.cc/t/stashappandroidtv-companion/1409 5 | 6 | exec: 7 | - python 8 | - "{pluginDir}/stashAppAndroidTvCompanion.py" 9 | interface: raw 10 | tasks: 11 | - name: logcat 12 | description: Send android app's logcat logs 13 | defaultArgs: 14 | mode: logcat 15 | - name: crash_report 16 | description: Send android app's crash report 17 | defaultArgs: 18 | mode: crash_report 19 | -------------------------------------------------------------------------------- /plugins/nfoSceneParser/nfoSceneParser.yml: -------------------------------------------------------------------------------- 1 | name: nfoSceneParser 2 | description: Fills scene data from NFO or filename pattern 3 | version: 1.5.0 4 | url: https://discourse.stashapp.cc/t/nfosceneparser/1385 5 | exec: 6 | - python 7 | - "{pluginDir}/nfoSceneParser.py" 8 | interface: raw 9 | hooks: 10 | - name: hook_nfoSceneParser 11 | description: Fills scene data on creation 12 | triggeredBy: 13 | - Scene.Create.Post 14 | tasks: 15 | - name: "Reload tagged scenes" 16 | description: Reload all scenes that have specific "marker" tag (see plugin's config.py) 17 | defaultArgs: 18 | mode: reload 19 | -------------------------------------------------------------------------------- /themes/Theme-ColorPalette/Theme-ColorPalette.yml: -------------------------------------------------------------------------------- 1 | name: Theme - colorPalette 2 | description: Based on the default theme, change the overall color of the page by setting the hue value. Make minor changes to the remaining styles. 3 | version: 0.1 4 | url: https://discourse.stashapp.cc/t/colorpalette/1420 5 | ui: 6 | requires: 7 | - CommunityScriptsUILibrary 8 | javascript: 9 | - Theme-ColorPalette.js 10 | css: 11 | - Theme-ColorPalette.css 12 | settings: 13 | hue: 14 | displayName: hue value 15 | description: An angle value of 0-360. Red (0) - Orange - Yellow - Green (120) - Cyan - Blue (240) - Purple - Red (360) 16 | type: NUMBER 17 | -------------------------------------------------------------------------------- /plugins/AITagger/utility.py: -------------------------------------------------------------------------------- 1 | import json 2 | import config 3 | 4 | def mutate_path(to_mutate): 5 | if isinstance(to_mutate, str): 6 | for key, value in config.path_mutation.items(): 7 | to_mutate = to_mutate.replace(key, value) 8 | elif isinstance(to_mutate, list): 9 | for i in range(len(to_mutate)): 10 | to_mutate[i] = mutate_path(to_mutate[i]) 11 | return to_mutate 12 | 13 | def read_json_from_file(file_path): 14 | with open(file_path, 'r') as f: 15 | return json.load(f) 16 | 17 | def write_json_to_file(file_path, json_data): 18 | with open(file_path, 'w') as f: 19 | f.write(json_data) -------------------------------------------------------------------------------- /plugins/AITagger/config.py: -------------------------------------------------------------------------------- 1 | CREATE_MARKERS = True 2 | FRAME_INTERVAL = 2 3 | IMAGE_THRESHOLD = 0.5 4 | 5 | API_BASE_URL = 'http://localhost:8000' 6 | IMAGE_REQUEST_BATCH_SIZE = 320 7 | CONCURRENT_TASK_LIMIT = 10 8 | SERVER_TIMEOUT = 3700 9 | AI_VIDEO_THRESHOLD = 0.3 10 | 11 | temp_image_dir = "./temp_images" 12 | output_data_dir = "./output_data" 13 | delete_incorrect_markers = True 14 | ai_base_tag_name = "AI" 15 | tagme_tag_name = "AI_TagMe" 16 | updateme_tag_name = "AI_UpdateMe" 17 | aitagged_tag_name = "AI_Tagged" 18 | aierrored_tag_name = "AI_Errored" 19 | 20 | # Example for mutating paths 21 | # path_mutation = {"E:": "F:", "G:": "D:"} 22 | path_mutation = {} 23 | -------------------------------------------------------------------------------- /plugins/e621_tagger/README.md: -------------------------------------------------------------------------------- 1 | # e621 tagger 2 | 3 | https://discourse.stashapp.cc/t/e621-tagger/1377 4 | 5 | Just a quick script to tag your uploadings 6 | 7 | Took some code from bulkImageScrape as example, because I'm not a python dev 8 | 9 | https://github.com/stashapp/CommunityScripts/blob/main/plugins/bulkImageScrape/bulkImageScrape.py 10 | 11 | ## How to use 12 | 13 | Go to Tasks -> e621_tagger -> Press Tag Everything 14 | 15 | ## Configuration 16 | 17 | You can configure which tags it will skip. By default, it will skip `e621_tagged` tag 18 | 19 | ## Rate limit 20 | 21 | Be aware, that e621 has rate limit. In script it's hardcoded 2 seconds on wait time 22 | -------------------------------------------------------------------------------- /plugins/e621_tagger/e621_tagger.yml: -------------------------------------------------------------------------------- 1 | name: e621_tagger 2 | description: Finding images and videos on e621 and tagging them. 3 | version: 0.5 4 | url: https://discourse.stashapp.cc/t/e621-tagger/1377 5 | exec: 6 | - python 7 | - "{pluginDir}/e621_tagger.py" 8 | 9 | interface: raw 10 | 11 | settings: 12 | SkipTags: 13 | displayName: List of tags to skip (comma separated). Default - e621_tagged 14 | type: STRING 15 | ExcludeOrganized: 16 | displayName: Exclude images that are set as organized (default is to include) 17 | type: BOOLEAN 18 | 19 | tasks: 20 | - name: "Tag everything" 21 | description: "Tag everything (Warning: can take a while)" 22 | -------------------------------------------------------------------------------- /themes/Theme-Minimal/Theme-Minimal.yml: -------------------------------------------------------------------------------- 1 | name: Theme - Minimal 2 | description: Minimal Theme for Stash 3 | version: 0.2.8 4 | url: https://discourse.stashapp.cc/t/minimal/1421 5 | ui: 6 | css: 7 | - index.css 8 | - _theme.css 9 | - _scrollbars.css 10 | - scenes.css 11 | - shared.css 12 | - settings.css 13 | - player.css 14 | - inputs.css 15 | - modal.css 16 | - stats.css 17 | - markers-wall.css 18 | - popover.css 19 | - studio.css 20 | - nav-bar.css 21 | - scene-tagger.css 22 | - studio-performer-tagger.css 23 | - skeleton.css 24 | assets: 25 | /: ./assets 26 | javascript: 27 | - reset-scrollbars.js 28 | -------------------------------------------------------------------------------- /plugins/imageGalleryNavigation/imageGalleryNavigation.yml: -------------------------------------------------------------------------------- 1 | name: imageGalleryNavigation 2 | # requires: CommunityScriptsUILibrary 3 | description: This plugin adds features for navigating between images within a Gallery from the Image details page. 4 | version: 0.3 5 | url: https://discourse.stashapp.cc/t/imagegallerynavigation/1857 6 | settings: 7 | enableTransform: 8 | displayName: Enable Zoom/Pan 9 | description: Enable zoom (mouse wheel) and pan (click and drag) on Image detail page. 10 | type: BOOLEAN 11 | ui: 12 | requires: 13 | - CommunityScriptsUILibrary 14 | javascript: 15 | - imageGalleryNavigation.js 16 | css: 17 | - imageGalleryNavigation.css 18 | -------------------------------------------------------------------------------- /plugins/setSceneCoverFromFile/set_scene_cover.yml: -------------------------------------------------------------------------------- 1 | name: Set Scene Cover 2 | description: searches Stash for Scenes with a cover image in the same folder and sets the cover image in stash to that image 3 | version: 0.4 4 | url: https://discourse.stashapp.cc/t/set-scene-cover/1391 5 | exec: 6 | - python 7 | - "{pluginDir}/set_cover.py" 8 | interface: raw 9 | tasks: 10 | - name: Scan 11 | description: searches stash dirs for cover images and logs results 12 | defaultArgs: 13 | mode: scan 14 | - name: Set Cover 15 | description: searches for cover images and sets any stash scene found in the same dir to that image 16 | defaultArgs: 17 | mode: set_cover 18 | -------------------------------------------------------------------------------- /plugins/DateParser/date_parser.yml: -------------------------------------------------------------------------------- 1 | name: Gallery Date Parser 2 | # requires: PythonDepManager 3 | description: Find date in path or filename and add it to the gallery 4 | version: 1.1.0 5 | url: https://discourse.stashapp.cc/t/gallery-date-parser/1330 6 | exec: 7 | - python 8 | - "{pluginDir}/date_parser.py" 9 | interface: raw 10 | tasks: 11 | - name: Find gallery dates 12 | description: Add the date on galleries based on their path 13 | defaultArgs: 14 | mode: gallery 15 | settings: 16 | setTitle: 17 | displayName: Set title ? 18 | description: Set title from folder name minus the found date ? Only applies on galleries without titles 19 | type: BOOLEAN 20 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "yaml.validate": true, 3 | "yaml.disableAdditionalProperties": true, 4 | "yaml.completion": true, 5 | "yaml.extension.recommendations": true, 6 | "yaml.hover": true, 7 | "yaml.format.singleQuote": false, 8 | "yaml.format.printWidth": 120, 9 | "yaml.format.proseWrap": "always", 10 | "yaml.schemas": { 11 | "schema/plugin.schema.json": ["/plugins/**", "/themes/**"] 12 | }, 13 | "[json]": { 14 | "editor.defaultFormatter": "esbenp.prettier-vscode" 15 | }, 16 | "[jsonc]": { 17 | "editor.defaultFormatter": "esbenp.prettier-vscode" 18 | }, 19 | "[yaml]": { 20 | "editor.defaultFormatter": "esbenp.prettier-vscode" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /plugins/AudioPlayer/AudioPlayer.yml: -------------------------------------------------------------------------------- 1 | name: AudioPlayer 2 | description: Add an audio-only toggle button and support for audio files in the Stash player through hls transcoder. 3 | version: 0.3 4 | url: https://discourse.stashapp.cc/t/audioplayer/1328 5 | ui: 6 | requires: 7 | - CommunityScriptsUILibrary 8 | javascript: 9 | - AudioPlayer.js 10 | css: 11 | - AudioPlayer.css 12 | 13 | settings: 14 | audioExtensions: 15 | displayName: Audio Extensions 16 | description: "Extensions to mach as audio files. Default: mp3, m4a" 17 | type: STRING 18 | useTag: 19 | displayName: Use Tag 20 | description: Use "Audio" tag instead of extensions. 21 | type: BOOLEAN 22 | -------------------------------------------------------------------------------- /plugins/image_date_from_metadata/image_date_from_metadata.yml: -------------------------------------------------------------------------------- 1 | name: Image Date From Metadata 2 | # requires: PythonDepManager 3 | description: Find date in image or clip metadata 4 | version: 1.0.0 5 | url: https://discourse.stashapp.cc/t/image-date-from-metadata/2225 6 | exec: 7 | - python 8 | - "{pluginDir}/image_date_from_metadata.py" 9 | interface: raw 10 | tasks: 11 | - name: Find all missing dates 12 | description: Try to find the date in the image or clip metadata and add it 13 | defaultArgs: 14 | mode: find 15 | hooks: 16 | - name: hook_image_created 17 | description: Get the image creation date from the file metadata 18 | triggeredBy: 19 | - Image.Create.Post 20 | -------------------------------------------------------------------------------- /plugins/performerStashboxUrlToID/performerStashboxUrlToID.yml: -------------------------------------------------------------------------------- 1 | name: Performer Stashbox Url to ID 2 | description: If the performer has a url for another stashbox add it as an id to the performer 3 | version: 0.3 4 | url: https://discourse.stashapp.cc/t/performer-stashbox-url-to-id/2629 5 | exec: 6 | - python 7 | - "{pluginDir}/performerStashboxUrlToID.py" 8 | interface: raw 9 | hooks: 10 | - name: Process Performer 11 | description: Adds/updates stashbox IDs based on the performer URLs 12 | triggeredBy: 13 | - Performer.Update.Post 14 | - Performer.Create.Post 15 | tasks: 16 | - name: Process all 17 | description: Process Performers 18 | defaultArgs: 19 | mode: processPerformers 20 | -------------------------------------------------------------------------------- /plugins/videoChapterMarkers/videoChapterMarkers.yml: -------------------------------------------------------------------------------- 1 | name: Video Chapter Markers 2 | description: Create markers from chapter information embedded in video files 3 | version: 1.1 4 | url: https://discourse.stashapp.cc/t/video-chapter-markers/1416 5 | exec: 6 | - python 7 | - "{pluginDir}/videoChapterMarkers.py" 8 | interface: raw 9 | tasks: 10 | - name: "Process All Videos" 11 | description: Extract chapters from all video files without existing markers and create Stash markers 12 | defaultArgs: 13 | mode: processAll 14 | 15 | hooks: 16 | - name: Create Markers on Scene Update 17 | description: Extract chapters and create markers when a scene is updated 18 | triggeredBy: 19 | - Scene.Update.Post 20 | -------------------------------------------------------------------------------- /plugins/comicInfoExtractor/comicInfoExtractor.yml: -------------------------------------------------------------------------------- 1 | name: Comic Info Extractor 2 | description: Extract the metadata from cbz with the Comicrack standard (ComicInfo.xml) 3 | version: 0.1 4 | url: https://discourse.stashapp.cc/t/comic-info-extractor/1372 5 | exec: 6 | - python 7 | - "{pluginDir}/comicInfoExtractor.py" 8 | interface: raw 9 | hooks: 10 | - name: Add Metadata to Gallery 11 | description: Update Metadata for Gallery by evaluating the ComicInfo.xml. 12 | triggeredBy: 13 | - Gallery.Update.Post 14 | - Gallery.Create.Post 15 | tasks: 16 | - name: Load all cbz Metadata 17 | description: Get Metadata for all Galleries by looking for ComicInfo.xml files in the Archive. 18 | defaultArgs: 19 | mode: process 20 | -------------------------------------------------------------------------------- /plugins/tagCopyPaste/tagCopyPaste.yml: -------------------------------------------------------------------------------- 1 | name: tagCopyPaste 2 | # requires: CommunityScriptsUILibrary 3 | description: Adds Copy/Paste buttons to Tags field. 4 | version: 0.4 5 | url: https://discourse.stashapp.cc/t/tagcopypaste/1858 6 | settings: 7 | createIfNotExists: 8 | displayName: Create If Not Exists 9 | description: If enabled, new tags will be created when pasted list contains entries that do not already exist. 10 | type: BOOLEAN 11 | requireConfirmation: 12 | displayName: Require Confirmation 13 | description: If enabled, user needs to confirm paste before changes are saved. 14 | type: BOOLEAN 15 | ui: 16 | requires: 17 | - CommunityScriptsUILibrary 18 | javascript: 19 | - tagCopyPaste.js 20 | css: 21 | - tagCopyPaste.css 22 | -------------------------------------------------------------------------------- /themes/Theme-Minimal/reset-scrollbars.js: -------------------------------------------------------------------------------- 1 | const existingStyles = document.styleSheets; 2 | 3 | for (let sheet of existingStyles) { 4 | try { 5 | const rules = sheet.cssRules || sheet.rules; // Get the CSS rules 6 | for (let i = 0; i < rules.length; i++) { 7 | const rule = rules[i]; 8 | // Check if the rule targets the scrollbar 9 | if ( 10 | rule.selectorText && 11 | rule.selectorText.includes("::-webkit-scrollbar") 12 | ) { 13 | // Remove the rule from the stylesheet 14 | sheet.deleteRule(i); 15 | i--; // Adjust index due to rule removal 16 | } 17 | } 18 | // console.log("done"); 19 | } catch (e) { 20 | // Catch and ignore cross-origin stylesheets (they can't be modified) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /plugins/AdulttimeInteractiveDL/AdulttimeInteractiveDL.yml: -------------------------------------------------------------------------------- 1 | name: "Adulttime Interactive Downloader" 2 | description: Download Interactive Files for Adulttime Scenes 3 | version: 0.1.2 4 | url: https://discourse.stashapp.cc/t/adulttime-interactive-downloader/1327 5 | exec: 6 | - python 7 | - "{pluginDir}/AdulttimeInteractiveDL.py" 8 | interface: raw 9 | tasks: 10 | - name: "Download" 11 | description: "Download Interactive Files from Adulttime Scenes" 12 | defaultArgs: 13 | mode: download 14 | - name: "Clear Cache" 15 | description: "Clean cached JSON Answers and Interactive Cache" 16 | defaultArgs: 17 | mode: cacheclean 18 | hooks: 19 | - name: download 20 | description: Try download if Scene updated 21 | triggeredBy: 22 | - Scene.Update.Post 23 | -------------------------------------------------------------------------------- /plugins/chooseYourAdventurePlayer/Sample/1234/choices/0.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "0", 3 | "type": null, 4 | "title": "Game Start", 5 | "map": { 6 | "title": "Demo", 7 | "description": "Your are viewing a demo of the Choose Your Adventure Player" 8 | }, 9 | "mapItem": [ 10 | { 11 | "choice_id": "TBD", 12 | "title": "TBD", 13 | "picture": "poster-1.png" 14 | } 15 | ], 16 | "skipto": "2", 17 | "choices": [ 18 | { 19 | "description": "Good Choice", 20 | "id": "1a", 21 | "type": "", 22 | "photo": "good.jpg" 23 | }, 24 | { 25 | "description": "Bad Choice", 26 | "id": "1b", 27 | "type": "", 28 | "photo": "bad.jpg" 29 | } 30 | ], 31 | "resource": { 32 | "resolved_content": "Scene0.mp4" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /plugins/SFWSwitch/README.md: -------------------------------------------------------------------------------- 1 | # SFW Switch 2 | 3 | https://discourse.stashapp.cc/t/sfw-switch/4658 4 | 5 | ## Features 6 | 7 | - Adds a SFW toggle button to the menu bar. 8 | - Green = Blur enabled 9 | - Gray = Blur disabled 10 | - Toggling the button blurs cover images and other content. 11 | - Hovering over an image temporarily removes the blur. 12 | 13 | ## Screenshots 14 | 15 | ![image](https://user-images.githubusercontent.com/23707269/206918500-0676e4ea-dcfb-4370-b35f-3f6887b76b54.png) 16 | 17 | ## Credit 18 | Original plugin by Belleyy [here](https://github.com/Belleyy/CommunityScripts/tree/pluginUI_SFWSwitch/plugins/SFW%20Switch). 19 | 20 | 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). -------------------------------------------------------------------------------- /plugins/SecondaryPerformerImage/SecondaryPerformerImage.yml: -------------------------------------------------------------------------------- 1 | name: Add secondary performer image 2 | description: Adds support for a secondary perfomrer image on the perform details page. 3 | version: 1.1 4 | url: https://discourse.stashapp.cc/t/add-secondary-performer-image/3996 5 | settings: 6 | imageMode: 7 | displayName: Image Mode 8 | 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. 9 | type: NUMBER 10 | ui: 11 | javascript: 12 | - SecondaryPerformerImage.js 13 | css: 14 | - SecondaryPerformerImage.css 15 | assets: 16 | /: . 17 | -------------------------------------------------------------------------------- /plugins/starIdentifier/star_identifier_config.py: -------------------------------------------------------------------------------- 1 | # 2 | # Paths 3 | # 4 | 5 | root_path = '' # defaults to plugins folder 6 | encodings_folder = 'star-identifier-encodings' 7 | encodings_filename = 'star-identifier-encodings.npz' 8 | encodings_error_filename = 'errors.json' 9 | 10 | # 11 | # Stash Settings 12 | # 13 | 14 | # The identifier will run on images / scenes tagged with this 15 | tag_name_identify = 'star identifier' 16 | 17 | # If the identifier can't find a face for a performer, 18 | # it will add this tag to that performer 19 | tag_name_encoding_error = 'star identifier performer error' 20 | 21 | # 22 | # Star Identifier Settings 23 | # 24 | 25 | # Tolerance: How much distance between faces to consider it a match. 26 | # Lower is more strict. 0.6 is typical best performance. 27 | tolerance = 0.6 -------------------------------------------------------------------------------- /themes/Theme-Minimal/_scrollbars.css: -------------------------------------------------------------------------------- 1 | body ::-webkit-scrollbar { 2 | -webkit-appearance: auto; 3 | height: auto !important; 4 | width: auto !important; 5 | scrollbar-width: thin; 6 | color: unset; 7 | } 8 | 9 | body ::-webkit-scrollbar-track { 10 | background: unset; 11 | border-radius: unset; 12 | scrollbar-width: thin; 13 | color: unset; 14 | } 15 | 16 | body ::-webkit-scrollbar-thumb { 17 | background: unset; 18 | border-radius: unset; 19 | cursor: unset; 20 | transition: unset; 21 | scrollbar-width: thin; 22 | color: unset; 23 | } 24 | 25 | body ::-webkit-scrollbar-thumb:window-inactive { 26 | background: unset; 27 | scrollbar-width: thin; 28 | color: unset; 29 | } 30 | 31 | body ::-webkit-scrollbar-thumb:hover { 32 | background: unset; 33 | scrollbar-width: thin; 34 | color: unset; 35 | } 36 | -------------------------------------------------------------------------------- /plugins/PythonDepManager/log.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import re 3 | from functools import partial 4 | 5 | 6 | def _log(level_char: str, s): 7 | lvl_char = "\x01{}\x02".format(level_char) 8 | s = re.sub(r"data:.+?;base64[^'\"]+", "[...]", str(s)) 9 | for line in s.splitlines(): 10 | print(lvl_char, line, file=sys.stderr, flush=True) 11 | 12 | 13 | trace = partial(_log, "t") 14 | debug = partial(_log, "d") 15 | info = partial(_log, "i") 16 | warning = partial(_log, "w") 17 | error = partial(_log, "e") 18 | 19 | 20 | def throw(s, e_type=None, e_from=None): 21 | error(s) 22 | 23 | if e_type and e_from: 24 | raise e_type(s) from e_from 25 | elif e_type and not e_from: 26 | raise e_type(s) 27 | elif not e_type and e_from: 28 | raise Exception(s) from e_from 29 | else: 30 | raise Exception(s) 31 | -------------------------------------------------------------------------------- /plugins/stashAppAndroidTvCompanion/stashAppAndroidTvCompanion.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import json 3 | import stashapi.log as log 4 | 5 | 6 | def do_logcat(args): 7 | if "logcat" not in args: 8 | log.error("logcat not found in args") 9 | return 10 | logs = args["logcat"] 11 | log.info(logs) 12 | 13 | def do_crash_report(args): 14 | if "crash_report" not in args: 15 | log.error("report not found in args") 16 | return 17 | report = args["crash_report"] 18 | log.error(report) 19 | 20 | json_input = json.loads(sys.stdin.read()) 21 | 22 | args = json_input["args"] 23 | if "mode" in args: 24 | mode = args["mode"] 25 | if mode == "logcat": 26 | do_logcat(args) 27 | elif mode == "crash_report": 28 | do_crash_report(args) 29 | else: 30 | log.warning("Unknown mode: " + mode) 31 | 32 | -------------------------------------------------------------------------------- /plugins/LocalVisage/Dockerfile: -------------------------------------------------------------------------------- 1 | # Use any base image based on glibc symbols (basically ubuntu/debian instead of alpine) 2 | FROM python:3.10-slim AS app 3 | 4 | # Choose which stash version you want here 5 | COPY --from=stashapp/stash:latest /usr/bin/stash /usr/bin/stash 6 | # COPY --from=stashapp/stash:development /usr/bin/stash /usr/bin/stash 7 | 8 | # Git has been added from stash's Dockerfile 9 | RUN apt-get update && apt-get install -y --no-install-recommends \ 10 | ca-certificates ffmpeg libvips-tools tzdata git \ 11 | && rm -rf /var/lib/apt/lists/* 12 | 13 | RUN pip install --no-cache-dir \ 14 | mechanicalsoup \ 15 | cloudscraper \ 16 | stashapp-tools 17 | 18 | ENV STASH_CONFIG_FILE=/root/.stash/config.yml 19 | 20 | # 7860 is the port used by deepface server 21 | EXPOSE 7860/tcp 22 | EXPOSE 9999/tcp 23 | CMD ["stash"] 24 | -------------------------------------------------------------------------------- /plugins/stashNotifications/stashNotifications.css: -------------------------------------------------------------------------------- 1 | .navbar-buttons .notification-btn-container { 2 | position: relative; 3 | display: inline-block; 4 | margin-top: 3px; 5 | } 6 | .navbar-buttons .notification-btn-container .dropdown-menu-end { 7 | right: 0 !important; 8 | left: auto !important; 9 | width: 300px; 10 | max-width: 300px; 11 | white-space: normal; 12 | word-break: break-word; 13 | } 14 | .navbar-buttons .notification-btn-container .dropdown-menu-end a.dropdown-item { 15 | white-space: normal; 16 | } 17 | .navbar-buttons .notification-btn-container .notification-btn { 18 | max-width: 45px; 19 | } 20 | .navbar-buttons .notification-btn-container .notification-btn .notification-badge { 21 | position: relative; 22 | right: 10px; 23 | top: -10px !important; 24 | } 25 | 26 | .stash-notification-modal h5 { 27 | margin-top: 0.5em; 28 | } 29 | -------------------------------------------------------------------------------- /plugins/timestampTrade/README.md: -------------------------------------------------------------------------------- 1 | # timestampTrade 2 | 3 | https://discourse.stashapp.cc/t/timestamp-trade/1415 4 | 5 | I've created the api https://timestamp.trade to sync markers between stash instances and xbvr. 6 | This sits along side other metadata databases like stashdb while we wait for the feature to be added there. 7 | 8 | The api does not currently require an api key but one may be required in the future. 9 | 10 | Fetching scenes require a stashdb id on the scene. 11 | Submitting markers does not require a stashid on the scene but it is recommended. 12 | 13 | ### Installation 14 | Move the `timestampTrade` directory into Stash's plugins directory, reload plugins. 15 | 16 | ### Tasks 17 | * Submit - Submit markers for all scenes that have markers. 18 | * Sync - Fetch markers for all scenes with a stash id. 19 | * Post update hook - Fetch markers for that scene -------------------------------------------------------------------------------- /plugins/imageGalleryNavigation/imageGalleryNavigation.css: -------------------------------------------------------------------------------- 1 | .imageGalleryNav-NavInput { 2 | -moz-appearance: textfield; 3 | width: 60px; 4 | } 5 | 6 | .imageGalleryNav-NavInput::-webkit-outer-spin-button, 7 | .imageGalleryNav-NavInput::-webkit-inner-spin-button { 8 | -webkit-appearance: none; 9 | margin: 0; 10 | } 11 | 12 | .imageGalleryNav-leftButton, 13 | .imageGalleryNav-rightButton { 14 | position: absolute; 15 | top: 0; 16 | bottom: 120px; 17 | width: 200px; 18 | background: none; 19 | border: none; 20 | user-select: none; 21 | opacity: 0; 22 | font-size: 40px; 23 | font-weight: bold; 24 | z-index: 999; 25 | } 26 | 27 | .imageGalleryNav-leftButton:hover, 28 | .imageGalleryNav-rightButton:hover { 29 | opacity: 0.6; 30 | } 31 | 32 | .imageGalleryNav-leftButton { 33 | left: 15px; 34 | } 35 | 36 | .imageGalleryNav-rightButton { 37 | right: 15px; 38 | } 39 | -------------------------------------------------------------------------------- /plugins/titleFromFilename/README.md: -------------------------------------------------------------------------------- 1 | # titleFromFilename 2 | 3 | https://discourse.stashapp.cc/t/titlefromfilename/1519 4 | 5 | Sets a scene's title 6 | 7 | ## Requirements 8 | - Stash ( versions after the files refactor PR, API>31 ) 9 | - Python 3.10 10 | - Requests Module (https://pypi.org/project/requests/) 11 | 12 | ## Installation 13 | 14 | - Download the whole folder `titleFromFilename` 15 | - Place it in your **plugins** folder (where the `config.yml` is). If its not there create it 16 | - Reload plugins from stash (Settings > Plugins -> Reload Plugins) 17 | - titleFromFilename should appear 18 | 19 | ## Usage 20 | When a scene is created the plugin will set the title to the filename. 21 | By default the file extension will not be added to the title. 22 | If you want to keep the file extension open `config.py` file and change `STRIP_EXT = True` to `STRIP_EXT = False` 23 | 24 | 25 | -------------------------------------------------------------------------------- /plugins/FileMonitor/version_history/README.md: -------------------------------------------------------------------------------- 1 | ##### This page was added starting on version 1.0.0 to keep track of newly added features between versions. 2 | 3 | ### 1.0.0 4 | 5 | - Added Tools-UI option to get FileMonitor running status. 6 | - Added Stash toolbar icon to get FileMonitor running status. 7 | 8 | ### 1.0.1 9 | 10 | - Added Docker support. 11 | 12 | ### 1.0.2 13 | 14 | - Added ability to monitor host file system for multiple Docker Stash installations. 15 | 16 | ### 1.0.3 17 | 18 | - Added start and stop FileMonitor button to Tools-UI FileMonitor Status 19 | - Fixed bug associated with starting FileMonitor service with no jobs waiting. 20 | 21 | ### 1.0.4 22 | 23 | - Added ping Docker GQL check before attempting to monitor Stash Dockers 24 | - Uploaded missing filemonitor.js to CommunityScripts 25 | 26 | ### 1.0.5 27 | 28 | - Fixed bug associated with library cleaning 29 | -------------------------------------------------------------------------------- /plugins/setPerformersFromTags/setPerformersFromTags.yml: -------------------------------------------------------------------------------- 1 | name: Set Performers From Tags 2 | description: Automatically sets performers in scenes and images based on tags. 3 | version: 1.0.0 4 | url: https://discourse.stashapp.cc/t/set-performers-from-tags/1390 5 | exec: 6 | - setPerformersFromTags.js 7 | interface: js 8 | errLog: info 9 | tasks: 10 | - name: Auto Set Performers From Tags 11 | description: Scans all scenes and images, matches performer names and aliases against scene/image tags, and updates them with the correct performers if necessary. May take a long time on large libraries. 12 | 13 | hooks: 14 | - name: Auto Set Performers From Tags Hook 15 | description: Automatically sets performers when a scene or image is created or updated. 16 | triggeredBy: 17 | - Scene.Create.Post 18 | - Scene.Update.Post 19 | - Image.Create.Post 20 | - Image.Update.Post 21 | -------------------------------------------------------------------------------- /plugins/themeSwitch/assets/snippets/performers/performer-image-as-backdrop.css: -------------------------------------------------------------------------------- 1 | /* [Performers tab] Place performer image in the background on performer page */ 2 | 3 | .performer-image-container.col-md-4.text-center { 4 | flex: 0 0 0%; 5 | max-width: 0%; 6 | } 7 | 8 | #performer-page .performer-image-container .btn.btn-link { 9 | position: fixed; 10 | width: 100%; 11 | top: 0; 12 | left: 0; 13 | padding: 0; 14 | } 15 | 16 | #performer-page .performer-image-container .btn.btn-link:before { 17 | content: ""; 18 | position: absolute; 19 | top: 0; 20 | left: 0; 21 | right: 0; 22 | bottom: 0; 23 | background: linear-gradient( 24 | to left, 25 | rgba(0, 0, 0, 0) 0%, 26 | rgb(0 0 0 / 75%) 100% 27 | ); 28 | z-index: 1; 29 | } 30 | 31 | #performer-page .performer-image-container .performer { 32 | max-height: none; 33 | max-width: none; 34 | width: 100%; 35 | } 36 | -------------------------------------------------------------------------------- /plugins/comicInfoExtractor/README.md: -------------------------------------------------------------------------------- 1 | # Comic Archive Metadata Extractor 2 | 3 | https://discourse.stashapp.cc/t/comic-info-extractor/1372 4 | 5 | Follows the Comicrack Standard for saving Comic Metadata in .cbz files by reading the ComicInfo.xml file in the archive and writing the result into the stash gallery. 6 | Use the config.py ImportList to define what XML names should be mapped to what. 7 | Currently, Bookmark and Type are recognized as chapters that are imported as well. 8 | The current Configuration will overwrite any value you try to set that is already set in the ComicInfo.xml. For a change in that, change the hook condition in the yml. 9 | 10 | ### Installation 11 | Move the `comicInfoExtractor` directory into Stash's plugins directory, reload plugins. 12 | 13 | ### Tasks 14 | * Load all cbz Metadata - Fetch metadata for all galleries. 15 | * Post update hook - Fetch metadata for that gallery 16 | -------------------------------------------------------------------------------- /archive/renamerOnUpdate/renamerOnUpdate.yml: -------------------------------------------------------------------------------- 1 | name: renamerOnUpdate 2 | description: Rename/move filename based on a template. 3 | url: https://github.com/stashapp/CommunityScripts 4 | version: 2.5.0 5 | exec: 6 | - python 7 | - "{pluginDir}/renamerOnUpdate.py" 8 | interface: raw 9 | hooks: 10 | - name: hook_rename 11 | description: Rename/move file when you update a scene. 12 | triggeredBy: 13 | - Scene.Update.Post 14 | tasks: 15 | - name: "Disable" 16 | description: Disable the hook 17 | defaultArgs: 18 | mode: disable 19 | - name: "Enable" 20 | description: Enable the hook 21 | defaultArgs: 22 | mode: enable 23 | - name: "Dryrun" 24 | description: Enable/disable dry-run 25 | defaultArgs: 26 | mode: dryrun 27 | - name: "Rename scenes" 28 | description: Rename all your scenes based on your config. 29 | defaultArgs: 30 | mode: bulk 31 | -------------------------------------------------------------------------------- /plugins/markerTagToScene/markerTagToScene.yml: -------------------------------------------------------------------------------- 1 | name: Scene Marker Tags to Scene 2 | description: Adds primary tag of Scene Marker to the Scene on marker create/update. 3 | version: 1.0 4 | url: https://discourse.stashapp.cc/t/scene-marker-tags-to-scene/1382 5 | exec: 6 | - markerTagToScene.js 7 | interface: js 8 | settings: 9 | allTags: 10 | displayName: All Tags 11 | description: Add all scene tags instead of just the primary scene tag. 12 | type: BOOLEAN 13 | hooks: 14 | - name: Update scene with scene marker tag 15 | description: Adds primary tag of Scene Marker to the Scene on marker create/update. 16 | triggeredBy: 17 | - SceneMarker.Create.Post 18 | - SceneMarker.Update.Post 19 | defaultArgs: 20 | mode: hook 21 | tasks: 22 | - name: Process all markers 23 | description: Add tags from all markers to scenes 24 | defaultArgs: 25 | mode: processMarkers 26 | -------------------------------------------------------------------------------- /plugins/themeSwitch/assets/snippets/studios/different-studio-cards-layout.css: -------------------------------------------------------------------------------- 1 | /* Author: Qx#1573 */ 2 | /* [Studios tab] Changes the layout of studio cards */ 3 | .studio-card.grid-card.card div.card-section div.rating-banner { 4 | display: none; 5 | } 6 | .slick-slide .studio-card-image { 7 | height: 300px; 8 | } 9 | 10 | .studio-card, 11 | .recommendation-row .studio-card { 12 | padding: 0; 13 | width: 500px; 14 | height: 300px; 15 | } 16 | 17 | .studio-card-image, 18 | .recommendation-row .studio-card .studio-card-image { 19 | max-height: 300px; 20 | width: 500px; 21 | } 22 | 23 | .studio-card.grid-card.card div.card-section { 24 | position: absolute; 25 | bottom: 0em; 26 | width: inherit; 27 | background-color: rgba(0, 0, 0, 0.7); 28 | overflow: hidden; 29 | height: 2.5em; 30 | transition: 0.5s ease-in-out; 31 | } 32 | 33 | .studio-card.grid-card.card div.card-section:hover { 34 | height: 7em; 35 | } 36 | -------------------------------------------------------------------------------- /plugins/AudioPlayerLite/AudioPlayerLite.css: -------------------------------------------------------------------------------- 1 | /* Stash Audio Player Lite */ 2 | .VideoPlayer.audio .scrubber-wrapper, 3 | .VideoPlayer.audio .vjs-big-play-button, 4 | .VideoPlayer.audio .vjs-big-button-group, 5 | .VideoPlayer.audio .vjs-loading-spinner, 6 | .VideoPlayer.audio .vjs-skip-button { 7 | display: none !important; 8 | } 9 | .VideoPlayer.audio .vjs-control-bar { 10 | display: flex !important; 11 | opacity: 100 !important; 12 | } 13 | .VideoPlayer.audio .vjs-seek-button { 14 | display: block !important; 15 | } 16 | .VideoPlayer.audio .video-js { 17 | background-color: #202b33 !important; 18 | width: 100%; 19 | } 20 | .VideoPlayer.audio #VideoJsPlayer_html5_api { 21 | display: none !important; 22 | } 23 | .VideoPlayer.audio .video-wrapper { 24 | height: 7rem !important; 25 | overflow: visible; 26 | } 27 | .VideoPlayer.audio > .vjs-poster { 28 | height: 56.25vw !important; 29 | position: relative !important; 30 | } 31 | -------------------------------------------------------------------------------- /plugins/performer-poster-backdrop/performer-poster-backdrop.yml: -------------------------------------------------------------------------------- 1 | name: Performer Poster Backdrop 2 | description: Adds a blurred poster backdrop to performer pages. 3 | version: 1.0.3 4 | url: https://discourse.stashapp.cc/t/performer-poster-backdrop/4897 5 | 6 | ui: 7 | javascript: 8 | - performer-poster-backdrop.js 9 | css: 10 | - performer-poster-backdrop.css 11 | 12 | settings: 13 | opacity: 14 | displayName: Backdrop opacity 15 | description: "0–1 (leave blank for default: 1)" 16 | type: STRING 17 | 18 | blur: 19 | displayName: Backdrop blur 20 | description: "Pixels (leave blank for default: 10)" 21 | type: STRING 22 | 23 | defaultYOffset: 24 | displayName: Default Y offset 25 | description: "Background position % (leave blank for default: 20)" 26 | type: STRING 27 | 28 | perPerformerOffsets: 29 | displayName: Per-performer Y overrides 30 | description: "Comma seperated: performerId:percent (blank = none)" 31 | type: STRING 32 | -------------------------------------------------------------------------------- /plugins/hotCards/utils/fetchInterceptor.js: -------------------------------------------------------------------------------- 1 | (() => { 2 | if (window.stashListener) return; 3 | 4 | const { fetch: originalFetch } = window; 5 | const stashListener = new EventTarget(); 6 | 7 | window.fetch = async (...args) => { 8 | let [resource, config] = args; 9 | const response = await originalFetch(resource, config); 10 | const contentType = response.headers.get("content-type"); 11 | 12 | if ( 13 | typeof resource === "string" && 14 | contentType && 15 | (contentType.indexOf('application/json') !== -1 || 16 | contentType.indexOf('application/graphql-response+json') !== -1) && 17 | resource.endsWith("/graphql") 18 | ) { 19 | try { 20 | const data = await response.clone().json(); 21 | stashListener.dispatchEvent( 22 | new CustomEvent("response", { detail: data }) 23 | ); 24 | } catch (e) { 25 | console.error("Error parsing JSON:", e); 26 | } 27 | } 28 | return response; 29 | }; 30 | 31 | window.stashListener = stashListener; 32 | })(); 33 | -------------------------------------------------------------------------------- /themes/Theme-ColorPalette/Theme-ColorPalette.js: -------------------------------------------------------------------------------- 1 | if (!localStorage.getItem("UserColor")) { 2 | localStorage.setItem("UserColor", 0); 3 | } 4 | document.documentElement.style.setProperty( 5 | "--maincolorDeg", 6 | localStorage.getItem("UserColor") + "deg" 7 | ); 8 | 9 | async function mainFunction() { 10 | let hueValue; 11 | try { 12 | const config = await csLib.getConfiguration("Theme-ColorPalette", {}); 13 | hueValue = config.hue; 14 | let UserColorValue = hueValue; 15 | localStorage.setItem("UserColor", UserColorValue); 16 | } catch (error) { 17 | console.error("Error getting configuration:", error); 18 | } 19 | return hueValue; 20 | } 21 | mainFunction(); 22 | 23 | if (!localStorage.getItem("UserColor")) { 24 | localStorage.setItem("UserColor", 0); 25 | } 26 | document.documentElement.style.setProperty( 27 | "--maincolorDeg", 28 | localStorage.getItem("UserColor") + "deg" 29 | ); 30 | 31 | document.querySelector("meta[name='theme-color']").content = 32 | window.getComputedStyle( 33 | document.querySelectorAll(".top-nav")[0] 34 | ).backgroundColor; 35 | -------------------------------------------------------------------------------- /plugins/externalLinksEnhanced/externalLinksEnhanced.css: -------------------------------------------------------------------------------- 1 | .external-links-button .btn.link { 2 | width: 36px; 3 | } 4 | .external-links-button .btn.link img { 5 | width: 100%; 6 | height: 100%; 7 | } 8 | .external-links-button .btn.link.facebook { 9 | color: #1877f2; 10 | } 11 | .external-links-button .btn.link.imdb { 12 | color: #f5c518; 13 | } 14 | .external-links-button .btn.link.patreon { 15 | color: #f96854; 16 | } 17 | .external-links-button .btn.link.reddit { 18 | color: #ff4500; 19 | } 20 | .external-links-button .btn.link.telegram { 21 | color: #0088cc; 22 | } 23 | .external-links-button .btn.link.tiktok { 24 | color: white; 25 | } 26 | .external-links-button .btn.link.tumblr { 27 | color: #4c75a3; 28 | } 29 | .external-links-button .btn.link.twitch { 30 | color: #784ec7; 31 | } 32 | .external-links-button .btn.link.twitter { 33 | color: #1da1f2; 34 | } 35 | .external-links-button .btn.link.vk { 36 | color: #4c75a3; 37 | } 38 | .external-links-button .btn.link.wordpress { 39 | color: gray; 40 | } 41 | .external-links-button .btn.link.youtube { 42 | color: red; 43 | } 44 | -------------------------------------------------------------------------------- /plugins/StashRandomButton/LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2025 Nightyonlyy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /plugins/starIdentifier/star_identifier.yml: -------------------------------------------------------------------------------- 1 | name: Star Identifier 2 | description: Use facial recognition to automatically identify who is in images or scene screenshots from the performers already in your Stash library. 3 | version: 1.0 4 | url: https://discourse.stashapp.cc/t/star-identifier/3761 5 | exec: 6 | - python 7 | - "{pluginDir}/py_plugins/star_identifier.py" 8 | interface: raw 9 | tasks: 10 | - name: Export Performers 11 | description: Run this first! Exports current performer images and adds them to an encoding file for recognition. 12 | defaultArgs: 13 | mode: export_known 14 | - name: Identify Images 15 | description: Compares images tagged with 'star identifier' (by default) to exported performers, and adds all possible matches to the images. 16 | defaultArgs: 17 | mode: identify_imgs 18 | - name: Identify Scene Screenshots 19 | description: Compares scene screenshots tagged with 'star identifier' (by default) to exported performers, and adds all possible matches to the scenes. 20 | defaultArgs: 21 | mode: identify_scene_screenshots 22 | -------------------------------------------------------------------------------- /plugins/themeSwitch/assets/snippets/global/blur-nsfw-images.css: -------------------------------------------------------------------------------- 1 | /* [Global changes] Blur NSFW images */ 2 | 3 | .scene-card-preview-video, 4 | .scene-card-preview-image, 5 | .image-card-preview-image, 6 | .image-thumbnail, 7 | .gallery-card-image, 8 | .performer-card-image, 9 | .tag-card-image, 10 | img.performer, 11 | .movie-card-image, 12 | .gallery .flexbin img, 13 | .wall-item-media, 14 | .scene-studio-overlay .image-thumbnail, 15 | .image-card-preview-image, 16 | #scene-details-container .text-input, 17 | #scene-details-container .scene-header, 18 | #scene-details-container .react-select__single-value, 19 | .scene-details .pre, 20 | #scene-tabs-tabpane-scene-file-info-panel span.col-8.text-truncate > a, 21 | .gallery .flexbin img, 22 | .movie-details .logo { 23 | filter: blur(12px); 24 | } 25 | 26 | .scene-card-video { 27 | filter: blur(13px); 28 | } 29 | 30 | .jw-video, 31 | .jw-preview, 32 | .jw-flag-floating, 33 | .image-container, 34 | .studio-logo, 35 | .scene-cover { 36 | filter: blur(20px); 37 | } 38 | 39 | .movie-card .text-truncate, 40 | .scene-card .card-section { 41 | filter: blur(4px); 42 | } 43 | -------------------------------------------------------------------------------- /plugins/AudioPlayer/AudioPlayer.css: -------------------------------------------------------------------------------- 1 | /* Stash Audio Player */ 2 | .VideoPlayer.audio .scrubber-wrapper, 3 | .VideoPlayer.audio .vjs-big-play-button, 4 | .VideoPlayer.audio .vjs-big-button-group, 5 | .VideoPlayer.audio .vjs-loading-spinner, 6 | .VideoPlayer.audio .vjs-skip-button { 7 | display: none !important; 8 | } 9 | .VideoPlayer.audio .vjs-control-bar { 10 | display: flex !important; 11 | opacity: 100 !important; 12 | } 13 | .VideoPlayer.audio .vjs-seek-button { 14 | display: block !important; 15 | } 16 | .VideoPlayer.audio .video-js { 17 | background-color: #202b33 !important; 18 | width: 100%; 19 | } 20 | .VideoPlayer.audio #VideoJsPlayer_html5_api { 21 | display: none !important; 22 | } 23 | .VideoPlayer.audio .video-wrapper { 24 | height: 7rem !important; 25 | overflow: visible; 26 | } 27 | .VideoPlayer.audio > .vjs-poster { 28 | height: 56.25vw !important; 29 | position: relative !important; 30 | } 31 | #audioToggle.enabled { 32 | color: #48aff0; 33 | } 34 | #audioToggle:hover { 35 | color: #fff; 36 | } 37 | #audioToggle { 38 | color: rgba(191,204,214,.5); 39 | } 40 | -------------------------------------------------------------------------------- /plugins/pathParser/pathParser.yml: -------------------------------------------------------------------------------- 1 | name: Path Parser 2 | description: Updates scene info based on the file path. 3 | version: 1.0 4 | url: https://discourse.stashapp.cc/t/path-parser/1386 5 | exec: 6 | - pathParser.js 7 | interface: js 8 | hooks: 9 | - name: Run Rules on scan 10 | description: Updates scene info whenever a new scene is added. 11 | triggeredBy: 12 | - Scene.Create.Post 13 | tasks: 14 | - name: Create Tags 15 | description: Create tags used by the path parser tasks. 16 | defaultArgs: 17 | task: createTags 18 | runTag: "[Run]" 19 | testTag: "[Test]" 20 | - name: Remove Tags 21 | description: Remove tags used by the path parser tasks. 22 | defaultArgs: 23 | task: removeTags 24 | runTag: "[Run]" 25 | testTag: "[Test]" 26 | - name: Run Rules 27 | description: Run rules for scenes containing the run tag. 28 | defaultArgs: 29 | task: runRules 30 | runTag: "[Run]" 31 | - name: Test Rules 32 | description: Test rules for scenes containing the test tag. 33 | defaultArgs: 34 | task: testRules 35 | testTag: "[Test]" 36 | -------------------------------------------------------------------------------- /plugins/externalLinksEnhanced/custom/bluesky.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /plugins/tagGalleriesFromImages/tagGalleriesFromImages.yml: -------------------------------------------------------------------------------- 1 | name: Tag galleries from images 2 | description: tags galleries with tags of contained images. 3 | version: 0.1 4 | url: https://discourse.stashapp.cc/t/tag-galleries-from-images/3904 5 | exec: 6 | - python 7 | - "{pluginDir}/tagGalleriesFromImages.py" 8 | interface: raw 9 | 10 | hooks: 11 | - name: update gallery 12 | description: Will tag galleries with tags of contained images 13 | triggeredBy: 14 | - Gallery.Update.Post 15 | - Gallery.Create.Post 16 | 17 | settings: 18 | excludeOrganized: 19 | displayName: Exclude galleries marked as organized 20 | description: Do not automatically tag galleries if it is marked as organized 21 | type: BOOLEAN 22 | excludeWithTag: 23 | displayName: Exclude galleries with tag from Hook 24 | description: Do not automatically tag galleries if the gallery has this tag 25 | type: STRING 26 | 27 | tasks: 28 | - name: "Tag all galleries" 29 | description: Loops through all galleries, and applies the tags of the contained images. Can take a long time on large db's. 30 | defaultArgs: 31 | mode: processAll 32 | -------------------------------------------------------------------------------- /plugins/nfoSceneParser/abstractParser.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | 4 | class AbstractParser: 5 | 6 | empty_default = { "actors": [], "tags": [] } 7 | 8 | # Max number if images to process (2 for front/back cover in movies). 9 | _image_Max = 2 10 | 11 | def __init__(self): 12 | self._defaults = [self.empty_default] 13 | 14 | def _find_in_parents(self, start_path, searched_file): 15 | parent_dir = os.path.dirname(start_path) 16 | file = os.path.join(start_path, searched_file) 17 | if os.path.exists(file): 18 | return file 19 | elif start_path != parent_dir: 20 | # Not found => recurse via parent 21 | return self._find_in_parents(parent_dir, searched_file) 22 | 23 | def _get_default(self, key, source=None): 24 | for default in self._defaults: 25 | # Source filter: skip default if it is not of the specified source 26 | if source and default.get("source") != source: 27 | continue 28 | if default.get(key) is not None: 29 | return default.get(key) 30 | 31 | def parse(self): 32 | pass 33 | -------------------------------------------------------------------------------- /plugins/SecondaryPerformerImage/SecondaryPerformerImage.css: -------------------------------------------------------------------------------- 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} 2 | -------------------------------------------------------------------------------- /plugins/AITagger/ai_tagger.yml: -------------------------------------------------------------------------------- 1 | name: AI Tagger 2 | description: Tag videos and Images with Locally hosted AI using Skier's Free and Patreon AI models 3 | version: 2.2 4 | url: https://discourse.stashapp.cc/t/ai-tagger/587 5 | exec: 6 | - python 7 | - "{pluginDir}/ai_tagger.py" 8 | interface: raw 9 | tasks: 10 | - name: Tag Images 11 | description: Run AI Tagger on images with AI_TagMe tag 12 | defaultArgs: 13 | mode: tag_images 14 | - name: Tag Scenes 15 | description: Run AI Tagger on scenes with AI_TagMe tag 16 | defaultArgs: 17 | mode: tag_scenes 18 | - name: Collect Incorrect Markers and Images 19 | description: Collects data from markers and images that were AI Tagged but were manually marked with AI_Incorrect due to the AI making a mistake. This will collect the data and output as a file which can be sent to Skier to improve the AI. 20 | defaultArgs: 21 | mode: collect_incorrect_markers 22 | - name: Find Marker Settings 23 | description: Find Optimal Marker Settings based on a video that has manually tuned markers and has been processed by the AI previously. Only 1 video should have AI_TagMe before running. 24 | defaultArgs: 25 | mode: find_marker_settings 26 | -------------------------------------------------------------------------------- /plugins/cjCardTweaks/cjCardTweaks.yml: -------------------------------------------------------------------------------- 1 | name: CJ's Card Tweaks. 2 | # requires: CommunityScriptsUILibrary 3 | description: Provides various tweaks for the Stash Cards. 4 | version: 1.1 5 | url: https://discourse.stashapp.cc/t/cjs-card-tweaks/1342 6 | ui: 7 | requires: 8 | - CommunityScriptsUILibrary 9 | javascript: 10 | - https://cdn.jsdelivr.net/gh/HandyRandyx/stash-plugins@main/utils/fetchInterceptor.js 11 | - https://cdn.jsdelivr.net/gh/HandyRandyx/stash-plugins@main/utils/stashHandler.js 12 | - https://cdn.jsdelivr.net/gh/HandyRandyx/stash-plugins@main/utils/registerPathChangeListener.js 13 | - https://cdn.jsdelivr.net/gh/cj12312021/stash-plugins@main/utils/waitForClass.js 14 | - cjCardTweaks.js 15 | settings: 16 | addBannerDimension: 17 | displayName: 3D rating banner 18 | description: "Adds additional dimension to the rating banners." 19 | type: BOOLEAN 20 | fileCount: 21 | displayName: Enable for file count 22 | description: "Displays file count on scene, gallery, and image cards." 23 | type: BOOLEAN 24 | performerProfileCards: 25 | displayName: Performer profile cards 26 | description: "Tweaks performer cards to use a traditional profile design." 27 | type: BOOLEAN 28 | -------------------------------------------------------------------------------- /plugins/tagImagesWithPerfTags/tagImagesWithPerfTags.yml: -------------------------------------------------------------------------------- 1 | name: Tag Images From Performer Tags 2 | description: tags images with performer tags. 3 | version: 0.1 4 | url: https://discourse.stashapp.cc/t/tag-images-from-performer-tags/2059 5 | exec: 6 | - python 7 | - "{pluginDir}/tagImagesWithPerfTags.py" 8 | interface: raw 9 | 10 | hooks: 11 | - name: update image 12 | description: Will tag image with selected performers tags 13 | triggeredBy: 14 | - Image.Update.Post 15 | - Image.Create.Post 16 | 17 | settings: 18 | excludeImageOrganized: 19 | displayName: Exclude Images marked as organized 20 | description: Do not automatically tag images with performer tags if the image is marked as organized 21 | type: BOOLEAN 22 | excludeImageWithTag: 23 | displayName: Exclude Images with Tag from Hook 24 | description: Do not automatically tag images with performer tags if the image has this tag 25 | type: STRING 26 | 27 | tasks: 28 | - name: "Tag All Images" 29 | description: Loops through all performers, finds all of their images, then applies the performers tags to each of the images they appear in. Can take a long time on large db's. 30 | defaultArgs: 31 | mode: processAll 32 | -------------------------------------------------------------------------------- /plugins/tagScenesWithPerfTags/tagScenesWithPerfTags.yml: -------------------------------------------------------------------------------- 1 | name: Tag Scenes From Performer Tags 2 | description: tags scenes with performer tags. 3 | version: 0.2.3 4 | url: https://discourse.stashapp.cc/t/tag-scenes-from-performer-tags/1413 5 | exec: 6 | - python 7 | - "{pluginDir}/tagScenesWithPerfTags.py" 8 | interface: raw 9 | 10 | hooks: 11 | - name: update scene 12 | description: Will tag scene with selected performers tags 13 | triggeredBy: 14 | - Scene.Update.Post 15 | - Scene.Create.Post 16 | 17 | settings: 18 | excludeSceneOrganized: 19 | displayName: Exclude Scenes marked as organized 20 | description: Do not automatically tag scenes with performer tags if the scene is marked as organized 21 | type: BOOLEAN 22 | excludeSceneWithTag: 23 | displayName: Exclude Scenes with Tag from Hook 24 | description: Do not automatically tag scenes with performer tags if the scene has this tag 25 | type: STRING 26 | 27 | tasks: 28 | - name: "Tag All Scenes" 29 | description: Loops through all performers, finds all of their scenes, then applies the performers tags to each of the scenes they appear in. Can take a long time on large db's. 30 | defaultArgs: 31 | mode: processAll 32 | -------------------------------------------------------------------------------- /plugins/LocalVisage/stashface/app.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | # Set DeepFace home directory 4 | os.environ["DEEPFACE_HOME"] = "." 5 | os.environ["CUDA_VISIBLE_DEVICES"] = "-1" 6 | os.environ["TF_ENABLE_ONEDNN_OPTS"] = "0" 7 | os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3" # Suppress TF logs 8 | # Add the plugins directory to sys.path 9 | plugins_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..')) 10 | if plugins_dir not in sys.path: 11 | sys.path.insert(0, plugins_dir) 12 | 13 | 14 | from stashapi.stashapp import StashInterface 15 | 16 | 17 | try: 18 | from models.data_manager import DataManager 19 | from web.interface import WebInterface 20 | except ImportError as e: 21 | print(f"Error importing modules: {e}") 22 | input("Ensure you have installed the required dependencies. Press Enter to exit.") 23 | 24 | 25 | 26 | def main(): 27 | """Main entry point for the application""" 28 | # Initialize data manager 29 | data_manager = DataManager( 30 | voy_root_folder=os.path.abspath(os.path.join(os.path.dirname(__file__),"../voy_db")), 31 | ) 32 | 33 | # Initialize and launch web interface 34 | web_interface = WebInterface(data_manager, default_threshold=0.5) 35 | web_interface.launch(server_name="0.0.0.0", server_port=7860, share=False) 36 | 37 | if __name__ == "__main__": 38 | main() 39 | -------------------------------------------------------------------------------- /plugins/RenameFile/version_history/README.md: -------------------------------------------------------------------------------- 1 | ##### This page was added starting on version 0.5.6 to keep track of newly added features between versions. 2 | ### 0.5.6 3 | - Fixed bug with studio getting the studio ID instead of the name of the studio in rename process. 4 | - Improved performance by having code get all required scene details in one call to stash. 5 | - To remove UI clutter, move rarely used options (performerAppendEnable, studioAppendEnable, tagAppendEnable, & fileRenameViaMove) to renamefile_settings.py 6 | - Change options (performerAppendEnable, studioAppendEnable, tagAppendEnable) to default to True (enabled) 7 | ### 0.5.7 8 | - Uploaded missing renamefile.js and renamefile.css files to CommunityScripts 9 | ### 0.5.8 10 | - Added setting max_performers to renamefile_settings.py, which allows limitting the quantity of performers added to file name. 11 | ### 0.5.9 12 | - Added rename associated file feature. When video file is renamed, associated files will get renamed. 13 | - Associated files are determind by file extensions listed in variable **associated_files_to_rename** which is in **renamefile_settings.py** file. 14 | - Option **rename_associated_files_enable** can be used to disable this feature. It's turned on by default. 15 | ### 1.0.0 16 | - Fixed Dry-Run bug, which changed the file name in the database when Dry-Run was enabled. 17 | ### 1.0.1 18 | - 19 | -------------------------------------------------------------------------------- /plugins/DupFileManager/DupFileManager.css: -------------------------------------------------------------------------------- 1 | .scene-card__date { 2 | color: #bfccd6; 3 | font-size: 0.85em; 4 | } 5 | 6 | .scene-card__performer { 7 | display: inline-block; 8 | font-weight: 500; 9 | margin-right: 0.5em; 10 | } 11 | .scene-card__performer a { 12 | color: #137cbd; 13 | } 14 | 15 | .scene-card__performers, 16 | .scene-card__tags { 17 | -webkit-box-orient: vertical; 18 | display: -webkit-box; 19 | -webkit-line-clamp: 1; 20 | overflow: hidden; 21 | } 22 | .scene-card__performers:hover, 23 | .scene-card__tags:hover { 24 | -webkit-line-clamp: unset; 25 | overflow: visible; 26 | } 27 | 28 | .scene-card__tags .tag-item { 29 | margin-left: 0; 30 | } 31 | 32 | .scene-performer-popover .image-thumbnail { 33 | margin: 1em; 34 | } 35 | 36 | /* Dashed border */ 37 | hr.dashed { 38 | border-top: 3px dashed #bbb; 39 | } 40 | 41 | /* Dotted border */ 42 | hr.dotted { 43 | border-top: 3px dotted #bbb; 44 | } 45 | 46 | /* Solid border */ 47 | hr.solid { 48 | border-top: 3px solid #bbb; 49 | } 50 | 51 | /* Rounded border */ 52 | hr.rounded { 53 | border-top: 8px solid #bbb; 54 | border-radius: 5px; 55 | } 56 | 57 | h3.under_construction { 58 | color: red; 59 | background-color: yellow; 60 | } 61 | 62 | h3.submenu { 63 | color: Tomato; 64 | background-color: rgba(100, 100, 100); 65 | } 66 | 67 | /*# sourceMappingURL=DupFileManager.css.map */ 68 | -------------------------------------------------------------------------------- /plugins/tagCopyPaste/README.md: -------------------------------------------------------------------------------- 1 | # Tag Copy/Paste 2 | 3 | https://discourse.stashapp.cc/t/tagcopypaste/1858 4 | 5 | This plugin adds Copy and Paste functionality to the Tags input field that allows for easier bulk adding and copying of tags, with the goal of making it easy to copy Tags between objects, bulk load manually created tag lists, or load tag lists copied from AI tagger output. 6 | 7 | Copy/Paste of Tags can be performed either with dedicated Copy/Paste buttons or by selecting the Tag input field and performing the typical CTRL+C/CTRL+V. 8 | 9 | Copying will create a comma delimited list of all currently entered tags and put this on your clipboard. 10 | 11 | Pasting will check your current clipboard for a comma and/or newline delimited string and add these as Tags, optionally creating any missing tags. Pasted tags will be checked against both primary Tag names and all aliases, comparisons are not case sensitive and allow "_" to be interpreted as a space. 12 | 13 | **Note**: This plugin will prompt you to grant access to the clipboard. This must be granted in order for this plugin to work. 14 | 15 | ## Config Options: 16 | - **Create If Not Exists**: If enabled, new tags will be created when pasted list contains entries that do not already exist. DEFAULT: Disabled 17 | - **Require Confirmation**: If enabled, user needs to confirm paste before changes are saved. DEFAULT: Disabled 18 | 19 | -------------------------------------------------------------------------------- /plugins/SecondaryPerformerImage/README.md: -------------------------------------------------------------------------------- 1 | # Secondary Performer Images 2 | 3 | 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. 4 | 5 | ## Modes 6 | 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. 7 | 8 | 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. 9 | 10 | 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. 11 | 12 | 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. 13 | -------------------------------------------------------------------------------- /plugins/nfoSceneParser/log.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | 4 | # Log messages sent from a plugin instance are transmitted via stderr and are 5 | # encoded with a prefix consisting of special character SOH, then the log 6 | # level (one of t, d, i, w, e, or p - corresponding to trace, debug, info, 7 | # warning, error and progress levels respectively), then special character 8 | # STX. 9 | # 10 | # The LogTrace, LogDebug, LogInfo, LogWarning, and LogError methods, and their equivalent 11 | # formatted methods are intended for use by plugin instances to transmit log 12 | # messages. The LogProgress method is also intended for sending progress data. 13 | # 14 | 15 | def __prefix(level_char): 16 | start_level_char = b'\x01' 17 | end_level_char = b'\x02' 18 | 19 | ret = start_level_char + level_char + end_level_char 20 | return ret.decode() 21 | 22 | 23 | def __log(level_char, s): 24 | if level_char == "": 25 | return 26 | 27 | print(__prefix(level_char) + s + "\n", file=sys.stderr, flush=True) 28 | 29 | 30 | def LogTrace(s): 31 | __log(b't', s) 32 | 33 | 34 | def LogDebug(s): 35 | __log(b'd', s) 36 | 37 | 38 | def LogInfo(s): 39 | __log(b'i', s) 40 | 41 | 42 | def LogWarning(s): 43 | __log(b'w', s) 44 | 45 | 46 | def LogError(s): 47 | __log(b'e', s) 48 | 49 | 50 | def LogProgress(p): 51 | progress = min(max(0, p), 1) 52 | __log(b'p', str(progress)) 53 | -------------------------------------------------------------------------------- /archive/renamerOnUpdate/log.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | 4 | # Log messages sent from a plugin instance are transmitted via stderr and are 5 | # encoded with a prefix consisting of special character SOH, then the log 6 | # level (one of t, d, i, w, e, or p - corresponding to trace, debug, info, 7 | # warning, error and progress levels respectively), then special character 8 | # STX. 9 | # 10 | # The LogTrace, LogDebug, LogInfo, LogWarning, and LogError methods, and their equivalent 11 | # formatted methods are intended for use by plugin instances to transmit log 12 | # messages. The LogProgress method is also intended for sending progress data. 13 | # 14 | 15 | 16 | def __prefix(level_char): 17 | start_level_char = b"\x01" 18 | end_level_char = b"\x02" 19 | 20 | ret = start_level_char + level_char + end_level_char 21 | return ret.decode() 22 | 23 | 24 | def __log(level_char, s): 25 | if level_char == "": 26 | return 27 | 28 | print(__prefix(level_char) + s + "\n", file=sys.stderr, flush=True) 29 | 30 | 31 | def LogTrace(s): 32 | __log(b"t", s) 33 | 34 | 35 | def LogDebug(s): 36 | __log(b"d", s) 37 | 38 | 39 | def LogInfo(s): 40 | __log(b"i", s) 41 | 42 | 43 | def LogWarning(s): 44 | __log(b"w", s) 45 | 46 | 47 | def LogError(s): 48 | __log(b"e", s) 49 | 50 | 51 | def LogProgress(p): 52 | progress = min(max(0, p), 1) 53 | __log(b"p", str(progress)) 54 | -------------------------------------------------------------------------------- /plugins/externalLinksEnhanced/custom/onlyfans.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /plugins/starIdentifier/log.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | 4 | # Log messages sent from a plugin instance are transmitted via stderr and are 5 | # encoded with a prefix consisting of special character SOH, then the log 6 | # level (one of t, d, i, w, e, or p - corresponding to trace, debug, info, 7 | # warning, error and progress levels respectively), then special character 8 | # STX. 9 | # 10 | # The LogTrace, LogDebug, LogInfo, LogWarning, and LogError methods, and their equivalent 11 | # formatted methods are intended for use by plugin instances to transmit log 12 | # messages. The LogProgress method is also intended for sending progress data. 13 | # 14 | 15 | def __prefix(level_char): 16 | start_level_char = b'\x01' 17 | end_level_char = b'\x02' 18 | 19 | ret = start_level_char + level_char + end_level_char 20 | return ret.decode() 21 | 22 | 23 | def __log(level_char, s): 24 | if level_char == "": 25 | return 26 | 27 | print(__prefix(level_char) + s + "\n", file=sys.stderr, flush=True) 28 | 29 | 30 | def LogTrace(s): 31 | __log(b't', s) 32 | 33 | 34 | def LogDebug(s): 35 | __log(b'd', s) 36 | 37 | 38 | def LogInfo(s): 39 | __log(b'i', s) 40 | 41 | 42 | def LogWarning(s): 43 | __log(b'w', s) 44 | 45 | 46 | def LogError(s): 47 | __log(b'e', s) 48 | 49 | 50 | def LogProgress(p): 51 | progress = min(max(0, p), 1) 52 | __log(b'p', str(progress)) 53 | -------------------------------------------------------------------------------- /plugins/titleFromFilename/log.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | 4 | # Log messages sent from a plugin instance are transmitted via stderr and are 5 | # encoded with a prefix consisting of special character SOH, then the log 6 | # level (one of t, d, i, w, e, or p - corresponding to trace, debug, info, 7 | # warning, error and progress levels respectively), then special character 8 | # STX. 9 | # 10 | # The LogTrace, LogDebug, LogInfo, LogWarning, and LogError methods, and their equivalent 11 | # formatted methods are intended for use by plugin instances to transmit log 12 | # messages. The LogProgress method is also intended for sending progress data. 13 | # 14 | 15 | 16 | def __prefix(level_char): 17 | start_level_char = b"\x01" 18 | end_level_char = b"\x02" 19 | 20 | ret = start_level_char + level_char + end_level_char 21 | return ret.decode() 22 | 23 | 24 | def __log(level_char, s): 25 | if level_char == "": 26 | return 27 | 28 | print(__prefix(level_char) + s + "\n", file=sys.stderr, flush=True) 29 | 30 | 31 | def LogTrace(s): 32 | __log(b"t", s) 33 | 34 | 35 | def LogDebug(s): 36 | __log(b"d", s) 37 | 38 | 39 | def LogInfo(s): 40 | __log(b"i", s) 41 | 42 | 43 | def LogWarning(s): 44 | __log(b"w", s) 45 | 46 | 47 | def LogError(s): 48 | __log(b"e", s) 49 | 50 | 51 | def LogProgress(p): 52 | progress = min(max(0, p), 1) 53 | __log(b"p", str(progress)) 54 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy repository to Github Pages 2 | 3 | on: 4 | push: 5 | branches: [main, stable] 6 | 7 | # Allows you to run this workflow manually from the Actions tab 8 | workflow_dispatch: 9 | 10 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 11 | permissions: 12 | contents: read 13 | pages: write 14 | id-token: write 15 | 16 | jobs: 17 | build: 18 | runs-on: ubuntu-22.04 19 | steps: 20 | - name: Checkout main 21 | uses: actions/checkout@v2 22 | with: 23 | path: main 24 | ref: main 25 | fetch-depth: "0" 26 | - run: | 27 | cd main 28 | ./build_site.sh ../_site/stable 29 | - name: Checkout dev 30 | uses: actions/checkout@v2 31 | with: 32 | path: dev 33 | # change this ref to whatever dev branch/tag we need when necessary 34 | ref: main 35 | fetch-depth: "0" 36 | - run: | 37 | cd dev 38 | ../main/build_site.sh ../_site/develop 39 | - uses: actions/upload-pages-artifact@v3 40 | 41 | deploy: 42 | environment: 43 | name: github-pages 44 | url: ${{ steps.deployment.outputs.page_url }} 45 | runs-on: ubuntu-22.04 46 | needs: build 47 | steps: 48 | - name: Deploy to GitHub Pages 49 | id: deployment 50 | uses: actions/deploy-pages@v4 51 | -------------------------------------------------------------------------------- /plugins/ExtraPerformerInfo/extraPerformerInfo.yml: -------------------------------------------------------------------------------- 1 | name: Extra Performer Info 2 | description: add award info from wikidata 3 | version: 0.2 4 | url: https://discourse.stashapp.cc/t/extra-performer-info/1332 5 | exec: 6 | - python 7 | - "{pluginDir}/extraPerformerInfo.py" 8 | interface: raw 9 | settings: 10 | processWikidata: 11 | displayName: process wikidata.org urls 12 | description: Enabling fetching info from wikidata 13 | type: BOOLEAN 14 | wikidatExtraUrls: 15 | displayName: Extra Urls 16 | description: Add extra porn related urls 17 | type: BOOLEAN 18 | awards: 19 | displayName: Add award info 20 | description: Add award info to custom fields if this has been added to wikidata 21 | type: BOOLEAN 22 | createTag: 23 | displayName: Add tags to performers 24 | description: Add [Award Winner], [Award Nominated] tags to performers 25 | type: BOOLEAN 26 | otherInfo: 27 | displayName: Add misc other info to custom fields 28 | description: Add other info such as birth place, ethnic group and Occupation to custom fields 29 | type: BOOLEAN 30 | 31 | hooks: 32 | - name: Process performer 33 | description: Process wikidata or other api url 34 | triggeredBy: 35 | - Performer.Update.Post 36 | tasks: 37 | - name: Process all 38 | description: Process all scenes and add extra tags if configured 39 | defaultArgs: 40 | mode: processAll 41 | -------------------------------------------------------------------------------- /themes/Theme-Minimal/nav-bar.css: -------------------------------------------------------------------------------- 1 | .top-nav { 2 | padding-top: 1cap; 3 | padding-bottom: 1cap; 4 | margin-bottom: 1cap; 5 | background-color: var(--primary-1) !important; 6 | position: sticky; 7 | 8 | .btn { 9 | font-size: var(--text-sm); 10 | color: var(--primary-11); 11 | padding-top: 0.5cap !important; 12 | padding-bottom: 0.5cap !important; 13 | transition: color 0.2s ease-in-out; 14 | 15 | &.active, 16 | &:active { 17 | color: var(--primary-12); 18 | background-color: unset !important; 19 | svg { 20 | color: var(--primary-12); 21 | } 22 | } 23 | 24 | &:hover { 25 | color: var(--primary-12); 26 | svg { 27 | color: var(--primary-11); 28 | } 29 | } 30 | 31 | svg { 32 | transition: color 0.2s ease-in-out; 33 | margin-left: 0.5ch; 34 | margin-right: 1ch; 35 | color: var(--primary-9); 36 | } 37 | } 38 | 39 | .navbar-brand { 40 | font-size: inherit; 41 | } 42 | 43 | .navbar-collapse { 44 | background-color: var(--primary-1) !important; 45 | } 46 | } 47 | 48 | .navbar-toggler { 49 | color: unset; 50 | padding: 0.5em 0; 51 | text-align: center; 52 | width: 3em; 53 | } 54 | 55 | .navbar-buttons .btn { 56 | align-items: center; 57 | display: flex; 58 | } 59 | 60 | .nav-link { 61 | padding: 0; 62 | svg { 63 | color: var(--primary-11); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /plugins/performer-poster-backdrop/performer-poster-backdrop.css: -------------------------------------------------------------------------------- 1 | .pb-hero { 2 | position: absolute; 3 | inset: 2px; 4 | 5 | background-size: cover; 6 | background-position: center var(--pb-y, 20%); 7 | 8 | opacity: var(--pb-opacity, 1); 9 | filter: blur(var(--pb-blur, 10px)); 10 | transform: scale(1.2); 11 | z-index: 0; 12 | pointer-events: none; 13 | } 14 | 15 | .pb-hero::after { 16 | content: ""; 17 | position: absolute; 18 | inset: 0; 19 | 20 | background: 21 | radial-gradient( 22 | ellipse at center, 23 | rgba(0,0,0,0.0) 0%, 24 | rgba(0,0,0,0.35) 55%, 25 | rgba(0,0,0,0.75) 100% 26 | ), 27 | linear-gradient( 28 | to bottom, 29 | rgba(0,0,0,0.15), 30 | rgba(0,0,0,0.75) 31 | ); 32 | 33 | box-shadow: 34 | inset 0 0 0 1px rgba(255,255,255,0.025), 35 | inset 0 14px 28px rgba(0,0,0,0.55), 36 | inset 0 -22px 36px rgba(0,0,0,0.85); 37 | } 38 | 39 | /* IMPORTANT: only the REAL header */ 40 | #performer-page .detail-header.full-width { 41 | position: relative; 42 | overflow: hidden; 43 | } 44 | 45 | /* Lift content above banner ONLY in the REAL header */ 46 | #performer-page .detail-header.full-width > *:not(.pb-hero) { 47 | position: relative; 48 | z-index: 1; 49 | } 50 | 51 | /* Hide while editing performer */ 52 | #performer-page .detail-header.edit.full-width .pb-hero { 53 | display: none; 54 | } 55 | -------------------------------------------------------------------------------- /plugins/cjCardTweaks/README.md: -------------------------------------------------------------------------------- 1 | # CJ's Card Tweaks 2 | 3 | https://discourse.stashapp.cc/t/cjs-card-tweaks/1342 4 | 5 | This plugin contains the various tweaks I've made to my Stash cards for anyone who may be interested. Each tweak will be toggleable, so users have the option to subscribe to some without subscribing to all. 6 | 7 | ## Tweaks 8 | 9 | ### File Count on Cards 10 | ![Screenshot 2024-07-24 173921](https://github.com/user-attachments/assets/8eaf0dce-a6c2-4d92-aa78-7ddc2322392a) 11 | 12 | Scenes, Galleries, or Images with a file count greater than one will have a badge similar to the badge present in the file count tab when more than one file is present. This badge will be located at the top right on the card where the studio logo used to live. ATM, the CSS that relocates the studio logo to the left of the title card, is not included as a toggleable tweak, but I plan to extract that out of my SCSS theme project and incorporate it here as a toggleable tweak. Until then, users who aren't using any other plugins that reposition the studio logo can tweak the CSS to reposition the file count to a new location. 13 | 14 | ### 3D rating banner 15 | ![Screenshot 2024-07-29 131937](https://github.com/user-attachments/assets/64d03cd7-6e31-4373-b831-e99a942216cf) 16 | 17 | Adds an additional dimension to the rating banners. 18 | 19 | ### Performer profile cards 20 | ![unnamed_2](https://github.com/user-attachments/assets/f505417d-ed0c-40c4-9c78-647081a41307) 21 | 22 | Modify the performer cards to use a traditional profile design 23 | -------------------------------------------------------------------------------- /plugins/TPDBMarkers/TPDBMarkers.yml: -------------------------------------------------------------------------------- 1 | name: The Porn DB Markers 2 | description: Sync Markers from The Porn DB aka theporndb.net 3 | version: 0.4.3 4 | url: https://discourse.stashapp.cc/t/the-porn-db-markers/1335 5 | exec: 6 | - python 7 | - "{pluginDir}/tpdbMarkers.py" 8 | interface: raw 9 | 10 | hooks: 11 | - name: Add Marker to Scene 12 | description: Makes Markers checking against The Porn DB aka theporndb.net 13 | triggeredBy: 14 | - Scene.Update.Post 15 | 16 | settings: 17 | disableSceneMarkerHook: 18 | displayName: Disable the Scene Markers hook 19 | type: BOOLEAN 20 | createMovieFromScene: 21 | displayName: Create Group from scene 22 | description: If there is a Group linked to the scene in the timestamp.trade database automatically create a group in stash with that info 23 | type: BOOLEAN 24 | runOnScenesWithMarkers: 25 | displayName: Run on scenes that already have markers. 26 | type: BOOLEAN 27 | addTPDBMarkerTag: 28 | displayName: Add the [TPDBMarker] tag to created markers. 29 | type: BOOLEAN 30 | addTPDBMarkerTitle: 31 | displayName: Add "[TPDBMarker]" to the start of generated marker titles. 32 | type: BOOLEAN 33 | overwriteMarkers: 34 | displayName: Overwrite Markers 35 | type: BOOLEAN 36 | mergeMarkers: 37 | displayName: Merge Markers 38 | type: BOOLEAN 39 | tasks: 40 | - name: "Sync" 41 | description: Get markers for all scenes with a stashid from theporndb.net and no markers 42 | defaultArgs: 43 | mode: processScene 44 | -------------------------------------------------------------------------------- /plugins/LocalVisage/stashface/utils/vtt_parser.py: -------------------------------------------------------------------------------- 1 | from typing import List, Tuple, Generator 2 | 3 | def parse_vtt_offsets(vtt_content: bytes) -> Generator[Tuple[int, int, int, int, float], None, None]: 4 | """ 5 | Parse VTT file content and extract offsets and timestamps. 6 | 7 | Parameters: 8 | vtt_content: Raw VTT file content as bytes 9 | 10 | Returns: 11 | Generator yielding tuples of (left, top, right, bottom, time_seconds) 12 | """ 13 | time_seconds = 0 14 | left = top = right = bottom = None 15 | 16 | for line in vtt_content.decode("utf-8").split("\n"): 17 | line = line.strip() 18 | 19 | if "-->" in line: 20 | # grab the start time 21 | # 00:00:00.000 --> 00:00:41.000 22 | start = line.split("-->")[0].strip().split(":") 23 | # convert to seconds 24 | time_seconds = ( 25 | int(start[0]) * 3600 26 | + int(start[1]) * 60 27 | + float(start[2]) 28 | ) 29 | left = top = right = bottom = None 30 | elif "xywh=" in line: 31 | left, top, right, bottom = line.split("xywh=")[-1].split(",") 32 | left, top, right, bottom = ( 33 | int(left), 34 | int(top), 35 | int(right), 36 | int(bottom), 37 | ) 38 | else: 39 | continue 40 | 41 | if not left: 42 | continue 43 | 44 | yield left, top, right, bottom, time_seconds -------------------------------------------------------------------------------- /plugins/additionalFilesDeleter/README.md: -------------------------------------------------------------------------------- 1 | # Addtional Files Deleter 2 | 3 | https://discourse.stashapp.cc/t/addtional-files-deleter/1337 4 | 5 | This is a plugin that will scan your Stash for either scenes or images where the file count is above 1. It will then skip over the primary file for each scene or image object and delete these extra files. Usually scene that contain multiple files are identical Phash matches (Unless you have manually merged unidentical Phashed files). Image objects that contain multiple files are grouped together under identical checksums, not Phashes. (You can't manually merge images as of yet.) 6 | 7 | ## Usage 8 | 9 | Copy repository into Stash plugins folder or add via the new plugins system and refresh your plugins from the Settings. 10 | 11 | If on first run you may want to run the Create Tag task, which creates an ignore tag that you can apply to Scenes or Images, so that they are bypassed when any of the other tasks are run. 12 | 13 | Other than Create Tag task you can run the following tasks. 14 | 15 | Images - Delete 16 | Images - Delete & Record 17 | Scenes - Delete 18 | Scenes - Delete & Record 19 | 20 | Tasks that just specify delete will just delete addtional files from their respective objects and Delete & Record will take the file paths of the files to be deleted, prefix them with "File: " (For latter easy searching) and it will append them to the current list of urls the object has and update the object. This is just a precaution to record perhaps usefull metadata an additional file path may hold for later use. 21 | -------------------------------------------------------------------------------- /plugins/imageGalleryNavigation/README.md: -------------------------------------------------------------------------------- 1 | # Image Gallery Navigation 2 | 3 | https://discourse.stashapp.cc/t/imagegallerynavigation/1857 4 | 5 | This plugin adds features for navigating between images within a Gallery from the Image details page. This is intended to make it easier to edit metadata on each Image in a Gallery one at a time without constantly having to go back and forth between the Gallery and Image page. 6 | 7 | This plugin currently adds the following to the Image details page: 8 | - A line above the tabs in the left panel that indicates the current page and total number of pages in the current Gallery. The current image number can be changed to jump to a specific image within the Gallery. 9 | - Buttons along the left/right side of the main Image display panel that allow moving to the previous/next image in the Gallery. 10 | - Basic zoom (mouse wheel) and pan (click and drag) functionality (can be enabled/disabled in plugin settings). Zooming out to original scale will also reset the image position. 11 | 12 | In the case of Images that are in multiple Galleries, the Gallery being navigated is set by accessing an Image from the Gallery you want to navigate. When navigating to an Image from a Gallery, the sorting/filtering currently on the Gallery view will also be applied to the Gallery navigation on the Image page. 13 | 14 | If you navigate directly to an Image (not through a Gallery), the first Gallery the Image belongs to will be used as the basis for navigation, and navigation will default to sorting by title with no filtering. 15 | -------------------------------------------------------------------------------- /plugins/AudioPlayerLite/AudioPlayerLite.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | "use strict"; 3 | const PluginApi = window.PluginApi; 4 | const React = PluginApi.React; 5 | 6 | function setStyle() { 7 | if(!document.querySelector(".VideoPlayer .video-wrapper") || !document.querySelector(".vjs-poster")) { 8 | window.setTimeout(setStyle, 100) 9 | } else { 10 | document.querySelector(".VideoPlayer .video-wrapper").before(document.querySelector(".vjs-poster")) 11 | document.querySelector(".VideoPlayer").classList.add("audio") 12 | } 13 | } 14 | 15 | PluginApi.Event.addEventListener("stash:location", async (e) => { 16 | const path = e.detail.data.location.pathname; 17 | const idRegExp = /.*\/scenes\/(\d+)/; 18 | if (idRegExp.test(path)) { 19 | await PluginApi.utils.loadComponents([ 20 | PluginApi.loadableComponents.ScenePlayer 21 | ]); 22 | PluginApi.patch.instead("ScenePlayer", function (props, _, originalComponent) { 23 | const file = props.scene.files[0]; 24 | let scene = props.scene; 25 | if (file.video_codec === "") { 26 | scene = { ...scene, 27 | sceneStreams: props.scene.sceneStreams.filter((ss) => ss.label.toUpperCase() === 'HSL') 28 | }; 29 | poster() 30 | } 31 | return originalComponent({ ...props, scene }); 32 | }); 33 | } 34 | }); 35 | })(); 36 | -------------------------------------------------------------------------------- /plugins/AIOverhaul/AIOverhaul.yml: -------------------------------------------------------------------------------- 1 | name: AIOverhaul 2 | description: AI Overhaul for Stash with a full plugin engine included to install and manage asynchronous stash plugins for AI or other purposes. 3 | version: 0.9.1 4 | url: https://discourse.stashapp.cc/t/aioverhaul/4847 5 | ui: 6 | javascript: 7 | - VersionInfo.js 8 | - BackendBase.js 9 | - BackendHealth.js 10 | - PageContext.js 11 | - RecommendationUtils.js 12 | - AIButton.js 13 | - TaskDashboard.js 14 | - PluginSettings.js # ensure settings component registers before integration 15 | - RecommendedScenes.js 16 | - SimilarScenes.js 17 | - SimilarTabIntegration.js 18 | - InteractionTracker.js 19 | - AIButtonIntegration.js # integration last after components 20 | css: 21 | - css/AIOverhaul.css 22 | - css/recommendedscenes.css 23 | - css/SimilarScenes.css 24 | csp: 25 | connect-src: 26 | - http://localhost:4153 27 | - ws://localhost:4153 28 | - https://localhost:4153 29 | # Add additional urls here for the stash-ai-server if your browser is not on the same host 30 | interface: raw 31 | exec: 32 | - python 33 | - "{pluginDir}/plugin_setup.py" 34 | tasks: 35 | - name: Setup AI Overhaul Plugin settings 36 | description: Use to set automatically set AI Overhaul Plugin settings 37 | defaultArgs: 38 | mode: plugin_setup 39 | settings: 40 | backend_base_url: 41 | displayName: Backend Base URL Override 42 | type: STRING 43 | capture_events: 44 | displayName: Capture Interaction Events 45 | type: BOOLEAN -------------------------------------------------------------------------------- /plugins/additionalFilesDeleter/deleter.yml: -------------------------------------------------------------------------------- 1 | name: Addtional Files Deleter 2 | description: Deletes addtional files assosiated with an image or scene object. Which will usually have identical PHashes for scenes or Checksum for images. Unless is a scene manually merged. Apply ignore tag to scene/image object for plugin to bypass. 3 | version: 0.1 4 | url: https://discourse.stashapp.cc/t/addtional-files-deleter/1337 5 | exec: 6 | - python 7 | - "{pluginDir}/deleter.py" 8 | interface: raw 9 | tasks: 10 | - name: Create Tag 11 | description: Create the plugin Ignore Tag 12 | defaultArgs: 13 | mode: create_tag 14 | - name: Remove Tag 15 | description: Remove the plugin Ignore Tag 16 | defaultArgs: 17 | mode: remove_tag 18 | - name: Images - Delete 19 | description: Image objects that contain addtional files will be deleted 20 | defaultArgs: 21 | mode: images_delete 22 | - name: Images - Delete & Record 23 | description: Addtional files will be deleted & old paths will be stored in Image object URLs field (Incase they contain future needed metadata) 24 | defaultArgs: 25 | mode: images_delete_record_paths 26 | - name: Scenes - Delete 27 | description: Scene objects that contain addtional files will be deleted 28 | defaultArgs: 29 | mode: scenes_delete 30 | - name: Scenes - Delete & Record 31 | description: Addtional files will be deleted & old paths will be stored in Scene object URLs field (Incase they contain future needed metadata) 32 | defaultArgs: 33 | mode: scenes_delete_record_paths 34 | -------------------------------------------------------------------------------- /plugins/scenePageRememberStates/scenePageRememberStates.js: -------------------------------------------------------------------------------- 1 | function sceneDivider(tabs) { 2 | const dividerBtn = document.querySelector(".scene-divider > button"); 3 | 4 | // check if tab is currently collapsed, and if it should be 5 | const isCollapsed = () => tabs.classList.contains("collapsed"); 6 | const storedCollapse = 7 | localStorage.getItem("remember-state-divider") == "true"; 8 | // if it should be, but is not, collapse 9 | if (storedCollapse && !isCollapsed()) dividerBtn.click(); 10 | 11 | // add listener to change desired state based on current collapsed state 12 | dividerBtn.addEventListener("click", () => { 13 | // isCollapsed does not update in time 14 | const newState = !isCollapsed(); 15 | localStorage.setItem("remember-state-divider", newState); 16 | }); 17 | } 18 | 19 | function navTab() { 20 | const detailsNav = document.querySelector(".nav-tabs"); 21 | 22 | // Check local storage for entries 23 | let activeKey = localStorage.getItem("remember-state-navtab"); 24 | 25 | // click on desired active key if defined 26 | if (activeKey) { 27 | detailsNav.querySelector(`a[data-rb-event-key="${activeKey}"]`).click(); 28 | } 29 | 30 | // add event listener 31 | detailsNav.querySelectorAll("a").forEach((href) => { 32 | href.addEventListener("click", function () { 33 | localStorage.setItem("remember-state-navtab", this.dataset.rbEventKey); 34 | }); 35 | }); 36 | } 37 | 38 | csLib.PathElementListener("/scenes/", ".nav-tabs", navTab); 39 | csLib.PathElementListener("/scenes/", ".scene-tabs", sceneDivider); 40 | -------------------------------------------------------------------------------- /themes/Theme-Minimal/studio-performer-tagger.css: -------------------------------------------------------------------------------- 1 | .StudioTagger, 2 | .PerformerTagger { 3 | .StudioTagger-studio, 4 | .PerformerTagger-performer { 5 | background-color: unset; 6 | .studio-card, 7 | .performer-card { 8 | border-radius: 12px; 9 | img { 10 | background-color: unset; 11 | } 12 | } 13 | .StudioTagger-details, 14 | .PerformerTagger-details { 15 | margin-top: 1rem; 16 | 17 | .StudioTagger-header, 18 | .PerformerTagger-header { 19 | h2 { 20 | font-size: var(--text-xl); 21 | } 22 | } 23 | 24 | .text-left h5 { 25 | color: var(--primary-11); 26 | font-weight: unset; 27 | font-size: var(--text-xs); 28 | } 29 | 30 | .PerformerTagger-performer-search { 31 | display: grid; 32 | grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr)); 33 | gap: 24px; 34 | button.PerformerTagger-performer-search-item { 35 | flex: unset; 36 | max-width: unset; 37 | width: min-content; 38 | border-radius: 8px; 39 | 40 | padding: 12px; 41 | align-items: flex-start; 42 | gap: 12px; 43 | 44 | img.PerformerTagger-thumb { 45 | height: 80px; 46 | border-radius: 6px; 47 | margin-right: 0; 48 | } 49 | 50 | span { 51 | white-space: nowrap; 52 | text-overflow: ellipsis; 53 | overflow: hidden; 54 | } 55 | } 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /plugins/bulkImageScrape/BulkImageScrape.yml: -------------------------------------------------------------------------------- 1 | name: Bulk Image Scrape 2 | # requires: PythonDepManager 3 | description: Apply an image scraper to all images 4 | version: 0.3.4 5 | url: https://discourse.stashapp.cc/t/bulk-image-scrape/1339 6 | exec: 7 | - python 8 | - "{pluginDir}/bulkImageScrape.py" 9 | interface: raw 10 | 11 | settings: 12 | ScraperID: 13 | displayName: The Scraper ID of the image scraper to use (for example SHALookup) 14 | type: STRING 15 | SkipTags: 16 | displayName: List of tags to skip (comma separated) 17 | type: STRING 18 | CreateMissingPerformers: 19 | displayName: Create missing performers from scrape result 20 | type: BOOLEAN 21 | CreateMissingStudios: 22 | displayName: Create missing studios from scrape result 23 | type: BOOLEAN 24 | CreateMissingMovies: 25 | displayName: Create missing movies/groups from scrape result 26 | type: BOOLEAN 27 | MergeExistingTags: 28 | displayName: Merge existing tags with the scraped tags (default is to overwrite) 29 | type: BOOLEAN 30 | ExcludeOrganized: 31 | displayName: Exclude images that are set as organized (default is to include) 32 | type: BOOLEAN 33 | SkipEntriesNum: 34 | displayName: number of entries to skip over (mostly for rerunning after an error on large collections) 35 | type: NUMBER 36 | PerformerIdsFilter: 37 | displayName: List of performers IDs (comma separated) to filter images the plugins sees 38 | type: STRING 39 | 40 | tasks: 41 | - name: "Bulk Image Scrape" 42 | description: Apply an image scraper to all images 43 | -------------------------------------------------------------------------------- /themes/Theme-Minimal/settings.css: -------------------------------------------------------------------------------- 1 | #tasks-panel .tasks-panel-queue { 2 | background-color: unset !important; 3 | position: unset !important; 4 | } 5 | 6 | .job-table.card { 7 | background-color: unset; 8 | } 9 | 10 | #settings-menu-container { 11 | padding-right: 16px; 12 | .nav-pills { 13 | gap: 4px; 14 | 15 | .nav-link { 16 | color: var(--primary-11) !important; 17 | background-color: unset; 18 | padding: 12px 16px; 19 | border: 0 !important; 20 | border-radius: 8px !important; 21 | line-height: 1; 22 | transition: all 0.2s ease-in-out; 23 | 24 | &:hover { 25 | background-color: var(--primary-4) !important; 26 | color: var(--primary-12) !important; 27 | } 28 | 29 | &.active { 30 | background-color: var(--primary-2) !important; 31 | color: var(--primary-12) !important; 32 | font-weight: 600; 33 | } 34 | } 35 | } 36 | } 37 | 38 | .package-manager table { 39 | & > thead { 40 | background-color: var(--primary-1); 41 | } 42 | } 43 | 44 | .setting-section { 45 | display: flex; 46 | flex-direction: column; 47 | gap: 8px; 48 | .card { 49 | display: flex; 50 | flex-direction: column; 51 | gap: 24px; 52 | margin: 0; 53 | } 54 | 55 | &:not(:first-child) { 56 | margin-top: 48px; 57 | } 58 | 59 | h1 { 60 | color: var(--primary-11); 61 | font-weight: normal; 62 | font-size: 1.2rem; 63 | } 64 | 65 | .setting { 66 | border-bottom-width: 0 !important; 67 | padding: 0; 68 | h3 { 69 | font-size: 1rem; 70 | margin-bottom: 0; 71 | } 72 | } 73 | 74 | .setting-group { 75 | border-bottom: 0 !important; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /plugins/PythonToolsInstaller/PythonToolsInstaller.py: -------------------------------------------------------------------------------- 1 | import shutil 2 | import sys, json, sysconfig 3 | from venv import create 4 | from os.path import abspath 5 | import subprocess 6 | import shutil 7 | 8 | def main(): 9 | input = None 10 | 11 | if len(sys.argv) < 2: 12 | input = readJSONInput() 13 | 14 | output = {} 15 | run(input, output) 16 | 17 | out = json.dumps(output) 18 | print(out + "\n") 19 | 20 | def readJSONInput(): 21 | input = sys.stdin.read() 22 | return json.loads(input) 23 | 24 | 25 | def run(input, output): 26 | 27 | if input == "None" or input == "": 28 | return 29 | 30 | 31 | PLUGIN_DIR = input["server_connection"]["PluginDir"] 32 | modeArg = input['args']["mode"] 33 | 34 | if modeArg == "" or modeArg == "add": 35 | return 36 | 37 | elif modeArg == "process_py_stashapi_tools": 38 | get_download_py_stashapp_tools(PLUGIN_DIR) 39 | 40 | output["output"] = "ok" 41 | 42 | def get_download_py_stashapp_tools(PLUGIN_DIR): 43 | 44 | org_packagedir = sysconfig.get_paths()["purelib"] # /usr/lib/python3.12/site-packages 45 | 46 | used_dir = f"{PLUGIN_DIR}" 47 | create(f"{used_dir}/venv/", with_pip=True) 48 | 49 | # where requirements.txt is in same dir as this script 50 | subprocess.run([f"{used_dir}/venv/bin/pip", "install", "-r", abspath(f"{used_dir}/packages/stashtools.txt")],stdout=None) 51 | 52 | # venv/lib/python3.11/site-packages/stashapp_tools- 53 | 54 | src = f"{used_dir}/venv/lib/python3.12/site-packages" 55 | destination = shutil.copytree(src, org_packagedir,ignore_func,None,shutil.copy2,False,True) 56 | fp = open(f'{used_dir}/copydo.txt', 'w+') 57 | fp.write("%s\n" % print(destination)) 58 | 59 | def ignore_func(src, names): 60 | return ['env'] if 'env' in names else [] 61 | 62 | 63 | main() 64 | -------------------------------------------------------------------------------- /plugins/themeSwitch/assets/snippets/tags/different-tag-cards.css: -------------------------------------------------------------------------------- 1 | /* Author: Qx#1573 */ 2 | /* [Tags changes] changes the layout of tag cards on tags page and hover */ 3 | .tag-parent-tags, 4 | .tag-sub-tags { 5 | display: none; 6 | } 7 | .tag-card.zoom-0.grid-card.card div.card-section div.card-popovers.btn-group { 8 | margin-top: 1em; 9 | } 10 | .tag-card.zoom-0.grid-card.card 11 | div.thumbnail-section 12 | a.tag-card-header 13 | img.tag-card-image { 14 | object-fit: cover; 15 | } 16 | .tag-card.zoom-0.grid-card.card div.card-section hr { 17 | display: none; 18 | } 19 | 20 | .tag-card.zoom-0.grid-card.card { 21 | padding: 0; 22 | width: 300px; 23 | height: 180px; 24 | } 25 | 26 | .tag-card.zoom-0.grid-card.card div.card-section { 27 | position: absolute; 28 | text-shadow: 2px 2px 2px #000; 29 | width: 100%; 30 | background-color: rgba(0, 0, 0, 0.3); 31 | height: 3em; 32 | overflow: hidden; 33 | transition: 0.8s ease-in-out; 34 | } 35 | 36 | .tag-card.zoom-0.grid-card.card div.card-section a { 37 | text-decoration: none; 38 | } 39 | 40 | .tag-card.zoom-0.grid-card.card div.card-section:hover { 41 | height: 22em; 42 | } 43 | 44 | .tag-card.zoom-0.grid-card.card 45 | div.card-section 46 | a 47 | h5.card-section-title.flex-aligned 48 | div.TruncatedText { 49 | white-space: nowrap; 50 | text-overflow: ellipsis; 51 | width: 300px; 52 | overflow: hidden; 53 | display: block; 54 | } 55 | 56 | .tag-card.zoom-0.grid-card.card 57 | div.card-section 58 | div.TruncatedText.tag-description { 59 | position: relative; 60 | top: 0.5em; 61 | -webkit-text-stroke-width: 1px; 62 | font-size: 16px; 63 | } 64 | 65 | .tag-card .card-popovers .btn { 66 | text-shadow: 1px 1px 1px #000; 67 | stroke: black; 68 | stroke-width: 15; 69 | } 70 | -------------------------------------------------------------------------------- /themes/Theme-Minimal/markers-wall.css: -------------------------------------------------------------------------------- 1 | .wall { 2 | display: grid; 3 | grid-template-columns: repeat(auto-fit, minmax(min(100%, 400px), 1fr)); 4 | 5 | gap: 12px; 6 | .wall-item { 7 | width: unset !important; 8 | min-width: unset !important; 9 | max-width: unset !important; 10 | height: unset !important; 11 | min-height: unset !important; 12 | max-height: unset !important; 13 | 14 | &::before { 15 | display: none; 16 | } 17 | 18 | &:hover { 19 | .wall-item-container { 20 | transform: unset; 21 | } 22 | } 23 | .wall-item-container { 24 | background-color: unset; 25 | width: unset !important; 26 | min-width: unset !important; 27 | max-width: unset !important; 28 | height: unset !important; 29 | min-height: unset !important; 30 | max-height: unset !important; 31 | 32 | .wall-item-anchor { 33 | width: 100%; 34 | 35 | .wall-item-media { 36 | height: unset; 37 | border-radius: 8px; 38 | aspect-ratio: 16/9; 39 | background-color: var(--primary-2); 40 | overflow: hidden; 41 | } 42 | 43 | .wall-item-text { 44 | display: block; 45 | position: unset; 46 | background: unset; 47 | 48 | margin-top: 4px; 49 | text-align: left; 50 | 51 | & > div { 52 | color: var(--primary-12); 53 | font-size: var(--text-sm); 54 | font-weight: 500; 55 | } 56 | 57 | .wall-tag { 58 | color: var(--primary-11); 59 | font-size: var(--text-2xs); 60 | margin: unset; 61 | &:not(:last-child)::after { 62 | content: ", "; 63 | } 64 | } 65 | } 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /userscripts/StashDB_Submission_Helper/README.md: -------------------------------------------------------------------------------- 1 | # StashDB Submission Helper 2 | 3 | https://discourse.stashapp.cc/t/stashdb-submission-helper/1417 4 | 5 | - Adds button to add all unmatched aliases to performer 6 | - Adds button to add all unmatched urls to performer 7 | - Adds button to add all unmatched measurements to performer (if they match expected formats) 8 | - Convert unmatched urls from regular strings to linked strings 9 | 10 | ## [**INSTALL USERSCRIPT**](https://raw.githubusercontent.com/stashapp/CommunityScripts/main/userscripts/StashDB_Submission_Helper/stashdb_submission_helper.user.js) 11 | 12 | Installation requires a browser extension such as [Violentmonkey](https://violentmonkey.github.io/) / [Tampermonkey](https://www.tampermonkey.net/) / [Greasemonkey](https://www.greasespot.net/). 13 | 14 | ### Screenshot 15 | ![script preview](https://user-images.githubusercontent.com/1358708/178110989-3bc33371-e3bb-4064-8851-a9356b5a4882.png) 16 | 17 | ### Demo GIF: 18 | ![demo gif](https://monosnap.com/image/p4pkcqrKWYp3V5quHl5LWOAZUG3oAP) 19 | 20 | ## Changelog 21 | 22 | ### 0.7 23 | - Allow alias separator to also be `/` or ` or ` (space on either side of the or). 24 | - Allow measurements to be added without the cup size 25 | - Support full current list of sites for adding URLS (previously only IAFD, DATA18, Indexxx, and Twitter were supported because I forgot to add the others) 26 | 27 | ### 0.6 28 | - Add input field / button to performer edit pages to add a comma separated list of aliases to a performer 29 | ![alias input](https://user-images.githubusercontent.com/1358708/179358258-89385345-36ed-42ea-8b71-4f7e84d3a253.png) 30 | - Cleaned up code so that it doesn't run on non-performer drafts 31 | - Added performer add and edit pages to the pages it runs on (since alias function isn't just draft related) 32 | 33 | ### 0.5 34 | Public Release 35 | -------------------------------------------------------------------------------- /plugins/chooseYourAdventurePlayer/Sample/1234/choices/2.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "2", 3 | "type": null, 4 | "title": "Action choice", 5 | "fragments": [ 6 | { 7 | "id": "frag-a", 8 | "photo": { 9 | "id": "frag-a_photo", 10 | "content": "frag-a.jpg" 11 | }, 12 | "video": { 13 | "id": "frag-a_video", 14 | "resolved_content": "Scene2A.mp4" 15 | }, 16 | "actions": [ 17 | { 18 | "id": "frag-a-action-a", 19 | "photo": { 20 | "id": "frag-a-action-a_photo", 21 | "content": "frag-a-action-a.jpg" 22 | }, 23 | "video": { 24 | "id": "frag-a-action-a_video", 25 | "resolved_content": "Scene2A-1.mp4" 26 | }, 27 | "title": "TBD" 28 | }, 29 | { 30 | "id": "frag-a-action-b", 31 | "photo": { 32 | "id": "frag-a-action-b_photo", 33 | "content": "frag-a-action-b.jpg" 34 | }, 35 | "video": { 36 | "id": "frag-a-action-b_video", 37 | "resolved_content": "Scene2A-1.mp4" 38 | }, 39 | "title": "TBD" 40 | } 41 | ], 42 | "title": "TBD" 43 | }, 44 | { 45 | "id": "frag-b", 46 | "photo": { 47 | "id": "frag-b_photo", 48 | "content": "frag-b.jpg" 49 | }, 50 | "video": { 51 | "id": "frag-b_video", 52 | "resolved_content": "Scene2B.mp4" 53 | }, 54 | "actions": null, 55 | "title": "TBA", 56 | "subtitles": "Scene3-08" 57 | } 58 | ], 59 | "skipto": null, 60 | "choices": [ 61 | { 62 | "description": null, 63 | "id": "2-e", 64 | "type": "end", 65 | "photo": "TBD" 66 | } 67 | ], 68 | "resource": { 69 | "id": "empty", 70 | "resolved_content": null 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /plugins/hotCards/utils/stashHandler.js: -------------------------------------------------------------------------------- 1 | const stash = { 2 | galleries: {}, 3 | images: {}, 4 | groups: {}, 5 | performers: {}, 6 | scenes: {}, 7 | studios: {}, 8 | }; 9 | 10 | stashListener.addEventListener("response", (event) => { 11 | const dataProcessors = { 12 | galleries: processData("findGalleries", "galleries"), 13 | images: processData("findImages", "images"), 14 | groups: processData("findGroups", "groups"), 15 | performers: processData("findPerformers", "performers"), 16 | scenes: processData("findScenes", "scenes"), 17 | studios: processData("findStudios", "studios"), 18 | }; 19 | 20 | for (const key in dataProcessors) { 21 | dataProcessors[key](event.detail); 22 | } 23 | 24 | processOtherData(event.detail); 25 | }); 26 | 27 | function processData(findKey, dataKey) { 28 | return function (data) { 29 | if (data.data[findKey]?.[dataKey]) { 30 | for (const item of data.data[findKey][dataKey]) { 31 | stash[dataKey][item.id] = item; 32 | } 33 | } 34 | }; 35 | } 36 | 37 | function processOtherData(data) { 38 | const otherDataMappings = [ 39 | { findKey: "findScene", key: "groups", nested: true }, 40 | { findKey: "findScene", key: "galleries", nested: false }, 41 | { findKey: "findScene", key: "performers", nested: false }, 42 | { findKey: "findImage", key: "performers", nested: false }, 43 | { findKey: "findGallery", key: "performers", nested: false }, 44 | { findKey: "findGallery", key: "scenes", nested: false }, 45 | ]; 46 | 47 | for (const mapping of otherDataMappings) { 48 | if (data.data[mapping.findKey]?.[mapping.key]) { 49 | for (const item of data.data[mapping.findKey][mapping.key]) { 50 | const value = mapping.nested ? item[mapping.key.slice(0, -1)] : item; 51 | stash[mapping.key][value.id] = value; 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CommunityScripts repository 2 | 3 | This repository contains plugins, themes, userscripts and other utility scripts created by the Stash community. 4 | 5 | ## Community support 6 | 7 | - **Forum:** [discourse.stashapp.cc](https://discourse.stashapp.cc) - Primary place for community support, feature requests, and discussions. 8 | - **Discord:** [discord.gg/2TsNFKt](https://discord.gg/2TsNFKt) - Real-time chat and community support. 9 | - **Lemmy:** [discuss.online/c/stashapp](https://discuss.online/c/stashapp) - Community discussions. 10 | 11 | ## Host your own plugins 12 | 13 | We have a GitHub template available for those that prefer hosting on their own with step-by-step instructions to get started. 14 | 15 | Repository: https://github.com/stashapp/plugins-repo-template 16 | 17 | ## Plugins 18 | 19 | > [!NOTE] 20 | > More plugins are available at [discourse.stashapp.cc](https://discourse.stashapp.cc/c/plugins/18). 21 | 22 | ## Themes 23 | 24 | > [!NOTE] 25 | > More themes are available at [discourse.stashapp.cc](https://discourse.stashapp.cc/tags/c/plugins/18/none/theme). 26 | 27 | ## Other projects 28 | 29 | > [!NOTE] 30 | > More community projects are available at [discourse.stashapp.cc](https://discourse.stashapp.cc/c/plugins/other-projects/15). 31 | 32 | ## Contributing 33 | 34 | Submit a PR to add your plugin, theme, userscript and other utility script to the repository. 35 | 36 | ## Deprecation 37 | 38 | 14-day notice and associated issue with the appropriate label will be created once plugins, themes, userscripts and other utility scripts are marked for deprecation to allow time for someone to take over the development. 39 | 40 | After the 14 days, if nobody takes up the development they will be moved to the [archive](./archive) directory. This will automatically remove plugins from the CommunityScripts source index. 41 | 42 | Maintainers are expected to show commitment and demonstrate meaningful effort to maintain the code. 43 | -------------------------------------------------------------------------------- /archive/stashRealbooru/stash-realbooru.css: -------------------------------------------------------------------------------- 1 | button.svelte-1m5gxnd { 2 | background-color: var(--nav-color); 3 | border: 0px; 4 | } 5 | .scanner.svelte-1m5gxnd { 6 | animation: svelte-1m5gxnd-pulse 2s infinite; 7 | } 8 | @keyframes svelte-1m5gxnd-pulse { 9 | 0% { 10 | transform: scale(0.95); 11 | box-shadow: 0 0 0 0 var(--light); 12 | } 13 | 70% { 14 | transform: scale(1.1); 15 | box-shadow: 0 0 0 10px var(--info); 16 | } 17 | 100% { 18 | transform: scale(0.95); 19 | box-shadow: 0 0 0 0 var(--primary); 20 | } 21 | } 22 | svg.svelte-1m5gxnd { 23 | fill: #ffffff; 24 | } 25 | .top-accent.svelte-9viihb { 26 | border-top: 10px solid var(--primary); 27 | border-radius: 3px; 28 | } 29 | .modal-header.svelte-9viihb { 30 | font-size: 2.4rem; 31 | border-bottom: 0px; 32 | padding: 20px; 33 | } 34 | .modal-footer.svelte-9viihb { 35 | border-top: 0px; 36 | } 37 | .lds-dual-ring.svelte-9viihb { 38 | display: inline-block; 39 | width: 16px; 40 | height: 16px; 41 | } 42 | .lds-dual-ring.svelte-9viihb:after { 43 | content: " "; 44 | display: block; 45 | width: 12px; 46 | height: 12px; 47 | margin: 3px; 48 | border-radius: 50%; 49 | border: 6px solid #fff; 50 | border-color: #fff transparent #fff transparent; 51 | animation: svelte-9viihb-lds-dual-ring 1.2s linear infinite; 52 | } 53 | @keyframes svelte-9viihb-lds-dual-ring { 54 | 0% { 55 | transform: rotate(0deg); 56 | } 57 | 100% { 58 | transform: rotate(360deg); 59 | } 60 | } 61 | .tag.svelte-ai57u1 { 62 | padding: 6px; 63 | } 64 | .tag-item.svelte-ai57u1 { 65 | background-color: var(--card-color); 66 | width: 100%; 67 | padding: 5px; 68 | margin: 0px; 69 | } 70 | .tag-item-select.svelte-ai57u1 { 71 | border: none; 72 | outline: none; 73 | scroll-behavior: smooth; 74 | } 75 | .tag-item-reject.svelte-ai57u1:hover { 76 | fill: #a82c2c; 77 | transition: fill 0.2s ease-out; 78 | } 79 | svg.svelte-ai57u1 { 80 | fill: #ffffff; 81 | } 82 | -------------------------------------------------------------------------------- /scripts/stash-watcher/defaults.toml: -------------------------------------------------------------------------------- 1 | #This is the information about your stash instance 2 | [Host] 3 | #The scheme (either http or https) 4 | Scheme = http 5 | #The full hostname for your stash instance. If you're running in docker you might want the 6 | #service name and not localhost here. 7 | Host = localhost 8 | #The port number for your stash instance 9 | Port = 9999 10 | #The api key, if your stash instance is password protected 11 | ApiKey = 12 | 13 | #Configuration for the listener itself 14 | [Config] 15 | #A comma separated list of paths to watch. 16 | Paths = /data 17 | #The minimum time to wait between triggering scans 18 | Cooldown = 300 19 | #A list of file extensions to watch. If this is omitted, it uses the extensions that are defined 20 | #in your Stash library (for videos, images, and galleries) 21 | Extensions = 22 | #If this is set to a non-zero numeric value, this forces the use of polling to 23 | #determine file system changes. If it is left blank, then the OS appropriate 24 | #mechanism is used. This is much less efficient than the OS mechanism, so it 25 | #should be used with care. The docs claim that this is required to watch SMB 26 | #shares, though in my testing I could watch them on Windows with the regular 27 | #WindowsApiObserver 28 | PollInterval= 29 | #This enables debug logging 30 | Debug= 31 | 32 | #Options for the Stash Scan. Stash defaults to everything disabled, so this is the default 33 | #Generate options that match up with what we can do in Scan 34 | [ScanOptions] 35 | #"Generate scene covers" from the UI 36 | Covers=true 37 | #"Generate previews" from the UI 38 | Previews=true 39 | #"Generate animated image previews" from the UI 40 | ImagePreviews=false 41 | #"Generate scrubber sprites" from the UI 42 | Sprites=false 43 | #"Generate perceptual hashes" from the UI 44 | Phashes=true 45 | #"Generate thumbnails for images" from the UI 46 | Thumbnails=true 47 | #"Generate previews for image clips" from the UI 48 | ClipPreviews=false 49 | -------------------------------------------------------------------------------- /plugins/stashdb-performer-gallery/stashdb-performer-gallery.yml: -------------------------------------------------------------------------------- 1 | name: stashdb performer gallery 2 | description: Automatically download performer images from stashdb or other stash-boxes. Add the [Stashbox Performer Gallery] tag to a performer and it will create a gallery of images from that stash-box database. Apply the tag [Set Profile Image] to an image to set it as the profile image of that performer. Note you will need to configure the download path and add this as a path under settings > library 3 | version: 0.2 4 | url: https://discourse.stashapp.cc/t/stashdb-performer-gallery/1411 5 | exec: 6 | - python 7 | - "{pluginDir}/stashdb-performer-gallery.py" 8 | interface: raw 9 | settings: 10 | path: 11 | displayName: Download parent folder 12 | description: Download location for files, note this should be in a different folder to stash and in a folder covered by stash. You may need to create a new library path to cover this directory. 13 | type: STRING 14 | runPerformerScraper: 15 | displayName: Run stash scrapers on profile urls 16 | description: Run scrapers on profile urls 17 | type: BOOLEAN 18 | 19 | hooks: 20 | - name: modify performer 21 | description: Download galleries on performer update if the [performer gallery] tag is applied 22 | triggeredBy: 23 | - Performer.Update.Post 24 | - name: image add 25 | description: Add images 26 | triggeredBy: 27 | - Image.Create.Post 28 | - name: set profile images 29 | description: Set profile images py adding the [Set Profile Image] tag to the image, there must be exactly 1 performer tagged on the image. 30 | triggeredBy: 31 | - Image.Update.Post 32 | 33 | tasks: 34 | - name: "Process Performers" 35 | description: Fetch performer images for performers with the [performer gallery] tag 36 | defaultArgs: 37 | mode: processPerformers 38 | - name: "relink missing images" 39 | description: reprocess missing images 40 | defaultArgs: 41 | mode: processImages 42 | -------------------------------------------------------------------------------- /plugins/PlexSync/README.md: -------------------------------------------------------------------------------- 1 | # Stash Plugin updating your Plex metadata automatically 2 | 3 | https://discourse.stashapp.cc/t/plex-sync/1926 4 | 5 | This plugin solves the problem of "I have many files in my Plex, but they don't get any of the changes I do in Stash, and doing a `refresh all metadata` takes too much time". 6 | 7 | With this, Stash behaves as the main source for all your Stash scenes in Plex, and it keeps Plex in sync with changes done via Stash. 8 | 9 | 10 | 11 | # Install 12 | 13 | Install the plugin in Stash first, and then install the updated [Stash-Plex-Agent](https://github.com/Darklyter/StashPlexAgent.bundle) on your Plex server. 14 | 15 | ## Stash side 16 | 17 | 1. Install plugin via the CommunityScripts repository. 18 | 19 | 2. Go to the install directory and install requirements: `python3 -m pip install -r requirements.txt -t .` -- or, install the required packages globally `python3 -m pip install stashapi unidecode requests` 20 | 21 | 3. Configure the plugin in Stash UI. 22 | 23 | ## Plex side 24 | 25 | Do this *after* making sure the Stash side is complete. 26 | 27 | 1. After installing the newest version of this agent, make sure that `AddPlexURL` is enabled ("Adds the Plex media ID to the scene in Stash; allows Stash to update Plex metadata.") 28 | 29 | 2. Refresh all metadata in Plex for the libraries using this agent. 30 | 31 | Now, you should see scenes being updated in Stash, adding this URL to the scenes: `plex/library/metadata/12345` (12345 being the metadata ID of the scene in Plex) 32 | 33 | # Usage 34 | 35 | Update your scenes in Stash like normal, and these scenes will be automatically refreshed in Plex. 🎉 36 | 37 | # Warnings 38 | - If you have the "clean titles" option enabled in plugin, all titles are processed with `unidecode`. Basically, all non-ASCII characters in your titles will be converted; Cyrillic script for example will be taken away. 39 | 40 | - This plugin connects to your Plex via TLS, but it ignores cert errors. But this is not really a problem, as your Stash is most likely on the same host as your Plex... 41 | -------------------------------------------------------------------------------- /plugins/untagRedundantTags/README.md: -------------------------------------------------------------------------------- 1 | # Untag redundant tags 2 | 3 | Removes parent tags from objects if a more specific child tag is also present. 4 | 5 | ## Install 6 | 7 | After you installed the plugin, make sure you have the latest version of stashapi installed by running `pip install -r /plugins/community/untagRedundantTags/requirements.txt`. 8 | 9 | ## Config 10 | 11 | A few config settings control what kind of parent tags are allowed for removal if redundant. The defaults are the behavior being disabled for all objects and no tags are allowed to be removed. 12 | 13 | Go into your Stash then under `Settings > Plugins` you'll find the config for Untag redundant tags 14 | 15 | - `Enable for scenes` 16 | 17 | > Enable the automatic tag removal behavior for scenes 18 | 19 | - `Enable for images` 20 | 21 | > Enable the automatic tag removal behavior for images 22 | 23 | - `Enable for galleries` 24 | 25 | > Enable the automatic tag removal behavior for galleries 26 | 27 | - `Enable for performers` 28 | 29 | > Enable the automatic tag removal behavior for performers 30 | 31 | - `Exclude objects marked as organized` 32 | 33 | > Disable automatic tag removal for items (scens / images / galleries) that are marked as organized 34 | 35 | - `Allow removing all root tags (tags without parent)` 36 | 37 | > If enabled, tags without a parent tag (root tags) are allowed to be removed. Useful if tag hierarchies are used to group thematically related tags, to prevent the group itself from being used. 38 | 39 | - `Allow removing tags ignored for Auto-Tagging` 40 | 41 | > If enabled, tags marked as "Ignore for Auto-Tag" are allowed to be removed. Useful for deeper tag hierarchies, gives selective control on a likely related attribute. 42 | 43 | - `Allow removing all intermediate tags (tags with parent and child tag)` 44 | 45 | > If enabled, all tags with a parent tag and a child tag are allowed to be removed. Useful for deeper tag hierarchies, but quite aggressive, will possibly remove parallel legitimate uses of less specialized and more spezialized tags. 46 | -------------------------------------------------------------------------------- /userscripts/FansDB_Submission_Helper/README.md: -------------------------------------------------------------------------------- 1 | # FansDB Submission Helper 2 | 3 | https://discourse.stashapp.cc/t/fansdb-submission-helper/1418 4 | 5 | > **NOTE:** Only change from [StashDB_Submission_Helper](https://github.com/stashapp/CommunityScripts/tree/main/userscripts/StashDB_Submission_Helper) is changed `@match` to fansdb.cc domain and added support for FansDB sites to match performer URLs. 6 | 7 | - Adds button to add all unmatched aliases to performer 8 | - Adds button to add all unmatched urls to performer 9 | - Adds button to add all unmatched measurements to performer (if they match expected formats) 10 | - Convert unmatched urls from regular strings to linked strings 11 | 12 | ## [**INSTALL USERSCRIPT**](https://raw.githubusercontent.com/stashapp/CommunityScripts/main/userscripts/FansDB_Submission_Helper/fansdb_submission_helper.user.js) 13 | 14 | Installation requires a browser extension such as [Violentmonkey](https://violentmonkey.github.io/) / [Tampermonkey](https://www.tampermonkey.net/) / [Greasemonkey](https://www.greasespot.net/). 15 | 16 | ### Screenshot 17 | ![script preview](https://user-images.githubusercontent.com/1358708/178110989-3bc33371-e3bb-4064-8851-a9356b5a4882.png) 18 | 19 | ### Demo GIF: 20 | ![demo gif](https://monosnap.com/image/p4pkcqrKWYp3V5quHl5LWOAZUG3oAP) 21 | 22 | ## Changelog 23 | 24 | ### 0.7 25 | - Allow alias separator to also be `/` or ` or ` (space on either side of the or). 26 | - Allow measurements to be added without the cup size 27 | - Support full current list of sites for adding URLS (previously only IAFD, DATA18, Indexxx, and Twitter were supported because I forgot to add the others) 28 | 29 | ### 0.6 30 | - Add input field / button to performer edit pages to add a comma separated list of aliases to a performer 31 | ![alias input](https://user-images.githubusercontent.com/1358708/179358258-89385345-36ed-42ea-8b71-4f7e84d3a253.png) 32 | - Cleaned up code so that it doesn't run on non-performer drafts 33 | - Added performer add and edit pages to the pages it runs on (since alias function isn't just draft related) 34 | 35 | ### 0.5 36 | Public Release 37 | -------------------------------------------------------------------------------- /themes/Theme-Plex/README.md: -------------------------------------------------------------------------------- 1 | # Plex 2 | 3 | https://discourse.stashapp.cc/t/plex/1426 4 | 5 | ![plex theme logo](https://user-images.githubusercontent.com/63812189/79496351-dddbf780-7fda-11ea-9e68-46d0eeb4e92f.png) 6 | 7 | ![plex theme preview](https://user-images.githubusercontent.com/1358708/178891502-c71e4278-0378-4154-91a6-07e1a8eaa1df.png) 8 | 9 | This is a community created theme for Stash inspired by the popular Plex Interface. Installation is quick and easy so you should be ready to install it in just a few simple steps. 10 | 11 | Feel free to experiment with CSS and modify it to fit your needs. In case you have any issues or improvements we will be happy to hear from you on our [Discord server](https://discord.gg/2TsNFKt)! You can also submit a PR to share improvements with others! 12 | 13 | The Plex Theme will only change the look and feel of the Stash interface. It will not affect any other data, so you are all safe and sound! :heart: 14 | 15 | ## Install 16 | 17 | 1. Open User Interface Configuration panel in settings. (http://localhost:9999/settings?tab=plugins) 18 | 19 | 2. Find the Theme in the listing, in the default Community repo 20 | 21 | 3. Click Install 22 | 23 | ### Optional - Host Backgrounds Locally 24 | 25 | _These steps are optional, by default this theme uses the Github hosted image links._ 26 | 27 | 1. Download [background.png](https://user-images.githubusercontent.com/63812189/79506691-4af78900-7feb-11ea-883e-87b8e05ceb1c.png) and [noise.png](https://user-images.githubusercontent.com/63812189/79506696-4c28b600-7feb-11ea-8176-12a46454d87a.png) 28 | 29 | 2. Place `background.png` and `noise.png` in `~/.stash` on macOS / Linux or `C:\Users\YourUsername\.stash` on Windows. Then edit the `background-image: url("")` attributes like below: 30 | 31 | The [body](https://github.com/stashapp/CommunityScripts/blob/main/themes/Theme-Plex/Theme-Plex.css#L7) one `background-image: url("./background.png");` 32 | 33 | The [root](https://github.com/stashapp/CommunityScripts/blob/main/themes/Theme-Plex/Theme-Plex.css#L18) one `background: rgba(0, 0, 0, 0) url("./noise.png") repeat scroll 0% 0%;` 34 | -------------------------------------------------------------------------------- /plugins/themeSwitch/assets/snippets/tags/alternative-tag-layout.css: -------------------------------------------------------------------------------- 1 | /*Tag layout changes - Author hijack_hornet */ 2 | .tag-card { 3 | width: 16rem; 4 | padding: 0; 5 | } 6 | 7 | .tag-card .card-section { 8 | height: 2.5rem; 9 | position: absolute; 10 | bottom: 0; 11 | left: 0; 12 | right: 0; 13 | background: #0000007a; 14 | line-height: none; 15 | } 16 | .tag-card .card-section .TruncatedText { 17 | -webkit-line-clamp: 1 !important; 18 | } 19 | .tag-card h1, 20 | h2, 21 | h3, 22 | h4, 23 | h5, 24 | h6, 25 | .h1, 26 | .h2, 27 | .h3, 28 | .h4, 29 | .h5, 30 | .h6 { 31 | line-height: normal; 32 | } 33 | .tag-card hr, 34 | .tag-description { 35 | display: none; 36 | } 37 | .tag-card .btn-group { 38 | position: absolute; 39 | width: 100%; 40 | bottom: 2.5rem; 41 | margin-bottom: 0; 42 | opacity: 0; 43 | transition: ease 0.2s; 44 | } 45 | .tag-card .btn-group:hover { 46 | opacity: 1; 47 | transition: ease 0.2s; 48 | background: #0000007a; 49 | } 50 | 51 | .tag-card-image { 52 | object-fit: cover; 53 | object-position: center; 54 | } 55 | 56 | .zoom-0 .tag-card-image { 57 | max-height: none; 58 | height: 16rem; 59 | width: 12rem; 60 | } 61 | 62 | .zoom-1 .tag-card-image { 63 | max-height: none; 64 | height: 20rem; 65 | width: 15rem; 66 | } 67 | 68 | .zoom-2 .tag-card-image { 69 | max-height: none; 70 | height: 24rem; 71 | width: 18rem; 72 | } 73 | 74 | .zoom-3 .tag-card-image { 75 | max-height: none; 76 | height: 28rem; 77 | width: 21rem; 78 | } 79 | 80 | .zoom-0.tag-card, 81 | .zoom-1.tag-card, 82 | .zoom-2.tag-card, 83 | .zoom-3.tag-card { 84 | width: initial; 85 | } 86 | 87 | .tag-card .card-section > a { 88 | position: absolute; 89 | width: 100%; 90 | height: 100%; 91 | display: block; 92 | left: 0; 93 | right: 0; 94 | top: 0; 95 | bottom: 0; 96 | padding: 7px 14px 0px 14px; 97 | } 98 | .tag-card .card-section .tag-sub-tags { 99 | position: relative; 100 | margin-top: 2rem; 101 | z-index: 1; 102 | } 103 | .tag-sub-tags { 104 | font-size: 0; 105 | } 106 | .tag-parent-tags { 107 | display: none; 108 | } 109 | -------------------------------------------------------------------------------- /plugins/PlexSync/PlexSync.yml: -------------------------------------------------------------------------------- 1 | name: Plex Sync 2 | description: Refresh Plex metadata when scene updated in Stash. Requires the StashPlexAgent.bundle agent. 3 | version: 0.1 4 | url: https://discourse.stashapp.cc/t/plex-sync/1926 5 | exec: 6 | - python3 7 | - "{pluginDir}/PlexSync.py" 8 | interface: raw 9 | settings: 10 | plexToken: 11 | displayName: Plex server token 12 | description: The X-Plex-Token for your connected Plex server. 13 | type: STRING 14 | plexHost: 15 | displayName: Plex server address 16 | description: Address to your Plex server; only hostname (do NOT add "https" or similar). "localhost" should work if Stash is on the same machine. 17 | type: STRING 18 | plexPort: 19 | displayName: Plex server port 20 | description: Port to your Plex server. Usually "32400" 21 | type: STRING 22 | tagStashIDs: 23 | displayName: Set tags on scenes based on their Stashbox source. Suggested default False. 24 | description: If enabled, you MUST manually create each tag, and set all the following tagID configs. 25 | type: BOOLEAN 26 | skipUnorganized: 27 | displayName: Do not process scenes that are not marked as "organized". Suggested default True. 28 | type: BOOLEAN 29 | cleanTitles: 30 | displayName: Convert away non-ASCII characters in titles. Suggested default True. 31 | description: This is useful especially for "new Plex experience", but it will remove non-ASCII characters such as Cyrillic script. 32 | type: BOOLEAN 33 | tagID_emptystashid: 34 | displayName: Tag ID for scenes not containing any Stashbox URL 35 | type: STRING 36 | tagID_stashdb: 37 | displayName: Tag ID for scenes from StashDB 38 | type: STRING 39 | tagID_pmvstash: 40 | displayName: Tag ID for scenes from PMVStash 41 | type: STRING 42 | tagID_porndb: 43 | displayName: Tag ID for scenes from PornDB 44 | type: STRING 45 | tagID_fansdb: 46 | displayName: Tag ID for scenes from FansDB 47 | type: STRING 48 | tagID_javstash: 49 | displayName: Tag ID for scenes from JAVStash 50 | type: STRING 51 | 52 | hooks: 53 | - name: Scene update 54 | description: Run tasks for PlexSync 55 | triggeredBy: 56 | - Scene.Update.Post 57 | -------------------------------------------------------------------------------- /themes/Theme-Minimal/README.md: -------------------------------------------------------------------------------- 1 | # Minimal Theme 2 | 3 | https://discourse.stashapp.cc/t/minimal/1421 4 | 5 | A theme that brings content to the front. 6 | 7 | It's still rough around the edges. Feedback is welcome. 8 | 9 | For intended experience: 10 | 11 | - Turn off studio images in card view. 12 | 13 | ## Screenshots 14 | 15 | stash--minimal-theme-v0 1--performers 16 | stash--minimal-theme-v0 1--tags 17 | stash--minimal-theme-v0 1--settings 18 | stash--minimal-theme-v0 1--scenes 19 | 20 | ## Changelog 21 | 22 | ### Version 0.2.7 - 2025-04-07 23 | - Theme studio rating for real. 24 | - Remove card hover. 25 | 26 | ### Version 0.2.6 - 2025-04-06 27 | 28 | - Theme studio rating. 29 | 30 | ### Version 0.2.5 - 2025-04-06 31 | 32 | - Theme tag card view. 33 | - Rework studio/tag card backgrounds. 34 | 35 | ### Version 0.2.4 - 2025-04-06 36 | 37 | - Increase contrast for settings toggles 38 | - Fix popover arrow theming 39 | - Theme performer scraper 40 | - Theme player vtt preview and markers 41 | 42 | ### Version 0.2.3 - 2025-04-05 43 | 44 | - Fix studio image in scene view. 45 | - Update performer/studio page. 46 | 47 | ### Version 0.2.2 - 2025-03-15 48 | 49 | - Theme popover arrow. 50 | - Theme card size slider. 51 | - Theme studio tagger. 52 | - Theme scene tagger search result. 53 | - Theme skeleton. 54 | - Center stats. 55 | 56 | ### Version 0.2.1 - 2025-03-15 57 | 58 | - Fix content offset from nav-bar. 59 | 60 | ### Version 0.2 - 2025-03-15 61 | 62 | - Themed the tagger view for a more cohesive look. 63 | - Restyled and refactored the navbar for improved usability and aesthetics. 64 | - Restyled all the scene card information overlays to enhance clarity and visual appeal. 65 | - Fixed the scene card selector to ensure proper functionality. 66 | - Fixed scene auto play. 67 | -------------------------------------------------------------------------------- /themes/Theme-BlackHole/Theme-BlackHole.css: -------------------------------------------------------------------------------- 1 | /* Black Hole Theme by BViking78 v2.0 */ 2 | /* STASH GENERAL */ 3 | /* Set Background to Black & Optional Custom Image */ 4 | body { 5 | background: black url("") no-repeat fixed center; 6 | background-size: contain; 7 | } 8 | 9 | /* Change Top Nav Bar Colors */ 10 | .bg-dark { 11 | background-color: #000000 !important; 12 | } 13 | 14 | /* Set Red Border on Button on Hover */ 15 | .btn-primary.btn:hover { 16 | border: 1px solid red; 17 | } 18 | 19 | /* Set Background to Transparent for Tags/Performers Popups*/ 20 | .fade.hover-popover-content { 21 | background: transparent; 22 | } 23 | 24 | /* Zoom Performers image when Hover*/ 25 | .hover-popover-content { 26 | max-width: initial; 27 | } 28 | .image-thumbnail:hover { 29 | height: 100%; 30 | } 31 | 32 | /* Set Opacity Studio Logo to 100% */ 33 | .scene-studio-overlay { 34 | opacity: 100%; 35 | } 36 | 37 | /* Making Checkbox Slightly Bigger */ 38 | .grid-card .card-check { 39 | top: 0.9rem; 40 | width: 1.75rem; 41 | } 42 | 43 | /* Center Titles on Cards */ 44 | .grid-card a .card-section-title { 45 | text-align: center; 46 | } 47 | 48 | /* Setting Background on Cards to Black and Borders to "Stash Grey" */ 49 | .card { 50 | background-color: black; 51 | border: 1px solid #30404d; 52 | } 53 | 54 | /* STASH MAIN PAGE*/ 55 | /* Change Card Header Color */ 56 | .card-header { 57 | background: black; 58 | border: 1px solid white; 59 | } 60 | /* Change Markdown Color */ 61 | .card-body { 62 | background: black; 63 | border: 1px solid white; 64 | } 65 | 66 | /* SCENE PAGE */ 67 | /* Hide the scene scrubber */ 68 | .scrubber-wrapper { 69 | display: none; 70 | } 71 | 72 | /* Setting Row "Scrape With" Background to Black */ 73 | #scene-edit-details .edit-buttons-container { 74 | background-color: black; 75 | } 76 | 77 | /* Setting Other Rows Background to Black */ 78 | div.react-select__control { 79 | background-color: black; 80 | } 81 | 82 | /* SETTING */ 83 | /* Setting Text Input Border to White */ 84 | .input-control, 85 | .text-input { 86 | border: 1px solid white; 87 | } 88 | 89 | /* Setting Background on Task Queue to Black */ 90 | #tasks-panel .tasks-panel-queue { 91 | background-color: black; 92 | } 93 | -------------------------------------------------------------------------------- /plugins/AIOverhaul/css/SimilarScenes.css: -------------------------------------------------------------------------------- 1 | /* Similar Scenes - queue-style list only */ 2 | 3 | /* Fix thumbnail sizing for queue-style list items in Similar tab */ 4 | /* Target the thumbnail containers within the Similar tab specifically */ 5 | .similar-scenes-tab .thumbnail-container { 6 | width: 160px; 7 | height: 90px; 8 | flex-shrink: 0; 9 | overflow: hidden; 10 | border-radius: 4px; 11 | margin-right: 12px; /* spacing between thumbnail and details to match queue layout */ 12 | } 13 | 14 | .similar-scenes-tab .thumbnail-container img { 15 | width: 100%; 16 | height: 100%; 17 | object-fit: cover; 18 | object-position: center; 19 | } 20 | 21 | /* Remove list bullets and match queue styling */ 22 | .similar-scenes-tab ul { 23 | list-style: none; 24 | padding: 0; 25 | margin: 0; 26 | } 27 | 28 | /* Match queue scene details styling exactly */ 29 | .similar-scenes-tab .queue-scene-details { 30 | width: 245px; 31 | display: grid; 32 | overflow: hidden; 33 | position: relative; 34 | font-weight: 500; 35 | } 36 | 37 | .similar-scenes-tab .queue-scene-title { 38 | color: #f5f8fa; 39 | font-weight: 500; 40 | } 41 | 42 | .similar-scenes-tab .queue-scene-studio, 43 | .similar-scenes-tab .queue-scene-performers, 44 | .similar-scenes-tab .queue-scene-date { 45 | color: hsla(0, 0%, 100%, 0.45); 46 | font-weight: 400; 47 | } 48 | 49 | .similar-scenes-loading, 50 | .similar-scenes-error, 51 | .similar-scenes-empty { 52 | text-align: center; 53 | padding: 2rem; 54 | color: var(--bs-text-muted, #6c757d); 55 | } 56 | 57 | .similar-scenes-error { 58 | color: var(--bs-danger, #dc3545); 59 | background-color: var(--bs-danger-bg-subtle, #f8d7da); 60 | border: 1px solid var(--bs-danger-border-subtle, #f5c2c7); 61 | border-radius: 0.375rem; 62 | } 63 | 64 | .similar-scenes-loading { 65 | display: flex; 66 | align-items: center; 67 | justify-content: center; 68 | gap: 0.5rem; 69 | } 70 | 71 | .similar-scenes-loading:before { 72 | content: ''; 73 | width: 1rem; 74 | height: 1rem; 75 | border: 2px solid var(--bs-border-color, #dee2e6); 76 | border-top-color: var(--bs-primary, #0d6efd); 77 | border-radius: 50%; 78 | animation: spin 1s linear infinite; 79 | } 80 | 81 | @keyframes spin { 82 | to { transform: rotate(360deg); } 83 | } -------------------------------------------------------------------------------- /plugins/hotCards/hotCards.yml: -------------------------------------------------------------------------------- 1 | name: Hot Cards 2 | # requires: CommunityScriptsUILibrary 3 | description: Adds custom styling to card elements that match a Tag ID or a Rating Threshold. 4 | version: 1.2.2 5 | url: https://discourse.stashapp.cc/t/hot-cards/1380 6 | ui: 7 | requires: 8 | - CommunityScriptsUILibrary 9 | javascript: 10 | - utils/configurationHandler.js 11 | - utils/helpers.js 12 | - utils/fetchInterceptor.js 13 | - utils/stashHandler.js 14 | - hotCards.js 15 | css: 16 | - hotCards.css 17 | settings: 18 | tagId: 19 | displayName: Tag ID 20 | description: Tag ID to match against. Leave blank to disable tag-based hot cards. 21 | type: STRING 22 | threshold: 23 | displayName: Rating Threshold 24 | description: Rating threshold (0-5). Use 6-100 for tenths. Set to 0 to disable rating-based hot cards. 25 | type: NUMBER 26 | home: 27 | displayName: Enable for the homepage 28 | description: Enable Hot Cards on the home page. 29 | type: BOOLEAN 30 | scenes: 31 | displayName: Enable for scenes 32 | description: "Empty to enable, 'd' to disable. Customize: [criterion]_[value]_[style]_[grad-opts]_[hover-opts]_[card-opts]. See docs." 33 | type: STRING 34 | images: 35 | displayName: Enable for images 36 | description: "Empty to enable, 'd' to disable. Customize: [criterion]_[value]_[style]_[grad-opts]_[hover-opts]_[card-opts]. See docs." 37 | type: STRING 38 | groups: 39 | displayName: Enable for groups 40 | description: "Empty to enable, 'd' to disable. Customize: [criterion]_[value]_[style]_[grad-opts]_[hover-opts]_[card-opts]. See docs." 41 | type: STRING 42 | galleries: 43 | displayName: Enable for galleries 44 | description: "Empty to enable, 'd' to disable. Customize: [criterion]_[value]_[style]_[grad-opts]_[hover-opts]_[card-opts]. See docs." 45 | type: STRING 46 | performers: 47 | displayName: Enable for performers 48 | description: "Empty to enable, 'd' to disable. Customize: [criterion]_[value]_[style]_[grad-opts]_[hover-opts]_[card-opts]. See docs." 49 | type: STRING 50 | studios: 51 | displayName: Enable for studios 52 | description: "Empty to enable, 'd' to disable. Customize: [criterion]_[value]_[style]_[grad-opts]_[hover-opts]_[card-opts]. See docs." 53 | type: STRING 54 | -------------------------------------------------------------------------------- /plugins/LocalVisage/LocalVisage.yml: -------------------------------------------------------------------------------- 1 | name: Local Visage 2 | description: Local Performer Recognition plugin using DeepFace 3 | # requires: PythonDepManager 4 | # requires: stashUserscriptLibrary7dJx1qP 5 | version: 1.0.1 6 | url: https://discourse.stashapp.cc/t/local-visage/2478 7 | exec: 8 | - python 9 | - "{pluginDir}/LocalVisage.py" 10 | interface: raw 11 | ui: 12 | requires: 13 | - stashUserscriptLibrary7dJx1qP 14 | javascript: 15 | - frontend.js 16 | - https://cdn.jsdelivr.net/npm/@gradio/client@1.15.3/dist/index.js 17 | css: 18 | - frontend.css 19 | csp: 20 | connect-src: 21 | - http://localhost:7860 22 | - http://192.168.1.198:7860 23 | - http://your-server-ip-address:7860 24 | script-src: 25 | - https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js 26 | - https://cdn.jsdelivr.net/npm/@gradio/client@1.15.3/dist/index.js 27 | 28 | tasks: 29 | - name: Rebuild Face Recognition Model 30 | description: Rebuild the face recognition model entirely 31 | defaultArgs: 32 | mode: rebuild_model 33 | - name: Update Face Recognition Model 34 | description: Update the face performers model with new images if there model was built on less than "Target image count per voy" images 35 | defaultArgs: 36 | mode: update_model 37 | - name: Start server 38 | description: Start the face recognition server (if not started) to allow the plugin to work 39 | defaultArgs: 40 | mode: spawn_server 41 | - name: Stop server 42 | description: Stop the face recognition server 43 | defaultArgs: 44 | mode: stop_server 45 | 46 | settings: 47 | voyCount: 48 | displayName: Target image count per voy (default is 15) 49 | description: Number of images to to use to create the face recognition model (per performer) 50 | type: NUMBER 51 | imgCount: 52 | displayName: Minimum number of images for performer to be added to model 53 | description: Minimum number of images a performer must have to be included in recognition (EXCLUDING THE PERFORMER THUMBNAIL). Set to 0 for best result. 54 | type: NUMBER 55 | sceneCount: 56 | displayName: Minimum number of scenes for performer to be added to model 57 | description: Minimum number of scenes a performer must have to be included in recognition 58 | type: NUMBER 59 | -------------------------------------------------------------------------------- /plugins/titleFromFilename/titleFromFilename.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | import sys 4 | import time 5 | 6 | import config 7 | 8 | # import log 9 | import graphql 10 | 11 | API_VERSION_BF_FILES = 31 # APP/DB Schema version prior to files refactor PR 12 | MAX_RETRY_COUNT = 25 13 | SLEEP_RETRY = 0.5 14 | 15 | FRAGMENT = json.loads(sys.stdin.read()) 16 | # log.LogDebug(json.dumps(FRAGMENT)) 17 | FRAGMENT_SERVER = FRAGMENT["server_connection"] 18 | FRAGMENT_SCENE_ID = FRAGMENT["args"].get("hookContext") 19 | 20 | if FRAGMENT_SCENE_ID: 21 | scene_id = FRAGMENT_SCENE_ID["id"] 22 | else: 23 | graphql.exit_plugin("No ID found") 24 | 25 | graphql_port = FRAGMENT_SERVER["Port"] 26 | graphql_scheme = FRAGMENT_SERVER["Scheme"] 27 | graphql_session = FRAGMENT_SERVER.get("SessionCookie").get("Value") 28 | 29 | system_status = graphql.get_api_version( 30 | port=graphql_port, session=graphql_session, scheme=graphql_scheme 31 | ) 32 | 33 | api_version = system_status.get("appSchema") 34 | 35 | basename = None 36 | 37 | if api_version > API_VERSION_BF_FILES: # only needed for versions after files refactor 38 | files_base = graphql.get_scene_base( 39 | scene_id=scene_id, 40 | port=graphql_port, 41 | session=graphql_session, 42 | scheme=graphql_scheme, 43 | ) 44 | if len(files_base["files"]) > 0: 45 | basename = files_base["files"][0].get("basename") 46 | else: 47 | graphql.exit_plugin( 48 | f"Stash with API version:{api_version} is not supported. You need at least {API_VERSION_BF_FILES}" 49 | ) 50 | 51 | if basename is None: 52 | graphql.exit_plugin("No basename found") # file-less scene 53 | 54 | if config.STRIP_EXT: 55 | basename = os.path.splitext(basename)[0] 56 | 57 | i = MAX_RETRY_COUNT 58 | while i >= 0: 59 | # log.LogDebug(f"TitleFromFilename: Retry attempt {i}") 60 | i -= 1 61 | updated_scene = graphql.update_scene_title( 62 | scene_id, 63 | basename, 64 | port=graphql_port, 65 | session=graphql_session, 66 | scheme=graphql_scheme, 67 | ) 68 | if updated_scene: 69 | graphql.exit_plugin( 70 | f"Scene title updated after {MAX_RETRY_COUNT - i} tries. Title:{updated_scene.get('title')}" 71 | ) 72 | time.sleep(SLEEP_RETRY) 73 | 74 | graphql.exit_plugin("Error updating scene") 75 | -------------------------------------------------------------------------------- /plugins/VideoScrollWheel/VideoScrollWheel.yml: -------------------------------------------------------------------------------- 1 | name: VideoScrollWheel 2 | # requires: CommunityScriptsUILibrary 3 | description: Adds functionality to change volume/time in scene video player by hovering over left/right side of player and scrolling with mouse scrollwheel. Scroll while hovering on left side to adjust volume, scroll on right side to skip forward/back. 4 | version: 0.4 5 | url: https://discourse.stashapp.cc/t/videoscrollwheel/1336 6 | settings: 7 | allowVolumeChange: 8 | displayName: Volume change via mouse wheel 9 | description: Allow volume to be changed via mouse wheel. When disabled it always changes video position. 10 | type: BOOLEAN 11 | volumeScrollSpeed: 12 | displayName: Volume Scroll Speed 13 | description: (Default=100.0) Scales the amount of change in volume per mouse wheel click. Negative value reverses scroll direction. 14 | type: NUMBER 15 | timeScrollSpeed: 16 | displayName: Time Scroll Speed 17 | description: (Default=100.0) Scales the amount of change in time per mouse wheel click. Negative value reverses scroll direction. 18 | type: NUMBER 19 | timeScrollAcceleration: 20 | displayName: Time Scroll Acceleration 21 | description: (Default=100.0) Scales how quickly time scrolling accelerates from min speed to max speed. Set to 0 to disable. 22 | type: NUMBER 23 | minTimeScrollSpeed: 24 | displayName: Min Time Scroll Velocity 25 | description: (Default=1.0) When acceleration is enabled, scroll speed will increase from min velocity to max velocity. 26 | type: NUMBER 27 | maxTimeScrollSpeed: 28 | displayName: Max Time Scroll Velocity 29 | description: (Default=5.0) When acceleration is enabled, scroll speed will increase from min velocity to max velocity. 30 | type: NUMBER 31 | timeScrollVelocityDecay: 32 | displayName: Time Scroll Velocity Decay 33 | description: (Default=100.0) When acceleration is enabled, this value sets how quickly velocity returns to min value while not scrolling. 34 | type: NUMBER 35 | timeScrollVelocityTimeout: 36 | displayName: Time Scroll Velocity Timeout 37 | description: (Default=2000.0) When acceleration is enabled, velocity will reset to minimum after this number of milliseconds. 38 | type: NUMBER 39 | ui: 40 | requires: 41 | - CommunityScriptsUILibrary 42 | javascript: 43 | - VideoScrollWheel.js 44 | -------------------------------------------------------------------------------- /plugins/dupeMarker/dupeMarker.py: -------------------------------------------------------------------------------- 1 | import json 2 | import sys 3 | import stashapi.log as log 4 | from stashapi.stashapp import StashInterface 5 | 6 | FRAGMENT = json.loads(sys.stdin.read()) 7 | MODE = FRAGMENT["args"]["mode"] 8 | stash = StashInterface(FRAGMENT["server_connection"]) 9 | dupe_marker_tag = stash.find_tag("[Marker: Duplicate]", create=True).get("id") 10 | 11 | def findScenesWithMarkers(): 12 | totalDupes = 0 13 | scenes = stash.find_scenes(f={"has_markers": "true"}, fragment="id") 14 | for scene in scenes: 15 | totalDupes += checkScene(scene) 16 | log.info("Found %d duplicate markers across %d scenes" % (totalDupes, len(scenes))) 17 | 18 | def addMarkerTag(marker): 19 | query = """ 20 | mutation SceneMarkerUpdate($input:SceneMarkerUpdateInput!) { 21 | sceneMarkerUpdate(input: $input) { 22 | id 23 | } 24 | } 25 | """ 26 | oldTags = [tag["id"] for tag in marker["tags"]] 27 | if dupe_marker_tag in oldTags: 28 | return 29 | oldTags.append(dupe_marker_tag) 30 | newMarker = {"id": marker["id"], "tag_ids": oldTags} 31 | stash._callGraphQL(query, {"input": newMarker}) 32 | # stash.update_scene_marker(newMarker, "id") 33 | 34 | 35 | def checkScene(scene): 36 | seen = set() 37 | dupes = [] 38 | markers = stash.get_scene_markers(scene["id"]) 39 | # find duplicate pairs 40 | for marker in markers: 41 | sortidx = ";".join( 42 | [ 43 | str(marker["title"]), 44 | str(marker["seconds"]), 45 | str(marker["primary_tag"]["id"]), 46 | ] 47 | ) 48 | if sortidx not in seen: 49 | seen.add(sortidx) 50 | else: 51 | dupes.append(marker) 52 | # add tag 53 | if dupes: 54 | log.debug("Found %d duplicate markers in scene %s" % (len(dupes), scene["id"])) 55 | for dupe in dupes: 56 | if MODE == "delete": 57 | stash.destroy_scene_marker(dupe["id"]) 58 | else: 59 | addMarkerTag(dupe) 60 | return len(dupes) 61 | 62 | def main(): 63 | if MODE == "mark": 64 | findScenesWithMarkers() 65 | elif MODE == "delete": 66 | findScenesWithMarkers() 67 | log.exit("Plugin exited normally.") 68 | 69 | if __name__ == "__main__": 70 | main() 71 | -------------------------------------------------------------------------------- /plugins/themeSwitch/assets/snippets/global/unblur-nsfw-images-on-mouse-hover.css: -------------------------------------------------------------------------------- 1 | /* [Global changes] Blur NSFW images and unblur on mouse over */ 2 | /* === MORE BLUR === */ 3 | /* scene */ 4 | .scene-card-preview, 5 | .vjs-poster, 6 | video, 7 | .scene-cover, 8 | .scrubber-item, 9 | 10 | /* image */ 11 | .image-card-preview, 12 | .image-image, 13 | .gallery-image, 14 | 15 | /* movie */ 16 | .movie-card-image, 17 | .movie-images, 18 | 19 | /* gallery */ 20 | .gallery-card-image, 21 | table > tbody > tr > td > a > img.w-100, 22 | 23 | /* performer */ 24 | .performer-card-image, 25 | img.performer, 26 | 27 | /* studio */ 28 | .studio-card-image, 29 | 30 | /* tag */ 31 | .tag-card-image { 32 | filter: blur(30px); 33 | } 34 | 35 | /* === LESS BLUR === */ 36 | /* common */ 37 | .card-section-title, 38 | 39 | /* scene */ 40 | .scene-studio-overlay, 41 | .scene-header > h3, 42 | h3.scene-header, 43 | .studio-logo, 44 | .image-thumbnail, 45 | 46 | /* image */ 47 | h3.image-header, 48 | 49 | /* movie */ 50 | .movie-details > div > h2, 51 | 52 | /* gallery */ 53 | h3.gallery-header, 54 | 55 | /* studio */ 56 | .studio-details .logo, 57 | .studio-details > div > h2, 58 | 59 | /* tag */ 60 | .logo-container > .logo, 61 | .logo-container > h2 { 62 | filter: blur(2px); 63 | } 64 | 65 | /* === UNBLUR ON HOVER === */ 66 | /* common */ 67 | .thumbnail-section:hover *, 68 | .card:hover .card-section-title, 69 | 70 | /* scene */ 71 | .card:hover .scene-studio-overlay, 72 | .video-js:hover .vjs-poster, 73 | video:hover, 74 | .scene-header:hover > h3, 75 | div:hover > .scene-header, 76 | .studio-logo:hover, 77 | .scene-cover:hover, 78 | .image-thumbnail:hover, 79 | .scene-card-preview:hover, 80 | .scrubber-item:hover, 81 | 82 | /* image */ 83 | .image-image:hover, 84 | div:hover > .image-header, 85 | .gallery-image:hover, 86 | 87 | /* movie */ 88 | .movie-images:hover, 89 | .movie-details > div > h2:hover, 90 | 91 | /* gallery */ 92 | div:hover > .gallery-header, 93 | table > tbody > tr > td:hover > a > img.w-100, 94 | 95 | /* performer */ 96 | img.performer:hover, 97 | 98 | /* studio */ 99 | .studio-details .logo:hover, 100 | .studio-details:hover > div > h2, 101 | 102 | /* tag */ 103 | .logo-container > .logo:hover, 104 | .logo-container:hover > h2 { 105 | filter: blur(0px); 106 | } 107 | -------------------------------------------------------------------------------- /plugins/chooseYourAdventurePlayer/chooseYourAdventurePlayer.yml: -------------------------------------------------------------------------------- 1 | name: Choose Your Own Adventure Player (Backer) 2 | # requires: CommunityScriptsUILibrary 3 | description: Plugin to adapt Choose Your Own Adventure Games to the VideoJS player. 4 | version: 1.0.1 5 | url: https://discourse.stashapp.cc/t/choose-your-own-adventure-player/1222 6 | settings: 7 | accessToken: 8 | displayName: Access Token 9 | description: Your personal Github access token needed to read private scripts 10 | type: STRING 11 | gameTag: 12 | displayName: Game tag 13 | description: Name of the tag indicating a scene is the starting point of game. Player will initialize once scene with this tag is loaded. Only use this tag on the starting scene. 14 | type: STRING 15 | enableGameSave: 16 | displayName: Enable game save 17 | description: When enabled you will be giving the opportunity to resume your games from where you last left off which all your choices in tack. 18 | type: BOOLEAN 19 | resourcePath: 20 | displayName: Resource path 21 | description: Location where game resources live (images, jsons). It is recommended to create custom served folder for this. 22 | type: STRING 23 | rootScenesPath: 24 | displayName: Root scenes directory path 25 | description: Data path where game scenes live. 26 | type: STRING 27 | useCustomPosters: 28 | displayName: Use custom game posters 29 | description: When enabled the t=start game screen will use provided poster instead of default scene cover. Poster should be named with the following comvention {resourcePath}/{gameId}/poster_{number}.jpg 30 | type: BOOLEAN 31 | ui: 32 | requires: 33 | - CommunityScriptsUILibrary 34 | javascript: 35 | - https://cdn.jsdelivr.net/gh/HandyRandyx/stash-plugins@main/utils/fetchInterceptor.js 36 | - https://cdn.jsdelivr.net/gh/HandyRandyx/stash-plugins@main/utils/stashHandler.js 37 | - https://cdn.jsdelivr.net/gh/HandyRandyx/stash-plugins@main/utils/registerPathChangeListener.js 38 | - https://cdn.jsdelivr.net/gh/cj12312021/stash-plugins@main/utils/waitForClass.js 39 | - https://vjs.zencdn.net/8.16.1/video.min.js 40 | - https://raw.githubusercontent.com/chrisboustead/videojs-vtt-thumbnails/master/src/plugin.js 41 | - cyap-min.js 42 | css: 43 | - chooseYourAdventurePlayer.css 44 | csp: 45 | connect-src: 46 | - https://api.github.com 47 | -------------------------------------------------------------------------------- /plugins/setSceneCoverFromFile/set_cover.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | import sys 4 | import json 5 | 6 | try: 7 | import stashapi.log as log 8 | from stashapi.tools import file_to_base64 9 | from stashapi.stashapp import StashInterface 10 | except ModuleNotFoundError: 11 | print( 12 | "You need to install the stashapi module. (pip install stashapp-tools)", 13 | file=sys.stderr, 14 | ) 15 | 16 | MANUAL_ROOT = None # /some/other/path to override scanning all stashes 17 | cover_pattern = r"(?:thumb|poster|cover)\.(?:jpg|png)" 18 | 19 | 20 | def main(): 21 | global stash, mode_arg 22 | json_input = json.loads(sys.stdin.read()) 23 | 24 | stash = StashInterface(json_input["server_connection"]) 25 | mode_arg = json_input["args"]["mode"] 26 | 27 | try: 28 | if MANUAL_ROOT: 29 | scan(MANUAL_ROOT, handle_cover) 30 | else: 31 | for stash_path in get_stash_paths(): 32 | scan(stash_path, handle_cover) 33 | except Exception as e: 34 | log.error(e) 35 | 36 | out = json.dumps({"output": "ok"}) 37 | print(out + "\n") 38 | 39 | 40 | def handle_cover(path, file): 41 | filepath = os.path.join(path, file) 42 | 43 | b64img = file_to_base64(filepath) 44 | if not b64img: 45 | log.warning(f"Could not parse {filepath} to b64image") 46 | return 47 | 48 | scenes = stash.find_scenes( 49 | f={"path": {"modifier": "INCLUDES", "value": f'{path}"'}}, fragment="id" 50 | ) 51 | 52 | log.info(f'Found Cover: {[int(s["id"]) for s in scenes]}|{filepath}') 53 | 54 | if mode_arg == "set_cover": 55 | for scene in scenes: 56 | stash.update_scene({"id": scene["id"], "cover_image": b64img}) 57 | log.info(f"Applied cover to {len(scenes)} scenes") 58 | 59 | 60 | def get_stash_paths(): 61 | config = stash.get_configuration("general { stashes { path excludeVideo } }") 62 | stashes = config["general"]["stashes"] 63 | return [s["path"] for s in stashes if not s["excludeVideo"]] 64 | 65 | 66 | def scan(ROOT_PATH, _callback): 67 | log.info(f"Scanning {ROOT_PATH}") 68 | for root, dirs, files in os.walk(ROOT_PATH): 69 | for file in files: 70 | if re.match(cover_pattern, file, re.IGNORECASE): 71 | _callback(root, file) 72 | 73 | 74 | if __name__ == "__main__": 75 | main() 76 | --------------------------------------------------------------------------------