├── 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 | 
9 | 
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 | 
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 |
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 |
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 | 
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 | 
16 |
17 | Adds an additional dimension to the rating banners.
18 |
19 | ### Performer profile cards
20 | 
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 | 
16 |
17 | ### Demo GIF:
18 | 
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 | 
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 | 
18 |
19 | ### Demo GIF:
20 | 
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 | 
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 | 
6 |
7 | 
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 |
16 |
17 |
18 |
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 |
--------------------------------------------------------------------------------