Initial version

This commit is contained in:
DogmaDragon 2022-12-01 11:28:23 +11:00 committed by WithoutPants
commit 466cd55874
117 changed files with 11257 additions and 0 deletions

50
.github/workflows/jekyll.yml vendored Normal file
View File

@ -0,0 +1,50 @@
# Sample workflow for building and deploying a Jekyll site to GitHub Pages
name: Deploy Jekyll with GitHub Pages dependencies preinstalled
on:
# Runs on pushes targeting the default branch
push:
branches: ["master"]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write
# Allow one concurrent deployment
concurrency:
group: "pages"
cancel-in-progress: true
jobs:
# Build job
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Pages
uses: actions/configure-pages@v2
- name: Build with Jekyll
uses: actions/jekyll-build-pages@v1
with:
source: ./
destination: ./_site
- name: Upload artifact
uses: actions/upload-pages-artifact@v1
# Deployment job
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v1

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
# ignore directories for local testing
.jekyll-cache/
_site/
.vscode
cheatsheet.md

8
Gemfile Normal file
View File

@ -0,0 +1,8 @@
source "https://rubygems.org"
gem "webrick", "~> 1.7"
gem "just-the-docs"
group :jekyll_plugins do
gem "jekyll-remote-theme", "~> 0.4.2"
end

83
Gemfile.lock Normal file
View File

@ -0,0 +1,83 @@
GEM
remote: https://rubygems.org/
specs:
addressable (2.8.1)
public_suffix (>= 2.0.2, < 6.0)
colorator (1.1.0)
concurrent-ruby (1.1.10)
em-websocket (0.5.3)
eventmachine (>= 0.12.9)
http_parser.rb (~> 0)
eventmachine (1.2.7)
ffi (1.15.5-x64-mingw-ucrt)
forwardable-extended (2.6.0)
http_parser.rb (0.8.0)
i18n (1.12.0)
concurrent-ruby (~> 1.0)
jekyll (4.2.2)
addressable (~> 2.4)
colorator (~> 1.0)
em-websocket (~> 0.5)
i18n (~> 1.0)
jekyll-sass-converter (~> 2.0)
jekyll-watch (~> 2.0)
kramdown (~> 2.3)
kramdown-parser-gfm (~> 1.0)
liquid (~> 4.0)
mercenary (~> 0.4.0)
pathutil (~> 0.9)
rouge (~> 3.0)
safe_yaml (~> 1.0)
terminal-table (~> 2.0)
jekyll-remote-theme (0.4.3)
addressable (~> 2.0)
jekyll (>= 3.5, < 5.0)
jekyll-sass-converter (>= 1.0, <= 3.0.0, != 2.0.0)
rubyzip (>= 1.3.0, < 3.0)
jekyll-sass-converter (2.2.0)
sassc (> 2.0.1, < 3.0)
jekyll-seo-tag (2.8.0)
jekyll (>= 3.8, < 5.0)
jekyll-watch (2.2.1)
listen (~> 3.0)
just-the-docs (0.3.3)
jekyll (>= 3.8.5)
jekyll-seo-tag (~> 2.0)
rake (>= 12.3.1, < 13.1.0)
kramdown (2.4.0)
rexml
kramdown-parser-gfm (1.1.0)
kramdown (~> 2.0)
liquid (4.0.3)
listen (3.7.1)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
mercenary (0.4.0)
pathutil (0.16.2)
forwardable-extended (~> 2.6)
public_suffix (5.0.0)
rake (13.0.6)
rb-fsevent (0.11.2)
rb-inotify (0.10.1)
ffi (~> 1.0)
rexml (3.2.5)
rouge (3.30.0)
rubyzip (2.3.2)
safe_yaml (1.0.5)
sassc (2.4.0)
ffi (~> 1.9)
terminal-table (2.0.0)
unicode-display_width (~> 1.1, >= 1.1.1)
unicode-display_width (1.8.0)
webrick (1.7.0)
PLATFORMS
x64-mingw-ucrt
DEPENDENCIES
jekyll-remote-theme (~> 0.4.2)
just-the-docs
webrick (~> 1.7)
BUNDLED WITH
2.3.21

2
README.md Normal file
View File

@ -0,0 +1,2 @@
# Stash-Docs (work-in-progress)
Website: https://dogmadragon.github.io/Stash-Docs/

65
_config.yml Normal file
View File

@ -0,0 +1,65 @@
remote_theme: just-the-docs/just-the-docs
plugins:
- jekyll-remote-theme
exclude: ["node_modules/", "*.gemspec", "*.gem", "Gemfile", "Gemfile.lock", "package.json", "package-lock.json", "script/", "LICENSE.txt", "lib/", "bin/", "README.md", "Rakefile"]
title: Stash-Docs
description: Documentation for Stash software
domain: dogmadragon.github.io
url: "https://dogmadragon.github.io/"
baseurl: "/Stash-Docs"
permalink: pretty
# Set a path/url to a logo that will be displayed instead of the title
logo: "/assets/logo_v2.png"
#logo: https://stashapp.cc/images/stash.svg
# Aux links for the upper right navigation
aux_links:
"Donate @ Open Collective":
- "https://opencollective.com/stashapp"
aux_links_new_tab: true
# External navigation links
nav_external_links:
- title: Community Discord
url: https://discord.gg/frQ7qBvB3S
# Footer content
# appears at the bottom of every page's main content
# Footer "Edit this page on GitHub" link text
gh_edit_link: true # show or hide edit this page link
gh_edit_link_text: "Edit this page on GitHub."
gh_edit_repository: "https://github.com/DogmaDragon/Stash-Docs" # the github URL for your repo
gh_edit_branch: "master" # the branch that your docs is served from
gh_edit_view_mode: "edit" # "tree" or "edit" if you want the user to jump into the editor immediately
# Back to top link
back_to_top: true
back_to_top_text: "Back to top"
footer_content: "Maintained by Stash community"
# Color scheme currently only supports "dark", "light"/nil (default), or a custom scheme that you define
color_scheme: stashdb-dark
callouts:
warning:
title: Warning
color: red
note:
title: Note
color: yellow
new:
title: New
color: green
important:
title: Important
color: blue
kramdown:
syntax_highlighter_opts:
block:
line_numbers: false

View File

@ -0,0 +1,21 @@
$link-color: #48aff0;
$btn-primary-color: #48aff0;
$base-button-color: $grey-dk-250;
$sidebar-color: #394B59;
$body-background-color: #202B33;
$body-text-color: #FFFFFF;
$body-heading-color: #F5F8FA;
$nav-child-link-color: #E9ECEF;
$border-color: #808080;
$table-background-color: $grey-dk-250;
$feedback-color: darken($sidebar-color, 3%);
$search-result-preview-color: $grey-dk-000;
$search-background-color: $grey-dk-250;
@import "./vendor/OneDarkJekyll/syntax-one-dark-vivid";
$code-background-color: #394B59;
.highlight, pre.highlight {
background-color: #394B59 !important;
}
th, td {
background-color: #394B59 !important;
}

View File

@ -0,0 +1,4 @@
$link-color: #137CBD;
$btn-primary-color: #137CBD;
$sidebar-color: #E9ECEF;
$body-text-color: #000000;

19
_sass/custom/custom.scss Normal file
View File

@ -0,0 +1,19 @@
@media (min-width: 50rem) { .site-title { padding-right: 0rem; padding-left: 2rem; } }
@media (min-width: 66.5rem) { .side-bar {
width: calc((100% - 1064px) / 2 + 264px);
min-width: 264px;
max-width: fit-content;
} }
@media (min-width: 66.5rem) {
.main {
margin-left: calc( (100% - 100% + 2px) / 2 + 264px );
max-width: -webkit-fill-available;
} }
@media (min-width: 50rem) {
.main-content-wrap {
padding-right: 4rem;
padding-left: 4rem;
} }

BIN
assets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

20
assets/logo.svg Normal file
View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="423" height="135" viewBox="0 0 423 135" xml:space="preserve">
<desc>Created with Fabric.js 3.6.6</desc>
<defs>
</defs>
<g transform="matrix(1 0 0 1 100.36 67.98)" >
<g style="" >
<g transform="matrix(1 0 0 1 0 0)" >
<path style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; fill: rgb(100,77,63); fill-rule: nonzero; opacity: 1;" transform=" translate(-281.27, -403.33)" d="m 281.375 335.34375 l -84.3125 24.53125 l -16.15625 20.4375 l 73.375 25.59375 l 21.28125 -25.8125 l 5.59375 2.375 l 5.59375 -2.375 l 22.15625 26.46875 l 72.71875 -25.8125 l -17 -22.15625 l -83.25 -23.25 z m 0 6.6875 l 58.96875 17.40625 L 281.375 376 l -59.59375 -17.21875 l 59.59375 -16.75 z m 4.625 51.1875 l -0.21875 77.875 l 78.3125 -32.0625 l 0.21875 -43.65625 l -58.71875 19.34375 l -17.21875 -21.5 l -2.375 0 z M 274.71875 393.4375 L 257.5 414.9375 L 198.78125 395.59375 L 199 439.25 l 78.3125 32.0625 l -0.21875 -77.875 l -2.375 0 z" stroke-linecap="round" />
</g>
<g transform="matrix(1 0 0 1 -30.72 -218.61)" style="" >
<text xml:space="preserve" font-family="Arial" font-size="64" font-style="normal" font-weight="normal" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; fill: rgb(0,0,0); fill-rule: nonzero; opacity: 1; white-space: pre;" ></text>
</g>
</g>
</g>
<g transform="matrix(4.52 0 0 4.52 316.86 70.23)" style="" >
<text xml:space="preserve" font-family="'Open Sans', sans-serif" font-size="42" font-style="normal" font-weight="normal" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; fill: rgb(255,255,255); fill-rule: nonzero; opacity: 1; white-space: pre;" ><tspan x="-24.69" y="5.65" style="stroke-width: 1; font-family: 'Open Sans', sans-serif; font-size: 18px; font-style: normal; font-weight: bold; fill: rgb(255,255,255); ">Stash</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
assets/logo_v2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

20
assets/logo_v2.svg Normal file
View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="526" height="135" viewBox="0 0 526 135" xml:space="preserve">
<desc>Created with Fabric.js 3.6.6</desc>
<defs>
</defs>
<g transform="matrix(1 0 0 1 100.36 67.98)" >
<g style="" >
<g transform="matrix(1 0 0 1 0 0)" id="path3824" >
<path style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; fill: rgb(100,77,63); fill-rule: nonzero; opacity: 1;" transform=" translate(-281.27, -403.33)" d="m 281.375 335.34375 l -84.3125 24.53125 l -16.15625 20.4375 l 73.375 25.59375 l 21.28125 -25.8125 l 5.59375 2.375 l 5.59375 -2.375 l 22.15625 26.46875 l 72.71875 -25.8125 l -17 -22.15625 l -83.25 -23.25 z m 0 6.6875 l 58.96875 17.40625 L 281.375 376 l -59.59375 -17.21875 l 59.59375 -16.75 z m 4.625 51.1875 l -0.21875 77.875 l 78.3125 -32.0625 l 0.21875 -43.65625 l -58.71875 19.34375 l -17.21875 -21.5 l -2.375 0 z M 274.71875 393.4375 L 257.5 414.9375 L 198.78125 395.59375 L 199 439.25 l 78.3125 32.0625 l -0.21875 -77.875 l -2.375 0 z" stroke-linecap="round" />
</g>
<g transform="matrix(1 0 0 1 -30.72 -218.61)" style="" id="text3837" >
<text xml:space="preserve" font-family="Arial" font-size="64" font-style="normal" font-weight="normal" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; fill: rgb(0,0,0); fill-rule: nonzero; opacity: 1; white-space: pre;" ></text>
</g>
</g>
</g>
<g transform="matrix(3.31 0 0 3.31 366.06 68.27)" style="" >
<text xml:space="preserve" font-family="'Open Sans', sans-serif" font-size="39" font-style="normal" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; fill: rgb(0,0,0); fill-rule: nonzero; opacity: 1; white-space: pre;" ><tspan x="-48.92" y="5.65" style="stroke-width: 1; font-family: 'Open Sans', sans-serif; font-size: 18px; font-style: normal; font-weight: bold; fill: rgb(255,255,255); ">Stash-Docs</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

24
docs/Add-ons.md Normal file
View File

@ -0,0 +1,24 @@
---
layout: default
title: Add-ons
nav_order: 5
has_children: true
has_toc: false
---
# **Add-ons**
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. [Scrapers]({{ site.baseurl }}/docs/Add-ons/Scrapers/)
2. [Plugins]({{ site.baseurl }}/docs/Add-ons/Plugins/)
3. [Scripts]({{ site.baseurl }}/docs/Add-ons/Scripts/)
4. [Userscripts]({{ site.baseurl }}/docs/Add-ons/Userscripts/)
5. [Utilities]({{ site.baseurl }}/docs/Add-ons/Utilities/)
6. [Third-Party Integrations]({{ site.baseurl }}/docs/Add-ons/Third-Party-Integrations/)
</details>

65
docs/Add-ons/Plugins.md Normal file
View File

