mirror of
https://github.com/stashapp/stash.git
synced 2026-06-10 21:42:03 -05:00
Compare commits
2 Commits
develop
...
i18n--v0.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
00deec2a6e | ||
|
|
5c81cb6361 |
@@ -17,7 +17,7 @@
|
||||
|
||||
# GraphQL generated output
|
||||
pkg/models/generated_*.go
|
||||
ui/v2.5/src/core/generated-graphql.ts
|
||||
ui/v2.5/src/core/generated-*.tsx
|
||||
|
||||
####
|
||||
# Jetbrains
|
||||
|
||||
12
.github/FUNDING.yml
vendored
Normal file
12
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
# github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
# patreon: # Replace with a single Patreon username
|
||||
open_collective: stashapp
|
||||
# ko_fi: # Replace with a single Ko-fi username
|
||||
# tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
# community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
# liberapay: StashApp
|
||||
# issuehunt: # Replace with a single IssueHunt username
|
||||
# otechie: # Replace with a single Otechie username
|
||||
# custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||
40
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
40
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: "[Bug Report] Short Form Subject (50 Chars or less)"
|
||||
labels: bug report
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem please ensure that your screenshots are SFW or at least appropriately censored.
|
||||
|
||||
**Stash Version: (from Settings -> About):**
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Smartphone (please complete the following information):**
|
||||
- Device: [e.g. iPhone6]
|
||||
- OS: [e.g. iOS8.1]
|
||||
- Browser [e.g. stock browser, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
73
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
73
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -1,73 +0,0 @@
|
||||
name: Bug Report
|
||||
description: Create a report to help us fix the bug
|
||||
labels: ["bug report"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: Thanks for taking the time to fill out this bug report! Make sure to read [Contributing](https://github.com/stashapp/stash/blob/develop/docs/CONTRIBUTING.md) document before submitting.
|
||||
- type: checkboxes
|
||||
id: confirm-troubleshooting
|
||||
attributes:
|
||||
label: Have you enabled troubleshooting mode?
|
||||
description: |
|
||||
To ensure the bug is not caused by custom modifications or plugins make sure to enable troubleshooting mode before filing the report. In Stash go to Settings and click **Troubleshooting mode** and then retest to see if the bug still occurs.
|
||||
It's important to note that troubleshooting mode only affects UI modifications and plugins.
|
||||
options:
|
||||
- label: I confirm that the troubleshooting mode is enabled.
|
||||
required: true
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Describe the bug
|
||||
description: Provide a clear and concise description of what the bug is.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: reproduction
|
||||
attributes:
|
||||
label: Steps to reproduce
|
||||
description: Detail the steps that would replicate this issue.
|
||||
placeholder: |
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: expected
|
||||
attributes:
|
||||
label: Expected behaviour
|
||||
description: Provide clear and concise description of what you expected to happen.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: context
|
||||
attributes:
|
||||
label: Screenshots or additional context
|
||||
description: Provide any additional context and SFW screenshots here to help us solve this issue.
|
||||
validations:
|
||||
required: false
|
||||
- type: input
|
||||
id: stashversion
|
||||
attributes:
|
||||
label: Stash version
|
||||
description: This can be found in Settings > About.
|
||||
placeholder: (e.g. v0.28.1)
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: devicedetails
|
||||
attributes:
|
||||
label: Device details
|
||||
description: |
|
||||
Please provide details about the device you are using, including the operating system and browser (if applicable).
|
||||
placeholder: Firefox 97 (64-bit) on Windows 11
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: Relevant log output
|
||||
description: Please copy and paste any relevant log output from Settings > Logs. This will be automatically formatted into code, so no need for backticks.
|
||||
render: shell
|
||||
11
.github/ISSUE_TEMPLATE/config.yml
vendored
11
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,11 +0,0 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Community forum
|
||||
url: https://discourse.stashapp.cc
|
||||
about: Start a discussion on the community forum.
|
||||
- name: Community Discord
|
||||
url: https://discord.gg/Y8MNsvQBvZ
|
||||
about: Chat with the community on Discord.
|
||||
- name: Documentation
|
||||
url: https://docs.stashapp.cc
|
||||
about: Check out documentation for help and information.
|
||||
24
.github/ISSUE_TEMPLATE/discussion---request-for-commentary--rfc-.md
vendored
Normal file
24
.github/ISSUE_TEMPLATE/discussion---request-for-commentary--rfc-.md
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
---
|
||||
name: Discussion / Request for Commentary [RFC]
|
||||
about: This is for issues that will be discussed and won't necessarily result directly
|
||||
in commits or pull requests.
|
||||
title: "[RFC] Short Form Title"
|
||||
labels: help wanted
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!-- Update or delete the title if you need to delegate your title gore to something
|
||||
# Title
|
||||
|
||||
*### Scope*
|
||||
<!-- describe the scope of your topic and your goals ideally within a single paragraph or TL;DR kind of summary so its easier for people to determine if they can contribute at a glance. -->
|
||||
|
||||
## Long Form
|
||||
<!-- Only required if your scope and titles can't cover everything. -->
|
||||
|
||||
## Examples
|
||||
<!-- if you can show a picture or video examples post them here, please ensure that you respect people's time and attention and understand that people are volunteering their time, so concision is ideal and considerate. -->
|
||||
|
||||
## Reference Reading
|
||||
<!-- if there is any reference reading or documentation, please refer to it here. -->
|
||||
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: "[Feature] Short Form Title (50 chars or less.)"
|
||||
labels: feature request
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
47
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
47
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@@ -1,47 +0,0 @@
|
||||
name: Feature Request
|
||||
description: Request a new feature or idea to be added to Stash
|
||||
labels: ["feature request"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: Thank you for taking the time to submit a feature request! Make sure to read [Contributing](https://github.com/stashapp/stash/blob/develop/docs/CONTRIBUTING.md) document before submitting.
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Describe the feature you'd like
|
||||
description: Provide a clear description of the feature you'd like implemented
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: benefits
|
||||
attributes:
|
||||
label: Describe the benefits this would bring to existing users
|
||||
description: |
|
||||
Explain the measurable benefits this feature would achieve for existing users.
|
||||
The benefits should be described in terms of outcomes for users, not specific implementations.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: already_possible
|
||||
attributes:
|
||||
label: Is there an existing way to achieve this goal?
|
||||
description: |
|
||||
Yes/No. If Yes, describe how your proposed feature differs from or improves upon the current method
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
id: confirm-search
|
||||
attributes:
|
||||
label: Have you searched for an existing open/closed issue?
|
||||
description: |
|
||||
To help us keep these issues under control, please ensure you have first [searched our issue list](https://github.com/stashapp/stash/issues?q=is%3Aissue) for any existing issues that cover the core request or benefit of your proposal.
|
||||
options:
|
||||
- label: I have searched for existing issues and none cover the core request of my proposal
|
||||
required: true
|
||||
- type: textarea
|
||||
id: context
|
||||
attributes:
|
||||
label: Additional context
|
||||
description: Add any other context or screenshots about the feature request here.
|
||||
validations:
|
||||
required: false
|
||||
18
.github/PULL_REQUEST_TEMPLATE/BugFix.md
vendored
Normal file
18
.github/PULL_REQUEST_TEMPLATE/BugFix.md
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
---
|
||||
name: Bug Fix
|
||||
about: Add a bug fix this project!
|
||||
title: "[Bug Fix] Short Form Title (50 chars or less.)"
|
||||
labels: bug
|
||||
assignees: 'WithoutPants, bnkai, Leopere'
|
||||
|
||||
---
|
||||
<!-- Please make sure to read https://github.com/stashapp/stash/docs/CONTRIBUTING.md and check that you understand and have followed it as best as possible -->
|
||||
<!-- Explain what your bugfix seeks to remedy in a short paragraph. -->
|
||||
# Scope
|
||||
|
||||
<!-- Declare any issues by typing `fixes #1` or `closes #1` for example so that the automation can kick in when this is merged -->
|
||||
## Closes/Fixes Issues
|
||||
|
||||
<!-- What have you tested specifically and what possible impacts/areas there are that may need retesting by others. -->
|
||||
## Other testing QA Notes
|
||||
|
||||
17
.github/PULL_REQUEST_TEMPLATE/Feature.md
vendored
Normal file
17
.github/PULL_REQUEST_TEMPLATE/Feature.md
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
name: Feature Addition
|
||||
about: Add a feature to this project!
|
||||
title: "[Feature] Short Form Title (50 chars or less.)"
|
||||
labels: enhancement
|
||||
assignees: 'WithoutPants, bnkai, Leopere'
|
||||
|
||||
---
|
||||
<!-- Please make sure to read https://github.com/stashapp/stash/docs/CONTRIBUTING.md and check that you understand and have followed it as best as possible
|
||||
Explain what your feature does in a short paragraph. -->
|
||||
# Scope
|
||||
|
||||
<!-- Declare any issues by typing `fixes #1` or `closes #1` for example so that the automation can kick in when this is merged -->
|
||||
## Closes/Fixes Issues
|
||||
|
||||
<!-- What have you tested specifically and what possible impacts/areas there are that may need retesting by others. -->
|
||||
## Other testing QA Notes
|
||||
39
.github/pull_request_template.md
vendored
39
.github/pull_request_template.md
vendored
@@ -1,39 +0,0 @@
|
||||
<!-- Thank you for submitting a pull request! Make sure to follow the contributing guidelines and this template. -->
|
||||
|
||||
## Description
|
||||
<!-- Please write a clear and concise description of what the pull request does. -->
|
||||
|
||||
|
||||
|
||||
## Related Issue
|
||||
<!-- Please link the issue your pull request is referring to. -->
|
||||
|
||||
|
||||
|
||||
## Testing
|
||||
<!-- Describe the testing steps you have performed. -->
|
||||
|
||||
|
||||
|
||||
## Screenshots
|
||||
<!-- For visual changes, please add before and after screenshots. -->
|
||||
|
||||
|
||||
|
||||
## Checklist
|
||||
<!-- Mark [x] to indicate completion. -->
|
||||
|
||||
- [ ] I have read and understood the [Contributing](https://github.com/stashapp/stash/blob/develop/docs/CONTRIBUTING.md) document.
|
||||
- [ ] I have read and understood the [AI Usage Policy](https://github.com/stashapp/stash/blob/develop/docs/AI_POLICY.md) document.
|
||||
- [ ] I have made corresponding changes to the documentation (if applicable).
|
||||
|
||||
## AI Usage Disclosure
|
||||
<!-- Mark [x] to indicate completion. -->
|
||||
- [ ] I have used AI tools to assist with this pull request, and I have disclosed the tools and how I used them below.
|
||||
<!-- If you used AI to assist with this pull request, please disclose what tools you used and how you used them. -->
|
||||
|
||||
|
||||
|
||||
## Additional Context
|
||||
<!-- Add any other context about the pull request here. -->
|
||||
|
||||
28
.github/workflows/build-compiler.yml
vendored
28
.github/workflows/build-compiler.yml
vendored
@@ -1,28 +0,0 @@
|
||||
name: Compiler Build
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
COMPILER_IMAGE: ghcr.io/stashapp/compiler:14
|
||||
|
||||
jobs:
|
||||
build-compiler:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
- uses: docker/build-push-action@v6
|
||||
with:
|
||||
push: true
|
||||
context: "{{defaultContext}}:docker/compiler"
|
||||
tags: |
|
||||
${{ env.COMPILER_IMAGE }}
|
||||
ghcr.io/stashapp/compiler:latest
|
||||
cache-from: type=gha,scope=all,mode=max
|
||||
cache-to: type=gha,scope=all,mode=max
|
||||
309
.github/workflows/build.yml
vendored
309
.github/workflows/build.yml
vendored
@@ -2,10 +2,7 @@ name: Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- develop
|
||||
- master
|
||||
- 'releases/**'
|
||||
branches: [ develop, master ]
|
||||
pull_request:
|
||||
release:
|
||||
types: [ published ]
|
||||
@@ -15,175 +12,45 @@ concurrency:
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
COMPILER_IMAGE: ghcr.io/stashapp/compiler:14
|
||||
COMPILER_IMAGE: stashapp/compiler:7
|
||||
|
||||
jobs:
|
||||
# Job 1: Generate code and build UI
|
||||
# Runs natively (no Docker) — go generate/gqlgen and node don't need cross-compilers.
|
||||
# Produces artifacts (generated Go files + UI build) consumed by test and build jobs.
|
||||
generate:
|
||||
runs-on: ubuntu-24.04
|
||||
build:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Checkout
|
||||
run: git fetch --prune --unshallow --tags
|
||||
|
||||
- name: Pull compiler image
|
||||
run: docker pull $COMPILER_IMAGE
|
||||
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v3
|
||||
env:
|
||||
cache-name: cache-node_modules
|
||||
with:
|
||||
fetch-depth: 0
|
||||
fetch-tags: true
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version-file: 'go.mod'
|
||||
|
||||
# pnpm version is read from the packageManager field in package.json
|
||||
# very broken (4.3, 4.4)
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061
|
||||
with:
|
||||
package_json_file: ui/v2.5/package.json
|
||||
|
||||
# ensure pnpm store path exists to fix post setup node.js error
|
||||
# https://github.com/actions/setup-node/issues/1137#issuecomment-2508963254
|
||||
- name: Ensure pnpm store path exists
|
||||
run: |
|
||||
PNPM_STORE_PATH="$( pnpm store path --silent )"
|
||||
if [ ! -d "$PNPM_STORE_PATH" ]; then
|
||||
echo "PNPM store directory does not exist, creating it."
|
||||
mkdir -p "$PNPM_STORE_PATH"
|
||||
fi
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'pnpm'
|
||||
cache-dependency-path: ui/v2.5/pnpm-lock.yaml
|
||||
|
||||
- name: Install UI dependencies
|
||||
run: make pre-ui
|
||||
|
||||
- name: Generate
|
||||
run: make generate
|
||||
path: ui/v2.5/node_modules
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('ui/v2.5/yarn.lock') }}
|
||||
|
||||
- name: Cache UI build
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v3
|
||||
id: cache-ui
|
||||
env:
|
||||
cache-name: cache-ui
|
||||
with:
|
||||
path: ui/v2.5/build
|
||||
key: ${{ runner.os }}-ui-build-${{ hashFiles('ui/v2.5/pnpm-lock.yaml', 'ui/v2.5/public/**', 'ui/v2.5/src/**', 'graphql/**/*.graphql') }}
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('ui/v2.5/yarn.lock', 'ui/v2.5/public/**', 'ui/v2.5/src/**', 'graphql/**/*.graphql') }}
|
||||
|
||||
- name: Validate UI
|
||||
# skip UI validation for pull requests if UI is unchanged
|
||||
if: ${{ github.event_name != 'pull_request' || steps.cache-ui.outputs.cache-hit != 'true' }}
|
||||
run: make validate-ui
|
||||
|
||||
- name: Build UI
|
||||
# skip UI build for pull requests if UI is unchanged (UI was cached)
|
||||
if: ${{ github.event_name != 'pull_request' || steps.cache-ui.outputs.cache-hit != 'true' }}
|
||||
run: make ui
|
||||
|
||||
# Bundle generated Go files + UI build for downstream jobs (test + build)
|
||||
- name: Upload generated artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: generated
|
||||
retention-days: 1
|
||||
path: |
|
||||
internal/api/generated_exec.go
|
||||
internal/api/generated_models.go
|
||||
ui/v2.5/build/
|
||||
ui/login/locales/
|
||||
|
||||
# Job 2: Integration tests
|
||||
# Runs natively (no Docker) — only needs Go + GCC (for CGO/SQLite), both on ubuntu-22.04.
|
||||
# Runs in parallel with the build matrix jobs.
|
||||
test:
|
||||
needs: generate
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version-file: 'go.mod'
|
||||
|
||||
# Places generated Go files + UI build into the working tree so the build compiles
|
||||
- name: Download generated artifacts
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: generated
|
||||
|
||||
- name: Test Backend
|
||||
run: make it
|
||||
|
||||
# Job 3: Cross-compile for all platforms
|
||||
# Each platform gets its own runner and Docker container (ghcr.io/stashapp/compiler:13).
|
||||
# Each build-cc-* make target is self-contained (sets its own GOOS/GOARCH/CC),
|
||||
# so running them in separate containers is functionally identical to one container.
|
||||
# Runs in parallel with the test job.
|
||||
build:
|
||||
needs: generate
|
||||
runs-on: ubuntu-24.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- platform: windows
|
||||
make-target: build-cc-windows
|
||||
artifact-paths: |
|
||||
dist/stash-win.exe
|
||||
tag: win
|
||||
- platform: macos
|
||||
make-target: build-cc-macos
|
||||
artifact-paths: |
|
||||
dist/stash-macos
|
||||
dist/Stash.app.zip
|
||||
tag: osx
|
||||
- platform: linux
|
||||
make-target: build-cc-linux
|
||||
artifact-paths: |
|
||||
dist/stash-linux
|
||||
tag: linux
|
||||
- platform: linux-arm64v8
|
||||
make-target: build-cc-linux-arm64v8
|
||||
artifact-paths: |
|
||||
dist/stash-linux-arm64v8
|
||||
tag: arm
|
||||
- platform: linux-arm32v7
|
||||
make-target: build-cc-linux-arm32v7
|
||||
artifact-paths: |
|
||||
dist/stash-linux-arm32v7
|
||||
tag: arm
|
||||
- platform: linux-arm32v6
|
||||
make-target: build-cc-linux-arm32v6
|
||||
artifact-paths: |
|
||||
dist/stash-linux-arm32v6
|
||||
tag: arm
|
||||
- platform: freebsd
|
||||
make-target: build-cc-freebsd
|
||||
artifact-paths: |
|
||||
dist/stash-freebsd
|
||||
tag: freebsd
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
fetch-tags: true
|
||||
|
||||
- name: Download generated artifacts
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: generated
|
||||
|
||||
- name: Cache Go build
|
||||
uses: actions/cache@v5
|
||||
- name: Cache go build
|
||||
uses: actions/cache@v3
|
||||
env:
|
||||
# increment the number suffix to bump the cache
|
||||
cache-name: cache-go-cache-1
|
||||
with:
|
||||
path: .go-cache
|
||||
key: ${{ runner.os }}-go-cache-${{ matrix.platform }}-${{ hashFiles('go.mod', '**/go.sum') }}
|
||||
|
||||
# kept separate to test timings
|
||||
- name: pull compiler image
|
||||
run: docker pull $COMPILER_IMAGE
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('go.mod', '**/go.sum') }}
|
||||
|
||||
- name: Start build container
|
||||
env:
|
||||
@@ -192,100 +59,76 @@ jobs:
|
||||
mkdir -p .go-cache
|
||||
docker run -d --name build --mount type=bind,source="$(pwd)",target=/stash,consistency=delegated --mount type=bind,source="$(pwd)/.go-cache",target=/root/.cache/go-build,consistency=delegated --env OFFICIAL_BUILD=${{ env.official-build }} -w /stash $COMPILER_IMAGE tail -f /dev/null
|
||||
|
||||
- name: Build (${{ matrix.platform }})
|
||||
run: docker exec -t build /bin/bash -c "make ${{ matrix.make-target }}"
|
||||
- name: Pre-install
|
||||
run: docker exec -t build /bin/bash -c "make pre-ui"
|
||||
|
||||
- name: Generate
|
||||
run: docker exec -t build /bin/bash -c "make generate"
|
||||
|
||||
- name: Validate UI
|
||||
# skip UI validation for pull requests if UI is unchanged
|
||||
if: ${{ github.event_name != 'pull_request' || steps.cache-ui.outputs.cache-hit != 'true' }}
|
||||
run: docker exec -t build /bin/bash -c "make validate-frontend"
|
||||
|
||||
# Static validation happens in the linter workflow in parallel to this workflow
|
||||
# Run Dynamic validation here, to make sure we pass all the projects integration tests
|
||||
- name: Test Backend
|
||||
run: docker exec -t build /bin/bash -c "make it"
|
||||
|
||||
- name: Build UI
|
||||
# skip UI build for pull requests if UI is unchanged (UI was cached)
|
||||
# this means that the build version/time may be incorrect if the UI is
|
||||
# not changed in a pull request
|
||||
if: ${{ github.event_name != 'pull_request' || steps.cache-ui.outputs.cache-hit != 'true' }}
|
||||
run: docker exec -t build /bin/bash -c "make ui"
|
||||
|
||||
- name: Compile for all supported platforms
|
||||
run: |
|
||||
docker exec -t build /bin/bash -c "make cross-compile-windows"
|
||||
docker exec -t build /bin/bash -c "make cross-compile-macos-intel"
|
||||
docker exec -t build /bin/bash -c "make cross-compile-macos-applesilicon"
|
||||
docker exec -t build /bin/bash -c "make cross-compile-linux"
|
||||
docker exec -t build /bin/bash -c "make cross-compile-linux-arm64v8"
|
||||
docker exec -t build /bin/bash -c "make cross-compile-linux-arm32v7"
|
||||
docker exec -t build /bin/bash -c "make cross-compile-linux-arm32v6"
|
||||
|
||||
- name: Cleanup build container
|
||||
run: docker rm -f -v build
|
||||
|
||||
- name: Upload build artifact
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: build-${{ matrix.platform }}
|
||||
retention-days: 1
|
||||
path: ${{ matrix.artifact-paths }}
|
||||
|
||||
# Job 4: Release
|
||||
# Waits for both test and build to pass, then collects all platform artifacts
|
||||
# into dist/ for checksums, GitHub releases, and multi-arch Docker push.
|
||||
release:
|
||||
needs: [test, build]
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
fetch-tags: true
|
||||
|
||||
# Downloads all artifacts (generated + 7 platform builds) into artifacts/ subdirectories
|
||||
- name: Download all build artifacts
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
path: artifacts
|
||||
|
||||
# Reassemble platform binaries from matrix job artifacts into a single dist/ directory
|
||||
# make sure that artifacts have executable bit set
|
||||
# upload-artifact@v4 strips the common path prefix (dist/), so files are at the artifact root
|
||||
- name: Collect binaries
|
||||
run: |
|
||||
mkdir -p dist
|
||||
cp artifacts/build-*/* dist/
|
||||
chmod +x dist/*
|
||||
|
||||
- name: Zip UI
|
||||
run: |
|
||||
cd artifacts/generated/ui/v2.5/build && zip -r ../../../../../dist/stash-ui.zip .
|
||||
|
||||
- name: Generate checksums
|
||||
run: |
|
||||
git describe --tags --exclude latest_develop | tee CHECKSUMS_SHA1
|
||||
sha1sum dist/Stash.app.zip dist/stash-* dist/stash-ui.zip | sed 's/dist\///g' | tee -a CHECKSUMS_SHA1
|
||||
sha1sum dist/stash-* | sed 's/dist\///g' | tee -a CHECKSUMS_SHA1
|
||||
echo "STASH_VERSION=$(git describe --tags --exclude latest_develop)" >> $GITHUB_ENV
|
||||
echo "RELEASE_DATE=$(date +'%Y-%m-%d %H:%M:%S %Z')" >> $GITHUB_ENV
|
||||
|
||||
- name: Upload Windows binary
|
||||
# only upload binaries for pull requests
|
||||
if: ${{ github.event_name == 'pull_request' && github.base_ref != 'refs/heads/develop' && github.base_ref != 'refs/heads/master'}}
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: stash-win.exe
|
||||
path: dist/stash-win.exe
|
||||
|
||||
- name: Upload macOS binary
|
||||
- name: Upload OSX binary
|
||||
# only upload binaries for pull requests
|
||||
if: ${{ github.event_name == 'pull_request' && github.base_ref != 'refs/heads/develop' && github.base_ref != 'refs/heads/master'}}
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: stash-macos
|
||||
path: dist/stash-macos
|
||||
|
||||
- name: Upload macOS bundle
|
||||
# only upload binaries for pull requests
|
||||
if: ${{ github.event_name == 'pull_request' && github.base_ref != 'refs/heads/develop' && github.base_ref != 'refs/heads/master'}}
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: Stash.app.zip
|
||||
path: dist/Stash.app.zip
|
||||
name: stash-macos-intel
|
||||
path: dist/stash-macos-intel
|
||||
|
||||
- name: Upload Linux binary
|
||||
# only upload binaries for pull requests
|
||||
if: ${{ github.event_name == 'pull_request' && github.base_ref != 'refs/heads/develop' && github.base_ref != 'refs/heads/master'}}
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: stash-linux
|
||||
path: dist/stash-linux
|
||||
|
||||
- name: Upload UI
|
||||
# only upload for pull requests
|
||||
if: ${{ github.event_name == 'pull_request' && github.base_ref != 'refs/heads/develop' && github.base_ref != 'refs/heads/master'}}
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: stash-ui.zip
|
||||
path: dist/stash-ui.zip
|
||||
|
||||
- name: Update latest_develop tag
|
||||
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/develop' }}
|
||||
run: git tag -f latest_develop; git push -f --tags
|
||||
run : git tag -f latest_develop; git push -f --tags
|
||||
|
||||
- name: Development Release
|
||||
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/develop' }}
|
||||
@@ -296,15 +139,13 @@ jobs:
|
||||
automatic_release_tag: latest_develop
|
||||
title: "${{ env.STASH_VERSION }}: Latest development build"
|
||||
files: |
|
||||
dist/Stash.app.zip
|
||||
dist/stash-macos
|
||||
dist/stash-macos-intel
|
||||
dist/stash-macos-applesilicon
|
||||
dist/stash-win.exe
|
||||
dist/stash-linux
|
||||
dist/stash-linux-arm64v8
|
||||
dist/stash-linux-arm32v7
|
||||
dist/stash-linux-arm32v6
|
||||
dist/stash-freebsd
|
||||
dist/stash-ui.zip
|
||||
CHECKSUMS_SHA1
|
||||
|
||||
- name: Master release
|
||||
@@ -316,15 +157,13 @@ jobs:
|
||||
token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
allow_override: true
|
||||
files: |
|
||||
dist/Stash.app.zip
|
||||
dist/stash-macos
|
||||
dist/stash-macos-intel
|
||||
dist/stash-macos-applesilicon
|
||||
dist/stash-win.exe
|
||||
dist/stash-linux
|
||||
dist/stash-linux-arm64v8
|
||||
dist/stash-linux-arm32v7
|
||||
dist/stash-linux-arm32v6
|
||||
dist/stash-freebsd
|
||||
dist/stash-ui.zip
|
||||
CHECKSUMS_SHA1
|
||||
gzip: false
|
||||
|
||||
@@ -335,7 +174,7 @@ jobs:
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
run: |
|
||||
docker run --rm --privileged tonistiigi/binfmt
|
||||
docker run --rm --privileged docker/binfmt:a7996909642ee92942dcd6cff44b9b95f08dad64
|
||||
docker info
|
||||
docker buildx create --name builder --use
|
||||
docker buildx inspect --bootstrap
|
||||
@@ -351,7 +190,7 @@ jobs:
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
run: |
|
||||
docker run --rm --privileged tonistiigi/binfmt
|
||||
docker run --rm --privileged docker/binfmt:a7996909642ee92942dcd6cff44b9b95f08dad64
|
||||
docker info
|
||||
docker buildx create --name builder --use
|
||||
docker buildx inspect --bootstrap
|
||||
|
||||
52
.github/workflows/golangci-lint.yml
vendored
52
.github/workflows/golangci-lint.yml
vendored
@@ -6,27 +6,55 @@ on:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
- 'releases/**'
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
COMPILER_IMAGE: stashapp/compiler:7
|
||||
|
||||
jobs:
|
||||
golangci:
|
||||
name: lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# no tags or depth needed for lint
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version-file: 'go.mod'
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Checkout
|
||||
run: git fetch --prune --unshallow --tags
|
||||
|
||||
- name: Pull compiler image
|
||||
run: docker pull $COMPILER_IMAGE
|
||||
|
||||
- name: Start build container
|
||||
run: |
|
||||
mkdir -p .go-cache
|
||||
docker run -d --name build --mount type=bind,source="$(pwd)",target=/stash,consistency=delegated --mount type=bind,source="$(pwd)/.go-cache",target=/root/.cache/go-build,consistency=delegated -w /stash $COMPILER_IMAGE tail -f /dev/null
|
||||
|
||||
# generate-backend runs natively (just go generate + touch-ui) — no Docker needed
|
||||
- name: Generate Backend
|
||||
run: make generate-backend
|
||||
run: docker exec -t build /bin/bash -c "make generate-backend"
|
||||
|
||||
## WARN
|
||||
## using v1, update in a later PR
|
||||
- name: Run golangci-lint
|
||||
uses: golangci/golangci-lint-action@v8
|
||||
uses: golangci/golangci-lint-action@v2
|
||||
with:
|
||||
version: v2.11.4
|
||||
# Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version
|
||||
version: latest
|
||||
|
||||
# Optional: working directory, useful for monorepos
|
||||
# working-directory: somedir
|
||||
|
||||
# Optional: golangci-lint command line arguments.
|
||||
args: --modules-download-mode=vendor --timeout=5m
|
||||
|
||||
# Optional: show only new issues if it's a pull request. The default value is `false`.
|
||||
# only-new-issues: true
|
||||
|
||||
# Optional: if set to true then the action will use pre-installed Go.
|
||||
# skip-go-installation: true
|
||||
|
||||
# Optional: if set to true then the action don't cache or restore ~/go/pkg.
|
||||
skip-pkg-cache: true
|
||||
|
||||
# Optional: if set to true then the action don't cache or restore ~/.cache/go-build.
|
||||
skip-build-cache: true
|
||||
|
||||
- name: Cleanup build container
|
||||
run: docker rm -f -v build
|
||||
|
||||
18
.gitignore
vendored
18
.gitignore
vendored
@@ -2,9 +2,6 @@
|
||||
# Go
|
||||
####
|
||||
|
||||
# Vendored dependencies
|
||||
vendor
|
||||
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
@@ -20,9 +17,12 @@ vendor
|
||||
|
||||
# GraphQL generated output
|
||||
internal/api/generated_*.go
|
||||
ui/v2.5/src/core/generated-*.tsx
|
||||
|
||||
####
|
||||
# Jetbrains
|
||||
####
|
||||
|
||||
# Generated locale files
|
||||
ui/login/locales/*
|
||||
|
||||
####
|
||||
# Visual Studio
|
||||
@@ -50,6 +50,9 @@ ui/login/locales/*
|
||||
.idea/**/uiDesigner.xml
|
||||
.idea/**/dbnavigator.xml
|
||||
|
||||
# Goland Junk
|
||||
pkg/pkg
|
||||
|
||||
####
|
||||
# Random
|
||||
####
|
||||
@@ -59,8 +62,5 @@ node_modules
|
||||
*.db
|
||||
|
||||
/stash
|
||||
/Stash.app
|
||||
/phasher
|
||||
dist
|
||||
.DS_Store
|
||||
/.local*
|
||||
.DS_Store
|
||||
172
.golangci.yml
172
.golangci.yml
@@ -1,100 +1,90 @@
|
||||
version: "2"
|
||||
# options for analysis running
|
||||
run:
|
||||
timeout: 5m
|
||||
modules-download-mode: vendor
|
||||
|
||||
linters:
|
||||
default: none
|
||||
disable-all: true
|
||||
enable:
|
||||
- copyloopvar
|
||||
- dogsled
|
||||
# Default set of linters from golangci-lint
|
||||
- errcheck
|
||||
- errchkjson
|
||||
- errorlint
|
||||
- gocritic
|
||||
- gosimple
|
||||
- govet
|
||||
- ineffassign
|
||||
- staticcheck
|
||||
- typecheck
|
||||
- unused
|
||||
# Linters added by the stash project.
|
||||
# - contextcheck
|
||||
- dogsled
|
||||
- errchkjson
|
||||
- errorlint
|
||||
# - exhaustive
|
||||
- exportloopref
|
||||
- gocritic
|
||||
# - goerr113
|
||||
- gofmt
|
||||
# - gomnd
|
||||
# - ifshort
|
||||
- misspell
|
||||
|
||||
# TODO - fix these in a later PR
|
||||
# - noctx
|
||||
|
||||
# - nakedret
|
||||
- noctx
|
||||
- revive
|
||||
- rowserrcheck
|
||||
- sqlclosecheck
|
||||
- staticcheck
|
||||
- unused
|
||||
|
||||
settings:
|
||||
staticcheck:
|
||||
checks:
|
||||
- all
|
||||
|
||||
# we specify (unnecessary) embedded fields for clarity in many places
|
||||
- -QF1008
|
||||
|
||||
# there's lots of misnamed (eg intId instead of intID) fields in the code.
|
||||
# it's not exactly world-ending, so I'm deferring fixing these for now
|
||||
- -ST1003
|
||||
errorlint:
|
||||
errorf: false
|
||||
asserts: true
|
||||
comparison: true
|
||||
revive:
|
||||
confidence: 0.8
|
||||
severity: error
|
||||
rules:
|
||||
- name: blank-imports
|
||||
disabled: true
|
||||
- name: context-as-argument
|
||||
- name: context-keys-type
|
||||
- name: dot-imports
|
||||
- name: error-return
|
||||
- name: error-strings
|
||||
- name: error-naming
|
||||
- name: exported
|
||||
disabled: true
|
||||
- name: if-return
|
||||
disabled: true
|
||||
- name: increment-decrement
|
||||
- name: var-naming
|
||||
disabled: true
|
||||
- name: var-declaration
|
||||
- name: package-comments
|
||||
- name: range
|
||||
- name: receiver-naming
|
||||
- name: time-naming
|
||||
- name: unexported-return
|
||||
disabled: true
|
||||
- name: indent-error-flow
|
||||
disabled: true
|
||||
- name: errorf
|
||||
- name: empty-block
|
||||
disabled: true
|
||||
- name: superfluous-else
|
||||
- name: unused-parameter
|
||||
disabled: true
|
||||
- name: unreachable-code
|
||||
- name: redefines-builtin-id
|
||||
rowserrcheck:
|
||||
packages:
|
||||
- github.com/jmoiron/sqlx
|
||||
exclusions:
|
||||
generated: lax
|
||||
presets:
|
||||
- comments
|
||||
- common-false-positives
|
||||
- legacy
|
||||
- std-error-handling
|
||||
paths:
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
formatters:
|
||||
enable:
|
||||
- gofmt
|
||||
settings:
|
||||
gofmt:
|
||||
simplify: false
|
||||
exclusions:
|
||||
generated: lax
|
||||
paths:
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
|
||||
# Project-specific linter overrides
|
||||
linters-settings:
|
||||
gofmt:
|
||||
simplify: false
|
||||
|
||||
errorlint:
|
||||
# Disable errorf because there are false positives, where you don't want to wrap
|
||||
# an error.
|
||||
errorf: false
|
||||
asserts: true
|
||||
comparison: true
|
||||
|
||||
revive:
|
||||
ignore-generated-header: true
|
||||
severity: error
|
||||
confidence: 0.8
|
||||
error-code: 1
|
||||
warning-code: 1
|
||||
rules:
|
||||
- name: blank-imports
|
||||
disabled: true
|
||||
- name: context-as-argument
|
||||
- name: context-keys-type
|
||||
- name: dot-imports
|
||||
- name: error-return
|
||||
- name: error-strings
|
||||
- name: error-naming
|
||||
- name: exported
|
||||
disabled: true
|
||||
- name: if-return
|
||||
disabled: true
|
||||
- name: increment-decrement
|
||||
- name: var-naming
|
||||
disabled: true
|
||||
- name: var-declaration
|
||||
- name: package-comments
|
||||
- name: range
|
||||
- name: receiver-naming
|
||||
- name: time-naming
|
||||
- name: unexported-return
|
||||
disabled: true
|
||||
- name: indent-error-flow
|
||||
disabled: true
|
||||
- name: errorf
|
||||
- name: empty-block
|
||||
disabled: true
|
||||
- name: superfluous-else
|
||||
- name: unused-parameter
|
||||
disabled: true
|
||||
- name: unreachable-code
|
||||
- name: redefines-builtin-id
|
||||
|
||||
rowserrcheck:
|
||||
packages:
|
||||
- github.com/jmoiron/sqlx
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
model:
|
||||
package: graphql
|
||||
filename: ./pkg/stashbox/graphql/generated_models.go
|
||||
filename: ./pkg/scraper/stashbox/graphql/generated_models.go
|
||||
client:
|
||||
package: graphql
|
||||
filename: ./pkg/stashbox/graphql/generated_client.go
|
||||
filename: ./pkg/scraper/stashbox/graphql/generated_client.go
|
||||
models:
|
||||
Date:
|
||||
model: github.com/99designs/gqlgen/graphql.String
|
||||
SceneDraftInput:
|
||||
model: github.com/stashapp/stash/pkg/scraper/stashbox/graphql.SceneDraftInput
|
||||
endpoint:
|
||||
# This points to stashdb.org currently, but can be directed at any stash-box
|
||||
# instance. It is used for generation only.
|
||||
|
||||
3
.idea/go.iml
generated
3
.idea/go.iml
generated
@@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="Go" enabled="true" />
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/certs" />
|
||||
@@ -11,4 +10,4 @@
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
</module>
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
dir: ./pkg/models
|
||||
name: ".*ReaderWriter"
|
||||
outpkg: mocks
|
||||
output: ./pkg/models/mocks
|
||||
486
Makefile
486
Makefile
@@ -7,305 +7,170 @@ ifeq (${SHELL}, cmd)
|
||||
endif
|
||||
|
||||
ifdef IS_WIN_SHELL
|
||||
RM := del /s /q
|
||||
RMDIR := rmdir /s /q
|
||||
NOOP := @@
|
||||
PREFIX := $(USERPROFILE)\\bin
|
||||
SEPARATOR := &&
|
||||
SET := set
|
||||
else
|
||||
RM := rm -f
|
||||
RMDIR := rm -rf
|
||||
NOOP := @:
|
||||
PREFIX := $(HOME)/.local
|
||||
SEPARATOR := ;
|
||||
SET := export
|
||||
endif
|
||||
|
||||
IS_WIN_OS =
|
||||
ifeq ($(OS),Windows_NT)
|
||||
IS_WIN_OS = true
|
||||
endif
|
||||
|
||||
# set LDFLAGS environment variable to any extra ldflags required
|
||||
# set OUTPUT to generate a specific binary name
|
||||
|
||||
LDFLAGS := $(LDFLAGS)
|
||||
|
||||
# set OUTPUT environment variable to generate a specific binary name
|
||||
# this will apply to both `stash` and `phasher`, so build them separately
|
||||
# alternatively use STASH_OUTPUT or PHASHER_OUTPUT to set the value individually
|
||||
ifdef OUTPUT
|
||||
STASH_OUTPUT := $(OUTPUT)
|
||||
PHASHER_OUTPUT := $(OUTPUT)
|
||||
endif
|
||||
ifdef STASH_OUTPUT
|
||||
STASH_OUTPUT := -o $(STASH_OUTPUT)
|
||||
endif
|
||||
ifdef PHASHER_OUTPUT
|
||||
PHASHER_OUTPUT := -o $(PHASHER_OUTPUT)
|
||||
OUTPUT := -o $(OUTPUT)
|
||||
endif
|
||||
|
||||
# set GO_BUILD_FLAGS environment variable to any extra build flags required
|
||||
GO_BUILD_FLAGS := $(GO_BUILD_FLAGS)
|
||||
export CGO_ENABLED = 1
|
||||
|
||||
# set GO_BUILD_TAGS environment variable to any extra build tags required
|
||||
GO_BUILD_TAGS := $(GO_BUILD_TAGS)
|
||||
GO_BUILD_TAGS += sqlite_stat4 sqlite_math_functions
|
||||
.PHONY: release pre-build
|
||||
|
||||
# set STASH_SOURCEMAPS environment variable or uncomment to enable UI sourcemaps
|
||||
# STASH_SOURCEMAPS := true
|
||||
release: pre-ui generate ui build-release
|
||||
|
||||
export CGO_ENABLED := 1
|
||||
|
||||
# define COMPILER_IMAGE for cross-compilation docker container
|
||||
ifndef COMPILER_IMAGE
|
||||
COMPILER_IMAGE := ghcr.io/stashapp/compiler:latest
|
||||
endif
|
||||
|
||||
# cannot really parallelise the release target
|
||||
# generate requires pre-ui, ui requires generate and build-release requires ui, so they must be run sequentially
|
||||
.PHONY: release
|
||||
release:
|
||||
$(MAKE) pre-ui
|
||||
$(MAKE) generate
|
||||
$(MAKE) ui
|
||||
$(MAKE) build-release
|
||||
|
||||
# targets to set various build flags
|
||||
# use combinations on the make command-line to configure a build, e.g.:
|
||||
# for a static-pie release build: `make flags-static-pie flags-release stash`
|
||||
# for a static windows debug build: `make flags-static-windows stash`
|
||||
|
||||
# $(NOOP) prevents "nothing to be done" warnings
|
||||
|
||||
.PHONY: flags-release
|
||||
flags-release:
|
||||
$(NOOP)
|
||||
$(eval LDFLAGS += -s -w)
|
||||
$(eval GO_BUILD_FLAGS += -trimpath)
|
||||
|
||||
.PHONY: flags-pie
|
||||
flags-pie:
|
||||
$(NOOP)
|
||||
$(eval GO_BUILD_FLAGS += -buildmode=pie)
|
||||
|
||||
.PHONY: flags-static
|
||||
flags-static:
|
||||
$(NOOP)
|
||||
$(eval LDFLAGS += -extldflags=-static)
|
||||
$(eval GO_BUILD_TAGS += sqlite_omit_load_extension osusergo netgo)
|
||||
|
||||
.PHONY: flags-static-pie
|
||||
flags-static-pie:
|
||||
$(NOOP)
|
||||
$(eval LDFLAGS += -extldflags=-static-pie)
|
||||
$(eval GO_BUILD_FLAGS += -buildmode=pie)
|
||||
$(eval GO_BUILD_TAGS += sqlite_omit_load_extension osusergo netgo)
|
||||
|
||||
# identical to flags-static-pie, but excluding netgo, which is not needed on windows
|
||||
.PHONY: flags-static-windows
|
||||
flags-static-windows:
|
||||
$(NOOP)
|
||||
$(eval LDFLAGS += -extldflags=-static-pie)
|
||||
$(eval GO_BUILD_FLAGS += -buildmode=pie)
|
||||
$(eval GO_BUILD_TAGS += sqlite_omit_load_extension osusergo)
|
||||
|
||||
.PHONY: build-info
|
||||
build-info:
|
||||
pre-build:
|
||||
ifndef BUILD_DATE
|
||||
$(eval BUILD_DATE := $(shell GOOS=$$(go env GOHOSTOS) GOARCH=$$(go env GOHOSTARCH) go run scripts/getDate.go))
|
||||
$(eval BUILD_DATE := $(shell go run -mod=vendor scripts/getDate.go))
|
||||
endif
|
||||
|
||||
ifndef GITHASH
|
||||
$(eval GITHASH := $(shell git rev-parse --short HEAD))
|
||||
endif
|
||||
|
||||
ifndef STASH_VERSION
|
||||
$(eval STASH_VERSION := $(shell git describe --tags --exclude latest_develop))
|
||||
endif
|
||||
|
||||
ifndef OFFICIAL_BUILD
|
||||
$(eval OFFICIAL_BUILD := false)
|
||||
$(eval OFFICIAL_BUILD := false)
|
||||
endif
|
||||
|
||||
.PHONY: build-flags
|
||||
build-flags: build-info
|
||||
$(eval BUILD_LDFLAGS := $(LDFLAGS))
|
||||
$(eval BUILD_LDFLAGS += -X 'github.com/stashapp/stash/internal/build.buildstamp=$(BUILD_DATE)')
|
||||
$(eval BUILD_LDFLAGS += -X 'github.com/stashapp/stash/internal/build.githash=$(GITHASH)')
|
||||
$(eval BUILD_LDFLAGS += -X 'github.com/stashapp/stash/internal/build.version=$(STASH_VERSION)')
|
||||
$(eval BUILD_LDFLAGS += -X 'github.com/stashapp/stash/internal/build.officialBuild=$(OFFICIAL_BUILD)')
|
||||
$(eval BUILD_FLAGS := -v -tags "$(GO_BUILD_TAGS)" $(GO_BUILD_FLAGS) -ldflags "$(BUILD_LDFLAGS)")
|
||||
build: pre-build
|
||||
build:
|
||||
$(eval LDFLAGS := $(LDFLAGS) -X 'github.com/stashapp/stash/internal/api.version=$(STASH_VERSION)' -X 'github.com/stashapp/stash/internal/api.buildstamp=$(BUILD_DATE)' -X 'github.com/stashapp/stash/internal/api.githash=$(GITHASH)')
|
||||
$(eval LDFLAGS := $(LDFLAGS) -X 'github.com/stashapp/stash/internal/manager/config.officialBuild=$(OFFICIAL_BUILD)')
|
||||
go build $(OUTPUT) -mod=vendor -v -tags "sqlite_omit_load_extension sqlite_stat4 osusergo netgo" $(GO_BUILD_FLAGS) -ldflags "$(LDFLAGS) $(EXTRA_LDFLAGS) $(PLATFORM_SPECIFIC_LDFLAGS)" ./cmd/stash
|
||||
|
||||
.PHONY: stash
|
||||
stash: build-flags
|
||||
go build $(STASH_OUTPUT) $(BUILD_FLAGS) ./cmd/stash
|
||||
# strips debug symbols from the release build
|
||||
build-release: EXTRA_LDFLAGS := -s -w
|
||||
build-release: GO_BUILD_FLAGS := -trimpath
|
||||
build-release: build
|
||||
|
||||
.PHONY: phasher
|
||||
phasher: build-flags
|
||||
go build $(PHASHER_OUTPUT) $(BUILD_FLAGS) ./cmd/phasher
|
||||
build-release-static: EXTRA_LDFLAGS := -extldflags=-static -s -w
|
||||
build-release-static: GO_BUILD_FLAGS := -trimpath
|
||||
build-release-static: build
|
||||
|
||||
# builds dynamically-linked debug binaries
|
||||
.PHONY: build
|
||||
build: stash
|
||||
# cross-compile- targets should be run within the compiler docker container
|
||||
cross-compile-windows: export GOOS := windows
|
||||
cross-compile-windows: export GOARCH := amd64
|
||||
cross-compile-windows: export CC := x86_64-w64-mingw32-gcc
|
||||
cross-compile-windows: export CXX := x86_64-w64-mingw32-g++
|
||||
cross-compile-windows: OUTPUT := -o dist/stash-win.exe
|
||||
cross-compile-windows: build-release-static
|
||||
|
||||
# builds dynamically-linked PIE release binaries
|
||||
.PHONY: build-release
|
||||
build-release: flags-release flags-pie build
|
||||
cross-compile-macos-intel: export GOOS := darwin
|
||||
cross-compile-macos-intel: export GOARCH := amd64
|
||||
cross-compile-macos-intel: export CC := o64-clang
|
||||
cross-compile-macos-intel: export CXX := o64-clang++
|
||||
cross-compile-macos-intel: OUTPUT := -o dist/stash-macos-intel
|
||||
# can't use static build for OSX
|
||||
cross-compile-macos-intel: build-release
|
||||
|
||||
# compile and bundle into Stash.app
|
||||
# for when on macOS itself
|
||||
.PHONY: stash-macapp
|
||||
stash-macapp: STASH_OUTPUT := -o stash
|
||||
stash-macapp: flags-release flags-pie stash
|
||||
rm -rf Stash.app
|
||||
cp -R scripts/macos-bundle Stash.app
|
||||
mkdir Stash.app/Contents/MacOS
|
||||
cp stash Stash.app/Contents/MacOS/stash
|
||||
|
||||
# build-cc- targets should be run within the compiler docker container
|
||||
|
||||
.PHONY: build-cc-windows
|
||||
build-cc-windows: export GOOS := windows
|
||||
build-cc-windows: export GOARCH := amd64
|
||||
build-cc-windows: export CC := x86_64-w64-mingw32-gcc
|
||||
build-cc-windows: STASH_OUTPUT := -o dist/stash-win.exe
|
||||
build-cc-windows: PHASHER_OUTPUT :=-o dist/phasher-win.exe
|
||||
build-cc-windows: flags-release
|
||||
build-cc-windows: flags-static-windows
|
||||
build-cc-windows: build
|
||||
|
||||
.PHONY: build-cc-macos-intel
|
||||
build-cc-macos-intel: export GOOS := darwin
|
||||
build-cc-macos-intel: export GOARCH := amd64
|
||||
build-cc-macos-intel: export CC := o64-clang
|
||||
build-cc-macos-intel: STASH_OUTPUT := -o dist/stash-macos-intel
|
||||
build-cc-macos-intel: PHASHER_OUTPUT := -o dist/phasher-macos-intel
|
||||
build-cc-macos-intel: flags-release
|
||||
# can't use static build for macOS
|
||||
build-cc-macos-intel: flags-pie
|
||||
build-cc-macos-intel: build
|
||||
|
||||
.PHONY: build-cc-macos-arm
|
||||
build-cc-macos-arm: export GOOS := darwin
|
||||
build-cc-macos-arm: export GOARCH := arm64
|
||||
build-cc-macos-arm: export CC := oa64e-clang
|
||||
build-cc-macos-arm: STASH_OUTPUT := -o dist/stash-macos-arm
|
||||
build-cc-macos-arm: PHASHER_OUTPUT := -o dist/phasher-macos-arm
|
||||
build-cc-macos-arm: flags-release
|
||||
# can't use static build for macOS
|
||||
build-cc-macos-arm: flags-pie
|
||||
build-cc-macos-arm: build
|
||||
|
||||
.PHONY: build-cc-macos
|
||||
build-cc-macos:
|
||||
make build-cc-macos-arm
|
||||
make build-cc-macos-intel
|
||||
|
||||
# Combine into universal binaries
|
||||
lipo -create -output dist/stash-macos dist/stash-macos-intel dist/stash-macos-arm
|
||||
rm dist/stash-macos-intel dist/stash-macos-arm
|
||||
cross-compile-macos-applesilicon: export GOOS := darwin
|
||||
cross-compile-macos-applesilicon: export GOARCH := arm64
|
||||
cross-compile-macos-applesilicon: export CC := oa64e-clang
|
||||
cross-compile-macos-applesilicon: export CXX := oa64e-clang++
|
||||
cross-compile-macos-applesilicon: OUTPUT := -o dist/stash-macos-applesilicon
|
||||
# can't use static build for OSX
|
||||
cross-compile-macos-applesilicon: build-release
|
||||
|
||||
cross-compile-macos:
|
||||
rm -rf dist/Stash.app dist/Stash-macos.zip
|
||||
make cross-compile-macos-applesilicon
|
||||
make cross-compile-macos-intel
|
||||
# Combine into one universal binary
|
||||
lipo -create -output dist/stash-macos-universal dist/stash-macos-intel dist/stash-macos-applesilicon
|
||||
rm dist/stash-macos-intel dist/stash-macos-applesilicon
|
||||
# Place into bundle and zip up
|
||||
rm -rf dist/Stash.app
|
||||
cp -R scripts/macos-bundle dist/Stash.app
|
||||
mkdir dist/Stash.app/Contents/MacOS
|
||||
cp dist/stash-macos dist/Stash.app/Contents/MacOS/stash
|
||||
cd dist && rm -f Stash.app.zip && zip -r Stash.app.zip Stash.app
|
||||
mv dist/stash-macos-universal dist/Stash.app/Contents/MacOS/stash
|
||||
cd dist && zip -r Stash-macos.zip Stash.app && cd ..
|
||||
rm -rf dist/Stash.app
|
||||
|
||||
.PHONY: build-cc-macos-phasher
|
||||
build-cc-macos-phasher:
|
||||
make build-cc-macos-arm
|
||||
make build-cc-macos-intel
|
||||
cross-compile-freebsd: export GOOS := freebsd
|
||||
cross-compile-freebsd: export GOARCH := amd64
|
||||
cross-compile-freebsd: OUTPUT := -o dist/stash-freebsd
|
||||
cross-compile-freebsd: build-release-static
|
||||
|
||||
# Combine into universal binaries
|
||||
lipo -create -output dist/phasher-macos dist/phasher-macos-intel dist/phasher-macos-arm
|
||||
rm dist/phasher-macos-intel dist/phasher-macos-arm
|
||||
# do not bundle phasher
|
||||
cross-compile-linux: export GOOS := linux
|
||||
cross-compile-linux: export GOARCH := amd64
|
||||
cross-compile-linux: OUTPUT := -o dist/stash-linux
|
||||
cross-compile-linux: build-release-static
|
||||
|
||||
.PHONY: build-cc-freebsd
|
||||
build-cc-freebsd: export GOOS := freebsd
|
||||
build-cc-freebsd: export GOARCH := amd64
|
||||
build-cc-freebsd: export CC := clang -target x86_64-unknown-freebsd12.0 --sysroot=/opt/cross-freebsd
|
||||
build-cc-freebsd: STASH_OUTPUT := -o dist/stash-freebsd
|
||||
build-cc-freebsd: PHASHER_OUTPUT := -o dist/phasher-freebsd
|
||||
build-cc-freebsd: flags-release
|
||||
build-cc-freebsd: flags-static-pie
|
||||
build-cc-freebsd: build
|
||||
cross-compile-linux-arm64v8: export GOOS := linux
|
||||
cross-compile-linux-arm64v8: export GOARCH := arm64
|
||||
cross-compile-linux-arm64v8: export CC := aarch64-linux-gnu-gcc
|
||||
cross-compile-linux-arm64v8: OUTPUT := -o dist/stash-linux-arm64v8
|
||||
cross-compile-linux-arm64v8: build-release-static
|
||||
|
||||
.PHONY: build-cc-linux
|
||||
build-cc-linux: export GOOS := linux
|
||||
build-cc-linux: export GOARCH := amd64
|
||||
build-cc-linux: STASH_OUTPUT := -o dist/stash-linux
|
||||
build-cc-linux: PHASHER_OUTPUT := -o dist/phasher-linux
|
||||
build-cc-linux: flags-release
|
||||
build-cc-linux: flags-static-pie
|
||||
build-cc-linux: build
|
||||
cross-compile-linux-arm32v7: export GOOS := linux
|
||||
cross-compile-linux-arm32v7: export GOARCH := arm
|
||||
cross-compile-linux-arm32v7: export GOARM := 7
|
||||
cross-compile-linux-arm32v7: export CC := arm-linux-gnueabihf-gcc
|
||||
cross-compile-linux-arm32v7: OUTPUT := -o dist/stash-linux-arm32v7
|
||||
cross-compile-linux-arm32v7: build-release-static
|
||||
|
||||
.PHONY: build-cc-linux-arm64v8
|
||||
build-cc-linux-arm64v8: export GOOS := linux
|
||||
build-cc-linux-arm64v8: export GOARCH := arm64
|
||||
build-cc-linux-arm64v8: export CC := aarch64-linux-gnu-gcc
|
||||
build-cc-linux-arm64v8: STASH_OUTPUT := -o dist/stash-linux-arm64v8
|
||||
build-cc-linux-arm64v8: PHASHER_OUTPUT := -o dist/phasher-linux-arm64v8
|
||||
build-cc-linux-arm64v8: flags-release
|
||||
build-cc-linux-arm64v8: flags-static-pie
|
||||
build-cc-linux-arm64v8: build
|
||||
cross-compile-linux-arm32v6: export GOOS := linux
|
||||
cross-compile-linux-arm32v6: export GOARCH := arm
|
||||
cross-compile-linux-arm32v6: export GOARM := 6
|
||||
cross-compile-linux-arm32v6: export CC := arm-linux-gnueabi-gcc
|
||||
cross-compile-linux-arm32v6: OUTPUT := -o dist/stash-linux-arm32v6
|
||||
cross-compile-linux-arm32v6: build-release-static
|
||||
|
||||
.PHONY: build-cc-linux-arm32v7
|
||||
build-cc-linux-arm32v7: export GOOS := linux
|
||||
build-cc-linux-arm32v7: export GOARCH := arm
|
||||
build-cc-linux-arm32v7: export GOARM := 7
|
||||
build-cc-linux-arm32v7: export CC := arm-linux-gnueabi-gcc -march=armv7-a
|
||||
build-cc-linux-arm32v7: STASH_OUTPUT := -o dist/stash-linux-arm32v7
|
||||
build-cc-linux-arm32v7: PHASHER_OUTPUT := -o dist/phasher-linux-arm32v7
|
||||
build-cc-linux-arm32v7: flags-release
|
||||
build-cc-linux-arm32v7: flags-static
|
||||
build-cc-linux-arm32v7: build
|
||||
|
||||
.PHONY: build-cc-linux-arm32v6
|
||||
build-cc-linux-arm32v6: export GOOS := linux
|
||||
build-cc-linux-arm32v6: export GOARCH := arm
|
||||
build-cc-linux-arm32v6: export GOARM := 6
|
||||
build-cc-linux-arm32v6: export CC := arm-linux-gnueabi-gcc
|
||||
build-cc-linux-arm32v6: STASH_OUTPUT := -o dist/stash-linux-arm32v6
|
||||
build-cc-linux-arm32v6: PHASHER_OUTPUT := -o dist/phasher-linux-arm32v6
|
||||
build-cc-linux-arm32v6: flags-release
|
||||
build-cc-linux-arm32v6: flags-static
|
||||
build-cc-linux-arm32v6: build
|
||||
|
||||
.PHONY: build-cc-all
|
||||
build-cc-all:
|
||||
make build-cc-windows
|
||||
make build-cc-macos
|
||||
make build-cc-linux
|
||||
make build-cc-linux-arm64v8
|
||||
make build-cc-linux-arm32v7
|
||||
make build-cc-linux-arm32v6
|
||||
make build-cc-freebsd
|
||||
cross-compile-all:
|
||||
make cross-compile-windows
|
||||
make cross-compile-macos-intel
|
||||
make cross-compile-macos-applesilicon
|
||||
make cross-compile-linux
|
||||
make cross-compile-linux-arm64v8
|
||||
make cross-compile-linux-arm32v7
|
||||
make cross-compile-linux-arm32v6
|
||||
|
||||
.PHONY: touch-ui
|
||||
touch-ui:
|
||||
ifdef IS_WIN_SHELL
|
||||
@if not exist "ui\\v2.5\\build" mkdir ui\\v2.5\\build
|
||||
@type nul >> ui/v2.5/build/index.html
|
||||
else
|
||||
ifndef IS_WIN_SHELL
|
||||
@mkdir -p ui/v2.5/build
|
||||
@touch ui/v2.5/build/index.html
|
||||
else
|
||||
@if not exist "ui\\v2.5\\build" mkdir ui\\v2.5\\build
|
||||
@type nul >> ui/v2.5/build/index.html
|
||||
endif
|
||||
|
||||
# Regenerates GraphQL files
|
||||
.PHONY: generate
|
||||
generate: generate-backend generate-ui
|
||||
generate: generate-backend generate-frontend
|
||||
|
||||
.PHONY: generate-ui
|
||||
generate-ui:
|
||||
cd ui/v2.5 && pnpm run gqlgen
|
||||
.PHONY: generate-frontend
|
||||
generate-frontend:
|
||||
cd ui/v2.5 && yarn run gqlgen
|
||||
|
||||
.PHONY: generate-backend
|
||||
generate-backend: touch-ui
|
||||
go generate ./cmd/stash
|
||||
|
||||
.PHONY: generate-login-locale
|
||||
generate-login-locale:
|
||||
go generate ./ui
|
||||
generate-backend: touch-ui
|
||||
go generate -mod=vendor ./cmd/stash
|
||||
|
||||
.PHONY: generate-dataloaders
|
||||
generate-dataloaders:
|
||||
go generate ./internal/api/loaders
|
||||
go generate -mod=vendor ./internal/api/loaders
|
||||
|
||||
# Regenerates stash-box client files
|
||||
.PHONY: generate-stash-box-client
|
||||
generate-stash-box-client:
|
||||
go run github.com/Yamashou/gqlgenc
|
||||
go run -mod=vendor github.com/Yamashou/gqlgenc
|
||||
|
||||
# Runs gofmt -w on the project's source code, modifying any files that do not match its style.
|
||||
.PHONY: fmt
|
||||
@@ -319,141 +184,60 @@ lint:
|
||||
# runs unit tests - excluding integration tests
|
||||
.PHONY: test
|
||||
test:
|
||||
go test ./...
|
||||
go test -mod=vendor ./...
|
||||
|
||||
# runs all tests - including integration tests
|
||||
.PHONY: it
|
||||
it:
|
||||
$(eval GO_BUILD_TAGS += integration)
|
||||
go test -tags "$(GO_BUILD_TAGS)" ./...
|
||||
go test -mod=vendor -tags=integration ./...
|
||||
|
||||
# generates test mocks
|
||||
.PHONY: generate-test-mocks
|
||||
generate-test-mocks:
|
||||
go run github.com/vektra/mockery/v2
|
||||
|
||||
# runs server
|
||||
# sets the config file to use the local dev config
|
||||
.PHONY: server-start
|
||||
server-start: export STASH_CONFIG_FILE := config.yml
|
||||
server-start: build-flags
|
||||
ifdef IS_WIN_SHELL
|
||||
@if not exist ".local" mkdir .local
|
||||
else
|
||||
@mkdir -p .local
|
||||
endif
|
||||
cd .local && go run $(BUILD_FLAGS) ../cmd/stash
|
||||
|
||||
# removes local dev config files
|
||||
.PHONY: server-clean
|
||||
server-clean:
|
||||
$(RMDIR) .local
|
||||
go run -mod=vendor github.com/vektra/mockery/v2 --dir ./pkg/models --name '.*ReaderWriter' --outpkg mocks --output ./pkg/models/mocks
|
||||
|
||||
# installs UI dependencies. Run when first cloning repository, or if UI
|
||||
# dependencies have changed
|
||||
# If CI is set, configures pnpm to use a local store to avoid
|
||||
# putting .pnpm-store in /stash
|
||||
# NOTE: to run in the docker build container, using the existing
|
||||
# node_modules folder, rename the .modules.yaml to .modules.yaml.bak
|
||||
# and a new one will be generated. This will need to be reversed after
|
||||
# building.
|
||||
.PHONY: pre-ui
|
||||
pre-ui:
|
||||
ifdef CI
|
||||
cd ui/v2.5 && pnpm config set store-dir ~/.pnpm-store && pnpm install --frozen-lockfile
|
||||
else
|
||||
cd ui/v2.5 && pnpm install --frozen-lockfile
|
||||
endif
|
||||
|
||||
.PHONY: ui-env
|
||||
ui-env: build-info
|
||||
$(eval export VITE_APP_DATE := $(BUILD_DATE))
|
||||
$(eval export VITE_APP_GITHASH := $(GITHASH))
|
||||
$(eval export VITE_APP_STASH_VERSION := $(STASH_VERSION))
|
||||
ifdef STASH_SOURCEMAPS
|
||||
$(eval export VITE_APP_SOURCEMAPS := true)
|
||||
endif
|
||||
cd ui/v2.5 && yarn install --frozen-lockfile
|
||||
|
||||
.PHONY: ui
|
||||
ui: ui-only generate-login-locale
|
||||
|
||||
.PHONY: ui-only
|
||||
ui-only: ui-env
|
||||
cd ui/v2.5 && pnpm run build
|
||||
|
||||
.PHONY: zip-ui
|
||||
zip-ui:
|
||||
rm -f dist/stash-ui.zip
|
||||
cd ui/v2.5/build && zip -r ../../../dist/stash-ui.zip .
|
||||
ui: pre-build
|
||||
$(SET) VITE_APP_DATE="$(BUILD_DATE)" $(SEPARATOR) \
|
||||
$(SET) VITE_APP_GITHASH=$(GITHASH) $(SEPARATOR) \
|
||||
$(SET) VITE_APP_STASH_VERSION=$(STASH_VERSION) $(SEPARATOR) \
|
||||
cd ui/v2.5 && yarn build
|
||||
|
||||
.PHONY: ui-start
|
||||
ui-start: ui-env
|
||||
cd ui/v2.5 && pnpm run start --host
|
||||
ui-start: pre-build
|
||||
$(SET) VITE_APP_DATE="$(BUILD_DATE)" $(SEPARATOR) \
|
||||
$(SET) VITE_APP_GITHASH=$(GITHASH) $(SEPARATOR) \
|
||||
$(SET) VITE_APP_STASH_VERSION=$(STASH_VERSION) $(SEPARATOR) \
|
||||
cd ui/v2.5 && yarn start --host
|
||||
|
||||
.PHONY: fmt-ui
|
||||
fmt-ui:
|
||||
cd ui/v2.5 && pnpm run format
|
||||
cd ui/v2.5 && yarn format
|
||||
|
||||
# runs tests and checks on the UI and builds it
|
||||
.PHONY: ui-validate
|
||||
ui-validate:
|
||||
cd ui/v2.5 && yarn run validate
|
||||
|
||||
# runs all of the tests and checks required for a PR to be accepted
|
||||
.PHONY: validate
|
||||
validate: validate-frontend validate-backend
|
||||
|
||||
# runs all of the frontend PR-acceptance steps
|
||||
.PHONY: validate-ui
|
||||
validate-ui:
|
||||
cd ui/v2.5 && pnpm run validate
|
||||
|
||||
# these targets run the same steps as fmt-ui and validate-ui, but only on files that have changed
|
||||
fmt-ui-quick:
|
||||
cd ui/v2.5 && \
|
||||
files=$$(git diff --name-only --relative --diff-filter d . ../../graphql); \
|
||||
if [ -n "$$files" ]; then \
|
||||
pnpm exec biome format --write $$files; \
|
||||
fi
|
||||
|
||||
# does not run tsc checks, as they are slow
|
||||
validate-ui-quick:
|
||||
cd ui/v2.5 && \
|
||||
tsfiles=$$(git diff --name-only --relative --diff-filter d src | grep -e "\.tsx\?\$$"); \
|
||||
scssfiles=$$(git diff --name-only --relative --diff-filter d src | grep "\.scss"); \
|
||||
prettyfiles=$$(git diff --name-only --relative --diff-filter d . ../../graphql); \
|
||||
if [ -n "$$tsfiles" ]; then pnpm exec biome check $$tsfiles; fi && \
|
||||
if [ -n "$$scssfiles" ]; then pnpm exec stylelint $$scssfiles; fi && \
|
||||
if [ -n "$$prettyfiles" ]; then pnpm exec biome format $$prettyfiles; fi
|
||||
.PHONY: validate-frontend
|
||||
validate-frontend: ui-validate
|
||||
|
||||
# runs all of the backend PR-acceptance steps
|
||||
.PHONY: validate-backend
|
||||
validate-backend: lint it
|
||||
|
||||
# runs all of the tests and checks required for a PR to be accepted
|
||||
.PHONY: validate
|
||||
validate: validate-ui validate-backend
|
||||
|
||||
# locally builds and tags a 'stash/build' docker image
|
||||
.PHONY: docker-build
|
||||
docker-build: build-info
|
||||
docker-build: pre-build
|
||||
docker build --build-arg GITHASH=$(GITHASH) --build-arg STASH_VERSION=$(STASH_VERSION) -t stash/build -f docker/build/x86_64/Dockerfile .
|
||||
|
||||
# locally builds and tags a 'stash/cuda-build' docker image
|
||||
.PHONY: docker-cuda-build
|
||||
docker-cuda-build: build-info
|
||||
docker build --build-arg GITHASH=$(GITHASH) --build-arg STASH_VERSION=$(STASH_VERSION) -t stash/cuda-build -f docker/build/x86_64/Dockerfile-CUDA .
|
||||
|
||||
# start the build container - for cross compilation
|
||||
# this is adapted from the github actions build.yml file
|
||||
.PHONY: start-compiler-container
|
||||
start-compiler-container:
|
||||
docker run -d --name build --mount type=bind,source="$(PWD)",target=/stash,consistency=delegated $(EXTRA_CONTAINER_ARGS) -w /stash $(COMPILER_IMAGE) tail -f /dev/null
|
||||
|
||||
# run the cross-compilation using
|
||||
# docker exec -t build /bin/bash -c "make build-cc-<platform>"
|
||||
|
||||
.PHONY: remove-compiler-container
|
||||
remove-compiler-container:
|
||||
docker rm -f -v build
|
||||
|
||||
.PHONY: install
|
||||
install:
|
||||
ifdef IS_WIN_SHELL
|
||||
@if not exist "$(PREFIX)" mkdir $(PREFIX)
|
||||
@copy "dist\\stash-win.exe" "$(PREFIX)\\stash-win.exe"
|
||||
else
|
||||
@mkdir -p $(PREFIX)/bin
|
||||
@install -m 755 $(STASH_OUTPUT) $(PREFIX)/bin/stash
|
||||
endif
|
||||
126
README.md
126
README.md
@@ -1,119 +1,73 @@
|
||||
# Stash
|
||||
https://stashapp.cc
|
||||
|
||||
[](https://github.com/stashapp/stash/actions/workflows/build.yml)
|
||||
[](https://hub.docker.com/r/stashapp/stash 'DockerHub')
|
||||
[](https://github.com/sponsors/stashapp)
|
||||
[](https://opencollective.com/stashapp)
|
||||
[](https://hub.docker.com/r/stashapp/stash 'DockerHub')
|
||||
[](https://goreportcard.com/report/github.com/stashapp/stash)
|
||||
[](https://discord.gg/2TsNFKt)
|
||||
[](https://github.com/stashapp/stash/releases/latest)
|
||||
[](https://translate.codeberg.org/engage/stash/)
|
||||
[](https://github.com/stashapp/stash/labels/bounty)
|
||||
|
||||
<h3>Stash is a self-hosted webapp written in Go which organizes and serves your diverse content collection, catering to both your SFW and NSFW needs.</h3>
|
||||
### **Stash is a self-hosted webapp written in Go which organizes and serves your porn.**
|
||||

|
||||
|
||||

|
||||
|
||||
- Stash gathers information about videos in your collection from the internet, and is extensible through the use of community-built plugins for a large number of content producers and sites.
|
||||
- Stash supports a wide variety of both video and image formats.
|
||||
- You can tag videos and find them later.
|
||||
- Stash provides statistics about performers, tags, studios and more.
|
||||
* Stash gathers information about videos in your collection from the internet, and is extensible through the use of community-built plugins for a large number of content producers and sites.
|
||||
* Stash supports a wide variety of both video and image formats.
|
||||
* You can tag videos and find them later.
|
||||
* Stash provides statistics about performers, tags, studios and more.
|
||||
|
||||
You can [watch a SFW demo video](https://vimeo.com/545323354) to see it in action.
|
||||
|
||||
For further information see [Support & Resources](#support--resources) section.
|
||||
For further information you can [read the in-app manual](ui/v2.5/src/docs/en).
|
||||
|
||||
## Installing Stash
|
||||
# Installing Stash
|
||||
|
||||
> [!tip]
|
||||
Step-by-step instructions are available at [docs.stashapp.cc/installation](https://docs.stashapp.cc/installation/).
|
||||
|
||||
> [!important]
|
||||
> **Windows Users**
|
||||
>
|
||||
> As of version 0.27.0, Stash no longer supports _Windows 7, 8, Server 2008 and Server 2012._
|
||||
> At least Windows 10 or Server 2016 is required.
|
||||
>
|
||||
> **macOS Users**
|
||||
>
|
||||
> As of version 0.29.0, Stash requires _macOS 11 Big Sur_ or later.
|
||||
> As of version 0.32.0, Stash requires _macOS 12 Monterey_ or later.
|
||||
> Older versions can still be run through Docker.
|
||||
|
||||
<img src="docs/readme_assets/windows_logo.svg" width="100%" height="75"> Windows | <img src="docs/readme_assets/mac_logo.svg" width="100%" height="75"> macOS | <img src="docs/readme_assets/linux_logo.svg" width="100%" height="75"> Linux | <img src="docs/readme_assets/docker_logo.svg" width="100%" height="75"> Docker
|
||||
<img src="docs/readme_assets/windows_logo.svg" width="100%" height="75"> Windows | <img src="docs/readme_assets/mac_logo.svg" width="100%" height="75"> MacOS| <img src="docs/readme_assets/linux_logo.svg" width="100%" height="75"> Linux | <img src="docs/readme_assets/docker_logo.svg" width="100%" height="75"> Docker
|
||||
:---:|:---:|:---:|:---:
|
||||
[Latest Release](https://github.com/stashapp/stash/releases/latest/download/stash-win.exe) <br /> <sup><sub>[Development Preview](https://github.com/stashapp/stash/releases/download/latest_develop/stash-win.exe)</sub></sup> | [Latest Release](https://github.com/stashapp/stash/releases/latest/download/Stash.app.zip) <br /> <sup><sub>[Development Preview](https://github.com/stashapp/stash/releases/download/latest_develop/Stash.app.zip)</sub></sup> | [Latest Release (amd64)](https://github.com/stashapp/stash/releases/latest/download/stash-linux) <br /> <sup><sub>[Development Preview (amd64)](https://github.com/stashapp/stash/releases/download/latest_develop/stash-linux)</sub></sup> <br /> [More Architectures...](https://github.com/stashapp/stash/releases/latest) | [Instructions](docker/production/README.md) <br /> <sup><sub>[Sample docker-compose.yml](docker/production/docker-compose.yml)</sub></sup>
|
||||
[Latest Release](https://github.com/stashapp/stash/releases/latest/download/stash-win.exe) <br /> <sup><sub>[Development Preview](https://github.com/stashapp/stash/releases/download/latest_develop/stash-win.exe)</sub></sup> | [Latest Release (Apple Silicon)](https://github.com/stashapp/stash/releases/latest/download/stash-macos-applesilicon) <br /> <sup><sub>[Development Preview (Apple Silicon)](https://github.com/stashapp/stash/releases/download/latest_develop/stash-macos-applesilicon)</sub></sup> <br />[Latest Release (Intel)](https://github.com/stashapp/stash/releases/latest/download/stash-macos-intel) <br /> <sup><sub>[Development Preview (Intel)](https://github.com/stashapp/stash/releases/download/latest_develop/stash-macos-intel)</sub></sup> | [Latest Release (amd64)](https://github.com/stashapp/stash/releases/latest/download/stash-linux) <br /> <sup><sub>[Development Preview (amd64)](https://github.com/stashapp/stash/releases/download/latest_develop/stash-linux)</sub></sup> <br /> [More Architectures...](https://github.com/stashapp/stash/releases/latest) | [Instructions](docker/production/README.md) <br /> <sup><sub> [Sample docker-compose.yml](docker/production/docker-compose.yml)</sub></sup>
|
||||
|
||||
Download links for other platforms and architectures are available on the [Releases](https://github.com/stashapp/stash/releases) page.
|
||||
## First Run
|
||||
#### Windows Users: Security Prompt
|
||||
Running the app might present a security prompt since the binary isn't yet signed. Bypass this by clicking "more info" and then the "run anyway" button.
|
||||
#### FFMPEG
|
||||
Stash requires ffmpeg. If you don't have it installed, Stash will download a copy for you. It is recommended that Linux users install `ffmpeg` from their distro's package manager.
|
||||
|
||||
### First Run
|
||||
# Usage
|
||||
|
||||
#### Windows/macOS Users: Security Prompt
|
||||
|
||||
On Windows or macOS, running the app might present a security prompt since the application binary isn't yet signed.
|
||||
|
||||
- On Windows, bypass this by clicking "more info" and then the "run anyway" button.
|
||||
- On macOS, Control+Click the app, click "Open", and then "Open" again.
|
||||
|
||||
#### ffmpeg
|
||||
|
||||
Stash requires FFmpeg. If you don't have it installed, Stash will prompt you to download a copy during setup. It is recommended that Linux users install `ffmpeg` from their distro's package manager.
|
||||
|
||||
## Usage
|
||||
|
||||
### Quickstart Guide
|
||||
|
||||
Stash is a web-based application. Once the application is running, the interface is available (by default) from `http://localhost:9999`.
|
||||
## Quickstart Guide
|
||||
Stash is a web-based application. Once the application is running, the interface is available (by default) from http://localhost:9999.
|
||||
|
||||
On first run, Stash will prompt you for some configuration options and media directories to index, called "Scanning" in Stash. After scanning, your media will be available for browsing, curating, editing, and tagging.
|
||||
|
||||
Stash can pull metadata (performers, tags, descriptions, studios, and more) directly from many sites through the use of [scrapers](https://github.com/stashapp/stash/blob/develop/ui/v2.5/src/docs/en/Manual/Scraping.md), which integrate directly into Stash. Identifying an entire collection will typically require a mix of multiple sources:
|
||||
- The stashapp team maintains [StashDB](https://stashdb.org/), a crowd-sourced repository of scene, studio, and performer information. Connecting it to Stash will allow you to automatically identify much of a typical media collection. It runs on our stash-box software and is primarily focused on mainstream digital scenes and studios. Instructions, invite codes, and more can be found in this guide to [Accessing StashDB](https://guidelines.stashdb.org/docs/faq_getting-started/stashdb/accessing-stashdb/).
|
||||
- Several community-managed stash-box databases can also be connected to Stash in a similar manner. Each one serves a slightly different niche and follows their own methodology. A rundown of each stash-box, their differences, and the information you need to sign up can be found in the [Metadata Sources](https://docs.stashapp.cc/metadata-sources/stash-box-instances/) section of the documentation.
|
||||
- Many community-maintained scrapers can also be downloaded, installed, and updated from within Stash, allowing you to pull data from a wide range of other websites and databases. They can be found by navigating to `Settings → Metadata Providers → Available Scrapers → Community (stable)`. These can be trickier to use than a stash-box because every scraper works a little differently. For more information, please visit the [CommunityScrapers repository](https://github.com/stashapp/CommunityScrapers).
|
||||
- All of the above methods of scraping data into Stash are also covered in more detail in our [Guide to Scraping](https://docs.stashapp.cc/beginner-guides/guide-to-scraping/).
|
||||
Stash can pull metadata (performers, tags, descriptions, studios, and more) directly from many sites through the use of [scrapers](https://github.com/stashapp/stash/tree/develop/ui/v2.5/src/docs/en/Scraping.md), which integrate directly into Stash.
|
||||
|
||||
<sub>[StashDB](http://stashdb.org) is the canonical instance of our open source metadata API, [stash-box](https://github.com/stashapp/stash-box).</sub>
|
||||
Many community-maintained scrapers are available for download at the [Community Scrapers Collection](https://github.com/stashapp/CommunityScrapers). The community also maintains StashDB, a crowd-sourced repository of scene, studio, and performer information, that can automatically identify much of a typical media collection. Inquire in the Discord for details. Identifying an entire collection will typically require a mix of multiple sources.
|
||||
|
||||
## Support & Resources
|
||||
<sub>StashDB is the canonical instance of our open source metadata API, [stash-box](https://github.com/stashapp/stash-box).</sub>
|
||||
|
||||
Need help or want to get involved? Start with the documentation, then reach out to the community if you need further assistance.
|
||||
# Translation
|
||||
[](https://translate.stashapp.cc/engage/stash/)
|
||||
🇧🇷 🇨🇳 🇩🇰 🇳🇱 🇬🇧 🇪🇪 🇫🇮 🇫🇷 🇩🇪 🇮🇹 🇯🇵 🇰🇷 🇵🇱 🇷🇺 🇪🇸 🇸🇪 🇹🇼 🇹🇷
|
||||
|
||||
### Documentation
|
||||
Stash is available in 18 languages (so far!) and it could be in your language too. If you want to help us translate Stash into your language, you can make an account at [translate.stashapp.cc](https://translate.stashapp.cc/projects/stash/stash-desktop-client/) to get started contributing new languages or improving existing ones. Thanks!
|
||||
|
||||
- [Official documentation](https://docs.stashapp.cc) - official guides guides and troubleshooting.
|
||||
- [In-app manual](https://docs.stashapp.cc/in-app-manual) press <kbd>Shift</kbd> + <kbd>?</kbd> in the app or view the manual online.
|
||||
- [FAQ](https://discourse.stashapp.cc/c/support/faq/28) - common questions and answers.
|
||||
- [Community wiki](https://discourse.stashapp.cc/tags/c/community-wiki/22/stash) - guides, how-to’s and tips.
|
||||
|
||||
### Community & Discussion
|
||||
# Support (FAQ)
|
||||
|
||||
- [Community forum](https://discourse.stashapp.cc) - community support, feature requests and discussions.
|
||||
- [Discord](https://discord.gg/2TsNFKt) - real-time chat and community support.
|
||||
- [GitHub discussions](https://github.com/stashapp/stash/discussions) - community support and feature discussions.
|
||||
- [Lemmy community](https://discuss.online/c/stashapp) - board-style community space.
|
||||
Answers to other Frequently Asked Questions can be found [on our Wiki](https://github.com/stashapp/stash/wiki/FAQ)
|
||||
|
||||
### Community Scrapers & Plugins
|
||||
For issues not addressed there, there are a few options.
|
||||
|
||||
- [Metadata sources](https://docs.stashapp.cc/metadata-sources/)
|
||||
- [Plugins](https://docs.stashapp.cc/plugins/)
|
||||
- [Themes](https://docs.stashapp.cc/themes/)
|
||||
- [Other projects](https://docs.stashapp.cc/other-projects/)
|
||||
* Read the [Wiki](https://github.com/stashapp/stash/wiki)
|
||||
* Check the in-app documentation, in the top right corner of the app (also available [here](https://github.com/stashapp/stash/tree/develop/ui/v2.5/src/docs/en)
|
||||
* Join the [Discord server](https://discord.gg/2TsNFKt), where the community can offer support.
|
||||
|
||||
## Architecture
|
||||
# Customization
|
||||
|
||||
You can find an overview of Stash's architecture in the [ARCHITECTURE.md](docs/ARCHITECTURE.md) document.
|
||||
## Themes and CSS Customization
|
||||
There is a [directory of community-created themes](https://github.com/stashapp/stash/wiki/Themes) on our Wiki, along with instructions on how to install them.
|
||||
|
||||
## Contributing
|
||||
You can also make Stash interface fit your desired style with [Custom CSS snippets](https://github.com/stashapp/stash/wiki/Custom-CSS-snippets).
|
||||
|
||||
We welcome contributions and help from all humans who want to improve the project.
|
||||
# For Developers
|
||||
|
||||
Before contributing, please read the [Contributing](docs/CONTRIBUTING.md) document to understand our guidelines and processes for contributing to the project.
|
||||
Pull requests are welcome!
|
||||
|
||||
You can learn about setting up a local development environment in the [Development](docs/DEVELOPMENT.md) document.
|
||||
|
||||
## Translation
|
||||
|
||||
The widget below shows the current translation status of Stash across all supported languages. If you want to help us translate Stash, you can make an account at [Codeberg Translate](https://translate.codeberg.org/projects/stash/stash/) to contribute to new or existing languages. Thanks!
|
||||
|
||||
[](https://translate.codeberg.org/engage/stash/)
|
||||
See [Development](docs/DEVELOPMENT.md) and [Contributing](docs/CONTRIBUTING.md) for information on working with the codebase, getting a local development setup, and contributing changes.
|
||||
|
||||
@@ -1,128 +0,0 @@
|
||||
// TODO: document in README.md
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
flag "github.com/spf13/pflag"
|
||||
"github.com/stashapp/stash/pkg/ffmpeg"
|
||||
"github.com/stashapp/stash/pkg/hash/imagephash"
|
||||
"github.com/stashapp/stash/pkg/hash/videophash"
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
)
|
||||
|
||||
func customUsage() {
|
||||
fmt.Fprintf(os.Stderr, "Usage:\n")
|
||||
fmt.Fprintf(os.Stderr, "%s [OPTIONS] FILE...\n\nOptions:\n", os.Args[0])
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
|
||||
func printPhash(ff *ffmpeg.FFMpeg, ffp *ffmpeg.FFProbe, inputfile string, quiet *bool) error {
|
||||
// Determine if this is a video or image file based on extension
|
||||
ext := filepath.Ext(inputfile)
|
||||
ext = ext[1:] // remove the leading dot
|
||||
|
||||
// Common image extensions
|
||||
imageExts := map[string]bool{
|
||||
"jpg": true, "jpeg": true, "png": true, "gif": true, "webp": true, "bmp": true, "avif": true,
|
||||
}
|
||||
|
||||
if imageExts[ext] {
|
||||
return printImagePhash(ff, inputfile, quiet)
|
||||
}
|
||||
|
||||
return printVideoPhash(ff, ffp, inputfile, quiet)
|
||||
}
|
||||
|
||||
func printVideoPhash(ff *ffmpeg.FFMpeg, ffp *ffmpeg.FFProbe, inputfile string, quiet *bool) error {
|
||||
ffvideoFile, err := ffp.NewVideoFile(inputfile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// All we need for videophash.Generate() is
|
||||
// videoFile.Path (from BaseFile)
|
||||
// videoFile.Duration
|
||||
// The rest of the struct isn't needed.
|
||||
vf := &models.VideoFile{
|
||||
BaseFile: &models.BaseFile{Path: inputfile},
|
||||
Duration: ffvideoFile.FileDuration,
|
||||
}
|
||||
|
||||
phash, err := videophash.Generate(ff, vf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if *quiet {
|
||||
fmt.Printf("%x\n", *phash)
|
||||
} else {
|
||||
fmt.Printf("%x %v\n", *phash, vf.Path)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func printImagePhash(ff *ffmpeg.FFMpeg, inputfile string, quiet *bool) error {
|
||||
imgFile := &models.ImageFile{
|
||||
BaseFile: &models.BaseFile{Path: inputfile},
|
||||
}
|
||||
|
||||
phash, err := imagephash.Generate(ff, imgFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if *quiet {
|
||||
fmt.Printf("%x\n", *phash)
|
||||
} else {
|
||||
fmt.Printf("%x %v\n", *phash, imgFile.Path)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getPaths() (string, string) {
|
||||
ffmpegPath, _ := exec.LookPath("ffmpeg")
|
||||
ffprobePath, _ := exec.LookPath("ffprobe")
|
||||
|
||||
return ffmpegPath, ffprobePath
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Usage = customUsage
|
||||
quiet := flag.BoolP("quiet", "q", false, "print only the phash")
|
||||
help := flag.BoolP("help", "h", false, "print this help output")
|
||||
flag.Parse()
|
||||
|
||||
if *help {
|
||||
flag.Usage()
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
args := flag.Args()
|
||||
|
||||
if len(args) < 1 {
|
||||
fmt.Fprintf(os.Stderr, "Missing FILE argument.\n")
|
||||
flag.Usage()
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
if len(args) > 1 {
|
||||
fmt.Fprintln(os.Stderr, "Files will be processed sequentially! If required, use e.g. GNU Parallel to run concurrently.")
|
||||
fmt.Fprintf(os.Stderr, "Example: parallel %v ::: *.mp4\n", os.Args[0])
|
||||
}
|
||||
|
||||
ffmpegPath, ffprobePath := getPaths()
|
||||
encoder := ffmpeg.NewEncoder(ffmpegPath)
|
||||
// don't need to InitHWSupport, phashing doesn't use hw acceleration
|
||||
ffprobe := ffmpeg.NewFFProbe(ffprobePath)
|
||||
|
||||
for _, item := range args {
|
||||
if err := printPhash(encoder, ffprobe, item, quiet); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,174 +1,68 @@
|
||||
//go:generate go run github.com/99designs/gqlgen
|
||||
//go:generate go run -mod=vendor github.com/99designs/gqlgen
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"runtime/debug"
|
||||
"runtime/pprof"
|
||||
"syscall"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"github.com/stashapp/stash/internal/api"
|
||||
"github.com/stashapp/stash/internal/build"
|
||||
"github.com/stashapp/stash/internal/desktop"
|
||||
"github.com/stashapp/stash/internal/log"
|
||||
"github.com/stashapp/stash/internal/manager"
|
||||
"github.com/stashapp/stash/internal/manager/config"
|
||||
"github.com/stashapp/stash/pkg/logger"
|
||||
"github.com/stashapp/stash/ui"
|
||||
|
||||
_ "github.com/golang-migrate/migrate/v4/database/sqlite3"
|
||||
_ "github.com/golang-migrate/migrate/v4/source/file"
|
||||
)
|
||||
|
||||
var exitCode = 0
|
||||
|
||||
func main() {
|
||||
defer func() {
|
||||
if exitCode != 0 {
|
||||
os.Exit(exitCode)
|
||||
}
|
||||
}()
|
||||
|
||||
defer recoverPanic()
|
||||
|
||||
initLogTemp()
|
||||
|
||||
helpFlag := false
|
||||
pflag.BoolVarP(&helpFlag, "help", "h", false, "show this help text and exit")
|
||||
|
||||
versionFlag := false
|
||||
pflag.BoolVarP(&versionFlag, "version", "v", false, "show version number and exit")
|
||||
|
||||
cpuProfilePath := ""
|
||||
pflag.StringVar(&cpuProfilePath, "cpuprofile", "", "write cpu profile to file")
|
||||
|
||||
pflag.Parse()
|
||||
|
||||
if helpFlag {
|
||||
pflag.Usage()
|
||||
return
|
||||
}
|
||||
|
||||
if versionFlag {
|
||||
fmt.Println(build.VersionString())
|
||||
return
|
||||
}
|
||||
|
||||
cfg, err := config.Initialize()
|
||||
_, err := manager.Initialize()
|
||||
if err != nil {
|
||||
exitError(fmt.Errorf("config initialization error: %w", err))
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
l := initLog(cfg)
|
||||
|
||||
if cpuProfilePath != "" {
|
||||
if err := initProfiling(cpuProfilePath); err != nil {
|
||||
exitError(err)
|
||||
return
|
||||
}
|
||||
defer pprof.StopCPUProfile()
|
||||
}
|
||||
|
||||
// initialise desktop.IsDesktop here so that it doesn't get affected by
|
||||
// ffmpeg hardware checks later on
|
||||
desktop.InitIsDesktop()
|
||||
|
||||
mgr, err := manager.Initialize(cfg, l)
|
||||
if err != nil {
|
||||
exitError(fmt.Errorf("manager initialization error: %w", err))
|
||||
return
|
||||
}
|
||||
defer mgr.Shutdown()
|
||||
|
||||
server, err := api.Initialize()
|
||||
if err != nil {
|
||||
exitError(fmt.Errorf("api initialization error: %w", err))
|
||||
return
|
||||
}
|
||||
defer server.Shutdown()
|
||||
|
||||
exit := make(chan int)
|
||||
|
||||
go func() {
|
||||
err := server.Start()
|
||||
if !errors.Is(err, http.ErrServerClosed) {
|
||||
exitError(fmt.Errorf("http server error: %w", err))
|
||||
exit <- 1
|
||||
defer recoverPanic()
|
||||
if err := api.Start(); err != nil {
|
||||
handleError(err)
|
||||
} else {
|
||||
manager.GetInstance().Shutdown(0)
|
||||
}
|
||||
}()
|
||||
|
||||
go handleSignals(exit)
|
||||
desktop.Start(exit, &ui.FaviconProvider)
|
||||
go handleSignals()
|
||||
desktop.Start(manager.GetInstance(), &manager.FaviconProvider{UIBox: ui.UIBox})
|
||||
|
||||
exitCode = <-exit
|
||||
}
|
||||
|
||||
// initLogTemp initializes a temporary logger for use before the config is loaded.
|
||||
// Logs only error level message to stderr.
|
||||
func initLogTemp() *log.Logger {
|
||||
l := log.NewLogger()
|
||||
l.Init("", true, "Error", 0)
|
||||
logger.Logger = l
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
func initLog(cfg *config.Config) *log.Logger {
|
||||
l := log.NewLogger()
|
||||
l.Init(cfg.GetLogFile(), cfg.GetLogOut(), cfg.GetLogLevel(), cfg.GetLogFileMaxSize())
|
||||
logger.Logger = l
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
func initProfiling(path string) error {
|
||||
f, err := os.Create(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to create CPU profile file: %v", err)
|
||||
}
|
||||
|
||||
if err = pprof.StartCPUProfile(f); err != nil {
|
||||
return fmt.Errorf("could not start CPU profiling: %v", err)
|
||||
}
|
||||
|
||||
logger.Infof("profiling to %s", path)
|
||||
|
||||
return nil
|
||||
blockForever()
|
||||
}
|
||||
|
||||
func recoverPanic() {
|
||||
if err := recover(); err != nil {
|
||||
exitCode = 1
|
||||
logger.Errorf("panic: %v\n%s", err, debug.Stack())
|
||||
if desktop.IsDesktop() {
|
||||
desktop.FatalError(fmt.Errorf("panic: %v", err))
|
||||
}
|
||||
if p := recover(); p != nil {
|
||||
handleError(fmt.Errorf("Panic: %v", p))
|
||||
}
|
||||
}
|
||||
|
||||
func exitError(err error) {
|
||||
exitCode = 1
|
||||
logger.Error(err)
|
||||
// #5784 - log to stdout as well as the logger
|
||||
// this does mean that it will log twice if the logger is set to stdout
|
||||
fmt.Println(err)
|
||||
func handleError(err error) {
|
||||
if desktop.IsDesktop() {
|
||||
desktop.FatalError(err)
|
||||
manager.GetInstance().Shutdown(0)
|
||||
} else {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func handleSignals(exit chan<- int) {
|
||||
func handleSignals() {
|
||||
// handle signals
|
||||
signals := make(chan os.Signal, 1)
|
||||
signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM)
|
||||
|
||||
<-signals
|
||||
exit <- 0
|
||||
manager.GetInstance().Shutdown(0)
|
||||
}
|
||||
|
||||
func blockForever() {
|
||||
select {}
|
||||
}
|
||||
|
||||
@@ -1,39 +1,35 @@
|
||||
# This dockerfile should be built with `make docker-build` from the stash root.
|
||||
|
||||
# Build Frontend
|
||||
FROM node:24-alpine AS frontend
|
||||
RUN apk add --no-cache make git
|
||||
FROM node:alpine as frontend
|
||||
RUN apk add --no-cache make
|
||||
## cache node_modules separately
|
||||
COPY ./ui/v2.5/package.json ./ui/v2.5/pnpm-lock.yaml /stash/ui/v2.5/
|
||||
COPY ./ui/v2.5/package.json ./ui/v2.5/yarn.lock /stash/ui/v2.5/
|
||||
WORKDIR /stash
|
||||
RUN yarn --cwd ui/v2.5 install --frozen-lockfile.
|
||||
COPY Makefile /stash/
|
||||
COPY ./graphql /stash/graphql/
|
||||
COPY ./ui /stash/ui/
|
||||
# pnpm install with npm
|
||||
RUN npm install -g pnpm
|
||||
RUN make pre-ui
|
||||
RUN make generate-ui
|
||||
RUN make generate-frontend
|
||||
ARG GITHASH
|
||||
ARG STASH_VERSION
|
||||
RUN BUILD_DATE=$(date +"%Y-%m-%d %H:%M:%S") make ui-only
|
||||
RUN BUILD_DATE=$(date +"%Y-%m-%d %H:%M:%S") make ui
|
||||
|
||||
# Build Backend
|
||||
FROM golang:1.25.9-alpine AS backend
|
||||
FROM golang:1.19-alpine as backend
|
||||
RUN apk add --no-cache make alpine-sdk
|
||||
WORKDIR /stash
|
||||
COPY ./go* ./*.go Makefile gqlgen.yml .gqlgenc.yml /stash/
|
||||
COPY ./graphql /stash/graphql/
|
||||
COPY ./scripts /stash/scripts/
|
||||
COPY ./vendor /stash/vendor/
|
||||
COPY ./pkg /stash/pkg/
|
||||
COPY ./cmd /stash/cmd/
|
||||
COPY ./internal /stash/internal/
|
||||
# needed for generate-login-locale
|
||||
COPY ./ui /stash/ui/
|
||||
RUN make generate-backend generate-login-locale
|
||||
COPY ./cmd /stash/cmd
|
||||
COPY ./internal /stash/internal
|
||||
COPY --from=frontend /stash /stash/
|
||||
RUN make generate-backend
|
||||
ARG GITHASH
|
||||
ARG STASH_VERSION
|
||||
RUN make flags-release flags-pie stash
|
||||
RUN make build
|
||||
|
||||
# Final Runnable Image
|
||||
FROM alpine:latest
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
# This dockerfile should be built with `make docker-cuda-build` from the stash root.
|
||||
ARG CUDA_VERSION=13.2.1
|
||||
|
||||
# Build Frontend
|
||||
FROM node:24-alpine AS frontend
|
||||
RUN apk add --no-cache make git
|
||||
## cache node_modules separately
|
||||
COPY ./ui/v2.5/package.json ./ui/v2.5/pnpm-lock.yaml /stash/ui/v2.5/
|
||||
WORKDIR /stash
|
||||
COPY Makefile /stash/
|
||||
COPY ./graphql /stash/graphql/
|
||||
COPY ./ui /stash/ui/
|
||||
# pnpm install with npm
|
||||
RUN npm install -g pnpm
|
||||
RUN make pre-ui
|
||||
RUN make generate-ui
|
||||
ARG GITHASH
|
||||
ARG STASH_VERSION
|
||||
RUN BUILD_DATE=$(date +"%Y-%m-%d %H:%M:%S") make ui-only
|
||||
|
||||
# Build Backend
|
||||
FROM golang:1.25.9-bullseye AS backend
|
||||
RUN apt update && apt install -y build-essential golang
|
||||
WORKDIR /stash
|
||||
COPY ./go* ./*.go Makefile gqlgen.yml .gqlgenc.yml /stash/
|
||||
COPY ./graphql /stash/graphql/
|
||||
COPY ./scripts /stash/scripts/
|
||||
COPY ./pkg /stash/pkg/
|
||||
COPY ./cmd /stash/cmd
|
||||
COPY ./internal /stash/internal
|
||||
# needed for generate-login-locale
|
||||
COPY ./ui /stash/ui/
|
||||
RUN make generate-backend generate-login-locale
|
||||
COPY --from=frontend /stash /stash/
|
||||
ARG GITHASH
|
||||
ARG STASH_VERSION
|
||||
RUN make flags-release flags-pie stash
|
||||
|
||||
# Final Runnable Image
|
||||
FROM nvidia/cuda:${CUDA_VERSION}-base-ubuntu24.04
|
||||
RUN apt update && apt upgrade -y && apt install -y \
|
||||
# stash dependencies
|
||||
ca-certificates libvips-tools ffmpeg \
|
||||
# intel dependencies
|
||||
intel-media-va-driver-non-free vainfo \
|
||||
# python tools
|
||||
python3 python3-pip && \
|
||||
# cleanup
|
||||
apt autoremove -y && apt clean && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
COPY --from=backend --chmod=555 /stash/stash /usr/bin/
|
||||
|
||||
# NVENC Patch
|
||||
RUN mkdir -p /usr/local/bin /patched-lib
|
||||
ADD --chmod=555 https://raw.githubusercontent.com/keylase/nvidia-patch/master/patch.sh /usr/local/bin/patch.sh
|
||||
ADD --chmod=555 https://raw.githubusercontent.com/keylase/nvidia-patch/master/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
|
||||
|
||||
ENV LANG=C.UTF-8
|
||||
ENV NVIDIA_VISIBLE_DEVICES=all
|
||||
ENV NVIDIA_DRIVER_CAPABILITIES=video,utility
|
||||
ENV STASH_CONFIG_FILE=/root/.stash/config.yml
|
||||
EXPOSE 9999
|
||||
ENTRYPOINT ["docker-entrypoint.sh", "stash"]
|
||||
|
||||
# vim: ft=dockerfile
|
||||
@@ -4,7 +4,7 @@ This dockerfile is used to build a stash docker container using the current sour
|
||||
|
||||
# Building the docker container
|
||||
|
||||
From the top-level directory (should contain `tools.go` file):
|
||||
From the top-level directory (should contain `main.go` file):
|
||||
|
||||
```
|
||||
make docker-build
|
||||
|
||||
@@ -11,18 +11,9 @@ RUN if [ "$TARGETPLATFORM" = "linux/arm/v6" ]; then BIN=stash-linux-arm32v6; \
|
||||
|
||||
FROM --platform=$TARGETPLATFORM alpine:latest AS app
|
||||
COPY --from=binary /stash /usr/bin/
|
||||
|
||||
RUN apk add --no-cache ca-certificates python3 py3-requests py3-requests-toolbelt py3-lxml py3-pip ffmpeg tzdata vips vips-tools vips-heif \
|
||||
&& pip install --break-system-packages mechanicalsoup cloudscraper stashapp-tools
|
||||
RUN apk add --no-cache ca-certificates python3 py3-requests py3-requests-toolbelt py3-lxml py3-pip ffmpeg vips-tools ruby && pip install --no-cache-dir mechanicalsoup cloudscraper && gem install faraday
|
||||
RUN ln -s /usr/bin/python3 /usr/bin/python
|
||||
ENV STASH_CONFIG_FILE=/root/.stash/config.yml
|
||||
|
||||
# Basic build-time metadata as defined at https://github.com/opencontainers/image-spec/blob/main/annotations.md#pre-defined-annotation-keys
|
||||
LABEL org.opencontainers.image.title="Stash" \
|
||||
org.opencontainers.image.description="An organizer for your porn, written in Go." \
|
||||
org.opencontainers.image.url="https://stashapp.cc" \
|
||||
org.opencontainers.image.documentation="https://docs.stashapp.cc" \
|
||||
org.opencontainers.image.source="https://github.com/stashapp/stash" \
|
||||
org.opencontainers.image.licenses="AGPL-3.0"
|
||||
|
||||
EXPOSE 9999
|
||||
CMD ["stash"]
|
||||
|
||||
@@ -1 +1 @@
|
||||
This Dockerfile is used by CI to build the `stashapp/stash` Docker image. It must be run after cross-compiling - that is, `stash-linux` must exist in the `dist` directory. This image must be built from the `dist` directory.
|
||||
This dockerfile is used by travis to build the stash image. It must be run after cross-compiling - that is, `stash-linux` must exist in the `dist` directory. This image must be built from the `dist` directory.
|
||||
1
docker/compiler/.gitignore
vendored
Normal file
1
docker/compiler/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.sdk.tar.*
|
||||
@@ -1,86 +1,67 @@
|
||||
### OSXCROSS
|
||||
FROM debian:bookworm AS osxcross
|
||||
# add osxcross
|
||||
WORKDIR /tmp/osxcross
|
||||
ARG OSXCROSS_REVISION=5e1b71fcceb23952f3229995edca1b6231525b5b
|
||||
ADD --checksum=sha256:d3f771bbc20612fea577b18a71be3af2eb5ad2dd44624196cf55de866d008647 https://codeload.github.com/tpoechtrager/osxcross/tar.gz/${OSXCROSS_REVISION} /tmp/osxcross.tar.gz
|
||||
FROM golang:1.19
|
||||
|
||||
ARG OSX_SDK_VERSION=12.3
|
||||
ARG OSX_SDK_DOWNLOAD_FILE=MacOSX${OSX_SDK_VERSION}.sdk.tar.xz
|
||||
ARG OSX_SDK_DOWNLOAD_URL=https://github.com/joseluisq/macosx-sdks/releases/download/${OSX_SDK_VERSION}/${OSX_SDK_DOWNLOAD_FILE}
|
||||
ADD --checksum=sha256:3abd261ceb483c44295a6623fdffe5d44fc4ac2c872526576ec5ab5ad0f6e26c ${OSX_SDK_DOWNLOAD_URL} /tmp/osxcross/tarballs/${OSX_SDK_DOWNLOAD_FILE}
|
||||
LABEL maintainer="https://discord.gg/2TsNFKt"
|
||||
|
||||
ENV UNATTENDED=yes \
|
||||
SDK_VERSION=${OSX_SDK_VERSION} \
|
||||
OSX_VERSION_MIN=12.0
|
||||
RUN apt update && \
|
||||
apt install -y --no-install-recommends \
|
||||
bash ca-certificates clang cmake git patch libssl-dev bzip2 cpio libbz2-dev libxml2-dev make python3 xz-utils zlib1g-dev
|
||||
# lzma-dev libxml2-dev xz
|
||||
RUN tar --strip=1 -C /tmp/osxcross -xf /tmp/osxcross.tar.gz
|
||||
RUN ./build.sh
|
||||
# Install tools
|
||||
RUN apt-get update && apt-get install -y apt-transport-https
|
||||
RUN curl -sL https://deb.nodesource.com/setup_lts.x | bash -
|
||||
|
||||
### FREEBSD cross-compilation stage
|
||||
# use alpine for cacheable image since apt is notorous for not caching
|
||||
FROM alpine:3 AS freebsd
|
||||
# match golang latest
|
||||
# https://go.dev/wiki/FreeBSD
|
||||
ARG FREEBSD_VERSION=12.4
|
||||
ADD --checksum=sha256:581c7edacfd2fca2bdf5791f667402d22fccd8a5e184635e0cac075564d57aa8 \
|
||||
http://ftp-archive.freebsd.org/mirror/FreeBSD-Archive/old-releases/amd64/${FREEBSD_VERSION}-RELEASE/base.txz \
|
||||
/tmp/base.txz
|
||||
# prevent caching of the key
|
||||
ADD https://dl.yarnpkg.com/debian/pubkey.gpg yarn.gpg
|
||||
RUN cat yarn.gpg | apt-key add - && \
|
||||
echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \
|
||||
rm yarn.gpg
|
||||
|
||||
WORKDIR /opt/cross-freebsd
|
||||
RUN apk add --no-cache tar xz
|
||||
RUN tar -xf /tmp/base.txz --strip-components=1 ./usr/lib ./usr/include ./lib
|
||||
RUN cd /opt/cross-freebsd/usr/lib && \
|
||||
find . -type l -exec sh -c ' \
|
||||
for link; do \
|
||||
target=$(readlink "$link"); \
|
||||
case "$target" in \
|
||||
/lib/*) ln -sf "/opt/cross-freebsd$target" "$link";; \
|
||||
esac; \
|
||||
done \
|
||||
' sh {} + && \
|
||||
ln -s libc++.a libstdc++.a && \
|
||||
ln -s libc++.so libstdc++.so
|
||||
|
||||
### BUILDER
|
||||
FROM golang:1.25.9 AS builder
|
||||
ENV PATH=/opt/osx-ndk-x86/bin:$PATH
|
||||
|
||||
# copy in nodejs instead of using nodesource :thumbsup:
|
||||
COPY --from=docker.io/library/node:24-bookworm /usr/local /usr/local
|
||||
# copy in osxcross
|
||||
COPY --from=osxcross /tmp/osxcross/target/lib /usr/lib
|
||||
COPY --from=osxcross /tmp/osxcross/target /opt/osx-ndk-x86
|
||||
# copy in cross-freebsd
|
||||
COPY --from=freebsd /opt/cross-freebsd /opt/cross-freebsd
|
||||
|
||||
# pnpm install with npm
|
||||
RUN npm install -g pnpm
|
||||
|
||||
# git for getting hash
|
||||
# make and bash for building
|
||||
|
||||
# clang for macos
|
||||
# zip for stashapp.zip
|
||||
# gcc-extensions for cross-arch build
|
||||
# we still target arm soft float?
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
git make bash \
|
||||
clang zip \
|
||||
gcc-mingw-w64 \
|
||||
gcc-arm-linux-gnueabi \
|
||||
libc-dev-armel-cross linux-libc-dev-armel-cross \
|
||||
gcc-aarch64-linux-gnu libc-dev-arm64-cross && \
|
||||
rm -rf /var/lib/apt/lists/*;
|
||||
RUN git config --global safe.directory '*'
|
||||
apt-get install -y automake autogen cmake \
|
||||
libtool libxml2-dev uuid-dev libssl-dev bash \
|
||||
patch make tar xz-utils bzip2 gzip zlib1g-dev sed cpio \
|
||||
gcc-10-multilib gcc-mingw-w64 g++-mingw-w64 clang llvm-dev \
|
||||
gcc-arm-linux-gnueabi libc-dev-armel-cross linux-libc-dev-armel-cross \
|
||||
gcc-arm-linux-gnueabihf libc-dev-armhf-cross \
|
||||
gcc-aarch64-linux-gnu libc-dev-arm64-cross \
|
||||
nodejs yarn zip --no-install-recommends || exit 1; \
|
||||
rm -rf /var/lib/apt/lists/*;
|
||||
|
||||
# Cross compile setup
|
||||
ENV OSX_SDK_VERSION 11.3
|
||||
ENV OSX_SDK_DOWNLOAD_FILE=MacOSX${OSX_SDK_VERSION}.sdk.tar.xz
|
||||
ENV OSX_SDK_DOWNLOAD_URL=https://github.com/phracker/MacOSX-SDKs/releases/download/${OSX_SDK_VERSION}/${OSX_SDK_DOWNLOAD_FILE}
|
||||
ENV OSX_SDK_SHA=cd4f08a75577145b8f05245a2975f7c81401d75e9535dcffbb879ee1deefcbf4
|
||||
ENV OSX_SDK MacOSX$OSX_SDK_VERSION.sdk
|
||||
ENV OSX_NDK_X86 /usr/local/osx-ndk-x86
|
||||
|
||||
RUN wget ${OSX_SDK_DOWNLOAD_URL}
|
||||
RUN echo "$OSX_SDK_SHA $OSX_SDK_DOWNLOAD_FILE" | sha256sum -c - || exit 1; \
|
||||
git clone https://github.com/tpoechtrager/osxcross.git; \
|
||||
mv $OSX_SDK_DOWNLOAD_FILE osxcross/tarballs/
|
||||
|
||||
RUN UNATTENDED=yes SDK_VERSION=${OSX_SDK_VERSION} OSX_VERSION_MIN=10.10 osxcross/build.sh || exit 1;
|
||||
RUN cp osxcross/target/lib/* /usr/lib/ ; \
|
||||
mv osxcross/target $OSX_NDK_X86; \
|
||||
rm -rf osxcross;
|
||||
|
||||
ENV PATH $OSX_NDK_X86/bin:$PATH
|
||||
|
||||
RUN mkdir -p /root/.ssh; \
|
||||
chmod 0700 /root/.ssh; \
|
||||
ssh-keyscan github.com > /root/.ssh/known_hosts;
|
||||
|
||||
# Notes for self:
|
||||
|
||||
# To test locally:
|
||||
# make generate
|
||||
# make ui
|
||||
# cd docker/compiler
|
||||
# docker build . -t ghcr.io/stashapp/compiler:latest
|
||||
# docker run --rm -v /PATH_TO_STASH:/stash -w /stash -i -t ghcr.io/stashapp/compiler:latest make build-cc-all
|
||||
# # binaries will show up in /dist
|
||||
# make build
|
||||
# docker run -it -v /PATH_TO_STASH:/go/stash stashapp/compiler:latest /bin/bash
|
||||
# cd stash
|
||||
# make cross-compile-all
|
||||
# # binaries will show up in /dist
|
||||
|
||||
# Windows:
|
||||
# GOOS=windows GOARCH=amd64 CGO_ENABLED=1 CC=x86_64-w64-mingw32-gcc CXX=x86_64-w64-mingw32-g++ go build -ldflags "-extldflags '-static'" -tags extended
|
||||
|
||||
# Darwin
|
||||
# CC=o64-clang CXX=o64-clang++ GOOS=darwin GOARCH=amd64 CGO_ENABLED=1 go build -tags extended
|
||||
# env goreleaser --config=goreleaser-extended.yml --skip-publish --skip-validate --rm-dist --release-notes=temp/0.48-relnotes-ready.md
|
||||
|
||||
@@ -1,22 +1,16 @@
|
||||
host=ghcr.io
|
||||
user=stashapp
|
||||
repo=compiler
|
||||
version=14
|
||||
|
||||
VERSION_IMAGE = ${host}/${user}/${repo}:${version}
|
||||
LATEST_IMAGE = ${host}/${user}/${repo}:latest
|
||||
version=7
|
||||
|
||||
latest:
|
||||
docker build -t ${LATEST_IMAGE} .
|
||||
docker build -t ${user}/${repo}:latest .
|
||||
|
||||
build:
|
||||
docker build -t ${VERSION_IMAGE} -t ${LATEST_IMAGE} .
|
||||
docker build -t ${user}/${repo}:${version} -t ${user}/${repo}:latest .
|
||||
|
||||
build-no-cache:
|
||||
docker build --no-cache -t ${VERSION_IMAGE} -t ${LATEST_IMAGE} .
|
||||
docker build --no-cache -t ${user}/${repo}:${version} -t ${user}/${repo}:latest .
|
||||
|
||||
# requires docker login ghcr.io
|
||||
# echo $CR_PAT | docker login ghcr.io -u USERNAME --password-stdin
|
||||
push:
|
||||
docker push ${VERSION_IMAGE}
|
||||
docker push ${LATEST_IMAGE}
|
||||
install: build
|
||||
docker push ${user}/${repo}:${version}
|
||||
docker push ${user}/${repo}:latest
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
Modified from https://github.com/bep/dockerfiles/tree/master/ci-goreleaser
|
||||
|
||||
When the Dockerfile is changed, the version number should be incremented in [.github/workflows/build-compiler.yml](../../.github/workflows/build-compiler.yml) and the workflow [manually ran](). `env: COMPILER_IMAGE` in [.github/workflows/build.yml](../../.github/workflows/build.yml) also needs to be updated to pull the correct image tag.
|
||||
When the dockerfile is changed, the version number should be incremented in the Makefile and the new version tag should be pushed to docker hub. The `scripts/cross-compile.sh` script should also be updated to use the new version number tag, and the github workflow files need to be updated to pull the correct image tag.
|
||||
|
||||
@@ -1,27 +1,25 @@
|
||||
# Docker Installation (for most 64-bit GNU/Linux systems)
|
||||
StashApp is supported on most systems that support Docker. Your OS likely ships with or makes available the necessary packages.
|
||||
StashApp is supported on most systems that support Docker and docker-compose. Your OS likely ships with or makes available the necessary packages.
|
||||
|
||||
## Dependencies
|
||||
Only `docker` is required. For the most part your understanding of the technologies can be superficial. So long as you can follow commands and are open to reading a bit, you should be fine.
|
||||
Only `docker` and `docker-compose` are required. For the most part your understanding of the technologies can be superficial. So long as you can follow commands and are open to reading a bit, you should be fine.
|
||||
|
||||
Installation instructions are available below, and if your distributions's repository ships a current version of docker, you may use that.
|
||||
Installation instructions are available below, and if your distrobution's repository ships a current version of docker, you may use that.
|
||||
https://docs.docker.com/engine/install/
|
||||
|
||||
On some distributions, `docker compose` is shipped separately, usually as `docker-cli-compose`. docker-compose is not recommended.
|
||||
|
||||
### Get the docker-compose.yml file
|
||||
|
||||
Now you can either navigate to the [docker-compose.yml](https://raw.githubusercontent.com/stashapp/stash/develop/docker/production/docker-compose.yml) in the repository, or if you have curl, you can make your Linux console do it for you:
|
||||
Now you can either navigate to the [docker-compose.yml](https://raw.githubusercontent.com/stashapp/stash/master/docker/production/docker-compose.yml) in the repository, or if you have curl, you can make your Linux console do it for you:
|
||||
|
||||
```
|
||||
mkdir stashapp && cd stashapp
|
||||
curl -o docker-compose.yml https://raw.githubusercontent.com/stashapp/stash/develop/docker/production/docker-compose.yml
|
||||
curl -o docker-compose.yml https://raw.githubusercontent.com/stashapp/stash/master/docker/production/docker-compose.yml
|
||||
```
|
||||
|
||||
Once you have that file where you want it, modify the settings as you please, and then run:
|
||||
|
||||
```
|
||||
docker compose up -d
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
Installing StashApp this way will by default bind stash to port 9999. This is available in your web browser locally at http://localhost:9999 or on your network as http://YOUR-LOCAL-IP:9999
|
||||
@@ -31,9 +29,9 @@ Good luck and have fun!
|
||||
### Docker
|
||||
Docker is effectively a cross-platform software package repository. It allows you to ship an entire environment in what's referred to as a container. Containers are intended to hold everything that is needed to run an application from one place to another, making it easy for everyone along the way to reproduce the environment.
|
||||
|
||||
The StashApp docker container ships with everything you need to automatically run stash, including ffmpeg.
|
||||
The StashApp docker container ships with everything you need to automatically build and run stash, including ffmpeg.
|
||||
|
||||
### docker compose
|
||||
Docker Compose lets you specify how and where to run your containers, and to manage their environment. The docker-compose.yml file in this folder gets you a fully working instance of StashApp exactly as you would need it to have a reasonable instance for testing / developing on. If you are deploying a live instance for production, a [reverse proxy](https://docs.stashapp.cc/guides/reverse-proxy/) (such as NGINX or Traefik) is recommended, but not required.
|
||||
### docker-compose
|
||||
Docker Compose lets you specify how and where to run your containers, and to manage their environment. The docker-compose.yml file in this folder gets you a fully working instance of StashApp exactly as you would need it to have a reasonable instance for testing / developing on. If you are deploying a live instance for production, a reverse proxy (such as NGINX or Traefik) is recommended, but not required.
|
||||
|
||||
The latest version is always recommended.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# APPNICENAME=Stash
|
||||
# APPDESCRIPTION=An organizer for your porn, written in Go
|
||||
version: '3.4'
|
||||
services:
|
||||
stash:
|
||||
image: stashapp/stash:latest
|
||||
@@ -26,18 +27,14 @@ services:
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
## Adjust below paths (the left part) to your liking.
|
||||
## E.g. you can change ./config:/root/.stash to ./stash:/root/.stash
|
||||
## The left part is the path on your host, the right part is the path in the stash container.
|
||||
|
||||
|
||||
## Keep configs, scrapers, and plugins here.
|
||||
- ./config:/root/.stash
|
||||
## Point this at your collection.
|
||||
## The left side is where your collection is on your host, the right side is where it will be in stash.
|
||||
- ./data:/data
|
||||
## This is where your stash's metadata lives
|
||||
- ./metadata:/metadata
|
||||
## Any other cache content.
|
||||
- ./cache:/cache
|
||||
## Where to store binary blob data (scene covers, images)
|
||||
- ./blobs:/blobs
|
||||
## Where to store generated content (screenshots,previews,transcodes,sprites)
|
||||
- ./generated:/generated
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
# AI Usage Policy
|
||||
|
||||
- AI agents are not welcome to contribute to this project.
|
||||
- All issues, pull request descriptions and comments must be written by humans.
|
||||
- Fully AI-generated contributions will be closed without comment.
|
||||
|
||||
## AI-Assisted Code Contributions
|
||||
|
||||
AI-assisted code contributions generated with the use of LLMs are permitted under the following conditions:
|
||||
|
||||
- AI usage and scope must be openly disclosed in the PR description.
|
||||
- You must be able to explain any line of code and design decision during the review process.
|
||||
- You must perform manual testing and describe the steps taken to sufficiently verify the changes.
|
||||
- You must take full responsibility for the code, including license compliance.
|
||||
|
||||
We are not accepting large, complex features generated by LLMs by outside contributors at this time.
|
||||
|
||||
## Respect Maintainers Time
|
||||
|
||||
Reviewing PRs takes a significant amount of time. Anyone with zero effort can generate code with an LLM, but it takes a human to understand it, test it, and ensure it fits with the overall design of the project.
|
||||
|
||||
We ask that contributors respect this by ensuring their PRs meet these standards before submitting them for review.
|
||||
@@ -1,415 +0,0 @@
|
||||
# Architecture
|
||||
|
||||
This document provides an overview of the Stash codebase architecture for new contributors.
|
||||
|
||||
## Project Overview
|
||||
|
||||
Stash is a self-hosted web application written in Go that organizes and serves diverse media collections, catering to both SFW and NSFW needs. It gathers information about videos and images from the internet through extensible community-built plugins and scrapers, supports a wide variety of formats, enables tagging and filtering, and provides statistics about performers, tags, studios, and more.
|
||||
|
||||
**Core purpose**: Manage local media libraries with automatic metadata scraping, tagging, and organization.
|
||||
|
||||
**Key design philosophy**:
|
||||
- Backend: Go with GraphQL API and SQLite database
|
||||
- Frontend: React/TypeScript with Apollo Client
|
||||
- Extensibility: Plugin and scraper systems for community contributions
|
||||
- Self-hosted: Single binary deployment with embedded frontend assets
|
||||
|
||||
## Repository Structure
|
||||
|
||||
```
|
||||
stash/
|
||||
├── cmd/ # Application entry points
|
||||
│ ├── phasher/ # Perceptual hash utility
|
||||
│ └── stash/ # Main application (cmd/stash/main.go)
|
||||
├── docker/ # Docker configuration
|
||||
│ ├── build/ # Build configurations
|
||||
│ ├── ci/ # CI configurations
|
||||
│ ├── compiler/ # Compiler Docker setup
|
||||
│ └── production/ # Production Docker setup
|
||||
├── graphql/ # GraphQL schema definitions
|
||||
│ ├── schema/ # Main schema files
|
||||
│ │ └── types/ # GraphQL type definitions
|
||||
│ └── stash-box/ # Stash-box integration schema
|
||||
├── internal/ # Internal application code
|
||||
│ ├── api/ # GraphQL API layer (resolvers, server)
|
||||
│ ├── autotag/ # Auto-tagging functionality
|
||||
│ ├── desktop/ # Desktop integration
|
||||
│ ├── dlna/ # DLNA media server
|
||||
│ ├── identify/ # Scene identification
|
||||
│ ├── log/ # Implementation of log system
|
||||
│ ├── manager/ # Core application manager and services
|
||||
│ └── static/ # Static asset serving
|
||||
├── pkg/ # Reusable Go packages
|
||||
│ ├── ffmpeg/ # FFmpeg integration for media processing
|
||||
│ ├── file/ # File system operations and scanning
|
||||
│ ├── gallery/ # Gallery-specific business logic
|
||||
│ ├── group/ # Group (movie) business logic
|
||||
│ ├── hash/ # Hashing utilities (MD5, oshash, phash)
|
||||
│ ├── image/ # Image-specific business logic
|
||||
│ ├── job/ # Background job management
|
||||
│ ├── logger/ # Logging utilities
|
||||
│ ├── models/ # Interface definitions for data entities
|
||||
│ ├── performer/ # Performer-specific business logic
|
||||
│ ├── plugin/ # Plugin system
|
||||
│ ├── scene/ # Scene-specific business logic
|
||||
│ ├── scraper/ # Metadata scraping system
|
||||
│ ├── sqlite/ # SQLite implementations of datalayer interfaces
|
||||
│ ├── studio/ # Studio-specific business logic
|
||||
│ ├── tag/ # Tag-specific business logic
|
||||
│ └── ... # Other utility packages
|
||||
├── ui/ # React/TypeScript frontend
|
||||
│ ├── login/ # Login page
|
||||
│ └── v2.5/ # Main frontend application
|
||||
├── docs/ # Documentation
|
||||
├── scripts/ # Utility scripts
|
||||
├── go.mod # Go module definition
|
||||
├── go.sum # Go dependency checksums
|
||||
├── gqlgen.yml # GraphQL code generation config
|
||||
└── Makefile # Build automation
|
||||
```
|
||||
|
||||
## Backend Architecture
|
||||
|
||||
### Go Package Organization
|
||||
|
||||
The backend follows a layered architecture with clear separation of concerns:
|
||||
|
||||
**`pkg/models/` - Interface Layer**
|
||||
- Defines interfaces for each entity (Scene, Image, Gallery, Performer, Studio, Tag, etc.)
|
||||
- Each entity has `Reader`, `Writer`, and `ReaderWriter` interfaces
|
||||
- Contains data model structs and query/filter types
|
||||
- Example: `repository.go` defines the main `Repository` struct with all entity repositories
|
||||
- Example: `repository_scene.go` defines `SceneReaderWriter` interface
|
||||
|
||||
**`pkg/sqlite/` - Implementation of datalayer interfaces**
|
||||
- Implements the interfaces defined in `pkg/models/`
|
||||
- Uses `goqu` for CRUD operations and standard queries, and a custom query builder for complex filtering/listing
|
||||
- Contains all database access logic
|
||||
- Example: `scene.go` implements `SceneStore` with CRUD operations
|
||||
- Example: `scene_filter.go` implements filtering logic
|
||||
- Handles transactions and connection pooling
|
||||
|
||||
**`internal/api/` - API Layer**
|
||||
- GraphQL resolvers that implement the schema
|
||||
- Each resolver method calls repository methods
|
||||
- Handles authentication, authorization, and validation
|
||||
- Example: `resolver_query_find_scene.go` implements scene query resolvers
|
||||
- Example: `resolver_mutation_scene.go` implements scene mutation resolvers
|
||||
- `server.go` sets up the HTTP server and GraphQL handler
|
||||
|
||||
### Layering Pattern
|
||||
|
||||
```
|
||||
GraphQL Query/Mutation
|
||||
↓
|
||||
Resolver (internal/api/resolver_*.go)
|
||||
↓ (complex entities: Scene, Gallery, Image, Group)
|
||||
Service Layer (pkg/scene/, pkg/gallery/, pkg/image/, pkg/group/)
|
||||
↓ (simpler entities: Performer, Studio, Tag)
|
||||
Validation (pkg/performer/, pkg/studio/, pkg/tag/)
|
||||
↓
|
||||
Repository Interface (pkg/models/repository_*.go)
|
||||
↓
|
||||
SQLite Implementation (pkg/sqlite/*.go)
|
||||
↓
|
||||
SQLite Database
|
||||
```
|
||||
|
||||
Note: `gqlgen.yml` maps GraphQL types to Go structs and controls code generation. Update it when adding new types or fields.
|
||||
|
||||
### GraphQL Request Lifecycle
|
||||
|
||||
1. **Request**: Frontend sends GraphQL query to `/graphql` endpoint
|
||||
2. **Routing**: `internal/api/server.go` routes to GraphQL handler (gqlgen)
|
||||
3. **Parsing**: gqlgen parses the query and validates against schema
|
||||
4. **Resolver Execution**: Appropriate resolver method in `internal/api/` is called
|
||||
5. **Transaction**: Resolver wraps operation in read or write transaction via `withReadTxn()` or `withTxn()`
|
||||
6. **Business Logic** (mutations only):
|
||||
- Complex entities (Scene, Gallery, Image, Group): resolver delegates to service layer (`pkg/scene/`, `pkg/gallery/`, etc.)
|
||||
- Simpler entities (Performer, Studio, Tag): resolver calls validation functions (`pkg/performer/`, `pkg/studio/`, etc.) then proceeds directly to repository
|
||||
- Queries and model field resolvers skip this step entirely
|
||||
7. **Repository Call**: Resolver or service calls repository method (e.g., `r.repository.Scene.Find()`)
|
||||
8. **SQL Execution**: SQLite implementation executes SQL query using a mix of goqu and a custom query builder
|
||||
9. **Response**: Data flows back through layers to frontend as JSON
|
||||
|
||||
### Plugin System
|
||||
|
||||
**Location**: `pkg/plugin/`
|
||||
|
||||
- Defines the plugin spec for UI-based plugins (including JavaScript), and supports executing external scripts, commands, and binaries via raw or RPC interface
|
||||
- Plugins are configured via YAML files in the plugins directory
|
||||
- Support for hooks that trigger on events (e.g., `Scene.Create.Post`)
|
||||
- Plugin cache in manager for performance
|
||||
- RPC communication between Go and JavaScript plugins
|
||||
- Example hooks: `Scene.Create.Post`, `Scene.Update.Post`, `Scan.Post`
|
||||
|
||||
Key files:
|
||||
- `plugins.go` - Plugin loading and execution
|
||||
- `hooks.go` - Hook system implementation
|
||||
- `config.go` - Plugin configuration parsing
|
||||
|
||||
### Scraper System
|
||||
|
||||
**Location**: `pkg/scraper/`
|
||||
|
||||
- YAML-configured scrapers for fetching metadata from websites
|
||||
- Supports multiple scraper types: XPath, JSON, GraphQL, script-based
|
||||
- Scrapers can fetch performers, scenes, galleries, studios, tags
|
||||
- Stash-box integration for crowd-sourced metadata
|
||||
- Cache for scraper definitions
|
||||
- Post-processing for transforming scraped data
|
||||
|
||||
Key files:
|
||||
- `cache.go` - Scraper caching
|
||||
- `definition.go` - Scraper configuration parsing
|
||||
- `xpath.go` - XPath-based scraping
|
||||
- `json.go` - JSON-based scraping
|
||||
- `mapped.go` - Mapping scraped data to Stash models
|
||||
|
||||
### Task/Job System
|
||||
|
||||
**Location**: `pkg/job/`
|
||||
|
||||
- Background job management for long-running operations
|
||||
- Progress reporting via GraphQL subscriptions
|
||||
- Task queue with parallel execution
|
||||
- Cancellation support
|
||||
- Job types: Scan, Generate, Clean, Auto-tag, Identify, Export, Import
|
||||
|
||||
Key files:
|
||||
- `manager.go` - Job manager implementation
|
||||
- `job.go` - Job interface and progress tracking
|
||||
- `subscribe.go` - Subscription support for job updates
|
||||
|
||||
Task implementations in `internal/manager/task/`:
|
||||
- `task_scan.go` - File scanning
|
||||
- `task_generate.go` - Thumbnail/sprite generation
|
||||
- `task_clean.go` - Orphaned file cleanup
|
||||
- `task_autotag.go` - Automatic tagging
|
||||
- `task_identify.go` - Scene identification
|
||||
|
||||
## Frontend Architecture
|
||||
|
||||
### React/TypeScript Structure
|
||||
|
||||
**Location**: `ui/v2.5/`
|
||||
|
||||
```
|
||||
ui/v2.5/
|
||||
├── src/
|
||||
│ ├── core/ # Core services and GraphQL client
|
||||
│ │ ├── StashService.ts # Main GraphQL client
|
||||
│ │ ├── generated-graphql.ts # Auto-generated TypeScript types
|
||||
│ │ ├── createClient.ts # Apollo client setup
|
||||
│ │ ├── config.ts # Configuration
|
||||
│ │ ├── scenes.ts # Scene-specific queries
|
||||
│ │ ├── performers.ts # Performer-specific queries
|
||||
│ │ └── ...
|
||||
│ ├── components/ # React components
|
||||
│ │ ├── Scenes/ # Scene-related components
|
||||
│ │ ├── Performers/ # Performer-related components
|
||||
│ │ ├── Galleries/ # Gallery-related components
|
||||
│ │ ├── Images/ # Image-related components
|
||||
│ │ ├── Studios/ # Studio-related components
|
||||
│ │ ├── Tags/ # Tag-related components
|
||||
│ │ ├── Settings/ # Settings components
|
||||
│ │ ├── Shared/ # Shared/reusable components
|
||||
│ │ └── ...
|
||||
│ ├── hooks/ # Custom React hooks
|
||||
│ │ ├── data.ts # Data fetching hooks
|
||||
│ │ ├── LocalForage.ts # Local storage hooks
|
||||
│ │ ├── Toast.tsx # Toast notifications
|
||||
│ │ └── ...
|
||||
│ ├── models/ # Frontend data models
|
||||
│ │ └── list-filter/ # Filter and list models
|
||||
│ ├── locales/ # i18n translations
|
||||
│ │ ├── en-GB.json # English
|
||||
│ │ ├── de-DE.json # German
|
||||
│ │ └── ...
|
||||
│ ├── utils/ # Utility functions
|
||||
│ ├── App.tsx # Main application component
|
||||
│ └── index.tsx # Application entry point
|
||||
├── graphql/ # GraphQL queries and fragments
|
||||
├── public/ # Static assets
|
||||
├── package.json # Dependencies and scripts
|
||||
├── codegen.ts # GraphQL codegen configuration
|
||||
└── vite.config.js # Vite build configuration
|
||||
```
|
||||
|
||||
### Communication with Backend
|
||||
|
||||
**GraphQL via Apollo Client**:
|
||||
- Frontend uses Apollo Client (`@apollo/client`) for GraphQL communication
|
||||
- GraphQL queries defined in `graphql/` directory
|
||||
- Code generation via `@graphql-codegen/cli` generates TypeScript types
|
||||
- Generated types in `src/core/generated-graphql.ts`
|
||||
- WebSocket subscriptions for real-time updates (job progress, logging)
|
||||
|
||||
|
||||
**Service Layer** (`src/core/`):
|
||||
- `StashService.ts` - Main GraphQL client with typed queries/mutations
|
||||
- Domain-specific files (scenes.ts, performers.ts, etc.) - Organized queries
|
||||
- `createClient.ts` - Apollo client setup with authentication and uploads
|
||||
|
||||
## Database Layer
|
||||
|
||||
### SQLite Usage
|
||||
|
||||
**Database**: Single SQLite database file (default: `stash-go.sqlite`)
|
||||
- WAL (Write-Ahead Logging) mode for concurrency
|
||||
- Connection pooling: 1 write connection, 10 read connections
|
||||
- 30-second idle connection timeout
|
||||
- Configurable cache size via `STASH_SQLITE_CACHE_SIZE` environment variable
|
||||
|
||||
**Blob Storage**:
|
||||
- Configurable storage for cover images and other binary data
|
||||
- Options: Database (BLOB columns) or Filesystem (separate directory)
|
||||
- Managed via `BlobStore` in `pkg/sqlite/blob.go`
|
||||
|
||||
### Migration System
|
||||
|
||||
**Location**: `pkg/sqlite/migrations/`
|
||||
|
||||
**Migration Files**:
|
||||
- Numbered `.up.sql` files (e.g., `32_files.up.sql`)
|
||||
- Current schema version is defined in `pkg/sqlite/database.go`
|
||||
- Migrations embedded via `//go:embed migrations/*.sql`
|
||||
- Uses `golang-migrate/migrate` library
|
||||
|
||||
**Custom Migrations**:
|
||||
- Pre-migration Go files (e.g., `32_premigrate.go`) - Run before SQL
|
||||
- Post-migration Go files (e.g., `32_postmigrate.go`) - Run after SQL
|
||||
- Used for data transformations that SQL cannot handle
|
||||
|
||||
**Migration Process** (`pkg/sqlite/migrate.go`):
|
||||
- The migrator runs pre-migration Go code, executes the SQL migration, then runs post-migration Go code for each version increment.
|
||||
|
||||
**Key Migrations**:
|
||||
- `32_files.up.sql` - Introduced file/folder abstraction
|
||||
- `45_blobs.up.sql` - Blob storage system
|
||||
- `71_custom_fields.up.sql` - Custom fields support
|
||||
|
||||
### Query Patterns
|
||||
|
||||
**Repository Pattern**:
|
||||
- All database access goes through repository interfaces
|
||||
- SQLite implementations use a mix of goqu and a custom query builder within the `sqlite` package
|
||||
- Transactions managed via `txn.Manager`
|
||||
|
||||
**Example Query** (`pkg/sqlite/scene.go`):
|
||||
```go
|
||||
func (qb *SceneStore) Find(ctx context.Context, id int) (*models.Scene, error) {
|
||||
var scene models.Scene
|
||||
err := qb.repository.queryStruct(ctx, qb.sceneQuery(), []interface{}{id}, &scene)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &scene, nil
|
||||
}
|
||||
```
|
||||
|
||||
**Filtering**:
|
||||
- Complex filtering via a custom query builder system (`query.go`, `filter.go`) that constructs raw SQL
|
||||
- Criterion handlers in `criterion_handlers.go` dynamically build WHERE, HAVING, and WITH clauses
|
||||
- Supports hierarchical filters (tags, studios) via recursive CTEs
|
||||
- Simpler queries (CRUD, join-table lookups) use `goqu` via the `table` abstraction
|
||||
|
||||
## Key Data Flows
|
||||
|
||||
### Example 1: GraphQL Query (findScene)
|
||||
|
||||
**Flow**:
|
||||
1. Frontend sends GraphQL query requesting scene data by ID
|
||||
|
||||
2. Request hits `internal/api/server.go` at `/graphql` endpoint
|
||||
|
||||
3. gqlgen routes to the appropriate resolver in `resolver_query_find_scene.go`
|
||||
|
||||
4. Resolver wraps the operation in a read transaction using `withReadTxn()` to ensure consistent database access
|
||||
|
||||
5. Repository calls the SQLite implementation in `pkg/sqlite/scene.go` to execute the query
|
||||
|
||||
6. SQLite generates and executes the SQL query (using goqu or the custom queryBuilder depending on operation) to fetch the scene record
|
||||
|
||||
7. Scene object flows back through layers: SQLite → Repository → Resolver → GraphQL → Frontend
|
||||
|
||||
8. Frontend receives JSON response with the requested scene data
|
||||
|
||||
### Example 2: Scanning a File
|
||||
|
||||
**Flow**:
|
||||
1. User triggers scan via UI (Settings → Metadata → Scan)
|
||||
|
||||
2. Frontend sends GraphQL mutation to start the scan job
|
||||
|
||||
3. Mutation resolver in `internal/api/resolver_mutation_metadata.go` creates a background job
|
||||
|
||||
4. Job manager queues `ScanJob` from `internal/manager/task_scan.go`
|
||||
|
||||
5. `ScanJob.Execute()` runs the scan operation with progress tracking
|
||||
|
||||
6. Filesystem walk traverses configured paths using `file.SymWalk`, queues files for processing, and filters based on modification time and .stashignore
|
||||
|
||||
7. File handlers process each file type: videos become Scenes, images become Images, zip files become Galleries, and folders get Folder records
|
||||
|
||||
8. For each video file, the system calculates checksums (MD5, oshash, phash), extracts metadata via FFmpeg, creates File and Scene records, and generates thumbnails, sprites, previews, and interactive heatmaps
|
||||
|
||||
9. Progress updates flow via GraphQL subscription with real-time updates on files processed
|
||||
|
||||
10. Scan completes with updated statistics, subscription notifies completion, and UI refreshes with new content
|
||||
|
||||
**Key Files**:
|
||||
- `internal/manager/task_scan.go` - Main scan logic
|
||||
- `pkg/file/` - File system operations
|
||||
- `pkg/scene/scan.go` - Scene-specific scan logic
|
||||
- `pkg/image/scan.go` - Image-specific scan logic
|
||||
- `pkg/gallery/scan.go` - Gallery-specific scan logic
|
||||
|
||||
## Development Workflow
|
||||
|
||||
### Adding a New GraphQL Field
|
||||
|
||||
1. Define field in `graphql/schema/schema.graphql`
|
||||
2. Run `make generate-backend` to regenerate types
|
||||
3. Implement resolver in `internal/api/resolver_*.go`
|
||||
4. If query requires new repository method:
|
||||
- Add interface to `pkg/models/repository_*.go`
|
||||
- Implement in `pkg/sqlite/*.go`
|
||||
5. Add frontend query in `ui/v2.5/graphql/`
|
||||
6. Run `make generate-ui` to regenerate frontend types
|
||||
- Frontend type checking runs in CI — you do not need to run `tsc` locally.
|
||||
|
||||
### Adding a Database Migration
|
||||
|
||||
1. Create new migration file: `pkg/sqlite/migrations/{version}_description.up.sql`
|
||||
2. If needed, create `{version}_premigrate.go` for pre-migration logic
|
||||
3. If needed, create `{version}_postmigrate.go` for post-migration logic
|
||||
4. Update `appSchemaVersion` in `pkg/sqlite/database.go`
|
||||
5. Test migration on development database
|
||||
|
||||
### Running Tests
|
||||
|
||||
```bash
|
||||
# Backend test
|
||||
make it
|
||||
```
|
||||
|
||||
### Building
|
||||
|
||||
```bash
|
||||
# Build frontend
|
||||
make ui
|
||||
# Develop frontend with hot-reload
|
||||
make ui-start
|
||||
# Build backend (requires frontend to be built first)
|
||||
make build
|
||||
```
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- **Development Guide**: See `docs/DEVELOPMENT.md`
|
||||
- **Contributing**: See `docs/CONTRIBUTING.md`
|
||||
- **GraphQL Schema**: `graphql/schema/schema.graphql`
|
||||
- **In-app Manual**: Available in-app via Shift+?
|
||||
- **GraphQL Playground**: Available at `/playground`
|
||||
- **Community**: [Discord](https://discord.gg/2TsNFKt) and [Discourse](https://discourse.stashapp.cc)
|
||||
@@ -1,48 +1,14 @@
|
||||
# Contributing to Stash
|
||||
|
||||
## AI Usage Policy
|
||||
## Technical Debt
|
||||
Please be sure to consider how heavily your contribution impacts the maintainability of the project long term, sometimes less is more. We don't want to merge collossal pull requests with hundreds of dependencies by a driveby contributor.
|
||||
|
||||
Please see our [AI Usage Policy](/docs/AI_POLICY.md) for guidelines on the use of AI in contributions to this project.
|
||||
## Contributor Checklist
|
||||
Please make sure that you've considered the following before you submit your Pull Requests as ready for merging.
|
||||
* I've run Code linters and [gofmt](https://golang.org/cmd/gofmt/) to make sure that my code is readable.
|
||||
* I have read through formerly submitted [pull requests](https://github.com/stashapp/stash/pulls) and [git issues](https://github.com/stashapp/stash/issues) to make sure that this contribution is required and isn't a duplicate. Also, so that I can manage to close any git Issues needing closed relating to this feature submission.
|
||||
* I commented adequately on my code with the expectation in mind that anyone else should be able to look at this code I've submitted and know exactly what's happening and what the expectations are.
|
||||
|
||||
## Issues
|
||||
### Legal Agreements
|
||||
* I acknowledge that if applicable to me, submitting and subsequent acceptance of this Pull Request I, the code contributor of this Pull Request, agree and acknowledge my understanding that the new code license has now been updated to [AGPL](/LICENSE.md). I agree that all code before this Pull Request, which I've previously submitted, is now to be re-licensed under the new license AGPL and no longer the former MIT license.
|
||||
|
||||
Bug reports and feature requests must use descriptive and concise titles and follow the provided templates. Please use the search function to make sure that you are not submitting duplicates, and that a similar report or request has not already been resolved or rejected.
|
||||
|
||||
All issues must be written by humans. Fully AI-generated issues will be closed without comment.
|
||||
|
||||
## Pull Requests
|
||||
|
||||
All pull requests must use descriptive and concise titles and follow the provided templates. In addition, they must follow the the following guidelines:
|
||||
|
||||
- You must link to an open issue that pull request addresses (see [GitHub documentation](https://docs.github.com/en/issues/tracking-your-work-with-issues/using-issues/linking-a-pull-request-to-an-issue) on how to do that).
|
||||
- Pull requests must be focused on a single issue or feature. Large, multi-purpose pull requests will be rejected.
|
||||
- Large features must be discussed with maintainers before submitting a pull request to ensure it fits with the overall design vision of the project. Failure to do so may result in the pull request being rejected.
|
||||
- Pull requests must include code tests that sufficiently cover the changes made.
|
||||
- You must detail the manual testing done and describe the steps taken to sufficiently verify the changes.
|
||||
- You must be able to explain any line of code and design decision during the review process.
|
||||
|
||||
By submitting a pull request, you agree that you have read and understood and that you are in compliance with the guidelines outlined here, including the [AI Usage Policy](docs/AI_POLICY.md).
|
||||
|
||||
You also agree to license your contribution under the [AGPL](/LICENSE.md) license, and that all of your previous contributions to the project are also licensed under the AGPL.
|
||||
|
||||
## Goals and Design Vision
|
||||
|
||||
The goal of Stash is to be:
|
||||
- An application for organising and viewing NSFW and SFW content - currently this is videos and images, in future this will be extended to include audio and text content
|
||||
- Organising includes scraping of metadata from websites and metadata repositories
|
||||
- Free and open-source
|
||||
- Portable and offline - can be run on a USB stick without needing to install dependencies (with the exception of FFmpeg)
|
||||
- Minimal, but highly extensible. The core feature set should be the minimum required to achieve the primary goal, while being extensible enough to extend via plugins
|
||||
- Easy to learn and use, with minimal technical knowledge required
|
||||
|
||||
The core Stash system is not intended for:
|
||||
- Managing downloading of content
|
||||
- Managing content on external websites
|
||||
- Publicly sharing content
|
||||
|
||||
Other requirements:
|
||||
- Support as many video and image formats as possible
|
||||
- Interfaces with external systems (for example stash-box) should be made as generic as possible.
|
||||
|
||||
Design considerations:
|
||||
- Features are easy to add and difficult to remove. Large superfluous features should be scrutinised and avoided where possible (e.g. DLNA, filename parser). Such features should be considered for third-party plugins instead.
|
||||
**In case you were unable to follow any of the above include an explanation as to why not in your Pull Request.**
|
||||
|
||||
@@ -4,126 +4,57 @@
|
||||
|
||||
* [Go](https://golang.org/dl/)
|
||||
* [GolangCI](https://golangci-lint.run/) - A meta-linter which runs several linters in parallel
|
||||
* To install, follow the [local installation instructions](https://golangci-lint.run/welcome/install/#local-installation)
|
||||
* [nodejs](https://nodejs.org/en/download) - nodejs runtime
|
||||
* corepack/[pnpm](https://pnpm.io/installation) - nodejs package manager (included with nodejs)
|
||||
* To install, follow the [local installation instructions](https://golangci-lint.run/usage/install/#local-installation)
|
||||
* [Yarn](https://yarnpkg.com/en/docs/install) - Yarn package manager
|
||||
* Run `yarn install --frozen-lockfile` in the `stash/ui/v2.5` folder (before running make generate for first time).
|
||||
|
||||
NOTE: You may need to run the `go get` commands outside the project directory to avoid modifying the projects module file.
|
||||
|
||||
## Environment
|
||||
|
||||
### Windows
|
||||
|
||||
1. Download and install [Go for Windows](https://golang.org/dl/)
|
||||
2. Download and extract [MinGW64](https://sourceforge.net/projects/mingw-w64/files/) (scroll down and select x86_64-posix-seh, don't use the autoinstaller, it doesn't work)
|
||||
3. Search for "Advanced System Settings" and open the System Properties dialog.
|
||||
2. Download and extract [MingW64](https://sourceforge.net/projects/mingw-w64/files/) (scroll down and select x86_64-posix-seh, dont use the autoinstaller it doesnt work)
|
||||
3. Search for "advanced system settings" and open the system properties dialog.
|
||||
1. Click the `Environment Variables` button
|
||||
2. Under System Variables find `Path`. Edit and add `C:\MinGW\bin` (replace with the correct path to where you extracted MingW64).
|
||||
2. Under system variables find the `Path`. Edit and add `C:\MinGW\bin` (replace with the correct path to where you extracted MingW64).
|
||||
|
||||
NOTE: The `make` command in Windows will be `mingw32-make` with MinGW. For example, `make pre-ui` will be `mingw32-make pre-ui`.
|
||||
NOTE: The `make` command in Windows will be `mingw32-make` with MingW. For example `make pre-ui` will be `mingw32-make pre-ui`
|
||||
|
||||
### macOS
|
||||
|
||||
1. If you don't have it already, install the [Homebrew package manager](https://brew.sh).
|
||||
2. Install dependencies: `brew install go git gcc make node ffmpeg`
|
||||
|
||||
### Linux
|
||||
|
||||
#### Arch Linux
|
||||
|
||||
1. Install dependencies: `sudo pacman -S go git gcc make nodejs ffmpeg --needed`
|
||||
|
||||
#### Ubuntu
|
||||
|
||||
1. Install dependencies: `sudo apt-get install golang git gcc nodejs ffmpeg -y`
|
||||
|
||||
### OpenBSD
|
||||
|
||||
1. Install dependencies `doas pkg_add gmake go git node cmake ffmpeg`
|
||||
2. Follow the instructions below to build a release, but replace the final step `make build-release` with `gmake flags-release stash`, to [avoid the PIE buildmode](https://github.com/golang/go/issues/59866).
|
||||
|
||||
NOTE: The `make` command in OpenBSD will be `gmake`. For example, `make pre-ui` will be `gmake pre-ui`.
|
||||
2. Install dependencies: `brew install go git yarn gcc make`
|
||||
|
||||
## Commands
|
||||
|
||||
* `make pre-ui` - Installs the UI dependencies. This only needs to be run once after cloning the repository, or if the dependencies are updated.
|
||||
* `make generate` - Generates Go and UI GraphQL files. Requires `make pre-ui` to have been run.
|
||||
* `make generate-stash-box-client` - Generate Go files for the Stash-box client code.
|
||||
* `make ui` - Builds the UI. Requires `make pre-ui` to have been run.
|
||||
* `make stash` - Builds the `stash` binary (make sure to build the UI as well... see below)
|
||||
* `make stash-macapp` - Builds the `Stash.app` macOS app (only works when on macOS, for cross-compilation see below)
|
||||
* `make phasher` - Builds the `phasher` binary
|
||||
* `make build` - Builds both the `stash` and `phasher` binaries, alias for `make stash phasher`
|
||||
* `make build-release` - Builds release versions (debug information removed) of both the `stash` and `phasher` binaries, alias for `make flags-release flags-pie build`
|
||||
* `make docker-build` - Locally builds and tags a complete 'stash/build' docker image
|
||||
* `make docker-cuda-build` - Locally builds and tags a complete 'stash/cuda-build' docker image
|
||||
* `make validate` - Runs all of the tests and checks required to submit a PR
|
||||
* `make lint` - Runs `golangci-lint` on the backend
|
||||
* `make it` - Runs all unit and integration tests
|
||||
* `make fmt` - Formats the Go source code
|
||||
* `make pre-ui` - Installs the UI dependencies. Only needs to be run once before building the UI for the first time, or if the dependencies are updated
|
||||
* `make generate` - Generate Go and UI GraphQL files
|
||||
* `make fmt-ui` - Formats the UI source code
|
||||
* `make validate-ui` - Runs tests and checks for the UI only
|
||||
* `make fmt-ui-quick` - (experimental) Formats only changed UI source code
|
||||
* `make validate-ui-quick` - (experimental) Runs tests and checks of changed UI code
|
||||
* `make server-start` - Runs a development stash server in the `.local` directory
|
||||
* `make server-clean` - Removes the `.local` directory and all of its contents
|
||||
* `make ui-start` - Runs the UI in development mode. Requires a running Stash server to connect to - the server URL can be changed from the default of `http://localhost:9999` using the environment variable `VITE_APP_PLATFORM_URL`, but keep in mind that authentication cannot be used since the session authorization cookie cannot be sent cross-origin. The UI runs on port `3000` or the next available port.
|
||||
|
||||
When building, you can optionally prepend `flags-*` targets to the target list in your `make` command to use different build flags:
|
||||
|
||||
* `flags-release` (e.g. `make flags-release stash`) - Remove debug information from the binary.
|
||||
* `flags-pie` (e.g. `make flags-pie build`) - Build a PIE (Position Independent Executable) binary. This provides increased security, but it is unsupported on some systems (notably 32-bit ARM and OpenBSD).
|
||||
* `flags-static` (e.g. `make flags-static phasher`) - Build a statically linked binary (the default is a dynamically linked binary).
|
||||
* `flags-static-pie` (e.g. `make flags-static-pie stash`) - Build a statically linked PIE binary (using `flags-static` and `flags-pie` separately will not work).
|
||||
* `flags-static-windows` (e.g. `make flags-static-windows build`) - Identical to `flags-static-pie`, but does not enable the `netgo` build tag, which is not needed for static builds on Windows.
|
||||
|
||||
## Local development quickstart
|
||||
|
||||
1. Run `make pre-ui` to install UI dependencies
|
||||
2. Run `make generate` to create generated files
|
||||
3. In one terminal, run `make server-start` to run the server code
|
||||
4. In a separate terminal, run `make ui-start` to run the UI in development mode
|
||||
5. Open the UI in a browser: `http://localhost:3000/`
|
||||
|
||||
Changes to the UI code can be seen by reloading the browser page.
|
||||
|
||||
Changes to the backend code require a server restart (`CTRL-C` in the server terminal, followed by `make server-start` again) to be seen.
|
||||
|
||||
On first launch:
|
||||
|
||||
1. On the "Stash Setup Wizard" screen, choose a directory with some files to test with
|
||||
2. Press "Next" to use the default locations for the database and generated content
|
||||
3. Press the "Confirm" and "Finish" buttons to get into the UI
|
||||
4. On the side menu, navigate to "Tasks -> Library -> Scan" and press the "Scan" button
|
||||
5. You're all set! Set any other configurations you'd like and test your code changes.
|
||||
|
||||
To start fresh with new configuration:
|
||||
|
||||
1. Stop the server (`CTRL-C` in the server terminal)
|
||||
2. Run `make server-clean` to clear all config, database, and generated files (under `.local`)
|
||||
3. Run `make server-start` to restart the server
|
||||
4. Follow the "On first launch" steps above
|
||||
* `make ui` - Builds the frontend
|
||||
* `make build` - Builds the binary (make sure to build the UI as well... see below)
|
||||
* `make docker-build` - Locally builds and tags a complete 'stash/build' docker image
|
||||
* `make lint` - Run the linter on the backend
|
||||
* `make fmt` - Run `go fmt`
|
||||
* `make it` - Run the unit and integration tests
|
||||
* `make validate` - Run all of the tests and checks required to submit a PR
|
||||
* `make ui-start` - Runs the UI in development mode. Requires a running stash server to connect to. Stash server port can be changed from the default of `9999` using environment variable `VITE_APP_PLATFORM_PORT`. UI runs on port `3000` or the next available port.
|
||||
|
||||
## Building a release
|
||||
|
||||
Simply run `make` or `make release`, or equivalently:
|
||||
|
||||
1. Run `make pre-ui` to install UI dependencies
|
||||
2. Run `make generate` to create generated files
|
||||
3. Run `make ui` to build the frontend
|
||||
4. Run `make build-release` to build a release executable for your current platform
|
||||
3. Run `make ui` to compile the frontend
|
||||
4. Run `make build` to build the executable for your current platform
|
||||
|
||||
## Cross-compiling
|
||||
## Cross compiling
|
||||
|
||||
This project uses a modification of the [CI-GoReleaser](https://github.com/bep/dockerfiles/tree/master/ci-goreleaser) Docker container for cross-compilation, defined in `docker/compiler/Dockerfile`.
|
||||
This project uses a modification of the [CI-GoReleaser](https://github.com/bep/dockerfiles/tree/master/ci-goreleaser) docker container to create an environment
|
||||
where the app can be cross-compiled. This process is kicked off by CI via the `scripts/cross-compile.sh` script. Run the following
|
||||
command to open a bash shell to the container to poke around:
|
||||
|
||||
To cross-compile the app yourself:
|
||||
|
||||
1. Run `make pre-ui`, `make generate` and `make ui` outside the container, to generate files and build the UI.
|
||||
2. Pull the latest compiler image from GHCR: `docker pull ghcr.io/stashapp/compiler`
|
||||
3. Run `docker run --rm --mount type=bind,source="$(pwd)",target=/stash -w /stash -it ghcr.io/stashapp/compiler /bin/bash` to open a shell inside the container.
|
||||
4. From inside the container, run `make build-cc-all` to build for all platforms, or run `make build-cc-{platform}` to build for a specific platform (have a look at the `Makefile` for the list of targets).
|
||||
5. You will find the compiled binaries in `dist/`.
|
||||
|
||||
NOTE: Since the container is run as UID 0 (root), the resulting binaries (and the `dist/` folder itself, if it had to be created) will be owned by root.
|
||||
`docker run --rm --mount type=bind,source="$(pwd)",target=/stash -w /stash -i -t stashapp/compiler:latest /bin/bash`
|
||||
|
||||
## Profiling
|
||||
|
||||
|
||||
188
go.mod
188
go.mod
@@ -1,141 +1,111 @@
|
||||
module github.com/stashapp/stash
|
||||
|
||||
go 1.25.0
|
||||
|
||||
require (
|
||||
github.com/99designs/gqlgen v0.17.73
|
||||
github.com/WithoutPants/sortorder v0.0.0-20230616003020-921c9ef69552
|
||||
github.com/Yamashou/gqlgenc v0.32.1
|
||||
github.com/99designs/gqlgen v0.17.2
|
||||
github.com/Yamashou/gqlgenc v0.0.6
|
||||
github.com/anacrolix/dms v1.2.2
|
||||
github.com/antchfx/htmlquery v1.3.5
|
||||
github.com/asticode/go-astisub v0.25.1
|
||||
github.com/chromedp/cdproto v0.0.0-20250803210736-d308e07a266d
|
||||
github.com/chromedp/chromedp v0.14.2
|
||||
github.com/corona10/goimagehash v1.1.0
|
||||
github.com/disintegration/imaging v1.6.2
|
||||
github.com/dop251/goja v0.0.0-20231027120936-b396bb4c349d
|
||||
github.com/doug-martin/goqu/v9 v9.18.0
|
||||
github.com/enetx/g v1.0.224
|
||||
github.com/enetx/surf v1.0.199
|
||||
github.com/feederbox826/gosx-notifier v0.2.2
|
||||
github.com/go-chi/chi/v5 v5.2.2
|
||||
github.com/go-chi/cors v1.2.1
|
||||
github.com/go-chi/httplog v0.3.1
|
||||
github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4
|
||||
github.com/golang-jwt/jwt/v4 v4.5.2
|
||||
github.com/golang-migrate/migrate/v4 v4.16.2
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/antchfx/htmlquery v1.2.5-0.20211125074323-810ee8082758
|
||||
github.com/chromedp/cdproto v0.0.0-20210622022015-fe1827b46b84
|
||||
github.com/chromedp/chromedp v0.7.3
|
||||
github.com/corona10/goimagehash v1.0.3
|
||||
github.com/disintegration/imaging v1.6.0
|
||||
github.com/fvbommel/sortorder v1.0.2
|
||||
github.com/go-chi/chi v4.0.2+incompatible
|
||||
github.com/golang-jwt/jwt/v4 v4.0.0
|
||||
github.com/golang-migrate/migrate/v4 v4.15.0-beta.1
|
||||
github.com/gorilla/securecookie v1.1.1
|
||||
github.com/gorilla/sessions v1.2.1
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7
|
||||
github.com/hasura/go-graphql-client v0.13.1
|
||||
github.com/jinzhu/copier v0.4.0
|
||||
github.com/jmoiron/sqlx v1.4.0
|
||||
github.com/gorilla/sessions v1.2.0
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a
|
||||
github.com/jmoiron/sqlx v1.3.1
|
||||
github.com/json-iterator/go v1.1.12
|
||||
github.com/kermieisinthehouse/systray v1.2.4
|
||||
github.com/knadh/koanf/parsers/yaml v1.1.0
|
||||
github.com/knadh/koanf/providers/env v1.1.0
|
||||
github.com/knadh/koanf/providers/file v1.2.0
|
||||
github.com/knadh/koanf/providers/posflag v1.0.1
|
||||
github.com/knadh/koanf/v2 v2.2.1
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0
|
||||
github.com/mattn/go-sqlite3 v1.14.22
|
||||
github.com/mitchellh/mapstructure v1.5.0
|
||||
github.com/mattn/go-sqlite3 v1.14.7
|
||||
github.com/natefinch/pie v0.0.0-20170715172608-9a0d72014007
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8
|
||||
github.com/remeh/sizedwaitgroup v1.0.0
|
||||
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd
|
||||
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/spf13/cast v1.6.0
|
||||
github.com/spf13/pflag v1.0.6
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/tidwall/gjson v1.16.0
|
||||
github.com/vearutop/statigz v1.4.0
|
||||
github.com/vektah/dataloaden v0.3.0
|
||||
github.com/vektah/gqlparser/v2 v2.5.27
|
||||
github.com/robertkrimen/otto v0.0.0-20200922221731-ef014fd054ac
|
||||
github.com/rs/cors v1.6.0
|
||||
github.com/shurcooL/graphql v0.0.0-20181231061246-d48a9a75455f
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/spf13/afero v1.8.2 // indirect
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/spf13/viper v1.10.1
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/tidwall/gjson v1.9.3
|
||||
github.com/tidwall/pretty v1.2.0 // indirect
|
||||
github.com/vektra/mockery/v2 v2.10.0
|
||||
github.com/xWTF/chardet v0.0.0-20230208095535-c780f2ac244e
|
||||
github.com/zencoder/go-dash/v3 v3.0.2
|
||||
golang.org/x/crypto v0.48.0
|
||||
golang.org/x/image v0.38.0
|
||||
golang.org/x/net v0.50.0
|
||||
golang.org/x/sys v0.41.0
|
||||
golang.org/x/term v0.40.0
|
||||
golang.org/x/text v0.35.0
|
||||
golang.org/x/time v0.10.0
|
||||
gopkg.in/guregu/null.v4 v4.0.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||
golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064
|
||||
golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b
|
||||
golang.org/x/sys v0.0.0-20220808155132-1c4a2a72c664
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
|
||||
golang.org/x/text v0.3.7
|
||||
golang.org/x/tools v0.1.12 // indirect
|
||||
gopkg.in/sourcemap.v1 v1.0.5 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/agnivade/levenshtein v1.2.1 // indirect
|
||||
github.com/andybalholm/brotli v1.2.1 // indirect
|
||||
github.com/antchfx/xpath v1.3.6 // indirect
|
||||
github.com/asticode/go-astisub v0.20.0
|
||||
github.com/doug-martin/goqu/v9 v9.18.0
|
||||
github.com/go-chi/httplog v0.2.1
|
||||
github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4
|
||||
github.com/hashicorp/golang-lru v0.5.4
|
||||
github.com/kermieisinthehouse/gosx-notifier v0.1.1
|
||||
github.com/kermieisinthehouse/systray v1.2.4
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0
|
||||
github.com/spf13/cast v1.4.1
|
||||
github.com/vearutop/statigz v1.1.6
|
||||
github.com/vektah/dataloaden v0.3.0
|
||||
github.com/vektah/gqlparser/v2 v2.4.1
|
||||
gopkg.in/guregu/null.v4 v4.0.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/agnivade/levenshtein v1.1.1 // indirect
|
||||
github.com/antchfx/xpath v1.2.0 // indirect
|
||||
github.com/asticode/go-astikit v0.20.0 // indirect
|
||||
github.com/asticode/go-astits v1.8.0 // indirect
|
||||
github.com/chromedp/sysutil v1.1.0 // indirect
|
||||
github.com/coder/websocket v1.8.12 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
|
||||
github.com/chromedp/sysutil v1.0.0 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dlclark/regexp2 v1.7.0 // indirect
|
||||
github.com/enetx/http v1.0.28 // indirect
|
||||
github.com/enetx/http2 v1.0.26 // indirect
|
||||
github.com/enetx/http3 v1.0.7 // indirect
|
||||
github.com/enetx/iter v0.0.0-20250912135656-f1583323588f // indirect
|
||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||
github.com/go-json-experiment/json v0.0.0-20250725192818-e39067aee2d2 // indirect
|
||||
github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.5.1 // indirect
|
||||
github.com/go-chi/chi/v5 v5.0.0 // indirect
|
||||
github.com/gobwas/httphead v0.1.0 // indirect
|
||||
github.com/gobwas/pool v0.2.1 // indirect
|
||||
github.com/gobwas/ws v1.4.0 // indirect
|
||||
github.com/goccy/go-yaml v1.18.0 // indirect
|
||||
github.com/gobwas/ws v1.1.0-rc.5 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/hashicorp/errwrap v1.0.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.0 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/klauspost/compress v1.18.5 // indirect
|
||||
github.com/knadh/koanf/maps v0.1.2 // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/magiconair/properties v1.8.6 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/matryer/moq v0.2.6 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
github.com/mitchellh/mapstructure v1.4.3 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
|
||||
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
|
||||
github.com/pelletier/go-toml v1.9.4 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/quic-go/qpack v0.6.0 // indirect
|
||||
github.com/quic-go/quic-go v0.59.0 // indirect
|
||||
github.com/refraction-networking/utls v1.8.3-0.20260301010127-aa6edf4b11af // indirect
|
||||
github.com/rs/zerolog v1.30.0 // indirect
|
||||
github.com/rs/zerolog v1.26.1 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/sosodev/duration v1.3.1 // indirect
|
||||
github.com/spf13/afero v1.9.5 // indirect
|
||||
github.com/spf13/cobra v1.7.0 // indirect
|
||||
github.com/spf13/cobra v1.4.0 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/viper v1.16.0 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/stretchr/objx v0.2.0 // indirect
|
||||
github.com/subosito/gotenv v1.2.0 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.1 // indirect
|
||||
github.com/urfave/cli/v2 v2.27.6 // indirect
|
||||
github.com/wzshiming/socks5 v0.7.0 // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
|
||||
go.uber.org/atomic v1.11.0 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.3 // indirect
|
||||
golang.org/x/mod v0.33.0 // indirect
|
||||
golang.org/x/sync v0.20.0 // indirect
|
||||
golang.org/x/tools v0.42.0 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
github.com/urfave/cli/v2 v2.4.0 // indirect
|
||||
go.uber.org/atomic v1.7.0 // indirect
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
|
||||
gopkg.in/ini.v1 v1.66.4 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
)
|
||||
|
||||
replace git.apache.org/thrift.git => github.com/apache/thrift v0.0.0-20180902110319-2566ecd5d999
|
||||
|
||||
go 1.19
|
||||
|
||||
68
gqlgen.yml
68
gqlgen.yml
@@ -7,51 +7,33 @@ exec:
|
||||
filename: internal/api/generated_exec.go
|
||||
model:
|
||||
filename: internal/api/generated_models.go
|
||||
resolver:
|
||||
filename: internal/api/resolver.go
|
||||
type: Resolver
|
||||
|
||||
struct_tag: gqlgen
|
||||
|
||||
autobind:
|
||||
- github.com/stashapp/stash/internal/api
|
||||
- github.com/stashapp/stash/pkg/models
|
||||
- github.com/stashapp/stash/pkg/plugin
|
||||
- github.com/stashapp/stash/pkg/scraper
|
||||
- github.com/stashapp/stash/internal/identify
|
||||
- github.com/stashapp/stash/internal/dlna
|
||||
- github.com/stashapp/stash/pkg/stashbox
|
||||
- github.com/stashapp/stash/pkg/scraper/stashbox
|
||||
|
||||
models:
|
||||
# Scalars
|
||||
ID:
|
||||
model:
|
||||
- github.com/99designs/gqlgen/graphql.ID
|
||||
- github.com/99designs/gqlgen/graphql.IntID
|
||||
- github.com/stashapp/stash/pkg/models.FileID
|
||||
- github.com/stashapp/stash/pkg/models.FolderID
|
||||
Int64:
|
||||
model: github.com/99designs/gqlgen/graphql.Int64
|
||||
Timestamp:
|
||||
model: github.com/stashapp/stash/internal/api.Timestamp
|
||||
BoolMap:
|
||||
model: github.com/stashapp/stash/internal/api.BoolMap
|
||||
PluginConfigMap:
|
||||
model: github.com/stashapp/stash/internal/api.PluginConfigMap
|
||||
File:
|
||||
model: github.com/stashapp/stash/internal/api.File
|
||||
VideoFile:
|
||||
model: github.com/stashapp/stash/pkg/models.Timestamp
|
||||
Int64:
|
||||
model: github.com/stashapp/stash/pkg/models.Int64
|
||||
# define to force resolvers
|
||||
Image:
|
||||
model: github.com/stashapp/stash/pkg/models.Image
|
||||
fields:
|
||||
# override float fields - #1572
|
||||
duration:
|
||||
fieldName: DurationFinite
|
||||
frame_rate:
|
||||
fieldName: FrameRateFinite
|
||||
# movie is group under the hood
|
||||
Movie:
|
||||
model: github.com/stashapp/stash/pkg/models.Group
|
||||
MovieFilterType:
|
||||
model: github.com/stashapp/stash/pkg/models.GroupFilterType
|
||||
title:
|
||||
resolver: true
|
||||
# autobind on config causes generation issues
|
||||
BlobsStorageType:
|
||||
model: github.com/stashapp/stash/internal/manager/config.BlobsStorageType
|
||||
StashConfig:
|
||||
model: github.com/stashapp/stash/internal/manager/config.StashConfig
|
||||
StashConfigInput:
|
||||
@@ -68,10 +50,14 @@ models:
|
||||
model: github.com/stashapp/stash/internal/manager/config.ConfigDisableDropdownCreate
|
||||
ScanMetadataOptions:
|
||||
model: github.com/stashapp/stash/internal/manager/config.ScanMetadataOptions
|
||||
CleanGeneratedInput:
|
||||
model: github.com/stashapp/stash/internal/manager/task.CleanGeneratedOptions
|
||||
AutoTagMetadataOptions:
|
||||
model: github.com/stashapp/stash/internal/manager/config.AutoTagMetadataOptions
|
||||
SceneParserInput:
|
||||
model: github.com/stashapp/stash/internal/manager.SceneParserInput
|
||||
SceneParserResult:
|
||||
model: github.com/stashapp/stash/internal/manager.SceneParserResult
|
||||
SceneMovieID:
|
||||
model: github.com/stashapp/stash/internal/manager.SceneMovieID
|
||||
SystemStatus:
|
||||
model: github.com/stashapp/stash/internal/manager.SystemStatus
|
||||
SystemStatusEnum:
|
||||
@@ -92,8 +78,8 @@ models:
|
||||
model: github.com/stashapp/stash/internal/manager.AutoTagMetadataInput
|
||||
CleanMetadataInput:
|
||||
model: github.com/stashapp/stash/internal/manager.CleanMetadataInput
|
||||
StashBoxBatchTagInput:
|
||||
model: github.com/stashapp/stash/internal/manager.StashBoxBatchTagInput
|
||||
StashBoxBatchPerformerTagInput:
|
||||
model: github.com/stashapp/stash/internal/manager.StashBoxBatchPerformerTagInput
|
||||
SceneStreamEndpoint:
|
||||
model: github.com/stashapp/stash/internal/manager.SceneStreamEndpoint
|
||||
ExportObjectTypeInput:
|
||||
@@ -125,6 +111,9 @@ models:
|
||||
model: github.com/stashapp/stash/internal/identify.FieldStrategy
|
||||
ScraperSource:
|
||||
model: github.com/stashapp/stash/pkg/scraper.Source
|
||||
# rebind inputs to types
|
||||
StashIDInput:
|
||||
model: github.com/stashapp/stash/pkg/models.StashID
|
||||
IdentifySourceInput:
|
||||
model: github.com/stashapp/stash/internal/identify.Source
|
||||
IdentifyFieldOptionsInput:
|
||||
@@ -133,15 +122,4 @@ models:
|
||||
model: github.com/stashapp/stash/internal/identify.MetadataOptions
|
||||
ScraperSourceInput:
|
||||
model: github.com/stashapp/stash/pkg/scraper.Source
|
||||
SavedFindFilterType:
|
||||
model: github.com/stashapp/stash/pkg/models.FindFilterType
|
||||
# force resolvers
|
||||
ConfigResult:
|
||||
fields:
|
||||
plugins:
|
||||
resolver: true
|
||||
Performer:
|
||||
fields:
|
||||
career_length:
|
||||
resolver: true
|
||||
|
||||
|
||||
194
graphql/documents/data/config.graphql
Normal file
194
graphql/documents/data/config.graphql
Normal file
@@ -0,0 +1,194 @@
|
||||
fragment ConfigGeneralData on ConfigGeneralResult {
|
||||
stashes {
|
||||
path
|
||||
excludeVideo
|
||||
excludeImage
|
||||
}
|
||||
databasePath
|
||||
backupDirectoryPath
|
||||
generatedPath
|
||||
metadataPath
|
||||
scrapersPath
|
||||
cachePath
|
||||
calculateMD5
|
||||
videoFileNamingAlgorithm
|
||||
parallelTasks
|
||||
previewAudio
|
||||
previewSegments
|
||||
previewSegmentDuration
|
||||
previewExcludeStart
|
||||
previewExcludeEnd
|
||||
previewPreset
|
||||
maxTranscodeSize
|
||||
maxStreamingTranscodeSize
|
||||
writeImageThumbnails
|
||||
apiKey
|
||||
username
|
||||
password
|
||||
maxSessionAge
|
||||
logFile
|
||||
logOut
|
||||
logLevel
|
||||
logAccess
|
||||
createGalleriesFromFolders
|
||||
videoExtensions
|
||||
imageExtensions
|
||||
galleryExtensions
|
||||
excludes
|
||||
imageExcludes
|
||||
customPerformerImageLocation
|
||||
scraperUserAgent
|
||||
scraperCertCheck
|
||||
scraperCDPPath
|
||||
stashBoxes {
|
||||
name
|
||||
endpoint
|
||||
api_key
|
||||
}
|
||||
pythonPath
|
||||
}
|
||||
|
||||
fragment ConfigInterfaceData on ConfigInterfaceResult {
|
||||
menuItems
|
||||
soundOnPreview
|
||||
wallShowTitle
|
||||
wallPlayback
|
||||
showScrubber
|
||||
maximumLoopDuration
|
||||
noBrowser
|
||||
notificationsEnabled
|
||||
autostartVideo
|
||||
autostartVideoOnPlaySelected
|
||||
continuePlaylistDefault
|
||||
showStudioAsText
|
||||
css
|
||||
cssEnabled
|
||||
javascript
|
||||
javascriptEnabled
|
||||
customLocales
|
||||
customLocalesEnabled
|
||||
language
|
||||
imageLightbox {
|
||||
slideshowDelay
|
||||
displayMode
|
||||
scaleUp
|
||||
resetZoomOnNav
|
||||
scrollMode
|
||||
scrollAttemptsBeforeChange
|
||||
}
|
||||
disableDropdownCreate {
|
||||
performer
|
||||
tag
|
||||
studio
|
||||
}
|
||||
handyKey
|
||||
funscriptOffset
|
||||
}
|
||||
|
||||
fragment ConfigDLNAData on ConfigDLNAResult {
|
||||
serverName
|
||||
enabled
|
||||
whitelistedIPs
|
||||
interfaces
|
||||
}
|
||||
|
||||
fragment ConfigScrapingData on ConfigScrapingResult {
|
||||
scraperUserAgent
|
||||
scraperCertCheck
|
||||
scraperCDPPath
|
||||
excludeTagPatterns
|
||||
}
|
||||
|
||||
fragment IdentifyFieldOptionsData on IdentifyFieldOptions {
|
||||
field
|
||||
strategy
|
||||
createMissing
|
||||
}
|
||||
|
||||
fragment IdentifyMetadataOptionsData on IdentifyMetadataOptions {
|
||||
fieldOptions {
|
||||
...IdentifyFieldOptionsData
|
||||
}
|
||||
setCoverImage
|
||||
setOrganized
|
||||
includeMalePerformers
|
||||
}
|
||||
|
||||
fragment ScraperSourceData on ScraperSource {
|
||||
stash_box_index
|
||||
stash_box_endpoint
|
||||
scraper_id
|
||||
}
|
||||
|
||||
fragment ConfigDefaultSettingsData on ConfigDefaultSettingsResult {
|
||||
scan {
|
||||
useFileMetadata
|
||||
stripFileExtension
|
||||
scanGeneratePreviews
|
||||
scanGenerateImagePreviews
|
||||
scanGenerateSprites
|
||||
scanGeneratePhashes
|
||||
scanGenerateThumbnails
|
||||
}
|
||||
|
||||
identify {
|
||||
sources {
|
||||
source {
|
||||
...ScraperSourceData
|
||||
}
|
||||
options {
|
||||
...IdentifyMetadataOptionsData
|
||||
}
|
||||
}
|
||||
options {
|
||||
...IdentifyMetadataOptionsData
|
||||
}
|
||||
}
|
||||
|
||||
autoTag {
|
||||
performers
|
||||
studios
|
||||
tags
|
||||
}
|
||||
|
||||
generate {
|
||||
sprites
|
||||
previews
|
||||
imagePreviews
|
||||
previewOptions {
|
||||
previewSegments
|
||||
previewSegmentDuration
|
||||
previewExcludeStart
|
||||
previewExcludeEnd
|
||||
previewPreset
|
||||
}
|
||||
markers
|
||||
markerImagePreviews
|
||||
markerScreenshots
|
||||
transcodes
|
||||
phashes
|
||||
interactiveHeatmapsSpeeds
|
||||
}
|
||||
|
||||
deleteFile
|
||||
deleteGenerated
|
||||
}
|
||||
|
||||
fragment ConfigData on ConfigResult {
|
||||
general {
|
||||
...ConfigGeneralData
|
||||
}
|
||||
interface {
|
||||
...ConfigInterfaceData
|
||||
}
|
||||
dlna {
|
||||
...ConfigDLNAData
|
||||
}
|
||||
scraping {
|
||||
...ConfigScrapingData
|
||||
}
|
||||
defaults {
|
||||
...ConfigDefaultSettingsData
|
||||
}
|
||||
ui
|
||||
}
|
||||
43
graphql/documents/data/file.graphql
Normal file
43
graphql/documents/data/file.graphql
Normal file
@@ -0,0 +1,43 @@
|
||||
fragment FolderData on Folder {
|
||||
id
|
||||
path
|
||||
}
|
||||
|
||||
fragment VideoFileData on VideoFile {
|
||||
id
|
||||
path
|
||||
size
|
||||
duration
|
||||
video_codec
|
||||
audio_codec
|
||||
width
|
||||
height
|
||||
frame_rate
|
||||
bit_rate
|
||||
fingerprints {
|
||||
type
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
fragment ImageFileData on ImageFile {
|
||||
id
|
||||
path
|
||||
size
|
||||
width
|
||||
height
|
||||
fingerprints {
|
||||
type
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
fragment GalleryFileData on GalleryFile {
|
||||
id
|
||||
path
|
||||
size
|
||||
fingerprints {
|
||||
type
|
||||
value
|
||||
}
|
||||
}
|
||||
6
graphql/documents/data/filter.graphql
Normal file
6
graphql/documents/data/filter.graphql
Normal file
@@ -0,0 +1,6 @@
|
||||
fragment SavedFilterData on SavedFilter {
|
||||
id
|
||||
mode
|
||||
name
|
||||
filter
|
||||
}
|
||||
@@ -1,11 +1,9 @@
|
||||
fragment SlimGalleryData on Gallery {
|
||||
id
|
||||
title
|
||||
code
|
||||
date
|
||||
urls
|
||||
url
|
||||
details
|
||||
photographer
|
||||
rating100
|
||||
organized
|
||||
files {
|
||||
@@ -15,10 +13,14 @@ fragment SlimGalleryData on Gallery {
|
||||
...FolderData
|
||||
}
|
||||
image_count
|
||||
chapters {
|
||||
id
|
||||
title
|
||||
image_index
|
||||
cover {
|
||||
files {
|
||||
...ImageFileData
|
||||
}
|
||||
|
||||
paths {
|
||||
thumbnail
|
||||
}
|
||||
}
|
||||
studio {
|
||||
id
|
||||
@@ -39,8 +41,4 @@ fragment SlimGalleryData on Gallery {
|
||||
scenes {
|
||||
...SlimSceneData
|
||||
}
|
||||
paths {
|
||||
cover
|
||||
preview
|
||||
}
|
||||
}
|
||||
38
graphql/documents/data/gallery.graphql
Normal file
38
graphql/documents/data/gallery.graphql
Normal file
@@ -0,0 +1,38 @@
|
||||
fragment GalleryData on Gallery {
|
||||
id
|
||||
created_at
|
||||
updated_at
|
||||
title
|
||||
date
|
||||
url
|
||||
details
|
||||
rating100
|
||||
organized
|
||||
|
||||
files {
|
||||
...GalleryFileData
|
||||
}
|
||||
folder {
|
||||
...FolderData
|
||||
}
|
||||
|
||||
images {
|
||||
...SlimImageData
|
||||
}
|
||||
cover {
|
||||
...SlimImageData
|
||||
}
|
||||
studio {
|
||||
...SlimStudioData
|
||||
}
|
||||
tags {
|
||||
...SlimTagData
|
||||
}
|
||||
|
||||
performers {
|
||||
...PerformerData
|
||||
}
|
||||
scenes {
|
||||
...SlimSceneData
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,16 @@
|
||||
fragment SlimImageData on Image {
|
||||
id
|
||||
title
|
||||
code
|
||||
date
|
||||
urls
|
||||
details
|
||||
photographer
|
||||
rating100
|
||||
organized
|
||||
o_counter
|
||||
|
||||
files {
|
||||
...ImageFileData
|
||||
}
|
||||
|
||||
paths {
|
||||
thumbnail
|
||||
preview
|
||||
image
|
||||
}
|
||||
|
||||
@@ -45,8 +43,4 @@ fragment SlimImageData on Image {
|
||||
favorite
|
||||
image_path
|
||||
}
|
||||
|
||||
visual_files {
|
||||
...VisualFileData
|
||||
}
|
||||
}
|
||||
34
graphql/documents/data/image.graphql
Normal file
34
graphql/documents/data/image.graphql
Normal file
@@ -0,0 +1,34 @@
|
||||
fragment ImageData on Image {
|
||||
id
|
||||
title
|
||||
rating100
|
||||
organized
|
||||
o_counter
|
||||
created_at
|
||||
updated_at
|
||||
|
||||
files {
|
||||
...ImageFileData
|
||||
}
|
||||
|
||||
paths {
|
||||
thumbnail
|
||||
image
|
||||
}
|
||||
|
||||
galleries {
|
||||
...GalleryData
|
||||
}
|
||||
|
||||
studio {
|
||||
...SlimStudioData
|
||||
}
|
||||
|
||||
tags {
|
||||
...SlimTagData
|
||||
}
|
||||
|
||||
performers {
|
||||
...PerformerData
|
||||
}
|
||||
}
|
||||
10
graphql/documents/data/job.graphql
Normal file
10
graphql/documents/data/job.graphql
Normal file
@@ -0,0 +1,10 @@
|
||||
fragment JobData on Job {
|
||||
id
|
||||
status
|
||||
subTasks
|
||||
description
|
||||
progress
|
||||
startTime
|
||||
endTime
|
||||
addTime
|
||||
}
|
||||
6
graphql/documents/data/movie-slim.graphql
Normal file
6
graphql/documents/data/movie-slim.graphql
Normal file
@@ -0,0 +1,6 @@
|
||||
fragment SlimMovieData on Movie {
|
||||
id
|
||||
name
|
||||
front_image_path
|
||||
rating100
|
||||
}
|
||||
26
graphql/documents/data/movie.graphql
Normal file
26
graphql/documents/data/movie.graphql
Normal file
@@ -0,0 +1,26 @@
|
||||
fragment MovieData on Movie {
|
||||
id
|
||||
checksum
|
||||
name
|
||||
aliases
|
||||
duration
|
||||
date
|
||||
rating100
|
||||
director
|
||||
|
||||
studio {
|
||||
...SlimStudioData
|
||||
}
|
||||
|
||||
synopsis
|
||||
url
|
||||
front_image_path
|
||||
back_image_path
|
||||
scene_count
|
||||
|
||||
scenes {
|
||||
id
|
||||
title
|
||||
path
|
||||
}
|
||||
}
|
||||
32
graphql/documents/data/performer-slim.graphql
Normal file
32
graphql/documents/data/performer-slim.graphql
Normal file
@@ -0,0 +1,32 @@
|
||||
fragment SlimPerformerData on Performer {
|
||||
id
|
||||
name
|
||||
gender
|
||||
url
|
||||
twitter
|
||||
instagram
|
||||
image_path
|
||||
favorite
|
||||
ignore_auto_tag
|
||||
country
|
||||
birthdate
|
||||
ethnicity
|
||||
hair_color
|
||||
eye_color
|
||||
height_cm
|
||||
fake_tits
|
||||
career_length
|
||||
tattoos
|
||||
piercings
|
||||
tags {
|
||||
id
|
||||
name
|
||||
}
|
||||
stash_ids {
|
||||
endpoint
|
||||
stash_id
|
||||
}
|
||||
rating100
|
||||
death_date
|
||||
weight
|
||||
}
|
||||
41
graphql/documents/data/performer.graphql
Normal file
41
graphql/documents/data/performer.graphql
Normal file
@@ -0,0 +1,41 @@
|
||||
fragment PerformerData on Performer {
|
||||
id
|
||||
checksum
|
||||
name
|
||||
url
|
||||
gender
|
||||
twitter
|
||||
instagram
|
||||
birthdate
|
||||
ethnicity
|
||||
country
|
||||
eye_color
|
||||
height_cm
|
||||
measurements
|
||||
fake_tits
|
||||
career_length
|
||||
tattoos
|
||||
piercings
|
||||
aliases
|
||||
favorite
|
||||
ignore_auto_tag
|
||||
image_path
|
||||
scene_count
|
||||
image_count
|
||||
gallery_count
|
||||
movie_count
|
||||
|
||||
tags {
|
||||
...SlimTagData
|
||||
}
|
||||
|
||||
stash_ids {
|
||||
stash_id
|
||||
endpoint
|
||||
}
|
||||
rating100
|
||||
details
|
||||
death_date
|
||||
hair_color
|
||||
weight
|
||||
}
|
||||
24
graphql/documents/data/scene-marker.graphql
Normal file
24
graphql/documents/data/scene-marker.graphql
Normal file
@@ -0,0 +1,24 @@
|
||||
fragment SceneMarkerData on SceneMarker {
|
||||
id
|
||||
title
|
||||
seconds
|
||||
stream
|
||||
preview
|
||||
screenshot
|
||||
|
||||
scene {
|
||||
id
|
||||
}
|
||||
|
||||
primary_tag {
|
||||
id
|
||||
name
|
||||
aliases
|
||||
}
|
||||
|
||||
tags {
|
||||
id
|
||||
name
|
||||
aliases
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ fragment SlimSceneData on Scene {
|
||||
code
|
||||
details
|
||||
director
|
||||
urls
|
||||
url
|
||||
date
|
||||
rating100
|
||||
o_counter
|
||||
@@ -46,9 +46,6 @@ fragment SlimSceneData on Scene {
|
||||
files {
|
||||
path
|
||||
}
|
||||
folder {
|
||||
path
|
||||
}
|
||||
title
|
||||
}
|
||||
|
||||
@@ -58,8 +55,8 @@ fragment SlimSceneData on Scene {
|
||||
image_path
|
||||
}
|
||||
|
||||
groups {
|
||||
group {
|
||||
movies {
|
||||
movie {
|
||||
id
|
||||
name
|
||||
front_image_path
|
||||
@@ -75,7 +72,6 @@ fragment SlimSceneData on Scene {
|
||||
performers {
|
||||
id
|
||||
name
|
||||
disambiguation
|
||||
gender
|
||||
favorite
|
||||
image_path
|
||||
@@ -84,6 +80,5 @@ fragment SlimSceneData on Scene {
|
||||
stash_ids {
|
||||
endpoint
|
||||
stash_id
|
||||
updated_at
|
||||
}
|
||||
}
|
||||
78
graphql/documents/data/scene.graphql
Normal file
78
graphql/documents/data/scene.graphql
Normal file
@@ -0,0 +1,78 @@
|
||||
fragment SceneData on Scene {
|
||||
id
|
||||
title
|
||||
code
|
||||
details
|
||||
director
|
||||
url
|
||||
date
|
||||
rating100
|
||||
o_counter
|
||||
organized
|
||||
interactive
|
||||
interactive_speed
|
||||
captions {
|
||||
language_code
|
||||
caption_type
|
||||
}
|
||||
created_at
|
||||
updated_at
|
||||
resume_time
|
||||
last_played_at
|
||||
play_duration
|
||||
play_count
|
||||
|
||||
files {
|
||||
...VideoFileData
|
||||
}
|
||||
|
||||
paths {
|
||||
screenshot
|
||||
preview
|
||||
stream
|
||||
webp
|
||||
vtt
|
||||
sprite
|
||||
funscript
|
||||
interactive_heatmap
|
||||
caption
|
||||
}
|
||||
|
||||
scene_markers {
|
||||
...SceneMarkerData
|
||||
}
|
||||
|
||||
galleries {
|
||||
...SlimGalleryData
|
||||
}
|
||||
|
||||
studio {
|
||||
...SlimStudioData
|
||||
}
|
||||
|
||||
movies {
|
||||
movie {
|
||||
...MovieData
|
||||
}
|
||||
scene_index
|
||||
}
|
||||
|
||||
tags {
|
||||
...SlimTagData
|
||||
}
|
||||
|
||||
performers {
|
||||
...PerformerData
|
||||
}
|
||||
|
||||
stash_ids {
|
||||
endpoint
|
||||
stash_id
|
||||
}
|
||||
|
||||
sceneStreams {
|
||||
url
|
||||
mime_type
|
||||
label
|
||||
}
|
||||
}
|
||||
219
graphql/documents/data/scrapers.graphql
Normal file
219
graphql/documents/data/scrapers.graphql
Normal file
@@ -0,0 +1,219 @@
|
||||
fragment ScrapedPerformerData on ScrapedPerformer {
|
||||
stored_id
|
||||
name
|
||||
gender
|
||||
url
|
||||
twitter
|
||||
instagram
|
||||
birthdate
|
||||
ethnicity
|
||||
country
|
||||
eye_color
|
||||
height
|
||||
measurements
|
||||
fake_tits
|
||||
career_length
|
||||
tattoos
|
||||
piercings
|
||||
aliases
|
||||
tags {
|
||||
...ScrapedSceneTagData
|
||||
}
|
||||
images
|
||||
details
|
||||
death_date
|
||||
hair_color
|
||||
weight
|
||||
remote_site_id
|
||||
}
|
||||
|
||||
fragment ScrapedScenePerformerData on ScrapedPerformer {
|
||||
stored_id
|
||||
name
|
||||
gender
|
||||
url
|
||||
twitter
|
||||
instagram
|
||||
birthdate
|
||||
ethnicity
|
||||
country
|
||||
eye_color
|
||||
height
|
||||
measurements
|
||||
fake_tits
|
||||
career_length
|
||||
tattoos
|
||||
piercings
|
||||
aliases
|
||||
tags {
|
||||
...ScrapedSceneTagData
|
||||
}
|
||||
remote_site_id
|
||||
images
|
||||
details
|
||||
death_date
|
||||
hair_color
|
||||
weight
|
||||
}
|
||||
|
||||
fragment ScrapedMovieStudioData on ScrapedStudio {
|
||||
stored_id
|
||||
name
|
||||
url
|
||||
}
|
||||
|
||||
fragment ScrapedMovieData on ScrapedMovie {
|
||||
name
|
||||
aliases
|
||||
duration
|
||||
date
|
||||
rating
|
||||
director
|
||||
url
|
||||
synopsis
|
||||
front_image
|
||||
back_image
|
||||
|
||||
studio {
|
||||
...ScrapedMovieStudioData
|
||||
}
|
||||
}
|
||||
|
||||
fragment ScrapedSceneMovieData on ScrapedMovie {
|
||||
stored_id
|
||||
name
|
||||
aliases
|
||||
duration
|
||||
date
|
||||
rating
|
||||
director
|
||||
url
|
||||
synopsis
|
||||
}
|
||||
|
||||
fragment ScrapedSceneStudioData on ScrapedStudio {
|
||||
stored_id
|
||||
name
|
||||
url
|
||||
remote_site_id
|
||||
}
|
||||
|
||||
fragment ScrapedSceneTagData on ScrapedTag {
|
||||
stored_id
|
||||
name
|
||||
}
|
||||
|
||||
fragment ScrapedSceneData on ScrapedScene {
|
||||
title
|
||||
code
|
||||
details
|
||||
director
|
||||
url
|
||||
date
|
||||
image
|
||||
remote_site_id
|
||||
|
||||
file {
|
||||
size
|
||||
duration
|
||||
video_codec
|
||||
audio_codec
|
||||
width
|
||||
height
|
||||
framerate
|
||||
bitrate
|
||||
}
|
||||
|
||||
studio {
|
||||
...ScrapedSceneStudioData
|
||||
}
|
||||
|
||||
tags {
|
||||
...ScrapedSceneTagData
|
||||
}
|
||||
|
||||
performers {
|
||||
...ScrapedScenePerformerData
|
||||
}
|
||||
|
||||
movies {
|
||||
...ScrapedSceneMovieData
|
||||
}
|
||||
|
||||
fingerprints {
|
||||
hash
|
||||
algorithm
|
||||
duration
|
||||
}
|
||||
}
|
||||
|
||||
fragment ScrapedGalleryData on ScrapedGallery {
|
||||
title
|
||||
details
|
||||
url
|
||||
date
|
||||
|
||||
studio {
|
||||
...ScrapedSceneStudioData
|
||||
}
|
||||
|
||||
tags {
|
||||
...ScrapedSceneTagData
|
||||
}
|
||||
|
||||
performers {
|
||||
...ScrapedScenePerformerData
|
||||
}
|
||||
}
|
||||
|
||||
fragment ScrapedStashBoxSceneData on ScrapedScene {
|
||||
title
|
||||
code
|
||||
details
|
||||
director
|
||||
url
|
||||
date
|
||||
image
|
||||
remote_site_id
|
||||
duration
|
||||
|
||||
file {
|
||||
size
|
||||
duration
|
||||
video_codec
|
||||
audio_codec
|
||||
width
|
||||
height
|
||||
framerate
|
||||
bitrate
|
||||
}
|
||||
|
||||
fingerprints {
|
||||
hash
|
||||
algorithm
|
||||
duration
|
||||
}
|
||||
|
||||
studio {
|
||||
...ScrapedSceneStudioData
|
||||
}
|
||||
|
||||
tags {
|
||||
...ScrapedSceneTagData
|
||||
}
|
||||
|
||||
performers {
|
||||
...ScrapedScenePerformerData
|
||||
}
|
||||
|
||||
movies {
|
||||
...ScrapedSceneMovieData
|
||||
}
|
||||
}
|
||||
|
||||
fragment ScrapedStashBoxPerformerData on StashBoxPerformerQueryResult {
|
||||
query
|
||||
results {
|
||||
...ScrapedScenePerformerData
|
||||
}
|
||||
}
|
||||
15
graphql/documents/data/studio-slim.graphql
Normal file
15
graphql/documents/data/studio-slim.graphql
Normal file
@@ -0,0 +1,15 @@
|
||||
fragment SlimStudioData on Studio {
|
||||
id
|
||||
name
|
||||
image_path
|
||||
stash_ids {
|
||||
endpoint
|
||||
stash_id
|
||||
}
|
||||
parent_studio {
|
||||
id
|
||||
}
|
||||
details
|
||||
rating100
|
||||
aliases
|
||||
}
|
||||
30
graphql/documents/data/studio.graphql
Normal file
30
graphql/documents/data/studio.graphql
Normal file
@@ -0,0 +1,30 @@
|
||||
fragment StudioData on Studio {
|
||||
id
|
||||
checksum
|
||||
name
|
||||
url
|
||||
parent_studio {
|
||||
id
|
||||
name
|
||||
url
|
||||
image_path
|
||||
}
|
||||
child_studios {
|
||||
id
|
||||
name
|
||||
image_path
|
||||
}
|
||||
ignore_auto_tag
|
||||
image_path
|
||||
scene_count
|
||||
image_count
|
||||
gallery_count
|
||||
movie_count
|
||||
stash_ids {
|
||||
stash_id
|
||||
endpoint
|
||||
}
|
||||
details
|
||||
rating100
|
||||
aliases
|
||||
}
|
||||
6
graphql/documents/data/tag-slim.graphql
Normal file
6
graphql/documents/data/tag-slim.graphql
Normal file
@@ -0,0 +1,6 @@
|
||||
fragment SlimTagData on Tag {
|
||||
id
|
||||
name
|
||||
aliases
|
||||
image_path
|
||||
}
|
||||
21
graphql/documents/data/tag.graphql
Normal file
21
graphql/documents/data/tag.graphql
Normal file
@@ -0,0 +1,21 @@
|
||||
fragment TagData on Tag {
|
||||
id
|
||||
name
|
||||
description
|
||||
aliases
|
||||
ignore_auto_tag
|
||||
image_path
|
||||
scene_count
|
||||
scene_marker_count
|
||||
image_count
|
||||
gallery_count
|
||||
performer_count
|
||||
|
||||
parents {
|
||||
...SlimTagData
|
||||
}
|
||||
|
||||
children {
|
||||
...SlimTagData
|
||||
}
|
||||
}
|
||||
45
graphql/documents/mutations/config.graphql
Normal file
45
graphql/documents/mutations/config.graphql
Normal file
@@ -0,0 +1,45 @@
|
||||
mutation Setup($input: SetupInput!) {
|
||||
setup(input: $input)
|
||||
}
|
||||
|
||||
mutation Migrate($input: MigrateInput!) {
|
||||
migrate(input: $input)
|
||||
}
|
||||
|
||||
mutation ConfigureGeneral($input: ConfigGeneralInput!) {
|
||||
configureGeneral(input: $input) {
|
||||
...ConfigGeneralData
|
||||
}
|
||||
}
|
||||
|
||||
mutation ConfigureInterface($input: ConfigInterfaceInput!) {
|
||||
configureInterface(input: $input) {
|
||||
...ConfigInterfaceData
|
||||
}
|
||||
}
|
||||
|
||||
mutation ConfigureDLNA($input: ConfigDLNAInput!) {
|
||||
configureDLNA(input: $input) {
|
||||
...ConfigDLNAData
|
||||
}
|
||||
}
|
||||
|
||||
mutation ConfigureScraping($input: ConfigScrapingInput!) {
|
||||
configureScraping(input: $input) {
|
||||
...ConfigScrapingData
|
||||
}
|
||||
}
|
||||
|
||||
mutation ConfigureDefaults($input: ConfigDefaultSettingsInput!) {
|
||||
configureDefaults(input: $input) {
|
||||
...ConfigDefaultSettingsData
|
||||
}
|
||||
}
|
||||
|
||||
mutation ConfigureUI($input: Map!) {
|
||||
configureUI(input: $input)
|
||||
}
|
||||
|
||||
mutation GenerateAPIKey($input: GenerateAPIKeyInput!) {
|
||||
generateAPIKey(input: $input)
|
||||
}
|
||||
3
graphql/documents/mutations/file.graphql
Normal file
3
graphql/documents/mutations/file.graphql
Normal file
@@ -0,0 +1,3 @@
|
||||
mutation DeleteFiles($ids: [ID!]!) {
|
||||
deleteFiles(ids: $ids)
|
||||
}
|
||||
13
graphql/documents/mutations/filter.graphql
Normal file
13
graphql/documents/mutations/filter.graphql
Normal file
@@ -0,0 +1,13 @@
|
||||
mutation SaveFilter($input: SaveFilterInput!) {
|
||||
saveFilter(input: $input) {
|
||||
...SavedFilterData
|
||||
}
|
||||
}
|
||||
|
||||
mutation DestroySavedFilter($input: DestroyFilterInput!) {
|
||||
destroySavedFilter(input: $input)
|
||||
}
|
||||
|
||||
mutation SetDefaultFilter($input: SetDefaultFilterInput!) {
|
||||
setDefaultFilter(input: $input)
|
||||
}
|
||||
41
graphql/documents/mutations/gallery.graphql
Normal file
41
graphql/documents/mutations/gallery.graphql
Normal file
@@ -0,0 +1,41 @@
|
||||
mutation GalleryCreate(
|
||||
$input: GalleryCreateInput!) {
|
||||
|
||||
galleryCreate(input: $input) {
|
||||
...GalleryData
|
||||
}
|
||||
}
|
||||
|
||||
mutation GalleryUpdate(
|
||||
$input: GalleryUpdateInput!) {
|
||||
|
||||
galleryUpdate(input: $input) {
|
||||
...GalleryData
|
||||
}
|
||||
}
|
||||
|
||||
mutation BulkGalleryUpdate(
|
||||
$input: BulkGalleryUpdateInput!) {
|
||||
|
||||
bulkGalleryUpdate(input: $input) {
|
||||
...GalleryData
|
||||
}
|
||||
}
|
||||
|
||||
mutation GalleriesUpdate($input : [GalleryUpdateInput!]!) {
|
||||
galleriesUpdate(input: $input) {
|
||||
...GalleryData
|
||||
}
|
||||
}
|
||||
|
||||
mutation GalleryDestroy($ids: [ID!]!, $delete_file: Boolean, $delete_generated : Boolean) {
|
||||
galleryDestroy(input: {ids: $ids, delete_file: $delete_file, delete_generated: $delete_generated})
|
||||
}
|
||||
|
||||
mutation AddGalleryImages($gallery_id: ID!, $image_ids: [ID!]!) {
|
||||
addGalleryImages(input: {gallery_id: $gallery_id, image_ids: $image_ids})
|
||||
}
|
||||
|
||||
mutation RemoveGalleryImages($gallery_id: ID!, $image_ids: [ID!]!) {
|
||||
removeGalleryImages(input: {gallery_id: $gallery_id, image_ids: $image_ids})
|
||||
}
|
||||
41
graphql/documents/mutations/image.graphql
Normal file
41
graphql/documents/mutations/image.graphql
Normal file
@@ -0,0 +1,41 @@
|
||||
mutation ImageUpdate(
|
||||
$input: ImageUpdateInput!) {
|
||||
|
||||
imageUpdate(input: $input) {
|
||||
...SlimImageData
|
||||
}
|
||||
}
|
||||
|
||||
mutation BulkImageUpdate(
|
||||
$input: BulkImageUpdateInput!) {
|
||||
|
||||
bulkImageUpdate(input: $input) {
|
||||
...SlimImageData
|
||||
}
|
||||
}
|
||||
|
||||
mutation ImagesUpdate($input : [ImageUpdateInput!]!) {
|
||||
imagesUpdate(input: $input) {
|
||||
...SlimImageData
|
||||
}
|
||||
}
|
||||
|
||||
mutation ImageIncrementO($id: ID!) {
|
||||
imageIncrementO(id: $id)
|
||||
}
|
||||
|
||||
mutation ImageDecrementO($id: ID!) {
|
||||
imageDecrementO(id: $id)
|
||||
}
|
||||
|
||||
mutation ImageResetO($id: ID!) {
|
||||
imageResetO(id: $id)
|
||||
}
|
||||
|
||||
mutation ImageDestroy($id: ID!, $delete_file: Boolean, $delete_generated : Boolean) {
|
||||
imageDestroy(input: {id: $id, delete_file: $delete_file, delete_generated: $delete_generated})
|
||||
}
|
||||
|
||||
mutation ImagesDestroy($ids: [ID!]!, $delete_file: Boolean, $delete_generated : Boolean) {
|
||||
imagesDestroy(input: {ids: $ids, delete_file: $delete_file, delete_generated: $delete_generated})
|
||||
}
|
||||
7
graphql/documents/mutations/job.graphql
Normal file
7
graphql/documents/mutations/job.graphql
Normal file
@@ -0,0 +1,7 @@
|
||||
mutation StopJob($job_id: ID!) {
|
||||
stopJob(job_id: $job_id)
|
||||
}
|
||||
|
||||
mutation StopAllJobs {
|
||||
stopAllJobs
|
||||
}
|
||||
43
graphql/documents/mutations/metadata.graphql
Normal file
43
graphql/documents/mutations/metadata.graphql
Normal file
@@ -0,0 +1,43 @@
|
||||
mutation MetadataImport {
|
||||
metadataImport
|
||||
}
|
||||
|
||||
mutation MetadataExport {
|
||||
metadataExport
|
||||
}
|
||||
|
||||
mutation ExportObjects($input: ExportObjectsInput!) {
|
||||
exportObjects(input: $input)
|
||||
}
|
||||
|
||||
mutation ImportObjects($input: ImportObjectsInput!) {
|
||||
importObjects(input: $input)
|
||||
}
|
||||
|
||||
mutation MetadataScan($input: ScanMetadataInput!) {
|
||||
metadataScan(input: $input)
|
||||
}
|
||||
|
||||
mutation MetadataGenerate($input: GenerateMetadataInput!) {
|
||||
metadataGenerate(input: $input)
|
||||
}
|
||||
|
||||
mutation MetadataAutoTag($input: AutoTagMetadataInput!) {
|
||||
metadataAutoTag(input: $input)
|
||||
}
|
||||
|
||||
mutation MetadataIdentify($input: IdentifyMetadataInput!) {
|
||||
metadataIdentify(input: $input)
|
||||
}
|
||||
|
||||
mutation MetadataClean($input: CleanMetadataInput!) {
|
||||
metadataClean(input: $input)
|
||||
}
|
||||
|
||||
mutation MigrateHashNaming {
|
||||
migrateHashNaming
|
||||
}
|
||||
|
||||
mutation BackupDatabase($input: BackupDatabaseInput!) {
|
||||
backupDatabase(input: $input)
|
||||
}
|
||||
37
graphql/documents/mutations/movie.graphql
Normal file
37
graphql/documents/mutations/movie.graphql
Normal file
@@ -0,0 +1,37 @@
|
||||
mutation MovieCreate(
|
||||
$name: String!,
|
||||
$aliases: String,
|
||||
$duration: Int,
|
||||
$date: String,
|
||||
$rating: Int,
|
||||
$studio_id: ID,
|
||||
$director: String,
|
||||
$synopsis: String,
|
||||
$url: String,
|
||||
$front_image: String,
|
||||
$back_image: String) {
|
||||
|
||||
movieCreate(input: { name: $name, aliases: $aliases, duration: $duration, date: $date, rating: $rating, studio_id: $studio_id, director: $director, synopsis: $synopsis, url: $url, front_image: $front_image, back_image: $back_image }) {
|
||||
...MovieData
|
||||
}
|
||||
}
|
||||
|
||||
mutation MovieUpdate($input: MovieUpdateInput!) {
|
||||
movieUpdate(input: $input) {
|
||||
...MovieData
|
||||
}
|
||||
}
|
||||
|
||||
mutation BulkMovieUpdate($input: BulkMovieUpdateInput!) {
|
||||
bulkMovieUpdate(input: $input) {
|
||||
...MovieData
|
||||
}
|
||||
}
|
||||
|
||||
mutation MovieDestroy($id: ID!) {
|
||||
movieDestroy(input: { id: $id })
|
||||
}
|
||||
|
||||
mutation MoviesDestroy($ids: [ID!]!) {
|
||||
moviesDestroy(ids: $ids)
|
||||
}
|
||||
31
graphql/documents/mutations/performer.graphql
Normal file
31
graphql/documents/mutations/performer.graphql
Normal file
@@ -0,0 +1,31 @@
|
||||
mutation PerformerCreate(
|
||||
$input: PerformerCreateInput!) {
|
||||
|
||||
performerCreate(input: $input) {
|
||||
...PerformerData
|
||||
}
|
||||
}
|
||||
|
||||
mutation PerformerUpdate(
|
||||
$input: PerformerUpdateInput!) {
|
||||
|
||||
performerUpdate(input: $input) {
|
||||
...PerformerData
|
||||
}
|
||||
}
|
||||
|
||||
mutation BulkPerformerUpdate(
|
||||
$input: BulkPerformerUpdateInput!) {
|
||||
|
||||
bulkPerformerUpdate(input: $input) {
|
||||
...PerformerData
|
||||
}
|
||||
}
|
||||
|
||||
mutation PerformerDestroy($id: ID!) {
|
||||
performerDestroy(input: { id: $id })
|
||||
}
|
||||
|
||||
mutation PerformersDestroy($ids: [ID!]!) {
|
||||
performersDestroy(ids: $ids)
|
||||
}
|
||||
7
graphql/documents/mutations/plugins.graphql
Normal file
7
graphql/documents/mutations/plugins.graphql
Normal file
@@ -0,0 +1,7 @@
|
||||
mutation ReloadPlugins {
|
||||
reloadPlugins
|
||||
}
|
||||
|
||||
mutation RunPluginTask($plugin_id: ID!, $task_name: String!, $args: [PluginArgInput!]) {
|
||||
runPluginTask(plugin_id: $plugin_id, task_name: $task_name, args: $args)
|
||||
}
|
||||
41
graphql/documents/mutations/scene-marker.graphql
Normal file
41
graphql/documents/mutations/scene-marker.graphql
Normal file
@@ -0,0 +1,41 @@
|
||||
mutation SceneMarkerCreate(
|
||||
$title: String!,
|
||||
$seconds: Float!,
|
||||
$scene_id: ID!,
|
||||
$primary_tag_id: ID!,
|
||||
$tag_ids: [ID!] = []) {
|
||||
|
||||
sceneMarkerCreate(input: {
|
||||
title: $title,
|
||||
seconds: $seconds,
|
||||
scene_id: $scene_id,
|
||||
primary_tag_id: $primary_tag_id,
|
||||
tag_ids: $tag_ids
|
||||
}) {
|
||||
...SceneMarkerData
|
||||
}
|
||||
}
|
||||
|
||||
mutation SceneMarkerUpdate(
|
||||
$id: ID!,
|
||||
$title: String!,
|
||||
$seconds: Float!,
|
||||
$scene_id: ID!,
|
||||
$primary_tag_id: ID!,
|
||||
$tag_ids: [ID!] = []) {
|
||||
|
||||
sceneMarkerUpdate(input: {
|
||||
id: $id,
|
||||
title: $title,
|
||||
seconds: $seconds,
|
||||
scene_id: $scene_id,
|
||||
primary_tag_id: $primary_tag_id,
|
||||
tag_ids: $tag_ids
|
||||
}) {
|
||||
...SceneMarkerData
|
||||
}
|
||||
}
|
||||
|
||||
mutation SceneMarkerDestroy($id: ID!) {
|
||||
sceneMarkerDestroy(id: $id)
|
||||
}
|
||||
71
graphql/documents/mutations/scene.graphql
Normal file
71
graphql/documents/mutations/scene.graphql
Normal file
@@ -0,0 +1,71 @@
|
||||
mutation SceneCreate(
|
||||
$input: SceneCreateInput!) {
|
||||
|
||||
sceneCreate(input: $input) {
|
||||
...SceneData
|
||||
}
|
||||
}
|
||||
|
||||
mutation SceneUpdate(
|
||||
$input: SceneUpdateInput!) {
|
||||
|
||||
sceneUpdate(input: $input) {
|
||||
...SceneData
|
||||
}
|
||||
}
|
||||
|
||||
mutation BulkSceneUpdate(
|
||||
$input: BulkSceneUpdateInput!) {
|
||||
|
||||
bulkSceneUpdate(input: $input) {
|
||||
...SceneData
|
||||
}
|
||||
}
|
||||
|
||||
mutation ScenesUpdate($input : [SceneUpdateInput!]!) {
|
||||
scenesUpdate(input: $input) {
|
||||
...SceneData
|
||||
}
|
||||
}
|
||||
|
||||
mutation SceneSaveActivity($id: ID!, $resume_time: Float, $playDuration: Float) {
|
||||
sceneSaveActivity(id: $id, resume_time: $resume_time, playDuration: $playDuration)
|
||||
}
|
||||
|
||||
mutation SceneIncrementPlayCount($id: ID!) {
|
||||
sceneIncrementPlayCount(id: $id)
|
||||
}
|
||||
|
||||
mutation SceneIncrementO($id: ID!) {
|
||||
sceneIncrementO(id: $id)
|
||||
}
|
||||
|
||||
mutation SceneDecrementO($id: ID!) {
|
||||
sceneDecrementO(id: $id)
|
||||
}
|
||||
|
||||
mutation SceneResetO($id: ID!) {
|
||||
sceneResetO(id: $id)
|
||||
}
|
||||
|
||||
mutation SceneDestroy($id: ID!, $delete_file: Boolean, $delete_generated : Boolean) {
|
||||
sceneDestroy(input: {id: $id, delete_file: $delete_file, delete_generated: $delete_generated})
|
||||
}
|
||||
|
||||
mutation ScenesDestroy($ids: [ID!]!, $delete_file: Boolean, $delete_generated : Boolean) {
|
||||
scenesDestroy(input: {ids: $ids, delete_file: $delete_file, delete_generated: $delete_generated})
|
||||
}
|
||||
|
||||
mutation SceneGenerateScreenshot($id: ID!, $at: Float) {
|
||||
sceneGenerateScreenshot(id: $id, at: $at)
|
||||
}
|
||||
|
||||
mutation SceneAssignFile($input: AssignSceneFileInput!) {
|
||||
sceneAssignFile(input: $input)
|
||||
}
|
||||
|
||||
mutation SceneMerge($input: SceneMergeInput!) {
|
||||
sceneMerge(input: $input) {
|
||||
id
|
||||
}
|
||||
}
|
||||
3
graphql/documents/mutations/scrapers.graphql
Normal file
3
graphql/documents/mutations/scrapers.graphql
Normal file
@@ -0,0 +1,3 @@
|
||||
mutation ReloadScrapers {
|
||||
reloadScrapers
|
||||
}
|
||||
15
graphql/documents/mutations/stash-box.graphql
Normal file
15
graphql/documents/mutations/stash-box.graphql
Normal file
@@ -0,0 +1,15 @@
|
||||
mutation SubmitStashBoxFingerprints($input: StashBoxFingerprintSubmissionInput!) {
|
||||
submitStashBoxFingerprints(input: $input)
|
||||
}
|
||||
|
||||
mutation StashBoxBatchPerformerTag($input: StashBoxBatchPerformerTagInput!) {
|
||||
stashBoxBatchPerformerTag(input: $input)
|
||||
}
|
||||
|
||||
mutation SubmitStashBoxSceneDraft($input: StashBoxDraftSubmissionInput!) {
|
||||
submitStashBoxSceneDraft(input: $input)
|
||||
}
|
||||
|
||||
mutation SubmitStashBoxPerformerDraft($input: StashBoxDraftSubmissionInput!) {
|
||||
submitStashBoxPerformerDraft(input: $input)
|
||||
}
|
||||
19
graphql/documents/mutations/studio.graphql
Normal file
19
graphql/documents/mutations/studio.graphql
Normal file
@@ -0,0 +1,19 @@
|
||||
mutation StudioCreate($input: StudioCreateInput!) {
|
||||
studioCreate(input: $input) {
|
||||
...StudioData
|
||||
}
|
||||
}
|
||||
|
||||
mutation StudioUpdate($input: StudioUpdateInput!) {
|
||||
studioUpdate(input: $input) {
|
||||
...StudioData
|
||||
}
|
||||
}
|
||||
|
||||
mutation StudioDestroy($id: ID!) {
|
||||
studioDestroy(input: { id: $id })
|
||||
}
|
||||
|
||||
mutation StudiosDestroy($ids: [ID!]!) {
|
||||
studiosDestroy(ids: $ids)
|
||||
}
|
||||
25
graphql/documents/mutations/tag.graphql
Normal file
25
graphql/documents/mutations/tag.graphql
Normal file
@@ -0,0 +1,25 @@
|
||||
mutation TagCreate($input: TagCreateInput!) {
|
||||
tagCreate(input: $input) {
|
||||
...TagData
|
||||
}
|
||||
}
|
||||
|
||||
mutation TagDestroy($id: ID!) {
|
||||
tagDestroy(input: { id: $id })
|
||||
}
|
||||
|
||||
mutation TagsDestroy($ids: [ID!]!) {
|
||||
tagsDestroy(ids: $ids)
|
||||
}
|
||||
|
||||
mutation TagUpdate($input: TagUpdateInput!) {
|
||||
tagUpdate(input: $input) {
|
||||
...TagData
|
||||
}
|
||||
}
|
||||
|
||||
mutation TagsMerge($source: [ID!]!, $destination: ID!) {
|
||||
tagsMerge(input: { source: $source, destination: $destination }) {
|
||||
...TagData
|
||||
}
|
||||
}
|
||||
@@ -8,4 +8,4 @@ query DLNAStatus {
|
||||
until
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
17
graphql/documents/queries/filter.graphql
Normal file
17
graphql/documents/queries/filter.graphql
Normal file
@@ -0,0 +1,17 @@
|
||||
query FindSavedFilter($id: ID!) {
|
||||
findSavedFilter(id: $id) {
|
||||
...SavedFilterData
|
||||
}
|
||||
}
|
||||
|
||||
query FindSavedFilters($mode: FilterMode) {
|
||||
findSavedFilters(mode: $mode) {
|
||||
...SavedFilterData
|
||||
}
|
||||
}
|
||||
|
||||
query FindDefaultFilter($mode: FilterMode!) {
|
||||
findDefaultFilter(mode: $mode) {
|
||||
...SavedFilterData
|
||||
}
|
||||
}
|
||||
14
graphql/documents/queries/gallery.graphql
Normal file
14
graphql/documents/queries/gallery.graphql
Normal file
@@ -0,0 +1,14 @@
|
||||
query FindGalleries($filter: FindFilterType, $gallery_filter: GalleryFilterType) {
|
||||
findGalleries(gallery_filter: $gallery_filter, filter: $filter) {
|
||||
count
|
||||
galleries {
|
||||
...SlimGalleryData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
query FindGallery($id: ID!) {
|
||||
findGallery(id: $id) {
|
||||
...GalleryData
|
||||
}
|
||||
}
|
||||
16
graphql/documents/queries/image.graphql
Normal file
16
graphql/documents/queries/image.graphql
Normal file
@@ -0,0 +1,16 @@
|
||||
query FindImages($filter: FindFilterType, $image_filter: ImageFilterType, $image_ids: [Int!]) {
|
||||
findImages(filter: $filter, image_filter: $image_filter, image_ids: $image_ids) {
|
||||
count
|
||||
megapixels
|
||||
filesize
|
||||
images {
|
||||
...SlimImageData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
query FindImage($id: ID!, $checksum: String) {
|
||||
findImage(id: $id, checksum: $checksum) {
|
||||
...ImageData
|
||||
}
|
||||
}
|
||||
11
graphql/documents/queries/job.graphql
Normal file
11
graphql/documents/queries/job.graphql
Normal file
@@ -0,0 +1,11 @@
|
||||
query JobQueue {
|
||||
jobQueue {
|
||||
...JobData
|
||||
}
|
||||
}
|
||||
|
||||
query FindJob($input: FindJobInput!) {
|
||||
findJob(input: $input) {
|
||||
...JobData
|
||||
}
|
||||
}
|
||||
@@ -8,4 +8,4 @@ query MarkerWall($q: String) {
|
||||
markerWall(q: $q) {
|
||||
...SceneMarkerData
|
||||
}
|
||||
}
|
||||
}
|
||||
73
graphql/documents/queries/misc.graphql
Normal file
73
graphql/documents/queries/misc.graphql
Normal file
@@ -0,0 +1,73 @@
|
||||
query MarkerStrings($q: String, $sort: String) {
|
||||
markerStrings(q: $q, sort: $sort) {
|
||||
id
|
||||
count
|
||||
title
|
||||
}
|
||||
}
|
||||
|
||||
query AllTags {
|
||||
allTags {
|
||||
...TagData
|
||||
}
|
||||
}
|
||||
|
||||
query AllPerformersForFilter {
|
||||
allPerformers {
|
||||
...SlimPerformerData
|
||||
}
|
||||
}
|
||||
|
||||
query AllStudiosForFilter {
|
||||
allStudios {
|
||||
...SlimStudioData
|
||||
}
|
||||
}
|
||||
query AllMoviesForFilter {
|
||||
allMovies {
|
||||
...SlimMovieData
|
||||
}
|
||||
}
|
||||
|
||||
query AllTagsForFilter {
|
||||
allTags {
|
||||
id
|
||||
name
|
||||
aliases
|
||||
}
|
||||
}
|
||||
|
||||
query Stats {
|
||||
stats {
|
||||
scene_count,
|
||||
scenes_size,
|
||||
scenes_duration,
|
||||
image_count,
|
||||
images_size,
|
||||
gallery_count,
|
||||
performer_count,
|
||||
studio_count,
|
||||
movie_count,
|
||||
tag_count
|
||||
}
|
||||
}
|
||||
|
||||
query Logs {
|
||||
logs {
|
||||
...LogEntryData
|
||||
}
|
||||
}
|
||||
query Version {
|
||||
version {
|
||||
version
|
||||
hash
|
||||
build_time
|
||||
}
|
||||
}
|
||||
|
||||
query LatestVersion {
|
||||
latestversion {
|
||||
shorthash
|
||||
url
|
||||
}
|
||||
}
|
||||
14
graphql/documents/queries/movie.graphql
Normal file
14
graphql/documents/queries/movie.graphql
Normal file
@@ -0,0 +1,14 @@
|
||||
query FindMovies($filter: FindFilterType, $movie_filter: MovieFilterType) {
|
||||
findMovies(filter: $filter, movie_filter: $movie_filter) {
|
||||
count
|
||||
movies {
|
||||
...MovieData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
query FindMovie($id: ID!) {
|
||||
findMovie(id: $id) {
|
||||
...MovieData
|
||||
}
|
||||
}
|
||||
14
graphql/documents/queries/performer.graphql
Normal file
14
graphql/documents/queries/performer.graphql
Normal file
@@ -0,0 +1,14 @@
|
||||
query FindPerformers($filter: FindFilterType, $performer_filter: PerformerFilterType) {
|
||||
findPerformers(filter: $filter, performer_filter: $performer_filter) {
|
||||
count
|
||||
performers {
|
||||
...PerformerData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
query FindPerformer($id: ID!) {
|
||||
findPerformer(id: $id) {
|
||||
...PerformerData
|
||||
}
|
||||
}
|
||||
31
graphql/documents/queries/plugins.graphql
Normal file
31
graphql/documents/queries/plugins.graphql
Normal file
@@ -0,0 +1,31 @@
|
||||
query Plugins {
|
||||
plugins {
|
||||
id
|
||||
name
|
||||
description
|
||||
url
|
||||
version
|
||||
|
||||
tasks {
|
||||
name
|
||||
description
|
||||
}
|
||||
|
||||
hooks {
|
||||
name
|
||||
description
|
||||
hooks
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
query PluginTasks {
|
||||
pluginTasks {
|
||||
name
|
||||
description
|
||||
plugin {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
8
graphql/documents/queries/scene-marker.graphql
Normal file
8
graphql/documents/queries/scene-marker.graphql
Normal file
@@ -0,0 +1,8 @@
|
||||
query FindSceneMarkers($filter: FindFilterType, $scene_marker_filter: SceneMarkerFilterType) {
|
||||
findSceneMarkers(filter: $filter, scene_marker_filter: $scene_marker_filter) {
|
||||
count
|
||||
scene_markers {
|
||||
...SceneMarkerData
|
||||
}
|
||||
}
|
||||
}
|
||||
80
graphql/documents/queries/scene.graphql
Normal file
80
graphql/documents/queries/scene.graphql
Normal file
@@ -0,0 +1,80 @@
|
||||
query FindScenes($filter: FindFilterType, $scene_filter: SceneFilterType, $scene_ids: [Int!]) {
|
||||
findScenes(filter: $filter, scene_filter: $scene_filter, scene_ids: $scene_ids) {
|
||||
count
|
||||
filesize
|
||||
duration
|
||||
scenes {
|
||||
...SlimSceneData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
query FindScenesByPathRegex($filter: FindFilterType) {
|
||||
findScenesByPathRegex(filter: $filter) {
|
||||
count
|
||||
filesize
|
||||
duration
|
||||
scenes {
|
||||
...SlimSceneData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
query FindDuplicateScenes($distance: Int) {
|
||||
findDuplicateScenes(distance: $distance) {
|
||||
...SlimSceneData
|
||||
}
|
||||
}
|
||||
|
||||
query FindScene($id: ID!, $checksum: String) {
|
||||
findScene(id: $id, checksum: $checksum) {
|
||||
...SceneData
|
||||
}
|
||||
}
|
||||
|
||||
query FindSceneMarkerTags($id: ID!) {
|
||||
sceneMarkerTags(scene_id: $id) {
|
||||
tag {
|
||||
id
|
||||
name
|
||||
}
|
||||
scene_markers {
|
||||
...SceneMarkerData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
query ParseSceneFilenames($filter: FindFilterType!, $config: SceneParserInput!) {
|
||||
parseSceneFilenames(filter: $filter, config: $config) {
|
||||
count
|
||||
results {
|
||||
scene {
|
||||
...SlimSceneData
|
||||
}
|
||||
title
|
||||
code
|
||||
details
|
||||
director
|
||||
url
|
||||
date
|
||||
rating
|
||||
studio_id
|
||||
gallery_ids
|
||||
movies {
|
||||
movie_id
|
||||
}
|
||||
performer_ids
|
||||
tag_ids
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
query SceneStreams($id: ID!) {
|
||||
findScene(id: $id) {
|
||||
sceneStreams {
|
||||
url
|
||||
mime_type
|
||||
label
|
||||
}
|
||||
}
|
||||
}
|
||||
3
graphql/documents/queries/scrapers/freeones.graphql
Normal file
3
graphql/documents/queries/scrapers/freeones.graphql
Normal file
@@ -0,0 +1,3 @@
|
||||
query ScrapeFreeonesPerformers($q: String!) {
|
||||
scrapeFreeonesPerformerList(query: $q)
|
||||
}
|
||||
97
graphql/documents/queries/scrapers/scrapers.graphql
Normal file
97
graphql/documents/queries/scrapers/scrapers.graphql
Normal file
@@ -0,0 +1,97 @@
|
||||
query ListPerformerScrapers {
|
||||
listPerformerScrapers {
|
||||
id
|
||||
name
|
||||
performer {
|
||||
urls
|
||||
supported_scrapes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
query ListSceneScrapers {
|
||||
listSceneScrapers {
|
||||
id
|
||||
name
|
||||
scene {
|
||||
urls
|
||||
supported_scrapes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
query ListGalleryScrapers {
|
||||
listGalleryScrapers {
|
||||
id
|
||||
name
|
||||
gallery {
|
||||
urls
|
||||
supported_scrapes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
query ListMovieScrapers {
|
||||
listMovieScrapers {
|
||||
id
|
||||
name
|
||||
movie {
|
||||
urls
|
||||
supported_scrapes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
query ScrapeSinglePerformer($source: ScraperSourceInput!, $input: ScrapeSinglePerformerInput!) {
|
||||
scrapeSinglePerformer(source: $source, input: $input) {
|
||||
...ScrapedPerformerData
|
||||
}
|
||||
}
|
||||
|
||||
query ScrapeMultiPerformers($source: ScraperSourceInput!, $input: ScrapeMultiPerformersInput!) {
|
||||
scrapeMultiPerformers(source: $source, input: $input) {
|
||||
...ScrapedPerformerData
|
||||
}
|
||||
}
|
||||
|
||||
query ScrapePerformerURL($url: String!) {
|
||||
scrapePerformerURL(url: $url) {
|
||||
...ScrapedPerformerData
|
||||
}
|
||||
}
|
||||
|
||||
query ScrapeSingleScene($source: ScraperSourceInput!, $input: ScrapeSingleSceneInput!) {
|
||||
scrapeSingleScene(source: $source, input: $input) {
|
||||
...ScrapedSceneData
|
||||
}
|
||||
}
|
||||
|
||||
query ScrapeMultiScenes($source: ScraperSourceInput!, $input: ScrapeMultiScenesInput!) {
|
||||
scrapeMultiScenes(source: $source, input: $input) {
|
||||
...ScrapedSceneData
|
||||
}
|
||||
}
|
||||
|
||||
query ScrapeSceneURL($url: String!) {
|
||||
scrapeSceneURL(url: $url) {
|
||||
...ScrapedSceneData
|
||||
}
|
||||
}
|
||||
|
||||
query ScrapeSingleGallery($source: ScraperSourceInput!, $input: ScrapeSingleGalleryInput!) {
|
||||
scrapeSingleGallery(source: $source, input: $input) {
|
||||
...ScrapedGalleryData
|
||||
}
|
||||
}
|
||||
|
||||
query ScrapeGalleryURL($url: String!) {
|
||||
scrapeGalleryURL(url: $url) {
|
||||
...ScrapedGalleryData
|
||||
}
|
||||
}
|
||||
|
||||
query ScrapeMovieURL($url: String!) {
|
||||
scrapeMovieURL(url: $url) {
|
||||
...ScrapedMovieData
|
||||
}
|
||||
}
|
||||
20
graphql/documents/queries/settings/config.graphql
Normal file
20
graphql/documents/queries/settings/config.graphql
Normal file
@@ -0,0 +1,20 @@
|
||||
query Configuration {
|
||||
configuration {
|
||||
...ConfigData
|
||||
}
|
||||
}
|
||||
|
||||
query Directory($path: String) {
|
||||
directory(path: $path) {
|
||||
path
|
||||
parent
|
||||
directories
|
||||
}
|
||||
}
|
||||
|
||||
query ValidateStashBox($input: StashBoxInput!) {
|
||||
validateStashBoxCredentials(input: $input) {
|
||||
valid
|
||||
status
|
||||
}
|
||||
}
|
||||
9
graphql/documents/queries/settings/metadata.graphql
Normal file
9
graphql/documents/queries/settings/metadata.graphql
Normal file
@@ -0,0 +1,9 @@
|
||||
query SystemStatus {
|
||||
systemStatus {
|
||||
databaseSchema
|
||||
databasePath
|
||||
appSchema
|
||||
status
|
||||
configPath
|
||||
}
|
||||
}
|
||||
14
graphql/documents/queries/studio.graphql
Normal file
14
graphql/documents/queries/studio.graphql
Normal file
@@ -0,0 +1,14 @@
|
||||
query FindStudios($filter: FindFilterType, $studio_filter: StudioFilterType ) {
|
||||
findStudios(filter: $filter, studio_filter: $studio_filter) {
|
||||
count
|
||||
studios {
|
||||
...StudioData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
query FindStudio($id: ID!) {
|
||||
findStudio(id: $id) {
|
||||
...StudioData
|
||||
}
|
||||
}
|
||||
14
graphql/documents/queries/tag.graphql
Normal file
14
graphql/documents/queries/tag.graphql
Normal file
@@ -0,0 +1,14 @@
|
||||
query FindTags($filter: FindFilterType, $tag_filter: TagFilterType ) {
|
||||
findTags(filter: $filter, tag_filter: $tag_filter) {
|
||||
count
|
||||
tags {
|
||||
...TagData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
query FindTag($id: ID!) {
|
||||
findTag(id: $id) {
|
||||
...TagData
|
||||
}
|
||||
}
|
||||
@@ -7,8 +7,6 @@ subscription JobsSubscribe {
|
||||
subTasks
|
||||
description
|
||||
progress
|
||||
error
|
||||
startTime
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,4 +19,4 @@ subscription LoggingSubscribe {
|
||||
|
||||
subscription ScanCompleteSubscribe {
|
||||
scanCompleteSubscribe
|
||||
}
|
||||
}
|
||||
@@ -1,248 +1,133 @@
|
||||
"The query root for this schema"
|
||||
"""The query root for this schema"""
|
||||
type Query {
|
||||
# Filters
|
||||
findSavedFilter(id: ID!): SavedFilter
|
||||
findSavedFilters(mode: FilterMode): [SavedFilter!]!
|
||||
findDefaultFilter(mode: FilterMode!): SavedFilter
|
||||
@deprecated(reason: "default filter now stored in UI config")
|
||||
|
||||
"Find a file by its id or path"
|
||||
findFile(id: ID, path: String): BaseFile!
|
||||
|
||||
"Queries for Files"
|
||||
findFiles(
|
||||
file_filter: FileFilterType
|
||||
filter: FindFilterType
|
||||
ids: [ID!]
|
||||
): FindFilesResultType!
|
||||
|
||||
"Find a file by its id or path"
|
||||
findFolder(id: ID, path: String): Folder!
|
||||
|
||||
"Queries for Files"
|
||||
findFolders(
|
||||
folder_filter: FolderFilterType
|
||||
filter: FindFilterType
|
||||
ids: [ID!]
|
||||
): FindFoldersResultType!
|
||||
|
||||
"Find a scene by ID or Checksum"
|
||||
"""Find a scene by ID or Checksum"""
|
||||
findScene(id: ID, checksum: String): Scene
|
||||
findSceneByHash(input: SceneHashInput!): Scene
|
||||
|
||||
"A function which queries Scene objects"
|
||||
findScenes(
|
||||
scene_filter: SceneFilterType
|
||||
scene_ids: [Int!] @deprecated(reason: "use ids")
|
||||
ids: [ID!]
|
||||
filter: FindFilterType
|
||||
): FindScenesResultType!
|
||||
"""A function which queries Scene objects"""
|
||||
findScenes(scene_filter: SceneFilterType, scene_ids: [Int!], filter: FindFilterType): FindScenesResultType!
|
||||
|
||||
findScenesByPathRegex(filter: FindFilterType): FindScenesResultType!
|
||||
|
||||
"""
|
||||
Returns any groups of scenes that are perceptual duplicates within the queried distance
|
||||
and the difference between their duration is smaller than durationDiff
|
||||
"""
|
||||
findDuplicateScenes(
|
||||
distance: Int
|
||||
"""
|
||||
Max difference in seconds between files in order to be considered for similarity matching.
|
||||
Fractional seconds are ok: 0.5 will mean only files that have durations within 0.5 seconds between them will be matched based on PHash distance.
|
||||
"""
|
||||
duration_diff: Float
|
||||
scene_filter: SceneFilterType
|
||||
): [[Scene!]!]!
|
||||
""" Returns any groups of scenes that are perceptual duplicates within the queried distance """
|
||||
findDuplicateScenes(distance: Int): [[Scene!]!]!
|
||||
|
||||
"Return valid stream paths"
|
||||
"""Return valid stream paths"""
|
||||
sceneStreams(id: ID): [SceneStreamEndpoint!]!
|
||||
|
||||
parseSceneFilenames(
|
||||
filter: FindFilterType
|
||||
config: SceneParserInput!
|
||||
): SceneParserResultType!
|
||||
parseSceneFilenames(filter: FindFilterType, config: SceneParserInput!): SceneParserResultType!
|
||||
|
||||
"A function which queries SceneMarker objects"
|
||||
findSceneMarkers(
|
||||
scene_marker_filter: SceneMarkerFilterType
|
||||
filter: FindFilterType
|
||||
ids: [ID!]
|
||||
): FindSceneMarkersResultType!
|
||||
"""A function which queries SceneMarker objects"""
|
||||
findSceneMarkers(scene_marker_filter: SceneMarkerFilterType filter: FindFilterType): FindSceneMarkersResultType!
|
||||
|
||||
findImage(id: ID, checksum: String): Image
|
||||
|
||||
"A function which queries Scene objects"
|
||||
findImages(
|
||||
image_filter: ImageFilterType
|
||||
image_ids: [Int!] @deprecated(reason: "use ids")
|
||||
ids: [ID!]
|
||||
filter: FindFilterType
|
||||
): FindImagesResultType!
|
||||
"""A function which queries Scene objects"""
|
||||
findImages(image_filter: ImageFilterType, image_ids: [Int!], filter: FindFilterType): FindImagesResultType!
|
||||
|
||||
"Find a performer by ID"
|
||||
"""Find a performer by ID"""
|
||||
findPerformer(id: ID!): Performer
|
||||
"A function which queries Performer objects"
|
||||
findPerformers(
|
||||
performer_filter: PerformerFilterType
|
||||
filter: FindFilterType
|
||||
performer_ids: [Int!] @deprecated(reason: "use ids")
|
||||
ids: [ID!]
|
||||
): FindPerformersResultType!
|
||||
"""A function which queries Performer objects"""
|
||||
findPerformers(performer_filter: PerformerFilterType, filter: FindFilterType): FindPerformersResultType!
|
||||
|
||||
"Find a studio by ID"
|
||||
"""Find a studio by ID"""
|
||||
findStudio(id: ID!): Studio
|
||||
"A function which queries Studio objects"
|
||||
findStudios(
|
||||
studio_filter: StudioFilterType
|
||||
filter: FindFilterType
|
||||
ids: [ID!]
|
||||
): FindStudiosResultType!
|
||||
"""A function which queries Studio objects"""
|
||||
findStudios(studio_filter: StudioFilterType, filter: FindFilterType): FindStudiosResultType!
|
||||
|
||||
"Find a movie by ID"
|
||||
findMovie(id: ID!): Movie @deprecated(reason: "Use findGroup instead")
|
||||
"A function which queries Movie objects"
|
||||
findMovies(
|
||||
movie_filter: MovieFilterType
|
||||
filter: FindFilterType
|
||||
ids: [ID!]
|
||||
): FindMoviesResultType! @deprecated(reason: "Use findGroups instead")
|
||||
|
||||
"Find a group by ID"
|
||||
findGroup(id: ID!): Group
|
||||
"A function which queries Group objects"
|
||||
findGroups(
|
||||
group_filter: GroupFilterType
|
||||
filter: FindFilterType
|
||||
ids: [ID!]
|
||||
): FindGroupsResultType!
|
||||
"""Find a movie by ID"""
|
||||
findMovie(id: ID!): Movie
|
||||
"""A function which queries Movie objects"""
|
||||
findMovies(movie_filter: MovieFilterType, filter: FindFilterType): FindMoviesResultType!
|
||||
|
||||
findGallery(id: ID!): Gallery
|
||||
findGalleries(
|
||||
gallery_filter: GalleryFilterType
|
||||
filter: FindFilterType
|
||||
ids: [ID!]
|
||||
): FindGalleriesResultType!
|
||||
findGalleries(gallery_filter: GalleryFilterType, filter: FindFilterType): FindGalleriesResultType!
|
||||
|
||||
findTag(id: ID!): Tag
|
||||
findTags(
|
||||
tag_filter: TagFilterType
|
||||
filter: FindFilterType
|
||||
ids: [ID!]
|
||||
): FindTagsResultType!
|
||||
findTags(tag_filter: TagFilterType, filter: FindFilterType): FindTagsResultType!
|
||||
|
||||
"Retrieve random scene markers for the wall"
|
||||
"""Retrieve random scene markers for the wall"""
|
||||
markerWall(q: String): [SceneMarker!]!
|
||||
"Retrieve random scenes for the wall"
|
||||
"""Retrieve random scenes for the wall"""
|
||||
sceneWall(q: String): [Scene!]!
|
||||
|
||||
"Get marker strings"
|
||||
"""Get marker strings"""
|
||||
markerStrings(q: String, sort: String): [MarkerStringsResultType]!
|
||||
"Get stats"
|
||||
"""Get stats"""
|
||||
stats: StatsResultType!
|
||||
"Organize scene markers by tag for a given scene ID"
|
||||
"""Organize scene markers by tag for a given scene ID"""
|
||||
sceneMarkerTags(scene_id: ID!): [SceneMarkerTag!]!
|
||||
|
||||
logs: [LogEntry!]!
|
||||
|
||||
# Scrapers
|
||||
|
||||
"List available scrapers"
|
||||
"""List available scrapers"""
|
||||
listScrapers(types: [ScrapeContentType!]!): [Scraper!]!
|
||||
listPerformerScrapers: [Scraper!]! @deprecated(reason: "Use listScrapers(types: [PERFORMER])")
|
||||
listSceneScrapers: [Scraper!]! @deprecated(reason: "Use listScrapers(types: [SCENE])")
|
||||
listGalleryScrapers: [Scraper!]! @deprecated(reason: "Use listScrapers(types: [GALLERY])")
|
||||
listMovieScrapers: [Scraper!]! @deprecated(reason: "Use listScrapers(types: [MOVIE])")
|
||||
|
||||
"Scrape for a single scene"
|
||||
scrapeSingleScene(
|
||||
source: ScraperSourceInput!
|
||||
input: ScrapeSingleSceneInput!
|
||||
): [ScrapedScene!]!
|
||||
"Scrape for multiple scenes"
|
||||
scrapeMultiScenes(
|
||||
source: ScraperSourceInput!
|
||||
input: ScrapeMultiScenesInput!
|
||||
): [[ScrapedScene!]!]!
|
||||
|
||||
"Scrape for a single studio"
|
||||
scrapeSingleStudio(
|
||||
source: ScraperSourceInput!
|
||||
input: ScrapeSingleStudioInput!
|
||||
): [ScrapedStudio!]!
|
||||
"""Scrape for a single scene"""
|
||||
scrapeSingleScene(source: ScraperSourceInput!, input: ScrapeSingleSceneInput!): [ScrapedScene!]!
|
||||
"""Scrape for multiple scenes"""
|
||||
scrapeMultiScenes(source: ScraperSourceInput!, input: ScrapeMultiScenesInput!): [[ScrapedScene!]!]!
|
||||
|
||||
"Scrape for a single tag"
|
||||
scrapeSingleTag(
|
||||
source: ScraperSourceInput!
|
||||
input: ScrapeSingleTagInput!
|
||||
): [ScrapedTag!]!
|
||||
"""Scrape for a single performer"""
|
||||
scrapeSinglePerformer(source: ScraperSourceInput!, input: ScrapeSinglePerformerInput!): [ScrapedPerformer!]!
|
||||
"""Scrape for multiple performers"""
|
||||
scrapeMultiPerformers(source: ScraperSourceInput!, input: ScrapeMultiPerformersInput!): [[ScrapedPerformer!]!]!
|
||||
|
||||
"Scrape for a single performer"
|
||||
scrapeSinglePerformer(
|
||||
source: ScraperSourceInput!
|
||||
input: ScrapeSinglePerformerInput!
|
||||
): [ScrapedPerformer!]!
|
||||
"Scrape for multiple performers"
|
||||
scrapeMultiPerformers(
|
||||
source: ScraperSourceInput!
|
||||
input: ScrapeMultiPerformersInput!
|
||||
): [[ScrapedPerformer!]!]!
|
||||
"""Scrape for a single gallery"""
|
||||
scrapeSingleGallery(source: ScraperSourceInput!, input: ScrapeSingleGalleryInput!): [ScrapedGallery!]!
|
||||
|
||||
"Scrape for a single gallery"
|
||||
scrapeSingleGallery(
|
||||
source: ScraperSourceInput!
|
||||
input: ScrapeSingleGalleryInput!
|
||||
): [ScrapedGallery!]!
|
||||
|
||||
"Scrape for a single movie"
|
||||
scrapeSingleMovie(
|
||||
source: ScraperSourceInput!
|
||||
input: ScrapeSingleMovieInput!
|
||||
): [ScrapedMovie!]! @deprecated(reason: "Use scrapeSingleGroup instead")
|
||||
|
||||
"Scrape for a single group"
|
||||
scrapeSingleGroup(
|
||||
source: ScraperSourceInput!
|
||||
input: ScrapeSingleGroupInput!
|
||||
): [ScrapedGroup!]!
|
||||
|
||||
"Scrape for a single image"
|
||||
scrapeSingleImage(
|
||||
source: ScraperSourceInput!
|
||||
input: ScrapeSingleImageInput!
|
||||
): [ScrapedImage!]!
|
||||
"""Scrape for a single movie"""
|
||||
scrapeSingleMovie(source: ScraperSourceInput!, input: ScrapeSingleMovieInput!): [ScrapedMovie!]!
|
||||
|
||||
"Scrapes content based on a URL"
|
||||
scrapeURL(url: String!, ty: ScrapeContentType!): ScrapedContent
|
||||
|
||||
"Scrapes a complete performer record based on a URL"
|
||||
"""Scrapes a complete performer record based on a URL"""
|
||||
scrapePerformerURL(url: String!): ScrapedPerformer
|
||||
"Scrapes a complete scene record based on a URL"
|
||||
"""Scrapes a complete performer record based on a URL"""
|
||||
scrapeSceneURL(url: String!): ScrapedScene
|
||||
"Scrapes a complete gallery record based on a URL"
|
||||
"""Scrapes a complete gallery record based on a URL"""
|
||||
scrapeGalleryURL(url: String!): ScrapedGallery
|
||||
"Scrapes a complete image record based on a URL"
|
||||
scrapeImageURL(url: String!): ScrapedImage
|
||||
"Scrapes a complete movie record based on a URL"
|
||||
"""Scrapes a complete movie record based on a URL"""
|
||||
scrapeMovieURL(url: String!): ScrapedMovie
|
||||
@deprecated(reason: "Use scrapeGroupURL instead")
|
||||
"Scrapes a complete group record based on a URL"
|
||||
scrapeGroupURL(url: String!): ScrapedGroup
|
||||
|
||||
"""Scrape a list of performers based on name"""
|
||||
scrapePerformerList(scraper_id: ID!, query: String!): [ScrapedPerformer!]! @deprecated(reason: "use scrapeSinglePerformer")
|
||||
"""Scrapes a complete performer record based on a scrapePerformerList result"""
|
||||
scrapePerformer(scraper_id: ID!, scraped_performer: ScrapedPerformerInput!): ScrapedPerformer @deprecated(reason: "use scrapeSinglePerformer")
|
||||
"""Scrapes a complete scene record based on an existing scene"""
|
||||
scrapeScene(scraper_id: ID!, scene: SceneUpdateInput!): ScrapedScene @deprecated(reason: "use scrapeSingleScene")
|
||||
"""Scrapes a complete gallery record based on an existing gallery"""
|
||||
scrapeGallery(scraper_id: ID!, gallery: GalleryUpdateInput!): ScrapedGallery @deprecated(reason: "use scrapeSingleGallery")
|
||||
|
||||
"""Scrape a list of performers from a query"""
|
||||
scrapeFreeonesPerformerList(query: String!): [String!]! @deprecated(reason: "use scrapeSinglePerformer with scraper_id = builtin_freeones")
|
||||
|
||||
# Plugins
|
||||
"List loaded plugins"
|
||||
"""List loaded plugins"""
|
||||
plugins: [Plugin!]
|
||||
"List available plugin operations"
|
||||
"""List available plugin operations"""
|
||||
pluginTasks: [PluginTask!]
|
||||
|
||||
# Packages
|
||||
"List installed packages"
|
||||
installedPackages(type: PackageType!): [Package!]!
|
||||
"List available packages"
|
||||
availablePackages(type: PackageType!, source: String!): [Package!]!
|
||||
|
||||
# Config
|
||||
"Returns the current, complete configuration"
|
||||
"""Returns the current, complete configuration"""
|
||||
configuration: ConfigResult!
|
||||
"Returns an array of paths for the given path"
|
||||
"""Returns an array of paths for the given path"""
|
||||
directory(
|
||||
"The directory path to list"
|
||||
path: String
|
||||
path: String,
|
||||
"Desired collation locale. Determines the order of the directory result. eg. 'en-US', 'pt-BR', ..."
|
||||
locale: String = "en"
|
||||
): Directory!
|
||||
@@ -259,16 +144,10 @@ type Query {
|
||||
|
||||
# Get everything
|
||||
|
||||
allScenes: [Scene!]! @deprecated(reason: "Use findScenes instead")
|
||||
allSceneMarkers: [SceneMarker!]!
|
||||
@deprecated(reason: "Use findSceneMarkers instead")
|
||||
allImages: [Image!]! @deprecated(reason: "Use findImages instead")
|
||||
allGalleries: [Gallery!]! @deprecated(reason: "Use findGalleries instead")
|
||||
|
||||
allPerformers: [Performer!]!
|
||||
allTags: [Tag!]! @deprecated(reason: "Use findTags instead")
|
||||
allStudios: [Studio!]! @deprecated(reason: "Use findStudios instead")
|
||||
allMovies: [Movie!]! @deprecated(reason: "Use findGroups instead")
|
||||
allStudios: [Studio!]!
|
||||
allMovies: [Movie!]!
|
||||
allTags: [Tag!]!
|
||||
|
||||
# Get everything with minimal metadata
|
||||
|
||||
@@ -276,17 +155,12 @@ type Query {
|
||||
version: Version!
|
||||
|
||||
# LatestVersion
|
||||
latestversion: LatestVersion!
|
||||
latestversion: ShortVersion!
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
setup(input: SetupInput!): Boolean!
|
||||
|
||||
"Migrates the schema to the required version. Returns the job ID"
|
||||
migrate(input: MigrateInput!): ID!
|
||||
|
||||
"Downloads and installs ffmpeg and ffprobe binaries into the configuration directory. Returns the job ID."
|
||||
downloadFFMpeg: ID!
|
||||
migrate(input: MigrateInput!): Boolean!
|
||||
|
||||
sceneCreate(input: SceneCreateInput!): Scene
|
||||
sceneUpdate(input: SceneUpdateInput!): Scene
|
||||
@@ -296,48 +170,25 @@ type Mutation {
|
||||
scenesDestroy(input: ScenesDestroyInput!): Boolean!
|
||||
scenesUpdate(input: [SceneUpdateInput!]!): [Scene]
|
||||
|
||||
"Increments the o-counter for a scene. Returns the new value"
|
||||
sceneIncrementO(id: ID!): Int! @deprecated(reason: "Use sceneAddO instead")
|
||||
"Decrements the o-counter for a scene. Returns the new value"
|
||||
sceneDecrementO(id: ID!): Int! @deprecated(reason: "Use sceneRemoveO instead")
|
||||
|
||||
"Increments the o-counter for a scene. Uses the current time if none provided."
|
||||
sceneAddO(id: ID!, times: [Timestamp!]): HistoryMutationResult!
|
||||
"Decrements the o-counter for a scene, removing the last recorded time if specific time not provided. Returns the new value"
|
||||
sceneDeleteO(id: ID!, times: [Timestamp!]): HistoryMutationResult!
|
||||
|
||||
"Resets the o-counter for a scene to 0. Returns the new value"
|
||||
"""Increments the o-counter for a scene. Returns the new value"""
|
||||
sceneIncrementO(id: ID!): Int!
|
||||
"""Decrements the o-counter for a scene. Returns the new value"""
|
||||
sceneDecrementO(id: ID!): Int!
|
||||
"""Resets the o-counter for a scene to 0. Returns the new value"""
|
||||
sceneResetO(id: ID!): Int!
|
||||
|
||||
"Sets the resume time point (if provided) and adds the provided duration to the scene's play duration"
|
||||
"""Sets the resume time point (if provided) and adds the provided duration to the scene's play duration"""
|
||||
sceneSaveActivity(id: ID!, resume_time: Float, playDuration: Float): Boolean!
|
||||
|
||||
"Resets the resume time point and play duration"
|
||||
sceneResetActivity(
|
||||
id: ID!
|
||||
reset_resume: Boolean
|
||||
reset_duration: Boolean
|
||||
): Boolean!
|
||||
|
||||
"Increments the play count for the scene. Returns the new play count value."
|
||||
"""Increments the play count for the scene. Returns the new play count value."""
|
||||
sceneIncrementPlayCount(id: ID!): Int!
|
||||
@deprecated(reason: "Use sceneAddPlay instead")
|
||||
|
||||
"Increments the play count for the scene. Uses the current time if none provided."
|
||||
sceneAddPlay(id: ID!, times: [Timestamp!]): HistoryMutationResult!
|
||||
"Decrements the play count for the scene, removing the specific times or the last recorded time if not provided."
|
||||
sceneDeletePlay(id: ID!, times: [Timestamp!]): HistoryMutationResult!
|
||||
"Resets the play count for a scene to 0. Returns the new play count value."
|
||||
sceneResetPlayCount(id: ID!): Int!
|
||||
|
||||
"Generates screenshot at specified time in seconds. Leave empty to generate default screenshot"
|
||||
"""Generates screenshot at specified time in seconds. Leave empty to generate default screenshot"""
|
||||
sceneGenerateScreenshot(id: ID!, at: Float): String!
|
||||
|
||||
sceneMarkerCreate(input: SceneMarkerCreateInput!): SceneMarker
|
||||
sceneMarkerUpdate(input: SceneMarkerUpdateInput!): SceneMarker
|
||||
bulkSceneMarkerUpdate(input: BulkSceneMarkerUpdateInput!): [SceneMarker!]
|
||||
sceneMarkerDestroy(id: ID!): Boolean!
|
||||
sceneMarkersDestroy(ids: [ID!]!): Boolean!
|
||||
|
||||
sceneAssignFile(input: AssignSceneFileInput!): Boolean!
|
||||
|
||||
@@ -347,11 +198,11 @@ type Mutation {
|
||||
imagesDestroy(input: ImagesDestroyInput!): Boolean!
|
||||
imagesUpdate(input: [ImageUpdateInput!]!): [Image]
|
||||
|
||||
"Increments the o-counter for an image. Returns the new value"
|
||||
"""Increments the o-counter for an image. Returns the new value"""
|
||||
imageIncrementO(id: ID!): Int!
|
||||
"Decrements the o-counter for an image. Returns the new value"
|
||||
"""Decrements the o-counter for an image. Returns the new value"""
|
||||
imageDecrementO(id: ID!): Int!
|
||||
"Resets the o-counter for a image to 0. Returns the new value"
|
||||
"""Resets the o-counter for a image to 0. Returns the new value"""
|
||||
imageResetO(id: ID!): Int!
|
||||
|
||||
galleryCreate(input: GalleryCreateInput!): Gallery
|
||||
@@ -362,243 +213,111 @@ type Mutation {
|
||||
|
||||
addGalleryImages(input: GalleryAddInput!): Boolean!
|
||||
removeGalleryImages(input: GalleryRemoveInput!): Boolean!
|
||||
setGalleryCover(input: GallerySetCoverInput!): Boolean!
|
||||
resetGalleryCover(input: GalleryResetCoverInput!): Boolean!
|
||||
|
||||
galleryChapterCreate(input: GalleryChapterCreateInput!): GalleryChapter
|
||||
galleryChapterUpdate(input: GalleryChapterUpdateInput!): GalleryChapter
|
||||
galleryChapterDestroy(id: ID!): Boolean!
|
||||
|
||||
performerCreate(input: PerformerCreateInput!): Performer
|
||||
performerUpdate(input: PerformerUpdateInput!): Performer
|
||||
performerDestroy(input: PerformerDestroyInput!): Boolean!
|
||||
performersDestroy(ids: [ID!]!): Boolean!
|
||||
bulkPerformerUpdate(input: BulkPerformerUpdateInput!): [Performer!]
|
||||
performerMerge(input: PerformerMergeInput!): Performer!
|
||||
|
||||
studioCreate(input: StudioCreateInput!): Studio
|
||||
studioUpdate(input: StudioUpdateInput!): Studio
|
||||
studioDestroy(input: StudioDestroyInput!): Boolean!
|
||||
studiosDestroy(ids: [ID!]!): Boolean!
|
||||
bulkStudioUpdate(input: BulkStudioUpdateInput!): [Studio!]
|
||||
|
||||
movieCreate(input: MovieCreateInput!): Movie
|
||||
@deprecated(reason: "Use groupCreate instead")
|
||||
movieUpdate(input: MovieUpdateInput!): Movie
|
||||
@deprecated(reason: "Use groupUpdate instead")
|
||||
movieDestroy(input: MovieDestroyInput!): Boolean!
|
||||
@deprecated(reason: "Use groupDestroy instead")
|
||||
moviesDestroy(ids: [ID!]!): Boolean!
|
||||
@deprecated(reason: "Use groupsDestroy instead")
|
||||
bulkMovieUpdate(input: BulkMovieUpdateInput!): [Movie!]
|
||||
@deprecated(reason: "Use bulkGroupUpdate instead")
|
||||
|
||||
groupCreate(input: GroupCreateInput!): Group
|
||||
groupUpdate(input: GroupUpdateInput!): Group
|
||||
groupDestroy(input: GroupDestroyInput!): Boolean!
|
||||
groupsDestroy(ids: [ID!]!): Boolean!
|
||||
bulkGroupUpdate(input: BulkGroupUpdateInput!): [Group!]
|
||||
|
||||
addGroupSubGroups(input: GroupSubGroupAddInput!): Boolean!
|
||||
removeGroupSubGroups(input: GroupSubGroupRemoveInput!): Boolean!
|
||||
|
||||
"Reorder sub groups within a group. Returns true if successful."
|
||||
reorderSubGroups(input: ReorderSubGroupsInput!): Boolean!
|
||||
|
||||
tagCreate(input: TagCreateInput!): Tag
|
||||
tagUpdate(input: TagUpdateInput!): Tag
|
||||
tagDestroy(input: TagDestroyInput!): Boolean!
|
||||
tagsDestroy(ids: [ID!]!): Boolean!
|
||||
tagsMerge(input: TagsMergeInput!): Tag
|
||||
bulkTagUpdate(input: BulkTagUpdateInput!): [Tag!]
|
||||
|
||||
"""
|
||||
Moves the given files to the given destination. Returns true if successful.
|
||||
Either the destination_folder or destination_folder_id must be provided.
|
||||
If both are provided, the destination_folder_id takes precedence.
|
||||
Destination folder must be a subfolder of one of the stash library paths.
|
||||
If provided, destination_basename must be a valid filename with an extension that
|
||||
matches one of the media extensions.
|
||||
Creates folder hierarchy if needed.
|
||||
"""
|
||||
moveFiles(input: MoveFilesInput!): Boolean!
|
||||
deleteFiles(ids: [ID!]!): Boolean!
|
||||
"Deletes file entries from the database without deleting the files from the filesystem"
|
||||
destroyFiles(ids: [ID!]!): Boolean!
|
||||
|
||||
fileSetFingerprints(input: FileSetFingerprintsInput!): Boolean!
|
||||
"Reveal the file in the system file manager"
|
||||
revealFileInFileManager(id: ID!): Boolean!
|
||||
"Reveal the folder in the system file manager"
|
||||
revealFolderInFileManager(id: ID!): Boolean!
|
||||
|
||||
# Saved filters
|
||||
saveFilter(input: SaveFilterInput!): SavedFilter!
|
||||
destroySavedFilter(input: DestroyFilterInput!): Boolean!
|
||||
setDefaultFilter(input: SetDefaultFilterInput!): Boolean!
|
||||
@deprecated(reason: "now uses UI config")
|
||||
|
||||
"Change general configuration options"
|
||||
"""Change general configuration options"""
|
||||
configureGeneral(input: ConfigGeneralInput!): ConfigGeneralResult!
|
||||
configureInterface(input: ConfigInterfaceInput!): ConfigInterfaceResult!
|
||||
configureDLNA(input: ConfigDLNAInput!): ConfigDLNAResult!
|
||||
configureScraping(input: ConfigScrapingInput!): ConfigScrapingResult!
|
||||
configureDefaults(
|
||||
input: ConfigDefaultSettingsInput!
|
||||
): ConfigDefaultSettingsResult!
|
||||
configureDefaults(input: ConfigDefaultSettingsInput!): ConfigDefaultSettingsResult!
|
||||
|
||||
"overwrites the entire plugin configuration for the given plugin"
|
||||
configurePlugin(plugin_id: ID!, input: Map!): Map!
|
||||
|
||||
"""
|
||||
overwrites the UI configuration
|
||||
if input is provided, then the entire UI configuration is replaced
|
||||
if partial is provided, then the partial UI configuration is merged into the existing UI configuration
|
||||
"""
|
||||
configureUI(input: Map, partial: Map): Map!
|
||||
"""
|
||||
sets a single UI key value
|
||||
key is a dot separated path to the value
|
||||
"""
|
||||
# overwrites the entire UI configuration
|
||||
configureUI(input: Map!): Map!
|
||||
# sets a single UI key value
|
||||
configureUISetting(key: String!, value: Any): Map!
|
||||
|
||||
"Generate and set (or clear) API key"
|
||||
"""Generate and set (or clear) API key"""
|
||||
generateAPIKey(input: GenerateAPIKeyInput!): String!
|
||||
|
||||
"Returns a link to download the result"
|
||||
"""Returns a link to download the result"""
|
||||
exportObjects(input: ExportObjectsInput!): String
|
||||
|
||||
"Performs an incremental import. Returns the job ID"
|
||||
"""Performs an incremental import. Returns the job ID"""
|
||||
importObjects(input: ImportObjectsInput!): ID!
|
||||
|
||||
"Start an full import. Completely wipes the database and imports from the metadata directory. Returns the job ID"
|
||||
"""Start an full import. Completely wipes the database and imports from the metadata directory. Returns the job ID"""
|
||||
metadataImport: ID!
|
||||
"Start a full export. Outputs to the metadata directory. Returns the job ID"
|
||||
"""Start a full export. Outputs to the metadata directory. Returns the job ID"""
|
||||
metadataExport: ID!
|
||||
"Start a scan. Returns the job ID"
|
||||
"""Start a scan. Returns the job ID"""
|
||||
metadataScan(input: ScanMetadataInput!): ID!
|
||||
"Start generating content. Returns the job ID"
|
||||
"""Start generating content. Returns the job ID"""
|
||||
metadataGenerate(input: GenerateMetadataInput!): ID!
|
||||
"Start auto-tagging. Returns the job ID"
|
||||
"""Start auto-tagging. Returns the job ID"""
|
||||
metadataAutoTag(input: AutoTagMetadataInput!): ID!
|
||||
"Clean metadata. Returns the job ID"
|
||||
"""Clean metadata. Returns the job ID"""
|
||||
metadataClean(input: CleanMetadataInput!): ID!
|
||||
"Clean generated files. Returns the job ID"
|
||||
metadataCleanGenerated(input: CleanGeneratedInput!): ID!
|
||||
"Identifies scenes using scrapers. Returns the job ID"
|
||||
"""Identifies scenes using scrapers. Returns the job ID"""
|
||||
metadataIdentify(input: IdentifyMetadataInput!): ID!
|
||||
|
||||
"Migrate generated files for the current hash naming"
|
||||
"""Migrate generated files for the current hash naming"""
|
||||
migrateHashNaming: ID!
|
||||
"Migrates legacy scene screenshot files into the blob storage"
|
||||
migrateSceneScreenshots(input: MigrateSceneScreenshotsInput!): ID!
|
||||
"Migrates blobs from the old storage system to the current one"
|
||||
migrateBlobs(input: MigrateBlobsInput!): ID!
|
||||
|
||||
"Anonymise the database in a separate file. Optionally returns a link to download the database file"
|
||||
anonymiseDatabase(input: AnonymiseDatabaseInput!): String
|
||||
|
||||
"Optimises the database. Returns the job ID"
|
||||
optimiseDatabase: ID!
|
||||
|
||||
"Reload scrapers"
|
||||
"""Reload scrapers"""
|
||||
reloadScrapers: Boolean!
|
||||
|
||||
"""
|
||||
Enable/disable plugins - enabledMap is a map of plugin IDs to enabled booleans.
|
||||
Plugins not in the map are not affected.
|
||||
"""
|
||||
setPluginsEnabled(enabledMap: BoolMap!): Boolean!
|
||||
|
||||
"""
|
||||
Run a plugin task.
|
||||
If task_name is provided, then the task must exist in the plugin config and the tasks configuration
|
||||
will be used to run the plugin.
|
||||
If no task_name is provided, then the plugin will be executed with the arguments provided only.
|
||||
Returns the job ID
|
||||
"""
|
||||
runPluginTask(
|
||||
plugin_id: ID!
|
||||
"if provided, then the default args will be applied"
|
||||
task_name: String
|
||||
"displayed in the task queue"
|
||||
description: String
|
||||
args: [PluginArgInput!] @deprecated(reason: "Use args_map instead")
|
||||
args_map: Map
|
||||
): ID!
|
||||
|
||||
"""
|
||||
Runs a plugin operation. The operation is run immediately and does not use the job queue.
|
||||
Returns a map of the result.
|
||||
"""
|
||||
runPluginOperation(plugin_id: ID!, args: Map): Any
|
||||
|
||||
"""Run plugin task. Returns the job ID"""
|
||||
runPluginTask(plugin_id: ID!, task_name: String!, args: [PluginArgInput!]): ID!
|
||||
reloadPlugins: Boolean!
|
||||
|
||||
"""
|
||||
Installs the given packages.
|
||||
If a package is already installed, it will be updated if needed..
|
||||
If an error occurs when installing a package, the job will continue to install the remaining packages.
|
||||
Returns the job ID
|
||||
"""
|
||||
installPackages(type: PackageType!, packages: [PackageSpecInput!]!): ID!
|
||||
"""
|
||||
Updates the given packages.
|
||||
If a package is not installed, it will not be installed.
|
||||
If a package does not need to be updated, it will not be updated.
|
||||
If no packages are provided, all packages of the given type will be updated.
|
||||
If an error occurs when updating a package, the job will continue to update the remaining packages.
|
||||
Returns the job ID.
|
||||
"""
|
||||
updatePackages(type: PackageType!, packages: [PackageSpecInput!]): ID!
|
||||
"""
|
||||
Uninstalls the given packages.
|
||||
If an error occurs when uninstalling a package, the job will continue to uninstall the remaining packages.
|
||||
Returns the job ID
|
||||
"""
|
||||
uninstallPackages(type: PackageType!, packages: [PackageSpecInput!]!): ID!
|
||||
|
||||
stopJob(job_id: ID!): Boolean!
|
||||
stopAllJobs: Boolean!
|
||||
|
||||
"Submit fingerprints to stash-box instance"
|
||||
submitStashBoxFingerprints(
|
||||
input: StashBoxFingerprintSubmissionInput!
|
||||
): Boolean!
|
||||
"""Submit fingerprints to stash-box instance"""
|
||||
submitStashBoxFingerprints(input: StashBoxFingerprintSubmissionInput!): Boolean!
|
||||
|
||||
"Submit scene as draft to stash-box instance"
|
||||
"""Submit scene as draft to stash-box instance"""
|
||||
submitStashBoxSceneDraft(input: StashBoxDraftSubmissionInput!): ID
|
||||
"Submit performer as draft to stash-box instance"
|
||||
"""Submit performer as draft to stash-box instance"""
|
||||
submitStashBoxPerformerDraft(input: StashBoxDraftSubmissionInput!): ID
|
||||
|
||||
"Backup the database. Optionally returns a link to download the database file"
|
||||
"""Backup the database. Optionally returns a link to download the database file"""
|
||||
backupDatabase(input: BackupDatabaseInput!): String
|
||||
|
||||
"DANGEROUS: Execute an arbitrary SQL statement that returns rows."
|
||||
querySQL(sql: String!, args: [Any]): SQLQueryResult!
|
||||
"""Run batch performer tag task. Returns the job ID."""
|
||||
stashBoxBatchPerformerTag(input: StashBoxBatchPerformerTagInput!): String!
|
||||
|
||||
"DANGEROUS: Execute an arbitrary SQL statement without returning any rows."
|
||||
execSQL(sql: String!, args: [Any]): SQLExecResult!
|
||||
|
||||
"Run batch performer tag task. Returns the job ID."
|
||||
stashBoxBatchPerformerTag(input: StashBoxBatchTagInput!): String!
|
||||
"Run batch studio tag task. Returns the job ID."
|
||||
stashBoxBatchStudioTag(input: StashBoxBatchTagInput!): String!
|
||||
"Run batch tag tag task. Returns the job ID."
|
||||
stashBoxBatchTagTag(input: StashBoxBatchTagInput!): String!
|
||||
|
||||
"Enables DLNA for an optional duration. Has no effect if DLNA is enabled by default"
|
||||
"""Enables DLNA for an optional duration. Has no effect if DLNA is enabled by default"""
|
||||
enableDLNA(input: EnableDLNAInput!): Boolean!
|
||||
"Disables DLNA for an optional duration. Has no effect if DLNA is disabled by default"
|
||||
"""Disables DLNA for an optional duration. Has no effect if DLNA is disabled by default"""
|
||||
disableDLNA(input: DisableDLNAInput!): Boolean!
|
||||
"Enables an IP address for DLNA for an optional duration"
|
||||
"""Enables an IP address for DLNA for an optional duration"""
|
||||
addTempDLNAIP(input: AddTempDLNAIPInput!): Boolean!
|
||||
"Removes an IP address from the temporary DLNA whitelist"
|
||||
"""Removes an IP address from the temporary DLNA whitelist"""
|
||||
removeTempDLNAIP(input: RemoveTempDLNAIPInput!): Boolean!
|
||||
}
|
||||
|
||||
type Subscription {
|
||||
"Update from the metadata manager"
|
||||
"""Update from the metadata manager"""
|
||||
jobsSubscribe: JobStatusUpdate!
|
||||
|
||||
loggingSubscribe: [LogEntry!]!
|
||||
|
||||
@@ -1,347 +1,207 @@
|
||||
input SetupInput {
|
||||
"Empty to indicate $HOME/.stash/config.yml default"
|
||||
"""Empty to indicate $HOME/.stash/config.yml default"""
|
||||
configLocation: String!
|
||||
stashes: [StashConfigInput!]!
|
||||
"True if SFW content mode is enabled"
|
||||
sfwContentMode: Boolean
|
||||
"Empty to indicate default"
|
||||
"""Empty to indicate default"""
|
||||
databaseFile: String!
|
||||
"Empty to indicate default"
|
||||
"""Empty to indicate default"""
|
||||
generatedLocation: String!
|
||||
"Empty to indicate default"
|
||||
cacheLocation: String!
|
||||
storeBlobsInDatabase: Boolean!
|
||||
"Empty to indicate default - only applicable if storeBlobsInDatabase is false"
|
||||
blobsLocation: String!
|
||||
}
|
||||
|
||||
enum StreamingResolutionEnum {
|
||||
"240p"
|
||||
LOW
|
||||
"480p"
|
||||
STANDARD
|
||||
"720p"
|
||||
STANDARD_HD
|
||||
"1080p"
|
||||
FULL_HD
|
||||
"4k"
|
||||
FOUR_K
|
||||
"Original"
|
||||
ORIGINAL
|
||||
"240p", LOW
|
||||
"480p", STANDARD
|
||||
"720p", STANDARD_HD
|
||||
"1080p", FULL_HD
|
||||
"4k", FOUR_K
|
||||
"Original", ORIGINAL
|
||||
}
|
||||
|
||||
enum PreviewPreset {
|
||||
"X264_ULTRAFAST"
|
||||
ultrafast
|
||||
"X264_VERYFAST"
|
||||
veryfast
|
||||
"X264_FAST"
|
||||
fast
|
||||
"X264_MEDIUM"
|
||||
medium
|
||||
"X264_SLOW"
|
||||
slow
|
||||
"X264_SLOWER"
|
||||
slower
|
||||
"X264_VERYSLOW"
|
||||
veryslow
|
||||
"X264_ULTRAFAST", ultrafast
|
||||
"X264_VERYFAST", veryfast
|
||||
"X264_FAST", fast
|
||||
"X264_MEDIUM", medium
|
||||
"X264_SLOW", slow
|
||||
"X264_SLOWER", slower
|
||||
"X264_VERYSLOW", veryslow
|
||||
}
|
||||
|
||||
enum HashAlgorithm {
|
||||
MD5
|
||||
"oshash"
|
||||
OSHASH
|
||||
}
|
||||
|
||||
enum BlobsStorageType {
|
||||
# blobs are stored in the database
|
||||
"Database"
|
||||
DATABASE
|
||||
# blobs are stored in the filesystem under the configured blobs directory
|
||||
"Filesystem"
|
||||
FILESYSTEM
|
||||
"oshash", OSHASH
|
||||
}
|
||||
|
||||
input ConfigGeneralInput {
|
||||
"Array of file paths to content"
|
||||
"""Array of file paths to content"""
|
||||
stashes: [StashConfigInput!]
|
||||
"Path to the SQLite database"
|
||||
"""Path to the SQLite database"""
|
||||
databasePath: String
|
||||
"Path to backup directory"
|
||||
"""Path to backup directory"""
|
||||
backupDirectoryPath: String
|
||||
"Path to trash directory - if set, deleted files will be moved here instead of being permanently deleted"
|
||||
deleteTrashPath: String
|
||||
"Path to generated files"
|
||||
"""Path to generated files"""
|
||||
generatedPath: String
|
||||
"Path to import/export files"
|
||||
"""Path to import/export files"""
|
||||
metadataPath: String
|
||||
"Path to scrapers"
|
||||
"""Path to scrapers"""
|
||||
scrapersPath: String
|
||||
"Path to plugins"
|
||||
pluginsPath: String
|
||||
"Path to cache"
|
||||
"""Path to cache"""
|
||||
cachePath: String
|
||||
"Path to blobs - required for filesystem blob storage"
|
||||
blobsPath: String
|
||||
"Where to store blobs"
|
||||
blobsStorage: BlobsStorageType
|
||||
"Path to the ffmpeg binary. If empty, stash will attempt to find it in the path or config directory"
|
||||
ffmpegPath: String
|
||||
"Path to the ffprobe binary. If empty, stash will attempt to find it in the path or config directory"
|
||||
ffprobePath: String
|
||||
"Whether to calculate MD5 checksums for scene video files"
|
||||
"""Whether to calculate MD5 checksums for scene video files"""
|
||||
calculateMD5: Boolean
|
||||
"Hash algorithm to use for generated file naming"
|
||||
"""Hash algorithm to use for generated file naming"""
|
||||
videoFileNamingAlgorithm: HashAlgorithm
|
||||
"Number of parallel tasks to start during scan/generate"
|
||||
"""Number of parallel tasks to start during scan/generate"""
|
||||
parallelTasks: Int
|
||||
"Include audio stream in previews"
|
||||
"""Include audio stream in previews"""
|
||||
previewAudio: Boolean
|
||||
"Number of segments in a preview file"
|
||||
"""Number of segments in a preview file"""
|
||||
previewSegments: Int
|
||||
"Preview segment duration, in seconds"
|
||||
"""Preview segment duration, in seconds"""
|
||||
previewSegmentDuration: Float
|
||||
"Duration of start of video to exclude when generating previews"
|
||||
"""Duration of start of video to exclude when generating previews"""
|
||||
previewExcludeStart: String
|
||||
"Duration of end of video to exclude when generating previews"
|
||||
"""Duration of end of video to exclude when generating previews"""
|
||||
previewExcludeEnd: String
|
||||
"Preset when generating preview"
|
||||
"""Preset when generating preview"""
|
||||
previewPreset: PreviewPreset
|
||||
"Transcode Hardware Acceleration"
|
||||
transcodeHardwareAcceleration: Boolean
|
||||
"Max generated transcode size"
|
||||
"""Max generated transcode size"""
|
||||
maxTranscodeSize: StreamingResolutionEnum
|
||||
"Max streaming transcode size"
|
||||
"""Max streaming transcode size"""
|
||||
maxStreamingTranscodeSize: StreamingResolutionEnum
|
||||
|
||||
"""
|
||||
ffmpeg transcode input args - injected before input file
|
||||
These are applied to generated transcodes (previews and transcodes)
|
||||
"""
|
||||
transcodeInputArgs: [String!]
|
||||
"""
|
||||
ffmpeg transcode output args - injected before output file
|
||||
These are applied to generated transcodes (previews and transcodes)
|
||||
"""
|
||||
transcodeOutputArgs: [String!]
|
||||
|
||||
"""
|
||||
ffmpeg stream input args - injected before input file
|
||||
These are applied when live transcoding
|
||||
"""
|
||||
liveTranscodeInputArgs: [String!]
|
||||
"""
|
||||
ffmpeg stream output args - injected before output file
|
||||
These are applied when live transcoding
|
||||
"""
|
||||
liveTranscodeOutputArgs: [String!]
|
||||
|
||||
"whether to include range in generated funscript heatmaps"
|
||||
drawFunscriptHeatmapRange: Boolean
|
||||
|
||||
"Write image thumbnails to disk when generating on the fly"
|
||||
"""Write image thumbnails to disk when generating on the fly"""
|
||||
writeImageThumbnails: Boolean
|
||||
"Create Image Clips from Video extensions when Videos are disabled in Library"
|
||||
createImageClipsFromVideos: Boolean
|
||||
"Username"
|
||||
"""Username"""
|
||||
username: String
|
||||
"Password"
|
||||
"""Password"""
|
||||
password: String
|
||||
"Maximum session cookie age"
|
||||
"""Maximum session cookie age"""
|
||||
maxSessionAge: Int
|
||||
"Name of the log file"
|
||||
"""Comma separated list of proxies to allow traffic from"""
|
||||
trustedProxies: [String!] @deprecated(reason: "no longer supported")
|
||||
"""Name of the log file"""
|
||||
logFile: String
|
||||
"Whether to also output to stderr"
|
||||
"""Whether to also output to stderr"""
|
||||
logOut: Boolean
|
||||
"Minimum log level"
|
||||
"""Minimum log level"""
|
||||
logLevel: String
|
||||
"Whether to log http access"
|
||||
"""Whether to log http access"""
|
||||
logAccess: Boolean
|
||||
"Maximum log size"
|
||||
logFileMaxSize: Int
|
||||
"True if galleries should be created from folders with images"
|
||||
"""True if galleries should be created from folders with images"""
|
||||
createGalleriesFromFolders: Boolean
|
||||
"Regex used to identify images as gallery covers"
|
||||
galleryCoverRegex: String
|
||||
"Array of video file extensions"
|
||||
"""Array of video file extensions"""
|
||||
videoExtensions: [String!]
|
||||
"Array of image file extensions"
|
||||
"""Array of image file extensions"""
|
||||
imageExtensions: [String!]
|
||||
"Array of gallery zip file extensions"
|
||||
"""Array of gallery zip file extensions"""
|
||||
galleryExtensions: [String!]
|
||||
"Array of file regexp to exclude from Video Scans"
|
||||
"""Array of file regexp to exclude from Video Scans"""
|
||||
excludes: [String!]
|
||||
"Array of file regexp to exclude from Image Scans"
|
||||
"""Array of file regexp to exclude from Image Scans"""
|
||||
imageExcludes: [String!]
|
||||
"Custom Performer Image Location"
|
||||
"""Custom Performer Image Location"""
|
||||
customPerformerImageLocation: String
|
||||
"Stash-box instances used for tagging"
|
||||
"""Scraper user agent string"""
|
||||
scraperUserAgent: String @deprecated(reason: "use mutation ConfigureScraping(input: ConfigScrapingInput) instead")
|
||||
"""Scraper CDP path. Path to chrome executable or remote address"""
|
||||
scraperCDPPath: String @deprecated(reason: "use mutation ConfigureScraping(input: ConfigScrapingInput) instead")
|
||||
"""Whether the scraper should check for invalid certificates"""
|
||||
scraperCertCheck: Boolean @deprecated(reason: "use mutation ConfigureScraping(input: ConfigScrapingInput) instead")
|
||||
"""Stash-box instances used for tagging"""
|
||||
stashBoxes: [StashBoxInput!]
|
||||
"Python path - resolved using path if unset"
|
||||
"""Python path - resolved using path if unset"""
|
||||
pythonPath: String
|
||||
|
||||
"Source of scraper packages"
|
||||
scraperPackageSources: [PackageSourceInput!]
|
||||
"Source of plugin packages"
|
||||
pluginPackageSources: [PackageSourceInput!]
|
||||
|
||||
"Size of the longest dimension for each sprite in pixels"
|
||||
spriteScreenshotSize: Int
|
||||
|
||||
"True if sprite generation should use the sprite interval and min/max sprites settings instead of the default"
|
||||
useCustomSpriteInterval: Boolean
|
||||
"Time between two different scrubber sprites in seconds - only used if useCustomSpriteInterval is true"
|
||||
spriteInterval: Float
|
||||
"Minimum number of sprites to be generated - only used if useCustomSpriteInterval is true"
|
||||
minimumSprites: Int
|
||||
"Minimum number of sprites to be generated - only used if useCustomSpriteInterval is true"
|
||||
maximumSprites: Int
|
||||
}
|
||||
|
||||
type ConfigGeneralResult {
|
||||
"Array of file paths to content"
|
||||
"""Array of file paths to content"""
|
||||
stashes: [StashConfig!]!
|
||||
"Path to the SQLite database"
|
||||
"""Path to the SQLite database"""
|
||||
databasePath: String!
|
||||
"Path to backup directory"
|
||||
"""Path to backup directory"""
|
||||
backupDirectoryPath: String!
|
||||
"Path to trash directory - if set, deleted files will be moved here instead of being permanently deleted"
|
||||
deleteTrashPath: String!
|
||||
"Path to generated files"
|
||||
"""Path to generated files"""
|
||||
generatedPath: String!
|
||||
"Path to import/export files"
|
||||
"""Path to import/export files"""
|
||||
metadataPath: String!
|
||||
"Path to the config file used"
|
||||
"""Path to the config file used"""
|
||||
configFilePath: String!
|
||||
"Path to scrapers"
|
||||
"""Path to scrapers"""
|
||||
scrapersPath: String!
|
||||
"Path to plugins"
|
||||
pluginsPath: String!
|
||||
"Path to cache"
|
||||
"""Path to cache"""
|
||||
cachePath: String!
|
||||
"Path to blobs - required for filesystem blob storage"
|
||||
blobsPath: String!
|
||||
"Where to store blobs"
|
||||
blobsStorage: BlobsStorageType!
|
||||
"Path to the ffmpeg binary. If empty, stash will attempt to find it in the path or config directory"
|
||||
ffmpegPath: String!
|
||||
"Path to the ffprobe binary. If empty, stash will attempt to find it in the path or config directory"
|
||||
ffprobePath: String!
|
||||
"Whether to calculate MD5 checksums for scene video files"
|
||||
"""Whether to calculate MD5 checksums for scene video files"""
|
||||
calculateMD5: Boolean!
|
||||
"Hash algorithm to use for generated file naming"
|
||||
"""Hash algorithm to use for generated file naming"""
|
||||
videoFileNamingAlgorithm: HashAlgorithm!
|
||||
"Number of parallel tasks to start during scan/generate"
|
||||
"""Number of parallel tasks to start during scan/generate"""
|
||||
parallelTasks: Int!
|
||||
"Include audio stream in previews"
|
||||
"""Include audio stream in previews"""
|
||||
previewAudio: Boolean!
|
||||
"Number of segments in a preview file"
|
||||
"""Number of segments in a preview file"""
|
||||
previewSegments: Int!
|
||||
"Preview segment duration, in seconds"
|
||||
"""Preview segment duration, in seconds"""
|
||||
previewSegmentDuration: Float!
|
||||
"Duration of start of video to exclude when generating previews"
|
||||
"""Duration of start of video to exclude when generating previews"""
|
||||
previewExcludeStart: String!
|
||||
"Duration of end of video to exclude when generating previews"
|
||||
"""Duration of end of video to exclude when generating previews"""
|
||||
previewExcludeEnd: String!
|
||||
"Preset when generating preview"
|
||||
"""Preset when generating preview"""
|
||||
previewPreset: PreviewPreset!
|
||||
"Transcode Hardware Acceleration"
|
||||
transcodeHardwareAcceleration: Boolean!
|
||||
"Max generated transcode size"
|
||||
"""Max generated transcode size"""
|
||||
maxTranscodeSize: StreamingResolutionEnum
|
||||
"Max streaming transcode size"
|
||||
"""Max streaming transcode size"""
|
||||
maxStreamingTranscodeSize: StreamingResolutionEnum
|
||||
|
||||
"""
|
||||
ffmpeg transcode input args - injected before input file
|
||||
These are applied to generated transcodes (previews and transcodes)
|
||||
"""
|
||||
transcodeInputArgs: [String!]!
|
||||
"""
|
||||
ffmpeg transcode output args - injected before output file
|
||||
These are applied to generated transcodes (previews and transcodes)
|
||||
"""
|
||||
transcodeOutputArgs: [String!]!
|
||||
|
||||
"""
|
||||
ffmpeg stream input args - injected before input file
|
||||
These are applied when live transcoding
|
||||
"""
|
||||
liveTranscodeInputArgs: [String!]!
|
||||
"""
|
||||
ffmpeg stream output args - injected before output file
|
||||
These are applied when live transcoding
|
||||
"""
|
||||
liveTranscodeOutputArgs: [String!]!
|
||||
|
||||
"whether to include range in generated funscript heatmaps"
|
||||
drawFunscriptHeatmapRange: Boolean!
|
||||
|
||||
"Write image thumbnails to disk when generating on the fly"
|
||||
"""Write image thumbnails to disk when generating on the fly"""
|
||||
writeImageThumbnails: Boolean!
|
||||
"Create Image Clips from Video extensions when Videos are disabled in Library"
|
||||
createImageClipsFromVideos: Boolean!
|
||||
"API Key"
|
||||
"""API Key"""
|
||||
apiKey: String!
|
||||
"Username"
|
||||
"""Username"""
|
||||
username: String!
|
||||
"Password"
|
||||
"""Password"""
|
||||
password: String!
|
||||
"Maximum session cookie age"
|
||||
"""Maximum session cookie age"""
|
||||
maxSessionAge: Int!
|
||||
"Name of the log file"
|
||||
"""Comma separated list of proxies to allow traffic from"""
|
||||
trustedProxies: [String!] @deprecated(reason: "no longer supported")
|
||||
"""Name of the log file"""
|
||||
logFile: String
|
||||
"Whether to also output to stderr"
|
||||
"""Whether to also output to stderr"""
|
||||
logOut: Boolean!
|
||||
"Minimum log level"
|
||||
"""Minimum log level"""
|
||||
logLevel: String!
|
||||
"Whether to log http access"
|
||||
"""Whether to log http access"""
|
||||
logAccess: Boolean!
|
||||
"Maximum log size"
|
||||
logFileMaxSize: Int!
|
||||
"True if sprite generation should use the sprite interval and min/max sprites settings instead of the default"
|
||||
useCustomSpriteInterval: Boolean!
|
||||
"Time between two different scrubber sprites in seconds - only used if useCustomSpriteInterval is true"
|
||||
spriteInterval: Float!
|
||||
"Minimum number of sprites to be generated - only used if useCustomSpriteInterval is true"
|
||||
minimumSprites: Int!
|
||||
"Maximum number of sprites to be generated - only used if useCustomSpriteInterval is true"
|
||||
maximumSprites: Int!
|
||||
"Size of the longest dimension for each sprite in pixels"
|
||||
spriteScreenshotSize: Int!
|
||||
"Array of video file extensions"
|
||||
"""Array of video file extensions"""
|
||||
videoExtensions: [String!]!
|
||||
"Array of image file extensions"
|
||||
"""Array of image file extensions"""
|
||||
imageExtensions: [String!]!
|
||||
"Array of gallery zip file extensions"
|
||||
"""Array of gallery zip file extensions"""
|
||||
galleryExtensions: [String!]!
|
||||
"True if galleries should be created from folders with images"
|
||||
"""True if galleries should be created from folders with images"""
|
||||
createGalleriesFromFolders: Boolean!
|
||||
"Regex used to identify images as gallery covers"
|
||||
galleryCoverRegex: String!
|
||||
"Array of file regexp to exclude from Video Scans"
|
||||
"""Array of file regexp to exclude from Video Scans"""
|
||||
excludes: [String!]!
|
||||
"Array of file regexp to exclude from Image Scans"
|
||||
"""Array of file regexp to exclude from Image Scans"""
|
||||
imageExcludes: [String!]!
|
||||
"Custom Performer Image Location"
|
||||
"""Custom Performer Image Location"""
|
||||
customPerformerImageLocation: String
|
||||
"Stash-box instances used for tagging"
|
||||
"""Scraper user agent string"""
|
||||
scraperUserAgent: String @deprecated(reason: "use ConfigResult.scraping instead")
|
||||
"""Scraper CDP path. Path to chrome executable or remote address"""
|
||||
scraperCDPPath: String @deprecated(reason: "use ConfigResult.scraping instead")
|
||||
"""Whether the scraper should check for invalid certificates"""
|
||||
scraperCertCheck: Boolean! @deprecated(reason: "use ConfigResult.scraping instead")
|
||||
"""Stash-box instances used for tagging"""
|
||||
stashBoxes: [StashBox!]!
|
||||
"Python path - resolved using path if unset"
|
||||
"""Python path - resolved using path if unset"""
|
||||
pythonPath: String!
|
||||
|
||||
"Source of scraper packages"
|
||||
scraperPackageSources: [PackageSource!]!
|
||||
"Source of plugin packages"
|
||||
pluginPackageSources: [PackageSource!]!
|
||||
}
|
||||
|
||||
input ConfigDisableDropdownCreateInput {
|
||||
performer: Boolean
|
||||
tag: Boolean
|
||||
studio: Boolean
|
||||
movie: Boolean
|
||||
gallery: Boolean
|
||||
}
|
||||
|
||||
enum ImageLightboxDisplayMode {
|
||||
@@ -362,7 +222,6 @@ input ConfigImageLightboxInput {
|
||||
resetZoomOnNav: Boolean
|
||||
scrollMode: ImageLightboxScrollMode
|
||||
scrollAttemptsBeforeChange: Int
|
||||
disableAnimation: Boolean
|
||||
}
|
||||
|
||||
type ConfigImageLightboxResult {
|
||||
@@ -372,71 +231,65 @@ type ConfigImageLightboxResult {
|
||||
resetZoomOnNav: Boolean
|
||||
scrollMode: ImageLightboxScrollMode
|
||||
scrollAttemptsBeforeChange: Int!
|
||||
disableAnimation: Boolean
|
||||
}
|
||||
|
||||
input ConfigInterfaceInput {
|
||||
"True if SFW content mode is enabled"
|
||||
sfwContentMode: Boolean
|
||||
|
||||
"Ordered list of items that should be shown in the menu"
|
||||
"""Ordered list of items that should be shown in the menu"""
|
||||
menuItems: [String!]
|
||||
|
||||
"Enable sound on mouseover previews"
|
||||
"""Enable sound on mouseover previews"""
|
||||
soundOnPreview: Boolean
|
||||
|
||||
"Show title and tags in wall view"
|
||||
|
||||
"""Show title and tags in wall view"""
|
||||
wallShowTitle: Boolean
|
||||
"Wall playback type"
|
||||
"""Wall playback type"""
|
||||
wallPlayback: String
|
||||
|
||||
"Show scene scrubber by default"
|
||||
"""Show scene scrubber by default"""
|
||||
showScrubber: Boolean
|
||||
|
||||
"Maximum duration (in seconds) in which a scene video will loop in the scene player"
|
||||
|
||||
"""Maximum duration (in seconds) in which a scene video will loop in the scene player"""
|
||||
maximumLoopDuration: Int
|
||||
"If true, video will autostart on load in the scene player"
|
||||
"""If true, video will autostart on load in the scene player"""
|
||||
autostartVideo: Boolean
|
||||
"If true, video will autostart when loading from play random or play selected"
|
||||
"""If true, video will autostart when loading from play random or play selected"""
|
||||
autostartVideoOnPlaySelected: Boolean
|
||||
"If true, next scene in playlist will be played at video end by default"
|
||||
"""If true, next scene in playlist will be played at video end by default"""
|
||||
continuePlaylistDefault: Boolean
|
||||
|
||||
"If true, studio overlays will be shown as text instead of logo images"
|
||||
|
||||
"""If true, studio overlays will be shown as text instead of logo images"""
|
||||
showStudioAsText: Boolean
|
||||
|
||||
"Custom CSS"
|
||||
|
||||
"""Custom CSS"""
|
||||
css: String
|
||||
cssEnabled: Boolean
|
||||
|
||||
"Custom Javascript"
|
||||
"""Custom Javascript"""
|
||||
javascript: String
|
||||
javascriptEnabled: Boolean
|
||||
|
||||
"Custom Locales"
|
||||
"""Custom Locales"""
|
||||
customLocales: String
|
||||
customLocalesEnabled: Boolean
|
||||
|
||||
"When true, disables all customizations (plugins, CSS, JavaScript, locales) for troubleshooting"
|
||||
disableCustomizations: Boolean
|
||||
|
||||
"Interface language"
|
||||
|
||||
"""Interface language"""
|
||||
language: String
|
||||
|
||||
"""Slideshow Delay"""
|
||||
slideshowDelay: Int @deprecated(reason: "Use imageLightbox.slideshowDelay")
|
||||
|
||||
imageLightbox: ConfigImageLightboxInput
|
||||
|
||||
"Set to true to disable creating new objects via the dropdown menus"
|
||||
|
||||
"""Set to true to disable creating new objects via the dropdown menus"""
|
||||
disableDropdownCreate: ConfigDisableDropdownCreateInput
|
||||
|
||||
"Handy Connection Key"
|
||||
|
||||
"""Handy Connection Key"""
|
||||
handyKey: String
|
||||
"Funscript Time Offset"
|
||||
"""Funscript Time Offset"""
|
||||
funscriptOffset: Int
|
||||
"Whether to use Stash Hosted Funscript"
|
||||
useStashHostedFunscript: Boolean
|
||||
"True if we should not auto-open a browser window on startup"
|
||||
"""True if we should not auto-open a browser window on startup"""
|
||||
noBrowser: Boolean
|
||||
"True if we should send notifications to the desktop"
|
||||
"""True if we should send notifications to the desktop"""
|
||||
notificationsEnabled: Boolean
|
||||
}
|
||||
|
||||
@@ -444,122 +297,108 @@ type ConfigDisableDropdownCreate {
|
||||
performer: Boolean!
|
||||
tag: Boolean!
|
||||
studio: Boolean!
|
||||
movie: Boolean!
|
||||
gallery: Boolean!
|
||||
}
|
||||
|
||||
type ConfigInterfaceResult {
|
||||
"True if SFW content mode is enabled"
|
||||
sfwContentMode: Boolean!
|
||||
|
||||
"Ordered list of items that should be shown in the menu"
|
||||
"""Ordered list of items that should be shown in the menu"""
|
||||
menuItems: [String!]
|
||||
|
||||
"Enable sound on mouseover previews"
|
||||
"""Enable sound on mouseover previews"""
|
||||
soundOnPreview: Boolean
|
||||
|
||||
"Show title and tags in wall view"
|
||||
"""Show title and tags in wall view"""
|
||||
wallShowTitle: Boolean
|
||||
"Wall playback type"
|
||||
"""Wall playback type"""
|
||||
wallPlayback: String
|
||||
|
||||
"Show scene scrubber by default"
|
||||
"""Show scene scrubber by default"""
|
||||
showScrubber: Boolean
|
||||
|
||||
"Maximum duration (in seconds) in which a scene video will loop in the scene player"
|
||||
"""Maximum duration (in seconds) in which a scene video will loop in the scene player"""
|
||||
maximumLoopDuration: Int
|
||||
"True if we should not auto-open a browser window on startup"
|
||||
"""True if we should not auto-open a browser window on startup"""
|
||||
noBrowser: Boolean
|
||||
"True if we should send desktop notifications"
|
||||
"""True if we should send desktop notifications"""
|
||||
notificationsEnabled: Boolean
|
||||
"If true, video will autostart on load in the scene player"
|
||||
"""If true, video will autostart on load in the scene player"""
|
||||
autostartVideo: Boolean
|
||||
"If true, video will autostart when loading from play random or play selected"
|
||||
"""If true, video will autostart when loading from play random or play selected"""
|
||||
autostartVideoOnPlaySelected: Boolean
|
||||
"If true, next scene in playlist will be played at video end by default"
|
||||
"""If true, next scene in playlist will be played at video end by default"""
|
||||
continuePlaylistDefault: Boolean
|
||||
|
||||
"If true, studio overlays will be shown as text instead of logo images"
|
||||
"""If true, studio overlays will be shown as text instead of logo images"""
|
||||
showStudioAsText: Boolean
|
||||
|
||||
"Custom CSS"
|
||||
"""Custom CSS"""
|
||||
css: String
|
||||
cssEnabled: Boolean
|
||||
|
||||
"Custom Javascript"
|
||||
"""Custom Javascript"""
|
||||
javascript: String
|
||||
javascriptEnabled: Boolean
|
||||
|
||||
"Custom Locales"
|
||||
"""Custom Locales"""
|
||||
customLocales: String
|
||||
customLocalesEnabled: Boolean
|
||||
|
||||
"When true, disables all customizations (plugins, CSS, JavaScript, locales) for troubleshooting"
|
||||
disableCustomizations: Boolean
|
||||
|
||||
"Interface language"
|
||||
|
||||
"""Interface language"""
|
||||
language: String
|
||||
|
||||
"""Slideshow Delay"""
|
||||
slideshowDelay: Int @deprecated(reason: "Use imageLightbox.slideshowDelay")
|
||||
|
||||
imageLightbox: ConfigImageLightboxResult!
|
||||
|
||||
"Fields are true if creating via dropdown menus are disabled"
|
||||
"""Fields are true if creating via dropdown menus are disabled"""
|
||||
disableDropdownCreate: ConfigDisableDropdownCreate!
|
||||
disabledDropdownCreate: ConfigDisableDropdownCreate! @deprecated(reason: "Use disableDropdownCreate")
|
||||
|
||||
"Handy Connection Key"
|
||||
"""Handy Connection Key"""
|
||||
handyKey: String
|
||||
"Funscript Time Offset"
|
||||
"""Funscript Time Offset"""
|
||||
funscriptOffset: Int
|
||||
"Whether to use Stash Hosted Funscript"
|
||||
useStashHostedFunscript: Boolean
|
||||
}
|
||||
|
||||
input ConfigDLNAInput {
|
||||
serverName: String
|
||||
"True if DLNA service should be enabled by default"
|
||||
"""True if DLNA service should be enabled by default"""
|
||||
enabled: Boolean
|
||||
"Defaults to 1338"
|
||||
port: Int
|
||||
"List of IPs whitelisted for DLNA service"
|
||||
"""List of IPs whitelisted for DLNA service"""
|
||||
whitelistedIPs: [String!]
|
||||
"List of interfaces to run DLNA on. Empty for all"
|
||||
"""List of interfaces to run DLNA on. Empty for all"""
|
||||
interfaces: [String!]
|
||||
"Order to sort videos"
|
||||
videoSortOrder: String
|
||||
}
|
||||
|
||||
type ConfigDLNAResult {
|
||||
serverName: String!
|
||||
"True if DLNA service should be enabled by default"
|
||||
"""True if DLNA service should be enabled by default"""
|
||||
enabled: Boolean!
|
||||
"Defaults to 1338"
|
||||
port: Int!
|
||||
"List of IPs whitelisted for DLNA service"
|
||||
"""List of IPs whitelisted for DLNA service"""
|
||||
whitelistedIPs: [String!]!
|
||||
"List of interfaces to run DLNA on. Empty for all"
|
||||
"""List of interfaces to run DLNA on. Empty for all"""
|
||||
interfaces: [String!]!
|
||||
"Order to sort videos"
|
||||
videoSortOrder: String!
|
||||
}
|
||||
|
||||
input ConfigScrapingInput {
|
||||
"Scraper user agent string"
|
||||
"""Scraper user agent string"""
|
||||
scraperUserAgent: String
|
||||
"Scraper CDP path. Path to chrome executable or remote address"
|
||||
"""Scraper CDP path. Path to chrome executable or remote address"""
|
||||
scraperCDPPath: String
|
||||
"Whether the scraper should check for invalid certificates"
|
||||
"""Whether the scraper should check for invalid certificates"""
|
||||
scraperCertCheck: Boolean
|
||||
"Tags blacklist during scraping"
|
||||
"""Tags blacklist during scraping"""
|
||||
excludeTagPatterns: [String!]
|
||||
}
|
||||
|
||||
type ConfigScrapingResult {
|
||||
"Scraper user agent string"
|
||||
"""Scraper user agent string"""
|
||||
scraperUserAgent: String
|
||||
"Scraper CDP path. Path to chrome executable or remote address"
|
||||
"""Scraper CDP path. Path to chrome executable or remote address"""
|
||||
scraperCDPPath: String
|
||||
"Whether the scraper should check for invalid certificates"
|
||||
"""Whether the scraper should check for invalid certificates"""
|
||||
scraperCertCheck: Boolean!
|
||||
"Tags blacklist during scraping"
|
||||
"""Tags blacklist during scraping"""
|
||||
excludeTagPatterns: [String!]!
|
||||
}
|
||||
|
||||
@@ -568,10 +407,10 @@ type ConfigDefaultSettingsResult {
|
||||
identify: IdentifyMetadataTaskOptions
|
||||
autoTag: AutoTagMetadataOptions
|
||||
generate: GenerateMetadataOptions
|
||||
|
||||
"If true, delete file checkbox will be checked by default"
|
||||
|
||||
"""If true, delete file checkbox will be checked by default"""
|
||||
deleteFile: Boolean
|
||||
"If true, delete generated supporting files checkbox will be checked by default"
|
||||
"""If true, delete generated supporting files checkbox will be checked by default"""
|
||||
deleteGenerated: Boolean
|
||||
}
|
||||
|
||||
@@ -581,13 +420,13 @@ input ConfigDefaultSettingsInput {
|
||||
autoTag: AutoTagMetadataInput
|
||||
generate: GenerateMetadataInput
|
||||
|
||||
"If true, delete file checkbox will be checked by default"
|
||||
"""If true, delete file checkbox will be checked by default"""
|
||||
deleteFile: Boolean
|
||||
"If true, delete generated files checkbox will be checked by default"
|
||||
"""If true, delete generated files checkbox will be checked by default"""
|
||||
deleteGenerated: Boolean
|
||||
}
|
||||
|
||||
"All configuration settings"
|
||||
"""All configuration settings"""
|
||||
type ConfigResult {
|
||||
general: ConfigGeneralResult!
|
||||
interface: ConfigInterfaceResult!
|
||||
@@ -595,17 +434,16 @@ type ConfigResult {
|
||||
scraping: ConfigScrapingResult!
|
||||
defaults: ConfigDefaultSettingsResult!
|
||||
ui: Map!
|
||||
plugins(include: [ID!]): PluginConfigMap!
|
||||
}
|
||||
|
||||
"Directory structure of a path"
|
||||
"""Directory structure of a path"""
|
||||
type Directory {
|
||||
path: String!
|
||||
parent: String
|
||||
directories: [String!]!
|
||||
path: String!
|
||||
parent: String
|
||||
directories: [String!]!
|
||||
}
|
||||
|
||||
"Stash configuration details"
|
||||
"""Stash configuration details"""
|
||||
input StashConfigInput {
|
||||
path: String!
|
||||
excludeVideo: Boolean!
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user