stash-graphql-client 0.0.0-stage → 0.31.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +225 -2
- package/dist/client.d.ts +15 -0
- package/dist/client.js +29 -0
- package/dist/generated/roots.d.ts +6 -0
- package/dist/generated/roots.js +281 -0
- package/dist/generated/schema.d.ts +4491 -0
- package/dist/generated/schema.js +0 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +3 -0
- package/dist/request.d.ts +23 -0
- package/dist/request.js +120 -0
- package/dist/transport.d.ts +40 -0
- package/dist/transport.js +140 -0
- package/dist/types.d.ts +110 -0
- package/dist/types.js +0 -0
- package/package.json +51 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 shalemore
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,226 @@
|
|
|
1
|
-
#
|
|
1
|
+
# stash-graphql-client
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A fully typed client for the [Stash](https://github.com/stashapp/stash) GraphQL API, without writing GraphQL.
|
|
4
|
+
|
|
5
|
+
- **Pick fields by reading them.** Your editor autocompletes them, and the result type has exactly the fields you picked.
|
|
6
|
+
- **Generated from Stash's own schema** (latest release, see `STASH_VERSION`), so the names match.
|
|
7
|
+
- **No dependencies.** It just uses `fetch`, and is about 4.5 KB gzipped.
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
bun add stash-graphql-client
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick start
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { createStash } from "stash-graphql-client";
|
|
17
|
+
|
|
18
|
+
const stash = createStash({
|
|
19
|
+
endpoint: "http://localhost:9999/graphql",
|
|
20
|
+
apiKey: process.env.STASH_API_KEY, // sent as the ApiKey header
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// Every root field is a method: (arguments, what to select)
|
|
24
|
+
const { count, performers } = await stash.findPerformers(
|
|
25
|
+
{ filter: { per_page: 20, sort: "name", direction: "ASC" } },
|
|
26
|
+
(result) => ({
|
|
27
|
+
count: result.count,
|
|
28
|
+
performers: result.performers((performer) => ({
|
|
29
|
+
id: performer.id,
|
|
30
|
+
name: performer.name,
|
|
31
|
+
favorite: performer.favorite,
|
|
32
|
+
})),
|
|
33
|
+
}),
|
|
34
|
+
);
|
|
35
|
+
// count: number
|
|
36
|
+
// performers: { id: string; name: string; favorite: boolean }[]
|
|
37
|
+
|
|
38
|
+
await stash.findPerformers((result) => ({ count: result.cont }));
|
|
39
|
+
// ~~~~ unknown field
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Good to know
|
|
43
|
+
|
|
44
|
+
- **Looking for a field or argument?** Everything is typed, with the schema's docs as JSDoc. In the installed package, see `dist/generated/schema.d.ts` for object and input types and `dist/generated/roots.d.ts` for the root fields.
|
|
45
|
+
- **Read scalars, call objects.** `found.title` selects a scalar; `found.studio(...)` selects an object with a nested selection.
|
|
46
|
+
- **Return only selections.** The returned object may contain just selected fields, nested selections and `$on` spreads. Compute things after the request, not inside the callback.
|
|
47
|
+
- **Reach for plain GraphQL last.** Use `stash.request` only for something the typed API can't express.
|
|
48
|
+
|
|
49
|
+
## Selecting fields
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
const scene = await stash.findScene({ id: "42" }, (found) => ({
|
|
53
|
+
id: found.id,
|
|
54
|
+
title: found.title, // string | null, same as in the schema
|
|
55
|
+
studio: found.studio((studio) => ({ id: studio.id, name: studio.name })), // call it to pick nested fields
|
|
56
|
+
}));
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Want a different name? Just use a different key. Fields with arguments can even be picked twice:
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
const { groups } = await stash.findGroups((result) => ({
|
|
63
|
+
groups: result.groups((group) => ({
|
|
64
|
+
id: group.id,
|
|
65
|
+
rating: group.rating100,
|
|
66
|
+
directScenes: group.scene_count({ depth: 0 }),
|
|
67
|
+
allScenes: group.scene_count({ depth: -1 }),
|
|
68
|
+
})),
|
|
69
|
+
}));
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
For unions and interfaces, use `$on` to pick fields of one type. `__typename` is added for you, so you can narrow on it:
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
const image = await stash.findImage({ id: "7" }, (found) => ({
|
|
76
|
+
visual_files: found.visual_files((file) => ({
|
|
77
|
+
...file.$on("ImageFile", (picture) => ({ path: picture.path, width: picture.width })),
|
|
78
|
+
...file.$on("VideoFile", (video) => ({ path: video.path, duration: video.duration })),
|
|
79
|
+
})),
|
|
80
|
+
}));
|
|
81
|
+
|
|
82
|
+
for (const file of image?.visual_files ?? []) {
|
|
83
|
+
if (file.__typename === "ImageFile") console.log(file.width);
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Enums and input types are exported with the same names as in the schema:
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
import type { CriterionModifier, SceneFilterType } from "stash-graphql-client";
|
|
91
|
+
|
|
92
|
+
const favorites: SceneFilterType = {
|
|
93
|
+
performer_favorite: true,
|
|
94
|
+
rating100: { value: 80, modifier: "GREATER_THAN" satisfies CriterionModifier },
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
await stash.findScenes({ scene_filter: favorites }, (result) => ({ count: result.count }));
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Reusing selections
|
|
101
|
+
|
|
102
|
+
A selection is just a function, so you can share it like a fragment. `Fields` and `Result` are only types, so they cost nothing at runtime:
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
import type { Args, Fields, Result } from "stash-graphql-client";
|
|
106
|
+
|
|
107
|
+
const tagFields = ({ id, name }: Fields<"Tag">) => ({ id, name });
|
|
108
|
+
|
|
109
|
+
export const performerCard = (performer: Fields<"Performer">) => ({
|
|
110
|
+
id: performer.id,
|
|
111
|
+
name: performer.name,
|
|
112
|
+
image: performer.image_path,
|
|
113
|
+
tags: performer.tags(tagFields),
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
export type PerformerCard = Result<typeof performerCard>;
|
|
117
|
+
// { id: string; name: string; image: string | null; tags: { id: string; name: string }[] }
|
|
118
|
+
|
|
119
|
+
type ScenesArgs = Args<"findScenes">;
|
|
120
|
+
|
|
121
|
+
// Add parameters to make it configurable
|
|
122
|
+
const sceneCard = (scene: Fields<"Scene">, withFiles: boolean) => ({
|
|
123
|
+
id: scene.id,
|
|
124
|
+
tags: scene.tags(tagFields),
|
|
125
|
+
...(withFiles ? { files: scene.files(({ path, size }) => ({ path, size })) } : {}), // files?: ...
|
|
126
|
+
});
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Callbacks run once, just to build the request, so don't `await` or do side effects in them. Destructuring the parameter picks those fields, but `...rest` picks nothing.
|
|
130
|
+
|
|
131
|
+
## Several root fields in one request
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
const totals = await stash.query((query) => ({
|
|
135
|
+
scenes: query.findScenes((result) => ({ count: result.count })),
|
|
136
|
+
favorites: query.findScenes({ scene_filter: { performer_favorite: true } }, (result) => ({
|
|
137
|
+
count: result.count,
|
|
138
|
+
})),
|
|
139
|
+
performers: query.findPerformers((result) => ({ count: result.count })),
|
|
140
|
+
}));
|
|
141
|
+
|
|
142
|
+
totals.favorites.count; // number
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
That's a single HTTP request, and the keys become aliases. `mutation` works the same way and runs in the order you wrote. A query and a mutation can't share a request, and Stash doesn't accept batched HTTP, so use `Promise.all` to run them side by side.
|
|
146
|
+
|
|
147
|
+
## Mutations and file uploads
|
|
148
|
+
|
|
149
|
+
```ts
|
|
150
|
+
const updated = await stash.performerUpdate(
|
|
151
|
+
{ input: { id: "3", favorite: true, rating100: 90 } },
|
|
152
|
+
(performer) => ({ id: performer.id, favorite: performer.favorite }),
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
// Returns a plain boolean, so there's nothing to select
|
|
156
|
+
const deleted: boolean = await stash.performerDestroy({ input: { id: "3" } });
|
|
157
|
+
|
|
158
|
+
// Pass a Blob or File for uploads
|
|
159
|
+
const jobId = await stash.importObjects({
|
|
160
|
+
input: {
|
|
161
|
+
file: Bun.file("export.zip"),
|
|
162
|
+
duplicateBehaviour: "IGNORE",
|
|
163
|
+
missingRefBehaviour: "IGNORE",
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Subscriptions
|
|
169
|
+
|
|
170
|
+
```ts
|
|
171
|
+
// Returns an async iterator; leaving the loop unsubscribes
|
|
172
|
+
const jobs = stash.jobsSubscribe((update) => ({
|
|
173
|
+
type: update.type,
|
|
174
|
+
job: update.job((job) => ({ id: job.id, progress: job.progress })),
|
|
175
|
+
}));
|
|
176
|
+
|
|
177
|
+
for await (const update of jobs) {
|
|
178
|
+
console.log(update.type, update.job.progress);
|
|
179
|
+
if (update.type === "REMOVE") break;
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Raw GraphQL and errors
|
|
184
|
+
|
|
185
|
+
```ts
|
|
186
|
+
import { StashError } from "stash-graphql-client";
|
|
187
|
+
|
|
188
|
+
try {
|
|
189
|
+
// Send any GraphQL you like; the result is untyped
|
|
190
|
+
await stash.request(`query ($id: ID!) { scene: findScene(id: $id) { id title } }`, { id: "42" });
|
|
191
|
+
} catch (error) {
|
|
192
|
+
// If part of the request worked, that data is in error.data
|
|
193
|
+
if (error instanceof StashError) console.error(error.status, error.errors);
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Options
|
|
198
|
+
|
|
199
|
+
```ts
|
|
200
|
+
createStash({
|
|
201
|
+
endpoint: "https://stash.example.com/graphql",
|
|
202
|
+
apiKey: "...",
|
|
203
|
+
headers: { "X-Proxy-Token": "..." }, // e.g. for a reverse proxy
|
|
204
|
+
fetch: (url, init) => fetch(url, { ...init, signal: AbortSignal.timeout(10_000) }), // or any fetch-like function
|
|
205
|
+
// subscriptions: your own graphql-ws client
|
|
206
|
+
});
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## Development
|
|
210
|
+
|
|
211
|
+
There's a script you can run against your own server in [`example/index.ts`](example/index.ts): `STASH_URL=http://localhost:9999 STASH_API_KEY=... bun example/index.ts`.
|
|
212
|
+
|
|
213
|
+
```sh
|
|
214
|
+
bun install
|
|
215
|
+
bun check # type check
|
|
216
|
+
bun run lint # oxfmt and oxlint
|
|
217
|
+
bun test # also type-checks the examples in this README
|
|
218
|
+
bun run build # writes dist
|
|
219
|
+
bun run schema # update to the latest Stash release (or: bun run schema v0.30.0)
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
> [!NOTE]
|
|
223
|
+
> `@defer` isn't supported. Stash declares it but ignores it and always sends the full result.
|
|
224
|
+
|
|
225
|
+
> [!NOTE]
|
|
226
|
+
> This project was written with AI, and it runs in my own projects day to day. If you hit a bug, please [open an issue](https://github.com/shalemore/stash-graphql-client/issues/new/choose).
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type StashOptions } from "./transport.ts";
|
|
2
|
+
import type { Fields, Output, RootMethods, Selection } from "./types.ts";
|
|
3
|
+
/** A client with one method per root field, plus `query`, `mutation` and `request`. */
|
|
4
|
+
export interface StashClient extends RootMethods {
|
|
5
|
+
/**
|
|
6
|
+
* Sends several queries in one request. The keys become the result keys, so the same root
|
|
7
|
+
* field can be selected twice with different arguments.
|
|
8
|
+
*/
|
|
9
|
+
query<S extends Selection>(select: (query: Fields<"Query">) => S): Promise<Output<"Query", S>>;
|
|
10
|
+
/** Sends several mutations in one request; Stash runs them in order. */
|
|
11
|
+
mutation<S extends Selection>(select: (mutation: Fields<"Mutation">) => S): Promise<Output<"Mutation", S>>;
|
|
12
|
+
/** Sends any GraphQL document as is and returns the untyped `data`. */
|
|
13
|
+
request(query: string, variables?: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
14
|
+
}
|
|
15
|
+
export declare function createStash(options: StashOptions): StashClient;
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { ROOTS } from "./generated/roots.js";
|
|
2
|
+
import { buildRequest, field, fields } from "./request.js";
|
|
3
|
+
import { createTransport } from "./transport.js";
|
|
4
|
+
export function createStash(options) {
|
|
5
|
+
const transport = createTransport(options);
|
|
6
|
+
// Async, so that errors in selection callbacks reject instead of throwing.
|
|
7
|
+
const send = async (kind, select) => {
|
|
8
|
+
const { request, upload } = buildRequest(kind, select());
|
|
9
|
+
return transport.send(request, upload);
|
|
10
|
+
};
|
|
11
|
+
const operation = (kind) => (select) => send(kind, () => select(fields));
|
|
12
|
+
const methods = Object.fromEntries(Object.entries(ROOTS).map(([name, [kind]]) => {
|
|
13
|
+
const root = (args, select) => ({ [name]: field(name)(args, select) });
|
|
14
|
+
return [
|
|
15
|
+
name,
|
|
16
|
+
kind === "subscription"
|
|
17
|
+
? (args, select) => transport.subscribe(buildRequest(kind, root(args, select)).request, name)
|
|
18
|
+
: async (args, select) => (await send(kind, () => root(args, select)))[name],
|
|
19
|
+
];
|
|
20
|
+
}));
|
|
21
|
+
const client = {
|
|
22
|
+
...methods,
|
|
23
|
+
query: operation("query"),
|
|
24
|
+
mutation: operation("mutation"),
|
|
25
|
+
request: transport.request,
|
|
26
|
+
};
|
|
27
|
+
// oxlint-disable-next-line typescript/no-unsafe-type-assertion -- one method per entry in ROOTS
|
|
28
|
+
return client;
|
|
29
|
+
}
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
// Generated by scripts/generate.ts from the schema of Stash v0.31.1. Do not edit.
|
|
2
|
+
export const STASH_VERSION = "v0.31.1";
|
|
3
|
+
export const ROOTS = {
|
|
4
|
+
findSavedFilter: ["query", { id: "ID!" }],
|
|
5
|
+
findSavedFilters: ["query", { mode: "FilterMode" }],
|
|
6
|
+
findDefaultFilter: ["query", { mode: "FilterMode!" }],
|
|
7
|
+
findFile: ["query", { id: "ID", path: "String" }],
|
|
8
|
+
findFiles: ["query", { file_filter: "FileFilterType", filter: "FindFilterType", ids: "[ID!]" }],
|
|
9
|
+
findFolder: ["query", { id: "ID", path: "String" }],
|
|
10
|
+
findFolders: [
|
|
11
|
+
"query",
|
|
12
|
+
{ folder_filter: "FolderFilterType", filter: "FindFilterType", ids: "[ID!]" },
|
|
13
|
+
],
|
|
14
|
+
findScene: ["query", { id: "ID", checksum: "String" }],
|
|
15
|
+
findSceneByHash: ["query", { input: "SceneHashInput!" }],
|
|
16
|
+
findScenes: [
|
|
17
|
+
"query",
|
|
18
|
+
{
|
|
19
|
+
scene_filter: "SceneFilterType",
|
|
20
|
+
scene_ids: "[Int!]",
|
|
21
|
+
ids: "[ID!]",
|
|
22
|
+
filter: "FindFilterType",
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
findScenesByPathRegex: ["query", { filter: "FindFilterType" }],
|
|
26
|
+
findDuplicateScenes: ["query", { distance: "Int", duration_diff: "Float" }],
|
|
27
|
+
sceneStreams: ["query", { id: "ID" }],
|
|
28
|
+
parseSceneFilenames: ["query", { filter: "FindFilterType", config: "SceneParserInput!" }],
|
|
29
|
+
findSceneMarkers: [
|
|
30
|
+
"query",
|
|
31
|
+
{ scene_marker_filter: "SceneMarkerFilterType", filter: "FindFilterType", ids: "[ID!]" },
|
|
32
|
+
],
|
|
33
|
+
findImage: ["query", { id: "ID", checksum: "String" }],
|
|
34
|
+
findImages: [
|
|
35
|
+
"query",
|
|
36
|
+
{
|
|
37
|
+
image_filter: "ImageFilterType",
|
|
38
|
+
image_ids: "[Int!]",
|
|
39
|
+
ids: "[ID!]",
|
|
40
|
+
filter: "FindFilterType",
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
findPerformer: ["query", { id: "ID!" }],
|
|
44
|
+
findPerformers: [
|
|
45
|
+
"query",
|
|
46
|
+
{
|
|
47
|
+
performer_filter: "PerformerFilterType",
|
|
48
|
+
filter: "FindFilterType",
|
|
49
|
+
performer_ids: "[Int!]",
|
|
50
|
+
ids: "[ID!]",
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
findStudio: ["query", { id: "ID!" }],
|
|
54
|
+
findStudios: [
|
|
55
|
+
"query",
|
|
56
|
+
{ studio_filter: "StudioFilterType", filter: "FindFilterType", ids: "[ID!]" },
|
|
57
|
+
],
|
|
58
|
+
findMovie: ["query", { id: "ID!" }],
|
|
59
|
+
findMovies: [
|
|
60
|
+
"query",
|
|
61
|
+
{ movie_filter: "MovieFilterType", filter: "FindFilterType", ids: "[ID!]" },
|
|
62
|
+
],
|
|
63
|
+
findGroup: ["query", { id: "ID!" }],
|
|
64
|
+
findGroups: [
|
|
65
|
+
"query",
|
|
66
|
+
{ group_filter: "GroupFilterType", filter: "FindFilterType", ids: "[ID!]" },
|
|
67
|
+
],
|
|
68
|
+
findGallery: ["query", { id: "ID!" }],
|
|
69
|
+
findGalleries: [
|
|
70
|
+
"query",
|
|
71
|
+
{ gallery_filter: "GalleryFilterType", filter: "FindFilterType", ids: "[ID!]" },
|
|
72
|
+
],
|
|
73
|
+
findTag: ["query", { id: "ID!" }],
|
|
74
|
+
findTags: ["query", { tag_filter: "TagFilterType", filter: "FindFilterType", ids: "[ID!]" }],
|
|
75
|
+
markerWall: ["query", { q: "String" }],
|
|
76
|
+
sceneWall: ["query", { q: "String" }],
|
|
77
|
+
markerStrings: ["query", { q: "String", sort: "String" }],
|
|
78
|
+
stats: ["query", {}],
|
|
79
|
+
sceneMarkerTags: ["query", { scene_id: "ID!" }],
|
|
80
|
+
logs: ["query", {}],
|
|
81
|
+
listScrapers: ["query", { types: "[ScrapeContentType!]!" }],
|
|
82
|
+
scrapeSingleScene: ["query", { source: "ScraperSourceInput!", input: "ScrapeSingleSceneInput!" }],
|
|
83
|
+
scrapeMultiScenes: ["query", { source: "ScraperSourceInput!", input: "ScrapeMultiScenesInput!" }],
|
|
84
|
+
scrapeSingleStudio: [
|
|
85
|
+
"query",
|
|
86
|
+
{ source: "ScraperSourceInput!", input: "ScrapeSingleStudioInput!" },
|
|
87
|
+
],
|
|
88
|
+
scrapeSingleTag: ["query", { source: "ScraperSourceInput!", input: "ScrapeSingleTagInput!" }],
|
|
89
|
+
scrapeSinglePerformer: [
|
|
90
|
+
"query",
|
|
91
|
+
{ source: "ScraperSourceInput!", input: "ScrapeSinglePerformerInput!" },
|
|
92
|
+
],
|
|
93
|
+
scrapeMultiPerformers: [
|
|
94
|
+
"query",
|
|
95
|
+
{ source: "ScraperSourceInput!", input: "ScrapeMultiPerformersInput!" },
|
|
96
|
+
],
|
|
97
|
+
scrapeSingleGallery: [
|
|
98
|
+
"query",
|
|
99
|
+
{ source: "ScraperSourceInput!", input: "ScrapeSingleGalleryInput!" },
|
|
100
|
+
],
|
|
101
|
+
scrapeSingleMovie: ["query", { source: "ScraperSourceInput!", input: "ScrapeSingleMovieInput!" }],
|
|
102
|
+
scrapeSingleGroup: ["query", { source: "ScraperSourceInput!", input: "ScrapeSingleGroupInput!" }],
|
|
103
|
+
scrapeSingleImage: ["query", { source: "ScraperSourceInput!", input: "ScrapeSingleImageInput!" }],
|
|
104
|
+
scrapeURL: ["query", { url: "String!", ty: "ScrapeContentType!" }],
|
|
105
|
+
scrapePerformerURL: ["query", { url: "String!" }],
|
|
106
|
+
scrapeSceneURL: ["query", { url: "String!" }],
|
|
107
|
+
scrapeGalleryURL: ["query", { url: "String!" }],
|
|
108
|
+
scrapeImageURL: ["query", { url: "String!" }],
|
|
109
|
+
scrapeMovieURL: ["query", { url: "String!" }],
|
|
110
|
+
scrapeGroupURL: ["query", { url: "String!" }],
|
|
111
|
+
plugins: ["query", {}],
|
|
112
|
+
pluginTasks: ["query", {}],
|
|
113
|
+
installedPackages: ["query", { type: "PackageType!" }],
|
|
114
|
+
availablePackages: ["query", { type: "PackageType!", source: "String!" }],
|
|
115
|
+
configuration: ["query", {}],
|
|
116
|
+
directory: ["query", { path: "String", locale: "String" }],
|
|
117
|
+
validateStashBoxCredentials: ["query", { input: "StashBoxInput!" }],
|
|
118
|
+
systemStatus: ["query", {}],
|
|
119
|
+
jobQueue: ["query", {}],
|
|
120
|
+
findJob: ["query", { input: "FindJobInput!" }],
|
|
121
|
+
dlnaStatus: ["query", {}],
|
|
122
|
+
allScenes: ["query", {}],
|
|
123
|
+
allSceneMarkers: ["query", {}],
|
|
124
|
+
allImages: ["query", {}],
|
|
125
|
+
allGalleries: ["query", {}],
|
|
126
|
+
allPerformers: ["query", {}],
|
|
127
|
+
allTags: ["query", {}],
|
|
128
|
+
allStudios: ["query", {}],
|
|
129
|
+
allMovies: ["query", {}],
|
|
130
|
+
version: ["query", {}],
|
|
131
|
+
latestversion: ["query", {}],
|
|
132
|
+
setup: ["mutation", { input: "SetupInput!" }],
|
|
133
|
+
migrate: ["mutation", { input: "MigrateInput!" }],
|
|
134
|
+
downloadFFMpeg: ["mutation", {}],
|
|
135
|
+
sceneCreate: ["mutation", { input: "SceneCreateInput!" }],
|
|
136
|
+
sceneUpdate: ["mutation", { input: "SceneUpdateInput!" }],
|
|
137
|
+
sceneMerge: ["mutation", { input: "SceneMergeInput!" }],
|
|
138
|
+
bulkSceneUpdate: ["mutation", { input: "BulkSceneUpdateInput!" }],
|
|
139
|
+
sceneDestroy: ["mutation", { input: "SceneDestroyInput!" }],
|
|
140
|
+
scenesDestroy: ["mutation", { input: "ScenesDestroyInput!" }],
|
|
141
|
+
scenesUpdate: ["mutation", { input: "[SceneUpdateInput!]!" }],
|
|
142
|
+
sceneIncrementO: ["mutation", { id: "ID!" }],
|
|
143
|
+
sceneDecrementO: ["mutation", { id: "ID!" }],
|
|
144
|
+
sceneAddO: ["mutation", { id: "ID!", times: "[Timestamp!]" }],
|
|
145
|
+
sceneDeleteO: ["mutation", { id: "ID!", times: "[Timestamp!]" }],
|
|
146
|
+
sceneResetO: ["mutation", { id: "ID!" }],
|
|
147
|
+
sceneSaveActivity: ["mutation", { id: "ID!", resume_time: "Float", playDuration: "Float" }],
|
|
148
|
+
sceneResetActivity: [
|
|
149
|
+
"mutation",
|
|
150
|
+
{ id: "ID!", reset_resume: "Boolean", reset_duration: "Boolean" },
|
|
151
|
+
],
|
|
152
|
+
sceneIncrementPlayCount: ["mutation", { id: "ID!" }],
|
|
153
|
+
sceneAddPlay: ["mutation", { id: "ID!", times: "[Timestamp!]" }],
|
|
154
|
+
sceneDeletePlay: ["mutation", { id: "ID!", times: "[Timestamp!]" }],
|
|
155
|
+
sceneResetPlayCount: ["mutation", { id: "ID!" }],
|
|
156
|
+
sceneGenerateScreenshot: ["mutation", { id: "ID!", at: "Float" }],
|
|
157
|
+
sceneMarkerCreate: ["mutation", { input: "SceneMarkerCreateInput!" }],
|
|
158
|
+
sceneMarkerUpdate: ["mutation", { input: "SceneMarkerUpdateInput!" }],
|
|
159
|
+
bulkSceneMarkerUpdate: ["mutation", { input: "BulkSceneMarkerUpdateInput!" }],
|
|
160
|
+
sceneMarkerDestroy: ["mutation", { id: "ID!" }],
|
|
161
|
+
sceneMarkersDestroy: ["mutation", { ids: "[ID!]!" }],
|
|
162
|
+
sceneAssignFile: ["mutation", { input: "AssignSceneFileInput!" }],
|
|
163
|
+
imageUpdate: ["mutation", { input: "ImageUpdateInput!" }],
|
|
164
|
+
bulkImageUpdate: ["mutation", { input: "BulkImageUpdateInput!" }],
|
|
165
|
+
imageDestroy: ["mutation", { input: "ImageDestroyInput!" }],
|
|
166
|
+
imagesDestroy: ["mutation", { input: "ImagesDestroyInput!" }],
|
|
167
|
+
imagesUpdate: ["mutation", { input: "[ImageUpdateInput!]!" }],
|
|
168
|
+
imageIncrementO: ["mutation", { id: "ID!" }],
|
|
169
|
+
imageDecrementO: ["mutation", { id: "ID!" }],
|
|
170
|
+
imageResetO: ["mutation", { id: "ID!" }],
|
|
171
|
+
galleryCreate: ["mutation", { input: "GalleryCreateInput!" }],
|
|
172
|
+
galleryUpdate: ["mutation", { input: "GalleryUpdateInput!" }],
|
|
173
|
+
bulkGalleryUpdate: ["mutation", { input: "BulkGalleryUpdateInput!" }],
|
|
174
|
+
galleryDestroy: ["mutation", { input: "GalleryDestroyInput!" }],
|
|
175
|
+
galleriesUpdate: ["mutation", { input: "[GalleryUpdateInput!]!" }],
|
|
176
|
+
addGalleryImages: ["mutation", { input: "GalleryAddInput!" }],
|
|
177
|
+
removeGalleryImages: ["mutation", { input: "GalleryRemoveInput!" }],
|
|
178
|
+
setGalleryCover: ["mutation", { input: "GallerySetCoverInput!" }],
|
|
179
|
+
resetGalleryCover: ["mutation", { input: "GalleryResetCoverInput!" }],
|
|
180
|
+
galleryChapterCreate: ["mutation", { input: "GalleryChapterCreateInput!" }],
|
|
181
|
+
galleryChapterUpdate: ["mutation", { input: "GalleryChapterUpdateInput!" }],
|
|
182
|
+
galleryChapterDestroy: ["mutation", { id: "ID!" }],
|
|
183
|
+
performerCreate: ["mutation", { input: "PerformerCreateInput!" }],
|
|
184
|
+
performerUpdate: ["mutation", { input: "PerformerUpdateInput!" }],
|
|
185
|
+
performerDestroy: ["mutation", { input: "PerformerDestroyInput!" }],
|
|
186
|
+
performersDestroy: ["mutation", { ids: "[ID!]!" }],
|
|
187
|
+
bulkPerformerUpdate: ["mutation", { input: "BulkPerformerUpdateInput!" }],
|
|
188
|
+
performerMerge: ["mutation", { input: "PerformerMergeInput!" }],
|
|
189
|
+
studioCreate: ["mutation", { input: "StudioCreateInput!" }],
|
|
190
|
+
studioUpdate: ["mutation", { input: "StudioUpdateInput!" }],
|
|
191
|
+
studioDestroy: ["mutation", { input: "StudioDestroyInput!" }],
|
|
192
|
+
studiosDestroy: ["mutation", { ids: "[ID!]!" }],
|
|
193
|
+
bulkStudioUpdate: ["mutation", { input: "BulkStudioUpdateInput!" }],
|
|
194
|
+
movieCreate: ["mutation", { input: "MovieCreateInput!" }],
|
|
195
|
+
movieUpdate: ["mutation", { input: "MovieUpdateInput!" }],
|
|
196
|
+
movieDestroy: ["mutation", { input: "MovieDestroyInput!" }],
|
|
197
|
+
moviesDestroy: ["mutation", { ids: "[ID!]!" }],
|
|
198
|
+
bulkMovieUpdate: ["mutation", { input: "BulkMovieUpdateInput!" }],
|
|
199
|
+
groupCreate: ["mutation", { input: "GroupCreateInput!" }],
|
|
200
|
+
groupUpdate: ["mutation", { input: "GroupUpdateInput!" }],
|
|
201
|
+
groupDestroy: ["mutation", { input: "GroupDestroyInput!" }],
|
|
202
|
+
groupsDestroy: ["mutation", { ids: "[ID!]!" }],
|
|
203
|
+
bulkGroupUpdate: ["mutation", { input: "BulkGroupUpdateInput!" }],
|
|
204
|
+
addGroupSubGroups: ["mutation", { input: "GroupSubGroupAddInput!" }],
|
|
205
|
+
removeGroupSubGroups: ["mutation", { input: "GroupSubGroupRemoveInput!" }],
|
|
206
|
+
reorderSubGroups: ["mutation", { input: "ReorderSubGroupsInput!" }],
|
|
207
|
+
tagCreate: ["mutation", { input: "TagCreateInput!" }],
|
|
208
|
+
tagUpdate: ["mutation", { input: "TagUpdateInput!" }],
|
|
209
|
+
tagDestroy: ["mutation", { input: "TagDestroyInput!" }],
|
|
210
|
+
tagsDestroy: ["mutation", { ids: "[ID!]!" }],
|
|
211
|
+
tagsMerge: ["mutation", { input: "TagsMergeInput!" }],
|
|
212
|
+
bulkTagUpdate: ["mutation", { input: "BulkTagUpdateInput!" }],
|
|
213
|
+
moveFiles: ["mutation", { input: "MoveFilesInput!" }],
|
|
214
|
+
deleteFiles: ["mutation", { ids: "[ID!]!" }],
|
|
215
|
+
destroyFiles: ["mutation", { ids: "[ID!]!" }],
|
|
216
|
+
fileSetFingerprints: ["mutation", { input: "FileSetFingerprintsInput!" }],
|
|
217
|
+
revealFileInFileManager: ["mutation", { id: "ID!" }],
|
|
218
|
+
revealFolderInFileManager: ["mutation", { id: "ID!" }],
|
|
219
|
+
saveFilter: ["mutation", { input: "SaveFilterInput!" }],
|
|
220
|
+
destroySavedFilter: ["mutation", { input: "DestroyFilterInput!" }],
|
|
221
|
+
setDefaultFilter: ["mutation", { input: "SetDefaultFilterInput!" }],
|
|
222
|
+
configureGeneral: ["mutation", { input: "ConfigGeneralInput!" }],
|
|
223
|
+
configureInterface: ["mutation", { input: "ConfigInterfaceInput!" }],
|
|
224
|
+
configureDLNA: ["mutation", { input: "ConfigDLNAInput!" }],
|
|
225
|
+
configureScraping: ["mutation", { input: "ConfigScrapingInput!" }],
|
|
226
|
+
configureDefaults: ["mutation", { input: "ConfigDefaultSettingsInput!" }],
|
|
227
|
+
configurePlugin: ["mutation", { plugin_id: "ID!", input: "Map!" }],
|
|
228
|
+
configureUI: ["mutation", { input: "Map", partial: "Map" }],
|
|
229
|
+
configureUISetting: ["mutation", { key: "String!", value: "Any" }],
|
|
230
|
+
generateAPIKey: ["mutation", { input: "GenerateAPIKeyInput!" }],
|
|
231
|
+
exportObjects: ["mutation", { input: "ExportObjectsInput!" }],
|
|
232
|
+
importObjects: ["mutation", { input: "ImportObjectsInput!" }, true],
|
|
233
|
+
metadataImport: ["mutation", {}],
|
|
234
|
+
metadataExport: ["mutation", {}],
|
|
235
|
+
metadataScan: ["mutation", { input: "ScanMetadataInput!" }],
|
|
236
|
+
metadataGenerate: ["mutation", { input: "GenerateMetadataInput!" }],
|
|
237
|
+
metadataAutoTag: ["mutation", { input: "AutoTagMetadataInput!" }],
|
|
238
|
+
metadataClean: ["mutation", { input: "CleanMetadataInput!" }],
|
|
239
|
+
metadataCleanGenerated: ["mutation", { input: "CleanGeneratedInput!" }],
|
|
240
|
+
metadataIdentify: ["mutation", { input: "IdentifyMetadataInput!" }],
|
|
241
|
+
migrateHashNaming: ["mutation", {}],
|
|
242
|
+
migrateSceneScreenshots: ["mutation", { input: "MigrateSceneScreenshotsInput!" }],
|
|
243
|
+
migrateBlobs: ["mutation", { input: "MigrateBlobsInput!" }],
|
|
244
|
+
anonymiseDatabase: ["mutation", { input: "AnonymiseDatabaseInput!" }],
|
|
245
|
+
optimiseDatabase: ["mutation", {}],
|
|
246
|
+
reloadScrapers: ["mutation", {}],
|
|
247
|
+
setPluginsEnabled: ["mutation", { enabledMap: "BoolMap!" }],
|
|
248
|
+
runPluginTask: [
|
|
249
|
+
"mutation",
|
|
250
|
+
{
|
|
251
|
+
plugin_id: "ID!",
|
|
252
|
+
task_name: "String",
|
|
253
|
+
description: "String",
|
|
254
|
+
args: "[PluginArgInput!]",
|
|
255
|
+
args_map: "Map",
|
|
256
|
+
},
|
|
257
|
+
],
|
|
258
|
+
runPluginOperation: ["mutation", { plugin_id: "ID!", args: "Map" }],
|
|
259
|
+
reloadPlugins: ["mutation", {}],
|
|
260
|
+
installPackages: ["mutation", { type: "PackageType!", packages: "[PackageSpecInput!]!" }],
|
|
261
|
+
updatePackages: ["mutation", { type: "PackageType!", packages: "[PackageSpecInput!]" }],
|
|
262
|
+
uninstallPackages: ["mutation", { type: "PackageType!", packages: "[PackageSpecInput!]!" }],
|
|
263
|
+
stopJob: ["mutation", { job_id: "ID!" }],
|
|
264
|
+
stopAllJobs: ["mutation", {}],
|
|
265
|
+
submitStashBoxFingerprints: ["mutation", { input: "StashBoxFingerprintSubmissionInput!" }],
|
|
266
|
+
submitStashBoxSceneDraft: ["mutation", { input: "StashBoxDraftSubmissionInput!" }],
|
|
267
|
+
submitStashBoxPerformerDraft: ["mutation", { input: "StashBoxDraftSubmissionInput!" }],
|
|
268
|
+
backupDatabase: ["mutation", { input: "BackupDatabaseInput!" }],
|
|
269
|
+
querySQL: ["mutation", { sql: "String!", args: "[Any]" }],
|
|
270
|
+
execSQL: ["mutation", { sql: "String!", args: "[Any]" }],
|
|
271
|
+
stashBoxBatchPerformerTag: ["mutation", { input: "StashBoxBatchTagInput!" }],
|
|
272
|
+
stashBoxBatchStudioTag: ["mutation", { input: "StashBoxBatchTagInput!" }],
|
|
273
|
+
stashBoxBatchTagTag: ["mutation", { input: "StashBoxBatchTagInput!" }],
|
|
274
|
+
enableDLNA: ["mutation", { input: "EnableDLNAInput!" }],
|
|
275
|
+
disableDLNA: ["mutation", { input: "DisableDLNAInput!" }],
|
|
276
|
+
addTempDLNAIP: ["mutation", { input: "AddTempDLNAIPInput!" }],
|
|
277
|
+
removeTempDLNAIP: ["mutation", { input: "RemoveTempDLNAIPInput!" }],
|
|
278
|
+
jobsSubscribe: ["subscription", {}],
|
|
279
|
+
loggingSubscribe: ["subscription", {}],
|
|
280
|
+
scanCompleteSubscribe: ["subscription", {}],
|
|
281
|
+
};
|