@ -0,0 +1,65 @@
---
layout: default
title: Plugins
nav_order: 2
parent: Add-ons
---
# Plugins
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
Stash plugins add further features that Stash doesn't itself provide.
To install a plugin, save them to `%USERPROFILE%\.stash\plugins` on Windows or `/root/.stash/plugins` on Unix systems (Mac, Linux, etc.). The `plugins` directory won't exist by default so you will have to create it. Once installed, go to the Plugins page in your Stash settings and reload plugins.
Plugins are separated into 2 sections:
---
## Official
Official plugins are hosted on [CommunityScripts repository](https://github.com/stashapp/CommunityScripts){:target="_blank"}. All code is reviewed by core Stash team and any questions or issues you have regarding these plugins can be raised in Discord or GitHub.
Category|Triggers|Plugin Name|Description|Minimum Stash version
--------|-----------|-----------|-----------|---------------------
Scraper | Task | [GHScraper_Checker](https://github.com/stashapp/CommunityScripts/tree/main/plugins/GHScraper_Checker){:target="_blank"} | Compare local file against github file from the community scraper repo. | v0.8
Maintenance|Task<br />Scene.Update|[renamerOnUpdate](https://github.com/stashapp/CommunityScripts/blob/main/plugins/renamerOnUpdate){:target="_blank"}|Rename/Move your file based on Stash metadata.|v0.7
Maintenance|Set Scene Cover|[setSceneCoverFromFile](https://github.com/stashapp/CommunityScripts/blob/main/plugins/setSceneCoverFromFile){:target="_blank"}|Searchs Stash for Scenes with a cover image in the same folder and sets the cover image in stash to that image|v0.7
Scenes|SceneMarker.Create<br />SceneMarker.Update|[markerTagToScene](https://github.com/stashapp/CommunityScripts/blob/main/plugins/markerTagToScene){:target="_blank"}|Adds primary tag of Scene Marker to the Scene on marker create/update.|v0.8 ([46bbede](https://github.com/stashapp/stash/commit/46bbede9a07144797d6f26cf414205b390ca88f9){:target="_blank"})
Scanning|Scene.Create<br />Gallery.Create<br />Image.Create|[defaultDataForPath](https://github.com/stashapp/CommunityScripts/blob/main/plugins/defaultDataForPath){:target="_blank"}|Adds configured Tags, Performers and/or Studio to all newly scanned Scenes, Images and Galleries..|v0.8
Scanning|Scene.Create<br />Gallery.Create|[filenameParser](https://github.com/stashapp/CommunityScripts/blob/main/plugins/filenameParser){:target="_blank"}|Tries to parse filenames, primarily in {studio}.{year}.{month}.{day}.{performer1firstname}.{performer1lastname}.{performer2}.{title} format, into the respective fields|v0.10
Scanning|Scene.Create|[pathParser](https://github.com/stashapp/CommunityScripts/blob/main/plugins/pathParser){:target="_blank"}|Updates scene info based on the file path.|v0.17
Scanning|Scene.Create|[titleFromFilename](https://github.com/stashapp/CommunityScripts/blob/main/plugins/titleFromFilename){:target="_blank"}|Sets the scene title to its filename|v0.17
Reporting||[TagGraph](https://github.com/stashapp/CommunityScripts/blob/main/plugins/tagGraph){:target="_blank"}|Creates a visual of the Tag relations.|v0.7
---
## Third-party
{: .note }
These plugins are created by third parties and not officially affiliated or supported by the core Stash team. If you have issues, please reach out to the original creators.
Category | Plugin | Author | Description
-|-|-|-
Downloader | [YT-DL Downloader](https://github.com/niemands/StashPlugins){:target="_blank"} | niemands | Download Videos automated with yt-dl and add the scrape tag for Bulk URL Scraper |
Images | [Gallery Tags](https://github.com/niemands/StashPlugins){:target="_blank"} | niemands | Copy information from attached scene to galleries |
Images | [Update Image Titles](https://github.com/niemands/StashPlugins){:target="_blank"} | niemands | Update all image titles (Fixes natural sort) |
Maintenance | [Duplicate Finder](https://github.com/WithoutPants/stash-plugin-duplicate-finder){:target="_blank"} | WithoutPants | Detects and marks duplicate scenes within Stash
Metadata | [Performer Creator](https://github.com/com1234475/stash-plugin-performer-creator){:target="_blank"} | com1234475 | Creates performers from scenes based on filenames.
Metadata | [Set PH Urls](https://github.com/niemands/StashPlugins){:target="_blank"} | niemands | Add urls to pornhub scenes downloaded by Youtube-dl |
Scraping | [Bulk URL Scraper](https://github.com/niemands/StashPlugins){:target="_blank"} | niemands | Bulk scene url scraping |
Scraping | [OnlyFans Scraper](https://github.com/ALonelyJuicebox/OFMetadataToStash){:target="_blank"} | ALonelyJuicebox | Scrape scene and performer info from OnlyFans
Scraping | [Scrape with](https://github.com/Tweeticoats/stash-plugin-scrape_with){:target="_blank"} | Tweeticoats | Batch scrape scenes and performers. For example tag a scene with scrape_with_xbvr and run the task to run that scraper. Run artist scrapers on all performers missing a url. Run the performer image scraper on all performers.
VR | [stash-deovr](https://github.com/Tweeticoats/stash-deovr){:target="_blank"} | Tweeticoats | Create an index json file /deovr used by the vr player allowing you to use DeoVR to play 2d and 3d videos from stash.

104
docs/Add-ons/Scrapers.md Normal file
View File

@ -0,0 +1,104 @@
---
layout: default
title: Scrapers
nav_order: 1
has_children: false
parent: Add-ons
---
# Scrapers
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
## Scrapers
Scrapers are the way to retrieve information from websites for your movies/scenes/galleries/performers...etc. Using scrapers wisely, you can avoid typing information manually and repetitively. They can help you quickly establish links between movies/scenes and performers/studios, add relative tags, then download covers/posters for easy recognition. It's a great feature to organize your video or image collections.
All scrapers are ".yml" files. Some scrapers like script scrapers also come with their own ".py", ".rb" files.
### Installation
To install a scraper:
1. Make sure you have the `scrapers` folder in the same location of your Stash app. If you don't have it, create that folder/directory. You can also specify the name of this folder in the `config.yml`.
2. Go to the [CommunityScrapers repository](https://github.com/stashapp/CommunityScrapers){:target="_blank"} and download the scraper you want. Read the [scrapers list](https://github.com/stashapp/CommunityScrapers/blob/master/SCRAPERS-LIST.md){:target="_blank"} and make sure which one to download. All scrapers are in .yml format. Some of them requires Python or Ruby if you use those, you need to install Python/Ruby in your machine, and don't forget to get the corresponding .py or .rb file as well.
{: .note }
For Python scrapers, it's also helpful to read the .py file. Some of them require extra python components installed to work.
{:style="counter-reset:none"}
3. Once the new scrapers are in position, you need to go to "Settings->Scraping" and click on "Reload Scrapers" button. You should see your scrapers listed in the list below that button.
{:style="counter-reset:none"}
4. Navigate to the scene/gallery/movie/performer you want to scrape.
- If that's a URL scraper, you need to paste the URL in its "URL" blanket, and the scrape icon next to it should light up.
- If that's a Search-By-Name scraper, you can provide the name and choose "Scrape With...", and the scraper should fetch the list of potential matches for you.
{:style="counter-reset:none"}
5. You will preview the scrape result before you commit the change. Check on the left will keep the original value, check on the right will make the change.
---
## Scraper Types
### By Searching Type
#### Fragment
This kind of scrapers will fetch the metadata from a website, by using existing data from Stash, like a scene's file name, performer's name...etc. Fragment scrapers will get all the data Stash knows about that scene/performer/gallery, so it's quite flexible when fetching information.
Scrapers includes: AdultTimeAPI.yml, JavLibrary.yml, ThePornDB.yml
#### Search By Name
A Search-By-Name scraper will get only "name" input from a scene or performer, then it will search a website with that name, and return a list of results.
Scrapers includes: Babepedia.yml, FreeonesCommunity.yml, IAFD.yml
#### Search By URL
Most scrapers fetch metadata from a given URL, either by using XPath, JSON, or scripts. For this kind of scrapers you need to know the URL for that scene/performer/gallery/movie so they can extract information from it.
Scrapers includes: All other scrapers.
### By Implementation
#### XPath and JSON Scrapers
This is the most common type of scrapers, which use either XPath parser to pin-point the information and retrieve them, or send out JSON requests to get the information. xpathScraper and jsonScraper can be mixed in the same .yml file.
#### CDP Scrapers
This type of scrapers is mostly the same as XPath/JSON scrapers, except it will launch a headless Chrome browser to retrieve the information from websites. It can also get cookies, simulate a mouse click and other actions. These scrapers have `useCDP: true` setting in them.
#### Python / Ruby Scrapers
This type of scrapers will launch Python, Ruby to retrieve information from websites. Script scrapers are powerful, versatile and cross-platform. So they usually can do much more than regular scrapers. To install this kind of scrapers, you need to copy not only the .yml file, but also all the script files like .py, .rb that associated with it.
---
## More details
You can view the [detailed information about scrapers here]({{ site.baseurl }}/docs/In-app-Manual/Scraping/) or [CommunityScrapers README](https://github.com/stashapp/CommunityScrapers/blob/master/README.md){:target="_blank"}.
---
## Create your own
To create your own scraper, there is [detailed information about that as well]({{ site.baseurl }}/docs/In-app-Manual/Metadata-Scraping/ScraperDevelopment/). Best way to start is to read the simple ones and understand how xpath works. The [XPath Cheetsheet](https://devhints.io/xpath){:target="_blank"} is quite useful in creating a .yml file. In Firefox you can use xpath search in "Web Developer Tools (F12)". The "search HTML" bar actually accepts xpath searches. You can use it to verify your xpath queries.
---
## Contribution
The Scraper community always welcome new members. If you create a nice scraper and find it stable and useful, you can share it via [the GitHub repository](https://github.com/stashapp/CommunityScrapers){:target="_blank"}. Create a pull request, and let the mod review your work. The mods are busy, so it will probably take a few days, or a couple of weeks, but it will be a great feeling once your contribution is accepted by the community.

49
docs/Add-ons/Scripts.md Normal file
View File

@ -0,0 +1,49 @@
---
layout: default
title: Scripts
nav_order: 3
parent: Add-ons
---
# Scripts
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
Scripts are standalone programs that can interact with stash either through graphql queries or by directly editing stash's database/files.
To install a script follow the script's install instructions.
Scripts are separated into 2 sections:
---
## Official
Official scripts are hosted on [CommunityScripts repository](https://github.com/stashapp/CommunityScripts){:target="_blank"}. All code is reviewed by core Stash team and any questions or issues you have regarding these scripts can be raised in Discord or GitHub.
Category | Plugin Name | Description | Minimum Stash version
--------|-----------|-----------|---------------------
Kodi | [Kodi Helper](https://github.com/stashapp/CommunityScripts/blob/main/scripts/kodi-helper){:target="_blank"} | Generates `nfo` and `strm` for use with Kodi. | v0.7
Maintenance | [Stash Sqlite Renamer](https://github.com/stashapp/CommunityScripts/blob/main/scripts/Sqlite_Renamer){:target="_blank"} | Renames your files using stash's metadata. | v0.7
---
## Third-party
{: .note }
These scripts are created by third parties and not officially affiliated or supported by the core Stash team. If you have issues, please reach out to the original creators.
Category | Plugin | Author | Description
-|-|-|-

View File

@ -0,0 +1,30 @@
---
layout: default
title: Third-Party Integrations
nav_order: 6
parent: Add-ons
---
# Third-Party Integrations
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
Integrations are repositories developed to integrate Stash into other programs.
{: .note }
These third-party integrations are created by third parties and not officially affiliated or supported by the core Stash team. If you have issues, please reach out to the original creators.
Category | Name | Author | Description
-|-|-|-
Kodi | [plugin.video.stash](https://github.com/gitgiggety/plugin.video.stash){:target="_blank"} | gitgiggety | plugin.video.stash is an add-on for the Kodi home theater center software to incorporate Stash, an organizer for your porn

View File

@ -0,0 +1,93 @@
---
layout: default
title: Userscripts
nav_order: 4
parent: Add-ons
---
# Userscripts
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
Userscripts are programs written in Javascript that modifies web pages to improve browsing with new features, formatting and more.
To install the userscript you will need a browser extension such as [Violentmonkey](https://violentmonkey.github.io){:target="_blank"} / [Tampermonkey](https://www.tampermonkey.net){:target="_blank"} / [Greasemonkey](https://www.greasespot.net){:target="_blank"}.
## Categories
- **Stash** - applies to Stash software
- **StashDB** - applies to StashDB.org stash-box instance (might work with other stash-box instances with some tweaking)
- **Utility** - applies to sites not related to Stash
Userscripts are separated into 2 sections:
---
## Official
Official userscripts are hosted on [CommunityScripts repository](https://github.com/stashapp/CommunityScripts){:target="_blank"}. All code is reviewed by core Stash team and any questions or issues you have regarding these userscripts can be raised in Discord or GitHub.
| Category | Userscript Name | Description |
---------|---------------|-----------|
StashDB | [StashDB Submission Helper](https://github.com/stashapp/CommunityScripts/blob/main/userscripts/StashDB_Submission_Helper){:target="_blank"} | Adds handy functions for StashDB submissions like buttons to add aliases in bulk to a performer |
---
## Third-party
{: .note }
These userscripts are created by third parties and not officially affiliated or supported by the core Stash team. If you have issues, please reach out to the original creators.
Category | Plugin | Author | Description
-|-|-|-
Stash | [Stash Batch Query Edit](https://github.com/7dJx1qP/stash-userscripts){:target="_blank"} | 7dJx1qP | In Scene Tagger, adds button to batch update all query fields with a configurable combination of Date, Studio, Performers, and Title
Stash | [Stash Batch Result Toggle](https://github.com/7dJx1qP/stash-userscripts){:target="_blank"} | 7dJx1qP | In Scene Tagger, adds button to toggle all stashdb scene match result fields. Saves clicks when you only want to save a few metadata fields. Instead of turning off every field, you batch toggle them off, then toggle on the ones you want
Stash | [Stash Batch Save](https://github.com/7dJx1qP/stash-userscripts){:target="_blank"} | 7dJx1qP | In Scene Tagger, adds button to batch save all scenes. Opens a confirmation popup with clicked
Stash | [Stash Batch Search](https://github.com/7dJx1qP/stash-userscripts){:target="_blank"} | 7dJx1qP | In Scene Tagger, adds button to batch search all scenes
Stash | [Stash Markdown](https://github.com/7dJx1qP/stash-userscripts){:target="_blank"} | 7dJx1qP | Adds markdown parsing to tag description fields
Stash | [Stash New Performer Filter Button](https://github.com/7dJx1qP/stash-userscripts){:target="_blank"} | 7dJx1qP | Adds button to performers page to switch to a filter by new performers tagger view
Stash | [Stash Open Media Player](https://github.com/7dJx1qP/stash-userscripts){:target="_blank"} | 7dJx1qP | Open filepath link on scene page 'File Info' tab in an external media player when clicked
Stash | [Stash Performer Audit Task Button](https://github.com/7dJx1qP/stash-userscripts){:target="_blank"} | 7dJx1qP | Adds a button to the performers page to check for duplicate performer urls. Task output shown in stash logs
Stash | [Stash Performer Image Cropper](https://github.com/7dJx1qP/stash-userscripts){:target="_blank"} | 7dJx1qP | Adds ability to crop performer image from performer page
Stash | [Stash Performer Markers Tab](https://github.com/7dJx1qP/stash-userscripts){:target="_blank"} | 7dJx1qP | Adds a Markers link to performer pages
Stash | [Stash Performer Tagger Additions](https://github.com/7dJx1qP/stash-userscripts){:target="_blank"} | 7dJx1qP | Adds performer birthdate and url to tagger view. Makes clicking performer name open stash profile in new tab instead of current tab
Stash | [Stash Performer URL Searchbox](https://github.com/7dJx1qP/stash-userscripts){:target="_blank"} | 7dJx1qP | Adds a performer URL search textbox to performers page for quicker searching by performer URL
Stash | [Stash Scene Tagger Additions](https://github.com/7dJx1qP/stash-userscripts){:target="_blank"} | 7dJx1qP | Adds scene duration, filepath, and url to tagger view in the collapsible scene details sections. Adds shift-click to collapsible scene details buttons to open/close all.
Stash | [Stash Scene Tagger Colorizer](https://github.com/7dJx1qP/stash-userscripts){:target="_blank"} | 7dJx1qP | In Scene Tagger, colorize scrape results. Matching fields are green, missing fields are yellow, and mismatching fields are red
Stash | [Stash Scene Tagger Draft Submit](https://github.com/7dJx1qP/stash-userscripts){:target="_blank"} | 7dJx1qP | Adds button to Scene Tagger to submit draft to stashdb
Stash | [Stash Scene Tagger Linkify](https://github.com/7dJx1qP/stash-userscripts){:target="_blank"} | 7dJx1qP | Turn all scene tagger result text referencing stash or stashbox studio and performer names into clickable links
Stash | [Stash Set Stashbox Favorite Performers](https://github.com/7dJx1qP/stash-userscripts){:target="_blank"} | 7dJx1qP | Sync Stashbox favorite performers whenever a stash performer is favorited or unfavorited. Also adds button to performers page to sync all
Stash | [Stash StashID Icon](https://github.com/7dJx1qP/stash-userscripts){:target="_blank"} | 7dJx1qP | Adds checkmark icon to performer and studio cards that have a stashid
Stash | [Stash StashID Input](https://github.com/7dJx1qP/stash-userscripts){:target="_blank"} | 7dJx1qP | Adds input textboxes to performer detail and studio detail pages for stashid entry. Also displays studio stashids on studio page without having to click edit to view
Stash | [Stash Stats](https://github.com/7dJx1qP/stash-userscripts){:target="_blank"} | 7dJx1qP | Adds new stats to the stats page: marker count, favorite performer count, studios with stashid %, performers with stashid %, scenes with stashid %
Stash | [Stash Studio Image And Parent On Create](https://github.com/7dJx1qP/stash-userscripts){:target="_blank"} | 7dJx1qP | In Scene Tagger, sets studio image and parent studio from StashDB when a studio is created
Stash | [Stash Tag Image Cropper](https://github.com/7dJx1qP/stash-userscripts){:target="_blank"} | 7dJx1qP | Adds ability to crop tag image from tag page
Stash | [Visage](https://github.com/cc1234475/visage){:target="_blank"} | cc1234475 | Visage is a series of tools to do facial recognition on images (and create a searchable database)
StashDB | [StashDB Copy StashID](https://github.com/7dJx1qP/stashdb-userscripts){:target="_blank"} | 7dJx1qP | Adds a button to copy StashID
StashDB | [StashDB Backlog Userscript](https://gist.github.com/peolic/e4713081f7ad063cd0e91f2482ac39a7){:target="_blank"} | peolic | This userscript is used to be able to view pending changes from the StashDB Backlog spreadsheet directly on StashDB
StashDB | [StashDB Images Userscript](https://gist.github.com/peolic/7368022947a28ef11bf44d0ae802df45){:target="_blank"} | peolic | This userscript adds image resolutions next to every performer image on StashDB
StashDB | [StashDB ID Copy Buttons](https://github.com/peolic/userscripts){:target="_blank"} | peolic | Adds buttons to copy StashID
Utility | [Indexxx Helper Userscript](https://github.com/peolic/userscripts){:target="_blank"} | peolic | This userscript adds useful tools to indexxx.com
Utility | [IAFD Helper Userscript](https://github.com/peolic/userscripts){:target="_blank"} | peolic | This userscript converts dates to YYYY-MM-DD and provides a filter for the studios search page on iafd.com
Utility | [ManyVids Release Year Userscript](https://github.com/peolic/userscripts){:target="_blank"} | peolic | This userscript adds the year to partial video release dates on manyvids.com
Utility | [Brazzers Sub-Studio Userscript](https://github.com/peolic/userscripts){:target="_blank"} | peolic | This userscript adds the sub-studio of a Brazzers scene to the video page
Utility | [1 Pass For All Sites - Better Episode Image](https://github.com/peolic/userscripts){:target="_blank"} | peolic | Attempts to grab a better episode image
Utility | [CumLouder](https://github.com/peolic/userscripts){:target="_blank"} | peolic | Adds site logo and name to video pages. May not work for all videos.
Utility | [ExploitedX Sites - Release Codes](https://github.com/peolic/userscripts){:target="_blank"} | peolic | Shows episode numbers for ExploitedX sites.
Utility | [FTVCash - Better Image](https://github.com/peolic/userscripts){:target="_blank"} | peolic | Attempts to grab a better episode image
Utility | [Gamma Entertainment - Scene Length](https://github.com/peolic/userscripts){:target="_blank"} | peolic | Adds scene length information on Gamma Entertainment sites
Utility | [Kink - Find Duration](https://github.com/peolic/userscripts){:target="_blank"} | peolic | Add "find duration" link to shoot pages
Utility | [MindGeek Scene Trailer](https://github.com/peolic/userscripts){:target="_blank"} | peolic | Shows trailers on MindGeek sites
Utility | [Porn Pros](https://github.com/peolic/userscripts){:target="_blank"} | peolic | Fixes duration on Porn Pros sites

32
docs/Add-ons/Utilities.md Normal file
View File

@ -0,0 +1,32 @@
---
layout: default
title: Utilities
nav_order: 5
parent: Add-ons
---
# Utilities
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
Utilities are other external applications that utilise or interact with Stash in some way.
{: .note }
These utilities are created by third parties and not officially affiliated or supported by the core Stash team. If you have issues, please reach out to the original creators.
Category | Name | Author | Description
-|-|-|-
Companion | [Stash_helper](https://github.com/philpw99/Stash_Helper){:target="_blank"} | philpw99 | Adds some windows-specific tools to Stash (navigate from icon, installation help, etc)
VR | [stash-vr](https://github.com/o-fl0w/stash-vr){:target="_blank"} | o-fl0w | Watch your stash library in VR.
VR | [stash-vr-companion](https://github.com/Tweeticoats/stash-vr-companion){:target="_blank"} | Tweeticoats | Similar to stash-deovr as above but designed as a web app that sits in a docker container next to stash to make it easier to use and add more functionality.

View File

@ -0,0 +1,21 @@
---
layout: default
title: Advanced Tutorials
nav_order: 8
has_children: true
has_toc: false
---
# Advanced Tutorials
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. [Advanced Configuration Options]({{ site.baseurl }}/docs/Advanced-Tutorials/Advanced-Configuration-Options/)
2. [Importing via CSV Using gql-iterate]({{ site.baseurl }}/docs/Advanced-Tutorials/Importing-via-CSV-Using-gql-iterate/)
3. [Manually Editing the Stash Sqlite3 Database]({{ site.baseurl }}/docs/Advanced-Tutorials/Manually-Editing-the-Stash-Sqlite3-Database/)
</details>

View File

@ -0,0 +1,42 @@
---
layout: default
title: Advanced Configuration Options
nav_order: 1
parent: Advanced Tutorials
---
# **Advanced Configuration Options**
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
Some configuration options can not be edited through the UI and should only be used if needed.
Depending on the option they can be configured either by editing the `config.yml` configuration file or by using an enviroment variable or in a few cases by using flags when running stash.
As an example the `port` option can be changed from the default `9999` to `1234` by one of the below methods
* adding `port: 1234` to the config.yml file
* setting the ENV variable **STASH_PORT** to **1234** eg `STASH_PORT=1234 ./stash`
* using the flag **--port** when running stash `./stash --port 1234`
Configuration Option | YML | ENV | FLAG | Description | Comments
---------------------|:---:|:---:|:----:|-------------|:-------------:
host|host|STASH_HOST|--host| The ip address for the host that stash is listening to | default: 0.0.0.0
port|port|STASH_PORT|--port| The port that stash serves to |default: 9999
external host|external_host|STASH_EXTERNAL_HOST|-| Needed in some cases when you use a reverse proxy | [Docs]({{ site.baseurl }}/docs/Network/Reverse-Proxy)
plugins path|plugins_path|-|-|The path to the stash plugins folder|Only use if you need to override the default
scrapers path|scrapers_path|-|-|The path to the scrapers folder|Only use if you need to override the default
custom served folders|custom_served_folders|-|-|Allows configuration of mapped URLs to file system folders|[PR](https://github.com/stashapp/stash/pull/620){:target="_blank"}
maximum upload size|max_upload_size|-|-|Change the maximum size (in MB) for partial imports| default: 1024 (1GB)

View File

@ -0,0 +1,151 @@
---
layout: default
title: Importing via CSV Using gql-iterate
nav_order: 2
parent: Advanced Tutorials
---
# **Importing via CSV Using gql-iterate**
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
If you want to add a collection of performers, tags, studios, etc, and you have a text/spreadsheet list of them, here's the walkthrough of how to do it via a simple CLI method.
---
## Step 1: Install gql-iterate
[gql-iterate repository](https://github.com/efstajas/gql-iterate){:target="_blank"}
`npm install @efstajas/gql-iterate -g`
or
`yarn add @efstajas/gql-iterate -g`
For latest versions of NodeJS (14>) you need to go where you globally install your npm modules (AppData/Roaming/npm/node_modules/@efstajas/gql-iterate/bin) and add the following to the first line of the cli.js file.
`import { createRequire } from 'module'; const require = createRequire(import.meta.url);`
---
## Step 2: Prepare your gql.file if needed
See below for example versions of performers.gql and tags.gql samples that should work for you.
Others can be figured out (with minor changes needeed) from stash/graphql/documents/mutations
These are NOT identical to the original files found above. Compare you'll see how they differ. This is very specific to be used for gql-iterate.
### tags.gql
mutation TagCreate(
$name: String!,
$image: String) {
tagCreate(input: {
name: $name,
image: $image
}) {
id
}
}
### performers.gql
mutation PerformerCreate(
$name: String!,
$url: String,
$gender: GenderEnum,
$birthdate: String,
$ethnicity: String,
$country: String,
$eye_color: String,
$height: String,
$measurements: String,
$fake_tits: String,
$career_length: String,
$tattoos: String,
$piercings: String,
$aliases: String,
$twitter: String,
$instagram: String,
$favorite: Boolean,
$image: String) {
performerCreate(input: {
name: $name,
url: $url,
gender: $gender,
birthdate: $birthdate,
ethnicity: $ethnicity,
country: $country,
eye_color: $eye_color,
height: $height,
measurements: $measurements,
fake_tits: $fake_tits,
career_length: $career_length,
tattoos: $tattoos,
piercings: $piercings,
aliases: $aliases,
twitter: $twitter,
instagram: $instagram,
favorite: $favorite,
image: $image }
)
{ id }
}
If you have a suggested change, please add it below.
---
## Step 3: Prepare your textfile or spreadsheet into a CSV
let's say you have a textfile with these performers (just names and eyecolors for a simple example)
If you have a spreadsheet, add a first line with the column headers, you HAVE to provide all fields listed above in the first line, but you don't have to actually have data in them.
`name,url,gender,birthdate,ethnicity,country,eye_color,height,measurements,fake_tits,career_length,tattoos,piercings,aliases,twitter,instagram,favorite,image`
then add your data if text, or export as CSV if it's a spreadsheet
(gender is not a string, see documentation in code, and favorite is a boolean, if you want to add those)
Alice,,,,,,blue
Betty,,,,,,green
Carter,,,,,,brown
the above lines all go into a file, like performerdata.csv
Gender is complicated due to Stash using GenderEnum.
For tags, you only need the tag title, and if desired, a url to a image
If you're unable to get this to work, then **omit** the **favorite** and **image** property from your CSV header, and from the **performers.gql** file.
---
## Step 4
{: .note }
Must use numerical IP instead of localhost.
Run this CLI command (assumes your files are in current directory and location for gql-interate is in your path)
`gql-iterate --host http://_yourserverIP:portgoeshere_/graphql --input ./performerdata.csv --query ./performers.gql`
It will run and add performers, it won't duplicate existing names, so you can run it multiple times if you want to add more names. Or delete something and readd it, if need be.

View File

@ -0,0 +1,51 @@
---
layout: default
title: Manually Editing the Stash Sqlite3 Database
nav_order: 3
parent: Advanced Tutorials
---
# **Manually Editing the Stash Sqlite3 Database**
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
## Location
The Stash Sqlite3 database file is located at `~/.stash/stash-go.sqlite`.
Before making changes to the Stash sqlite3 database - **please make a backup first!**
You can use the `sqlite3` client to directly edit this file.
---
## Opening the database file
```
cd ~/.stash
sqlite3 stash-go.sqlite
```
---
## Deleting all tags
If you need to delete all tags, you can use the following commands:
```
sqlite> DELETE FROM scenes_tags;
sqlite> DELETE FROM tags;
```
Please note that this will not work if you have Scene market tags (TODO: What to do then?)

22
docs/Beginner-Guides.md Normal file
View File

@ -0,0 +1,22 @@
---
layout: default
title: Beginner Guides
nav_order: 4
has_children: true
has_toc: false
---
# **Beginner Guides**
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. [Guide to Scraping]({{ site.baseurl }}/docs/Beginner-Guides/Guide-To-Scraping/)
2. [Backup & Restore Database]({{ site.baseurl }}/docs/Beginner-Guides/Backup-&-Restore-Database/)
3. [Exclude File Configuration]({{ site.baseurl }}/docs/Beginner-Guides/Exclude-File-Configuration/)
4. [Troubleshooting Video Playback]({{ site.baseurl }}/docs/Beginner-Guides/Troubleshooting-Video-Playback/)
</details>

View File

@ -0,0 +1,58 @@
---
layout: default
title: Backup & Restore Database
nav_order: 2
parent: Beginner Guides
---
# **Backup & Restore Database**
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
## Backup
Always use the UI to create a backup of the DB. As with all live databases **DO NOT** copy manually the database file as a means of backup.
The `Backup` or `Download Backup` tasks are the proper way to create a backup file.
Stash uses an sqlite database with `WAL` mode enabled. This practically means that along with the main db file `stash.go-sqlite` there can be a `-shm` and a `-wal` file present ([more info](https://sqlite.org/wal.html){:target="_blank"}). Even stopping Stash might leave some of these index files present so again **DO NOT** manually copy the database file.
---
## Restore
Assuming you have properly created a backup file you can use it to restore your database if needed.
For the location of the db file check your configuration's `Database Path`.
The restore procedure uses the default `stash-go.sqlite` filename, if you changed that when configuring stash adjust accordingly.
The following steps are recommended when restoring a database file.
1. Create a backup of the current database (Optional)
1. Stop stash
1. Move or delete the `stash-go.sqlite` database file (along with the `-shm` `-wal` `.journal` files if present)
1. Copy the backup file that you want to restore to `stash-go.sqlite`
1. Make sure that you now have a `stash-go.sqlite` file and that no `-shm` `-wal` `.journal` files are present.
1. Start stash
You should now have stash running with a working and restored database.
---
## Advanced Troubleshooting
If you get a database malformed message during upgrade or backup that probably means that the database is already corrupt. One way to get past that is to do a full export and check the error log. If there are not a lot of errors you can then try to do a full import and get a working db with minimal losses. As the full import is destructive proceed with caution.
For cases like this it is better to visit the [discord channel](https://discord.gg/frQ7qBvB3S).

View File

@ -0,0 +1,52 @@
---
layout: default
title: Exclude File Configuration
nav_order: 3
parent: Beginner Guides
---
# **Exclude File Configuration**
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
Given a valid [regex](https://github.com/google/re2/wiki/Syntax){:target="_blank"}, files that match even partially are excluded during the Scan process and are not entered in the database.Also during the Clean task if these files exist in the DB they are removed from it and their generated files get deleted.
Prior to matching both the filenames and patterns are converted to lower case so the match is case insensitive.
Regex patterns can be added in the config file or from the UI.
If you add manually to the config file a restart is needed while from the UI you just need to click the Save button.
When added through the config file directly special care must be given to double escape the `\` character.
Some examples
For the config file you need the following added
```
exclude:
- "sample\\.mp4$"
- "/\\.[[:word:]]+/"
- "c:\\\\stash\\\\videos\\\\exclude"
- "^/stash/videos/exclude/"
- "\\\\\\\\stash\\network\\\\share\\\\excl\\\\"
```
* the first excludes all files ending in `sample.mp4` ( `.` needs to be escaped also)
* the second hidden directories `/.directoryname/`
* the third is an example for a windows directory `c:\stash\videos\exclude`
* the fourth the directory `/stash/videos/exclude/`
* and the last a windows network path `\\stash\network\share\excl\`
and thats how it looks if you use UI (notice the difference for the `\` character)
![regexUI](assets/regexui.png)
{: .note }
Useful [link](https://regex101.com/?flavor=golang){:target="_blank"} to experiment with regexps (we use "Golang" as the regex flavour).

View File

@ -0,0 +1,131 @@
---
layout: default
title: Guide to Scraping
nav_order: 1
parent: Beginner Guides
---
# **Guide to Scraping**
{: .no_toc }
The following is our recommended procedure for new Stash users who want to get info for their scenes as quickly, easily, and as accurately as possible. Pulling info directly from StashDB is still the best option, but unfortunately this will not always be possible. Alternative methods are also covered for when StashDB doesn't have what you need. This is an expanded version of @Scruffy's [pinned post](https://discord.com/channels/559159668438728723/798641040029777980/997893396733640737){:target="_blank"} in the **#stashdb-general** channel in our Discord. Go there if you'd like something more succinct and direct. Hopefully this guide will reduce the pain and frustration for those who are lost and don't know where to start.
**The following sections are in this particular order for a reason, so please follow this guide from the beginning**.
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
## Generate PHashes
1. Navigate to the Settings page (⚙ icon in top right), make sure you're on "Tasks" in the sidebar to the left, then find the first heading "Library" in the middle of the page. Make sure "Generate perceptual hashes" is turned on so PHashes will be created automatically each time you run a scan to add new scenes. This will slow down the scanning process, but for most users [it's worth it](https://guidelines.stashdb.org/docs/getting-started-stashdb/#whats-a-phash){:target="_blank"}. PHashes are the main way to match your scenes with our data on StashDB.
![How to generate PHashes on scan](assets/gen-phashes-on-scan.jpg)
{:style="counter-reset:none"}
1. PHash generation is not turned on by default, so you'll need to generate them manually if you haven't already. This can be done on your entire library on the same Settings --> Tasks page, scroll down to the "Generated Content" heading. Make sure "Perceptual hashes (for deduplication)" is turned on and click the "Generate" button at the top. As long as "Overwrite existing generated files" is turned off, this will only generate missing files and hashes.
![How to generate PHashes manually](assets/gen-phashes-manually.jpg)
---
## Join StashDB
If you haven't set up StashDB in your Stash settings yet, now's the time to do it. Get an invite code in the **#stashdb-invites** channel on [Discord](https://discord.com/channels/559159668438728723){:target="_blank"}. Check the pinned messages (📌 icon in top right) there for details and up-to-date instructions on how to create an account and connect to it from Stash.
### How to connect your account to Stash
1. Go to [https://stashdb.org/register](https://stashdb.org/register) to create a new account using one of the invite codes pinned to **#stashdb-invites**.
- If all of the codes are used/expired, you can ping **@Infinite** in **#stashdb-invites** and politely ask to refresh them. A few other elevated users can also generate an invite code for you.*
2. Once you're logged in to StashDB, click on your username at the top next to **Logged in as**.
3. Copy your unique **API key**.
4. Go to the **Settings** page in Stash ([http://localhost:9999/settings](http://localhost:9999/settings) by default), click **Metadata Providers** on the left side and find **Stash-box Endpoints** at the top.
5. Click **Add** and fill out the form that pops up: paste in your API key from step #3, enter `stashdb.org` (or anything else you'd like) for **Name**, and enter `https://stashdb.org/graphql` as the **GraphQL endpoint**
6. Click **Test Credentials** to make sure you've entered everything correctly, then click **Confirm**.
---
## Use the Scene Tagger
1. Go to your Scenes page on Stash and click the double 🔖 icon to the far right of the search bar. This is your Scene Tagger view and should be your first choice for pulling data, not Identify / Autotag / Filename Parser / URL Scrapers / etc. Always use the Scene Tagger first, the rest are for users with more specific needs who understand the strengths and weaknesses of each tool.
![How to find the Scene Tagger](assets/find-scene-tagger.jpg)
{:style="counter-reset:none"}
1. First, click the "Scrape All" button. This will use your PHashes to find matching scenes on StashDB for every scene on the current page. The "Scrape by fragment" buttons will do the same thing but just for one scene at a time. Also, you may want to change your Scene Tagger settings with the ⚙ icon next to "Scrape All." You can tell it to Merge (keep all) tags, Overwrite (keep only new) tags, or ignore StashDB's tags entirely (leave box unchecked). If you plan on [contributing to StashDB](https://guidelines.stashdb.org/docs/getting-started-stashdb/#contributing-to-stashdb){:target="_blank"}, you should have "Show male performers" turned on to better follow [these guidelines](https://guidelines.stashdb.org/docs/scenes/scene-performers/#missing-scene-performers){:target="_blank"}.
![Running "Scrape All" and "Search"](assets/scrape-all-and-search.jpg)
{:style="counter-reset:none"}
1. If your fingerprint search doesn't return a correct result for your scene, you can try searching with the "Query" field using title, performer, release date, or studio. Try to use as little text as possible to find your scene. Otherwise, unnecessary words that do not match StashDB's info may block correct results. If you can find the matching scene on StashDB.org but can't find it using the Scene Tagger, you can use the scene's [StashID](https://guidelines.stashdb.org/docs/getting-started-stashdb/#whats-a-stashid){:target="_blank"} as your Tagger query.
![How to find a StashID](assets/find-stashid.jpg)
{:style="counter-reset:none"}
1. Be sure to double-check that each selected result matches your scene before clicking the "Save" button at the bottom of each card. **If you're confident that all of your saved matches are accurate**, you may click the "Submit fingerprints" button after you're done. This will add your files' hashes to StashDB to make them easier for other users to find. But again, **make sure your files were matched correctly first**. The button will appear right next to the "Scrape All" button.
---
## Use ThePornDB Scraper
1. If you are absolutely sure a scene isn't on StashDB anywhere, the next easiest method is to scrape from ThePornDB. They have significantly more scenes than StashDB thanks to their automated scrapers, but their info isn't always as complete or accurate compared to StashDB's manually curated approach. They also don't host PHashes so matching scenes can be trickier as well.
{:style="counter-reset:none"}
1. First step is to make an account at [metadataapi.net](https://metadataapi.net/register){:target="_blank"} which is ThePornDB's website. With your account created, navigate to your [API Tokens](https://metadataapi.net/user/api-tokens){:target="_blank"} page. Type "stash" as your token's name (or whatever you'd prefer), make sure the "read" permission is checked (you don't need the others), and click the "Create" button. A pop-up will display your newly created token. **Save your API token somewhere so you can find it later!** It will not be visible on ThePornDB's website after you close the pop-up. If you lose it, you may need to create a new one and repeat this entire setup process. This can be done in a password manager, notes app, or a well-placed text file.
![How to create an API token on ThePornDB](assets/create-tpdb-token.jpg)
{:style="counter-reset:none"}
1. Next, we need to download and setup our TPDB scraper for Stash. The scraper file we need is at [this page](https://raw.githubusercontent.com/stashapp/CommunityScrapers/master/scrapers/ThePornDB.yml){:target="_blank"} in our scrapers repo. Right-click the page in your browser, select "Save as...", and save it somewhere you can easily find. We'll move it into our scrapers folder later. Now open your saved file with a basic text editor like Notepad for Windows users. Your browser's defaults should have saved it as a text file for you, so simply opening the file should automatically use your default text editor. Find the two lines at the very bottom that start with `#- Key: Authorization` and `# Value: Bearer`. First remove the `#` characters before `- Key:` and `Value:`. The string starting with `zUo...` is a placeholder. Replace it with your own API key, then save your changes. It should now look like this:
```yaml
- Key: Authorization # Uncomment and add a valid API Key after the `Bearer ` part
Value: Bearer YOUR-API-KEY-HERE
```
{:style="counter-reset:none"}
1. If you saved the file with your browser's default filename, we'll need to rename it. Currently, it should be named `ThePornDB.yml.txt`. We'll need to rename it to remove `.txt` from the end, giving us the filename `ThePornDB.yml`. If you are using Windows, you may need to change the view settings in Windows Explorer to make file extensions visible before changing the filename.
{:style="counter-reset:none"}
1. Now we need to move our scraper file into the right folder for Stash to see it. The exact location is going to be a little different for everybody depending on where and how you've installed Stash. By default, it should go in a folder named `scrapers` in the same directory as your `config.yml` file. In the example directory below, you'll also see `ThePornDB_api-key.txt` where I've saved my API key for safekeeping in case I need it again later. If you're having trouble finding where this folder is, feel free to ask in the **#help** channel on [Discord](https://discord.com/channels/559159668438728723){:target="_blank"}.
![Location of "scrapers" folder](assets/scrapers-folder-location.jpg)
{:style="counter-reset:none"}
1. For our last step of setup, we need to go back into Stash. Navigate to the Settings page, click on "Metadata Providers" in the sidebar to the left, and scroll down to the "Scrapers" heading. Click the "🔃 Reload scrapers" button just below it. Now click "> Scene Scrapers" to expand that list of recognized scraper files. If you've done everything right, you should find your TPDB scraper here.
![ThePornDB in the list of scene scrapers](assets/tpdb-scene-scraper-entry.jpg)
{:style="counter-reset:none"}
1. Finally, we can use the Scene Tagger to scrape from ThePornDB. First you'll need to switch to "ThePornDB" as the Tagger's source. It will always default to a Stash-Box source, so you'll need to switch it every time you need something else. Use the same process here as [we used with StashDB and the Scene Tagger](#use-the-scene-tagger). The "Scrape All" and "Scrape by fragment" buttons will search using your filename and your OSHash. If these don't give you the right results, you can try the "Search" button and customize your search terms as needed. Remember to use as few terms as possible in your search because unneccessary words that don't match ThePornDB's info may block correct results. You could also [search TPDB itself](https://metadataapi.net/scenes){:target="_blank"} to find your scene first if you're having trouble with the Tagger's search or if you don't have the right search terms in your filename. Then, you can copy and paste the scene's URL slug (everything in the address bar after `https://metadataapi.net/scenes/`) into the Scene Tagger to quickly search with the exact same studio and title on TPDB. You can also copy-paste additional terms from TPDB's page if you need to narrow down the Tagger's results more.
![Tagger source set to ThePornDB](assets/tagger-source-tpdb.png)
- **For those wanting to contribute to StashDB**: As noted before, ThePornDB leans heavily on automated scrapers to pull all of their info. Often that data is incomplete or inaccurate compared to what we'd want on StashDB. Before you [submit your scene to StashDB](https://guidelines.stashdb.org/docs/getting-started-stashdb/#submitting-drafts-to-stashdb){:target="_blank"}, you'll need to double-check your info, clean it up a bit first, and make sure you're following [these guidelines](https://guidelines.stashdb.org/docs/scenes/){:target="_blank"}. Submitting to StashDB is discussed further in the [last step](#submit-to-stashdb) of this guide.
---
## Use Site-Specific Scrapers
1. If you've already [tried StashDB](#use-the-scene-tagger), you've already [tried ThePornDB](#use-theporndb-scraper), and you still want to scrape a site directly, you can try using a site-specific scraper. However, every scraper is going to work differently. Some will need Python installed. Others will need you to set a user agent or a Chrome CDP path. A handful will need to be edited and configured first. Only a few can search a studio's website for the right scene. The entire process is much more advanced and is different for each scraper, which is why we recommend StashDB and TPDB first for new users. They can all be found in [the same repo](https://github.com/stashapp/CommunityScrapers){:target="_blank"} where we found ThePornDB's scraper. You can download them individually like we did with the TPDB scraper or you can download the entire repo as a zip archive.
{:style="counter-reset:none"}
1. First you'll want to search [this page](https://github.com/stashapp/CommunityScrapers/blob/master/SCRAPERS-LIST.md){:target="_blank"} for your website to see if we have a scraper for it and to get an idea of what its requirements are. For most of these scrapers, you'll need to find and save a studio URL to your scene first. If you've managed to set up a fragment scraper, you can click on the "Scrape with..." button in the scene's edit tab to select your scraper. Typically fragment scrapers are better than URL scrapers, such as the MindGeekAPI scraper. Otherwise, you'll need to click the button at the far right end of the URL field to run a URL scraper. It should light up when the field is filled with a URL that matches one of your installed scrapers. That's about as detailed as we can get in this guide for new users. If you've tried StashDB, tried TPDB, checked the relevant documentation, checked your logs, and still can't get one of these scrapers working, you can ask for help on [Discord](https://discord.com/channels/559159668438728723){:target="_blank"} in the **#help** channel or in **#the-scraping-initiative**.
---
## Submit to StashDB
1. If you're certain a scene isn't on StashDB and you've found the info using ThePornDB or some other scraper, please consider submitting it to StashDB yourself. That way nobody else will have to duplicate the same work you've done for that particular scene if they can match their [PHash](#generate-phashes) with yours. You'll need to ask for edit privileges in our [Discord](https://discord.com/channels/559159668438728723){:target="_blank"} and follow the guidelines on [this website](https://guidelines.stashdb.org/docs/scenes/){:target="_blank"}. In particular, please note that [not every scene can be added to StashDB](https://guidelines.stashdb.org/docs/scenes/adding-scenes/){:target="_blank"} at this time. Some [studios aren't allowed](https://guidelines.stashdb.org/docs/scenes/movies-dvds/){:target="_blank"} and [full movies likely won't be eligible](https://guidelines.stashdb.org/docs/scenes/movies-dvds/){:target="_blank"} either.
{:style="counter-reset:none"}
1. Also, please don't blindly submit data from ThePornDB. You should verify the data is correct and complete first, make sure the URL is right, check for any [missing performers](https://guidelines.stashdb.org/docs/scenes/scene-performers/#missing-scene-performers){:target="_blank"}, and look up any relevant [guidelines](https://guidelines.stashdb.org/docs/scenes/){:target="_blank"} if something else seems funky to you. You can update your data within Stash before submitting a draft or you can edit the draft on StashDB itself before creating the edit. You should also have the studio URL now, so you should also compare with that page manually or even scrape again with a site-specific scraper as explained in the [previous step](#use-site-specific-scrapers) of this guide. Also please note in your [edit comment](https://guidelines.stashdb.org/docs/scenes/getting-started-scenes/#scene-edit-comments){:target="_blank"} where your data is coming from.

View File

@ -0,0 +1,53 @@
---
layout: default
title: Troubleshooting Video Playback
nav_order: 4
parent: Beginner Guides
---
# **Troubleshooting Video Playback**
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
## Inspecting files
You can use the ffprobe command to gather useful information about a video file:
```
ffprobe -show_format -show_streams big_buck_bunny.mkv
```
This can be useful for example, when filing bug reports, or discussing in chat.
---
## Remuxing files
Another good test, is to see if remuxing the file into a new video file helps:
```
ffmpeg -i big_buck_bunny.mkv -c:v copy -c:a copy remuxed_file.mkv
```
---
## Extracting a sample of a video
If you are asked for a sample of a video (e.g. for developers to analyse), you can use
```
ffmpeg -ss 120 -i big_buck_bunny.mkv -t 30 -c:v copy -c:a copy 30_second_sample.mkv
```
The above command starts at the 120-second marker, and takes a 30-second sample of the video file.

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -0,0 +1,121 @@
---
layout: default
title: Frequently Asked Questions
nav_order: 9
---
# **Frequently Asked Questions (FAQ)**
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
## Logging in
### How do I recover a forgotten username or password?
Stash saves login credentials in the `config.yml` file. You must reset both login and password if you have forgotten your password by doing the following:
- Close your Stash process;
- Open the `config.yml` file found in your Stash directory with a text editor;
- Delete the `login` and `password` lines from the file and save;
- Stash authentication should now be reset with no authentication credentials.
### How can I connect to my server from elsewhere within my network?
Find the local IP address of your Stash Server (guides for [Windows](https://support.microsoft.com/en-us/windows/find-your-ip-address-in-windows-f21a9bbc-c582-55cd-35e0-73431160a1b9){:target="_blank"}, [MacOS](https://support.apple.com/guide/mac-help/find-your-computers-name-and-network-address-mchlp1177/11.0/mac/11.0){:target="_blank"}, [Linux](https://wiki.archlinux.org/title/Network_configuration#IP_addresses){:target="_blank"}). Then, on another device on your local network, open a browser to http://SERVER.IP.ADDRESS.HERE:9999/
See [this article]({{ site.baseurl }}/docs/Network/Authentication-Required-When-Accessing-Stash-From-the-Internet#alternative-and-safe-methods-to-access-your-stash) for ideas on accessing your stash from outside your network.
### How do I serve Stash over SSL/TLS (HTTPS)?
This is typically accomplished by putting Stash behind a reverse proxy, such as Nginx or Caddy. Stash can also serve SSL directly.
To use the built-in SSL:
First you must generate a SSL certificate and key combo. Here is an example using openssl:
`openssl req -x509 -newkey rsa:4096 -sha256 -days 7300 -nodes -keyout stash.key -out stash.crt -extensions san -config <(echo "[req]"; echo distinguished_name=req; echo "[san]"; echo subjectAltName=DNS:stash.server,IP:127.0.0.1) -subj /CN=stash.server`
This command would need customizing for your environment. [This link](https://stackoverflow.com/questions/10175812/how-to-create-a-self-signed-certificate-with-openssl){:target="_blank"} might be useful.
Once you have a certificate and key file name them `stash.crt` and `stash.key` and place them in the same directory as the `config.yml` file, or the `~/.stash` directory. Stash detects these and starts up using HTTPS rather than HTTP.
### How do I serve Stash in a Subpath?
The basepath defaults to `/`. When running stash via a reverse proxy in a subpath, the basepath can be changed by having the reverse proxy pass `X-Forwarded-Prefix` (and optionally `X-Forwarded-Port`) headers. When detects these headers, it alters the basepath URL of the UI.
---
## Working with Content
### What's the best way to add metadata to Stash?
* StashDB is a service that allows for crowdsourcing of porn metadata. Check the pinned messages in the #stashbox-invites channel in Discord to start using it.
* Stash includes a single scraper, but you can add more via the [CommunityScrapers repository](https://github.com/stashapp/CommunityScrapers){:target="_blank"}. See the README for installation and usage instructions.
### How do I rename or relocate a library folder or scenes?
**If you need to move or rename a folder**, you can remove the existing directory from your library and re-add the new location.
**If you need to move or rename scenes**, you can simply move the scenes and rescan, being sure the new location is in your library.
Stash will recognize the files on the next scan and re-link to the new location.
**Do not run a Clean in between these steps, or you will lose the information from your relocated folders** (your files will not be affected).
Stash uses 2 different methods to track your scenes: oshash and filename/path. Changing ONE (either moving OR editing/altering the file) will be recovered on your next scan. Stash will update the 'wrong' entry, and restore it knowing 2 things about each file. If both are changed at once (like a reencode and a rename), Stash has no way to associate the file with the old version and will add the file as new entry, **losing** the old metadata associated with it. (The data isn't deleted until a clean though)
> **⚠️ Note:** Don't forget to click `Save` after updating these directories!
### How do I add galleries?
For gallery-related issues check the relevant Documentation [section]({{ site.baseurl }}/docs/In-app-Manual/Galleries/).
---
## Scraping
### Scrape behind paywall
A regular scraper can only scrape information from webpages that are open to the public access. If you want to scrape a webpage that requires login or behind a paywall, you need to use the "Visible CDP" technique. So far this technique was tested and passed under Windows 10 only.
Normal CDP scraping will launch a headless chrome browser, which will not show up for any user interactions. "Visible CDP" turns the "headless chrome" into a "visible" instance.
Steps:
1. Prepare your scraper's .yml file and make sure it's valid and working. Your scraper should have the following setting inside:
```
driver:
useCDP: true
```
2. Run a command console. Go to the Chrome's binary directory and run `chrome.exe --remote-debugging-port=9222`. This will launch a special Chrome instance that Stash Scrapers can control later on.
3. In Stash, make sure that the "Chrome CDP Path" setting is `http://localhost:9222/json/version`.
4. Use the special Chrome instance you launched earlier, go to the webpage you want to scrape, type in your user/pass or pass any other human tests, until you see the page with desired content.
5. Paste the webpage's URL in your Stash scene and start scraping. It should get the information correctly.
---
## Troubleshooting
### Known Issues
- Performer images uploaded in WebP format will not display on versions of Safari prior to version 13 or on anything earlier than MacOS Big Sur. This is a [limitation of Safari](https://caniuse.com/webp){:target="_blank"}. As a workaround, ensure you are uploading performer images in jpg or png format.
### Stash is showing a "FFMPEG Not Found" error
If Stash is unable to find or download FFMPEG, then download and install it yourself.
The `ffmpeg.exe` and `ffprobe.exe` files should be placed in `C:\Users\YourUsername\.stash` on Windows.
The `ffmpeg` and `ffprobe` files should be placed in `~/.stash` on macOS / Linux.
---
## Other FAQs
### I have a question not answered here.
Join the Stash [Discord server](https://discord.gg/2TsNFKt){:target="_blank"}.

54
docs/Getting-Started.md Normal file
View File

@ -0,0 +1,54 @@
---
layout: default
title: Getting Started
nav_order: 2
has_children: true
has_toc: false
---
# **Getting Started**
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
<style>
.no-border > img {
border: none;
}
</style>
---
## Downloading Stash
| Windows | macOS | Linux | Docker |
| :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
| <span class="no-border">![Windows](Getting-Started/assets/windows_logo.svg)</span> | <span class="no-border">![macOS](Getting-Started/assets/mac_logo.svg)</span> | <span class="no-border">![Linux](Getting-Started/assets/linux_logo.svg)</span> | <span class="no-border">![Docker](Getting-Started/assets/docker_logo.svg)</span> |
| [Latest Release](https://github.com/stashapp/stash/releases/latest/download/stash-win.exe){:target="_blank"} <br /> <sup><sub>[Development Preview](https://github.com/stashapp/stash/releases/download/latest_develop/stash-win.exe){:target="_blank"}</sub></sup> | [Latest Release (Apple Silicon)](https://github.com/stashapp/stash/releases/latest/download/stash-macos-applesilicon){:target="_blank"} <br /> <sup><sub>[Development Preview (Apple Silicon)](https://github.com/stashapp/stash/releases/download/latest_develop/stash-macos-applesilicon){:target="_blank"}</sub></sup> <br />[Latest Release (Intel)](https://github.com/stashapp/stash/releases/latest/download/stash-macos-intel){:target="_blank"} <br /> <sup><sub>[Development Preview (Intel)](https://github.com/stashapp/stash/releases/download/latest_develop/stash-macos-intel){:target="_blank"}</sub></sup> | [Latest Release (amd64)](https://github.com/stashapp/stash/releases/latest/download/stash-linux){:target="_blank"} <br /> <sup><sub>[Development Preview (amd64)](https://github.com/stashapp/stash/releases/download/latest_develop/stash-linux){:target="_blank"}</sub></sup> <br /> [More Architectures...](https://github.com/stashapp/stash/releases/latest){:target="_blank"} | [Instructions](https://github.com/stashapp/stash/blob/master/docker/production/README.md){:target="_blank"} <br /> <sup><sub> [Sample docker-compose.yml](https://github.com/stashapp/stash/blob/master/docker/production/docker-compose.yml){:target="_blank"}</sub></sup> |
---
## Installing Stash
1. [Installing on Windows]({{ site.baseurl }}/docs/Getting-Started/Installation/Installing-on-Windows)
2. [Installing on macOS]({{ site.baseurl }}/docs/Getting-Started/Installation/Installing-on-macOS)
3. [Installing on Linux]({{ site.baseurl }}/docs/Getting-Started/Installation/Installing-on-Linux)
4. [Installing on Synology NAS]({{ site.baseurl }}/docs/Getting-Started/Installation/Installing-on-Synology-NAS)
5. [Installing on FreeNAS/TrueNAS]({{ site.baseurl }}/docs/Getting-Started/Installation/Installing-on-FreeNAS-TrueNAS)
6. [Unraid Support]({{ site.baseurl }}/docs/Getting-Started/Installation/Unraid-Support)
---
## First Steps
1. [Setting Up Content Libraries]({{ site.baseurl }}/docs/Getting-Started/First-Steps/#setting-up-content-libraries)
2. [Configuring Scan Options]({{ site.baseurl }}/docs/Getting-Started/First-Steps/#configuring-scan-options)
3. [Initiating Scan]({{ site.baseurl }}/docs/Getting-Started/First-Steps/#initiating-scan)

View File

@ -0,0 +1,58 @@
---
layout: default
title: First Steps
nav_order: 2
parent: Getting Started
---
# **First Steps**
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
## Setting Up Content Libraries
1. Open your Stash.
2. Go to `Settings` > `Library` and click `Add Directory`.
3. To change the drive letter/volume, just type it in the field.
![Drive location](assets/drive_location.png)
4. Select the folder containing the content you want to add. It will select the folder and all sub-folders.
5. Click `Confirm`.
---
## Configuring Scan Options
1. Open your Stash.
2. Go to `Settings` > `Tasks`.
![Scan options](assets/scan_options.png)
| Option | Description |
| -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| Generate previews | Generates video previews which play when hovering over a scene. |
| Generate animated image previews | Generates animated webp previews. Only required if the Preview Type is set to Animated Image. Requires Generate previews to be enabled. |
| Generate scrubber sprites | Generates sprites for the scene scrubber. |
| Generate perceptual hashes | Generates perceptual hashes for scene deduplication and identification. |
| Generate thumbnails for images | Generates thumbnails for image files. |
{:style="counter-reset:none"}
3. Select options you want based on your preferences, but every option increases time the scan will take.
4. You can stop and re-scan it will ignore the files that are already scanned and just scan new or missing files.
---
## Initiating Scan
1. Open your Stash.
2. Go to `Settings` > `Tasks`.
3. Click `Scan` to start scanning for the content from your setup libraries or `Selective Scan...` if you want to scan a specific location.

View File

@ -0,0 +1,26 @@
---
layout: default
title: Installation
nav_order: 1
has_children: true
has_toc: false
parent: Getting Started
---
# **Installation**
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. [Installing on Windows]({{ site.baseurl }}/docs/Getting-Started/Installation/Installing-on-Windows)
2. [Installing on macOS]({{ site.baseurl }}/docs/Getting-Started/Installation/Installing-on-macOS)
3. [Installing on Linux]({{ site.baseurl }}/docs/Getting-Started/Installation/Installing-on-Linux)
4. [Installing on Synology NAS]({{ site.baseurl }}/docs/Getting-Started/Installation/Installing-on-Synology-NAS)
5. [Installing on FreeNAS/TrueNAS]({{ site.baseurl }}/docs/Getting-Started/Installation/Installing-on-FreeNAS-TrueNAS)
6. [Unraid Support]({{ site.baseurl }}/docs/Getting-Started/Installation/Unraid-Support)
</details>

View File

@ -0,0 +1,66 @@
---
layout: default
title: Installing on FreeNAS/TrueNAS
nav_order: 5
parent: Installation
grand_parent: Getting Started
---
# **Installing on FreeNAS/TrueNAS**
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
## Caveats and assumptions
* the method documented here has only been given cursory testing, so there may be compatibility problems
* this method assumes that stash will be run within a previously created iocage jail - the process to create and configure the jail is not included here
* if the jail is in NAT mode, ensure that port 9999 is forwarded to your TrueNAS host
* the alternative to this method is to compile from source, which is a more involved process and not documented here
---
## Linux compatibility
In order for the `stash-linux` binary to work in a FreeBSD system, Linux compatibility must be enabled both in the system and the jail. To enable Linux compatibility:
1. navigate to `System -> Tunables` in the TrueNAS Web UI
2. click `Add` and enter the following:
* Variable: `linux_enable`
* Value: `YES`
* Type: `rc.conf`
3. Click submit.
4. In a shell in your iocage jail, edit `/etc/rc.conf` to add:
```
enable_linux="YES"
```
5. Reboot the system.
---
## ffmpeg/ffprobe
`ffmpeg` can be downloaded using `pkg install ffmpeg`. For some reason, stash is unable to find the `ffmpeg` and `ffprobe` binaries even after installing them. To work around this, create symbolic links to the binaries in `$HOME/.stash`:
```
ln -s /usr/local/bin/ffmpeg ~/.stash/ffmpeg
ln -s /usr/local/bin/ffprobe ~/.stash/ffprobe
```
---
## Downloading and running
Download `stash-linux` for your chosen release. Make sure to enable execution with: `chmod +x stash-linux`
Run with: `./stash-linux` (assuming the binary is in the current directory)

View File

@ -0,0 +1,26 @@
---
layout: default
title: Installing on Linux
nav_order: 3
parent: Installation
grand_parent: Getting Started
---
# **Installing on Linux**
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
1. Run `./stash-linux` or `./stash-linux-arm32v6` or `./stash-linux-arm32v7` or `./stash-linux-arm64v8` from the terminal on Linux depending on your architecture.
- If you have trouble, try running `chmod u+x stash-linux` or `chmod u+x stash-linux-arm32v6` or `chmod u+x stash-linux-arm32v7` or `chmod u+x stash-linux-arm64v8` to make the file executable.
2. It should open a browser tab http://localhost:9999 to get started.

View File

@ -0,0 +1,229 @@
---
layout: default
title: Installing on Synology NAS
nav_order: 4
parent: Installation
grand_parent: Getting Started
---
# **Installing on Synology NAS**
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
## Foreword
Synology devices comes in two categories : those who support containerization through Docker, and those who don't. To see in which category you stand, refer to the "Applied Models" section of [the Docker Package page](https://www.synology.com/dsm/packages/Docker){:target="_blank"}.
Now, follow the installation instructions based on whether you [can use Docker](#to-install-stash-with-docker) or [you cannot use Docker](#to-install-stash-without-docker).
---
## To install Stash with Docker
- Make sure [the Docker app is installed](https://blog.pavelsklenar.com/how-to-install-and-use-docker-on-synology/){:target="_blank"} and running correctly.
- [Search the registry for stash](https://hub.docker.com/r/stashapp/stash){:target="_blank"} and install.
- Create a stash image with the following set up in 'advanced options'
### "Volume" Tab
| File/Folder | Mount Path | Description |
|---|---|---|
| docker/Stash/generated | /generated | Thumbnails, clips, etc |
| docker/Stash/metadata | /metadata | Database |
| docker/Stash/config | /root/.stash | Configuration Files |
| docker/Stash/cache | /cache | Cache Files |
| (where your porn lives) | /data | Location of your porn |
### "Environment" Tab
(These will need to be the same as the Volumes you created in the "Volume" tab.
| variable | Value |
|---|---|
| PATH | (keep as is) |
| STASH_CACHE | /cache |
| STASH_METADATA | /metadata |
| STASH_GENERATED | /generated |
| STASH_STASH | /data |
| | | |
### "Port" Tab
You will need to set a default port in the "Port" tab, otherwise Docker will assign a different port every time Stash is launched. Leave the container port as-is.
### "Network" Tab
Make sure that "Use The Same Network As Docker Host" is checked.
(thanks to backer Herelam80 for these instructions)
---
## To install Stash without Docker
Warning : this method uses SSH to run command lines on the NAS. If you are unfamiliar with SSH or linux command lines, I suggest you not to go further, as making a mistake in the SSH session can really screw your NAS.
This is intended to work on DSM 7.0 and later. It will not work on any version prior to 7.0.
### Enable SSH
In DSM, navigate to `Control Panel > Terminal & SNMP` and check the `enable SSH service` box.
_Note : only members of the adminstrator group are able to use SSH, you'll need one of them to complete the installation._
### Create a user that will run stash
1. In DSM, navigate to `Control Panel > User & Group`
2. Click on the `Create` button
3. Give it a name (eg _stash_) and Generate a Random Password (you won't need it later)
4. Click `Next` until you are on the "Assign shared folders permissions" screen
5. Assign the read write permission to your porn folder (the write permission is needed to allow the deletion of clips from the stash app)
6. Click `Next` until you are on the "Assign application permissions" screen
7. Check `Deny` for all applications
6. Click `Next` until you can click on `Done`
### Connect to your NAS
With your terminal, connect to your NAS using an account that is part of the _administrator_ group.
1. SSH to your NAS
```
ssh admin@nas-hostname
```
{:style="counter-reset:none"}
2. navigate to the _stash_ user home directory
```
cd ../stash/
```
### Download Stash
{:style="counter-reset:none"}
3. Download the lastest ARM64 version of stash and its checksum from github
```
wget https://github.com/stashapp/stash/releases/download/[version]/stash-linux-arm64v8
wget https://github.com/stashapp/stash/releases/download/[version]/CHECKSUMS_SHA1
```
{:style="counter-reset:none"}
4. Perform the checksum validation
```
sha1sum -c --ignore-missing CHECKSUMS_SHA1
# you should see a line that says `stash-linux-arm64v8: OK`
```
{:style="counter-reset:none"}
5. Clean up unnecessary file
```
rm CHECKSUMS_SHA1
```
_Note : DO NOT run stash yet or it will generated a bunch of files/folders where we don't want them_
### Download ffmpeg
{:style="counter-reset:none"}
6. Donwload the ARM64 static build of ffmpeg and its checksum
```
wget https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-arm64-static.tar.xz
wget https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-arm64-static.tar.xz.md5
```
{:style="counter-reset:none"}
7. Perform the checksum validation
```
md5sum -c ffmpeg-release-arm64-static.tar.xz.md5
# you should see a line that says `ffmpeg-release-arm64-static.tar.xz: OK`
```
{:style="counter-reset:none"}
8. Unpack & move ffmpeg to the .stash/ folder
```
tar xvf ffmpeg-release-arm64-static.tar.xz
mv ffmpeg-4.4-arm64-static/ffmpeg ffmpeg-4.4-arm64-static/ffprobe .stash/
```
{:style="counter-reset:none"}
9. Clean up unnecessary files
```
rm ffmpeg-release-arm64-static.tar.xz
rm ffmpeg-release-arm64-static.tar.xz.md5
rm -rd ffmpeg-4.4-arm64-static/
```
{:style="counter-reset:none"}
10. Prepare a python environment (for scrapers)
```
sudo -H python -m ensurepip --upgrade
python3 -m venv stash-env
source stash-env/bin/activate
pip3 install pipreqs
```
Whenever you install a new scraper, do the following from the _stash_ user home directory
```
source stash-env/bin/activate
cd your_scraper_directory
pipreqs .
sudo pip3 install -r requirements.txt
```
### Configure your NAS to run Stash upon startup
{:style="counter-reset:none"}
10. Create the service file by running `cat > /etc/systemd/system/stash.service`, copy/pasting the following, and hitting CTRL+D when it's done to save the file (hit again if you are not back to the prompt) :
```
[Unit]
Description=Run Stash at startup
After=network.target
[Service]
Type=simple
User=stash
ExecStart=/bin/bash -c '\
source /var/services/homes/stash/stash-env/bin/activate stash-env; \
exec /var/services/homes/stash/stash-linux-arm64v8'
Restart=on-failure
[Install]
WantedBy=multi-user.target
# end
```
{:style="counter-reset:none"}
11. Start and activate the service
```
sudo systemctl start stash.service
sudo systemctl enable stash.service
```
### Verify that it is working
You can now access to stash by navigating to your NAS url on port 9999 : `http://nas-hostname:9999`

View File

@ -0,0 +1,30 @@
---
layout: default
title: Installing on Windows
nav_order: 1
parent: Installation
grand_parent: Getting Started
---
# **Installing on Windows**
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
1. Run the executable (typically `stash-win.exe`).
- Running the app might present a security prompt since the binary isn't signed yet. Just click more info and then the `run anyway` button.
2. It should open a browser tab http://localhost:9999 to get started.
3. The program will show an icon in your notification area. You can access some quick links or quit the server from there.
{: .note }
Some Windows 11 versions might open Stash via terminal. You can bypass that by running the program as administrator or [use a shortcut to run it via conhost.exe](https://github.com/stashapp/stash/issues/2598){:target="_blank"}

View File

@ -0,0 +1,38 @@
---
layout: default
title: Installing on macOS
nav_order: 2
parent: Installation
grand_parent: Getting Started
---
# **Installing on macOS**
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
## Apple Silicon
1. Run `./stash-macos-applesilicon` from the terminal on macOS.
- If you have trouble, try running `chmod u+x stash-macos-applesilicon` to make the file executable.
2. It should open a browser tab http://localhost:9999 to get started.
3. The program will show an icon in your notification area. You can access some quick links or quit the server from there.
---
## Intel
1. Run `./stash-macos-intel` from the terminal on macOS.
- If you have trouble, try running `chmod u+x stash-macos-intel` to make the file executable.
2. It should open a browser tab http://localhost:9999 to get started.
3. The program will show an icon in your notification area. You can access some quick links or quit the server from there.

View File

@ -0,0 +1,31 @@
---
layout: default
title: Unraid Support
nav_order: 6
parent: Installation
grand_parent: Getting Started
---
# **Unraid Support**
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
Currently stash is supported in Unraid via the [Community Applications](https://forums.unraid.net/topic/38582-plug-in-community-applications/){:target="_blank"} plugin.
As of April 6, 2020 the Stash app is now published to the container directory as well.
Please install and use StashApp with that template.
Unraid specific support can be found [here](https://forums.unraid.net/topic/90861-support-stash-corneliousjd-repo/){:target="_blank"}.
For users that want to try the development branch of stash you can change the repo to this one `stashapp/stash:development`.

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="256px" height="185px" viewBox="0 0 256 185" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
<g>
<path d="M250.715745,70.4971666 C244.951102,66.4973277 231.740464,64.997388 221.412146,66.9973071 C220.211179,56.9977092 214.68673,48.2480609 205.078993,40.4983724 L199.554544,36.4985331 L195.711449,42.248302 C190.90758,49.7480006 188.505646,60.2475786 189.226226,70.2471769 C189.46642,73.7470364 190.667387,79.9967847 194.270289,85.496564 C190.90758,87.4964838 183.941971,89.996383 174.814621,89.996383 L1.15476998,89.996383 L0.674383104,91.9963028 C-1.00697093,101.9959 -1.00697093,133.244645 18.6888904,157.243681 C33.5808831,175.492947 55.6786788,184.742575 84.7420842,184.742575 C147.672763,184.742575 194.270289,154.493791 216.127891,99.7459909 C224.774854,99.9959813 243.269748,99.7459909 252.637292,80.996745 C252.877486,80.4967649 253.357872,79.4968046 255.039227,75.7469554 L256,73.7470364 L250.715745,70.4971666 L250.715745,70.4971666 Z M139.986573,0 L113.565295,0 L113.565295,24.9989952 L139.986573,24.9989952 L139.986573,0 L139.986573,0 Z M139.986573,29.9987943 L113.565295,29.9987943 L113.565295,54.9977896 L139.986573,54.9977896 L139.986573,29.9987943 L139.986573,29.9987943 Z M108.761427,29.9987943 L82.3401495,29.9987943 L82.3401495,54.9977896 L108.761427,54.9977896 L108.761427,29.9987943 L108.761427,29.9987943 Z M77.5362814,29.9987943 L51.1150037,29.9987943 L51.1150037,54.9977896 L77.5362814,54.9977896 L77.5362814,29.9987943 L77.5362814,29.9987943 Z M46.311135,59.9975886 L19.8898576,59.9975886 L19.8898576,84.9965839 L46.311135,84.9965839 L46.311135,59.9975886 L46.311135,59.9975886 Z M77.5362814,59.9975886 L51.1150037,59.9975886 L51.1150037,84.9965839 L77.5362814,84.9965839 L77.5362814,59.9975886 L77.5362814,59.9975886 Z M108.761427,59.9975886 L82.3401495,59.9975886 L82.3401495,84.9965839 L108.761427,84.9965839 L108.761427,59.9975886 L108.761427,59.9975886 Z M139.986573,59.9975886 L113.565295,59.9975886 L113.565295,84.9965839 L139.986573,84.9965839 L139.986573,59.9975886 L139.986573,59.9975886 Z M171.211719,59.9975886 L144.790441,59.9975886 L144.790441,84.9965839 L171.211719,84.9965839 L171.211719,59.9975886 L171.211719,59.9975886 Z" fill="#2396ED" fill-rule="nonzero"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -0,0 +1,121 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="256px" height="295px" viewBox="0 0 256 295" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
<defs>
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="filter-1">
<feOffset dx="0" dy="0" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="6.5" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
</filter>
<linearGradient x1="48.5477412%" y1="115.276174%" x2="51.0473804%" y2="41.3637237%" id="linearGradient-2">
<stop stop-color="#FFEED7" offset="0%"></stop>
<stop stop-color="#BDBFC2" offset="100%"></stop>
</linearGradient>
<linearGradient x1="54.4065463%" y1="2.40410545%" x2="46.1753957%" y2="90.5422349%" id="linearGradient-3">
<stop stop-color="#FFFFFF" stop-opacity="0.8" offset="0%"></stop>
<stop stop-color="#FFFFFF" stop-opacity="0" offset="100%"></stop>
</linearGradient>
<linearGradient x1="51.859653%" y1="88.2477484%" x2="47.9469396%" y2="9.74782136%" id="linearGradient-4">
<stop stop-color="#FFEED7" offset="0%"></stop>
<stop stop-color="#BDBFC2" offset="100%"></stop>
</linearGradient>
<linearGradient x1="49.9251097%" y1="85.4900173%" x2="49.9236843%" y2="13.8109272%" id="linearGradient-5">
<stop stop-color="#FFEED7" offset="0%"></stop>
<stop stop-color="#BDBFC2" offset="100%"></stop>
</linearGradient>
<linearGradient x1="53.9014071%" y1="3.10177585%" x2="45.9555354%" y2="93.8949571%" id="linearGradient-6">
<stop stop-color="#FFFFFF" stop-opacity="0.65" offset="0%"></stop>
<stop stop-color="#FFFFFF" stop-opacity="0" offset="100%"></stop>
</linearGradient>
<linearGradient x1="45.5928761%" y1="5.47459052%" x2="54.811359%" y2="93.5235162%" id="linearGradient-7">
<stop stop-color="#FFFFFF" stop-opacity="0.65" offset="0%"></stop>
<stop stop-color="#FFFFFF" stop-opacity="0" offset="100%"></stop>
</linearGradient>
<linearGradient x1="49.9844987%" y1="89.8452442%" x2="49.9844987%" y2="40.6316864%" id="linearGradient-8">
<stop stop-color="#FFEED7" offset="0%"></stop>
<stop stop-color="#BDBFC2" offset="100%"></stop>
</linearGradient>
<linearGradient x1="53.5047131%" y1="99.97524%" x2="42.7455968%" y2="23.5451715%" id="linearGradient-9">
<stop stop-color="#FFEED7" offset="0%"></stop>
<stop stop-color="#BDBFC2" offset="100%"></stop>
</linearGradient>
<linearGradient x1="49.8413363%" y1="13.2289558%" x2="50.2412612%" y2="94.6729694%" id="linearGradient-10">
<stop stop-color="#FFFFFF" stop-opacity="0.8" offset="0%"></stop>
<stop stop-color="#FFFFFF" stop-opacity="0" offset="100%"></stop>
</linearGradient>
<linearGradient x1="49.9272298%" y1="37.3270337%" x2="50.7270446%" y2="92.7824735%" id="linearGradient-11">
<stop stop-color="#FFFFFF" stop-opacity="0.65" offset="0%"></stop>
<stop stop-color="#FFFFFF" stop-opacity="0" offset="100%"></stop>
</linearGradient>
<linearGradient x1="49.8755597%" y1="2.29900584%" x2="49.8755597%" y2="81.203617%" id="linearGradient-12">
<stop stop-color="#FFFFFF" stop-opacity="0.65" offset="0%"></stop>
<stop stop-color="#FFFFFF" stop-opacity="0" offset="100%"></stop>
</linearGradient>
<linearGradient x1="49.8334391%" y1="2.27189065%" x2="49.8240398%" y2="71.7989617%" id="linearGradient-13">
<stop stop-color="#FFFFFF" stop-opacity="0.65" offset="0%"></stop>
<stop stop-color="#FFFFFF" stop-opacity="0" offset="100%"></stop>
</linearGradient>
<linearGradient x1="53.4670683%" y1="48.9213861%" x2="38.9488708%" y2="98.0999776%" id="linearGradient-14">
<stop stop-color="#FFA63F" offset="0%"></stop>
<stop stop-color="#FFFF00" offset="100%"></stop>
</linearGradient>
<linearGradient x1="52.3731508%" y1="143.008909%" x2="47.57909%" y2="-64.6215389%" id="linearGradient-15">
<stop stop-color="#FFEED7" offset="0%"></stop>
<stop stop-color="#BDBFC2" offset="100%"></stop>
</linearGradient>
<linearGradient x1="30.580815%" y1="34.0241079%" x2="65.8867024%" y2="89.175349%" id="linearGradient-16">
<stop stop-color="#FFA63F" offset="0%"></stop>
<stop stop-color="#FFFF00" offset="100%"></stop>
</linearGradient>
<linearGradient x1="59.5715091%" y1="-17.2155207%" x2="48.3608522%" y2="66.1184465%" id="linearGradient-17">
<stop stop-color="#FFFFFF" stop-opacity="0.65" offset="0%"></stop>
<stop stop-color="#FFFFFF" stop-opacity="0" offset="100%"></stop>
</linearGradient>
<linearGradient x1="47.7689553%" y1="1.56481301%" x2="51.3733028%" y2="104.312856%" id="linearGradient-18">
<stop stop-color="#FFFFFF" stop-opacity="0.65" offset="0%"></stop>
<stop stop-color="#FFFFFF" stop-opacity="0" offset="100%"></stop>
</linearGradient>
<linearGradient x1="43.5495626%" y1="4.5334861%" x2="57.1143288%" y2="92.8267174%" id="linearGradient-19">
<stop stop-color="#FFFFFF" stop-opacity="0.65" offset="0%"></stop>
<stop stop-color="#FFFFFF" stop-opacity="0" offset="100%"></stop>
</linearGradient>
<linearGradient x1="49.7328042%" y1="17.6085216%" x2="50.5582487%" y2="99.3854667%" id="linearGradient-20">
<stop stop-color="#FFA63F" offset="0%"></stop>
<stop stop-color="#FFFF00" offset="100%"></stop>
</linearGradient>
<linearGradient x1="50.1697217%" y1="2.89048531%" x2="49.6802359%" y2="94.1704279%" id="linearGradient-21">
<stop stop-color="#FFFFFF" stop-opacity="0.65" offset="0%"></stop>
<stop stop-color="#FFFFFF" stop-opacity="0" offset="100%"></stop>
</linearGradient>
</defs>
<g fill="none">
<g transform="translate(10.000000, 0.000000)">
<path d="M235.125423,249.358628 C235.125423,266.714271 182.507524,280.855905 117.584567,280.855905 C52.6616093,280.855905 0.0437105058,266.806099 0.0437105058,249.358628 L0.0437105058,249.358628 C0.0437105058,232.002986 52.6616093,217.861352 117.584567,217.861352 C182.507524,217.861352 235.033594,232.002986 235.125423,249.358628 L235.125423,249.358628 L235.125423,249.358628 Z" fill="#000" fill-opacity="0.2" filter="url(#filter-1)"></path>
<path d="M53.2125821,215.473804 C41.8258117,199.128278 39.6219206,145.867578 66.160442,113.084699 C79.2919595,97.3819748 82.6896249,86.4543483 83.6997416,71.6699125 C84.434372,54.8652433 71.8538272,4.81855066 119.237485,1.05357012 C167.263944,-2.80323922 164.600909,44.5804184 164.325423,69.6496791 C164.141765,90.7703016 179.844489,102.799874 190.680286,119.329056 C210.607135,149.632558 208.954216,201.791313 186.915306,230.074582 C158.999353,265.428667 135.123866,250.093259 119.237485,251.378862 C89.4849556,253.123609 88.4748389,268.918162 53.2125821,215.473804 L53.2125821,215.473804 Z" fill="#000000"></path>
<path d="M169.10052,122.451235 C177.365111,130.073025 198.76122,164.141508 164.876395,185.445788 C152.938652,192.88392 175.528535,221.167189 186.364333,207.484699 C205.556551,182.874582 193.343321,143.571858 181.772893,129.522053 C174.059275,119.604543 162.121532,115.747734 169.10052,122.451235 L169.10052,122.451235 Z" fill="url(#linearGradient-2)"></path>
<path d="M166.8048,117.859796 C180.395461,128.879251 205.097407,167.447344 169.008691,192.608434 C157.162777,200.413881 179.477174,225.115827 192.057718,212.535282 C235.676395,168.641119 190.955773,118.227111 175.528535,100.871469 C161.754216,85.719718 149.540987,104.360963 166.8048,117.859796 L166.8048,117.859796 Z" stroke="#000000" stroke-width="0.9773" fill="#000000"></path>
<path d="M147.245267,25.0208853 C146.786123,37.60143 132.919975,48.5290565 116.298963,49.5391732 C99.6779518,50.54929 86.638263,40.9990954 87.097407,28.4185507 L87.097407,28.4185507 C87.556551,15.8380059 101.422699,4.91037946 118.043711,3.90026272 C134.664722,2.98197479 147.704411,12.4403405 147.245267,25.0208853 L147.245267,25.0208853 L147.245267,25.0208853 Z" fill="url(#linearGradient-3)"></path>
<path d="M107.483399,54.9570721 C107.942543,63.1298347 104.085734,70.0169942 98.7596638,70.2924806 C93.4335938,70.567967 88.7503253,64.2317802 88.2911813,56.0590176 L88.2911813,56.0590176 C87.8320374,47.8862549 91.6888467,40.9990954 97.0149167,40.723609 C102.340987,40.4481226 107.024255,46.7843094 107.483399,54.9570721 L107.483399,54.9570721 L107.483399,54.9570721 Z" fill="url(#linearGradient-4)"></path>
<path d="M117.125423,55.5998736 C117.30908,65.0582394 123.461609,72.5882005 130.807913,72.4045429 C138.154216,72.2208853 143.93943,64.4154378 143.755773,54.8652433 L143.755773,54.8652433 C143.572115,45.4068775 137.419586,37.8769164 130.073282,38.060574 C122.726979,38.2442316 116.849936,46.1415079 117.125423,55.5998736 L117.125423,55.5998736 L117.125423,55.5998736 Z" fill="url(#linearGradient-5)"></path>
<path d="M123.186123,57.7119359 C123.094294,62.9461771 125.6655,67.1703016 129.063166,67.1703016 C132.369002,67.1703016 135.215695,62.9461771 135.307524,57.8037647 L135.307524,57.8037647 C135.399353,52.5695234 132.828146,48.3453989 129.430481,48.3453989 C126.032816,48.3453989 123.277952,52.5695234 123.186123,57.7119359 L123.186123,57.7119359 L123.186123,57.7119359 Z" fill="#000000"></path>
<path d="M101.973672,57.8037647 C102.432816,62.119718 100.779897,65.7928697 98.3923486,66.1601849 C96.0048,66.4356713 93.7090802,63.2216635 93.2499362,58.9057102 L93.2499362,58.9057102 C92.7907922,54.5897569 94.4437105,50.9166051 96.8312591,50.54929 C99.2188078,50.2738036 101.514528,53.4878114 101.973672,57.8037647 L101.973672,57.8037647 L101.973672,57.8037647 Z" fill="#000000"></path>
<path d="M124.563555,54.7734145 C124.288068,57.7119359 125.6655,60.0994845 127.593905,60.2831421 C129.52231,60.4667997 131.358886,58.1710798 131.634372,55.3243872 L131.634372,55.3243872 C131.909858,52.3858658 130.532426,49.9983172 128.604022,49.8146596 C126.675617,49.631002 124.839041,51.9267219 124.563555,54.7734145 L124.563555,54.7734145 L124.563555,54.7734145 Z" fill="url(#linearGradient-6)"></path>
<path d="M99.9534381,55.5080448 C100.228925,57.8955935 99.2188078,60.0076557 97.7495471,60.1913133 C96.2802864,60.3749709 94.9028545,58.538395 94.6273681,56.0590176 L94.6273681,56.0590176 C94.3518817,53.6714689 95.3619984,51.5594067 96.8312591,51.3757491 C98.3005198,51.1920915 99.6779518,53.1204962 99.9534381,55.5080448 L99.9534381,55.5080448 L99.9534381,55.5080448 Z" fill="url(#linearGradient-7)"></path>
<path d="M71.0273681,145.68392 C77.5472125,130.899485 91.4133603,104.911936 91.6888467,84.80143 C91.6888467,68.8232199 139.531648,64.9664106 143.388458,80.9446207 C147.245267,96.9228308 156.979119,120.798317 163.223477,132.368745 C169.467835,143.847344 187.558107,180.487033 168.274061,212.443453 C150.918419,240.726722 98.3005198,263.132948 70.2009089,208.586644 C60.6507144,189.669913 62.3954615,166.25357 71.0273681,145.68392 L71.0273681,145.68392 Z" fill="url(#linearGradient-8)"></path>
<path d="M65.1503253,134.664465 C59.5487689,145.224776 47.9783409,172.957072 76.2616093,188.108823 C106.65694,204.270691 106.565111,237.420885 70.0172514,221.626333 C36.5915704,207.39287 51.3760062,149.724387 60.7425432,135.950068 C66.8032436,126.308045 75.986123,114.46213 65.1503253,134.664465 L65.1503253,134.664465 Z" fill="url(#linearGradient-9)"></path>
<path d="M69.9254226,122.726722 C61.0180296,137.235671 39.7137494,171.395983 68.2725043,189.210769 C106.65694,212.810769 95.8211424,236.31894 60.7425432,215.106488 C11.3386521,185.537617 54.7736716,125.848901 74.5168623,103.07536 C97.1067455,77.5469553 78.8328156,107.758628 69.9254226,122.726722 L69.9254226,122.726722 Z" stroke="#000000" stroke-width="1.25" fill="#000000"></path>
<path d="M156.428146,151.285477 C156.428146,167.447344 140.90908,188.384309 114.27873,188.200652 C86.8219206,188.384309 75.1596638,167.447344 75.1596638,151.285477 C75.1596638,135.123609 93.341765,121.992092 115.747991,121.992092 C138.246045,122.08392 156.428146,135.123609 156.428146,151.285477 L156.428146,151.285477 Z" fill="url(#linearGradient-10)"></path>
<path d="M141.919197,100.504154 C141.643711,117.216994 130.716084,121.165632 116.941765,121.165632 C103.167446,121.165632 93.1581074,118.686255 91.9643331,100.504154 C91.9643331,89.1173833 103.167446,82.5057102 116.941765,82.5057102 C130.716084,82.4138814 141.919197,89.0255546 141.919197,100.504154 L141.919197,100.504154 Z" fill="url(#linearGradient-11)"></path>
<path d="M58.6304809,126.216216 C67.6297027,112.533726 86.638263,91.504932 62.2118039,129.154737 C42.3767844,160.19287 54.8655004,180.119718 61.293516,185.629446 C79.8429323,202.158628 79.1083019,213.269913 64.5075237,204.546177 C33.1939051,185.904932 39.7137494,154.499485 58.6304809,126.216216 L58.6304809,126.216216 Z" fill="url(#linearGradient-12)"></path>
<path d="M188.935539,131.817772 C181.130092,115.747734 156.336318,74.9757491 190.129314,122.359407 C220.89196,165.243453 199.312193,195.087811 195.455384,198.026333 C191.598574,200.964854 178.650714,206.933726 182.415695,196.557072 C186.272504,186.180418 205.372893,166.529056 188.935539,131.817772 L188.935539,131.817772 Z" fill="url(#linearGradient-13)"></path>
<path d="M51.8351502,258.541508 C31.2655004,247.613881 1.42114241,260.65357 12.2569401,231.084699 C14.4608311,224.381197 9.0429323,214.280029 12.5324265,207.760185 C16.6647222,199.77108 25.5721152,201.515827 30.8981852,196.189757 C36.1324265,190.680029 39.438263,181.129835 49.263944,182.599095 C58.9977961,184.068356 65.5176405,196.006099 72.3129712,210.698706 C77.3635549,221.167189 95.1783409,235.951625 93.9845665,247.70571 C92.5153058,265.704154 72.0374848,269.101819 51.8351502,258.541508 L51.8351502,258.541508 Z" stroke="#E68C3F" stroke-width="6.25" fill="url(#linearGradient-14)"></path>
<path d="M201.607913,189.11894 C198.485734,194.995983 185.446045,204.454348 176.72231,201.974971 C167.906746,199.587422 163.866279,186.180418 165.611026,175.987422 C167.263944,164.600652 176.72231,163.95785 188.660053,169.651235 C201.516084,175.987422 205.372893,181.313492 201.607913,189.11894 L201.607913,189.11894 Z" fill="url(#linearGradient-15)"></path>
<path d="M194.445267,253.490924 C209.505189,235.216994 243.022699,238.981975 220.432816,213.912714 C215.657718,208.494815 217.126979,196.924387 211.249936,191.965632 C204.362777,185.904932 196.740987,190.863687 189.761998,187.741508 C182.78301,184.343842 175.436707,177.823998 166.896629,182.415438 C158.356551,187.098706 157.438263,199.220107 156.611804,215.198317 C155.877174,226.676916 145.408691,245.869134 151.010247,256.429446 C159.091181,272.774971 180.119975,270.57108 194.445267,253.490924 L194.445267,253.490924 Z" stroke="#E68C3F" stroke-width="6.2507" fill="url(#linearGradient-16)"></path>
<path d="M187.925423,229.064465 C211.249936,194.628667 193.894294,194.904154 188.017251,192.241119 C182.140209,189.486255 175.987679,184.068356 169.10052,187.833337 C162.21336,191.690146 161.846045,201.607656 161.662388,214.647344 C161.386901,224.013881 153.581454,239.716605 158.264722,248.440341 C163.958107,258.633337 177.732426,243.848901 187.925423,229.064465 L187.925423,229.064465 Z" fill="url(#linearGradient-17)"></path>
<path d="M47.0600529,234.02322 C12.1651113,211.433337 28.5106366,203.719718 33.7448778,200.138395 C40.0810646,195.546955 40.1728934,186.731391 47.9783409,187.55785 C55.7837883,188.384309 60.375228,198.026333 65.6094693,209.964076 C69.4662786,218.504154 82.8732825,229.890924 81.8631658,239.716605 C80.5775626,251.287033 62.1199751,243.665243 47.0600529,234.02322 L47.0600529,234.02322 Z" fill="url(#linearGradient-18)"></path>
<path d="M199.587679,188.843453 C196.832816,193.618551 185.629703,201.148512 178.19157,199.128278 C170.569781,197.199874 167.080286,186.455905 168.641376,178.374971 C170.018808,169.192092 178.19157,168.732948 188.476395,173.324387 C199.404022,178.283142 202.801687,182.507267 199.587679,188.843453 L199.587679,188.843453 Z" fill="#000000"></path>
<path d="M192.057718,186.180418 C190.312971,189.486255 182.966668,194.720496 177.824255,193.343064 C172.681843,191.965632 170.110637,184.5275 170.937096,178.925944 C171.671726,172.589757 177.181454,172.222442 184.160442,175.344621 C191.690403,178.834115 194.077952,181.772636 192.057718,186.180418 L192.057718,186.180418 Z" fill="url(#linearGradient-19)"></path>
<path d="M97.1067455,66.3438425 C100.779897,62.9461771 109.68729,52.5695234 126.583788,63.4053211 C129.705967,65.4255546 132.277174,65.6092121 138.246045,68.1804184 C150.275617,73.1391732 144.582232,85.0769164 131.726201,89.1173833 C126.216473,90.8621304 121.257718,97.5656324 111.340209,96.9228308 C102.800131,96.4636868 100.59624,90.8621304 95.3619984,87.8317802 C86.0872903,82.597539 84.7098584,75.5267219 89.760442,71.7617413 C94.8110257,67.9967608 96.7394304,66.6193289 97.1067455,66.3438425 L97.1067455,66.3438425 Z" stroke="#E68C3F" stroke-width="3.75" fill="url(#linearGradient-20)"></path>
<path d="M138.429703,75.9858658 C133.379119,76.2613522 122.451493,87.1889787 110.972893,87.1889787 C99.4942942,87.1889787 92.6071346,76.5368386 90.8623875,76.5368386" stroke="#E68C3F" stroke-width="2.5"></path>
<path d="M102.800131,65.4255546 C104.636707,63.7726363 110.421921,59.2730254 118.043711,63.8644651 C119.696629,64.782753 121.349547,65.7928697 123.737096,67.1703016 C128.604022,70.0169942 126.216473,74.14929 120.33943,76.7204962 C117.676395,77.8224417 113.268613,80.2099904 109.962777,80.0263328 C106.289625,79.6590176 103.810247,77.2714689 101.422699,75.7103795 C96.9230879,72.7718581 97.1985743,70.2924806 99.3106366,68.364076 C100.871726,66.8948153 102.616473,65.5173833 102.800131,65.4255546 L102.800131,65.4255546 Z" fill="url(#linearGradient-21)"></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg version="1.1" viewBox="0 0 42 42" xmlns="http://www.w3.org/2000/svg"><path d="m23.091 14.018v-0.342l-1.063 0.073c-0.301 0.019-0.527 0.083-0.679 0.191-0.152 0.109-0.228 0.26-0.228 0.453 0 0.188 0.075 0.338 0.226 0.449 0.15 0.112 0.352 0.167 0.604 0.167 0.161 0 0.312-0.025 0.451-0.074s0.261-0.118 0.363-0.206c0.102-0.087 0.182-0.191 0.239-0.312 0.058-0.121 0.087-0.254 0.087-0.399zm-2.091-13.768c-11.579 0-20.75 9.171-20.75 20.75 0 11.58 9.171 20.75 20.75 20.75s20.75-9.17 20.75-20.75c0-11.579-9.17-20.75-20.75-20.75zm4.028 12.299c0.098-0.275 0.236-0.511 0.415-0.707s0.394-0.347 0.646-0.453 0.533-0.159 0.842-0.159c0.279 0 0.531 0.042 0.755 0.125 0.225 0.083 0.417 0.195 0.578 0.336s0.289 0.305 0.383 0.493 0.15 0.387 0.169 0.596h-0.833c-0.021-0.115-0.059-0.223-0.113-0.322s-0.125-0.185-0.213-0.258c-0.089-0.073-0.193-0.13-0.312-0.171-0.12-0.042-0.254-0.062-0.405-0.062-0.177 0-0.338 0.036-0.481 0.107-0.144 0.071-0.267 0.172-0.369 0.302s-0.181 0.289-0.237 0.475c-0.057 0.187-0.085 0.394-0.085 0.622 0 0.236 0.028 0.448 0.085 0.634 0.056 0.187 0.136 0.344 0.24 0.473 0.103 0.129 0.228 0.228 0.373 0.296s0.305 0.103 0.479 0.103c0.285 0 0.517-0.067 0.697-0.201s0.296-0.33 0.35-0.588h0.834c-0.024 0.228-0.087 0.436-0.189 0.624s-0.234 0.348-0.396 0.481c-0.163 0.133-0.354 0.236-0.574 0.308s-0.462 0.109-0.725 0.109c-0.312 0-0.593-0.052-0.846-0.155-0.252-0.103-0.469-0.252-0.649-0.445s-0.319-0.428-0.417-0.705-0.147-0.588-0.147-0.935c-2e-3 -0.339 0.047-0.647 0.145-0.923zm-11.853-1.262h0.834v0.741h0.016c0.051-0.123 0.118-0.234 0.2-0.33 0.082-0.097 0.176-0.179 0.284-0.248 0.107-0.069 0.226-0.121 0.354-0.157 0.129-0.036 0.265-0.054 0.407-0.054 0.306 0 0.565 0.073 0.775 0.219 0.211 0.146 0.361 0.356 0.449 0.63h0.021c0.056-0.132 0.13-0.25 0.221-0.354s0.196-0.194 0.314-0.268 0.248-0.13 0.389-0.169 0.289-0.058 0.445-0.058c0.215 0 0.41 0.034 0.586 0.103s0.326 0.165 0.451 0.29 0.221 0.277 0.288 0.455 0.101 0.376 0.101 0.594v2.981h-0.87v-2.772c0-0.287-0.074-0.51-0.222-0.667-0.147-0.157-0.358-0.236-0.632-0.236-0.134 0-0.257 0.024-0.369 0.071-0.111 0.047-0.208 0.113-0.288 0.198-0.081 0.084-0.144 0.186-0.189 0.304-0.046 0.118-0.069 0.247-0.069 0.387v2.715h-0.858v-2.844c0-0.126-0.02-0.24-0.059-0.342s-0.094-0.189-0.167-0.262c-0.072-0.073-0.161-0.128-0.264-0.167-0.104-0.039-0.22-0.059-0.349-0.059-0.134 0-0.258 0.025-0.373 0.075-0.114 0.05-0.212 0.119-0.294 0.207-0.082 0.089-0.146 0.193-0.191 0.314-0.044 0.12-0.116 0.252-0.116 0.394v2.683h-0.825v-4.374zm1.893 20.939c-3.825 0-6.224-2.658-6.224-6.9s2.399-6.909 6.224-6.909 6.215 2.667 6.215 6.909c0 4.241-2.39 6.9-6.215 6.9zm7.082-16.575c-0.141 0.036-0.285 0.054-0.433 0.054-0.218 0-0.417-0.031-0.598-0.093-0.182-0.062-0.337-0.149-0.467-0.262s-0.232-0.249-0.304-0.409c-0.073-0.16-0.109-0.338-0.109-0.534 0-0.384 0.143-0.684 0.429-0.9s0.7-0.342 1.243-0.377l1.18-0.068v-0.338c0-0.252-0.08-0.445-0.24-0.576s-0.386-0.197-0.679-0.197c-0.118 0-0.229 0.015-0.331 0.044-0.102 0.03-0.192 0.072-0.27 0.127s-0.143 0.121-0.193 0.198c-0.051 0.076-0.086 0.162-0.105 0.256h-0.818c5e-3 -0.193 0.053-0.372 0.143-0.536s0.212-0.306 0.367-0.427 0.336-0.215 0.546-0.282 0.438-0.101 0.685-0.101c0.266 0 0.507 0.033 0.723 0.101s0.401 0.163 0.554 0.288 0.271 0.275 0.354 0.451 0.125 0.373 0.125 0.59v3.001h-0.833v-0.729h-0.021c-0.062 0.118-0.14 0.225-0.235 0.32-0.096 0.095-0.203 0.177-0.322 0.244-0.12 0.067-0.25 0.119-0.391 0.155zm5.503 16.575c-2.917 0-4.9-1.528-5.038-3.927h1.899c0.148 1.371 1.473 2.279 3.288 2.279 1.741 0 2.992-0.908 2.992-2.149 0-1.074-0.76-1.723-2.519-2.167l-1.714-0.426c-2.464-0.611-3.584-1.732-3.584-3.575 0-2.269 1.982-3.844 4.807-3.844 2.76 0 4.686 1.584 4.76 3.862h-1.88c-0.13-1.371-1.25-2.214-2.918-2.214-1.658 0-2.806 0.852-2.806 2.084 0 0.972 0.722 1.547 2.482 1.991l1.445 0.361c2.751 0.667 3.881 1.751 3.881 3.696-1e-3 2.482-1.964 4.029-5.095 4.029zm-12.585-12.106c-2.621 0-4.26 2.01-4.26 5.205 0 3.186 1.639 5.196 4.26 5.196 2.612 0 4.26-2.01 4.26-5.196 1e-3 -3.195-1.648-5.205-4.26-5.205z"/></svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="256px" height="257px" viewBox="0 0 256 257" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
<g>
<path d="M0,36.3573818 L104.619084,22.1093454 L104.664817,123.02292 L0.0955693151,123.618411 L0,36.3573818 Z M104.569248,134.650129 L104.650452,235.651651 L0.0812046021,221.274919 L0.0753414539,133.972642 L104.569248,134.650129 Z M117.25153,20.2454506 L255.967753,6.21724894e-15 L255.967753,121.739477 L117.25153,122.840723 L117.25153,20.2454506 Z M256,135.599959 L255.96746,256.791232 L117.251237,237.213007 L117.056874,135.373055 L256,135.599959 Z" fill="#00ADEF"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 734 B

BIN
docs/Home/assets/video.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 704 KiB

41
docs/In-app-Manual.md Normal file
View File

@ -0,0 +1,41 @@
---
layout: default
title: In-app Manual
nav_order: 3
has_children: true
has_toc: false
---
# **In-app Manual**
{: .no_toc }
Internal Stash documentation, mirrored from [Stash repository](https://github.com/stashapp/stash/tree/master/ui/v2.5/src/docs/en/Manual){:target="_blank"}.
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. [Introduction]({{ site.baseurl }}/docs/In-app-Manual/Introduction)
2. [Configuration]({{ site.baseurl }}/docs/In-app-Manual/Configuration)
3. [Interface Options]({{ site.baseurl }}/docs/In-app-Manual/Interface)
4. [Tasks]({{ site.baseurl }}/docs/In-app-Manual/Tasks)
- [Identify]({{ site.baseurl }}/docs/In-app-Manual/Identify)
- [Auto Tagging]({{ site.baseurl }}/docs/In-app-Manual/AutoTagging)
- [Scene Filename Parser]({{ site.baseurl }}/docs/In-app-Manual/SceneFilenameParser)
- [JSON Specification]({{ site.baseurl }}/docs/In-app-Manual/JSONSpec)
5. [Browsing]({{ site.baseurl }}/docs/In-app-Manual/Browsing)
6. [Image Galleries]({{ site.baseurl }}/docs/In-app-Manual/Galleries)
7. [Metadata Scraping]({{ site.baseurl }}/docs/In-app-Manual/Scraping)
- [Scraper Development]({{ site.baseurl }}/docs/In-app-Manual/ScraperDevelopment)
8. [Plugins]({{ site.baseurl }}/docs/In-app-Manual/Plugins)
- [External]({{ site.baseurl }}/docs/In-app-Manual/ExternalPlugins)
- [Embedded]({{ site.baseurl }}/docs/In-app-Manual/EmbeddedPlugins)
9. [Scene Tagger]({{ site.baseurl }}/docs/In-app-Manual/Tagger)
10. [Dupe Checker]({{ site.baseurl }}/docs/In-app-Manual/Deduplication)
11. [Interactivity]({{ site.baseurl }}/docs/In-app-Manual/Interactive)
12. [Captions]({{ site.baseurl }}/docs/In-app-Manual/Captions)
13. [Keyboard Shorcuts]({{ site.baseurl }}/docs/In-app-Manual/KeyboardShortcuts)
14. [Contributing]({{ site.baseurl }}/docs/In-app-Manual/Contributing)
15. [Further Help]({{ site.baseurl }}/docs/In-app-Manual/Help)
</details>

View File

@ -0,0 +1,66 @@
---
layout: default
title: Browsing
nav_order: 5
parent: In-app Manual
---
# **Browsing**
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
## Querying and Filtering
### Keyword searching
The text field allows you to search using keywords. Keyword searching matches on different fields depending on the object type:
| Type | Fields searched |
|------|-----------------|
| Scene | Title, Details, Path, OSHash, Checksum, Marker titles |
| Image | Title, Path, Checksum |
| Movie | Title |
| Marker | Title, Scene title |
| Gallery | Title, Path, Checksum |
| Performer | Name, Aliases |
| Studio | Name, Aliases |
| Tag | Name, Aliases |
Keyword matching uses the following rules:
* all words are required in the matching field. For example, `foo bar` matches scenes with both `foo` and `bar` in the title.
* the `or` keyword or symbol (`|`) is used to match either fields. For example, `foo or bar` (or `foo | bar`) matches scenes with `foo` or `bar` in the title. Or sets can be combined. For example, `foo or bar or baz xyz or zyx` matches scenes with one of `foo`, `bar` and `baz`, *and* `xyz` or `zyx`.
* the not symbol (`-`) is used to exclude terms. For example, `foo -bar` matches scenes with `foo` and excludes those with `bar`. The not symbol cannot be combined with an or operand. That is, `-foo or bar` will be interpreted to match `-foo` or `bar`. On the other hand, `foo or bar -baz` will match `foo` or `bar` and exclude `baz`.
* surrounding a phrase in quotes (`"`) matches on that exact phrase. For example, `"foo bar"` matches scenes with `foo bar` in the title. Quotes may also be used to escape the keywords and symbols. For example, `foo "-bar"` will match scenes with `foo` and `-bar`.
* quoted phrases may be used with the or and not operators. For example, `"foo bar" or baz -"xyz zyx"` will match scenes with `foo bar` *or* `baz`, and exclude those with `xyz zyx`.
* `or` keywords or symbols at the start or end of a line will be treated literally. That is, `or foo` will match scenes with `or` and `foo`.
* all matching is case-insensitive
### Filters
Filters can be accessed by clicking the filter button on the right side of the query text field.
Note that only one filter criterion per criterion type may be assigned.
### Sorting and page size
The current sorting field is shown next to the query text field, indicating the current sort field and order. The page size dropdown allows selecting from a standard set of objects per page, and allows setting a custom page size.
### Saved filters
Saved filters can be accessed with the bookmark button on the left of the query text field. The current filter can be saved by entering a filter name and clicking on the save button. Existing saved filters may be overwritten with the current filter by clicking on the save button next to the filter name. Saved filters may also be deleted by pressing the delete button next to the filter name.
### Default filter
The default filter for the top-level pages may be set to the current filter by clicking the `Set as default` button in the saved filter menu.

View File

@ -0,0 +1,37 @@
---
layout: default
title: Captions
nav_order: 12
parent: In-app Manual
---
# **Captions**
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
Stash supports captioning with SRT and VTT files.
These files need to be named as follows:
---
## Scene
- {scene_name}.{language_code}.ext
- {scene_name}.ext
Where `{language_code}` is defined by the [ISO-6399-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes){:target="_blank"} (2 letters) standard and `ext` is the file extension. Captions files without a language code will be labeled as Unknown in the video player but will work fine.
Scenes with captions can be filtered with the `captions` criterion.

View File

@ -0,0 +1,171 @@
---
layout: default
title: Configuration
nav_order: 2
parent: In-app Manual
---
# **Configuration**
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
## Stashes
This section allows you to add and remove directories from your library list. Files in these directories will be included when scanning. Files that are outside of these directories will be removed when running the Clean task.
> **⚠️ Note:** Don't forget to click `Save` after updating these directories!
---
## Excluded Patterns
Given a valid [regex](https://github.com/google/re2/wiki/Syntax){:target="_blank"}, files that match even partially are excluded during the Scan process and are not entered in the database. Also during the Clean task if these files exist in the DB they are removed from it and their generated files get deleted.
Prior to matching both the filenames and patterns are converted to lower case so the match is case insensitive.
Regex patterns can be added in the config file or from the UI.
If you add manually to the config file a restart is needed while from the UI you just need to click the Save button.
When added through the config file directly special care must be given to double escape the `\` character.
Some examples
For the config file you need the following added
```
exclude:
- "sample\\.mp4$"
- "/\\.[[:word:]]+/"
- "c:\\\\stash\\\\videos\\\\exclude"
- "^/stash/videos/exclude/"
- "\\\\\\\\stash\\network\\\\share\\\\excl\\\\"
```
* the first excludes all files ending in `sample.mp4` ( `.` needs to be escaped also)
* the second hidden directories `/.directoryname/`
* the third is an example for a windows directory `c:\stash\videos\exclude`
* the fourth the directory `/stash/videos/exclude/`
* and the last a windows network path `\\stash\network\share\excl\`
**Note:** if a directory is excluded for images and videos, then the directory will be excluded from scans completely.
_a useful [link](https://regex101.com/){:target="_blank"} to experiment with regexps_
---
## Hashing algorithms
Stash identifies video files by calculating a hash of the file. There are two algorithms available for hashing: `oshash` and `MD5`. `MD5` requires reading the entire file, and can therefore be slow, particularly when reading files over a network. `oshash` (which uses OpenSubtitle's hashing algorithm) only reads 64k from each end of the file.
The hash is used to name the generated files such as preview images and videos, and sprite images.
By default, new systems have MD5 calculation disabled for optimal performance. Existing systems that are upgraded will have the oshash populated for each scene on the next scan.
### Changing the hashing algorithm
To change the file naming hash to oshash, all scenes must have their oshash values populated. oshash population is done automatically when scanning.
To change the file naming hash to `MD5`, the MD5 must be populated for all scenes. To do this, `Calculate MD5` for videos must be enabled and the library must be rescanned.
MD5 calculation may only be disabled if the file naming hash is set to `oshash`.
After changing the file naming hash, any existing generated files will now be named incorrectly. This means that stash will not find them and may regenerate them if the `Generate task` is used. To remedy this, run the `Rename generated files` task, which will rename existing generated files to their correct names.
#### Step-by-step instructions to migrate to oshash for existing users
These instructions are for existing users whose systems will be defaulted to use and calculate MD5 checksums. Once completed, MD5 checksums will no longer be calculated when scanning, and oshash will be used for generated file naming. Existing calculated MD5 checksums will remain on scenes, but checksums will not be calculated for new scenes.
1. Scan the library (to populate oshash for all existing scenes).
2. In Settings -> Configuration page, untick `Calculate MD5` and select `oshash` as file naming hash. Save the configuration.
3. In Settings -> Tasks page, click on the `Rename generated files` migration button.
---
## Parallel Scan/Generation
### Number of parallel task for scan/generation
This setting controls how many sub-tasks will be run in parallel during scanning and generation tasks. (See Tasks)
Auto-detection can be enabled by setting this to zero. This will calculate the number of parallel tasks to be cpu_cores/4 + 1.
This setting can be used to increase/decrease overall CPU utilisation in two scenarios:
1) High performance 4+ core cpus.
2) Media files stored on remote/cloud filesystem.
Note: If this is set too high it will decrease overall performance and causes failures (out of memory).
---
## Scraping
### User Agent string
Some websites require a legitimate User-Agent string when receiving requests, or they will be rejected. If entered, this string will be applied as the `User-Agent` header value in http scrape requests.
### Chrome CDP path
Some scrapers require a Chrome instance to function correctly. If left empty, stash will attempt to find the Chrome executable in the path environment, and will fail if it cannot find one.
`Chrome CDP path` can be set to a path to the chrome executable, or an http(s) address to remote chrome instance (for example: `http://localhost:9222/json/version`).
---
## Authentication
By default, stash is not configured with any sort of password protection. To enable password protection, both `Username` and `Password` must be populated. Note that when entering a new username and password where none was set previously, the system will immediately request these credentials to log you in.
---
## API key
If password protection is enabled, you may also generate an API key. An API key is used by external systems to access your stash system without needing to login first.
External systems using the API key must set the `ApiKey` header value to the configured API key in order to bypass the login requirement.
### Logging out
The logout button is situated in the upper-right part of the screen when you are logged in.
### Recovering from a forgotten username or password
Stash saves login credentials in the config.yml file. You must reset both login and password if you have forgotten your password by doing the following:
* Close your Stash process
* Open the `config.yml` file found in your Stash directory with a text editor
* Delete the `login` and `password` lines from the file and save
Stash authentication should now be reset with no authentication credentials.
---
## Advanced configuration options
These options are typically not exposed in the UI and must be changed manually in the `config.yml` file.
| Field | Remarks |
|-------|---------|
| `custom_served_folders` | A map of URLs to file system folders. See below. |
| `custom_ui_location` | The file system folder where the UI files will be served from, instead of using the embedded UI. Empty to disable. Stash must be restarted to take effect. |
| `max_upload_size` | Maximum file upload size for import files. Defaults to 1GB. |
| `theme_color` | Sets the `theme-color` property in the UI. |
### Custom served folders
Custom served folders are served when the server handles a request with the `/custom` URL prefix. The following is an example configuration:
```
custom_served_folders:
/: D:\stash\static
/foo: D:\bar
```
With the above configuration, a request for `/custom/foo/bar.png` would serve `D:\bar\bar.png`.
The `/` entry matches anything that is not otherwise mapped by the other entries. For example, `/custom/baz/xyz.png` would serve `D:\stash\static\baz\xyz.png`.

View File

@ -0,0 +1,77 @@
---
layout: default
title: Ways to Contribute
nav_order: 14
parent: In-app Manual
---
# **Ways to Contribute**
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
## Financial
Financial contributions are welcomed and are accepted using [Open Collective](https://opencollective.com/stashapp){:target="_blank"}.
---
## Development-related
The Stash backend is written in golang with a sqlite database. The UI is written in react. Bug fixes, improvements and new features are welcomed. Please see the [README.md](https://github.com/stashapp/stash/blob/develop/docs/DEVELOPMENT.md){:target="_blank"} file for details on how to get started. Assistance can be provided via our [Discord](https://discord.gg/2TsNFKt){:target="_blank"}.
---
## Documentation
Efforts to improve documentation in stash helps new users and reduces the amount of questions we have to field in Discord. Contributions to documentation are welcomed. While submitting documentation changes via git pull requests is ideal, we will gladly accept submissions via [github issues](https://github.com/stashapp/stash/issues){:target="_blank"} or on [Discord](https://discord.gg/2TsNFKt){:target="_blank"}.
For those with web page experience, we also welcome contributions to our [website](https://stashapp.cc/){:target="_blank"} (which as of writing is very undeveloped).
---
## Testing features, improvements and bug fixes
Testing is currently covered by a very small group, so new testers are welcomed. Being able to build stash locally is ideal, but custom binaries for pull requests are available by navigating to the `continuous-integration/travis-ci/pr` travis check details.
The link to the custom binary for each platform can be found at the end of the build log, and looks like the following:
```
$ if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then sh ./scripts/upload-pull-request.sh; fi
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 43.1M 100 35 100 43.1M 3 3812k 0:00:11 0:00:11 --:--:-- 5576k
stash-osx uploaded to url: https://transfer.sh/.../stash-osx
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 60.7M 100 39 100 60.7M 3 5391k 0:00:13 0:00:11 0:00:02 7350k
stash-win.exe uploaded to url: https://transfer.sh/.../stash-win.exe
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 44.6M 100 37 100 44.6M 2 3648k 0:00:18 0:00:12 0:00:06 7504k
stash-linux uploaded to url: https://transfer.sh/.../stash-linux
```
The `if` line will need to be expanded to see the details.
---
## Submitting and contributing to bug reports, improvements and new features
We welcome contributions for future improvements and features, and bug reports help everyone. These can all be found in the [github issues](https://github.com/stashapp/stash/issues){:target="_blank"}.
---
## Providing support
Offering support for new users on [Discord](https://discord.gg/2TsNFKt){:target="_blank"} is also welcomed.

View File

@ -0,0 +1,30 @@
---
layout: default
title: Dupe Checker
nav_order: 10
parent: In-app Manual
---
# **Dupe Checker**
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
[The dupe checker](http://localhost:9999/sceneDuplicateChecker){:target="_blank"} searches your collection for scenes that are perceptually similar. This means that the files don't need to be identical, and will be identified even with different bitrates, resolutions, and intros/outros.
To achieve this stash needs to generate what's called a phash, or perceptual hash. Similar to sprite generation stash will generate a set of 25 images from fixed points in the scene. These images will be stitched together, and then hashed using the phash algorithm. The phash can then be used to find scenes that are the same or similar to others in the database. Phash generation can be run during scan, or as a separate task. Note that generation can take a while due to the work involved with extracting screenshots.
The dupe checker can be run with four different levels of accuracy. `Exact` looks for scenes that have exactly the same phash. This is a fast and accurate operation that should not yield any false positives except in very rare cases. The other accuracy levels look for duplicate files within a set distance of each other. This means the scenes don't have exactly the same phash, but are very similar. `High` and `Medium` should still yield very good results with few or no false positives. `Low` is likely to produce some false positives, but might still be useful for finding dupes.
Note that to generate a phash stash requires an uncorrupted file. If any errors are encountered during sprite generation the phash will not be generated. This is to prevent false positives.

View File

@ -0,0 +1,33 @@
---
layout: default
title: Galleries
nav_order: 6
parent: In-app Manual
---
# **Galleries**
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
**Note:** images are now included during the scan process and are loaded independently of galleries. It is _no longer necessary_ to have images in zip files to be scanned into your library.
Galleries are automatically created from zip files found during scanning that contain images. It is also possible to automatically create galleries from folders containing images, by selecting the "Create galleries from folders containing images" checkbox in the Configuration page. It is also possible to manually create galleries.
For best results, images in zip file should be stored without compression (copy, store or no compression options depending on the software you use. Eg on linux: `zip -0 -r gallery.zip foldertozip/`). This impacts **heavily** on the zip read performance.
If an filename of an image in the gallery zip file ends with `cover.jpg`, it will be treated like a cover and presented first in the gallery view page and as a gallery cover in the gallery list view. If more than one images match the name the first one found in natural sort order is selected.
Images can be added to a gallery by navigating to the gallery's page, selecting the "Add" tab, querying for and selecting the images to add, then selecting "Add to Gallery" from the `...` menu button. Likewise, images may be removed from a gallery by selecting the "Images" tab, selecting the images to remove and selecting "Remove from Gallery" from the `...` menu button.

View File

@ -0,0 +1,28 @@
---
layout: default
title: Where to Get Further Help
nav_order: 15
parent: In-app Manual
---
# **Where to Get Further Help**
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
Join our [Discord](https://discord.gg/2TsNFKt){:target="_blank"}.
The [Github wiki](https://github.com/stashapp/stash/wiki){:target="_blank"} covers some areas not covered in the in-app help.
Raise a [github issue](https://github.com/stashapp/stash/issues){:target="_blank"}.

View File

@ -0,0 +1,30 @@
---
layout: default
title: Interactivity
nav_order: 11
parent: In-app Manual
---
# **Interactivity**
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
Stash currently supports syncing with Handy devices, using funscript files.
In order for stash to connect to your Handy device, the Handy Connection Key must be entered in Settings -> Interface.
Funscript files must be in the same directory as the matching video file and must have the same base name. For example, a funscript file for `video.mp4` must be named `video.funscript`. A scan must be run to update scenes with matching funscript files.
Scenes with funscript files can be filtered with the `interactive` criterion.

View File

@ -0,0 +1,99 @@
---
layout: default
title: Interface Options
nav_order: 3
parent: In-app Manual
---
# **Interface Options**
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
## Language
Setting the language affects the formatting of numbers and dates.
---
## Scene/Marker Wall Preview Type
The Scene Wall and Marker pages display scene preview videos by default. This can be changed to animated image (webp) or static image.
> **⚠️ Note:** scene/marker preview videos must be generated to see them in the applicable wall page if Video preview type is selected. Likewise, if Animated Image is selected, then Image Previews must be generated.
---
## Show Studios as text
By default, a scene's studio will be shown as an image overlay. Checking this option changes this to display studios as a text name instead.
---
## Scene Player options
By default, scene videos do not automatically start when navigating to the scenes page. Checking the "Auto-start video" option changes this to auto play scene videos.
The maximum loop duration option allows looping of shorter videos. Set this value to the maximum scene duration that scene videos should loop. Setting this to 0 disables this functionality.
---
## Custom CSS
The stash UI can be customised using custom CSS. See [here](https://github.com/stashapp/stash/wiki/Custom-CSS-snippets){:target="_blank"} for a community-curated set of CSS snippets to customise your UI.
[Stash Plex Theme](https://github.com/stashapp/stash/wiki/Theme-Plex){:target="_blank"} is a community created theme inspired by the popular Plex interface.
---
## Custom Locales
The localisation strings can be customised. The master list of default (en-GB) locale strings can be found [here](https://github.com/stashapp/stash/blob/develop/ui/v2.5/src/locales/en-GB.json){:target="_blank"}. The custom locale format is the same as this json file.
For example, to override the `actions.add_directory` label (which is `Add Directory` by default), you would have the following in the custom locale:
```
{
"actions": {
"add_directory": "Some other description"
}
}
```
---
## Custom served folders
It is possible to expose specific folders to the UI. This configuration is performed manually in the `config.yml` file only.
Custom served content is exposed via the `/custom` URL path prefix.
For example, in the `config.yml` file:
```
custom_served_folders:
/: D:\stash\static
/foo: D:\bar
```
With the above configuration, a request for `/custom/foo/bar.png` would return `D:\bar\bar.png`. The `/` entry matches anything that is not otherwise mapped by the other entries. For example, `/custom/baz/xyz.png` would return `D:\stash\static\baz\xyz.png`.
Applications for this include using static images in custom css, like the Plex theme. For example, using the following config:
```yml
custom_served_folders:
/: <stash folder>\custom
```
The `background.png` and `noise.png` files can be placed in the `custom` folder, then in the custom css, the `./background.png` and `./noise.png` strings can be replaced with `/custom/background.png` and `/custom/noise.png` respectively.
Other applications are to add custom UIs to stash, accessible via `/custom`.

View File

@ -0,0 +1,28 @@
---
layout: default
title: Introduction
nav_order: 1
parent: In-app Manual
---
# **Introduction**
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
Stash works by cataloging your media using the paths that you provide. Once you have [configured](http://localhost:9999/settings?tab=library){:target="_blank"} the locations where your media is stored, you can click the Scan button in [`Settings -> Tasks`](http://localhost:9999/settings?tab=tasks){:target="_blank"} and stash will begin scanning and importing your media into its library.
For the best experience, it is recommmended that after a scan is finished, that video previews and sprites are generated. You can do this in [`Settings -> Tasks`](http://localhost:9999/settings?tab=tasks){:target="_blank"}. Note that currently it is only possible to perform one task at a time and there is no task queue, so the Generate task should be performed after Scan is complete.
Once your media is imported, you are ready to begin creating Performers, Studios and Tags, and curating your content!

View File

@ -0,0 +1,218 @@
---
layout: default
title: Keyboard Shortcuts
nav_order: 13
parent: In-app Manual
---
# **Keyboard Shortcuts**
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
## Global shortcuts
| Keyboard sequence | Action |
|-------------------|--------|
| `?` | Display manual |
### Global Navigation
| Keyboard sequence | Target page |
|-------------------|--------|
| `g s` | Scenes |
| `g i` | Images |
| `g v` | Movies |
| `g k` | Markers |
| `g l` | Galleries |
| `g p` | Performers |
| `g u` | Studios |
| `g t` | Tags |
| `g z` | Settings |
---
## Query page shortcuts
| Keyboard sequence | Action |
|-------------------|--------|
| `/` | Focus search field |
| `f` | Show Add Filter dialog |
| `r` | Reshuffle if sorted by random |
| `v g` | Set view to grid |
| `v l` | Set view to list |
| `v w` | Set view to wall |
| `+` | Increase zoom slider |
| `-` | Decrease zoom slider |
| `←` | Previous page of results |
| `→` | Next page of results |
| `Shift + ←` | Go to current results page -10 |
| `Shift + →` | Go to current results page +10 |
| `Ctrl + Home` | Go to first page of results |
| `Ctrl + End` | Go to last page of results |
| `s a` | Select all on page |
| `s n` | Unselect all |
| `e` | Edit selected |
| `d d` | Delete selected |
---
## Scenes page shortcuts
| Keyboard sequence | Action |
|-------------------|--------|
| `p r` | Play random scene |
---
## Scene page shortcuts
| Keyboard sequence | Action |
|-------------------|--------|
| `a` | Details tab |
| `q` | Queue tab |
| `k` | Markers tab |
| `i` | File info tab |
| `e` | Edit tab |
| `,` | Hide/Show sidebar |
| `.` | Hide/Show scene scrubber |
| `o` | Increment O-Counter |
| `p n` | Play next scene in queue |
| `p p` | Play previous scene in queue |
| `p r` | Play random scene in queue |
| `{1-9}` | Seek to 10-90% duration |
| `[` | Scrub backwards 10% duration |
| `]` | Scrub forwards 10% duration |
### Scene Markers tab shortcuts
| Keyboard sequence | Action |
|-------------------|--------|
| `n` | Display Create Markers dialog |
### Edit Scene tab shortcuts
| Keyboard sequence | Action |
|-------------------|--------|
| `r {1-5}` | Set rating |
| `r 0` | Unset rating |
| `s s` | Save Scene |
| `d d` | Delete Scene |
| `Ctrl + v` | Paste Scene cover |
[//]: # "Commented until implementation is dealt with"
[//]: # "(| `l` | Focus Gallery selector |)"
[//]: # "(| `u` | Focus Studio selector |)"
[//]: # "(| `p` | Focus Performers selector |)"
[//]: # "(| `v` | Focus Movies selector |)"
[//]: # "(| `t` | Focus Tags selector |)"
---
## Movies Page shortcuts
| Keyboard sequence | Action |
|-------------------|--------|
| `n` | New Movie |
---
## Movie Page shortcuts
| Keyboard sequence | Action |
|-------------------|--------|
| `e` | Edit Movie |
| `s s` | Save Movie |
| `d d` | Delete Movie |
| `r {1-5}` | Set rating (in edit mode) |
| `r 0` | Unset rating (in edit mode) |
| `Ctrl + v` | Paste Movie image |
[//]: # "Commented until implementation is dealt with"
[//]: # "(| `u` | Focus Studio selector (in edit mode) |)"
---
## Markers Page shortcuts
| Keyboard sequence | Action |
|-------------------|--------|
| `p r` | Play random marker |
---
## Performers Page shortcuts
| Keyboard sequence | Action |
|-------------------|--------|
| `n` | New Performer |
| `p r` | Open random Performer |
---
## Performer Page shortcuts
| Keyboard sequence | Action |
|-------------------|--------|
| `a` | Details tab |
| `c` | Scenes tab |
| `e` | Edit tab |
| `o` | Operations tab |
| `f` | Toggle favourite |
### Edit Performer tab shortcuts
| Keyboard sequence | Action |
|-------------------|--------|
| `s s` | Save Performer |
| `d d` | Delete Performer |
| `Ctrl + v` | Paste Performer image |
---
## Studios Page shortcuts
| Keyboard sequence | Action |
|-------------------|--------|
| `n` | New Studio |
---
## Studio Page shortcuts
| Keyboard sequence | Action |
|-------------------|--------|
| `e` | Edit Studio |
| `s s` | Save Studio |
| `d d` | Delete Studio |
| `Ctrl + v` | Paste Studio image |
---
## Tags Page shortcuts
| Keyboard sequence | Action |
|-------------------|--------|
| `n` | New Tag |
---
## Tag Page shortcuts
| Keyboard sequence | Action |
|-------------------|--------|
| `e` | Edit Tag |
| `s s` | Save Tag |
| `d d` | Delete Tag |
| `Ctrl + v` | Paste Tag image |

View File

@ -0,0 +1,885 @@
---
layout: default
title: Contributing Scrapers
nav_order: 1
parent: Metadata Scraping
grand_parent: In-app Manual
---
# **Contributing Scrapers**
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
Scrapers can be contributed to the community by creating a PR in [this repository](https://github.com/stashapp/CommunityScrapers/pulls){:target="_blank"}.
---
## Scraper configuration file format
```yaml
name: <site>
performerByName:
<single scraper config>
performerByFragment:
<single scraper config>
performerByURL:
<multiple scraper URL configs>
sceneByName:
<single scraper config>
sceneByQueryFragment:
<single scraper config>
sceneByFragment:
<single scraper config>
sceneByURL:
<multiple scraper URL configs>
movieByURL:
<multiple scraper URL configs>
galleryByFragment:
<single scraper config>
galleryByURL:
<multiple scraper URL configs>
<other configurations>
```
`name` is mandatory, all other top-level fields are optional. The inclusion of each top-level field determines what capabilities the scraper has.
A scraper configuration in any of the top-level fields must at least have an `action` field. The other fields are required based on the value of the `action` field.
The scraping types and their required fields are outlined in the following table:
| Behavior | Required configuration |
|-----------|------------------------|
| Scraper in `Scrape...` dropdown button in Performer Edit page | Valid `performerByName` and `performerByFragment` configurations. |
| Scrape performer from URL | Valid `performerByURL` configuration with matching URL. |
| Scraper in query dropdown button in Scene Edit page | Valid `sceneByName` and `sceneByQueryFragment` configurations. |
| Scraper in `Scrape...` dropdown button in Scene Edit page | Valid `sceneByFragment` configuration. |
| Scrape scene from URL | Valid `sceneByURL` configuration with matching URL. |
| Scrape movie from URL | Valid `movieByURL` configuration with matching URL. |
| Scraper in `Scrape...` dropdown button in Gallery Edit page | Valid `galleryByFragment` configuration. |
| Scrape gallery from URL | Valid `galleryByURL` configuration with matching URL. |
URL-based scraping accepts multiple scrape configurations, and each configuration requires a `url` field. stash iterates through these configurations, attempting to match the entered URL against the `url` fields in the configuration. It executes the first scraping configuration where the entered URL contains the value of the `url` field.
---
## Actions
### Script
Executes a script to perform the scrape. The `script` field is required for this action and accepts a list of string arguments. For example:
```yaml
action: script
script:
- python
- iafdScrape.py
- query
```
If the script specifies the python executable, Stash will find the correct python executable for your system, either `python` or `python3`. So for example. this configuration could execute `python iafdScrape.py query` or `python3 iafdScrape.py query`.
`python3` will be looked for first and if it's not found, we'll check for `python`. In the case neither are found, you will get an error.
Stash sends data to the script process's `stdin` stream and expects the output to be streamed to the `stdout` stream. Any errors and progress messages should be output to `stderr`.
The script is sent input and expects output based on the scraping type, as detailed in the following table:
| Scrape type | Input | Output |
|-------------|-------|--------|
| `performerByName` | `{"name": "<performer query string>"}` | Array of JSON-encoded performer fragments (including at least `name`) |
| `performerByFragment` | JSON-encoded performer fragment | JSON-encoded performer fragment |
| `performerByURL` | `{"url": "<url>"}` | JSON-encoded performer fragment |
| `sceneByName` | `{"name": "<scene query string>"}` | Array of JSON-encoded scene fragments |
| `sceneByQueryFragment`, `sceneByFragment` | JSON-encoded scene fragment | JSON-encoded scene fragment |
| `sceneByURL` | `{"url": "<url>"}` | JSON-encoded scene fragment |
| `movieByURL` | `{"url": "<url>"}` | JSON-encoded movie fragment |
| `galleryByFragment` | JSON-encoded gallery fragment | JSON-encoded gallery fragment |
| `galleryByURL` | `{"url": "<url>"}` | JSON-encoded gallery fragment |
For `performerByName`, only `name` is required in the returned performer fragments. One entire object is sent back to `performerByFragment` to scrape a specific performer, so the other fields may be included to assist in scraping a performer. For example, the `url` field may be filled in for the specific performer page, then `performerByFragment` can extract by using its value.
Python example of a performer Scraper:
```python
import json
import sys
import string
def readJSONInput():
input = sys.stdin.read()
return json.loads(input)
def searchPerformer(name):
# perform scraping here - using name for the query
# fill in the output
ret = []
# example shown for a single found performer
p = {}
p['name'] = "some name"
p['url'] = "performer url"
ret.append(p)
return ret
def scrapePerformer(input):
ret = []
# get the url from the input
url = input['url']
return scrapePerformerURL(url)
def debugPrint(t):
sys.stderr.write(t + "\n")
def scrapePerformerURL(url):
debugPrint("Reading url...")
debugPrint("Parsing html...")
# parse html
# fill in performer details - single object
ret = {}
ret['name'] = "fred"
ret['aliases'] = "freddy"
ret['ethnicity'] = ""
# and so on
return ret
# read the input
i = readJSONInput()
if sys.argv[1] == "query":
ret = searchPerformer(i['name'])
print(json.dumps(ret))
elif sys.argv[1] == "scrape":
ret = scrapePerformer(i)
print(json.dumps(ret))
elif sys.argv[1] == "scrapeURL":
ret = scrapePerformerURL(i['url'])
print(json.dumps(ret))
```
### scrapeXPath
This action scrapes a web page using an xpath configuration to parse. This action is **not valid** for `performerByFragment`.
This action requires that the top-level `xPathScrapers` configuration is populated. The `scraper` field is required and must match the name of a scraper name configured in `xPathScrapers`. For example:
```yaml
sceneByURL:
- action: scrapeXPath
url:
- pornhub.com/view_video.php
scraper: sceneScraper
```
The above configuration requires that `sceneScraper` exists in the `xPathScrapers` configuration.
XPath scraping configurations specify the mapping between object fields and an xpath selector. The xpath scraper scrapes the applicable URL and uses xpath to populate the object fields.
>
### scrapeJson
This action works in the same way as `scrapeXPath`, but uses a mapped json configuration to parse. It uses the top-level `jsonScrapers` configuration. This action is **not valid** for `performerByFragment`.
JSON scraping configurations specify the mapping between object fields and a GJSON selector. The JSON scraper scrapes the applicable URL and uses [GJSON](https://github.com/tidwall/gjson/blob/master/SYNTAX.md){:target="_blank"} to parse the returned JSON object and populate the object fields.
### scrapeXPath and scrapeJson use with `performerByName`
For `performerByName`, the `queryURL` field must be present also. This field is used to perform a search query URL for performer names. The placeholder string sequence `{}` is replaced with the performer name search string. For the subsequent performer scrape to work, the `URL` field must be filled in with the URL of the performer page that matches a URL given in a `performerByURL` scraping configuration. For example:
```yaml
name: Boobpedia
performerByName:
action: scrapeXPath
queryURL: http://www.boobpedia.com/wiki/index.php?title=Special%3ASearch&search={}&fulltext=Search
scraper: performerSearch
performerByURL:
- action: scrapeXPath
url:
- boobpedia.com/boobs/
scraper: performerScraper
xPathScrapers:
performerSearch:
performer:
Name: # name element
URL: # URL element that matches the boobpedia.com/boobs/ URL above
performerScraper:
# ... performer scraper details ...
```
### scrapeXPath and scrapeJson use with `sceneByFragment` and `sceneByQueryFragment`
For `sceneByFragment` and `sceneByQueryFragment`, the `queryURL` field must also be present. This field is used to build a query URL for scenes. For `sceneByFragment`, the `queryURL` field supports the following placeholder fields:
* `{checksum}` - the MD5 checksum of the scene
* `{oshash}` - the oshash of the scene
* `{filename}` - the base filename of the scene
* `{title}` - the title of the scene
* `{url}` - the url of the scene
These placeholder field values may be manipulated with regex replacements by adding a `queryURLReplace` section, containing a map of placeholder field to regex configuration which uses the same format as the `replace` post-process action covered below.
For example:
```yaml
sceneByFragment:
action: scrapeJson
scraper: sceneQueryScraper
queryURL: https://metadataapi.net/api/scenes?parse={filename}&limit=1
queryURLReplace:
filename:
- regex: <some regex>
with: <replacement>
```
The above configuration would scrape from the value of `queryURL`, replacing `{filename}` with the base filename of the scene, after it has been manipulated by the regex replacements.
### scrapeXPath and scrapeJson use with `<scene|performer|gallery|movie>ByURL`
For `sceneByURL`, `performerByURL`, `galleryByURL` the `queryURL` can also be present if we want to use `queryURLReplace`. The functionality is the same as `sceneByFragment`, the only placeholder field available though is the `url`:
* `{url}` - the url of the scene/performer/gallery
```yaml
sceneByURL:
- action: scrapeJson
url:
- metartnetwork.com
scraper: sceneScraper
queryURL: "{url}"
queryURLReplace:
url:
- regex: '^(?:.+\.)?([^.]+)\.com/.+movie/(\d+)/(\w+)/?$'
with: https://www.$1.com/api/movie?name=$3&date=$2
```
### Stash
A different stash server can be configured as a scraping source. This action applies only to `performerByName`, `performerByFragment`, and `sceneByFragment` types. This action requires that the top-level `stashServer` field is configured.
`stashServer` contains a single `url` field for the remote stash server. The username and password can be embedded in this string using `username:password@host`.
An example stash scrape configuration is below:
```yaml
name: stash
performerByName:
action: stash
performerByFragment:
action: stash
sceneByFragment:
action: stash
stashServer:
url: http://stashserver.com:9999
```
---
## Xpath and JSON scrapers configuration
The top-level `xPathScrapers` field contains xpath scraping configurations, freely named. These are referenced in the `scraper` field for `scrapeXPath` scrapers.
Likewise, the top-level `jsonScrapers` field contains json scraping configurations.
Collectively, these configurations are known as mapped scraping configurations.
A mapped scraping configuration may contain a `common` field, and must contain `performer`, `scene`, `movie` or `gallery` depending on the scraping type it is configured for.
Within the `performer`/`scene`/`movie`/`gallery` field are key/value pairs corresponding to the [golang fields](#object-fields) on the performer/scene object. These fields are case-sensitive.
The values of these may be either a simple selector value, which tells the system where to get the value of the field from, or a more advanced configuration (see below). For example, for an xpath configuration:
```yaml
performer:
Name: //h1[@itemprop="name"]
```
This will set the `Name` attribute of the returned performer to the text content of the element that matches `<h1 itemprop="name">...`.
For a json configuration:
```yaml
performer:
Name: data.name
```
The value may also be a sub-object. If it is a sub-object, then the selector must be set to the `selector` key of the sub-object. For example, using the same xpath as above:
```yaml
performer:
Name:
selector: //h1[@itemprop="name"]
postProcess:
# post-processing config values
```
### Fixed attribute values
Alternatively, an attribute value may be set to a fixed value, rather than scraping it from the webpage. This can be done by replacing `selector` with `fixed`. For example:
```yaml
performer:
Gender:
fixed: Female
```
### Common fragments
The `common` field is used to configure selector fragments that can be referenced in the selector strings. These are key-value pairs where the key is the string to reference the fragment, and the value is the string that the fragment will be replaced with. For example:
```yaml
common:
$infoPiece: //div[@class="infoPiece"]/span
performer:
Measurements: $infoPiece[text() = 'Measurements:']/../span[@class="smallInfo"]
```
The `Measurements` xpath string will replace `$infoPiece` with `//div[@class="infoPiece"]/span`, resulting in: `//div[@class="infoPiece"]/span[text() = 'Measurements:']/../span[@class="smallInfo"]`.
> **⚠️ Note:** Recursive common fragments are **not** supported.
Referencing a common fragment within another common fragment will cause an error. For example:
```yaml
common:
$info: //div[@class="info"]
# Referencing $info in $models will cause an error
$models: $info/a[@class="model"]
scene:
Title: $info/h1
Performers:
Name: $models
URL: $models/@href
```
### Post-processing options
Post-processing operations are contained in the `postProcess` key. Post-processing operations are performed in the order they are specified. The following post-processing operations are available:
* `feetToCm`: converts a string containing feet and inches numbers into centimeters. Looks for up to two separate integers and interprets the first as the number of feet, and the second as the number of inches. The numbers can be separated by any non-numeric character including the `.` character. It does not handle decimal numbers. For example `6.3` and `6ft3.3` would both be interpreted as 6 feet, 3 inches before converting into centimeters.
* `lbToKg`: converts a string containing lbs to kg.
* `map`: contains a map of input values to output values. Where a value matches one of the input values, it is replaced with the matching output value. If no value is matched, then value is unmodified.
Example:
```yaml
performer:
Gender:
selector: //div[@class="example element"]
postProcess:
- map:
F: Female
M: Male
Height:
selector: //span[@id="height"]
postProcess:
- feetToCm: true
Weight:
selector: //span[@id="weight"]
postProcess:
- lbToKg: true
```
Gets the contents of the selected div element, and sets the returned value to `Female` if the scraped value is `F`; `Male` if the scraped value is `M`.
Height and weight are extracted from the selected spans and converted to `cm` and `kg`.
* `parseDate`: if present, the value is the date format using go's reference date (2006-01-02). For example, if an example date was `14-Mar-2003`, then the date format would be `02-Jan-2006`. See the [time.Parse documentation](https://golang.org/pkg/time/#Parse){:target="_blank"} for details. When present, the scraper will convert the input string into a date, then convert it to the string format used by stash (`YYYY-MM-DD`). Strings "Today", "Yesterday" are matched (case insensitive) and converted by the scraper so you don't need to edit/replace them.
Unix timestamps (example: 1660169451) can also be parsed by selecting `unix` as the date format.
Example:
```yaml
Date:
selector: //div[@class="value epoch"]/text()
postProcess:
- parseDate: unix
```
* `subtractDays`: if set to `true` it subtracts the value in days from the current date and returns the resulting date in stash's date format.
Example:
```yaml
Date:
selector: //strong[contains(text(),"Added:")]/following-sibling::text()
postProcess:
- replace:
- regex: (\d+)\sdays\sago.+
with: $1
- subtractDays: true
```
* `replace`: contains an array of sub-objects. Each sub-object must have a `regex` and `with` field. The `regex` field is the regex pattern to replace, and `with` is the string to replace it with. `$` is used to reference capture groups - `$1` is the first capture group, `$2` the second and so on. Replacements are performed in order of the array.
Example:
```yaml
CareerLength:
selector: $infoPiece[text() = 'Career Start and End:']/../span[@class="smallInfo"]
postProcess:
- replace:
- regex: \s+to\s+
with: "-"
```
Replaces `2001 to 2003` with `2001-2003`.
* `subScraper`: if present, the sub-scraper will be executed after all other post-processes are complete and before parseDate. It then takes the value and performs an http request, using the value as the URL. Within the `subScraper` config is a nested scraping configuration. This allows you to traverse to other webpages to get the attribute value you are after. For more info and examples have a look at [#370](https://github.com/stashapp/stash/pull/370){:target="_blank"}, [#606](https://github.com/stashapp/stash/pull/606){:target="_blank"}
Additionally, there are a number of fixed post-processing fields that are specified at the attribute level (not in `postProcess`) that are performed after the `postProcess` operations:
* `concat`: if an xpath matches multiple elements, and `concat` is present, then all of the elements will be concatenated together
* `split`: the inverse of `concat`. Splits a string to more elements using the separator given. For more info and examples have a look at PR [#579](https://github.com/stashapp/stash/pull/579){:target="_blank"}
Example:
```yaml
Tags:
Name:
selector: //span[@class="list_attributes"]
split: ","
```
Splits a comma separated list of tags located in the span and returns the tags.
For backwards compatibility, `replace`, `subscraper` and `parseDate` are also allowed as keys for the attribute.
Post-processing on attribute post-process is done in the following order: `concat`, `replace`, `subscraper`, `parseDate` and then `split`.
### XPath resources:
- Test XPaths in Firefox: [](https://addons.mozilla.org/en-US/firefox/addon/try-xpath/){:target="_blank"}
- XPath cheatsheet: [](https://devhints.io/xpath){:target="_blank"}
### GJSON resources:
- GJSON Path Syntax: [](https://github.com/tidwall/gjson/blob/master/SYNTAX.md){:target="_blank"}
### Debugging support
To print the received html/json from a scraper request to the log file, add the following to your scraper yml file:
```yaml
debug:
printHTML: true
```
### CDP support
Some websites deliver content that cannot be scraped using the raw html file alone. These websites use javascript to dynamically load the content. As such, direct xpath scraping will not work on these websites. There is an option to use Chrome DevTools Protocol to load the webpage using an instance of Chrome, then scrape the result.
Chrome CDP support can be enabled for a specific scraping configuration by adding the following to the root of the yml configuration:
```yaml
driver:
useCDP: true
```
Optionally, you can add a `sleep` value under the `driver` section. This specifies the amount of time (in seconds) that the scraper should wait after loading the website to perform the scrape. This is needed as some sites need more time for loading scripts to finish. If unset, this value defaults to 2 seconds.
When `useCDP` is set to true, stash will execute or connect to an instance of Chrome. The behavior is dictated by the `Chrome CDP path` setting in the user configuration. If left empty, stash will attempt to find the Chrome executable in the path environment, and will fail if it cannot find one.
`Chrome CDP path` can be set to a path to the chrome executable, or an http(s) address to remote chrome instance (for example: `http://localhost:9222/json/version`). As remote instance a docker container can also be used with the `chromedp/headless-shell` image being highly recommended.
### CDP Click support
When using CDP you can use the `clicks` part of the `driver` section to do Mouse Clicks on elements you need to collapse or toggle. Each click element has an `xpath` value that holds the XPath for the button/element you need to click and an optional `sleep` value that is the time in seconds to wait for after clicking.
If the `sleep` value is not set it defaults to `2` seconds.
A demo scraper using `clicks` follows.
```yaml
name: clickDemo # demo only for a single URL
sceneByURL:
- action: scrapeXPath
url:
- https://getbootstrap.com/docs/4.3/components/collapse/
scraper: sceneScraper
xPathScrapers:
sceneScraper:
scene:
Title: //head/title
Details: # shows the id/s of the the visible div/s for the Multiple targets example of the page
selector: //div[@class="bd-example"]//div[@class="multi-collapse collapse show"]/@id
concat: "\n\n"
driver:
useCDP: true
sleep: 1
clicks: # demo usage toggle on off multiple times
- xpath: //a[@href="#multiCollapseExample1"] # toggle on first element
- xpath: //button[@data-target="#multiCollapseExample2"] # toggle on second element
sleep: 4
- xpath: //a[@href="#multiCollapseExample1"] # toggle off fist element
sleep: 1
- xpath: //button[@data-target="#multiCollapseExample2"] # toggle off second element
- xpath: //button[@data-target="#multiCollapseExample2"] # toggle on second element
```
> **⚠️ Note:** each `click` adds an extra delay of `clicks sleep` seconds, so the above adds `2+4+1+2+2=11` seconds to the loading time of the page.
### Cookie support
In some websites the use of cookies is needed to bypass a welcoming message or some other kind of protection. Stash supports the setting of cookies for the direct xpath scraper and the CDP based one. Due to implementation issues the usage varies a bit.
To use the cookie functionality a `cookies` sub section needs to be added to the `driver` section.
Each cookie element can consist of a `CookieURL` and a number of `Cookies`.
* `CookieURL` is only needed if you are using the direct / native scraper method. It is the request url that we expect from the site we scrape. It must be in the same domain as the cookies we try to set otherwise all cookies in the same group will fail to set. If the `CookieURL` is not a valid URL then again the cookies of that group will fail.
* `Cookies` are the actual cookies we set. When using CDP that's the only part required. They have `Name`, `Value`, `Domain`, `Path` values.
In the following example we use cookies for a site using the direct / native xpath scraper. We expect requests to come from `https://www.example.com` and `https://api.somewhere.com` that look for a `_warning` and a `_warn` cookie. A `_test2` cookie is also set just as a demo.
```yaml
driver:
cookies:
- CookieURL: "https://www.example.com"
Cookies:
- Name: "_warning"
Domain: ".example.com"
Value: "true"
Path: "/"
- Name: "_test2"
Value: "123412"
Domain: ".example.com"
Path: "/"
- CookieURL: "https://api.somewhere.com"
Cookies:
- Name: "_warn"
Value: "123"
Domain: ".somewhere.com"
```
The same functionality when using CDP would look like this:
```yaml
driver:
useCDP: true
cookies:
- Cookies:
- Name: "_warning"
Domain: ".example.com"
Value: "true"
Path: "/"
- Name: "_test2"
Value: "123412"
Domain: ".example.com"
Path: "/"
- Cookies:
- Name: "_warn"
Value: "123"
Domain: ".somewhere.com"
```
For some sites, the value of the cookie itself doesn't actually matter. In these cases, we can use the `ValueRandom`
property instead of `Value`. Unlike `Value`, `ValueRandom` requires an integer value greater than `0` where the value
indicates how long the cookie string should be.
In the following example, we will adapt the previous cookies to use `ValueRandom` instead. We set the `_test2` cookie
to randomly generate a value with a length of 6 characters and the `_warn` cookie to a length of 3.
```yaml
driver:
cookies:
- CookieURL: "https://www.example.com"
Cookies:
- Name: "_warning"
Domain: ".example.com"
Value: "true"
Path: "/"
- Name: "_test2"
ValueRandom: 6
Domain: ".example.com"
Path: "/"
- CookieURL: "https://api.somewhere.com"
Cookies:
- Name: "_warn"
ValueRandom: 3
Domain: ".somewhere.com"
```
When developing a scraper you can have a look at the cookies set by a site by adding
* a `CookieURL` if you use the direct xpath scraper
* a `Domain` if you use the CDP scraper
and having a look at the log / console in debug mode.
### Headers
Sending request headers is possible when using a scraper.
Headers can be set in the `driver` section and are supported for plain, CDP enabled and JSON scrapers.
They consist of a Key and a Value. If the the Key is empty or not defined then the header is ignored.
```yaml
driver:
headers:
- Key: User-Agent
Value: My Stash Scraper
- Key: Authorization
Value: Bearer ds3sdfcFdfY17p4qBkTVF03zscUU2glSjWF17bZyoe8
```
* headers are set after stash's `User-Agent` configuration option is applied.
This means setting a `User-Agent` header from the scraper overrides the one in the configuration settings.
### XPath scraper example
A performer and scene xpath scraper is shown as an example below:
```yaml
name: Pornhub
performerByURL:
- action: scrapeXPath
url:
- pornhub.com
scraper: performerScraper
sceneByURL:
- action: scrapeXPath
url:
- pornhub.com/view_video.php
scraper: sceneScraper
xPathScrapers:
performerScraper:
common:
$infoPiece: //div[@class="infoPiece"]/span
performer:
Name: //h1[@itemprop="name"]
Birthdate:
selector: //span[@itemprop="birthDate"]
parseDate: Jan 2, 2006
Twitter: //span[text() = 'Twitter']/../@href
Instagram: //span[text() = 'Instagram']/../@href
Measurements: $infoPiece[text() = 'Measurements:']/../span[@class="smallInfo"]
Height:
selector: $infoPiece[text() = 'Height:']/../span[@class="smallInfo"]
postProcess:
- replace:
- regex: .*\((\d+) cm\)
with: $1
Ethnicity: $infoPiece[text() = 'Ethnicity:']/../span[@class="smallInfo"]
FakeTits: $infoPiece[text() = 'Fake Boobs:']/../span[@class="smallInfo"]
Piercings: $infoPiece[text() = 'Piercings:']/../span[@class="smallInfo"]
Tattoos: $infoPiece[text() = 'Tattoos:']/../span[@class="smallInfo"]
CareerLength:
selector: $infoPiece[text() = 'Career Start and End:']/../span[@class="smallInfo"]
postProcess:
- replace:
- regex: \s+to\s+
with: "-"
sceneScraper:
common:
$performer: //div[@class="pornstarsWrapper"]/a[@data-mxptype="Pornstar"]
$studio: //div[@data-type="channel"]/a
scene:
Title: //div[@id="main-container"]/@data-video-title
Tags:
Name: //div[@class="categoriesWrapper"]//a[not(@class="add-btn-small ")]
Performers:
Name: $performer/@data-mxptext
URL: $performer/@href
Studio:
Name: $studio
URL: $studio/@href
```
See also [#333](https://github.com/stashapp/stash/pull/333){:target="_blank"} for more examples.
### JSON scraper example
A performer and scene scraper for ThePornDB is shown below:
```yaml
name: ThePornDB
performerByName:
action: scrapeJson
queryURL: https://api.metadataapi.net/performers?q={}
scraper: performerSearch
performerByURL:
- action: scrapeJson
url:
- https://api.metadataapi.net/performers/
scraper: performerScraper
sceneByURL:
- action: scrapeJson
url:
- https://api.metadataapi.net/scenes/
scraper: sceneScraper
sceneByFragment:
action: scrapeJson
queryURL: https://api.metadataapi.net/scenes?parse={filename}&hash={oshash}&limit=1
scraper: sceneQueryScraper
queryURLReplace:
filename:
- regex: "[^a-zA-Z\\d\\-._~]" # clean filename so that it can construct a valid url
with: "." # "%20"
- regex: HEVC
with:
- regex: x265
with:
- regex: \.+
with: "."
jsonScrapers:
performerSearch:
performer:
Name: data.#.name
URL:
selector: data.#.id
postProcess:
- replace:
- regex: ^
with: https://api.metadataapi.net/performers/
performerScraper:
common:
$extras: data.extras
performer:
Name: data.name
Gender: $extras.gender
Birthdate: $extras.birthday
Ethnicity: $extras.ethnicity
Height:
selector: $extras.height
postProcess:
- replace:
- regex: cm
with:
Measurements: $extras.measurements
Tattoos: $extras.tattoos
Piercings: $extras.piercings
Aliases: data.aliases
Image: data.image
sceneScraper:
common:
$performers: data.performers
scene:
Title: data.title
Details: data.description
Date: data.date
URL: data.url
Image: data.background.small
Performers:
Name: data.performers.#.name
Studio:
Name: data.site.name
Tags:
Name: data.tags.#.tag
sceneQueryScraper:
common:
$data: data.0
$performers: data.0.performers
scene:
Title: $data.title
Details: $data.description
Date: $data.date
URL: $data.url
Image: $data.background.small
Performers:
Name: $data.performers.#.name
Studio:
Name: $data.site.name
Tags:
Name: $data.tags.#.tag
driver:
headers:
- Key: User-Agent
Value: Stash JSON Scraper
- Key: Authorization
Value: Bearer lPdwFdfY17p4qBkTVF03zscUU2glSjdf17bZyoe # use an actual API Key here
# Last Updated April 7, 2021
```
---
## Object fields
### Performer
```
Name
Gender
URL
Twitter
Instagram
Birthdate
DeathDate
Ethnicity
Country
HairColor
EyeColor
Height
Weight
Measurements
FakeTits
CareerLength
Tattoos
Piercings
Aliases
Tags (see Tag fields)
Image
Details
```
*Note:* - `Gender` must be one of `male`, `female`, `transgender_male`, `transgender_female`, `intersex`, `non_binary` (case insensitive).
### Scene
```
Title
Details
Code
Director
URL
Date
Image
Studio (see Studio Fields)
Movies (see Movie Fields)
Tags (see Tag fields)
Performers (list of Performer fields)
```
### Studio
```
Name
URL
```
### Tag
```
Name
```
### Movie
```
Name
Aliases
Duration
Date
Rating
Director
Studio
Synopsis
URL
FrontImage
BackImage
```
### Gallery
```
Title
Details
URL
Date
Rating
Studio (see Studio Fields)
Tags (see Tag fields)
Performers (list of Performer fields)
```

View File

@ -0,0 +1,206 @@
---
layout: default
title: Plugins
nav_order: 8
parent: In-app Manual
has_children: true
has_toc: false
---
# **Plugins**
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
Stash supports the running tasks via plugins. Plugins can be implemented using embedded Javascript, or by calling an external binary.
Stash also supports triggering of plugin hooks from specific stash operations.
> **⚠️ Note:** Plugin support is still experimental and is likely to change.
---
## Adding plugins
By default, Stash looks for plugin configurations in the `plugins` sub-directory of the directory where the stash `config.yml` is read. This will either be the `$HOME/.stash` directory or the current working directory.
Plugins are added by adding configuration yaml files (format: `pluginName.yml`) to the `plugins` directory.
Loaded plugins can be viewed in the Plugins page of the Settings. After plugins are added, removed or edited while stash is running, they can be reloaded by clicking `Reload Plugins` button.
---
## Using plugins
Plugins provide tasks which can be run from the Tasks page.
---
## Creating plugins
See [External Plugins]({{ site.baseurl }}/docs/In-app-Manual/Plugins/ExternalPlugins) for details for making external plugins.
See [Embedded Plugins]({{ site.baseurl }}/docs/In-app-Manual/Plugins/EmbeddedPlugins) for details for making embedded plugins.
### Plugin input
Plugins may accept an input from the stash server. This input is encoded according to the interface, and has the following structure (presented here in JSON format):
```
{
"server_connection": {
"Scheme": "http",
"Port": 9999,
"SessionCookie": {
"Name":"session",
"Value":"cookie-value",
"Path":"",
"Domain":"",
"Expires":"0001-01-01T00:00:00Z",
"RawExpires":"",
"MaxAge":0,
"Secure":false,
"HttpOnly":false,
"SameSite":0,
"Raw":"",
"Unparsed":null
},
"Dir": <path to stash config directory>,
"PluginDir": <path to plugin config directory>,
},
"args": {
"argKey": "argValue"
}
}
```
The `server_connection` field contains all the information needed for a plugin to access the parent stash server, if necessary.
### Plugin output
Plugin output is expected in the following structure (presented here as JSON format):
```
{
"error": <optional error string>
"output": <anything>
}
```
The `error` field is logged in stash at the `error` log level if present. The `output` is written at the `debug` log level.
### Task configuration
Tasks are configured using the following structure:
```
tasks:
- name: <operation name>
description: <optional description>
defaultArgs:
argKey: argValue
```
A plugin configuration may contain multiple tasks.
The `defaultArgs` field is used to add inputs to the plugin input sent to the plugin.
### Hook configuration
Stash supports executing plugin operations via triggering of a hook during a stash operation.
Hooks are configured using a similar structure to tasks:
```
hooks:
- name: <operation name>
description: <optional description>
triggeredBy:
- <trigger types>...
defaultArgs:
argKey: argValue
```
**Note:** it is possible for hooks to trigger eachother or themselves if they perform mutations. For safety, hooks will not be triggered if they have already been triggered in the context of the operation. Stash uses cookies to track this context, so it's important for plugins to send cookies when performing operations.
#### Trigger types
Trigger types use the following format:
`<object type>.<operation>.<hook type>`
For example, a post-hook on a scene create operation will be `Scene.Create.Post`.
The following object types are supported:
* `Scene`
* `SceneMarker`
* `Image`
* `Gallery`
* `Movie`
* `Performer`
* `Studio`
* `Tag`
The following operations are supported:
* `Create`
* `Update`
* `Destroy`
* `Merge` (for `Tag` only)
Currently, only `Post` hook types are supported. These are executed after the operation has completed and the transaction is committed.
#### Hook input
Plugin tasks triggered by a hook include an argument named `hookContext` in the `args` object structure. The `hookContext` is structured as follows:
```
{
"id": <object id>,
"type": <trigger type>,
"input": <operation input>,
"inputFields": <fields included in input>
}
```
The `input` field contains the JSON graphql input passed to the original operation. This will differ between operations. For hooks triggered by operations in a scan or clean, the input will be nil. `inputFields` is populated in update operations to indicate which fields were passed to the operation, to differentiate between missing and empty fields.
For example, here is the `args` values for a Scene update operation:
```
{
"hookContext": {
"type":"Scene.Update.Post",
"id":45,
"input":{
"clientMutationId":null,
"id":"45",
"title":null,
"details":null,
"url":null,
"date":null,
"rating":null,
"organized":null,
"studio_id":null,
"gallery_ids":null,
"performer_ids":null,
"movies":null,
"tag_ids":["21"],
"cover_image":null,
"stash_ids":null
},
"inputFields":[
"tag_ids",
"id"
]
}
}
```

View File

@ -0,0 +1,164 @@
---
layout: default
title: Embedded Plugins
nav_order: 2
parent: Plugins
grand_parent: In-app Manual
---
# **Embedded Plugins**
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
Embedded plugins are executed within the stash process using a scripting system.
---
## Supported script languages
Stash currently supports Javascript embedded plugins using [otto](https://github.com/robertkrimen/otto){:target="_blank"}.
---
## Javascript plugins
### Plugin input
The input is provided to Javascript plugins using the `input` global variable, and is an object based on the structure provided in the `Plugin input` section of the [Plugins]({{ site.baseurl }}/docs/In-app-Manual/Plugins) page. Note that the `server_connection` field should not be necessary in most embedded plugins.
### Plugin output
The output of a Javascript plugin is derived from the evaluated value of the script. The output should conform to the structure provided in the `Plugin output` section of the [Plugins]({{ site.baseurl }}/docs/In-app-Manual/Plugins) page.
There are a number of ways to return the plugin output:
#### Example #1
```
(function() {
return {
Output: "ok"
};
})();
```
#### Example #2
```
function main() {
return {
Output: "ok"
};
}
main();
```
#### Example #3
```
var output = {
Output: "ok"
};
output;
```
### Logging
See the `Javascript API` section below on how to log with Javascript plugins.
---
## Plugin configuration file format
The basic structure of an embedded plugin configuration file is as follows:
```
name: <plugin name>
description: <optional description of the plugin>
version: <optional version tag>
url: <optional url>
exec:
- <path to script>
interface: [interface type]
tasks:
- ...
```
The `name`, `description`, `version` and `url` fields are displayed on the plugins page.
### exec
For embedded plugins, the `exec` field is a list with the first element being the path to the Javascript file that will be executed. It is expected that the path to the Javascript file is relative to the directory of the plugin configuration file.
### interface
For embedded plugins, the `interface` field must be set to one of the following values:
* `js`
---
## Javascript API
### Logging
Stash provides the following API for logging in Javascript plugins:
| Method | Description |
|--------|-------------|
| `log.Trace(<string>)` | Log with the `trace` log level. |
| `log.Debug(<string>)` | Log with the `debug` log level. |
| `log.Info(<string>)` | Log with the `info` log level. |
| `log.Warn(<string>)` | Log with the `warn` log level. |
| `log.Error(<string>)` | Log with the `error` log level. |
| `log.Progress(<float between 0 and 1>)` | Sets the progress of the plugin task, as a float, where `0` represents 0% and `1` represents 100%. |
### GQL
Stash provides the following API for communicating with stash using the graphql interface:
| Method | Description |
|--------|-------------|
| `gql.Do(<query/mutation string>, <variables object>)` | Executes a graphql query/mutation on the stash server. Returns an object in the same way as a graphql query does. |
#### Example
```
// creates a tag
var mutation = "\
mutation tagCreate($input: TagCreateInput!) {\
tagCreate(input: $input) {\
id\
}\
}";
var variables = {
input: {
'name': tagName
}
};
result = gql.Do(mutation, variables);
log.Info("tag id = " + result.tagCreate.id);
```
### Utility functions
Stash provides the following API for utility functions:
| Method | Description |
|--------|-------------|
| `util.Sleep(<milliseconds>)` | Suspends the current thread for the specified duration. |

View File

@ -0,0 +1,131 @@
---
layout: default
title: External Plugins
nav_order: 1
parent: Plugins
grand_parent: In-app Manual
---
# **External Plugins**
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
External plugins are executed by running an external binary.
## Plugin interfaces
---
Stash communicates with external plugins using an interface. Stash currently supports RPC and raw interface types.
### RPC interface
The RPC interface uses JSON-RPC to communicate with the plugin process. A golang plugin utilising the RPC interface is available in the stash source code under `pkg/plugin/examples/gorpc`. RPC plugins are expected to provide an interface that fulfils the `RPCRunner` interface in `pkg/plugin/common`.
RPC plugins are expected to accept requests asynchronously.
When stopping an RPC plugin task, the stash server sends a stop request to the plugin and relies on the plugin to stop itself.
### Raw interface
Raw interface plugins are not required to conform to any particular interface. The stash server will send the plugin input to the plugin process via its stdin stream, encoded as JSON. Raw interface plugins are not required to read the input.
The stash server reads stdout for the plugin's output. If the output can be decoded as a JSON representation of the plugin output data structure then it will do so. If not, it will treat the entire stdout string as the plugin's output.
When stopping a raw plugin task, the stash server kills the spawned process without warning or signals.
### Logging
External plugins may log to the stash server by writing to stderr. By default, data written to stderr will be logged by stash at the `error` level. This default behaviour can be changed by setting the `errLog` field in the plugin configuration file.
Plugins can log for specific levels or log progress by prefixing the output string with special control characters. See `pkg/plugin/common/log` for how this is done in go.
---
## Plugin configuration file format
The basic structure of an external plugin configuration file is as follows:
```
name: <plugin name>
description: <optional description of the plugin>
version: <optional version tag>
url: <optional url>
exec:
- <binary name>
- <other args...>
interface: [interface type]
errLog: [one of none trace, debug, info, warning, error]
tasks:
- ...
```
The `name`, `description`, `version` and `url` fields are displayed on the plugins page.
### exec
For external plugins, the `exec` field is a list with the first element being the binary that will be executed, and the subsequent elements are the arguments passed. The execution process will search the path for the binary, then will attempt to find the program in the same directory as the plugin configuration file. The `exe` extension is not necessary on Windows systems.
> **⚠️ Note:** The plugin execution process sets the current working directory to that of the stash process.
Arguments can include the plugin's directory with the special string `{pluginDir}`.
For example, if the plugin executable `my_plugin` is placed in the `plugins` subdirectory and requires arguments `foo` and `bar`, then the `exec` part of the configuration would look like the following:
```
exec:
- my_plugin
- foo
- bar
```
Another example might use a python script to execute the plugin. Assuming the python script `foo.py` is placed in the same directory as the plugin config file, the `exec` fragment would look like the following:
```
exec:
- python
- {pluginDir}/foo.py
```
### interface
For external plugins, the `interface` field must be set to one of the following values:
* `rpc`
* `raw`
See the `Plugin interfaces` section above for details on these interface types.
The `interface` field defaults to `raw` if not provided.
### errLog
The `errLog` field tells stash what the default log level should be when the plugin outputs to stderr without encoding a log level. It defaults to the `error` level if no provided. This field is not necessary if the plugin outputs logging with the appropriate encoding. See the `Logging` section above for details.
---
## Task configuration
In addition to the standard task configuration, external tags may be configured with an optional `execArgs` field to add extra parameters to the execution arguments for the task.
For example:
```
tasks:
- name: <operation name>
description: <optional description>
execArgs:
- <arg to add to the exec line>
```

View File

@ -0,0 +1,98 @@
---
layout: default
title: Metadata Scraping
nav_order: 7
parent: In-app Manual
has_children: true
has_toc: false
---
# **Metadata Scraping**
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
Stash supports scraping of metadata from various external sources.
### Scraper Types
| Type | Description |
|---|:---|
| Fragment | Uses existing metadata for an Item and match it to a result from a metadata source. |
| Search/By Name | Uses a provided query string to search a metadata source for a list of matches for the user to pick from. |
| URL | Extracts metadata from a given URL. |
### Supported Scrapers
| | Fragment | Search | URL |
|---|:---:|:---:|:---:|
| gallery | ✔️ | | ✔️ |
| movie | | | ✔️ |
| performer | | ✔️ | ✔️ |
| scene | ✔️ | ✔️ | ✔️ |
---
## Scraper Operation
### Included Scrapers
Stash provides the following built-in scrapers:
| Scraper | Description |
|---|--|
| Freeones | `search` Performer scraper for freeones.xxx. |
| Auto Tag | Scene `fragment` scraper that matches existing performers, studio and tags using the filename. |
### Adding Scrapers
By default, Stash looks for scraper configurations in the `scrapers` sub-directory of the directory where the stash `config.yml` is read. This will either be the `$HOME/.stash` directory or the current working directory.
Scrapers are added by placing yaml configuration files (format: `scrapername.yml`) in the `scrapers` directory.
> **⚠️ Note:** Some scrapers may require more than just the yaml file, consult the individual scraper documentation
After the yaml files are added, removed or edited while stash is running, they can be reloaded going to `Settings > Metadata Providers > Scrapers` and clicking `Reload Scrapers`.
The stash community maintains a number of custom scraper configuration files that can be found [here](https://github.com/stashapp/CommunityScrapers){:target="_blank"}.
### Using Scrapers
#### Fragment Scraper
Click on the `Scrape With...` button in the `edit` tab of an item, then select the scraper you wish to use.
#### Search Scraper
Click on the 🔍 button in the `edit` tab of an item. You will be presented with a search dialog with a pre-populated query to search for, after searching you will be presented with a list of results to pick from
#### URL Scraper
Enter the URL in the `edit` tab of an Item. If a scraper is installed that supports that url, then a button will appear to scrape the metadata.
### Tagger View
The Tagger view is accessed from the scenes page. It allows the user to run scrapers on all items on the current page. The Tagger presents the user with potential matches for an item from a selected stash-box instance or metadata source if supported. The user needs to select the correct metadata information to save.
When used in combination with stash-box, the user can optionally submit scene fingerprints to contribute to a stash-box instance. A scene fingerprint consists of any generated hashes (`phash`, `oshash`, `md5`) and the scene duration. Fingerprint submissions are associated with your stash-box account. Submitting fingerprints assists others in matching their files, because stash-box returns a count of matching user submitted fingerprints with every potential match.
| | Has Tagger | Source Selection |
|---|:---:|:---:|
| gallery | | |
| movie | | |
| performer | ✔️ | |
| scene | ✔️ | ✔️ |
### Identify Task
This task iterates through your Scenes and attempts to identify the scene using a selection of scraping sources. This task can be found under `Settings -> Tasks -> "Identify..." (Button)`. For more information see the [Tasks > Identify]({{ site.baseurl }}/docs/In-app-Manual/Tasks/Identify) page.

View File

@ -0,0 +1,43 @@
---
layout: default
title: Scene Tagger
nav_order: 9
parent: In-app Manual
---
# **Scene Tagger**
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
Stash can be integrated with stash-box which acts as a centralized metadata database. This is in the early stages of development but can be used for fingerprint/keyword lookups and automated tagging of performers and scenes. The batch tagging interface can be accessed from the [scene view](http://localhost:9999/scenes?disp=3). For more information join our [Discord](https://discord.gg/2TsNFKt){:target="_blank"}.
#### Searching
The fingerprint search matches your current selection of files against the remote stash-box instance. Any scenes with a matching fingerprint will be returned, although there is currently no validation of fingerprints so it&rsquo;s recommended to double-check the validity before saving.
If no fingerprint match is found it&rsquo;s possible to search by keywords. The search works by matching the query against a scene&rsquo;s _title_, _release date_, _studio name_, and _performer names_. By default the tagger uses metadata set on the file, or parses the filename, this can be changed in the config.
An important thing to note is that it only returns a match *if all query terms are a match*. As an example, if a scene is titled `"A Trip to the Mall"` with the performer `"Jane Doe"`, a search for `"Trip to the Mall 1080p"` will *not* match, however `"trip mall doe"` would. Usually a few pieces of info is enough, for instance performer name + release date or studio name. To avoid common non-related keywords you can add them to the blacklist in the tagger config. Any items in the blacklist are stripped out of the query.
#### Saving
When a scene is matched stash will try to match the studio and performers against your local studios and performers. If you have previously matched them, they will automatically be selected. If not you either have to select the correct performer/studio from the dropdown, choose create to create a new entity, or skip to ignore it.
Once a scene is saved the scene and the matched studio/performers will have the `stash_id` saved which will then be used for future tagging.
By default male performers are not shown, this can be enabled in the tagger config. Likewise scene tags are by default not saved. They can be set to either merge with existing tags on the scene, or overwrite them. It is not recommended to set tags currently since they are hard to deduplicate and can litter your data.
#### Submitting fingerprints
After a scene is saved you will prompted to submit the fingerprint back to the stash-box instance. This is optional, but can be helpful for other users who have an identical copy who will then be able to match via the fingerprint search. No other information than the `stash_id` and file fingerprint is submitted.

116
docs/In-app-Manual/Tasks.md Normal file
View File

@ -0,0 +1,116 @@
---
layout: default
title: Tasks
nav_order: 4
parent: In-app Manual
has_children: true
has_toc: false
---
# **Tasks**
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
This page allows you to direct the stash server to perform a variety of tasks.
---
## Scanning
The scan function walks through the stash directories you have configured for new and moved files.
Stash currently identifies files by performing a quick file hash. This means that if the file is renamed for moved elsewhere within your configured stash directories, then the scan will detect this and update its database accordingly.
Stash currently ignores duplicate files. If two files contain identical content, only the first one it comes across is used.
The scan task accepts the following options:
| Option | Description |
|--------|-------------|
| Generate previews | Generates video previews which play when hovering over a scene. |
| Generate animated image previews | Generates animated webp previews. Only required if the Preview Type is set to Animated Image. Requires Generate previews to be enabled. |
| Generate sprites | Generates sprites for the scene scrubber. |
| Generate perceptual hashes | Generates perceptual hashes for scene deduplication and identification. |
| Generate thumbnails for images | Generates thumbnails for image files. |
| Don't include file extension in title | By default, scenes, images and galleries have their title created using the file basename. When the flag is enabled, the file extension is stripped when setting the title. |
| Set name, date, details from embedded file metadata. | Parse the video file metadata (where supported) and set the scene attributes accordingly. It has previously been noted that this information is frequently incorrect, so only use this option where you are certain that the metadata is correct in the files. |
---
## Auto Tagging
See the [Auto Tagging]({{ site.baseurl }}/docs/In-app-Manual/Tasks/AutoTagging) page.
---
## Scene Filename Parser
See the [Scene Filename Parser]({{ site.baseurl }}/docs/In-app-Manual/Tasks/SceneFilenameParser) page.
----
## Generated Content
The scanning function automatically generates a screenshot of each scene. The generated content provides the following:
* Video or image previews that are played when mousing over the scene card
* Perceptual hashes - helps match against StashDB, and feeds the duplicate finder
* Sprites (scene stills for parts of each scene) that are shown in the scene scrubber
* Marker video previews that are shown in the markers page
* Transcoded versions of scenes. See below
* Image thumbnails of galleries
The generate task accepts the following options:
| Option | Description |
|--------|-------------|
| Previews | Generates video previews which play when hovering over a scene. |
| Animated image previews | Generates animated webp previews. Only required if the Preview Type is set to Animated Image. Requires Generate previews to be enabled. |
| Scene Scrubber Sprites | Generates sprites for the scene scrubber. |
| Markers Previews | Generates 20 second videos which begin at the marker timecode. |
| Marker Animated Image Previews | Generates animated webp previews for markers. Only required if the Preview Type is set to Animated Image. Requires Markers to be enabled. |
| Marker Screenshots | Generates static JPG images for markers. Only required if Preview Type is set to Static Image. Requires Marker Previews to be enabled. |
| Transcodes | MP4 conversions of unsupported video formats. Allows direct streaming instead of live transcoding. |
| Perceptual hashes | Generates perceptual hashes for scene deduplication and identification. |
| Overwrite existing generated files | By default, where a generated file exists, it is not regenerated. When this flag is enabled, then the generated files are regenerated. |
### Transcodes
Web browsers support a limited number of video and audio codecs and containers. Stash will directly stream video files where the browser supports the codecs and container. Originally, stash did not support viewing scene videos where the browser did not support the codecs/container, and generating transcodes was a way of viewing these files.
Stash has since implemented live transcoding, so transcodes are essentially unnecessary now. Further, transcodes use up a significant amount of disk space and are not guaranteed to be lossless.
### Image gallery thumbnails
These are generated when the gallery is first viewed, so generating them beforehand is not necessary.
---
## Cleaning
This task will walk through your configured media directories and remove any scene from the database that can no longer be found. It will also remove generated files for scenes that subsequently no longer exist.
Care should be taken with this task, especially where the configured media directories may be inaccessible due to network issues.
---
## Exporting and Importing
The import and export tasks read and write JSON files to the configured metadata directory. Import from file will merge your database with a file.
> **⚠️ Note:** The full import task wipes the current database completely before importing.
See the [JSON Specification]({{ site.baseurl }}/docs/In-app-Manual/Tasks/JSONSpec) page for details on the exported JSON format.
---

View File

@ -0,0 +1,39 @@
---
layout: default
title: Auto Tagging
nav_order: 2
parent: Tasks
grand_parent: In-app Manual
---
# **Auto Tagging**
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
This task matches your Performers, Studios, and Tags against your media, based on names only. It finds Scenes, Images, and Galleries where the path or filename contains the Performer/Studio/Tag.
For each scene it finds that matches, it sets the applicable field. It will **only** tag based on performers, studios, and tags that already exist in your database. In order to completely identify and gather information about the scenes in your collection, you will need to use the Tagger view and/or Scraping tools.
When the Performer/Studio/Tag name has multiple words, the search will include paths/filenames where the Performer/Studio/Tag name is separated with `.`, `-` or `_` characters, as well as whitespace.
For example, auto tagging for performer `Jane Doe` will match the following filenames:
* `Jane.Doe.1.mp4`
* `Jane_Doe.2.mp4`
* `Jane-Doe.3.mp4`
* `Jane Doe.4.mp4`
Matching is case insensitive, and should only match exact wording within word boundaries. For example, `Jane Doe` will not match `Maryjane-Doe`, but will match `Mary-Jane-Doe`.
Auto tagging for only specific Performers, Studios and Tags can be performed from the individual Performer/Studio/Tag page.

View File

@ -0,0 +1,55 @@
---
layout: default
title: Identify
nav_order: 1
parent: Tasks
grand_parent: In-app Manual
---
# **Identify**
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
This task iterates through your Scenes and attempts to identify the scene using a selection of scraping sources.
This task accepts one or more scraper sources. Valid scraper sources for the Identify task are stash-box instances, and scene scrapers which support scraping via Scene Fragment. The order of the sources may be rearranged.
For each Scene, the Identify task iterates through the scraper sources, in the order provided, and tries to identify the scene using each source. If a result is found in a source, then the Scene is updated, and no further sources are checked for that scene.
---
## Options
The following options can be set:
| Option | Description |
|--------|-------------|
| Include male performers | If false, then male performers will not be created or set on scenes. |
| Set cover images | If false, then scene cover images will not be modified. |
| Set organised flag | If true, the organised flag is set to true when a scene is organised. |
Field specific options may be set as well. Each field may have a Strategy. The behaviour for each strategy value is as follows:
| Strategy | Description |
|----------|-------------|
| Ignore | Not set. |
| Overwrite | Overwrite existing value. |
| Merge (*default*) | For multi-value fields, adds to existing values. For single-value fields, only sets if not already set. |
For Studio, Performers and Tags, an option is also available to Create Missing objects. This is false by default. When true, if a Studio/Performer/Tag is included during the identification process and does not exist in the system, then it will be created.
Default Options are applied to all sources unless overridden in specific source options.
The result of the identification process for each scene is output to the log.

View File

@ -0,0 +1,510 @@
---
layout: default
title: Import/Export JSON Specification
nav_order: 4
parent: Tasks
grand_parent: In-app Manual
---
# **Import/Export JSON Specification**
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
## Import/Export JSON Specification
The metadata given to Stash can be exported into the JSON format. This structure can be modified, or replicated by other means. The resulting data can then be imported again, giving the possibility for automatic scraping of all kinds. The format of this metadata bulk is a folder structure, containing the following folders:
* `files`
* `galleries`
* `images`
* `performers`
* `scenes`
* `studios`
* `movies`
---
## File naming
When exported, files are named with different formats depending on the object type:
| Type | Format |
|------|--------|
| Files/Folders | `<path depth in hex, two character width>.<basename>.<hash>.json` |
| Galleries | `<first zip filename>.<path hash>.json` or `<folder basename>.<path hash>.json` or `<title>.json` |
| Images | `<title or first file basename>.<hash>.json` |
| Performers | `<name>.json` |
| Scenes | `<title or first file basename>.<hash>.json` |
| Studios | `<name>.json` |
| Movies | `<name>.json` |
Note that the file naming is not significant when importing. All json files will be read from the subdirectories.
---
## Content of the json files
In the following, the values of the according jsons will be shown. If the value should be a number, it is written with after comma values (like `29.98` or `50.0`), but still as a string. The meaning from most of them should be obvious due to the previous explanation or from the possible values stash offers when editing, otherwise a short comment will be added.
The json values are given as strings, if not stated otherwise. Every new line will stand for a new value in the json. If the value is a list of objects, the values of that object will be shown indented.
If a value is empty in any file, it can be left out of the file entirely.
Many files have an `created_at` and `updated_at`, both are kept in the following format:
```
YYYY-MM-DDThh:mm:ssTZD
```
Example:
```
"created_at": "2019-05-03T21:36:58+01:00"
```
### Performer
```
name
url
twitter
instagram
birthdate
death_date
ethnicity
country
hair_color
eye_color
height
weight
measurements
fake_tits
career_length
tattoos
piercings
image (base64 encoding of the image file)
created_at
updated_at
rating (integer)
details
```
### Studio
```
name
url
image (base64 encoding of the image file)
created_at
updated_at
rating (integer)
details
```
### Scene
```
title
studio
url
date
rating (integer)
details
performers (list of strings, performers name)
tags (list of strings)
markers
title
seconds
primary_tag
tags (list of strings)
created_at
updated_at
file (not a list, but a single object)
size (in bytes, no after comma values)
duration (in seconds)
video_codec (example value: h264)
audio_codec (example value: aac)
width (integer, in pixel)
height (integer, in pixel)
framerate
bitrate (integer, in Bit)
created_at
updated_at
```
### Image
```
title
studio
rating (integer)
performers (list of strings, performers name)
tags (list of strings)
files (list of path strings)
galleries
zip_files (list of path strings)
folder_path
title (for user-created gallery)
created_at
updated_at
```
### Gallery
```
title
studio
url
date
rating (integer)
details
performers (list of strings, performers name)
tags (list of strings)
zip_files (list of path strings)
folder_path
created_at
updated_at
```
---
## Files
### Folder
```
zip_file (path to containing zip file)
mod_time
type (= folder)
path
created_at
updated_at
```
### Video file
```
zip_file (path to containing zip file)
mod_time
type (= video)
path
fingerprints
type
fingerprint
size
format
width
height
duration
video_codec
audio_codec
frame
bitrate
interactive (bool)
interactive_speed (integer)
created_at
updated_at
```
### Image file
```
zip_file (path to containing zip file)
mod_time
type (= image)
path
fingerprints
type
fingerprint
size
format
width
height
created_at
updated_at
```
### Other files
```
zip_file (path to containing zip file)
mod_time
type (= file)
path
fingerprints
type
fingerprint
size
created_at
updated_at
```
---
## In JSON format
For those preferring the json-format, defined [here](https://json-schema.org/){:target="_blank"}, the following format may be more interesting:
### performer.json
``` json
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://github.com/stashapp/stash/wiki/JSON-Specification/performer.json",
"title": "performer",
"description": "A json file representing a performer. The file is named by a MD5 Code.",
"type": "object",
"properties": {
"name": {
"description": "Name of the performer",
"type": "string"
},
"url": {
"description": "URL to website of the performer",
"type": "string"
},
"twitter": {
"description": "Twitter name of the performer",
"type": "string"
},
"instagram": {
"description": "Instagram name of the performer",
"type": "string"
},
"birthdate": {
"description": "Birthdate of the performer. Format is YYYY-MM-DD",
"type": "string"
},
"death_date": {
"description": "Death date of the performer. Format is YYYY-MM-DD",
"type": "string"
},
"ethnicity": {
"description": "Ethnicity of the Performer. Possible values are black, white, asian or hispanic",
"type": "string"
},
"country": {
"description": "Country of the performer",
"type": "string"
},
"hair_color": {
"description": "Hair color of the performer",
"type": "string"
},
"eye_color": {
"description": "Eye color of the performer",
"type": "string"
},
"height": {
"description": "Height of the performer in centimeters",
"type": "string"
},
"weight": {
"description": "Weight of the performer in kilograms",
"type": "string"
},
"measurements": {
"description": "Measurements of the performer",
"type": "string"
},
"fake_tits": {
"description": "Whether performer has fake tits. Possible are Yes or No",
"type": "string"
},
"career_length": {
"description": "The time the performer has been in business. In the format YYYY-YYYY",
"type": "string"
},
"tattoos": {
"description": "Giving a description of Tattoos of the performer if any",
"type": "string"
},
"piercings": {
"description": "Giving a description of Piercings of the performer if any",
"type": "string"
},
"image": {
"description": "Image of the performer, parsed into base64",
"type": "string"
},
"created_at": {
"description": "The time this performers data was added to the database. Format is YYYY-MM-DDThh:mm:ssTZD",
"type": "string"
},
"updated_at": {
"description": "The time this performers data was last changed in the database. Format is YYYY-MM-DDThh:mm:ssTZD",
"type": "string"
},
"details": {
"description": "Description of the performer",
"type": "string"
}
},
"required": ["name", "ethnicity", "image", "created_at", "updated_at"]
}
```
### studio.json
``` json
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://github.com/stashapp/stash/wiki/JSON-Specification/studio.json",
"title": "studio",
"description": "A json file representing a studio. The file is named by a MD5 Code.",
"type": "object",
"properties": {
"name": {
"description": "Name of the studio",
"type": "string"
},
"url": {
"description": "URL to the studios websites",
"type": "string"
},
"image": {
"description": "Logo of the studio, parsed into base64",
"type": "string"
},
"created_at": {
"description": "The time this studios data was added to the database. Format is YYYY-MM-DDThh:mm:ssTZD",
"type": "string"
},
"updated_at": {
"description": "The time this studios data was last changed in the database. Format is YYYY-MM-DDThh:mm:ssTZD",
"type": "string"
},
"details": {
"description": "Description of the studio",
"type": "string"
}
},
"required": ["name", "image", "created_at", "updated_at"]
}
```
### scene.json
```json
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://github.com/stashapp/stash/wiki/JSON-Specification/scene.json",
"title": "scene",
"description": "A json file representing a scene. The file is named by the MD5 Code of the file its data is referring to.",
"type": "object",
"properties": {
"title": {
"description": "Title of the scene",
"type": "string"
},
"studio": {
"description": "The name of the studio that produced that scene",
"type": "string"
},
"url": {
"description": "The url to the scenes original source",
"type": "string"
},
"date": {
"description": "The release date of the scene. Its given in the format YYYY-MM-DD",
"type": "string"
},
"rating": {
"description": "The scenes Rating. Its given in stars, from 1 to 5",
"type": "integer"
},
"details": {
"description": "A description of the scene, containing things like the story arc",
"type": "string"
},
"performers": {
"description": "A list of names of the performers in this gallery",
"type": "array",
"items": {
"type": "string"
},
"minItems": 1,
"uniqueItems": true
},
"tags": {
"description": "A list of the tags associated with this scene",
"type": "array",
"items": {
"type": "string"
},
"minItems": 1,
"uniqueItems": true
},
"markers": {
"description": "Markers mark certain events in the scene, most often the change of the position. They are attributed with their own tags.",
"type": "array",
"items": {
"type": "object",
"properties": {
"title": {
"description": "Searchable name of the marker",
"type": "string"
},
"seconds": {
"description": "At what second the marker is set. It is given with after comma values, such as 10.0 or 17.5",
"type": "string"
},
"primary_tag": {
"description": "A tag identifying this marker. Multiple markers from the same scene with the same primary tag are concatenated, showing them as similar in nature",
"type": "string"
},
"tags": {
"description": "A list of the tags associated with this marker",
"type": "array",
"items": {
"type": "string"
},
"minItems": 1,
"uniqueItems": true
},
"created_at": {
"description": "The time this marker was added to the database. Format is YYYY-MM-DDThh:mm:ssTZD",
"type": "string"
},
"updated_at": {
"description": "The time this marker was updated the last time. Format is YYYY-MM-DDThh:mm:ssTZD",
"type": "string"
}
},
"required": ["seconds", "primary_tag", "created_at", "updated_at"]
},
"minItems": 1,
"uniqueItems": true
},
"files": {
"description": "A list of paths of the files for this scene",
"type": "array",
"items": {
"type": "string"
},
"minItems": 1,
"uniqueItems": true
},
"created_at": {
"description": "The time this studios data was added to the database. Format is YYYY-MM-DDThh:mm:ssTZD",
"type": "string"
},
"updated_at": {
"description": "The time this studios data was last changed in the database. Format is YYYY-MM-DDThh:mm:ssTZD",
"type": "string"
}
},
"required": ["files", "created_at", "updated_at"]
}
```

View File

@ -0,0 +1,89 @@
---
layout: default
title: Scene Filename Parser
nav_order: 3
parent: Tasks
grand_parent: In-app Manual
---
# **Scene Filename Parser**
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
[This tool](http://localhost:9999/sceneFilenameParser) parses the scene filenames in your library and allows setting the metadata from those filenames.
---
## Parser Options
To use this tool, a filename pattern must be entered. The pattern accepts the following fields:
| Field | Remark |
|-------|--------|
| `title` | Text captured within is set as the title of the scene. |
|`ext`|Matches the end of the filename. It is not captured. Does not include the last `.` character.|
|`d`|Matches delimiter characters (`-_.`). Not captured.|
|`i`|Matches any ignored word entered in the `Ignored words` field. Ignored words are entered as space-delimited words. Not captured. Use this to match release artifacts like `DVDRip` or release groups.|
|`date`|Matches `yyyy-mm-dd` and sets the date of the scene.|
|`rating`|Matches a single digit and sets the rating of the scene.|
|`performer`| Sets the scene performer, based on the text captured.|
|`tag`| Sets the scene tag, based on the text captured.|
|`studio`| Sets the studio performer, based on the text captured.|
|`{}`|Matches any characters. Not captured.|
> **⚠️ Note:** `performer`, `tag` and `studio` fields will only match against Performers/Tags/Studios that already exist in the system.
The `performer`/`tag`/`studio` fields will remove any delimiter characters (`.-_`) before querying. Name matching is case-insensitive.
The following partial date fields are also supported. The date will only be set on the scene if a date string can be built using the partial date components:
| Field | Remark |
|-------|--------|
|`yyyy`|Four digit year|
|`yy`|Two digit year. Assumes the first two digits are `20`|
|`mm`|Two digit month|
|`mmm`|Three letter month, such as `Jan` (case-insensitive)|
|`dd`|Two digit date|
The following full date fields are supported, using the same partial date rules as above:
* `yyyymmdd`
* `yymmdd`
* `ddmmyyyy`
* `ddmmyy`
* `mmddyyyy`
* `mmddyy`
All of these fields are available from the `Add Field` button.
Title generation also has the following options:
| Option | Remark |
|--------|--------|
|Whitespace characters| These characters are replaced with whitespace (defaults to `._`, to handle filenames like `three.word.title.avi`|
|Capitalize title| capitalises the first letter of each word|
The fields to display can be customised with the `Display Fields` drop-down section. By default, any field with new/different values will be displayed.
---
## Applying the results
Once the options are correct, click on the `Find` button. The system will search for scenes that have filenames that match the given pattern.
The results are presented in a table showing the existing and generated values of the discovered fields, along with a checkbox to determine whether or not the field will be set on each scene. These fields can also be edited manually.
The `Apply` button updates the scenes based on the set fields.
> **⚠️ Note:** results are paged and the `Apply` button only applies to scenes on the current page.

21
docs/Network.md Normal file
View File

@ -0,0 +1,21 @@
---
layout: default
title: Network
nav_order: 6
has_children: true
has_toc: false
---
# Network
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. [API]({{ site.baseurl }}/docs/Network/API/)
2. [Reverse Proxy]({{ site.baseurl }}/docs/Network/Reverse-Proxy/)
3. [Authentication Required When Accessing Stash From the Internet]({{ site.baseurl }}/docs/Network/Authentication-Required-When-Accessing-Stash-From-the-Internet/)
</details>

141
docs/Network/API.md Normal file
View File

@ -0,0 +1,141 @@
---
layout: default
title: API
nav_order: 1
parent: Network
---
# **API**
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
There is a GraphQL API which allows to do things automatically.
All http requests have to go to ``http://IP:PORT/graphql``
Data from the API can be outdated from time to time.
In that case you can either visit our [Discord](https://discord.gg/2TsNFKt){:target="_blank"} for more help or make an issue.
For up to date info you can check stash's playground `http://IP:PORT/playground` ( SCHEMA/DOCS section to the right).
### Scan for new files
Request: `HTTP-POST`
```json
{
"query": "mutation { metadataScan ( input: {} ) }"
}
```
_Example using curl_
`curl -X POST -H "Content-Type: application/json" --data '{ "query": " mutation { metadataScan (input: {} ) }" }' localhost:9998/graphql`
### Authentication
If you have configured a username/password you have to use cookies to authenticate
```
curl --verbose --cookie-jar cookie.txt --data 'username=stash&password=**' localhost:9998/login
curl --cookie cookie.txt -H "Content-Type: application/json" --data '{ "query": "mutation { metadataScan ( input: {} ) } "}' localhost:9998/graphql
```
Latest dev version has support for API Keys.
Using the `API Key` is recommended instead of the above cookie method.
You just need to add the key you generated in stash ( for more info about the API Key check stash's help section ) in a header for every request you make
```
curl -X POST -H "ApiKey: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiJiaWxsIiwiaWF0IjoxNjE3MDkzMDYwLCJzdWIiOiJBUElLZXkifQ.WhUyvmnVeW8wGV5fkVyje3xLfz5A97HFwyZy-4i8Q-I" -H "Content-Type: application/json" --data '{ "query": "mutation { metadataScan (input:{})}" }' localhost:9998/graphql
```
### Generate content
Request: `HTTP-POST`
Payload (set desired content to generate **sprites**,**previews**,**imagePreviews**,**markers**,**transcodes** to ```true``` or ```false```):
```json
{
"query": "mutation { metadataGenerate ( input : { sprites: true previews: false imagePreviews: false markers: false transcodes: false } ) }"
}
```
_Example using curl_
`curl -X POST -H "Content-Type: application/json" --data '{ "query": "mutation { metadataGenerate ( input : { sprites: false previews: true imagePreviews: false markers: false transcodes: false } ) }" }' localhost:9998/graphql`
### Get Studios
Request: `HTTP-POST`
Payload ( must be at least one of ```id``` ```checksum``` ```url``` ```name``` ```image_path``` ```scene_count``` ):
```json
{
"query": "{ allStudios { id checksum url name image_path scene_count } }"
}
```
_Example using curl_
`curl -X POST -H "Content-Type: application/json" --data '{ "query": "{ allStudios { name url scene_count} }" }' localhost:9998/graphql`
### Scrape perfomer attributes from Freeones
Request: `HTTP-POST`
Payload (
* ```$performer name``` is the name of the Performer you are scraping for
* return values must be at least one of ```name``` ```url``` ```twitter``` ```instagram``` ```birthdate``` ```ethnicity``` ```country``` ```eye_color``` ```height``` ```measurements``` ```fake_tits``` ```career_length``` ```tattoos``` ```piercings``` ```aliases```
):
```json
{
"query": "{ scrapeFreeones(performer_name: $performer_name) { name url twitter instagram birthdate ethnicity country eye_color height measurements fake_tits career_length tattoos piercings aliases } }"
}
```
1. Caution is needed when used in a script.Always set a waiting/sleep period between calls to avoid getting blacklisted by Freeones
2. Due to way it's used in Stash and to work reliably it requires the prior use of **scrapeFreeonesPerfomerList** (described beneath) to make sure the name of the performer exists in the list returned and thus in the Freeones db
_Example using curl_
`curl -X POST -H "Content-Type: application/json" --data '{ "query": "{ scrapeFreeones ( performer_name : \"Abella Danger\" ) { name height birthdate} }" }' localhost:9998/graphql`
### Get list of perfomer names that match a name or alias from Freeones
Request: `HTTP-POST`
Payload ( $q is the name or alias (or partial name , alias) of the performer you are looking for
):
```json
{
"query": "{ scrapeFreeonesPerformerList(query: $q) }"
}
```
1. Caution is needed when used in a script.Always set a waiting/sleep period between calls to avoid getting blacklisted by Freeones
_Example using curl_
`curl -X POST -H "Content-Type: application/json" --data '{ "query": "{ scrapeFreeonesPerformerList (query: \"bella\" ) }" }' localhost:9998/graphql`
### Get System Status
Request: `HTTP-POST`
```json
{
"query": "{ systemStatus { databaseSchema databasePath configPath appSchema status } }"
}
```
_Example using curl_
`curl -X POST -H "Content-Type: application/json" --data '{ "query": "{ systemStatus { appSchema status } }" }' localhost:9999/graphql`

View File

@ -0,0 +1,44 @@
---
layout: default
title: Authentication Required When Accessing Stash From the Internet
nav_order: 3
parent: Network
---
# **Authentication Required When Accessing Stash From the Internet**
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
Support is always available on our [Discord](https://discord.gg/2TsNFKt){:target="_blank"}.
---
## Protecting against accidental exposure to the internet
Stash data is considered private, and Stash is not designed to be publicly exposed, except to trusted confidants. Stash has a built-in protection against accidentally exposing itself publicly outside of your network. If Stash receives a request from the public internet, and you do not have a password enabled, Stash will reject the request and stop handling requests to protect your privacy.
This often happens when you use the port-forwarding feature of your router or install Stash on a publicly accessible server, such as a VPS. When you do this, anybody in the world can access your Stash instance, so we enforce a password requirement. If your Stash instance has shutdown due to an insecure configuration, it will not handle requests again until you tell it that you have fixed the problem. After setting up either authentication, firewall, or removing your port forwarding rules, you can edit `.stash/config/config.yml` and remove the key `security_tripwire_accessed_from_public_internet`.
### Alternative and safe methods to access your Stash
You may use several methods to safely access Stash from outside of your home network. In the most basic, you can enable authentication in Stash, and re-enable port forwarding. You can also use a VPN solution that allows you to securely access your home network, such as [Tailscale](https://tailscale.com){:target="_blank"}, [Zerotier](https://zerotier.com){:target="_blank"}, [Wireguard](https://www.digitalocean.com/community/tutorials/how-to-set-up-wireguard-on-ubuntu-20-04){:target="_blank"}, or others.
### Using an external authentication provider
If you are an advanced user, and have secured your Stash instance behind an authwall provided by a reverse proxy or hosting solution, you may continue to use that. You simply have to edit `.stash/config/config.yml` and set `dangerous_allow_public_without_auth` to `true`. If you have already tripped the security feature, you will also have to remove the `security_tripwire_accessed_from_public_internet` key in order to allow Stash to serve requests.
### Using a reverse proxy located outside of your private network
By default, all private IPs are trusted proxies, so you almost certainly do not need to edit your settings.
However, if you are using a reverse proxy outside of your private network (uncommon), it should be added to `trustedProxies` in your Configuration tab to allow it to serve requests.

View File

@ -0,0 +1,208 @@
---
layout: default
title: Reverse Proxy
nav_order: 2
parent: Network
---
# **Reverse proxy**
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
The use of a reverse proxy for Stash is possible.
---
## General
Generally, the following headers will need to be set (check your proxy's documentation for how to configure) .
- Host (http host)
- X-Real-IP
- X-Forwarded-For
- X-Forwarded-Proto
See [issue 134](https://github.com/stashapp/stash/pull/134){:target="_blank"} for more information.
---
## Setting External URL
You can set the base URL that will be served by Stash by adding an `external_host:` setting in your Stash config.yml and assigning it the full publicly accessible url
```
external_host: http://example.domain.com
```
---
## Server Configuration Examples
### NGinx
```bash
location / {
proxy_pass http://127.0.0.1:9999;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
}
```
---
## NGinX + Docker (Linuxserver Letsencrypt)
If you are using the linuxserver letencrypt docker you can use create a `stash.subdomain.conf` file in your `proxy-confs` folder and use this as the config:
```bash
# make sure that your dns has a cname set for stash
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name stash.*;
include /config/nginx/ssl.conf;
client_max_body_size 0;
# enable for ldap auth, fill in ldap details in ldap.conf
#include /config/nginx/ldap.conf;
location / {
# enable the next two lines for http auth
#auth_basic "Restricted";
#auth_basic_user_file /config/nginx/.htpasswd;
# enable the next two lines for ldap auth
#auth_request /auth;
#error_page 401 =200 /login;
include /config/nginx/proxy.conf;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
resolver 127.0.0.11 valid=30s;
set $upstream_app stash;
set $upstream_port 9999;
set $upstream_proto http;
proxy_pass $upstream_proto://$upstream_app:$upstream_port;
proxy_set_header Host $http_host;
}
}
```
### Nginx with external_host
Another example for `nginx`:
In this case we are using `stash.home` as our domain and `192.168.0.1` is stash's ip so edit acccordingly.
The `external_host` configuration option should also be set, in this case `external_host: http://stash.home`. Refer to [external_host](https://github.com/stashapp/stash/pull/369){:target="_blank"} for more details
```bash
server {
listen 80;
listen [::]:80;
server_name stash.home;
client_max_body_size 0;
location / {
proxy_pass http://192.168.0.1:9999/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
```
### Apache
```
ProxyPass "/stash" "http://127.0.0.1:9999"
ProxyPassReverse "/stash" "http://127.0.0.1:9999"
RequestHeader setIfEmpty X-Forwarded-Prefix "/stash"
ProxyPreserveHost on
# for name resolution
ServerAdmin admin@example.com
ServerName example.com
ServerAlias stash.example.com
# to enable websockets
RewriteEngine on
RewriteCond %{HTTP:Upgrade} websocket [NC]
RewriteCond %{HTTP:Connection} upgrade [NC]
RewriteRule ^/?stash/(.*) "ws://127.0.0.1:9999/$1" [P,L]
# to add SSL
SSLEngine on
SSLCertificateFile /path/to/cert.pem
SSLCertificateKeyFile /path/to/cert.key
```
#### Prerequisites
```
sudo a2enmod proxy
sudo a2enmod proxy_http
sudo a2enmod proxy_balancer
sudo a2enmod lbmethod_byrequests
sudo a2enmod rewrite
sudo a2enmod headers
# for SSL
sudo a2enmod ssl
```
### Caddy
```
example.domain.com
reverse_proxy 127.0.0.1:9999 {
header_up X-Forwarded-Host {host}
header_up Host {upstream_hostport}
header_up X-Real-IP {remote_host}
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Port {server_port}
header_up X-Forwarded-Proto {scheme}
}
}
```
---
## Troubleshooting
**504 Errors**
- In some cases with big database files you might encounter `504` errors during stash db migration due to timeout. Adjusting the `proxy_read_timeout` value ( `proxy.conf` file in Letencrypt/Swag docker container)
**422 Errors**
- In order for the websocket to work, you may need to also add these lines to your server block (`proxy.conf` file in the Letencrypt Unraid docker container for instance) as mentioned [here](https://github.com/stashapp/stash/issues/532){:target="_blank"} should fix the issue.
```bash
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
```

26
docs/User-Interface-UI.md Normal file
View File

@ -0,0 +1,26 @@
---
layout: default
title: User Interface (UI)
nav_order: 7
has_children: true
has_toc: false
---
# User Interface (UI)
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. [Themes]({{ site.baseurl }}/docs/User-Interface-UI/Themes/)
- [Theme Pulsar]({{ site.baseurl }}/docs/User-Interface-UI/Themes/Theme-Pulsar/)
- [Theme Light Pulsar]({{ site.baseurl }}/docs/User-Interface-UI/Themes/Theme-Pulsar/)
- [Theme Night]({{ site.baseurl }}/docs/User-Interface-UI/Themes/Theme-Pulsar/)
- [Theme Plex]({{ site.baseurl }}/docs/User-Interface-UI/Themes/Theme-Pulsar/)
- [Theme Black Hole]({{ site.baseurl }}/docs/User-Interface-UI/Themes/Theme-Pulsar/)
- [Modern Dark]({{ site.baseurl }}/docs/User-Interface-UI/Themes/Theme-Pulsar/)
2. [Custom CSS Snippets]({{ site.baseurl }}/docs/User-Interface-UI/Custom-CSS-Snippets/)
</details>

View File

@ -0,0 +1,529 @@
---
layout: default
title: Custom CSS Snippets
nav_order: 2
parent: User Interface (UI)
---
# **Custom CSS Snippets**
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
**Custom CSS** allows you to modify Stash's stock style sheets.
The following is a list of some useful CSS snippets. You may use them by copying-and-pasting them into the Custom CSS editor found in the `Settings` > `Interface Configuration` panel or by navigating to `localhost:9999/settings?tab=interface`
Note: Future releases of Stash may break these CSS tweaks. CSS tweaks may not appear without flushing the Stash browser cache first on Chrome.
---
## Scenes
### Fit more thumbnails on each row
Reduce left and right padding on Scene and Performer grid pages allowing for more thumbnails on each row.
```css
/* [Scenes tab] Fit more thumbnails on each row */
.grid { padding: 0px !important; }
```
### Allow for longer string when displaying "Studio as Text" on scene thumbnails
```css
/* [Scenes tab] Allow for longer string when displaying "Studio as Text" on scene thumbnails */
.scene-studio-overlay {
font-weight: 600 !important;
opacity: 1 !important;
width: 60% !important;
text-overflow: ellipsis !important;
}
```
### Hide scene specs (resolution, duration) from scene card
```css
/* [Scenes tab] Hide scene specs (resolution, duration) from scene card */
.scene-specs-overlay {
display: none;
}
```
### Hide studio logo/text from scene card
```css
/* [Scenes tab] Hide studio logo/text from scene card */
.scene-studio-overlay {
display: none;
}
```
### Make the list of tags take up less width
```css
/* [Scenes tab] Make the list of tags take up less width */
.bs-popover-bottom {
max-width: 500px
}
```
### Swap studio and resolution/duration positions
```css
/* [Scenes tab] Swap studio and resolution/duration positions */
.scene-studio-overlay {
bottom: 1rem;
right: 0.7rem;
height: inherit;
top: inherit;
}
.scene-specs-overlay {
right: 0.7rem;
top: 0.7rem;
bottom: inherit;
}
```
### Adjust the mouse over behaviour in wall mode
```css
/* [Scenes tab] Adjust the mouse over behaviour in wall mode */
@media (min-width: 576px) {
.wall-item:hover::before {
opacity: 0;
}
.wall-item:hover .wall-item-container {
transform: scale(1.5);
}
}
```
### Disable zoom on hover in wall mode
```css
/* [Scenes tab] Disable zoom on hover in wall mode */
.wall-item:hover .wall-item-container {
transform: none;
}
.wall-item:before {
opacity: 0 !important;
}
```
### Hide the scene scrubber
This will hide the large scene scrubber under the video player and max out the player's height.
![Image](Themes/assets/css-scrubber.png)
```css
/* [Scenes tab] Hide the scene scrubber and max out the player's height */
.scrubber-wrapper {
display: none;
}
```
### Hide the truncated text
This will hide the truncated text that appears under the tile and date.
```css
/* [Scenes Tab] - Hide the truncated text on scene card */
.TruncatedText.scene-card__description {
display: none;
}
```
---
## Performers
### Show entire performer image in performer card
```css
/* [Performers tab] Show entire performer image in performer card */
.performer.image {
background-size: contain !important;
}
```
### Show a larger image in performer's page for desktop
```css
.performer-image-container{
flex: 0 0 50%;
max-width: 50%;
}
/* Changing .col-md-8 settings also affects studios and tags display. 50% should be good enough. */
.col-md-8 {
flex: 0 0 50%;
max-width: 50%;
}
```
### Show larger performer images in performers list
```css
/* original value: height: 30rem; min-width:13.25rem; */
.performer-card-image{
height: 45rem;
min-width: 20rem;
}
```
### Move the buttons in the Performer's edit panel to the top instead of bottom
```css
/* [Performers tab] Move the buttons in the Performer's edit panel to the top instead of bottom (in newer version of Stash, the buttons are already positioned both at top and bottom. */
form#performer-edit {
display: flex;
flex-direction: column;
}
#performer-edit > .row {
order: 1;
}
#performer-edit > .row:last-child {
order: 0;
margin-bottom: 1rem;
}
```
### Move the tags row in the Performer's edit panel to the second position (just after name)
```css
/* [Performers tab] Move the tags row in the Performer's edit panel to the second position (just after name). */
form#performer-edit {
display: flex;
flex-direction: column;
}
#performer-edit > .row:nth-child(21) {
order: -1;
}
#performer-edit > .row:first-child {
order: -2;
}
```
---
## Galleries
### Grid view for galleries
```css
/* [Galleries tab] Grid view for galleries */
.col.col-sm-6.mx-auto.table .d-none.d-sm-block {
display: none !important;
}
.col.col-sm-6.mx-auto.table .w-100.w-sm-auto {
width: 175px !important;
background-color: rgba(0, 0, 0, .45);
box-shadow: 0 0 2px rgba(0, 0, 0, .35);
}
.col.col-sm-6.mx-auto.table tr {
display: inline-table;
}
```
---
## Images
### Disable lightbox animation
```css
/* [Images tab] Disable lightbox animation */
.Lightbox-carousel {
transition: none;
}
```
### Don't crop preview thumbnails
```css
/* [Images tab] Don't crop preview thumbnails */
.flexbin > * > img {
object-fit: inherit;
max-width: none;
min-width: initial;
}
```
---
## Movies
### Better Movie layout for desktops
Making the front and back image much bigger. Left panel uses 70% while the right uses 30%.
#### Layout 1, regular size poster.
```css
/* [Movies tab] Better Movie layout for desktops: Regular size poster */
.movie-details.mb-3.col.col-xl-4.col-lg-6 {
flex-basis: 70%
}
.col-xl-8.col-lg-6{
flex-basis: 30%
}
.movie-images{
flex-wrap: wrap
}
.movie-image-container {
flex: 0 0 500px
}
```
#### Layout 2, larger size poster.
```css
/* [Movies tab] Better Movie layout for desktops: Larger size poster */
.movie-details.mb-3.col.col-xl-4.col-lg-6 {
flex-basis: 70%
}
.col-xl-8.col-lg-6{
flex-basis: 30%
}
.movie-images{
flex-direction: column;
flex-wrap: wrap
}
.movie-image-container {
flex: 1 1 700px
}
```
---
## Global
### Change the order of navigation bar buttons
Use `order` values below 0 to move specific buttons to the left of the non-ordered buttons,
and values above 1 to move them to the right of the non-ordered buttons.
**Before:**
![Navigation before](Themes/assets/navigation-before.png)
**After:**
![Navigation after](Themes/assets/navigation-after.png)
```css
/* [Global changes] Change the order of navigation bar buttons */
nav .navbar-nav:first-child {
display: flex;
flex-direction: row;
}
div.nav-link[data-rb-event-key="/tags"] {
order: -2;
}
div.nav-link[data-rb-event-key="/movies"] {
order: -1;
}
div.nav-link[data-rb-event-key="/scenes"] {
order: 1;
}
```
### Hide the Donate button
```css
/* [Global changes] Hide the Donate button */
.btn-primary.btn.donate.minimal {
display: none;
}
```
### Blur NSFW images
Use for when working on stash but don't want to expose NSFW images and text. May not be exhaustive:
```css
/* [Global changes] Blur NSFW images */
.scene-card-preview-video,
.scene-card-preview-image,
.image-card-preview-image,
.image-thumbnail,
.gallery-card-image,
.performer-card-image,
img.performer,
.movie-card-image,
.gallery .flexbin img,
.wall-item-media,
.scene-studio-overlay .image-thumbnail,
.image-card-preview-image,
#scene-details-container .text-input,
#scene-details-container .scene-header,
#scene-details-container .react-select__single-value,
.scene-details .pre,
#scene-tabs-tabpane-scene-file-info-panel span.col-8.text-truncate > a,
.gallery .flexbin img,
.movie-details .logo {
filter: blur(8px);
}
.scene-card-video {
filter: blur(13px);
}
.jw-video,
.jw-preview,
.jw-flag-floating,
.image-container,
.studio-logo,
.scene-cover {
filter: blur(20px);
}
.movie-card .text-truncate,
.scene-card .card-section {
filter: blur(4px);
}
```
### Blur NSFW images and unblur on mouse over
```css
/* [Global changes] Blur NSFW images and unblur on mouse over */
/* === MORE BLUR === */
/* scene */
.scene-card-preview,
.vjs-poster,
video,
.scene-cover,
.scrubber-item,
/* image */
.image-card-preview,
.image-image,
.gallery-image,
/* movie */
.movie-card-image,
.movie-images,
/* gallery */
.gallery-card-image,
table > tbody > tr > td > a > img.w-100,
/* performer */
.performer-card-image,
img.performer,
/* studio */
.studio-card-image,
/* tag */
.tag-card-image
{
filter: blur(30px);
}
/* === LESS BLUR === */
/* common */
.card-section-title,
/* scene */
.scene-studio-overlay,
.scene-header > h3,
h3.scene-header,
.studio-logo,
.image-thumbnail,
/* image */
h3.image-header,
/* movie */
.movie-details > div > h2,
/* gallery */
h3.gallery-header,
/* studio */
.studio-details .logo,
.studio-details > div > h2,
/* tag */
.logo-container > .logo,
.logo-container > h2
{
filter: blur(2px);
}
/* === UNBLUR ON HOVER === */
/* common */
.thumbnail-section:hover *,
.card:hover .card-section-title,
/* scene */
.card:hover .scene-studio-overlay,
.video-js:hover .vjs-poster,
video:hover,
.scene-header:hover > h3,
div:hover > .scene-header,
.studio-logo:hover,
.scene-cover:hover,
.image-thumbnail:hover,
.scene-card-preview:hover,
.scrubber-item:hover,
/* image */
.image-image:hover,
div:hover > .image-header,
.gallery-image:hover,
/* movie */
.movie-images:hover,
.movie-details > div > h2:hover,
/* gallery */
div:hover > .gallery-header,
table > tbody > tr > td:hover > a > img.w-100,
/* performer */
img.performer:hover,
/* studio */
.studio-details .logo:hover,
.studio-details:hover > div > h2,
/* tag */
.logo-container > .logo:hover,
.logo-container:hover > h2
{
filter: blur(0px);
}
```

View File

@ -0,0 +1,50 @@
---
layout: default
title: Themes
nav_order: 1
has_children: true
has_toc: false
parent: User Interface (UI)
---
# **Themes**
{: .no_toc }
Stash supports Custom CSS themes to adjust the look-and-feel of the interface. There are several that have been created by the maintainers and users.
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
## Installing a theme
1. Select a theme from the directory below, and copy the CSS code to your clipboard. You may also need to download image files, as per the instructions.
2. In Stash, go to the `Settings` section, then select `Interface`.
3. Make sure `Custom CSS` is checked, then paste the CSS code into the text box.
4. You will need to force-reload (Shift+F5) in order to see the theme.
---
## Creating Themes
If you would like to develop your own theme, we have a [reference guide of CSS snippets that may be useful]({{ site.baseurl }}/docs/User-Interface-UI/Custom-CSS-snippets).
---
## Theme Directory
| Name | Preview |
| ---------------------------------------------------------------- | ------------------------------------------------------------------------- |
| [Pulsar]({{ site.baseurl }}/docs/User-Interface-UI/Themes/Theme-Pulsar) | ![Screenshot of Pulsar Theme]({{ site.baseurl }}/docs/User-Interface-UI/Themes/assets/Pulsar-preview.jpg) |
| [Light Pulsar]({{ site.baseurl }}/docs/User-Interface-UI/Themes/Theme-Light-Pulsar) | ![Screenshot of Light Pulsar Theme]({{ site.baseurl }}/docs/User-Interface-UI/Themes/assets/Light-Pulsar-preview.jpg) |
| [Night]({{ site.baseurl }}/docs/User-Interface-UI/Themes/Theme-Night) | ![Screenshot of Night Theme]({{ site.baseurl }}/docs/User-Interface-UI/Themes/assets/Night-preview.png) |
| [Plex]({{ site.baseurl }}/docs/User-Interface-UI/Themes/Theme-Plex) | ![Screenshot of Plex Theme]({{ site.baseurl }}/docs/User-Interface-UI/Themes/assets/Plex-preview.png) |
| [Black Hole]({{ site.baseurl }}/docs/User-Interface-UI/Themes/Theme-Black-Hole) | ![Screenshot of Black Hole Theme]({{ site.baseurl }}/docs/User-Interface-UI/Themes/assets/Black-Hole-preview.png) |
| [Modern Dark]({{ site.baseurl }}/docs/User-Interface-UI/Themes/Theme-Modern-Dark) | ![Screenshot of Modern Dark]({{ site.baseurl }}/docs/User-Interface-UI/Themes/assets/Modern-Dark-preview.jpg) |

View File

@ -0,0 +1,540 @@
---
layout: default
title: Modern Dark
nav_order: 6
parent: Themes
grand_parent: User Interface (UI)
---
# **Modern Dark**
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
This theme was inspired by Plex Interface. Installation is quick and easy, so you should be ready to install it in just a few simple steps.
Feel free to experiment with CSS and modify it to fit your needs. In case you have any issues or improvements, DM me on Discord!
{: .note }
The Images and Markers pages are hidden on mobile in this theme to keep a perfect 2x3 nav menu. Those pages can be restored by commenting out or removing the following section:
```css
div[data-rb-event-key="/images"] {
display: none;
}
div[data-rb-event-key="/scenes/markers"] {
display: none;
}
```
### Screenshots
![Modern Dark preview #1](assets/modern-dark-1.jpg)
## Install
1. Open User **Interface** Configuration panel in **settings**. (http://localhost:9999/settings?tab=interface)
2. Tick/Enable Custom CSS ✅
3. Copy&Paste [CSS Code](#css-code) to the Custom CSS text area.
4. In your config.yml file, update the value of the `theme_color` property to `#191919`.
Enjoy!
## CSS Code
```css
/* Modern Dark Theme by cj13 v1.2 */
:root {
--nav-color: #212121;
--body-color: #191919;
--card-color: #2a2a2a;
--plex-yelow: #e5a00d;
--tag-color: #555555;
}
#scrubber-position-indicator {
background-color: #e5a00d;
}
.scrubber-tags-background {
background-color: #e5a00d;
opacity: 30%;
}
body {
width: 100%;
height: 100%;
background: var(--body-color);
}
.text-white {
color: #cbced2 !important;
}
#root {
position: absolute;
width: 100%;
height: 100%;
z-index: 2;
}
div.react-select__menu, div.dropdown-menu {
background-color: var(--card-color);
}
* {
scrollbar-color: hsla(0, 0%, 100%, .2) transparent;
}
.bg-dark {
background-color: var(--nav-color)!important;
}
.card {
background-color: #30404d;
border-radius: 3px;
box-shadow: 0 0 0 1px rgba(16, 22, 26, .4), 0 0 0 rgba(16, 22, 26, 0), 0 0 0 rgba(16, 22, 26, 0);
padding: 20px;
background-color: var(--card-color);
}
.text-input, .text-input:focus, .text-input[readonly], .text-input:disabled {
background-color: var(--card-color);
}
#scrubber-forward {
background-color: transparent;
border: 1px solid #555;
}
.scrubber-button {
background-color: transparent;
border: 1px solid #555;
}
.bg-secondary {
background-color: var(--card-color) !important;
}
.text-white {
color: #eee !important;
}
.border-secondary {
border-color: #2f3335 !important;
}
.btn-secondary.filter-item.col-1.d-none.d-sm-inline.form-control {
background-color: rgba(0, 0, 0, .15);
}
.btn-secondary {
color: #eee;
background-color: rgba(0, 0, 0, .15);
border-color: var(--tag-color);
}
.pagination .btn:last-child {
border-right: 1px solid var(--tag-color);
}
.pagination .btn:first-child {
border-left: 1px solid var(--tag-color);
}
a {
color: hsla(0, 0%, 100%, .45);
}
.btn.active {
background-color: #2f3335;
color: #f5f8fa;
}
minimal.w-100.active.btn.btn-primary {
background-color: #2f3335;
color: #f5f8fa;
}
.btn-primary {
color: #fff;
background-color: #cc7b19;
border-color: #cc7b19;
font-weight: bold;
}
.btn-primary:hover {
color: #fff;
background-color: #e59029;
border-color: #e59029 font-weight: bold;
}
.nav-tabs .nav-link.active {
color: #eee;
}
.nav-tabs .nav-link.active:hover {
border-bottom-color: #eee;
outline: 0;
}
.nav-tabs .nav-link {
color: hsla(0,0%,100%,.65);
}
.tag-item {
background-color: var(--tag-color);
color: #fff;
}
.input-control, .input-control:focus {
background-color: rgba(16, 22, 26, .3);
}
#performer-page .image-container .performer {
background-color: rgba(0, 0, 0, .45);
box-shadow: 0 0 2px rgba(0, 0, 0, .35);
}
.btn-primary:not(:disabled):not(.disabled).active, .btn-primary:not(:disabled):not(.disabled):active, .show>.btn-primary.dropdown-toggle {
color: #fff;
border-color: #eee;
}
.nav-pills .nav-link.active, .nav-pills .show>.nav-link {
background-color: var(--nav-color);
}
.btn-primary.focus, .btn-primary:focus, .btn-primary:not(:disabled):not(.disabled).active:focus, .btn-primary:not(:disabled):not(.disabled):active:focus, .show>.btn-primary.dropdown-toggle:focus {
box-shadow: none;
}
.btn-primary:not(:disabled):not(.disabled).active, .btn-primary:not(:disabled):not(.disabled):active, .show>.btn-primary.dropdown-toggle {
color: #fff;
background-color: #2f3335;
border-color: #eee;
}
input[type="range"]::-moz-range-track {
background: hsla(0, 0%, 100%, .25);
}
input[type="range"]::-moz-range-thumb {
background: #bcbcbc;
}
div.react-select__control {
background-color: hsla(0, 0%, 39.2%, .4);
color: #182026;
border-color: var(--card-color);
cursor: pointer;
}
.scene-wall-item-text-container {
background: radial-gradient(farthest-corner at 50% 50%, rgba(50, 50, 50, .5) 50%, #323232 100%);
color: #eee;
}
.filter-container, .operation-container {
background-color: rgba(0, 0, 0, .15);
box-shadow: none;
margin-top: -10px;
padding: 10px;
}
.container-fluid, .container-lg, .container-md, .container-sm, .container-xl {
width: 100%;
margin-right: 0px;
margin-left: 0px;
}
.btn-link {
font-weight: 500;
color: #eee;
text-decoration: none;
}
button.minimal.brand-link.d-none.d-md-inline-block.btn.btn-primary {
text-transform: uppercase;
font-weight: bold;
}
a:hover {
color: hsla(0, 0%, 100%, .7);
}
option {
background-color: var(--nav-color);
}
.folder-list .btn-link {
color: #2c2e30;
}
#performer-scraper-popover {
z-index: 10;
}
.filter-container, .operation-container {
background-color: transparent;
}
.search-item {
background-color: var(--card-color);
}
.selected-result {
background-color: var(--card-color);
}
.selected-result:hover {
background-color: var(--card-color);
}
.performer-select-active .react-select__control, .studio-select-active .react-select__control {
background-color: hsla(0, 0%, 39.2%, .4);
}
#scene-edit-details .edit-buttons-container {
margin: 0px;
background: var(--body-color);
}
#tasks-panel .tasks-panel-queue {
background: var(--body-color);
}
.job-table.card {
background-color: var(--card-color);
}
.modal-header, .modal-body, .modal-footer {
background-color: #2d3744;
background-repeat: no-repeat;
background-size: cover;
background-attachment: fixed;
/* background-position: center;
*/
}
.folder-list .btn-link {
color: #fff;
}
.modal-header, .modal-body, .modal-footer {
background-color: #30404d;
color: #f5f8fa;
background-repeat: no-repeat;
background-size: cover;
background-attachment: fixed;
background-position: center;
}
@media (max-width: 575.98px) and (orientation: portrait) {
.scene-card-preview-image {
height: calc(100vw * (9 / 16));
}
.gallery-tabs .mr-auto.nav.nav-tabs {
display: grid;
grid-auto-flow: column;
text-align: center;
left: 0;
right: 0;
position: fixed;
}
.VideoPlayer.portrait .video-js {
height: 56.25vw;
}
.gallery-container .tab-content {
top: 3rem;
position: fixed;
height: calc(100vh - 6.5rem);
}
.gallery-tabs {
display: none;
}
.btn-toolbar {
padding-top: 1rem;
}
body {
padding: 0rem 0 5rem;
}
.scene-tabs .mr-auto.nav.nav-tabs {
background-color: #121212;
display: grid;
grid-auto-flow: column;
height: 3rem;
left: 0;
margin: 0;
margin-bottom: 0;
max-height: 3rem;
padding-bottom: 2.2rem;
padding-top: 0.1rem;
position: fixed;
right: 0;
text-align: center;
top: calc(100vw * (9 / 16));
white-space: nowrap;
z-index: 20;
}
.scene-tabs.order-xl-first.order-last {
height: calc(100vh - (100vw * (9 / 16) + 8.5rem));
top: calc((100vw * (9 / 16)) + 5rem);
position: fixed;
}
.tab-content {
overflow-y: auto;
overflow-x: hidden;
}
.studio-card {
width: 100%;
height: 294px;
}
.movie-card {
width: 45%;
}
.performer-card-image {
height: 19rem;
}
.performer-card {
width: 14rem;
height: 27.5rem;
}
.scene-performers .performer-card-image {
height: 19rem;
}
.scene-performers .performer-card {
width: 14rem;
height: 27.5rem;
}
.movie-card .TruncatedText {
display: none;
}
.nav-tabs .nav-link.active:hover {
outline: 0;
border-bottom: 2px solid
}
#performer-details-tab-edit{
display: none;
}
#performer-details-tab-operations{
display: none;
}
.scene-tabs .ml-auto.btn-group {
position: fixed;
right: 1rem;
top: calc((100vw * (9 / 16)) + 2.7rem);
}
.stats-element {
flex-grow: 1;
margin: auto 0rem;
}
.stats {
margin-left: 0px;
}
.top-nav {
bottom: 0;
top: auto;
}
div[data-rb-event-key="/images"] {
display: none;
}
div[data-rb-event-key="/scenes/markers"] {
display: none;
}
.row.justify-content-center.scene-performers {
max-height: 450px;
display: flex;
flex-direction: column;
overflow: auto;
border-top: solid 2px #2d3035;
border-bottom: solid 2px #2d3035;
padding-top: .5rem;
padding-bottom: .5rem;
}
.scene-tabs {
max-height: 100%;
}
}
dd {
word-break: break-word;
}
.btn-secondary {
color: hsla(0,0%,100%,.65);
}
.btn-secondary:hover {
color: white;
z-index: 0;
border-color: var(--nav-color);
background-color: rgba(0, 0, 0, .15);
}
.btn-secondary:not(:disabled):not(.disabled):active, .btn-secondary:not(:disabled):not(.disabled).active, .show>.btn-secondary.dropdown-toggle {
background-color: rgba(0, 0, 0, .35);
}
.btn-secondary:focus, .btn-secondary.focus {
background-color: rgba(0, 0, 0, .35);
}
.scrubber-wrapper {
background-color: rgba(0, 0, 0, .3);
border-style: ridge;
border-color: #555555;
}
a.minimal:hover:not(:disabled), button.minimal:hover:not(:disabled) {
background: none;
color: white;
}
.btn-primary:not(:disabled):not(.disabled).active, .btn-primary:not(:disabled):not(.disabled):active, .show>.btn-primary.dropdown-toggle {
color: var(--plex-yelow);
border-bottom: solid;
background: none;
}
a.minimal, button.minimal {
color: hsla(0,0%,100%,.65);
}
.nav-pills .nav-link.active, .nav-pills .show>.nav-link {
background: none;
border-left: solid;
color: var(--plex-yelow);
}
.nav-link {
color: hsla(0,0%,100%,.65);
}
.nav-link:hover, .nav-link:focus {
color: white;
background-color: hsla(0,0%,100%,.08);
}
.navbar-dark .navbar-nav .nav-link:hover, .navbar-dark .navbar-nav .nav-link:focus {
background-color: transparent;
}
.btn-secondary:not(:disabled):not(.disabled):active, .btn-secondary:not(:disabled):not(.disabled).active, .show>.btn-secondary.dropdown-toggle {
background: none;
color: var(--plex-yelow);
border-bottom: solid;
}
.nav-tabs .nav-link.active {
color: var(--plex-yelow);
background: none;
}
.nav-tabs .nav-link:hover {
border-bottom: none;
}
.custom-control-input:checked~.custom-control-label:before {
color: #fff;
border-color: var(--plex-yelow);
background-color: var(--plex-yelow);
}
.custom-switch .custom-control-input:disabled:checked~.custom-control-label:before {
background-color: var(--plex-yelow);
}
.btn-primary.disabled, .btn-primary:disabled {
color: #fff;
background-color: #e59029;
border-color: #e59029;
font-weight: bold;
}
.btn-primary:focus, .btn-primary.focus {
background-color: #cc7b19;
border-color: #cc7b19;
font-weight: bold;
}
.btn-danger {
color: hsla(0,0%,100%,.75);
background-color: #b32;
border-color: #b32;
font-weight: bold;
}
.btn-danger:hover {
color: white;
background-color: #b32;
border-color: #b32;
}
.brand-link {
background-color: var(--nav-color)!important;
}
.hover-popover-content {
max-width: 32rem;
text-align: center;
background: var(--nav-color);
}
.progress-bar {
background-color: var(--plex-yelow);
}
.modal-header, .modal-body, .modal-footer {
background-color: var(--body-color);
}
.btn-secondary.disabled, .btn-secondary:disabled {
background-color: var(--card-color);
border-color: var(--card-color);
border-left: none!important;
border-right: none!important;
}
#queue-viewer .current {
background-color: var(--card-color);
}
.tab-content .card-popovers .btn {
padding-left: .4rem;
padding-right: .4rem;
}
/* .gallery-tabs, .scene-tabs, .scene-player-container {
background-color: var(--nav-color);
}
*/
.react-select__menu-portal {
z-index: 2;
}
.video-js .vjs-play-progress {
background-color: #e5a00d;
}
```

View File

@ -0,0 +1,159 @@
---
layout: default
title: Theme Black Hole
nav_order: 5
parent: Themes
grand_parent: User Interface (UI)
---
# **Theme Black Hole**
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
## Install
1. Open User **Interface** Configuration panel in **settings**. (http://localhost:port/settings?tab=interface)
2. Tick/Enable Custom CSS ✅
3. Copy&Paste [CSS Code](#css-code) to the Custom CSS text area.
---
## Notes
Just a simple all black theme.
Everything in this css is commented, so if you don't like something just remove it!
Don't like the color of the border, or the border at all, change it or remove it!
If you're not sure how DM me on Discord.
---
## Important changes
1. By default scrubber is hidden.
2. By default hovering on performers images when checking them below a scene/gallery makes them bigger, useful if you're not really sure who the hell is that person!!! This is a little tricky when you have a lot of performers on an item but I manage to check them all. To achieve this I had to "break" tags, now they never to go a new line.
3. Made checkboxes, on scenes/galleries/etc., a little bigger so it's easier to click on them and not on the item itself.
4. Removed transparency from logos when on scenes/etc., it's easier to see them and it's not a big deal since they disappear when you hover on an item to check it.
5. You can set a custom background from an online image and yes you can use gifs but mind the size :)
By default it's set to "contain" so if the image is extremely small, well you get the catch... shouldn't be a problem with bigger images.
Have fun and if in doubt you should find me on Discord!
---
## Changelog
V2.0
Code simplified<br>
Changed borders color from red to "Stash grey"<br>
Edits to paint it all black again!
## CSS Code
```css
/* Black Hole Theme by BViking78 v2.0 */
/* STASH GENERAL */
/* Set Background to Black & Optional Custom Image */
body {
background: black url("") no-repeat fixed center;
background-size: contain;
}
/* Change Top Nav Bar Colors */
.bg-dark {
background-color: #000000!important;
}
/* Set Red Border on Button on Hover */
.btn-primary.btn:hover {
border: 1px solid red;
}
/* Set Background to Transparent for Tags/Performers Popups*/
.fade.hover-popover-content {
background: transparent;
}
/* Zoom Performers image when Hover*/
.hover-popover-content {
max-width: initial;
}
.image-thumbnail:hover {
height: 100%;
}
/* Set Opacity Studio Logo to 100% */
.scene-studio-overlay {
opacity: 100%;
}
/* Making Checkbox Slightly Bigger */
.grid-card .card-check {
top: 0.9rem;
width: 1.75rem;
}
/* Center Titles on Cards */
.grid-card a .card-section-title {
text-align: center;
}
/* Setting Background on Cards to Black and Borders to "Stash Grey" */
.card {
background-color: black;
border: 1px solid #30404d;
}
/* STASH MAIN PAGE*/
/* Change Card Header Color */
.card-header {
background: black;
border: 1px solid white;
}
/* Change Markdown Color */
.card-body {
background: black;
border: 1px solid white;
}
/* SCENE PAGE */
/* Hide the scene scrubber */
.scrubber-wrapper {
display: none;
}
/* Setting Row "Scrape With" Background to Black */
#scene-edit-details .edit-buttons-container {
background-color: black;
}
/* Setting Other Rows Background to Black */
div.react-select__control {
background-color: black;
}
/* SETTING */
/* Setting Text Input Border to White */
.input-control, .text-input {
border: 1px solid white;
}
/* Setting Background on Task Queue to Black */
#tasks-panel .tasks-panel-queue {
background-color: black;
}
```

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,220 @@
---
layout: default
title: Theme Night
nav_order: 3
parent: Themes
grand_parent: User Interface (UI)
---
# **Theme Night**
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
---
### Screenshots
![Stash Night Theme](assets/night-1.png)
## CSS Code
```css
/*
Night theme Version 0.1.
*/
body {
color: #bb0009;
background-color: #000000;
}
.bg-dark {
background-color: #1a1a1b!important;
}
.card {
background-color: #30404d;
border-radius: 3px;
box-shadow: 0 0 0 1px rgba(16, 22, 26, .4), 0 0 0 rgba(16, 22, 26, 0), 0 0 0 rgba(16, 22, 26, 0);
padding: 20px;
background-color: rgba(0, 0, 0, .3);
}
.bg-secondary {
background-color: #313437 !important;
}
.text-white {
color: #bb0009 !important;
}
.border-secondary {
border-color: #2f3335 !important;
}
.btn-secondary.filter-item.col-1.d-none.d-sm-inline.form-control {
background-color: rgba(0, 0, 0, .15);
}
.btn-secondary {
color: #bb0009;
background-color: rgba(0, 0, 0, .15);
}
.btn-primary {
color: #bb0009;
background-color: #bb0009;
}
a {
color: hsla(0, 0%, 100%, .45);
}
.btn.active {
background-color: #2f3335;
color: #bb0009;
}
minimal.w-100.active.btn.btn-primary {
background-color: #bb0009;
color: #bb0009;
}
.btn-primary {
color: #fff;
background-color: #1a1a1b;
border-color: #374242;
}
.nav-tabs .nav-link.active {
color: #bb0009;
}
.nav-tabs .nav-link.active:hover {
border-bottom-color: #bb0009;
outline: 0;
}
.btn-primary.btn.donate.minimal {
display: none;
}
.btn-primary.btn.help-button.minimal {
display: none;
}
.changelog {
display: none;
}
.nav-tabs .nav-link {
outline: 0;
color #bb0009;
}
.input-control,
.input-control:focus {
background-color: rgba(16, 22, 26, .3);
}
#performer-page .image-container .performer {
background-color: rgba(0, 0, 0, .45);
box-shadow: 0 0 2px rgba(0, 0, 0, .35);
}
.btn-primary:not(:disabled):not(.disabled).active,
.btn-primary:not(:disabled):not(.disabled):active,
.show>.btn-primary.dropdown-toggle {
color: #fff;
border-color: #bb0009;
}
.nav-pills .nav-link.active,
.nav-pills .show>.nav-link {
background-color: #1a1a1b;
}
.btn-primary:not(:disabled):not(.disabled).active,
.btn-primary:not(:disabled):not(.disabled):active,
.show>.btn-primary.dropdown-toggle {
color: #fff;
background-color: #2f3335;
border-color: #bb0009;
}
input[type="range"]::-moz-range-track {
background: hsla(0, 0%, 100%, .25);
}
input[type="range"]::-moz-range-thumb {
background: #bcbcbc;
}
div.react-select__control {
background-color: hsla(0, 0%, 39.2%, .4);
color: #182026;
border-color: #394b59;
cursor: pointer;
}
.scene-wall-item-text-container {
background: radial-gradient(farthest-corner at 50% 50%, rgba(50, 50, 50, .5) 50%, #323232 100%);
color: #bb0009;
}
.btn-link {
font-weight: 500;
color: #bb0009;
text-decoration: none;
}
button.minimal.brand-link.d-none.d-md-inline-block.btn.btn-primary {
text-transform: uppercase;
font-weight: bold;
color: #bb0009
}
a.minimal {
text-transform: uppercase;
font-weight: bold;
color: #bb0009
}
a:hover {
color: hsla(0, 0%, 100%, .7);
}
option {
background-color: #1a1a1b;
}
.scrubber-tags-background {
background-color: #202351;
}
.btn-primary.btn.settings-button.minimal {
color: #007dd0;
}
button.minimal.btn.btn-primary {
color: #007dd0;
}
.btn-primary.btn.logout-button.minimal {
color: #00bb2f;
}
.scrubber-viewport {
background-color: #1f062d;
}
```

View File

@ -0,0 +1,265 @@
---
layout: default
title: Theme Plex
nav_order: 4
parent: Themes
grand_parent: User Interface (UI)
---
# **Theme Plex**
{: .no_toc }
---
<details open markdown="block">
<summary>
Table of Contents
</summary>
{: .text-delta }
1. TOC
{:toc}
</details>
<style>
.no-border > img {
border: none;
}
</style>
---
<span class="no-border">![plex theme logo](assets/plex-logo.png)</span>
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.
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!
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:
### Screenshots
![plex theme preview](assets/plex-1.png)
## Install
1. Open User Interface Configuration panel in settings. (http://localhost:9999/settings?tab=interface)
2. Tick/Enable Custom CSS ✅
3. Copy & Paste [CSS Code](#css-code) to the Custom CSS text area.
### Optional - Host Backgrounds Locally
_These steps are optional, by default this theme uses the Github hosted image links._
1. Download [plex-background.png](assets/plex-background.png) and [plex-noise.png](assets/plex-noise.png)
2. Place `plex-background.png` and `plex-noise.png` in `~/.stash` on macOS / Linux or `C:\Users\YourUsername\.stash` on Windows. Then edit the `background-image: url("")` attributes like below:
- Replace line `background-image: url("https://user-images.githubusercontent.com/63812189/79506691-4af78900-7feb-11ea-883e-87b8e05ceb1c.png");` with `background-image: url("./plex-background.png");`
- Replace line `background: rgba(0, 0, 0, 0) url("https://user-images.githubusercontent.com/63812189/79506696-4c28b600-7feb-11ea-8176-12a46454d87a.png") repeat scroll 0% 0%;` with `background: rgba(0, 0, 0, 0) url("./plex-noise.png") repeat scroll 0% 0%;`
## CSS Code
```css
/*
Originally created by Fidelio 2020
StashApp Plex Theme - v1.0.5
*/
body {
background-image: url("https://user-images.githubusercontent.com/63812189/79506691-4af78900-7feb-11ea-883e-87b8e05ceb1c.png");
width: 100%;
height: 100%;
background-size: cover;
background-repeat: no-repeat;
background-color: #3f4245;
background-attachment: fixed;
background-position: center;
}
#root {
background: rgba(0, 0, 0, 0) url("https://user-images.githubusercontent.com/63812189/79506696-4c28b600-7feb-11ea-8176-12a46454d87a.png") repeat scroll 0% 0%;
position: absolute;
width: 100%;
height: 100%;
z-index: 2;
}
* {
scrollbar-color: hsla(0, 0%, 100%, .2) transparent;
}
.bg-dark {
background-color: #1f2326!important;
}
.job-table.card,
.card {
background-color: #30404d;
border-radius: 3px;
box-shadow: 0 0 0 1px rgba(16, 22, 26, .4), 0 0 0 rgba(16, 22, 26, 0), 0 0 0 rgba(16, 22, 26, 0);
padding: 20px;
background-color: rgba(0, 0, 0, .3);
}
.bg-secondary {
background-color: #313437 !important;
}
.text-white {
color: #eee !important;
}
.border-secondary {
border-color: #2f3335 !important;
}
.btn-secondary.filter-item.col-1.d-none.d-sm-inline.form-control {
background-color: rgba(0, 0, 0, .15);
}
.btn-secondary {
color: #eee;
background-color: rgba(0, 0, 0, .15);
}
a {
color: hsla(0, 0%, 100%, .45);
}
.btn.active {
background-color: #2f3335;
color: #f5f8fa;
}
minimal.w-100.active.btn.btn-primary {
background-color: #2f3335;
color: #f5f8fa;
}
.btn-primary {
color: #fff;
background-color: #1f2326;
border-color: #374242;
}
.nav-tabs .nav-link.active {
color: #eee;
}
.nav-tabs .nav-link.active:hover {
border-bottom-color: #eee;
outline: 0;
}
.nav-tabs .nav-link {
outline: 0;
}
.input-control,
.input-control:focus {
background-color: rgba(16, 22, 26, .3);
}
#performer-page .image-container .performer {
background-color: rgba(0, 0, 0, .45);
box-shadow: 0 0 2px rgba(0, 0, 0, .35);
}
.btn-primary:not(:disabled):not(.disabled).active,
.btn-primary:not(:disabled):not(.disabled):active,
.show>.btn-primary.dropdown-toggle {
color: #fff;
border-color: #eee;
}
.nav-pills .nav-link.active,
.nav-pills .show>.nav-link {
background-color: #1f2326;
}
.btn-primary.focus,
.btn-primary:focus,
.btn-primary:not(:disabled):not(.disabled).active:focus,
.btn-primary:not(:disabled):not(.disabled):active:focus,
.show>.btn-primary.dropdown-toggle:focus {
box-shadow: none;
}
.btn-primary:not(:disabled):not(.disabled).active,
.btn-primary:not(:disabled):not(.disabled):active,
.show>.btn-primary.dropdown-toggle {
color: #fff;
background-color: #2f3335;
border-color: #eee;
}
input[type="range"]::-moz-range-track {
background: hsla(0, 0%, 100%, .25);
}
input[type="range"]::-moz-range-thumb {
background: #bcbcbc;
}
div.react-select__control {
background-color: hsla(0, 0%, 39.2%, .4);
color: #182026;
border-color: #394b59;
cursor: pointer;
}
.scene-wall-item-text-container {
background: radial-gradient(farthest-corner at 50% 50%, rgba(50, 50, 50, .5) 50%, #323232 100%);
color: #eee;
}
.filter-container,
.operation-container {
background-color: rgba(0, 0, 0, .15);
box-shadow: none;
margin-top: -10px;
padding: 10px;
}
.container-fluid,
.container-lg,
.container-md,
.container-sm,
.container-xl {
width: 100%;
margin-right: 0px;
margin-left: 0px;
}
.btn-link {
font-weight: 500;
color: #eee;
text-decoration: none;
}
button.minimal.brand-link.d-none.d-md-inline-block.btn.btn-primary {
text-transform: uppercase;
font-weight: bold;
}
a:hover {
color: hsla(0, 0%, 100%, .7);
}
option {
background-color: #1f2326;
}
.folder-list .btn-link {
color: #2c2e30;
}
#performer-scraper-popover {
z-index: 10;
}
#tasks-panel .tasks-panel-queue {
background: rgba(0, 0, 0, 0);
}
div.react-select__menu-portal {
z-index: 2;
}
```

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

Some files were not shown because too many files have changed in this diff Show More