mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 12:24:38 +03:00
In-app help manual (#628)
This commit is contained in:
@@ -49,7 +49,7 @@ const Version: React.FC<IVersionProps> = ({
|
|||||||
</Card.Header>
|
</Card.Header>
|
||||||
<Card.Body>
|
<Card.Body>
|
||||||
<Collapse in={open}>
|
<Collapse in={open}>
|
||||||
<div className="changelog-version-body">{children}</div>
|
<div className="changelog-version-body markdown">{children}</div>
|
||||||
</Collapse>
|
</Collapse>
|
||||||
</Card.Body>
|
</Card.Body>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import ReactMarkdown from "react-markdown";
|
|||||||
|
|
||||||
const markup = `
|
const markup = `
|
||||||
### ✨ New Features
|
### ✨ New Features
|
||||||
|
* Add in-app help manual.
|
||||||
* Add support for custom served folders.
|
* Add support for custom served folders.
|
||||||
* Add support for parent/child studios.
|
* Add support for parent/child studios.
|
||||||
|
|
||||||
|
|||||||
150
ui/v2.5/src/components/Help/Manual.tsx
Normal file
150
ui/v2.5/src/components/Help/Manual.tsx
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
|
import { Modal, Container, Row, Col, Nav, Tab } from "react-bootstrap";
|
||||||
|
import Introduction from "src/docs/en/Introduction.md";
|
||||||
|
import Tasks from "src/docs/en/Tasks.md";
|
||||||
|
import AutoTagging from "src/docs/en/AutoTagging.md";
|
||||||
|
import JSONSpec from "src/docs/en/JSONSpec.md";
|
||||||
|
import Configuration from "src/docs/en/Configuration.md";
|
||||||
|
import Interface from "src/docs/en/Interface.md";
|
||||||
|
import Galleries from "src/docs/en/Galleries.md";
|
||||||
|
import Scraping from "src/docs/en/Scraping.md";
|
||||||
|
import Contributing from "src/docs/en/Contributing.md";
|
||||||
|
import SceneFilenameParser from "src/docs/en/SceneFilenameParser.md";
|
||||||
|
import Help from "src/docs/en/Help.md";
|
||||||
|
import { Page } from "./Page";
|
||||||
|
|
||||||
|
interface IManualProps {
|
||||||
|
show: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Manual: React.FC<IManualProps> = ({ show, onClose }) => {
|
||||||
|
const content = [
|
||||||
|
{
|
||||||
|
key: "Introduction.md",
|
||||||
|
title: "Introduction",
|
||||||
|
content: Introduction,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "Configuration.md",
|
||||||
|
title: "Configuration",
|
||||||
|
content: Configuration,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "Interface.md",
|
||||||
|
title: "Interface",
|
||||||
|
content: Interface,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "Tasks.md",
|
||||||
|
title: "Tasks",
|
||||||
|
content: Tasks,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "AutoTagging.md",
|
||||||
|
title: "Auto Tagging",
|
||||||
|
content: AutoTagging,
|
||||||
|
className: "indent-1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "SceneFilenameParser.md",
|
||||||
|
title: "Scene Filename Parser",
|
||||||
|
content: SceneFilenameParser,
|
||||||
|
className: "indent-1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "JSONSpec.md",
|
||||||
|
title: "JSON Specification",
|
||||||
|
content: JSONSpec,
|
||||||
|
className: "indent-1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "Galleries.md",
|
||||||
|
title: "Image Galleries",
|
||||||
|
content: Galleries,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "Scraping.md",
|
||||||
|
title: "Metadata Scraping",
|
||||||
|
content: Scraping,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "Contributing.md",
|
||||||
|
title: "Contributing",
|
||||||
|
content: Contributing,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "Help.md",
|
||||||
|
title: "Further Help",
|
||||||
|
content: Help,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const [activeTab, setActiveTab] = useState(content[0].key);
|
||||||
|
|
||||||
|
// links to other manual pages are specified as "/help/page.md"
|
||||||
|
// intercept clicks to these pages and set the tab accordingly
|
||||||
|
function interceptLinkClick(
|
||||||
|
event: React.MouseEvent<HTMLDivElement, MouseEvent>
|
||||||
|
) {
|
||||||
|
if (event.target instanceof HTMLAnchorElement) {
|
||||||
|
const href = (event.target as HTMLAnchorElement).getAttribute("href");
|
||||||
|
if (href && href.startsWith("/help")) {
|
||||||
|
const newKey = (event.target as HTMLAnchorElement).pathname.substring(
|
||||||
|
"/help/".length
|
||||||
|
);
|
||||||
|
setActiveTab(newKey);
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
show={show}
|
||||||
|
onHide={onClose}
|
||||||
|
dialogClassName="modal-dialog-scrollable manual modal-xl"
|
||||||
|
>
|
||||||
|
<Modal.Header closeButton>
|
||||||
|
<Modal.Title>Help</Modal.Title>
|
||||||
|
</Modal.Header>
|
||||||
|
<Modal.Body>
|
||||||
|
<Container className="manual-container">
|
||||||
|
<Tab.Container
|
||||||
|
activeKey={activeTab}
|
||||||
|
onSelect={(k) => setActiveTab(k)}
|
||||||
|
id="manual-tabs"
|
||||||
|
>
|
||||||
|
<Row>
|
||||||
|
<Col lg={3} className="mb-3 mb-lg-0">
|
||||||
|
<Nav variant="pills" className="flex-column">
|
||||||
|
{content.map((c) => {
|
||||||
|
return (
|
||||||
|
<Nav.Item>
|
||||||
|
<Nav.Link className={c.className} eventKey={c.key}>
|
||||||
|
{c.title}
|
||||||
|
</Nav.Link>
|
||||||
|
</Nav.Item>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
<hr className="d-sm-none" />
|
||||||
|
</Nav>
|
||||||
|
</Col>
|
||||||
|
<Col lg={9} className="manual-content">
|
||||||
|
<Tab.Content>
|
||||||
|
{content.map((c) => {
|
||||||
|
return (
|
||||||
|
<Tab.Pane eventKey={c.key} onClick={interceptLinkClick}>
|
||||||
|
<Page page={c.content} />
|
||||||
|
</Tab.Pane>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Tab.Content>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Tab.Container>
|
||||||
|
</Container>
|
||||||
|
</Modal.Body>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
22
ui/v2.5/src/components/Help/Page.tsx
Normal file
22
ui/v2.5/src/components/Help/Page.tsx
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import ReactMarkdown from "react-markdown";
|
||||||
|
|
||||||
|
interface IPageProps {
|
||||||
|
// page is a markdown module
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
page: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Page: React.FC<IPageProps> = ({ page }) => {
|
||||||
|
const [markdown, setMarkdown] = useState("");
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!markdown) {
|
||||||
|
fetch(page)
|
||||||
|
.then((res) => res.text())
|
||||||
|
.then((text) => setMarkdown(text));
|
||||||
|
}
|
||||||
|
}, [page, markdown]);
|
||||||
|
|
||||||
|
return <ReactMarkdown className="markdown" source={markdown} />;
|
||||||
|
};
|
||||||
40
ui/v2.5/src/components/Help/styles.scss
Normal file
40
ui/v2.5/src/components/Help/styles.scss
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
.manual {
|
||||||
|
background-color: #30404d;
|
||||||
|
color: $text-color;
|
||||||
|
|
||||||
|
.close {
|
||||||
|
color: $text-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.manual-container {
|
||||||
|
padding-left: 1px;
|
||||||
|
padding-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-header,
|
||||||
|
.modal-body {
|
||||||
|
background-color: #30404d;
|
||||||
|
color: $text-color;
|
||||||
|
overflow-y: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.manual .manual-content {
|
||||||
|
max-height: calc(100vh - 10rem);
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
.indent-1 {
|
||||||
|
padding-left: 2rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 992px) {
|
||||||
|
.manual .modal-body {
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
.manual-content {
|
||||||
|
max-height: inherit;
|
||||||
|
overflow-y: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,6 +12,7 @@ import { Link, NavLink, useLocation } from "react-router-dom";
|
|||||||
import { SessionUtils } from "src/utils";
|
import { SessionUtils } from "src/utils";
|
||||||
|
|
||||||
import { Icon } from "src/components/Shared";
|
import { Icon } from "src/components/Shared";
|
||||||
|
import { Manual } from "./Help/Manual";
|
||||||
|
|
||||||
interface IMenuItem {
|
interface IMenuItem {
|
||||||
message: MessageDescriptor;
|
message: MessageDescriptor;
|
||||||
@@ -91,6 +92,8 @@ const menuItems: IMenuItem[] = [
|
|||||||
export const MainNavbar: React.FC = () => {
|
export const MainNavbar: React.FC = () => {
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const [expanded, setExpanded] = useState(false);
|
const [expanded, setExpanded] = useState(false);
|
||||||
|
const [showManual, setShowManual] = useState(false);
|
||||||
|
|
||||||
// react-bootstrap typing bug
|
// react-bootstrap typing bug
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
const navbarRef = useRef<any>();
|
const navbarRef = useRef<any>();
|
||||||
@@ -147,6 +150,8 @@ export const MainNavbar: React.FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<Manual show={showManual} onClose={() => setShowManual(false)} />
|
||||||
<Navbar
|
<Navbar
|
||||||
collapseOnSelect
|
collapseOnSelect
|
||||||
fixed="top"
|
fixed="top"
|
||||||
@@ -190,12 +195,20 @@ export const MainNavbar: React.FC = () => {
|
|||||||
<Nav className="order-2 flex-row">
|
<Nav className="order-2 flex-row">
|
||||||
<div className="d-none d-sm-block">{newButton}</div>
|
<div className="d-none d-sm-block">{newButton}</div>
|
||||||
<NavLink exact to="/settings" onClick={() => setExpanded(false)}>
|
<NavLink exact to="/settings" onClick={() => setExpanded(false)}>
|
||||||
<Button className="minimal settings-button">
|
<Button className="minimal settings-button" title="Settings">
|
||||||
<Icon icon="cog" />
|
<Icon icon="cog" />
|
||||||
</Button>
|
</Button>
|
||||||
</NavLink>
|
</NavLink>
|
||||||
|
<Button
|
||||||
|
className="minimal help-button"
|
||||||
|
onClick={() => setShowManual(true)}
|
||||||
|
title="Help"
|
||||||
|
>
|
||||||
|
<Icon icon="question-circle" />
|
||||||
|
</Button>
|
||||||
{maybeRenderLogout()}
|
{maybeRenderLogout()}
|
||||||
</Nav>
|
</Nav>
|
||||||
</Navbar>
|
</Navbar>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
15
ui/v2.5/src/docs/en/AutoTagging.md
Normal file
15
ui/v2.5/src/docs/en/AutoTagging.md
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# Auto Tagging
|
||||||
|
|
||||||
|
This task iterates through your created Performers, Studios and Tags - based on what options you ticked. For each, it finds scenes where the filename contains the Performer/Studio/Tag name. For each scene it finds that matches, it sets the applicable field.
|
||||||
|
|
||||||
|
Where the Performer/Studio/Tag name has multiple words, the search will include 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 may match `Mary-Jane-Doe`.
|
||||||
|
|
||||||
|
Auto tagging for specific Performers, Studios and Tags can be performed from the individual Performer/Studio/Tag page.
|
||||||
56
ui/v2.5/src/docs/en/Configuration.md
Normal file
56
ui/v2.5/src/docs/en/Configuration.md
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
# Configuration
|
||||||
|
|
||||||
|
## 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), 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\`
|
||||||
|
|
||||||
|
_a useful [link](https://regex101.com/) to experiment with regexps_
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
### 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.
|
||||||
|
|
||||||
47
ui/v2.5/src/docs/en/Contributing.md
Normal file
47
ui/v2.5/src/docs/en/Contributing.md
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
# Ways to contribute
|
||||||
|
|
||||||
|
## Financial
|
||||||
|
|
||||||
|
Financial contributions are welcomed and are accepted using [Open Collective](https://opencollective.com/stashapp).
|
||||||
|
|
||||||
|
## 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/raw/develop/README.md) file for details on how to get started. Assistance can be provided via our [Discord](https://discord.gg/2TsNFKt).
|
||||||
|
|
||||||
|
## 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) or on [Discord](https://discord.gg/2TsNFKt).
|
||||||
|
|
||||||
|
For those with web page experience, we also welcome contributions to our [website](https://stashapp.cc/) (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).
|
||||||
|
|
||||||
|
|
||||||
|
## Providing support
|
||||||
|
|
||||||
|
Offering support for new users on [Discord](https://discord.gg/2TsNFKt) is also welcomed.
|
||||||
13
ui/v2.5/src/docs/en/Galleries.md
Normal file
13
ui/v2.5/src/docs/en/Galleries.md
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# Galleries
|
||||||
|
|
||||||
|
Stash offers support for image galleries.
|
||||||
|
Here are some remarks on using them:
|
||||||
|
|
||||||
|
- **Galleries are zip-folders with images (e.g. jpeg or png) in them.**
|
||||||
|
- Stash searches for zip galleries in the same paths it searches for videos.
|
||||||
|
- In order for a gallery to be associated with a scene, the zip file and the video file must be in the same folder.
|
||||||
|
- 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.
|
||||||
|
- Stash uses the golang native (pure go) image decoders (more suitable for cross compilation). With huge images, decoding and converting to thumbnails can be slow and in some cases cause visual errors or delays when loading the gallery page.
|
||||||
|
- Stash adds a gallery to its related scene during the scanning process if they have matching names. For example, gallery `/my/stash/collection/media_filename.zip` will be auto assigned to `/my/stash/collection/media_filename.mp4` (where **mp4** can any supported video extension).
|
||||||
|
- 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.
|
||||||
|
- Gallery thumbnails are cached. The first time you go to a gallery page the thumbnails of the images are created and stored to the disk cache for later use. If you want to populate the cache beforehand, this can be done using the Generate task.
|
||||||
7
ui/v2.5/src/docs/en/Help.md
Normal file
7
ui/v2.5/src/docs/en/Help.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# Where to get further help
|
||||||
|
|
||||||
|
Join our [Discord](https://discord.gg/2TsNFKt).
|
||||||
|
|
||||||
|
The [Github wiki](https://github.com/stashapp/stash/wiki) covers some areas not covered in the in-app help.
|
||||||
|
|
||||||
|
Raise a [github issue](https://github.com/stashapp/stash/issues).
|
||||||
53
ui/v2.5/src/docs/en/Interface.md
Normal file
53
ui/v2.5/src/docs/en/Interface.md
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
# Interface Options
|
||||||
|
|
||||||
|
## 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) for a community-curated set of CSS snippets to customise your UI.
|
||||||
|
|
||||||
|
[Stash Plex Theme](https://github.com/stashapp/stash/wiki/Stash-Plex-Theme) is a community created theme inspired by the popular Plex interface.
|
||||||
|
|
||||||
|
|
||||||
|
## 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`.
|
||||||
7
ui/v2.5/src/docs/en/Introduction.md
Normal file
7
ui/v2.5/src/docs/en/Introduction.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# Introduction
|
||||||
|
|
||||||
|
Stash works by cataloging your media using the paths that you provide. Once you have [configured](/settings?tab=configuration) the locations where your media is stored, you can click the Scan button in [`Settings -> Tasks`](/settings?tab=tasks) 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`](/settings?tab=tasks). 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!
|
||||||
470
ui/v2.5/src/docs/en/JSONSpec.md
Normal file
470
ui/v2.5/src/docs/en/JSONSpec.md
Normal file
@@ -0,0 +1,470 @@
|
|||||||
|
# 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:
|
||||||
|
|
||||||
|
* `downloads`
|
||||||
|
* `galleries`
|
||||||
|
* `performers`
|
||||||
|
* `scenes`
|
||||||
|
* `studios`
|
||||||
|
* `movies`
|
||||||
|
|
||||||
|
Additionally, it contains a `mappings.json` file.
|
||||||
|
|
||||||
|
The mappings file contains a reference to all files within the folders, by including their checksum. All files in the aforementioned folders are named by their checksum (like `967ddf2e028f10fc8d36901833c25732.json`), which (at least in the case of galleries and scenes) is generated from the file that this metadata relates to. The algorithm for the checksum is MD5.
|
||||||
|
|
||||||
|
# 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 but the `mappings.json` file, it can be left out of the file entirely. In the `mappings.json` however, all values must be present, if there are no objects of a type (for example, no performers), the value is simply null.
|
||||||
|
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"
|
||||||
|
```
|
||||||
|
|
||||||
|
## `mappings.json`
|
||||||
|
```
|
||||||
|
performers
|
||||||
|
name
|
||||||
|
checksum
|
||||||
|
studios
|
||||||
|
name
|
||||||
|
checksum
|
||||||
|
galleries
|
||||||
|
path
|
||||||
|
checksum
|
||||||
|
scenes
|
||||||
|
path
|
||||||
|
checksum
|
||||||
|
```
|
||||||
|
|
||||||
|
## Performer
|
||||||
|
```
|
||||||
|
name
|
||||||
|
url
|
||||||
|
twitter
|
||||||
|
instagram
|
||||||
|
birthdate
|
||||||
|
ethnicity
|
||||||
|
country
|
||||||
|
eye_color
|
||||||
|
height
|
||||||
|
measurements
|
||||||
|
fake_tits
|
||||||
|
career_length
|
||||||
|
tattoos
|
||||||
|
piercings
|
||||||
|
image (base64 encoding of the image file)
|
||||||
|
created_at
|
||||||
|
updated_at
|
||||||
|
```
|
||||||
|
|
||||||
|
## Studio
|
||||||
|
```
|
||||||
|
name
|
||||||
|
url
|
||||||
|
image (base64 encoding of the image file)
|
||||||
|
created_at
|
||||||
|
updated_at
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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
|
||||||
|
```
|
||||||
|
|
||||||
|
## Gallery
|
||||||
|
|
||||||
|
No files of this kind are generated yet.
|
||||||
|
|
||||||
|
# In JSON format
|
||||||
|
|
||||||
|
For those preferring the json-format, defined [here](https://json-schema.org/), the following format may be more interesting:
|
||||||
|
|
||||||
|
## mappings.json
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"$id": "https://github.com/stashapp/stash/wiki/JSON-Specification/mappings.json",
|
||||||
|
"title": "mappings",
|
||||||
|
"description": "The base file for the metadata. Referring to all other files with names, as well as providing the path to files.",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"performers": {
|
||||||
|
"description": "Link to the performers files along with names",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"checksum": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["name", "checksum"]
|
||||||
|
},
|
||||||
|
"minItems": 0,
|
||||||
|
"uniqueItems": true
|
||||||
|
},
|
||||||
|
"studios": {
|
||||||
|
"description": "Link to the studio files along with names",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"checksum": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["name", "checksum"]
|
||||||
|
},
|
||||||
|
"minItems": 0,
|
||||||
|
"uniqueItems": true
|
||||||
|
},
|
||||||
|
"galleries": {
|
||||||
|
"description": "Link to the gallery files along with the path to the content",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"path": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"checksum": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["path", "checksum"]
|
||||||
|
},
|
||||||
|
"minItems": 0,
|
||||||
|
"uniqueItems": true
|
||||||
|
},
|
||||||
|
"scenes": {
|
||||||
|
"description": "Link to the scene files along with the path to the content",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"path": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"checksum": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["path", "checksum"]
|
||||||
|
},
|
||||||
|
"minItems": 0,
|
||||||
|
"uniqueItems": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["performers", "studios", "galleries", "scenes"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
## 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"
|
||||||
|
},
|
||||||
|
"ethnicity": {
|
||||||
|
"description": "Ethnicity of the Performer. Possible values are black, white, asian or hispanic",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"country": {
|
||||||
|
"description": "Country 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"
|
||||||
|
},
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"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
|
||||||
|
},
|
||||||
|
"file": {
|
||||||
|
"description": "Some technical data about the scenes file.",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"size": {
|
||||||
|
"description": "The size of the file in bytes",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"duration": {
|
||||||
|
"description": "Duration of the scene in seconds. It is given with after comma values, such as 10.0 or 17.5",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"video_codec": {
|
||||||
|
"description": "The coding of the video part of the scene file. An example would be h264",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"audio_codec": {
|
||||||
|
"description": "The coding of the audio part of the scene file. An example would be aac",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"width": {
|
||||||
|
"description": "The width of the scene in pixels",
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"height": {
|
||||||
|
"description": "The height of the scene in pixels",
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"framerate": {
|
||||||
|
"description": "Framerate of the scene. It is given with after comma values, such as 29.95",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"bitrate": {
|
||||||
|
"description": "The bitrate of the video, in bits",
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
"required": ["size", "duration", "video_codec", "audio_codec", "height", "width", "framerate", "bitrate"]
|
||||||
|
},
|
||||||
|
"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"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Gallery
|
||||||
|
|
||||||
|
No files of this kind are created here yet
|
||||||
63
ui/v2.5/src/docs/en/SceneFilenameParser.md
Normal file
63
ui/v2.5/src/docs/en/SceneFilenameParser.md
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
# Scene Filename Parser
|
||||||
|
|
||||||
|
This tool 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.
|
||||||
395
ui/v2.5/src/docs/en/Scraping.md
Normal file
395
ui/v2.5/src/docs/en/Scraping.md
Normal file
@@ -0,0 +1,395 @@
|
|||||||
|
# Metadata Scraping
|
||||||
|
|
||||||
|
Stash supports scraping of performer and scene details.
|
||||||
|
|
||||||
|
Stash includes a freeones.xxx performer scraper built in.
|
||||||
|
|
||||||
|
# Adding custom 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.
|
||||||
|
|
||||||
|
Custom scrapers are added by adding configuration yaml files (format: `scrapername.yml`) to the `scrapers` directory.
|
||||||
|
|
||||||
|
After scrapers are added, removed or edited while stash is running, they can be reloaded by clicking the `Scrape With...` button in New/Edit Performer or Scene page and clicking `Reload Scrapers`.
|
||||||
|
|
||||||
|
# Using custom scrapers
|
||||||
|
|
||||||
|
Scrapers support a number of different scraping types.
|
||||||
|
|
||||||
|
Performer details can be scraped from the new/edit Performer page in two different ways:
|
||||||
|
|
||||||
|
* click on the `Scrape With...` button and select the scraper to scrape with. You will be presented with a search dialog to search for the performer by their name
|
||||||
|
* enter the URL containing the Performer's details in the URL field. If the URL matches a pattern known to one of the scrapers, then a button will appear to scrape the details.
|
||||||
|
|
||||||
|
Scene details can be scraped using URL as above, or via the `Scrape With...` button, which scrapes using the current scene metadata.
|
||||||
|
|
||||||
|
# Community Scrapers
|
||||||
|
The stash community maintains a number of custom scraper configuration files that can be found [here](https://github.com/stashapp/CommunityScrapers).
|
||||||
|
|
||||||
|
# Scraper configuration file format
|
||||||
|
|
||||||
|
## Basic scraper configuration file structure
|
||||||
|
|
||||||
|
```
|
||||||
|
name: <site>
|
||||||
|
performerByName:
|
||||||
|
<single scraper config>
|
||||||
|
performerByFragment:
|
||||||
|
<single scraper config>
|
||||||
|
performerByURL:
|
||||||
|
<multiple scraper URL configs>
|
||||||
|
sceneByFragment:
|
||||||
|
<single scraper config>
|
||||||
|
sceneByURL:
|
||||||
|
<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:
|
||||||
|
|
||||||
|
| Behaviour | 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 `Scrape...` dropdown button in Scene Edit page | Valid `sceneByFragment` configuration. |
|
||||||
|
| Scrape scene from URL | Valid `sceneByURL` 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.
|
||||||
|
|
||||||
|
## Scraper 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:
|
||||||
|
|
||||||
|
```
|
||||||
|
action: script
|
||||||
|
script:
|
||||||
|
- python
|
||||||
|
- iafdScrape.py
|
||||||
|
- query
|
||||||
|
```
|
||||||
|
|
||||||
|
This configuration would execute `python iafdScrape.py query`.
|
||||||
|
|
||||||
|
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 |
|
||||||
|
| `sceneByFragment` | JSON-encoded scene fragment | JSON-encoded scene fragment |
|
||||||
|
| `sceneByURL` | `{"url": "<url>"}` | JSON-encoded scene 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.
|
||||||
|
|
||||||
|
As an example, the following python code snippet can be used to scrape a performer:
|
||||||
|
|
||||||
|
```
|
||||||
|
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 valid for `performerByName`, `performerByURL` and `sceneByURL` only.
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
```
|
||||||
|
sceneByURL:
|
||||||
|
- action: scrapeXPath
|
||||||
|
url:
|
||||||
|
- pornhub.com/view_video.php
|
||||||
|
scraper: sceneScraper
|
||||||
|
```
|
||||||
|
|
||||||
|
The above configuration requires that `sceneScraper` exists in the `xPathScrapers` configuration.
|
||||||
|
|
||||||
|
#### 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:
|
||||||
|
|
||||||
|
```
|
||||||
|
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 ...
|
||||||
|
```
|
||||||
|
|
||||||
|
#### XPath scrapers configuration
|
||||||
|
|
||||||
|
The top-level `xPathScrapers` field contains xpath scraping configurations, freely named. The scraping configuration may contain a `common` field, and must contain `performer` or `scene` depending on the scraping type it is configured for.
|
||||||
|
|
||||||
|
Within the `performer`/`scene` field are key/value pairs corresponding to the golang fields (see below) on the performer/scene object. These fields are case-sensitive.
|
||||||
|
|
||||||
|
The values of these may be either a simple xpath value, which tells the system where to get the value of the field from, or a more advanced configuration (see below). For example:
|
||||||
|
|
||||||
|
```
|
||||||
|
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">...`.
|
||||||
|
|
||||||
|
The value may also be a sub-object, indicating that post-processing is required. If it is a sub-object, then the xpath must be set to the `selector` key of the sub-object. For example, using the same xpath as above:
|
||||||
|
|
||||||
|
```
|
||||||
|
performer:
|
||||||
|
Name:
|
||||||
|
selector: //h1[@itemprop="name"]
|
||||||
|
# post-processing config values
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Common fragments
|
||||||
|
|
||||||
|
The `common` field is used to configure xpath fragments that can be referenced in the xpath 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:
|
||||||
|
|
||||||
|
```
|
||||||
|
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"]`.
|
||||||
|
|
||||||
|
##### Post-processing options
|
||||||
|
|
||||||
|
The following post-processing keys are available:
|
||||||
|
* `concat`: if an xpath matches multiple elements, and `concat` is present, then all of the elements will be concatenated together
|
||||||
|
* `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 - `` is the first capture group, `` the second and so on. Replacements are performed in order of the array.
|
||||||
|
* `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), [#606](https://github.com/stashapp/stash/pull/606)
|
||||||
|
* `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) 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`).
|
||||||
|
* `split`: Its 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)
|
||||||
|
|
||||||
|
Post-processing is done in order of the fields above - `concat`, `regex`, `subscraper`, `parseDate` and then `split`.
|
||||||
|
|
||||||
|
##### Example
|
||||||
|
|
||||||
|
A performer and scene xpath scraper is shown as an example below:
|
||||||
|
|
||||||
|
```
|
||||||
|
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"]
|
||||||
|
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"]
|
||||||
|
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) for more examples.
|
||||||
|
|
||||||
|
#### XPath resources:
|
||||||
|
|
||||||
|
- Test XPaths in Firefox: https://addons.mozilla.org/en-US/firefox/addon/try-xpath/
|
||||||
|
- XPath cheatsheet: https://devhints.io/xpath
|
||||||
|
|
||||||
|
#### Object fields
|
||||||
|
##### Performer
|
||||||
|
|
||||||
|
```
|
||||||
|
Name
|
||||||
|
Gender
|
||||||
|
URL
|
||||||
|
Twitter
|
||||||
|
Instagram
|
||||||
|
Birthdate
|
||||||
|
Ethnicity
|
||||||
|
Country
|
||||||
|
EyeColor
|
||||||
|
Height
|
||||||
|
Measurements
|
||||||
|
FakeTits
|
||||||
|
CareerLength
|
||||||
|
Tattoos
|
||||||
|
Piercings
|
||||||
|
Aliases
|
||||||
|
Image
|
||||||
|
```
|
||||||
|
|
||||||
|
*Note:* - `Gender` must be one of `male`, `female`, `transgender_male`, `transgender_female` (case insensitive).
|
||||||
|
|
||||||
|
##### Scene
|
||||||
|
```
|
||||||
|
Title
|
||||||
|
Details
|
||||||
|
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
|
||||||
|
Synopsis
|
||||||
|
URL
|
||||||
|
```
|
||||||
|
|
||||||
|
### 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:
|
||||||
|
|
||||||
|
```
|
||||||
|
name: stash
|
||||||
|
performerByName:
|
||||||
|
action: stash
|
||||||
|
performerByFragment:
|
||||||
|
action: stash
|
||||||
|
sceneByFragment:
|
||||||
|
- action: stash
|
||||||
|
stashServer:
|
||||||
|
url: http://stashserver.com:9999
|
||||||
|
```
|
||||||
|
|
||||||
|
### Debugging support
|
||||||
|
To print the received html from a scraper request to the log file, add the following to your scraper yml file:
|
||||||
|
```
|
||||||
|
debug:
|
||||||
|
printHTML: true
|
||||||
|
```
|
||||||
56
ui/v2.5/src/docs/en/Tasks.md
Normal file
56
ui/v2.5/src/docs/en/Tasks.md
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
# Tasks
|
||||||
|
|
||||||
|
This page allows you to direct the stash server to perform a variety of tasks.
|
||||||
|
|
||||||
|
> **⚠️ Note:** It is currently only possible to run one task at a time. No queuing is currently implemented.
|
||||||
|
|
||||||
|
# Scanning
|
||||||
|
|
||||||
|
The scan function walks through the stash directories you have configured for new and moved files.
|
||||||
|
|
||||||
|
Stash currently identifies files by performing a full MD5 hash on them. 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 a file is detected with the same hash as a file already in the database (and that file still exists on the filesystem), then the duplicate file is ignored.
|
||||||
|
|
||||||
|
The "Set name, data, details from metadata" option will parse the files 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](/help/AutoTagging.md) page.
|
||||||
|
|
||||||
|
# Scene Filename Parser
|
||||||
|
See the [Scene Filename Parser](/help/SceneFilenameParser.md) 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
|
||||||
|
* 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
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
> **⚠️ Note:** The import task wipes the current database completely before importing.
|
||||||
|
|
||||||
|
See the [JSON Specification](/help/JSONSpec.md) page for details on the exported JSON format.
|
||||||
|
|
||||||
|
---
|
||||||
1
ui/v2.5/src/globals.d.ts
vendored
Normal file
1
ui/v2.5/src/globals.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
declare module "*.md";
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
@import "styles/scrollbars";
|
@import "styles/scrollbars";
|
||||||
@import "src/components/Changelog/styles.scss";
|
@import "src/components/Changelog/styles.scss";
|
||||||
@import "src/components/Galleries/styles.scss";
|
@import "src/components/Galleries/styles.scss";
|
||||||
|
@import "src/components/Help/styles.scss";
|
||||||
@import "src/components/List/styles.scss";
|
@import "src/components/List/styles.scss";
|
||||||
@import "src/components/Movies/styles.scss";
|
@import "src/components/Movies/styles.scss";
|
||||||
@import "src/components/Performers/styles.scss";
|
@import "src/components/Performers/styles.scss";
|
||||||
@@ -497,3 +498,100 @@ div.dropdown-menu {
|
|||||||
.pre {
|
.pre {
|
||||||
white-space: pre-line;
|
white-space: pre-line;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.markdown {
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6 {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
margin-top: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > h1:first-child,
|
||||||
|
& > h2:first-child,
|
||||||
|
& > h3:first-child,
|
||||||
|
& > h4:first-child,
|
||||||
|
& > h5:first-child,
|
||||||
|
& > h6:first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2 {
|
||||||
|
padding-bottom: 0.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 1.83rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 1.67rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h5 {
|
||||||
|
font-size: 1.33rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
background-color: darken($color: #30404d, $amount: 3);
|
||||||
|
color: $text-color;
|
||||||
|
padding: 0.2em 0.4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote {
|
||||||
|
p {
|
||||||
|
margin-bottom: 0;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote,
|
||||||
|
pre {
|
||||||
|
code {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
background-color: darken($color: #30404d, $amount: 3);
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
font-size: 85%;
|
||||||
|
line-height: 1.45;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
overflow: auto;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
tr {
|
||||||
|
border-top: 1px solid darken($color: #201d1a, $amount: 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
tr:nth-child(2n) {
|
||||||
|
background-color: darken($color: #30404d, $amount: 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
td,
|
||||||
|
th {
|
||||||
|
border: 1px solid darken($color: #201d1a, $amount: 3);
|
||||||
|
padding: 6px 13px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user