Documentation
¶
Overview ¶
Package stash is a Go client for the GraphQL API of a running Stash server (https://stashapp.cc).
It has no dependencies outside the standard library, and it does not impose an HTTP client: pass your own with WithHTTPClient to get whatever retry, timeout and transport behaviour your program already uses.
c := stash.NewClient("http://localhost:9999", stash.WithAPIKey(key))
scenes, _, err := c.FindScenes(ctx, stash.SceneFilter{}, 1, 100)
The server's schema varies by version. Ask before relying on a field that older releases lack — see Client.Supports.
Index ¶
- Constants
- Variables
- func Union(dst Scene, srcs []Scene, p UnionPolicy) (SceneUpdate, []Conflict)
- func VerifySQLite(r io.ReaderAt, size int64) error
- type APIError
- type AutoTagOptions
- type BackupManifest
- type BackupOptions
- type BackupServer
- type BatchTagOptions
- type BatchTagTarget
- type Caption
- type CleanOptions
- type Client
- func (c *Client) AddScenePerformers(ctx context.Context, performerIDs []string, sceneIDs ...string) error
- func (c *Client) AddSceneTags(ctx context.Context, tagIDs []string, sceneIDs ...string) error
- func (c *Client) AllowDLNAIP(ctx context.Context, address string, d time.Duration) error
- func (c *Client) AnonymiseDatabase(ctx context.Context) (serverPath string, err error)
- func (c *Client) AssignFile(ctx context.Context, sceneID, fileID string) error
- func (c *Client) AvailablePackages(ctx context.Context, t PackageType, sourceURL string) ([]Package, error)
- func (c *Client) BackupDatabase(ctx context.Context, opts BackupOptions) (serverPath string, err error)
- func (c *Client) ClearAPIKey(ctx context.Context) error
- func (c *Client) ClearPerformerFields(ctx context.Context, id string, fields ...string) error
- func (c *Client) ClearSceneFields(ctx context.Context, id string, fields ...string) error
- func (c *Client) ClearStudioFields(ctx context.Context, id string, fields ...string) error
- func (c *Client) ClearTagFields(ctx context.Context, id string, fields ...string) error
- func (c *Client) Configuration(ctx context.Context) (map[string]any, error)
- func (c *Client) ConfigureGeneral(ctx context.Context, settings map[string]any) error
- func (c *Client) ConfigureInterface(ctx context.Context, settings map[string]any) error
- func (c *Client) CreatePerformer(ctx context.Context, name string) (string, error)
- func (c *Client) CreatePerformerFrom(ctx context.Context, in PerformerInput) (string, error)
- func (c *Client) CreateStudio(ctx context.Context, name string) (string, error)
- func (c *Client) CreateStudioFrom(ctx context.Context, in StudioInput) (string, error)
- func (c *Client) CreateTag(ctx context.Context, name string) (string, error)
- func (c *Client) CreateTagFrom(ctx context.Context, in TagInput) (string, error)
- func (c *Client) DLNAStatus(ctx context.Context) (DLNAStatus, error)
- func (c *Client) DeleteFiles(ctx context.Context, fileIDs ...string) error
- func (c *Client) DeletePerformer(ctx context.Context, id string) error
- func (c *Client) DeletePerformers(ctx context.Context, ids ...string) error
- func (c *Client) DeleteScene(ctx context.Context, id string, opts DeleteOptions) error
- func (c *Client) DeleteScenes(ctx context.Context, ids []string, opts DeleteOptions) error
- func (c *Client) DeleteStudio(ctx context.Context, id string) error
- func (c *Client) DeleteStudios(ctx context.Context, ids ...string) error
- func (c *Client) DeleteTag(ctx context.Context, id string) error
- func (c *Client) DeleteTags(ctx context.Context, ids ...string) error
- func (c *Client) DestroyFiles(ctx context.Context, fileIDs ...string) error
- func (c *Client) DestroySavedFilter(ctx context.Context, id string) error
- func (c *Client) DisableDLNA(ctx context.Context, d time.Duration) error
- func (c *Client) DisallowDLNAIP(ctx context.Context, address string) error
- func (c *Client) DownloadAnonymisedDatabase(ctx context.Context, w io.Writer) (name string, written int64, err error)
- func (c *Client) DownloadBackup(ctx context.Context, opts BackupOptions, w io.Writer) (name string, written int64, err error)
- func (c *Client) DownloadFFMpeg(ctx context.Context) (jobID string, err error)
- func (c *Client) DownloadVerifiedBackup(ctx context.Context, opts BackupOptions, dir string) (BackupManifest, error)
- func (c *Client) EnableDLNA(ctx context.Context, d time.Duration) error
- func (c *Client) EnsurePerformer(ctx context.Context, name string) (string, error)
- func (c *Client) EnsureStudio(ctx context.Context, name string) (string, error)
- func (c *Client) EnsureTag(ctx context.Context, name string) (string, error)
- func (c *Client) Execute(ctx context.Context, query string, variables map[string]any) (json.RawMessage, error)
- func (c *Client) Fetch(ctx context.Context, url string, w io.Writer) (contentType string, n int64, err error)
- func (c *Client) FindAllScenes(ctx context.Context, filter SceneFilter, progress Progress) ([]Scene, error)
- func (c *Client) FindDuplicateScenes(ctx context.Context, distance int, durationDiff float64) ([][]Scene, error)
- func (c *Client) FindFile(ctx context.Context, id string) (file *File, found bool, err error)
- func (c *Client) FindFileByPath(ctx context.Context, path string) (file *File, found bool, err error)
- func (c *Client) FindJob(ctx context.Context, id string) (job *Job, found bool, err error)
- func (c *Client) FindPerformer(ctx context.Context, name string) (id string, found bool, err error)
- func (c *Client) FindPerformerByID(ctx context.Context, id string) (performer *Performer, found bool, err error)
- func (c *Client) FindPerformerByStashID(ctx context.Context, endpoint, stashID string) (id string, found bool, err error)
- func (c *Client) FindPerformers(ctx context.Context, filter PerformerFilter, page, perPage int) ([]Performer, int, error)
- func (c *Client) FindSavedFilter(ctx context.Context, mode FilterMode, name string) (filter *SavedFilter, found bool, err error)
- func (c *Client) FindScene(ctx context.Context, id string) (scene *Scene, found bool, err error)
- func (c *Client) FindSceneByHash(ctx context.Context, algorithm, hash string) (scene *Scene, found bool, err error)
- func (c *Client) FindScenes(ctx context.Context, filter SceneFilter, page, perPage int) ([]Scene, int, error)
- func (c *Client) FindScenesByPathRegex(ctx context.Context, pattern string, page, perPage int) ([]Scene, int, error)
- func (c *Client) FindStudio(ctx context.Context, name string) (id string, found bool, err error)
- func (c *Client) FindStudioByID(ctx context.Context, id string) (studio *Studio, found bool, err error)
- func (c *Client) FindTag(ctx context.Context, name string) (id string, found bool, err error)
- func (c *Client) FindTagByAlias(ctx context.Context, alias string) (id string, found bool, err error)
- func (c *Client) FindTagByID(ctx context.Context, id string) (tag *Tag, found bool, err error)
- func (c *Client) GeneralConfig(ctx context.Context, fields ...string) (map[string]any, error)
- func (c *Client) GenerateAPIKey(ctx context.Context) (string, error)
- func (c *Client) InstallPackages(ctx context.Context, t PackageType, specs ...PackageSpec) (jobID string, err error)
- func (c *Client) InstalledPackages(ctx context.Context, t PackageType) ([]Package, error)
- func (c *Client) InterfaceConfig(ctx context.Context, fields ...string) (map[string]any, error)
- func (c *Client) JobQueue(ctx context.Context) ([]Job, error)
- func (c *Client) LatestVersion(ctx context.Context) (shortHash, url string, err error)
- func (c *Client) LibraryStats(ctx context.Context) (LibraryStats, error)
- func (c *Client) Logs(ctx context.Context) ([]LogEntry, error)
- func (c *Client) MergePerformers(ctx context.Context, destinationID string, sourceIDs []string, ...) error
- func (c *Client) MergeScenes(ctx context.Context, destinationID string, sourceIDs []string, ...) error
- func (c *Client) MergeTags(ctx context.Context, destinationID string, sourceIDs []string, ...) error
- func (c *Client) MetadataAutoTag(ctx context.Context, opts AutoTagOptions) (jobID string, err error)
- func (c *Client) MetadataClean(ctx context.Context, opts CleanOptions) (jobID string, err error)
- func (c *Client) MetadataGenerate(ctx context.Context, opts GenerateOptions) (jobID string, err error)
- func (c *Client) MetadataIdentify(ctx context.Context, opts IdentifyOptions) (jobID string, err error)
- func (c *Client) MetadataScan(ctx context.Context, opts ScanOptions) (jobID string, err error)
- func (c *Client) Migrate(ctx context.Context, backupPath string) error
- func (c *Client) MigrateBlobs(ctx context.Context, deleteOld bool) (jobID string, err error)
- func (c *Client) MigrateHashNaming(ctx context.Context) (jobID string, err error)
- func (c *Client) MigrateSceneScreenshots(ctx context.Context, opts ScreenshotMigration) (jobID string, err error)
- func (c *Client) MoveFiles(ctx context.Context, fileIDs []string, to MoveTarget) error
- func (c *Client) OptimiseDatabase(ctx context.Context) (jobID string, err error)
- func (c *Client) PackageSources(ctx context.Context, t PackageType) ([]PackageSource, error)
- func (c *Client) Ping(ctx context.Context) error
- func (c *Client) PluginSettings(ctx context.Context, pluginID string) (map[string]any, error)
- func (c *Client) Plugins(ctx context.Context) ([]Plugin, error)
- func (c *Client) ReloadPlugins(ctx context.Context) error
- func (c *Client) RemoveScenePerformers(ctx context.Context, performerIDs []string, sceneIDs ...string) error
- func (c *Client) RemoveSceneTags(ctx context.Context, tagIDs []string, sceneIDs ...string) error
- func (c *Client) SaveFilter(ctx context.Context, filter SavedFilter) (string, error)
- func (c *Client) SaveSceneFilter(ctx context.Context, name string, filter SceneFilter, find *FindFilter) (string, error)
- func (c *Client) SavedFilters(ctx context.Context, mode FilterMode) ([]SavedFilter, error)
- func (c *Client) SceneFilterCriteria(ctx context.Context, filter SceneFilter) (map[string]any, error)
- func (c *Client) ScenePaths(ctx context.Context, id string) (ScenePaths, error)
- func (c *Client) ScrapeMultiScenes(ctx context.Context, endpoint string, sceneIDs []string) ([][]ScrapedScene, error)
- func (c *Client) ScrapePerformers(ctx context.Context, endpoint, query string) ([]ScrapedPerformer, error)
- func (c *Client) ScrapeSceneByID(ctx context.Context, endpoint, sceneID string) ([]ScrapedScene, error)
- func (c *Client) ScrapeScenes(ctx context.Context, endpoint, query string) ([]ScrapedScene, error)
- func (c *Client) ServerVersion(ctx context.Context) (ServerVersion, error)
- func (c *Client) SetFingerprints(ctx context.Context, fileID string, fingerprints []Fingerprint) error
- func (c *Client) SetPluginsEnabled(ctx context.Context, enabled map[string]bool) error
- func (c *Client) SetPrimaryFile(ctx context.Context, sceneID, fileID string) error
- func (c *Client) SetSceneStashIDs(ctx context.Context, sceneID string, ids []StashID) error
- func (c *Client) SetStashBoxes(ctx context.Context, boxes []StashBoxConfig) error
- func (c *Client) StashBoxBatchTag(ctx context.Context, target BatchTagTarget, opts BatchTagOptions) (jobID string, err error)
- func (c *Client) StashBoxConfigs(ctx context.Context) ([]StashBoxConfig, error)
- func (c *Client) StashBoxes(ctx context.Context) ([]StashBox, error)
- func (c *Client) StopAllJobs(ctx context.Context) error
- func (c *Client) StopJob(ctx context.Context, jobID string) error
- func (c *Client) Studios(ctx context.Context, page, perPage int) ([]Studio, int, error)
- func (c *Client) SubmitFingerprints(ctx context.Context, endpoint string, sceneIDs ...string) (ok bool, err error)
- func (c *Client) SubmitPerformerDraft(ctx context.Context, performerID, endpoint string) (draftID string, err error)
- func (c *Client) SubmitSceneDraft(ctx context.Context, sceneID, endpoint string) (draftID string, err error)
- func (c *Client) Supports(ctx context.Context, field string) (bool, error)
- func (c *Client) SystemStatus(ctx context.Context) (SystemStatus, error)
- func (c *Client) Tags(ctx context.Context, page, perPage int) ([]Tag, int, error)
- func (c *Client) UninstallPackages(ctx context.Context, t PackageType, specs ...PackageSpec) (jobID string, err error)
- func (c *Client) UpdatePackages(ctx context.Context, t PackageType, specs ...PackageSpec) (jobID string, err error)
- func (c *Client) UpdatePerformer(ctx context.Context, update PerformerUpdate) error
- func (c *Client) UpdateScene(ctx context.Context, update SceneUpdate) error
- func (c *Client) UpdateStudio(ctx context.Context, in StudioInput) error
- func (c *Client) UpdateTag(ctx context.Context, in TagInput) error
- func (c *Client) ValidateStashBox(ctx context.Context, endpoint, apiKey string) (valid bool, status string, err error)
- func (c *Client) Version(ctx context.Context) (string, error)
- type Conflict
- type DLNAIP
- type DLNAStatus
- type DeleteOptions
- type FieldPolicy
- type File
- type FilterMode
- type FindFilter
- type Fingerprint
- type Gallery
- type GenerateOptions
- type GraphQLError
- type Group
- type HTTPError
- type IdentifyOptions
- type Job
- type JobStatus
- type LibraryStats
- type LogEntry
- type MergeOptions
- type MoveTarget
- type Option
- type Package
- type PackageSource
- type PackageSpec
- type PackageType
- type ParsedFilename
- type Performer
- type PerformerFilter
- type PerformerInput
- type PerformerUpdate
- type Plugin
- type Progress
- type SavedFilter
- type ScanOptions
- type Scene
- type SceneFilter
- type SceneGroup
- type ScenePaths
- type SceneUpdate
- type ScrapedPerformer
- type ScrapedScene
- type ScrapedStudio
- type ScrapedTag
- type ScreenshotMigration
- type ServerVersion
- type StashBox
- type StashBoxConfig
- type StashID
- type Studio
- type StudioInput
- type SystemState
- type SystemStatus
- type Tag
- type TagInput
- type Tier
- type UnionPolicy
Examples ¶
Constants ¶
const DefaultMaxResponseBytes = 50 << 20 // 50 MiB
DefaultMaxResponseBytes caps how much of a response is read. A full page of scenes with metadata is large, but a runaway server should not be able to exhaust the caller's memory.
const FileFields = `` /* 201-byte string literal not displayed */
FileFields is the selection set File decodes from, exported for the same reason as SceneFields.
const ManifestSuffix = ".manifest.json"
ManifestSuffix is appended to a backup's filename to name its manifest.
const PerformerFields = `` /* 293-byte string literal not displayed */
PerformerFields is the selection the performer queries use. Exported for the same reason SceneFields is: a caller writing its own query through Client.Execute can drop it in and decode the result into Performer.
const SQLiteMagic = "SQLite format 3\x00"
SQLiteMagic opens every SQLite 3 database file.
const SceneFields = `` /* 588-byte string literal not displayed */
SceneFields is the selection set Scene decodes from, exported so a caller writing its own query with Client.Execute fills the same type completely rather than maintaining a parallel field list that drifts.
query := `query { sceneWall(q: "beach") { ` + stash.SceneFields + ` } }`
Every field here must exist on the oldest supported server — see docs/design.md.
const StudioFields = `` /* 132-byte string literal not displayed */
StudioFields is the selection the studio queries use. Exported for the same reason SceneFields is.
child_studios is deliberately absent: a studio with many children would carry them all on every query, and a caller that wants the tree can ask for it. parent_studio is one level deep for the same reason.
const TagFields = `` /* 147-byte string literal not displayed */
TagFields is the selection the tag queries use.
Variables ¶
var ( // ErrNotSQLite means the file does not begin with [SQLiteMagic]: // whatever was downloaded, it is not a database. A login page or a // proxy's error page answering the download URL looks like this. ErrNotSQLite = errors.New("stash: not a SQLite database") // ErrTruncatedBackup means the file is a SQLite database whose header // describes more bytes than the file has. A transfer that dropped // mid-stream looks like this, and the prefix it leaves opens fine. ErrTruncatedBackup = errors.New("stash: truncated backup") // ErrBlobsNotVerifiable means [BackupOptions.IncludeBlobs] was set: a // server keeping blobs on disk then answers with a zip of database and // blobs, which is not a SQLite file and cannot be checked. ErrBlobsNotVerifiable = errors.New("stash: a backup with blobs cannot be verified") )
var ( // ErrPerformerNotFound means no performer has the requested name. Stash // matches names exactly, so this is usually a typo or stray whitespace. ErrPerformerNotFound = errors.New("stash: no such performer") // ErrStudioNotFound means no studio has the requested name. ErrStudioNotFound = errors.New("stash: no such studio") // ErrTagNotFound means no tag has the requested name. ErrTagNotFound = errors.New("stash: no such tag") )
Sentinel errors. Stash answers "no such performer" with an empty result set rather than an error, so a typo in a filter is otherwise indistinguishable from a filter that legitimately matched nothing.
var ErrNotFound = errors.New("stash: not found")
ErrNotFound is what Client.Fetch returns for a 404, so a caller can tell it from the other failures without inspecting a status code. Stash generates sprites, previews and covers lazily, which makes a missing one an ordinary state of a scene rather than a reason to stop.
Functions ¶
func Union ¶ added in v0.10.0
func Union(dst Scene, srcs []Scene, p UnionPolicy) (SceneUpdate, []Conflict)
Union computes the values a merge into dst needs so that nothing the sources know is lost, since sceneMerge on its own keeps only dst's metadata. The result is partial: only fields that would change are set, and it is the zero SceneUpdate when nothing would. Its ID is left empty; Client.MergeScenes addresses it at the destination.
Lists deduplicate by ID, stash IDs by (endpoint, id), and dst's order comes first. When the scenes disagree on the ID for one endpoint, dst wins over a source and an earlier source over a later one; every ID dropped that way is returned as a Conflict.
The function is pure: it neither reads nor writes Stash, and does not modify its arguments.
func VerifySQLite ¶ added in v0.10.0
VerifySQLite reports whether r, of exactly size bytes, is a whole SQLite database: it has to begin with SQLiteMagic, and the page count in its header has to agree with size.
The page-count comparison is skipped, rather than failed, when the header's count is not authoritative. SQLite only trusts it while the change counter at offset 24 matches the version-valid-for number at offset 92, and a database last written by a very old library leaves it zero. The magic is still required in that case; the length simply cannot be confirmed from the header alone.
Types ¶
type APIError ¶
type APIError struct {
Errors []GraphQLError
}
APIError is one or more errors returned in a GraphQL response body. The request itself succeeded at the HTTP level.
Inspect Errors to distinguish a schema mismatch ("Cannot query field …") from an authentication failure, rather than matching on error text.
type AutoTagOptions ¶ added in v0.8.0
AutoTagOptions configures Client.MetadataAutoTag.
Each list names what to match against, and "*" means all of that kind — which is what the UI's button sends. Empty lists match nothing, so a call with none set is a job that does nothing.
type BackupManifest ¶ added in v0.10.0
type BackupManifest struct {
// File is the server's name for the backup, and the filename it was
// saved under.
File string `json:"file"`
Bytes int64 `json:"bytes"`
SHA256 string `json:"sha256"`
Server BackupServer `json:"server"`
}
BackupManifest records what Client.DownloadVerifiedBackup wrote and which server it came from. It is written beside the backup as JSON.
type BackupOptions ¶ added in v0.7.0
type BackupOptions struct {
// IncludeBlobs asks for blob data — covers, and the rest of what Stash
// stores as blobs — to be included.
//
// It changes nothing on a server that keeps blobs in the database, which
// is what an empty blobsPath in the configuration means: there they are
// part of the file whatever this is set to. It matters on a server
// storing blobs on the filesystem, where a backup without them is not a
// backup of everything.
IncludeBlobs bool
}
BackupOptions configures a database backup.
type BackupServer ¶ added in v0.10.0
type BackupServer struct {
Version string `json:"version"`
Schema int `json:"schema"`
OS string `json:"os"`
DatabasePath string `json:"database_path"`
SceneCount int `json:"scene_count"`
}
BackupServer is what a manifest records about the server a backup came from. The database path in particular: a file called local.sqlite says nothing about which machine or which Stash it belongs to.
type BatchTagOptions ¶ added in v0.9.0
type BatchTagOptions struct {
// Endpoint is the stash-box GraphQL URL to match against. Required —
// Stash will not guess even when only one box is configured.
Endpoint string
// IDs restricts the job to these entities. Empty means every one of that
// kind, which on a large library is a long job.
IDs []string
// Names asks the box about these names instead of about entities.
Names []string
// Refresh re-queries entities that already carry a stash id for this
// endpoint. Without it Stash only visits the ones with none, which is
// what makes a repeat run cheap.
Refresh bool
// ExcludeFields names the fields the job must not write, so a library
// that trusts its own data more than the box's can keep it. The names are
// the box's, not Stash's: "name", "aliases", "description", "image".
ExcludeFields []string
// CreateParent creates a missing parent studio rather than leaving the
// studio unparented. Only meaningful for [BatchTagStudios].
CreateParent bool
}
BatchTagOptions selects what a batch tag job covers and how much of it it is allowed to change.
Either IDs or Names, not both: ids name entities the library already has, where names ask the box to find something the library only knows by name.
type BatchTagTarget ¶ added in v0.9.0
type BatchTagTarget string
BatchTagTarget is what a stash-box batch tag job works on.
const ( // BatchTagPerformers matches performers against the box. BatchTagPerformers BatchTagTarget = "stashBoxBatchPerformerTag" // BatchTagStudios matches studios. BatchTagStudios BatchTagTarget = "stashBoxBatchStudioTag" // BatchTagTags matches tags. BatchTagTags BatchTagTarget = "stashBoxBatchTagTag" )
type Caption ¶ added in v0.6.0
type Caption struct {
LanguageCode string `json:"language_code"`
CaptionType string `json:"caption_type"`
}
Caption is one subtitle track Stash has attached to a scene. Stash discovers these by scanning for sidecar files next to the video; they are read-only in GraphQL, so a caption cannot be attached over the API — only written to disk and picked up by Client.MetadataScan.
LanguageCode is the bare ISO 639 subtag Stash parsed off the filename (`clip.pt.srt` gives "pt"). Stash parses it with language.ParseBase and silently attaches nothing for anything it cannot parse, so a regional tag never appears here — a file named `clip.pt-BR.srt` is simply not a caption as far as Stash is concerned.
type CleanOptions ¶ added in v0.8.0
type CleanOptions struct {
// Paths restricts the clean to these library paths.
Paths []string
// DryRun makes Stash report what it would remove without removing it.
// Worth using first: clean deletes database records for files it cannot
// find, and a library on a disconnected drive looks exactly like a
// library whose files were deleted.
DryRun bool
// IgnoreZipFileContents skips files inside zips.
IgnoreZipFileContents bool
}
CleanOptions configures Client.MetadataClean.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client talks to one Stash server. It is safe for concurrent use.
func NewClient ¶
NewClient returns a client for the Stash server at baseURL, which is the server root ("http://localhost:9999") — "/graphql" is appended.
func (*Client) AddScenePerformers ¶ added in v0.7.0
func (c *Client) AddScenePerformers(ctx context.Context, performerIDs []string, sceneIDs ...string) error
AddScenePerformers adds performers to scenes, leaving the ones they already have alone — the same reason Client.AddSceneTags exists.
func (*Client) AddSceneTags ¶ added in v0.7.0
AddSceneTags adds tags to scenes, leaving the tags they already have alone.
This is not what SceneUpdate.TagIDs does. That field *replaces* a scene's tags, so adding one through it means reading the current list, appending, and writing it back — which drops any tag added in between, and drops all of them if the read is skipped. Stash's bulk update takes an ADD mode instead, and applies it to every scene named in one request.
func (*Client) AllowDLNAIP ¶ added in v0.9.0
AllowDLNAIP lets one address use the DLNA service for the given duration, or until the server restarts when d is zero. It is a temporary grant on top of the configured whitelist, not an addition to it.
The address is a bare IP — the form Client.DLNAStatus reports in RecentIPAddresses, which is where a device that has just tried and been refused shows up.
func (*Client) AnonymiseDatabase ¶ added in v0.9.0
AnonymiseDatabase writes a copy of the database with every name, path, URL and free-text field stripped, and returns the path it wrote.
This is what a bug report attaches: it keeps the shape of a library — the counts, the relationships, the schema — and none of what it is a library of. The path is on the *server's* filesystem; Client.DownloadAnonymisedDatabase fetches the copy instead, which is usually the point.
It is a copy. Nothing about the live database changes.
func (*Client) AssignFile ¶ added in v0.9.0
AssignFile attaches an existing file to a scene, moving it from whatever scene held it.
This is how a file that Stash matched to the wrong scene is put right without deleting anything.
func (*Client) AvailablePackages ¶ added in v0.7.0
func (c *Client) AvailablePackages(ctx context.Context, t PackageType, sourceURL string) ([]Package, error)
AvailablePackages returns what the index at sourceURL offers.
The server fetches that index over the internet when the call is made, so this is slower than it looks and fails when the server is offline — not when this program is.
func (*Client) BackupDatabase ¶ added in v0.7.0
func (c *Client) BackupDatabase(ctx context.Context, opts BackupOptions) (serverPath string, err error)
BackupDatabase asks the server to write a backup of its database and returns the path it wrote.
That path is on the *server's* filesystem, in the server's own notation: a Windows-hosted Stash answers with something like `C:\Users\you\.stash\local.sqlite.85.20260101_000000`, which the calling machine has no way to open. Where it lands is the server's backupDirectoryPath setting, or the database's own directory when that is unset.
A backup that stays on the machine being backed up is a limited kind of insurance. Client.DownloadBackup fetches one instead.
func (*Client) ClearAPIKey ¶ added in v0.9.0
ClearAPIKey removes the server's API key without issuing a new one. Clients authenticating with it — this one included — stop working; a session login still does.
func (*Client) ClearPerformerFields ¶ added in v0.8.0
ClearPerformerFields empties the named fields, which PerformerUpdate cannot: it omits what is unset so that a partial update is safe, and that makes "" indistinguishable from "leave it".
Names are the PerformerUpdateInput field names — "birthdate", "details", "alias_list", "measurements". A name Stash does not know fails the whole mutation rather than being ignored.
func (*Client) ClearSceneFields ¶ added in v0.8.0
ClearSceneFields empties the named fields, which SceneUpdate cannot: it omits what is unset so that a partial update is safe, and that makes "" and "leave it alone" the same request.
Names are the SceneUpdateInput field names — "title", "details", "date", "code". A name Stash does not know fails the whole mutation rather than being ignored.
func (*Client) ClearStudioFields ¶ added in v0.8.0
ClearStudioFields empties the named fields, which StudioInput cannot.
func (*Client) ClearTagFields ¶ added in v0.8.0
ClearTagFields empties the named fields, which TagInput cannot.
func (*Client) Configuration ¶ added in v0.6.0
Configuration returns the server's whole configuration tree as decoded JSON. Stash's ConfigResult is a large, version-dependent shape whose sections gain and lose fields between releases, so this deliberately does not model it: a typed struct would fail the entire query the first time a field it names is dropped, which is the failure mode this library exists to spare callers.
Prefer Client.PluginSettings when that is what you want — it asks for one section rather than the whole tree.
func (*Client) ConfigureGeneral ¶ added in v0.9.0
ConfigureGeneral writes the given general settings and leaves the rest alone: Stash applies only the keys present in the input.
Keys are the ConfigGeneralInput field names ("logLevel", "maxSessionAge", "previewSegments", …), unmodelled here for the reason Client.Configuration gives.
Two things this cannot do gently. A list-valued field is *replaced* rather than extended — sending one entry of "stashes" makes it the only library path Stash has, and Client.SetStashBoxes exists because that same trap costs API keys. And several of these are how the server reaches its own data: point databasePath or generatedPath somewhere new and Stash starts afresh there rather than moving anything. Read a field before writing it.
func (*Client) ConfigureInterface ¶ added in v0.7.0
ConfigureInterface writes the given interface settings and leaves the rest alone, the same way Client.UpdateScene does — Stash applies only the keys present in the input.
Keys are the ConfigInterfaceInput field names ("javascript", "javascriptEnabled", "css", "cssEnabled", …). They are not modelled here for the reason Client.Configuration gives: the section gains and loses fields between releases, and a struct naming them all would fail the whole mutation the first time one went away.
Custom JavaScript in particular runs in every browser that opens this Stash. Read it before writing it, and preserve what is there.
func (*Client) CreatePerformer ¶
CreatePerformer creates a performer and returns its ID.
func (*Client) CreatePerformerFrom ¶ added in v0.7.0
CreatePerformerFrom creates a performer with full details and returns its ID. Client.CreatePerformer is the same call for a name alone.
func (*Client) CreateStudio ¶
CreateStudio creates a studio and returns its ID.
func (*Client) CreateStudioFrom ¶ added in v0.8.0
CreateStudioFrom creates a studio with full details and returns its ID. Client.CreateStudio is the same call for a name alone.
func (*Client) CreateTagFrom ¶ added in v0.8.0
CreateTagFrom creates a tag with full details and returns its ID. Client.CreateTag is the same call for a name alone.
func (*Client) DLNAStatus ¶ added in v0.9.0
func (c *Client) DLNAStatus(ctx context.Context) (DLNAStatus, error)
DLNAStatus reports whether the DLNA service is running, which addresses have been asking for it, and which are allowed to.
func (*Client) DeleteFiles ¶ added in v0.9.0
DeleteFiles deletes the files from disk and removes their records.
Permanent, and it says so plainly because the name does not distinguish itself from Client.DestroyFiles: this one is "delete these videos". Stash offers both mutations and they are not synonyms — the other keeps the file.
func (*Client) DeletePerformer ¶ added in v0.8.0
DeletePerformer removes a performer.
The performer's scenes are not touched; they simply lose the credit. There is no undo, and no confirmation — Stash deletes on being asked.
func (*Client) DeletePerformers ¶ added in v0.8.0
DeletePerformers removes several performers in one request.
All or nothing: Stash checks every id first, and one that does not exist fails the whole call with nothing deleted. That matters after a merge, which has already removed its sources — passing them again deletes none of the rest.
func (*Client) DeleteScene ¶ added in v0.9.0
DeleteScene removes one scene.
With a zero DeleteOptions this removes the database record only and the video stays on disk. Set DeleteFile and the video is deleted, permanently.
func (*Client) DeleteScenes ¶ added in v0.9.0
DeleteScenes removes several scenes in one request, on the same terms as Client.DeleteScene.
func (*Client) DeleteStudio ¶ added in v0.8.0
DeleteStudio removes a studio. Its scenes are not touched; they lose the studio.
func (*Client) DeleteStudios ¶ added in v0.8.0
DeleteStudios removes several studios in one request. All or nothing: one id that does not exist fails the call with nothing deleted.
func (*Client) DeleteTag ¶ added in v0.8.0
DeleteTag removes a tag. Scenes carrying it simply lose it.
func (*Client) DeleteTags ¶ added in v0.8.0
DeleteTags removes several tags in one request. All or nothing: one id that does not exist fails the call with nothing deleted.
func (*Client) DestroyFiles ¶ added in v0.9.0
DestroyFiles removes the files' records from the database and leaves the videos on disk.
This is the reversible one, and the name is the opposite way round from what that suggests: Stash's own description is "deletes file entries from the database without deleting the files from the filesystem". A later scan finds the files again and re-adds them, so this un-does itself unless the files are moved out of a library path first.
Client.DeleteFiles is the one that deletes the video.
func (*Client) DestroySavedFilter ¶ added in v0.7.0
DestroySavedFilter deletes a saved filter by id.
func (*Client) DisableDLNA ¶ added in v0.9.0
DisableDLNA stops the DLNA service for the given duration, or until it is enabled again when d is zero.
func (*Client) DisallowDLNAIP ¶ added in v0.9.0
DisallowDLNAIP revokes a grant made by Client.AllowDLNAIP. It says nothing about an address in the configured whitelist, which this cannot reach.
func (*Client) DownloadAnonymisedDatabase ¶ added in v0.9.0
func (c *Client) DownloadAnonymisedDatabase(ctx context.Context, w io.Writer) (name string, written int64, err error)
DownloadAnonymisedDatabase anonymises the database and streams the copy to w, returning the server's name for it and the number of bytes written.
The caveats are Client.DownloadBackup's, for the same reasons: the transfer is not bounded by WithMaxResponseBytes, the HTTP client's timeout covers the whole of it, and the server leaves its temporary copy behind until it restarts.
func (*Client) DownloadBackup ¶ added in v0.7.0
func (c *Client) DownloadBackup(ctx context.Context, opts BackupOptions, w io.Writer) (name string, written int64, err error)
DownloadBackup backs up the server's database and streams it to w, returning the server's name for the backup and the number of bytes written.
The server writes the backup to a temporary file and serves that over HTTP. It does not delete the temporary file once the download finishes — it clears that directory on restart — so a program backing up on a schedule leaves copies on the server's temp volume.
Nothing here is bounded by WithMaxResponseBytes. That cap protects a caller decoding a GraphQL response into memory; this is a stream to a writer the caller chose, of a database that is hundreds of megabytes on a real library. For the same reason the HTTP client's timeout matters more than usual: it covers the whole transfer, not just the response headers, and the default client's is 30 seconds. Pass one with no timeout (see WithHTTPClient) and bound the transfer with ctx instead.
A short write to w aborts the download and is returned as-is, so a full disk does not leave a truncated file looking like a complete one.
func (*Client) DownloadFFMpeg ¶ added in v0.9.0
DownloadFFMpeg has the server fetch ffmpeg and ffprobe for its own platform and put them beside its configuration, returning the id of the job doing it.
The download is the *server's*, from the internet, and it is what a Stash with no system ffmpeg needs before it can generate anything. A server that already found ffmpeg on its PATH does not need this, and running it anyway gives Stash its own copy to prefer.
func (*Client) DownloadVerifiedBackup ¶ added in v0.10.0
func (c *Client) DownloadVerifiedBackup(ctx context.Context, opts BackupOptions, dir string) (BackupManifest, error)
DownloadVerifiedBackup backs up the server's database into dir, checks that what arrived is a whole SQLite database, and writes a manifest beside it.
The download lands in `<name>.part`, where name is the server's name for the backup, and is renamed once VerifySQLite accepts it; the manifest goes to `<name>.manifest.json`. Any failure removes the partial file and returns the error, so the directory never holds a half-backup under a usable name. ErrNotSQLite and ErrTruncatedBackup are the two ways the transfer itself can be bad.
The server is described before the backup is taken, and one that is not SystemStatus.Ready is refused: a database mid-migration is the one moment the file on disk is least worth having. Everything Client.DownloadBackup says about timeouts applies here too. Blobs cannot be included — see ErrBlobsNotVerifiable.
func (*Client) EnableDLNA ¶ added in v0.9.0
EnableDLNA starts the DLNA service for the given duration, or until it is disabled again when d is zero.
None of this touches the configuration: a temporary enable is forgotten on restart, when the dlnaEnabled setting decides again.
func (*Client) EnsurePerformer ¶
EnsurePerformer returns the ID of a performer with this name, creating it if absent.
func (*Client) EnsureStudio ¶
EnsureStudio returns the ID of a studio with this name, creating it if absent.
func (*Client) EnsureTag ¶
EnsureTag returns the ID of a tag with this name, creating it if neither the name nor an alias matches.
func (*Client) Execute ¶
func (c *Client) Execute(ctx context.Context, query string, variables map[string]any) (json.RawMessage, error)
Execute runs a raw GraphQL query or mutation and returns the `data` object.
Exported so callers can reach parts of the schema this package does not wrap, without hand-rolling the transport, auth and error handling again.
func (*Client) Fetch ¶ added in v0.7.0
func (c *Client) Fetch(ctx context.Context, url string, w io.Writer) (contentType string, n int64, err error)
Fetch streams one of the server's plain HTTP resources to w and returns its content type and length.
Scenes carry URLs to things GraphQL will not hand over as data — the sprite sheet, its WebVTT, the cover, the stream — and those routes want the same credential as /graphql. This applies it, and re-roots the URL the way Client.DownloadBackup does, so a URL the server built from a proxied request still resolves to the address this client was given.
url may be absolute or a path. As with the backup download, neither WithMaxResponseBytes nor a short HTTP client timeout suits a stream; bound it with ctx.
func (*Client) FindAllScenes ¶
func (c *Client) FindAllScenes(ctx context.Context, filter SceneFilter, progress Progress) ([]Scene, error)
FindAllScenes pages through every scene matching the filter.
On a large library this is a long operation — a 61k-scene instance takes several minutes and hundreds of requests. Pass a non-nil progress to report it, and a cancellable context: cancellation returns what was collected so far alongside ctx.Err().
Example ¶
c := NewClient("http://localhost:9999", WithAPIKey("key"))
scenes, err := c.FindAllScenes(context.Background(), SceneFilter{StudioName: "Example"},
func(fetched, total int) { fmt.Printf("\r%d/%d", fetched, total) })
if err != nil {
return
}
fmt.Println(len(scenes))
func (*Client) FindDuplicateScenes ¶ added in v0.9.0
func (c *Client) FindDuplicateScenes(ctx context.Context, distance int, durationDiff float64) ([][]Scene, error)
FindDuplicateScenes returns groups of scenes Stash considers the same content, matched on the perceptual hash of the video rather than on any metadata. Each group holds two or more scenes; a library with no duplicates returns none.
distance is the hamming distance allowed between two phashes. 0 demands identical hashes, which finds the same encode stored twice. 4 is the useful setting and catches re-encodes and resolution changes. Past 8 the matches stop being trustworthy.
durationDiff bounds how far apart two scenes' runtimes may be, in seconds, and is the strong filter of the two: phash collides across unrelated videos often enough to matter, but a collision that also agrees on length to within a second rarely does. Pass 0 to demand equal durations, or a negative value to leave duration out of it.
The whole result arrives in one response, and every scene in it carries the full selection set — on a large library that is megabytes. There is no paged form of this query: Stash computes the grouping in one pass and has nowhere to hold it between calls.
func (*Client) FindFileByPath ¶ added in v0.9.0
func (c *Client) FindFileByPath(ctx context.Context, path string) (file *File, found bool, err error)
FindFileByPath looks up one file by its path on disk.
The path must be exactly as Stash stored it, separators included — on a Windows server that means backslashes. found is false when no such file is known, which is not an error.
func (*Client) FindJob ¶ added in v0.6.0
FindJob returns one job by id. found is false when the job has aged out of the queue, which is not an error — Stash drops finished jobs after a while, so a poll that starts too late sees the same thing as a poll for a job that never existed.
func (*Client) FindPerformer ¶
FindPerformer returns the ID of the performer with this exact name.
func (*Client) FindPerformerByID ¶ added in v0.8.0
func (c *Client) FindPerformerByID(ctx context.Context, id string) (performer *Performer, found bool, err error)
FindPerformerByID returns one performer with everything Stash stores about them. found is false when there is no such performer, which is not an error.
func (*Client) FindPerformerByStashID ¶ added in v0.7.0
func (c *Client) FindPerformerByStashID(ctx context.Context, endpoint, stashID string) (id string, found bool, err error)
FindPerformerByStashID returns the ID of the performer carrying this stash-box id.
This is the identity check worth making before creating anything. Client.FindPerformer matches on a name, and a name is neither unique nor stable — two performers share one, one performer changes theirs, and a scraper writes it with different punctuation. A stash-box id is the same string forever.
func (*Client) FindPerformers ¶ added in v0.8.0
func (c *Client) FindPerformers(ctx context.Context, filter PerformerFilter, page, perPage int) ([]Performer, int, error)
FindPerformers returns one page of performers plus the total count. Pages are 1-based and sorted by name, so paging is stable.
func (*Client) FindSavedFilter ¶ added in v0.7.0
func (c *Client) FindSavedFilter(ctx context.Context, mode FilterMode, name string) (filter *SavedFilter, found bool, err error)
FindSavedFilter returns the saved filter with this name in this list.
Stash does not require names to be unique, so this returns the first match and a caller creating filters should check before creating a second one of the same name — which is what Client.SaveSceneFilter does.
func (*Client) FindScene ¶
FindScene returns one scene by ID. found is false when no scene has that ID, which is not an error.
func (*Client) FindSceneByHash ¶ added in v0.9.0
func (c *Client) FindSceneByHash(ctx context.Context, algorithm, hash string) (scene *Scene, found bool, err error)
FindSceneByHash finds the scene holding a file with this fingerprint.
Exact, unlike matching on a title or a path: the hash names one file. Which algorithm to pass depends on what the library has — "oshash" is computed on every scan, "phash" only when generated.
func (*Client) FindScenes ¶
func (c *Client) FindScenes(ctx context.Context, filter SceneFilter, page, perPage int) ([]Scene, int, error)
FindScenes returns one page of scenes plus the total count matching the filter. Pages are 1-based and sorted by path, so paging is stable.
A filter naming a performer or studio that does not exist returns ErrPerformerNotFound or ErrStudioNotFound rather than an empty page — otherwise a typo is indistinguishable from a genuine zero-result query.
func (*Client) FindScenesByPathRegex ¶ added in v0.9.0
func (c *Client) FindScenesByPathRegex(ctx context.Context, pattern string, page, perPage int) ([]Scene, int, error)
FindScenesByPathRegex finds scenes whose file path matches a regular expression, one page at a time, and reports the total match count.
The pattern is evaluated by the server, in Go's regexp syntax, against the full path. This is the call for questions a filter cannot ask — "which scenes still live under the old naming scheme" — where SceneFilter's PathContains only does a substring.
func (*Client) FindStudio ¶
FindStudio returns the ID of the studio with this exact name.
func (*Client) FindStudioByID ¶ added in v0.8.0
func (c *Client) FindStudioByID(ctx context.Context, id string) (studio *Studio, found bool, err error)
FindStudioByID returns one studio with everything Stash stores about it.
func (*Client) FindTagByAlias ¶
func (c *Client) FindTagByAlias(ctx context.Context, alias string) (id string, found bool, err error)
FindTagByAlias returns the ID of a tag carrying this alias. Stash treats aliases as first-class, so a tag can be present under a name the caller does not know — checking aliases before creating avoids duplicates.
func (*Client) FindTagByID ¶ added in v0.8.0
FindTagByID returns one tag with everything Stash stores about it.
func (*Client) GeneralConfig ¶ added in v0.9.0
GeneralConfig returns the named fields of the server's general configuration — the section holding library paths, the database and blob locations, ffmpeg settings, the log file and the rest of Settings > System.
The caller names the fields for the reason Client.InterfaceConfig gives: the section is large, it changes between releases, and one field the schema lacks fails the whole query rather than just that field.
cfg, err := c.GeneralConfig(ctx, "databasePath", "blobsPath", "logFile")
Client.StashBoxConfigs is the way to read stashBoxes: it is the one field in here that carries credentials, and it has a type of its own.
func (*Client) GenerateAPIKey ¶ added in v0.9.0
GenerateAPIKey replaces the server's API key and returns the new one.
There is exactly one key per Stash, so this invalidates the old one — and that includes the key this client is authenticating with, which stops working the moment the mutation returns. The new key does not apply itself: build a fresh client with it.
key, err := c.GenerateAPIKey(ctx) c = stash.NewClient(url, stash.WithAPIKey(key))
The returned key is a credential in a variable rather than in an error string, so nothing redacts it for you — WithAPIKey's scrubbing covers the key a client was built with, not one it has just been handed.
A server with authentication disabled — no username and password configured — refuses this: Stash will not hand out a key that would then be the only thing standing in front of the library, or not stand there at all.
func (*Client) InstallPackages ¶ added in v0.7.0
func (c *Client) InstallPackages(ctx context.Context, t PackageType, specs ...PackageSpec) (jobID string, err error)
InstallPackages installs packages and returns the id of the job doing it. It does not wait; follow the job with Client.FindJob.
Stash downloads each package from its source and unpacks it into the server's plugin or scraper directory. A package already installed is reinstalled at the index's current version, so this is also how an update is forced.
Requirements are not resolved: a package whose Requires names something absent installs anyway, and fails when it runs. Check Package.Requires first.
func (*Client) InstalledPackages ¶ added in v0.7.0
InstalledPackages returns what is installed, whether or not its source is still configured.
func (*Client) InterfaceConfig ¶ added in v0.7.0
InterfaceConfig returns the named fields of the server's interface configuration.
The caller names the fields because the section is large and changes between releases, and one field the schema lacks fails the whole query. A caller asking for what it is about to write cannot be broken by a field it does not use.
func (*Client) JobQueue ¶ added in v0.6.0
JobQueue returns every job Stash currently knows about, queued or running. Empty when the server is idle.
func (*Client) LatestVersion ¶ added in v0.9.0
LatestVersion returns the newest release Stash knows of, as a short commit hash and the URL to it.
The *server* fetches this from GitHub when the call is made. It therefore fails when the server has no route to the internet — which is not the same thing as this program having none — and it is slower than a local query. Nothing here compares it to Client.ServerVersion: the two are a tag and a hash, and only the server knows whether it is behind.
func (*Client) LibraryStats ¶ added in v0.9.0
func (c *Client) LibraryStats(ctx context.Context) (LibraryStats, error)
LibraryStats reports the server's own counts for the library.
This is the cheap way to size a library before doing anything with it: one query, answered from the database, where counting the same thing through Client.FindScenes would page through every scene.
func (*Client) Logs ¶ added in v0.9.0
Logs returns the server's recent log entries, newest first.
This is not the log file. Stash keeps a bounded in-memory ring of the last few hundred entries and serves that, so a server restarted since the event has nothing to say about it, and a busy one has already dropped it. For anything that must not be missed, read the file the server's logFile setting names — Client.GeneralConfig reports where that is.
There is no way to follow the log from here: Stash streams new entries over a GraphQL subscription, which is a websocket this package does not open.
func (*Client) MergePerformers ¶ added in v0.8.0
func (c *Client) MergePerformers(ctx context.Context, destinationID string, sourceIDs []string, values *PerformerUpdate) error
MergePerformers folds the source performers into the destination and deletes them, moving their scenes across.
values, when set, is applied to the destination as part of the merge — the place to keep a source's better name or birthdate, since the destination's own fields otherwise win and the sources are gone afterwards.
This is not reversible.
func (*Client) MergeScenes ¶ added in v0.9.0
func (c *Client) MergeScenes(ctx context.Context, destinationID string, sourceIDs []string, values *SceneUpdate, opts MergeOptions) error
MergeScenes folds the source scenes into the destination and deletes them, moving their files across.
The destination keeps its own metadata; values is applied to it as part of the merge, which is where a source's better title or date goes — afterwards the sources are gone and there is nothing left to copy from. Stash does not union metadata by itself, so values is the whole of what survives from a source: compute it before calling.
This deletes database records, not files on disk: the sources' files are reattached to the destination. Deleting a file is Client.DeleteScene with DeleteFile set, or Client.DeleteFiles.
Not reversible.
func (*Client) MergeTags ¶ added in v0.8.0
func (c *Client) MergeTags(ctx context.Context, destinationID string, sourceIDs []string, values *TagInput) error
MergeTags folds the source tags into the destination and deletes them, moving everything they were on across.
values, when set, is applied to the destination as part of the merge — the place to keep a source's better name or description, since the sources are gone afterwards. This is not reversible.
func (*Client) MetadataAutoTag ¶ added in v0.8.0
func (c *Client) MetadataAutoTag(ctx context.Context, opts AutoTagOptions) (jobID string, err error)
MetadataAutoTag starts an auto-tag job and returns its id.
Auto-tag attaches performers, studios and tags to scenes whose *path* contains their name. That is a guess about filenames, and it writes: on a library whose files are named after their content it is useful, and on one where a performer is called "Angel" it is not.
func (*Client) MetadataClean ¶ added in v0.8.0
MetadataClean starts a clean job and returns its id.
Clean removes the records of files that are no longer on disk. That is destructive and depends on the disk being readable at the time: an unmounted drive presents as a library whose files have all been deleted. Use DryRun first.
func (*Client) MetadataGenerate ¶ added in v0.8.0
func (c *Client) MetadataGenerate(ctx context.Context, opts GenerateOptions) (jobID string, err error)
MetadataGenerate starts a generate job and returns the id of the job doing it. It does not wait; follow it with Client.FindJob.
This is how a scene gets the sprite, cover or perceptual hash that other work needs and a plain scan does not produce.
func (*Client) MetadataIdentify ¶ added in v0.8.0
func (c *Client) MetadataIdentify(ctx context.Context, opts IdentifyOptions) (jobID string, err error)
MetadataIdentify starts an identify job and returns its id.
Identify is a *writing* task: it matches scenes against a stash-box and applies what it finds, according to the field rules configured on the server. Those rules decide whether it overwrites what is already there, and this call cannot see them — check them before starting one on a library whose metadata you care about.
func (*Client) MetadataScan ¶ added in v0.6.0
MetadataScan starts a library scan and returns the id of the job doing it. It does not wait: scanning is a long-running background task, and the id is how you follow it with Client.FindJob.
This is the only way to make Stash notice a file that appeared on disk. Captions in particular are read-only in GraphQL, so writing a sidecar next to a video and calling this is the entire mechanism for attaching a subtitle.
func (*Client) Migrate ¶ added in v0.9.0
Migrate runs the database schema migration a server in SystemNeedsMigration is waiting for, writing a copy of the old database to backupPath first. An empty backupPath skips that copy.
Three things make this unlike the rest of the package. It is *irreversible* — a migrated database cannot be opened by the older Stash that wrote it, which is what the backup is for. It runs synchronously rather than as a job, so it holds the request open for as long as the migration takes; on a large library that is minutes, and the default HTTP client gives up after thirty seconds while the server carries on regardless (pass one without a timeout via WithHTTPClient and bound it with ctx). And a server in this state answers almost nothing else: Client.SystemStatus and this are what work.
backupPath is a path on the *server's* filesystem, in the server's own notation.
func (*Client) MigrateBlobs ¶ added in v0.9.0
MigrateBlobs moves blob data — covers, images, and the rest — between the database and the filesystem, following whatever the blobsPath setting now says. It returns the id of the job doing it; follow it with Client.FindJob.
deleteOld removes each blob from where it came from once it has been written to where it is going. With it false the data exists in both places afterwards, which is the safe way round and the reason it is not the default: a migration that turns out to have gone wrong is then still undoable by putting the setting back.
func (*Client) MigrateHashNaming ¶ added in v0.9.0
MigrateHashNaming renames generated files — sprites, previews, covers — from the MD5 naming an old Stash used to the oshash naming it uses now, and returns the id of the job doing it.
Only a library that predates the change has anything to rename; on everything else the job runs and finds nothing. It is not reversible, and while it runs the generated files it has not reached yet are the ones the UI cannot find.
func (*Client) MigrateSceneScreenshots ¶ added in v0.9.0
func (c *Client) MigrateSceneScreenshots(ctx context.Context, opts ScreenshotMigration) (jobID string, err error)
MigrateSceneScreenshots reads the loose screenshot files an older Stash wrote next to its generated content and stores them as scene covers, returning the id of the job doing it.
This is the one-off that follows an upgrade past the release where covers stopped being files. A library that never had those files has nothing to migrate.
func (*Client) MoveFiles ¶ added in v0.9.0
MoveFiles moves files on disk and updates Stash to match.
This is a real move: the video changes place in the filesystem. Stash needs the destination to be inside a configured library path, or it will refuse rather than move a file somewhere it cannot see.
func (*Client) OptimiseDatabase ¶ added in v0.8.0
OptimiseDatabase starts a database optimisation job and returns its id.
func (*Client) PackageSources ¶ added in v0.7.0
func (c *Client) PackageSources(ctx context.Context, t PackageType) ([]PackageSource, error)
PackageSources returns the indexes the server is configured to install from. A PackageSpec needs one of these URLs.
func (*Client) PluginSettings ¶ added in v0.6.0
PluginSettings returns the stored settings for one plugin, keyed by the setting name its YAML declares. The plugin id is what Stash derives from the config filename — `moansubs.yml` gives "moansubs".
An unconfigured plugin, or one Stash has never heard of, returns an empty map rather than an error: a plugin whose settings have all been left at their defaults is indistinguishable from one that is not installed, and both mean "nothing has been set".
Values are whatever JSON Stash stored, so a caller asserting a type must tolerate what the UI actually writes. In particular a boolean setting the user has never touched can come back as nil rather than false, and a task's declared default arrives as a string.
func (*Client) Plugins ¶ added in v0.7.0
Plugins returns every plugin the server has loaded, enabled or not.
This is not the same list as Client.InstalledPackages: a plugin installed from a source appears in both, one dropped into the plugins directory by hand appears only here, and one whose files are present but unparseable appears in neither.
func (*Client) ReloadPlugins ¶ added in v0.7.0
ReloadPlugins makes the server re-read its plugin directory. Needed after files change on disk; an install through the package manager does it itself.
func (*Client) RemoveScenePerformers ¶ added in v0.7.0
func (c *Client) RemoveScenePerformers(ctx context.Context, performerIDs []string, sceneIDs ...string) error
RemoveScenePerformers removes performers from scenes, leaving their others alone.
func (*Client) RemoveSceneTags ¶ added in v0.7.0
RemoveSceneTags removes tags from scenes, leaving their other tags alone. Removing a tag a scene does not have is not an error.
func (*Client) SaveFilter ¶ added in v0.7.0
SaveFilter creates a saved filter, or updates the one whose ID is set, and returns its id.
func (*Client) SaveSceneFilter ¶ added in v0.7.0
func (c *Client) SaveSceneFilter(ctx context.Context, name string, filter SceneFilter, find *FindFilter) (string, error)
SaveSceneFilter saves a scene filter under a name, replacing one of that name if it exists.
The criteria come from a SceneFilter, so a filter that selects scenes in a program and a filter the user clicks in the sidebar are the same thing written once — including the translation into the notation saved filters use, which is not the one queries use. find may be nil, in which case Stash's own defaults apply.
func (*Client) SavedFilters ¶ added in v0.7.0
func (c *Client) SavedFilters(ctx context.Context, mode FilterMode) ([]SavedFilter, error)
SavedFilters returns the saved filters for one list, in the order Stash holds them.
func (*Client) SceneFilterCriteria ¶ added in v0.7.0
func (c *Client) SceneFilterCriteria(ctx context.Context, filter SceneFilter) (map[string]any, error)
SceneFilterCriteria renders a SceneFilter as the criterion map Stash speaks, resolving performer, studio and tag names to ids on the way.
Exported because it is what a saved filter stores: the same filter that selects scenes here can be handed to Client.SaveSceneFilter and appear in the UI, rather than being described twice in two notations.
func (*Client) ScenePaths ¶ added in v0.7.0
ScenePaths returns the media URLs for one scene.
This is a call of its own rather than a field on Scene because SceneFields is shared by every scene query, and a field that turns out to be missing on an older server costs all of them their whole response. A caller that wants paths asks for paths.
func (*Client) ScrapeMultiScenes ¶ added in v0.9.0
func (c *Client) ScrapeMultiScenes(ctx context.Context, endpoint string, sceneIDs []string) ([][]ScrapedScene, error)
ScrapeMultiScenes asks a stash-box about many scenes at once, each matched on its files' fingerprints the way Client.ScrapeSceneByID matches one.
The result is parallel to sceneIDs: one entry per scene, in the order given, holding that scene's candidates. A scene the stash-box does not recognise gets an empty entry rather than being dropped, so the two slices can be walked together.
This is the call to reach for when identifying a whole library. Stash queries the stash-box once per scene either way, but a batch costs one HTTP round trip instead of one per scene, and Stash's own max_requests_per_minute paces the upstream calls — so the batch size is a question of how much work to lose if the request fails, not of politeness. Twenty or so is comfortable.
func (*Client) ScrapePerformers ¶ added in v0.7.0
func (c *Client) ScrapePerformers(ctx context.Context, endpoint, query string) ([]ScrapedPerformer, error)
ScrapePerformers searches a stash-box through the server and returns what it found.
query is matched against names, and a name search returns everything close to it — ten results for a common first name is normal, so a caller picking blindly will pick wrong. Passing a stash id instead returns the one performer it belongs to, which is the reliable way to use this when the id is already known.
The server does the scraping, using the API key configured for that stash-box. A stash-box with no key configured returns nothing rather than an error.
func (*Client) ScrapeSceneByID ¶ added in v0.7.0
func (c *Client) ScrapeSceneByID(ctx context.Context, endpoint, sceneID string) ([]ScrapedScene, error)
ScrapeSceneByID asks a stash-box about a scene Stash already has, matched on the file's fingerprints rather than on any text.
An empty result is the ordinary answer for a library the stash-box does not cover, not a failure.
func (*Client) ScrapeScenes ¶ added in v0.7.0
ScrapeScenes searches a stash-box for scenes through the server.
Passing a scene id Stash already knows — see Client.ScrapeSceneByID — matches on the file's fingerprints, which is exact. A text query matches on whatever the stash-box searches, so it returns near-misses too: check something about each result before believing it, because "one result" is not the same as "the right one".
func (*Client) ServerVersion ¶ added in v0.9.0
func (c *Client) ServerVersion(ctx context.Context) (ServerVersion, error)
ServerVersion returns the build the server is running, hash and build time included. Client.Version is the same call when only the version string is wanted.
func (*Client) SetFingerprints ¶ added in v0.9.0
func (c *Client) SetFingerprints(ctx context.Context, fileID string, fingerprints []Fingerprint) error
SetFingerprints replaces a file's fingerprints.
Replaces, not merges: a hash the file had and this call omits is dropped. Read the file first and append if you mean to add one.
func (*Client) SetPluginsEnabled ¶ added in v0.7.0
SetPluginsEnabled enables and disables plugins by id. Plugins not named are left as they are.
func (*Client) SetPrimaryFile ¶ added in v0.9.0
SetPrimaryFile chooses which of a scene's files is the primary one — the file Stash streams, and the one whose resolution and codec the scene reports as its own.
This is not Client.AssignFile: that moves a file between scenes, while this reorders the files a scene already has. A scene left with several files after a merge is the usual reason to call it, picking the best of them before the rest are destroyed.
The file must already belong to the scene; Stash rejects the update otherwise.
func (*Client) SetSceneStashIDs ¶ added in v0.7.0
SetSceneStashIDs replaces a scene's stash-box ids, and can clear them.
SceneUpdate cannot. Its fields are omitted when empty so that an unset one leaves the stored value alone, which is what makes partial updates safe — and it means an empty StashIDs slice is indistinguishable from "do not touch the stash ids". Removing the last one therefore needs a call that always sends the field.
Passing nil clears them.
func (*Client) SetStashBoxes ¶ added in v0.8.0
func (c *Client) SetStashBoxes(ctx context.Context, boxes []StashBoxConfig) error
SetStashBoxes replaces the configured stash-boxes with exactly this list.
It replaces; it does not add. Passing one box removes every other, along with its API key, and Stash asks nothing before doing so. Read the current list with Client.StashBoxConfigs, append to it, and send the result.
Passing an empty list removes them all, which is a legitimate thing to want and so is not refused.
func (*Client) StashBoxBatchTag ¶ added in v0.9.0
func (c *Client) StashBoxBatchTag(ctx context.Context, target BatchTagTarget, opts BatchTagOptions) (jobID string, err error)
StashBoxBatchTag starts Stash's own batch tagger and returns the id of the job doing it. It does not wait; follow it with Client.FindJob.
This is the server-side version of matching a library against a stash-box: Stash queries the box for each entity, and writes back the stash id plus whatever fields are not excluded. Doing the same thing client-side means one round trip per entity and no access to Stash's own matching, which is why this exists even though Client.ScrapePerformers can reach the same data.
What it writes is not reviewable in advance. ExcludeFields is the only control over that, so on a library whose own metadata is better than the box's, exclude everything except the stash id.
func (*Client) StashBoxConfigs ¶ added in v0.8.0
func (c *Client) StashBoxConfigs(ctx context.Context) ([]StashBoxConfig, error)
StashBoxConfigs returns the configured stash-boxes with their API keys.
Prefer Client.StashBoxes for anything that only needs to know which boxes exist. This one hands back credentials, and is here for the one job that needs them: rewriting the list without destroying the entries it keeps.
func (*Client) StashBoxes ¶ added in v0.7.0
StashBoxes returns the stash-boxes the server is configured against, in the order Stash holds them. A scrape needs one of these endpoints, and an empty result means nothing can be scraped from a stash-box at all.
func (*Client) StopAllJobs ¶ added in v0.8.0
StopAllJobs asks Stash to stop everything queued and running.
func (*Client) StopJob ¶ added in v0.8.0
StopJob asks Stash to stop one running job. It returns without waiting: the job moves to STOPPING and reaches a terminal state in its own time, which Client.FindJob reports.
func (*Client) Studios ¶ added in v0.8.0
Studios returns one page of studios plus the total count, sorted by name.
func (*Client) SubmitFingerprints ¶ added in v0.8.0
func (c *Client) SubmitFingerprints(ctx context.Context, endpoint string, sceneIDs ...string) (ok bool, err error)
SubmitFingerprints sends the scenes' file fingerprints to a stash-box, against whatever entries they are already linked to there.
Only linked scenes contribute: the stash id is what says which upstream scene the hashes belong to, so a scene with none is silently nothing to submit. ok is what the server reports.
func (*Client) SubmitPerformerDraft ¶ added in v0.8.0
func (c *Client) SubmitPerformerDraft(ctx context.Context, performerID, endpoint string) (draftID string, err error)
SubmitPerformerDraft sends a performer to a stash-box as a draft.
func (*Client) SubmitSceneDraft ¶ added in v0.8.0
func (c *Client) SubmitSceneDraft(ctx context.Context, sceneID, endpoint string) (draftID string, err error)
SubmitSceneDraft sends a scene to a stash-box as a draft and returns the draft's id there.
A draft is not an edit. It lands in the stash-box as a proposal that someone — usually the submitter — then turns into a create or a modify through the stash-box's own interface. Nothing changes upstream until they do.
func (*Client) Supports ¶
Supports reports whether the server's schema has a named field on Scene.
This exists because GraphQL fails the *whole* query when it is asked for a field the schema lacks — one unknown field costs the entire response, not just that field. Probing once is cheaper than discovering it mid-import against an older server.
if ok, _ := c.Supports(ctx, "captions"); ok { ... }
The schema is fetched once per client and cached.
func (*Client) SystemStatus ¶ added in v0.9.0
func (c *Client) SystemStatus(ctx context.Context) (SystemStatus, error)
SystemStatus asks the server what state it is in.
Worth a call before a long unattended run: Client.Ping succeeds against a server that is showing its setup wizard or refusing to open an unmigrated database, because answering "SETUP" is itself a successful answer. This is how the two are told apart.
func (*Client) Tags ¶ added in v0.8.0
Tags returns one page of tags plus the total count, sorted by name.
func (*Client) UninstallPackages ¶ added in v0.7.0
func (c *Client) UninstallPackages(ctx context.Context, t PackageType, specs ...PackageSpec) (jobID string, err error)
UninstallPackages removes packages and returns the id of the job doing it.
It deletes the package's directory on the server. Anything a plugin wrote inside its own directory goes with it.
func (*Client) UpdatePackages ¶ added in v0.7.0
func (c *Client) UpdatePackages(ctx context.Context, t PackageType, specs ...PackageSpec) (jobID string, err error)
UpdatePackages updates packages, or every installed package of that type when no spec is given, and returns the id of the job doing it.
func (*Client) UpdatePerformer ¶ added in v0.8.0
func (c *Client) UpdatePerformer(ctx context.Context, update PerformerUpdate) error
UpdatePerformer writes the fields that are set and leaves the rest alone.
func (*Client) UpdateScene ¶
func (c *Client) UpdateScene(ctx context.Context, update SceneUpdate) error
UpdateScene writes the fields set on update, leaving the rest untouched.
func (*Client) UpdateStudio ¶ added in v0.8.0
func (c *Client) UpdateStudio(ctx context.Context, in StudioInput) error
UpdateStudio writes the fields that are set and leaves the rest alone.
func (*Client) UpdateTag ¶ added in v0.8.0
UpdateTag writes the fields that are set and leaves the rest alone.
func (*Client) ValidateStashBox ¶ added in v0.8.0
func (c *Client) ValidateStashBox(ctx context.Context, endpoint, apiKey string) (valid bool, status string, err error)
ValidateStashBox asks the server whether it can reach a stash-box with the given credential, and returns what it says.
The request is made by the *server*, so this tests the server's route to the box rather than this program's — which is the whole point when the two are on different machines.
type Conflict ¶ added in v0.10.0
Conflict is a stash ID Union dropped because the scene already had a different one for the same endpoint. A scene holds one ID per stash-box, so two IDs for one endpoint means two different remote scenes claim the same local one — which is for a person to settle, not this function.
type DLNAIP ¶ added in v0.9.0
type DLNAIP struct {
Address string `json:"ipAddress"`
// Until is when the grant lapses, or nil for one made without a
// duration, which lasts until the server restarts.
Until *string `json:"until"`
}
DLNAIP is one temporarily allowed address.
type DLNAStatus ¶ added in v0.9.0
type DLNAStatus struct {
Running bool `json:"running"`
// Until is when the current state ends, and reads in whichever
// direction Running points: while running it is when the service will
// stop, while stopped it is when it will start. nil means the state has
// no end — which is the usual case, since only a timed
// [Client.EnableDLNA] or [Client.DisableDLNA] sets one.
Until *string `json:"until"`
// RecentIPAddresses are the addresses that have asked the service for
// something lately, whether or not it answered. This is where the
// address for [Client.AllowDLNAIP] comes from: a device is identified
// by having just tried.
RecentIPAddresses []string `json:"recentIPAddresses"`
// AllowedIPAddresses are the temporary grants — the permanent list
// lives in the configuration, under dlnaInterfaces and dlnaWhitelist.
AllowedIPAddresses []DLNAIP `json:"allowedIPAddresses"`
}
DLNAStatus is what the server's DLNA service is currently doing.
type DeleteOptions ¶ added in v0.9.0
type DeleteOptions struct {
// DeleteFile removes the video from disk. There is no undo, and Stash
// does not move it to a wastebasket.
DeleteFile bool
// DeleteGenerated removes the sprites, previews and covers Stash made
// for it. Those can be regenerated from the video, so this is only
// destructive alongside DeleteFile.
DeleteGenerated bool
// DestroyFileEntry removes the file's row as well as the scene's. Without
// it Stash remembers the file and will not re-add it on the next scan,
// which is what you want when deleting a duplicate whose video is still
// on disk under another scene — and what you do not want when the file
// is gone and you may restore it later.
DestroyFileEntry bool
}
DeleteOptions says how far a scene deletion reaches.
Every field defaults to false, which removes only the scene record and leaves the video where it is. That is the recoverable choice: a scan finds the file again. The others are not.
type FieldPolicy ¶ added in v0.10.0
type FieldPolicy int
FieldPolicy says how Union combines one scene field.
The zero value is Keep, so a field left out of a UnionPolicy is never written.
const ( // Keep never touches the field. Keep FieldPolicy = iota // FillEmpty keeps the destination's value unless it is empty, and then // takes the first source that has one. This is what a merge wants: the // scene being kept is right, the ones being folded away fill its gaps. FillEmpty // PreferSource takes the first source that has a value, over whatever the // destination has. An empty source never clears the destination. PreferSource // Combine takes everything: list fields get every element from every // scene, the destination's first, deduplicated; Rating100 takes the // highest; Organized is true if any scene is. On a plain string field it // behaves as FillEmpty, there being nothing to combine. Combine )
type File ¶
type File struct {
ID string `json:"id"`
Basename string `json:"basename"`
Path string `json:"path"`
Size int64 `json:"size"`
ModTime string `json:"mod_time"`
Format string `json:"format"`
Width int `json:"width"`
Height int `json:"height"`
Duration float64 `json:"duration"`
VideoCodec string `json:"video_codec"`
AudioCodec string `json:"audio_codec"`
FrameRate float64 `json:"frame_rate"`
BitRate int64 `json:"bit_rate"`
Fingerprints []Fingerprint `json:"fingerprints"`
}
File is one video file backing a scene. A scene has several when Stash has attached re-detected duplicates to it.
func (*File) Fingerprint ¶ added in v0.4.0
Fingerprint returns the value of the named hash ("oshash", "phash", "md5").
type FilterMode ¶ added in v0.7.0
type FilterMode string
FilterMode is which list a saved filter belongs to.
const ( FilterScenes FilterMode = "SCENES" FilterPerformers FilterMode = "PERFORMERS" FilterStudios FilterMode = "STUDIOS" FilterGalleries FilterMode = "GALLERIES" FilterSceneMarkers FilterMode = "SCENE_MARKERS" FilterGroups FilterMode = "GROUPS" FilterTags FilterMode = "TAGS" FilterImages FilterMode = "IMAGES" )
FilterMode values, one per list a saved filter can belong to.
type FindFilter ¶ added in v0.7.0
type FindFilter struct {
// Query is the free-text box.
Query string `json:"q,omitempty"`
// Sort is a field name ("date", "path", "title"), and Direction is
// "ASC" or "DESC".
Sort string `json:"sort,omitempty"`
Direction string `json:"direction,omitempty"`
PerPage int `json:"per_page,omitempty"`
}
FindFilter is the sorting and paging half of a filter — what the UI puts above the list rather than in the sidebar.
type Fingerprint ¶ added in v0.4.0
Fingerprint is one content hash of a file.
type GenerateOptions ¶ added in v0.8.0
type GenerateOptions struct {
// Covers, Sprites and Phashes are the three that other work depends on.
// A scene with no sprite cannot be read for a title card; a scene with
// no phash cannot be matched against a stash-box.
Covers bool
Sprites bool
Phashes bool
Previews bool
ImagePreviews bool
Markers bool
Transcodes bool
// ForceTranscodes re-encodes even where a transcode already exists.
ForceTranscodes bool
InteractiveHeatmapsSpeeds bool
ImagePhashes bool
ImageThumbnails bool
ClipPreviews bool
// SceneIDs restricts the job to these scenes; Paths to these library
// paths. Both empty means the whole library.
SceneIDs []string
Paths []string
// Overwrite regenerates what is already there. Without it Stash skips
// anything it has, which is what makes a second run cheap.
Overwrite bool
}
GenerateOptions selects what Client.MetadataGenerate produces.
Every flag defaults to off, for the reason ScanOptions gives: generating across a library is hours of work and gigabytes of output, and a library call that quietly started doing it would be an expensive surprise. Ask for what you want.
type GraphQLError ¶ added in v0.2.0
type GraphQLError struct {
Message string `json:"message"`
Path []any `json:"path,omitempty"`
Extensions map[string]any `json:"extensions,omitempty"`
}
GraphQLError is one entry from a GraphQL `errors` array.
Path and Extensions are kept because they are where a server says something useful: Path names the field that failed, and Stash puts its own error codes in Extensions. Flattening these to a string loses the machine-readable part of the only structured error the API offers.
type Group ¶ added in v0.9.0
Group is a Stash group, as a scene reports its membership of one. Only the fields the scene selection asks for.
type HTTPError ¶
HTTPError is a non-2xx response from the server.
Body carries what the server actually said, truncated. Stash returns useful text on an auth failure or a bad endpoint, and a bare status code sends the reader to the server logs for something that was already in the response.
type IdentifyOptions ¶ added in v0.8.0
type IdentifyOptions struct {
// Sources are the stash-box endpoints or scraper ids to try, in order.
// Empty uses whatever the server has configured as its defaults.
Sources []string
// SceneIDs restricts the job to these scenes; Paths to these library
// paths. Both empty means everything.
SceneIDs []string
Paths []string
}
IdentifyOptions configures Client.MetadataIdentify, Stash's own matching of scenes against a stash-box.
type Job ¶ added in v0.6.0
type Job struct {
ID string `json:"id"`
Status JobStatus `json:"status"`
Description string `json:"description"`
// Progress is 0..1 while running, and negative when Stash has not
// worked out a total yet — not 0, which would render as "just started"
// rather than "unknown".
Progress *float64 `json:"progress"`
Error *string `json:"error"`
AddTime string `json:"addTime"`
StartTime *string `json:"startTime"`
EndTime *string `json:"endTime"`
}
Job is one entry in Stash's task queue.
type JobStatus ¶ added in v0.6.0
type JobStatus string
JobStatus is where a job has got to. Stash's own vocabulary, not a normalisation of it.
type LibraryStats ¶ added in v0.9.0
type LibraryStats struct {
SceneCount int `json:"scene_count"`
// ScenesSize is the total size of every scene's files, in bytes.
ScenesSize float64 `json:"scenes_size"`
// ScenesDuration is the total runtime of every scene, in seconds.
ScenesDuration float64 `json:"scenes_duration"`
ImageCount int `json:"image_count"`
ImagesSize float64 `json:"images_size"`
GalleryCount int `json:"gallery_count"`
PerformerCount int `json:"performer_count"`
StudioCount int `json:"studio_count"`
GroupCount int `json:"group_count"`
TagCount int `json:"tag_count"`
// TotalPlayDuration is how long scenes have been watched for in total,
// in seconds, and ScenesPlayed how many have been watched at all.
TotalOCount int `json:"total_o_count"`
TotalPlayCount int `json:"total_play_count"`
TotalPlayDuration float64 `json:"total_play_duration"`
ScenesPlayed int `json:"scenes_played"`
}
LibraryStats is what the server counts about the library as a whole. Every field is the server's own tally, computed from the database rather than by walking the scenes.
The counts cover what Stash has indexed, which is not the same as what is on disk: a file the last scan did not reach is not here.
type LogEntry ¶ added in v0.9.0
type LogEntry struct {
Time string `json:"time"`
// Level is Stash's own word for it: "Trace", "Debug", "Info",
// "Progress", "Warning" or "Error".
Level string `json:"level"`
Message string `json:"message"`
}
LogEntry is one line from the server's log.
type MergeOptions ¶ added in v0.9.0
type MergeOptions struct {
// PlayHistory folds the sources' play timestamps into the
// destination's.
PlayHistory bool
// OHistory folds the sources' o timestamps into the destination's.
OHistory bool
}
MergeOptions says what a merge carries across besides the files.
Both default to false, which is Stash's own default and discards the sources' watch history. A deduplicating tool usually wants both: the copy being kept is the same content, so the times it was watched belong to it.
type MoveTarget ¶ added in v0.9.0
MoveTarget says where Client.MoveFiles should put them: a folder Stash already knows by id, or a path, and optionally a new name.
Renaming one file at a time is what Basename is for; moving several with a Basename set would give them all the same name, so it is refused.
type Option ¶
type Option func(*Client)
Option configures a Client.
func WithAPIKey ¶
WithAPIKey authenticates as the given API key. Omit it for a Stash instance with authentication disabled.
func WithCaptions
deprecated
added in
v0.6.0
func WithCaptions() Option
WithCaptions once asked the scene queries to include Scene.Captions, which they now always do.
It survives as a no-op so that callers written against the option still compile. There is nothing to opt into: the supported server has the field, so it is in SceneFields with everything else.
Deprecated: captions are always selected. Remove the option.
func WithCookie ¶ added in v0.3.0
WithCookie authenticates with a session cookie.
Stash hands its plugin processes a session cookie in server_connection, and a plugin has no API key unless the operator configured one. An API key takes precedence when both are set: session cookies expire mid-run, which on a long task fails partway through rather than at startup.
func WithHTTPClient ¶
WithHTTPClient supplies the HTTP client used for every request, so retry, backoff, proxying and timeouts stay under the caller's control.
The default is a plain client with a 30s timeout and no retry.
func WithMaxResponseBytes ¶
WithMaxResponseBytes overrides DefaultMaxResponseBytes.
type Package ¶ added in v0.7.0
type Package struct {
ID string `json:"package_id"`
Name string `json:"name"`
Version string `json:"version"`
Date string `json:"date"`
SourceURL string `json:"sourceURL"`
// Requires names the packages this one needs. Stash does not install
// them for you — an install that leaves a requirement unmet succeeds and
// the plugin then fails at runtime.
Requires []Package `json:"requires"`
// Metadata is the index's free-form block, which in practice carries a
// "description". Free-form is why it is not modelled.
Metadata map[string]any `json:"metadata"`
}
Package is one entry in a package index, or one thing already installed.
func (Package) Description ¶ added in v0.7.0
Description returns the package's description, or "" when the index gives none.
func (Package) Spec ¶ added in v0.7.0
func (p Package) Spec() PackageSpec
Spec returns the PackageSpec that names this package for an install or uninstall.
type PackageSource ¶ added in v0.7.0
type PackageSource struct {
Name string `json:"name"`
// URL of the index. It is also the sourceURL a [PackageSpec] has to
// carry: Stash identifies a package by id *and* source, because two
// indexes may both offer an id.
URL string `json:"url"`
LocalPath string `json:"local_path"`
}
PackageSource is one index a package manager installs from, as configured in the server's settings.
type PackageSpec ¶ added in v0.7.0
PackageSpec names one package to install or uninstall. Both fields are required: an id alone is ambiguous across sources.
type PackageType ¶ added in v0.7.0
type PackageType string
PackageType selects which of Stash's two package managers a call talks to.
const ( PackagePlugin PackageType = "Plugin" PackageScraper PackageType = "Scraper" )
PackageType values, one per package manager Stash exposes.
type ParsedFilename ¶ added in v0.10.0
type ParsedFilename struct {
Date string // "2024-12-15" — normalized to YYYY-MM-DD
Title string // "A Long Scene Title With Words"
Performers []string // ["Some Performer"]
}
ParsedFilename is what ParseFilename extracted from a structured basename.
func ParseFilename ¶ added in v0.10.0
func ParseFilename(basename string) (ParsedFilename, bool)
ParseFilename extracts a date, title and performer names from a basename following the convention YYYY-MM-DD_Performers-Title_Resolution.ext. It reports false when the name doesn't match that convention at all — most filenames simply aren't structured this way, so that is not an error.
Stash has its own scan-time filename parser; this one exists alongside it because it additionally handles multiple underscore-separated performers and a dashed or dotted title the way one library is actually named.
type Performer ¶
type Performer struct {
ID string `json:"id"`
Name string `json:"name"`
Disambiguation string `json:"disambiguation"`
Gender string `json:"gender"`
Birthdate string `json:"birthdate"`
DeathDate string `json:"death_date"`
Country string `json:"country"`
Ethnicity string `json:"ethnicity"`
EyeColor string `json:"eye_color"`
HairColor string `json:"hair_color"`
HeightCM int `json:"height_cm"`
Weight int `json:"weight"`
Measurements string `json:"measurements"`
FakeTits string `json:"fake_tits"`
// CareerStart and CareerEnd are strings on the wire, not numbers: Stash
// stores them as years but declares them String, and decoding them as
// ints fails every performer query.
CareerStart string `json:"career_start"`
CareerEnd string `json:"career_end"`
Tattoos string `json:"tattoos"`
Piercings string `json:"piercings"`
Aliases []string `json:"alias_list"`
URLs []string `json:"urls"`
Details string `json:"details"`
Favorite bool `json:"favorite"`
Rating100 *int `json:"rating100"`
ImagePath string `json:"image_path"`
SceneCount int `json:"scene_count"`
Tags []Tag `json:"tags"`
StashIDs []StashID `json:"stash_ids"`
}
Performer attached to a scene. Performer as returned by the performer queries.
A performer reached through a scene carries only ID and Name: the shared scene selection asks for nothing more, because a page of scenes would otherwise drag a full performer record along for every credit. Client.FindPerformerByID and Client.FindPerformers fill the rest in.
type PerformerFilter ¶ added in v0.8.0
type PerformerFilter struct {
// NameContains matches anywhere in the name, case-insensitively.
NameContains string
// Gender is Stash's own vocabulary — "FEMALE", "MALE",
// "TRANSGENDER_FEMALE" and the rest.
Gender string
// Favorite selects favourites (true) or everything else (false).
Favorite *bool
// HasStashID selects performers that do (true) or do not (false) carry
// stash-box metadata.
HasStashID *bool
// HasScenes selects performers with at least one scene (true) or none
// (false). Performers with none are usually leftovers.
HasScenes *bool
}
PerformerFilter selects performers. An unset field does not filter.
type PerformerInput ¶ added in v0.7.0
type PerformerInput struct {
Name string
Disambiguation string
Gender string
Birthdate string
DeathDate string
Country string
Ethnicity string
EyeColor string
HairColor string
HeightCM int
Weight int
Measurements string
FakeTits string
CareerLength string
Tattoos string
Piercings string
Aliases []string
URLs []string
Details string
// Image is either a URL or a data: URI. Given a URL, Stash fetches it
// itself — which means the fetch happens from the server, and a URL only
// this machine can reach will not work.
Image string
// StashIDs ties the performer to its entry in a stash-box. Worth setting
// whenever it is known: it is the only stable identity a performer has,
// and it is what stops the same person being created twice under two
// spellings.
StashIDs []StashID
}
PerformerInput is a performer to create, with everything Stash will accept about one.
Every field but Name is optional and omitted when empty, so a caller that knows only a name sends the same request Client.CreatePerformer does.
type PerformerUpdate ¶ added in v0.8.0
type PerformerUpdate struct {
ID string
Name *string
Disambiguation *string
Gender *string
Birthdate *string
DeathDate *string
Country *string
Ethnicity *string
EyeColor *string
HairColor *string
HeightCM *int
Weight *int
Measurements *string
FakeTits *string
CareerLength *string
Tattoos *string
Piercings *string
Details *string
Favorite *bool
Rating100 *int
// Aliases, URLs, TagIDs and StashIDs replace what is stored rather than
// adding to it. Read the performer first and send the union if adding is
// what you meant.
Aliases []string
URLs []string
TagIDs []string
StashIDs []StashID
// Image is a URL or a data: URI. Given a URL, the server fetches it.
Image *string
}
PerformerUpdate is the payload for Client.UpdatePerformer.
Only the fields you set are sent, so an unset one leaves the stored value alone — the same shape as SceneUpdate, and with the same limitation: it cannot clear a field, because empty and absent look identical on the wire. Client.ClearPerformerFields is the way to empty one.
type Plugin ¶ added in v0.7.0
type Plugin struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
URL string `json:"url"`
Version string `json:"version"`
Enabled bool `json:"enabled"`
}
Plugin is one plugin the server has loaded.
type Progress ¶
type Progress func(fetched, total int)
Progress reports pagination advancing. total is the count reported by the first page.
type SavedFilter ¶ added in v0.7.0
type SavedFilter struct {
ID string `json:"id"`
Mode FilterMode `json:"mode"`
Name string `json:"name"`
// FindFilter is the sort and page size.
FindFilter *FindFilter `json:"find_filter"`
// ObjectFilter is the criteria, in Stash's own notation. Build one for
// scenes with [Client.SceneFilterCriteria] rather than by hand.
ObjectFilter map[string]any `json:"object_filter"`
// UIOptions is what the UI remembers about how to display the list —
// card size, zoom, which columns. Carried so an update does not discard
// it.
UIOptions map[string]any `json:"ui_options"`
}
SavedFilter is one of the filters that appear in Stash's sidebar.
type ScanOptions ¶ added in v0.6.0
type ScanOptions struct {
// Paths restricts the scan to these library paths. Empty scans every
// configured library path, which on a large library is an hours-long
// job — pass the directory you actually changed.
Paths []string
// Rescan re-reads files Stash has already indexed rather than only
// picking up new ones.
Rescan bool
// GeneratePhashes computes perceptual hashes for newly scanned files.
// Worth setting for anything that matches on content rather than
// filename: without a phash, only byte-identical files can be matched.
GeneratePhashes bool
GenerateCovers bool
GeneratePreviews bool
GenerateSprites bool
GenerateThumbnails bool
}
ScanOptions selects what Client.MetadataScan scans and what it generates while it does.
Every generate flag defaults to off. That is not Stash's own default — the UI remembers whatever was ticked last — but a library call that quietly started generating covers, previews and sprites across a library would be a very expensive surprise. Ask for what you want.
type Scene ¶
type Scene struct {
ID string `json:"id"`
Title string `json:"title"`
Code string `json:"code"`
Date string `json:"date"`
Details string `json:"details"`
Director string `json:"director"`
URLs []string `json:"urls"`
Rating100 *int `json:"rating100"`
Organized bool `json:"organized"`
OCounter int `json:"o_counter"`
Files []File `json:"files"`
Tags []Tag `json:"tags"`
Performers []Performer `json:"performers"`
Studio *Studio `json:"studio"`
StashIDs []StashID `json:"stash_ids"`
Galleries []Gallery `json:"galleries"`
Captions []Caption `json:"captions"`
Groups []SceneGroup `json:"groups"`
// PlayDuration is how long this scene has been watched for in total,
// in seconds, and ResumeTime where playback left off.
PlayCount int `json:"play_count"`
PlayDuration float64 `json:"play_duration"`
LastPlayedAt *string `json:"last_played_at"`
ResumeTime float64 `json:"resume_time"`
// CreatedAt and UpdatedAt are the record's timestamps, as the server
// formats them; pass UpdatedAt back as [SceneFilter.UpdatedAfter] to
// mirror incrementally.
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
}
Scene is a scene as returned by findScene / findScenes.
func (*Scene) HasStashID ¶ added in v0.5.0
HasStashID reports whether the scene carries stash-box metadata.
func (*Scene) PrimaryFile ¶ added in v0.4.0
PrimaryFile returns the file Stash treats as canonical, or nil when the scene has none.
type SceneFilter ¶
type SceneFilter struct {
Organized *bool `json:"organized,omitempty"`
PerformerName string `json:"-"`
StudioName string `json:"-"`
// HasStashID selects scenes that do (true) or do not (false) carry
// stash-box metadata. Nil means "either".
HasStashID *bool `json:"-"`
// PathContains matches scenes whose file path contains this substring.
PathContains string `json:"-"`
// HasDate selects scenes that do (true) or do not (false) carry a date.
// Nil means "either".
HasDate *bool `json:"-"`
// MultiFile selects scenes with more than one file attached (true) or
// with exactly one (false). Nil means "either".
//
// Stash attaches a re-detected file to the scene that already has its
// hash rather than creating a second scene, so true is how the
// duplicates that never became separate scenes are found.
MultiFile *bool `json:"-"`
// TagNames selects scenes carrying every one of these tags, and
// ExcludeTagNames scenes carrying none of them. Both are resolved to
// ids first, and a name no tag has is an error rather than an empty
// result — the same reason [ErrPerformerNotFound] exists.
TagNames []string `json:"-"`
ExcludeTagNames []string `json:"-"`
// DateBefore and DateAfter bound the date, exclusive at both ends, in
// Stash's own "2006-01-02" notation. A scene with no date matches
// neither: an absent date is not an early one.
DateBefore string `json:"-"`
DateAfter string `json:"-"`
// UpdatedAfter selects scenes whose record changed after this
// timestamp, exclusive, in RFC 3339 or Stash's "2006-01-02 15:04:05".
// Remember the newest updated_at seen and pass it next time to mirror
// a library incrementally.
UpdatedAfter string `json:"-"`
}
SceneFilter narrows FindScenes.
Fields tagged `json:"-"` are resolved client-side into the server's filter shape — PerformerName and StudioName each cost an extra lookup to turn a name into an ID.
type SceneGroup ¶ added in v0.9.0
SceneGroup is a scene's membership of a group — what Stash called a movie before 0.28. SceneIndex is the scene's place in it, and is nil for a group that does not order its scenes.
type ScenePaths ¶ added in v0.7.0
type ScenePaths struct {
// Screenshot is the scene's cover, at the video's own resolution.
Screenshot string `json:"screenshot"`
// Preview is a short video, Webp a short animation.
Preview string `json:"preview"`
Webp string `json:"webp"`
// Stream serves the video itself, and honours range requests — which is
// what lets a frame be pulled from the middle of a file without
// downloading it.
Stream string `json:"stream"`
Sprite string `json:"sprite"`
VTT string `json:"vtt"`
}
ScenePaths are the URLs of a scene's generated media — the things GraphQL will not return as data. Fetch one with Client.Fetch.
An empty field means Stash has not generated that piece for this scene. Sprite and VTT go together: the sheet is a grid of frames and the WebVTT is what says which frame is which moment.
type SceneUpdate ¶
type SceneUpdate struct {
ID string `json:"id"`
Title *string `json:"title,omitempty"`
Code *string `json:"code,omitempty"`
Details *string `json:"details,omitempty"`
Director *string `json:"director,omitempty"`
Date *string `json:"date,omitempty"`
Rating100 *int `json:"rating100,omitempty"`
URLs []string `json:"urls,omitempty"`
TagIDs []string `json:"tag_ids,omitempty"`
PerformerIDs []string `json:"performer_ids,omitempty"`
StudioID *string `json:"studio_id,omitempty"`
GalleryIDs []string `json:"gallery_ids,omitempty"`
Organized *bool `json:"organized,omitempty"`
StashIDs []StashID `json:"stash_ids,omitempty"`
// PrimaryFileID picks which of the scene's files it streams from, and
// which one's resolution and codec it reports as its own. The file must
// already belong to the scene. [Client.SetPrimaryFile] is this field on
// its own.
PrimaryFileID *string `json:"primary_file_id,omitempty"`
// CoverImage is a data URI ("data:image/jpeg;base64,…").
//
// This package deliberately does not fetch it for you: downloading an
// arbitrary scraped URL needs SSRF validation, a size cap and an expiry
// policy, all of which belong to the calling program rather than to a
// Stash client.
CoverImage *string `json:"cover_image,omitempty"`
}
SceneUpdate is the payload for a scene update.
Pointer and slice fields are omitted when nil, so only what you set is written. That is what makes a partial metadata push non-destructive — an unset Title leaves the existing title alone rather than clearing it.
type ScrapedPerformer ¶ added in v0.7.0
type ScrapedPerformer struct {
Name string `json:"name"`
Disambiguation string `json:"disambiguation"`
Gender string `json:"gender"`
Birthdate string `json:"birthdate"`
DeathDate string `json:"death_date"`
Country string `json:"country"`
Ethnicity string `json:"ethnicity"`
EyeColor string `json:"eye_color"`
HairColor string `json:"hair_color"`
Height string `json:"height"`
Weight string `json:"weight"`
Measurements string `json:"measurements"`
FakeTits string `json:"fake_tits"`
CareerLength string `json:"career_length"`
Tattoos string `json:"tattoos"`
Piercings string `json:"piercings"`
Details string `json:"details"`
URLs []string `json:"urls"`
// Aliases is one comma-separated string, not a list. Stash's own input
// wants a list, which is half of what Input is for.
Aliases string `json:"aliases"`
// Images are URLs, best first.
Images []string `json:"images"`
// RemoteSiteID is the performer's id at the source. For a stash-box that
// is the stash id, which is what makes the result worth keeping.
RemoteSiteID string `json:"remote_site_id"`
// StoredID is set when Stash already has this performer, which saves
// creating a second one under a name that differs by punctuation.
StoredID string `json:"stored_id"`
}
ScrapedPerformer is a performer as a scraper describes one. Its fields are strings because that is what scrapers return, including the numeric ones — ScrapedPerformer.Input does the converting.
func (ScrapedPerformer) Input ¶ added in v0.7.0
func (p ScrapedPerformer) Input(endpoint string) PerformerInput
Input converts a scraped performer into something Client.CreatePerformerFrom can take, with endpoint naming the stash-box it came from so the stash id is recorded against the right one.
The conversions are the fiddly part and the reason this exists: heights and weights arrive as strings and sometimes with a unit attached, aliases arrive comma-separated where the input wants a list, and the first image is the one to keep.
type ScrapedScene ¶ added in v0.7.0
type ScrapedScene struct {
Title string `json:"title"`
Code string `json:"code"`
Date string `json:"date"`
Details string `json:"details"`
Director string `json:"director"`
URLs []string `json:"urls"`
// Image is a URL to the scene's cover.
Image string `json:"image"`
// Duration is in seconds, and is the check worth making before
// believing a match: two scenes with the same code and wildly different
// lengths are not the same scene.
Duration int `json:"duration"`
// RemoteSiteID is the scene's stash id.
RemoteSiteID string `json:"remote_site_id"`
Studio *ScrapedStudio `json:"studio"`
Performers []ScrapedPerformer `json:"performers"`
Tags []ScrapedTag `json:"tags"`
}
ScrapedScene is a scene as a stash-box describes one.
type ScrapedStudio ¶ added in v0.7.0
type ScrapedStudio struct {
Name string `json:"name"`
StoredID string `json:"stored_id"`
RemoteSiteID string `json:"remote_site_id"`
URL string `json:"url"`
}
ScrapedStudio is a studio as a scraper describes one. StoredID is set when Stash already has it, which saves looking it up by a name that may differ.
type ScrapedTag ¶ added in v0.7.0
ScrapedTag is a tag as a scraper describes one.
type ScreenshotMigration ¶ added in v0.9.0
type ScreenshotMigration struct {
// DeleteFiles removes each screenshot file once it has been read into
// the database as a blob.
DeleteFiles bool
// OverwriteExisting replaces a cover the scene already has. Off, a
// scene with a cover keeps it and the file on disk is ignored.
OverwriteExisting bool
}
ScreenshotMigration configures Client.MigrateSceneScreenshots.
type ServerVersion ¶ added in v0.9.0
type ServerVersion struct {
// Version is the release tag ("v0.31.1"), and is empty on a binary
// built from source outside a release — where Hash is what identifies
// it.
Version string `json:"version"`
Hash string `json:"hash"`
BuildTime string `json:"build_time"`
}
ServerVersion is the running build.
type StashBox ¶ added in v0.7.0
StashBox is one stash-box the server is configured against — stashdb.org and its siblings, the shared metadata databases Stash matches against.
The API key is deliberately not carried here. It is the server's credential for a third party, and a library handing it back invites it into a log line.
type StashBoxConfig ¶ added in v0.8.0
type StashBoxConfig struct {
Name string `json:"name"`
Endpoint string `json:"endpoint"`
APIKey string `json:"api_key"`
// MaxRequestsPerMinute throttles the server's calls to this box. Zero
// means the server's default rather than "no requests".
MaxRequestsPerMinute int `json:"max_requests_per_minute"`
}
StashBoxConfig is a stash-box as it is configured, credential included.
Separate from StashBox on purpose: that type is what a caller reads and deliberately has no API key, because it is the server's credential for a third party. This one exists because configuring a stash-box means sending the key, and rewriting the list means sending back the keys of the entries being kept.
type StashID ¶
StashID links a scene to its entry in an external stash-box instance. Endpoint is the stash-box GraphQL URL; ID is the remote UUID.
type Studio ¶
type Studio struct {
ID string `json:"id"`
Name string `json:"name"`
URLs []string `json:"urls"`
Details string `json:"details"`
Aliases []string `json:"aliases"`
Rating100 *int `json:"rating100"`
Favorite bool `json:"favorite"`
ImagePath string `json:"image_path"`
SceneCount int `json:"scene_count"`
StashIDs []StashID `json:"stash_ids"`
ParentStudio *Studio `json:"parent_studio"`
}
Studio a scene belongs to, and as the studio queries return one.
A studio reached through a scene carries only ID and Name, for the reason Performer does: the shared scene selection asks for no more, because a page of scenes should not drag a full record along for each one.
type StudioInput ¶ added in v0.8.0
type StudioInput struct {
// ID is set on an update and empty on a create.
ID string
Name string
Details string
ParentID string
Aliases []string
URLs []string
TagIDs []string
StashIDs []StashID
// Image is a URL or a data: URI. Given a URL the server fetches it.
Image string
Rating100 *int
Favorite *bool
}
StudioInput is a studio to create or update. Every field but Name is optional and omitted when empty, so an unset one leaves the stored value alone — the same shape as SceneUpdate, with the same limitation: Client.ClearStudioFields is what empties one.
type SystemState ¶ added in v0.9.0
type SystemState string
SystemState is what the server says about its own readiness. Stash's own vocabulary, not a normalisation of it.
const ( // SystemOK means the server is set up, migrated and serving. SystemOK SystemState = "OK" // SystemSetup means Stash has no configuration file yet and is showing // its setup wizard. Nothing in the library API answers usefully. SystemSetup SystemState = "SETUP" // SystemNeedsMigration means the database on disk is older than the // binary reading it. Stash refuses to touch a library in that state // until [Client.Migrate] runs. SystemNeedsMigration SystemState = "NEEDS_MIGRATION" )
type SystemStatus ¶ added in v0.9.0
type SystemStatus struct {
Status SystemState `json:"status"`
// DatabaseSchema is the version of the database on disk, and is nil on
// a server that has none yet — which is what SETUP means.
DatabaseSchema *int `json:"databaseSchema"`
// AppSchema is the version the running binary expects. It being ahead
// of DatabaseSchema is exactly the NEEDS_MIGRATION condition.
AppSchema int `json:"appSchema"`
DatabasePath string `json:"databasePath"`
ConfigPath string `json:"configPath"`
}
SystemStatus is the server's account of itself: what state it is in, which schema version its database is at, and where it keeps both.
The fields are the ones every supported server has. Stash has added others since (the operating system, the working and home directories, the resolved ffmpeg and ffprobe paths), and naming one here would fail the whole query against a server that lacks it — reach those through Client.Execute.
func (SystemStatus) Ready ¶ added in v0.9.0
func (s SystemStatus) Ready() bool
Ready reports whether the server is in a state where the rest of this package will work. A server mid-setup or awaiting migration answers queries, but answers them with errors.
type Tag ¶
type Tag struct {
ID string `json:"id"`
Name string `json:"name"`
SortName string `json:"sort_name"`
Description string `json:"description"`
Aliases []string `json:"aliases"`
Favorite bool `json:"favorite"`
ImagePath string `json:"image_path"`
SceneCount int `json:"scene_count"`
StashIDs []StashID `json:"stash_ids"`
// Parents and Children are one level deep: a hierarchy queried in full
// would carry the whole tree on every tag in it.
Parents []Tag `json:"parents"`
Children []Tag `json:"children"`
}
Tag attached to a scene, and as the tag queries return one.
A tag reached through a scene carries only ID and Name — the shared scene selection asks for no more.
type TagInput ¶ added in v0.8.0
type TagInput struct {
// ID is set on an update and empty on a create.
ID string
Name string
SortName string
Description string
Aliases []string
// ParentIDs and ChildIDs replace the tag's place in the hierarchy rather
// than adding to it. Read the tag first and send the union if adding is
// what you meant.
ParentIDs []string
ChildIDs []string
StashIDs []StashID
Image string
Favorite *bool
}
TagInput is a tag to create or update. Every field but Name is optional and omitted when empty, so an unset one leaves the stored value alone.
type Tier ¶ added in v0.10.0
type Tier int
Tier is the resolution class a file belongs to, judged from its pixel dimensions. Stash's own `resolution` label is not to be trusted for this: libraries carry 720x404 files labelled HD because someone read "720 wide" as 720p, and square cover art labelled 8K.
The tiers are bands on the longer side, so 4K is not also 1080-tier and a portrait file is judged by its height.
const ( TierUnknown Tier = iota // no dimensions, or a side of zero TierSD // longer side under 1280 Tier720 // 1280 up to 1920 Tier1080 // 1920 up to 2560 Tier1440 // 2560 up to 3840 Tier4K // 3840 up to 7680 Tier8K // 7680 and beyond )
The bands, on the longer side; each line gives its lower bound.
type UnionPolicy ¶ added in v0.10.0
type UnionPolicy struct {
Title FieldPolicy
Code FieldPolicy
Date FieldPolicy
Details FieldPolicy
Director FieldPolicy
Studio FieldPolicy
URLs FieldPolicy
Tags FieldPolicy
Performers FieldPolicy
Galleries FieldPolicy
StashIDs FieldPolicy
Rating100 FieldPolicy
Organized FieldPolicy
}
UnionPolicy is a FieldPolicy per scene field Union knows how to combine. Fields Stash's sceneMerge already handles — files, markers, play history — are not here.
func DefaultUnionPolicy ¶ added in v0.10.0
func DefaultUnionPolicy() UnionPolicy
DefaultUnionPolicy is the policy a merge wants: lists are unioned, scalars are kept unless the destination has none, the rating is the highest and the scene is organized if any copy was.