2023-07-28 00:36:00 +00:00
|
|
|
"The query root for this schema"
|
2019-03-26 15:35:06 +00:00
|
|
|
type Query {
|
2021-06-16 04:53:32 +00:00
|
|
|
# Filters
|
2022-06-14 00:34:04 +00:00
|
|
|
findSavedFilter(id: ID!): SavedFilter
|
|
|
|
findSavedFilters(mode: FilterMode): [SavedFilter!]!
|
2021-06-16 04:53:32 +00:00
|
|
|
findDefaultFilter(mode: FilterMode!): SavedFilter
|
|
|
|
|
2023-07-28 00:36:00 +00:00
|
|
|
"Find a scene by ID or Checksum"
|
2019-03-26 15:35:06 +00:00
|
|
|
findScene(id: ID, checksum: String): Scene
|
2020-08-06 01:21:14 +00:00
|
|
|
findSceneByHash(input: SceneHashInput!): Scene
|
2021-10-14 04:16:45 +00:00
|
|
|
|
2023-07-28 00:36:00 +00:00
|
|
|
"A function which queries Scene objects"
|
|
|
|
findScenes(
|
|
|
|
scene_filter: SceneFilterType
|
2024-02-18 23:25:08 +00:00
|
|
|
scene_ids: [Int!] @deprecated(reason: "use ids")
|
|
|
|
ids: [ID!]
|
2023-07-28 00:36:00 +00:00
|
|
|
filter: FindFilterType
|
|
|
|
): FindScenesResultType!
|
2019-03-26 15:35:06 +00:00
|
|
|
|
2019-10-30 13:37:21 +00:00
|
|
|
findScenesByPathRegex(filter: FindFilterType): FindScenesResultType!
|
|
|
|
|
2023-05-03 05:01:59 +00:00
|
|
|
"""
|
|
|
|
Returns any groups of scenes that are perceptual duplicates within the queried distance
|
|
|
|
and the difference between their duration is smaller than durationDiff
|
|
|
|
"""
|
|
|
|
findDuplicateScenes(
|
2023-07-28 00:36:00 +00:00
|
|
|
distance: Int
|
|
|
|
"""
|
|
|
|
Max difference in seconds between files in order to be considered for similarity matching.
|
|
|
|
Fractional seconds are ok: 0.5 will mean only files that have durations within 0.5 seconds between them will be matched based on PHash distance.
|
|
|
|
"""
|
2023-05-03 05:01:59 +00:00
|
|
|
duration_diff: Float
|
|
|
|
): [[Scene!]!]!
|
2021-04-11 23:04:40 +00:00
|
|
|
|
2023-07-28 00:36:00 +00:00
|
|
|
"Return valid stream paths"
|
2020-07-23 01:56:08 +00:00
|
|
|
sceneStreams(id: ID): [SceneStreamEndpoint!]!
|
|
|
|
|
2023-07-28 00:36:00 +00:00
|
|
|
parseSceneFilenames(
|
|
|
|
filter: FindFilterType
|
|
|
|
config: SceneParserInput!
|
|
|
|
): SceneParserResultType!
|
2019-11-07 12:49:08 +00:00
|
|
|
|
2023-07-28 00:36:00 +00:00
|
|
|
"A function which queries SceneMarker objects"
|
|
|
|
findSceneMarkers(
|
|
|
|
scene_marker_filter: SceneMarkerFilterType
|
|
|
|
filter: FindFilterType
|
|
|
|
): FindSceneMarkersResultType!
|
2019-03-26 15:35:06 +00:00
|
|
|
|
2020-10-12 23:12:46 +00:00
|
|
|
findImage(id: ID, checksum: String): Image
|
2021-10-14 04:16:45 +00:00
|
|
|
|
2023-07-28 00:36:00 +00:00
|
|
|
"A function which queries Scene objects"
|
|
|
|
findImages(
|
|
|
|
image_filter: ImageFilterType
|
2024-02-18 23:25:08 +00:00
|
|
|
image_ids: [Int!] @deprecated(reason: "use ids")
|
|
|
|
ids: [ID!]
|
2023-07-28 00:36:00 +00:00
|
|
|
filter: FindFilterType
|
|
|
|
): FindImagesResultType!
|
2020-10-12 23:12:46 +00:00
|
|
|
|
2023-07-28 00:36:00 +00:00
|
|
|
"Find a performer by ID"
|
2019-03-26 15:35:06 +00:00
|
|
|
findPerformer(id: ID!): Performer
|
2023-07-28 00:36:00 +00:00
|
|
|
"A function which queries Performer objects"
|
|
|
|
findPerformers(
|
|
|
|
performer_filter: PerformerFilterType
|
|
|
|
filter: FindFilterType
|
2024-02-18 23:25:08 +00:00
|
|
|
performer_ids: [Int!] @deprecated(reason: "use ids")
|
|
|
|
ids: [ID!]
|
2023-07-28 00:36:00 +00:00
|
|
|
): FindPerformersResultType!
|
2019-03-26 15:35:06 +00:00
|
|
|
|
2023-07-28 00:36:00 +00:00
|
|
|
"Find a studio by ID"
|
2019-03-26 15:35:06 +00:00
|
|
|
findStudio(id: ID!): Studio
|
2023-07-28 00:36:00 +00:00
|
|
|
"A function which queries Studio objects"
|
|
|
|
findStudios(
|
|
|
|
studio_filter: StudioFilterType
|
|
|
|
filter: FindFilterType
|
2024-02-06 00:26:16 +00:00
|
|
|
ids: [ID!]
|
2023-07-28 00:36:00 +00:00
|
|
|
): FindStudiosResultType!
|
2019-03-26 15:35:06 +00:00
|
|
|
|
2023-07-28 00:36:00 +00:00
|
|
|
"Find a movie by ID"
|
2020-03-10 03:28:15 +00:00
|
|
|
findMovie(id: ID!): Movie
|
2023-07-28 00:36:00 +00:00
|
|
|
"A function which queries Movie objects"
|
|
|
|
findMovies(
|
|
|
|
movie_filter: MovieFilterType
|
|
|
|
filter: FindFilterType
|
2024-02-18 23:25:08 +00:00
|
|
|
ids: [ID!]
|
2023-07-28 00:36:00 +00:00
|
|
|
): FindMoviesResultType!
|
2020-03-10 03:28:15 +00:00
|
|
|
|
2019-03-26 15:35:06 +00:00
|
|
|
findGallery(id: ID!): Gallery
|
2023-07-28 00:36:00 +00:00
|
|
|
findGalleries(
|
|
|
|
gallery_filter: GalleryFilterType
|
|
|
|
filter: FindFilterType
|
2024-02-09 05:42:07 +00:00
|
|
|
ids: [ID!]
|
2023-07-28 00:36:00 +00:00
|
|
|
): FindGalleriesResultType!
|
2019-03-26 15:35:06 +00:00
|
|
|
|
|
|
|
findTag(id: ID!): Tag
|
2023-07-28 00:36:00 +00:00
|
|
|
findTags(
|
|
|
|
tag_filter: TagFilterType
|
|
|
|
filter: FindFilterType
|
2024-02-18 23:25:08 +00:00
|
|
|
ids: [ID!]
|
2023-07-28 00:36:00 +00:00
|
|
|
): FindTagsResultType!
|
2019-03-26 15:35:06 +00:00
|
|
|
|
2023-07-28 00:36:00 +00:00
|
|
|
"Retrieve random scene markers for the wall"
|
2019-03-26 15:35:06 +00:00
|
|
|
markerWall(q: String): [SceneMarker!]!
|
2023-07-28 00:36:00 +00:00
|
|
|
"Retrieve random scenes for the wall"
|
2019-03-26 15:35:06 +00:00
|
|
|
sceneWall(q: String): [Scene!]!
|
|
|
|
|
2023-07-28 00:36:00 +00:00
|
|
|
"Get marker strings"
|
2019-03-26 15:35:06 +00:00
|
|
|
markerStrings(q: String, sort: String): [MarkerStringsResultType]!
|
2023-07-28 00:36:00 +00:00
|
|
|
"Get stats"
|
2019-03-26 15:35:06 +00:00
|
|
|
stats: StatsResultType!
|
2023-07-28 00:36:00 +00:00
|
|
|
"Organize scene markers by tag for a given scene ID"
|
2019-03-26 15:35:06 +00:00
|
|
|
sceneMarkerTags(scene_id: ID!): [SceneMarkerTag!]!
|
|
|
|
|
2019-10-24 23:07:07 +00:00
|
|
|
logs: [LogEntry!]!
|
|
|
|
|
2019-03-26 15:35:06 +00:00
|
|
|
# Scrapers
|
|
|
|
|
2023-07-28 00:36:00 +00:00
|
|
|
"List available scrapers"
|
Refactor scraper top half (#1893)
* Simplify scraper listing
Introduce an enum, scraper.Kind, which explains what we are looking
for. Make it possible to match this from a scraper struct.
Use the enum to rewrite all the listing code to use the same code path.
* Use a map, nitpick ScrapePerformerList
Let the cache store a map from ID of a scraper to the scraper. This
improves lookups when there are many scrapers, making it practically
O(1) rather than O(n). If many scrapers are stored, this is faster.
Since range expressions work unchanged, we don't have to change much,
and things will still work.
make Kind a Stringer
Rename ScraperPerformerList -> ScraperPerformerQuery since that name
is used in the other scrapers, and we value consistency.
Tune ScraperPerformerQuery:
* Return static errors
* Use the new functionality
* When loading scrapers, do so directly
Rather than first walking the directory structure to obtain file paths,
fold the load directly in the the filepath walk. This makes the code
for more direct.
* Use static ErrNotFound
If a scraper isn't found, return one static error. This paves the way
for eventually doing our own error-presenter in gqlgen.
* Store the cache in the Resolver state
Putting the scraperCache directly in the resolver avoids the need to
call manager.GetInstance() all over the place to get access to the
scraper cache. The cache is stored by pointer, so it should be safe,
since the cache will just update its internal state rather than being
overwritten.
We can now utilize the resolver state to grab the cache where needed.
While here, pass context.Context from the resolver down into a function,
which removes a context.TODO()
* Introduce ScrapedContent
Create a union in the GraphQL schema for all scraped content. This
simplifies the internal implementation because we get variance on
the output content type.
Introduce a new type ScrapedContentType which signifies the scraped
content you want as a caller.
Use these to generalize the List interface and the URL scraping
interface.
* Simplify the scraper API
Introduce a new interface for scraping. This interface is then
used in the upper half of the scraper code, to make the code use one
code flow rather than multiple code flows. Variance is currently at
the old scraper structure.
Add extending interfaces for the different ways of invoking scrapes.
Use interface conversions to convert a scraper from the cache to a
scraper supporting the extra methods.
The return path returns models.ScrapedContent.
Write a general postProcess function in the scraper, handling all
ScrapedContent via type switching. This consolidates all postprocessing
code flows.
Introduce marhsallers in the resolver code for converting ScrapedContent
into the underlying concrete types. Use this to plug the existing
fields in the Query resolver, so everything still works.
* ScrapedContent: add more marshalling functions
Handle all marshalling of ScrapedContent through marhsalling functions.
Removes some hand-rolled early variants of it, and replaces it with
a canonical code flow.
* Support loadByName via scraper_s
In order to temporarily plug a hole in the current implementation, we
use the older implementation as a hook to get the newer implementation
to run.
Later on, this can serve as a guide for how to implement the lower level
bits inside the scrapers themselves. For now, it just enables support.
* Plug the remaining scraper functions for now
Since we would like to have a scraper which works in between refactors,
plug the lower level parts of the scraper for now. It avoids us having
to tackle this part just yet.
* Move postprocessing to its own file
There's enough postprocessing to clutter the main scrapers.go file.
Move all of this into a new file, postprocessing to make the API
simpler. It now lives in scrapers.go.
* Scraper: Invoke API consistency
scraper.Cache.ScrapeByName -> ScrapeName
* Fix scraping scenes by URL
Simple typo. While here, also make a single marshaller nil-aware.
* Introduce scraper groups, consolidate loadByURL
Rename `scraper_s` into `group`. A group is a group of scrapers with
the same identity. This corresponds to a single YAML file for a scraper
configuration. It defines a group which supports different types of
scraping contexts.
Move config into the group, and lift txnManager and globalConfig to
the group.
Because we now return models.ScrapedContent we can use interfaces to
get variance from the different underlying scrapers. Use a type
switch for the URL matcher candidates. And then again for the scrapers.
This consolidates all URL scraping paths into one.
While here, remove the urlMatcher interface which isn't needed. Also
clean up the remaining interfaces for url scraping and delete code
which has no purpose anymore.
* Consolidate fragment scraping in one code path
While here, abide the linters checks.
* Refactor loadByFragment
Give it the same treatment as loadByURL:
Step 1: find a scraperActionImpl which works for the data.
Step 2: use that to scrape
Most of this is simple analysis on the data at hand. It can be pushed
down further in a later commit, but for now we leave it here.
* Remove configScraper, autotag is a scraper
Remove the remains of the configScraper struct. It now lives on in the
group struct. Kill the remaining interfaces from the old implementation
while here.
Remove group.specification since it can now be handled by a simple
func call to spec().
Work through the autotag scraper. It now implements the scraper
interface, so it can be used as a scraper. This also simplifies the
autotag scraper quite a bit since it doens't have to implement a number
of unsupported func calls.
* Simplify the fragment scraper flow
* Pass the context
Eliminate a round of context.TODO() in the scraper code by passing
the calling context down into the subsystem. This will gracefully
allow for termination of remote calls if the client goes away for some
reason in GraphQL requests.
* Improve listScrapers in the schema
Support lists of types we accept.
* Be graceful on nil values in conversion
Supporting nil-values make the API more robust in the
case of partial results in a multi-scrape situation.
* Improve listScrapers: output at-most-once
Use the ID of a scraper to reduce the output set. If a scraper has
been included, don't include it again.
* Consolidate all API level errors into resolver.go
* Reorder files and functions:
scrapers.go -> cache.go:
It almost contains nothing but the cache code.
Move errors into scraper.go from here because
It is a better place to have them living right now
group.go:
All of the group structure. This can now go from
scraper.go, making it more lean. Move group create
from config_scraper to here.
config.go:
Move the `(c config) spec()` call to here.
config_scraper.go:
Empty file by now
* Name-update the scraper interfaces
Use 'via' rather than 'loadBy'.
The scrape happens via a given scrape method, so I think this is a nice
name for it.
* Rename scrapers for consistency.
While here, improve the error formatting, so different errors come
back differently.
* Nuke the freeones field from the GraphQL schema
* Fix autotag interfacing, refactor
The autotag scraper uses a pointer receiver, but the rest of the code
we use for scraping doesn't expect a pointer-receiver. Hence, to fix
the autotag scraper, we change it to be a value receiver, like the
rest of the code.
Fix: viaScene, and viaGallery.
While here, remove a couple of pointer-receiver methods which can be
trivially rewritten into plain functions.
* Protect against pointer interfaces
The underlying code can be a bit inconsistent in what it returns.
Introduce pointer-types in the postprocessing layer and handle them
accordingly for now. Once a better understanding of the lower levels
are understood, we can lift this.
* Move ErrConversion into the models package.
The conversion error pertains to the logic of converting models.
Because of this, it should move there, so it is centralized.
* Be consistent in scraper resolver error handling
If we have a static error
Err = errors.New(..)
Then use it wrapped at the start:
fmt.Errorf("%w: ...context...", Err)
This reads better.
While here, avoid using the underlying Atoi errors: they are verbose,
and like 99% of the time, the user know what is wrong from the input
string, so just give that back.
Also, remove the scraper id from the error contexts: it is implicit,
and the error wouldn't change if we used a different scraper, which
the error message would imply.
* Mark the list*Scrapers() API as deprecated
The same functionality is now present in listScrapers.
* Improve error formatting
Think about how each error is going to be used and tweak them to be
nicer.
* Return a sorted list of scrapers
This helps testing, it's closer to what we had, caches like stable data,
and it is easier for humans. It also makes the output stable, because
map iteration is randomized.
* Fix listScrapers calls to return in ID-order
Since we need the ordering to be by ID in all situations, it is easier
to just generalize the cache listScrapers call to support multiple
scraper types.
This avoids a de-dupe map up the chain, since every scraper is only
considered once. Sorting now happens in the cache listScrapers call.
Use this generalized function in all resolvers, which are now simple
passthroughs.
* Remove UpdateConfig from the scraper cache.
This isn't needed, so get rid of it.
* Pull a context into identify
Scraping scenes in the identify tasks now use a context from up the
call chain.
* Do not store the scraper cache in the resolver.
Scraper caches are updated through
manager.singleton•RefreshScraperCache, so we can't keep a pointer to
it in the resolver. Instead, solve this by adding a fetcher method to
the resolver type. This keeps it local to the resolver, while handling
the problem of updating caches in the configuration.
2021-11-18 23:55:34 +00:00
|
|
|
listScrapers(types: [ScrapeContentType!]!): [Scraper!]!
|
2023-07-28 00:36:00 +00:00
|
|
|
|
|
|
|
"Scrape for a single scene"
|
|
|
|
scrapeSingleScene(
|
|
|
|
source: ScraperSourceInput!
|
|
|
|
input: ScrapeSingleSceneInput!
|
|
|
|
): [ScrapedScene!]!
|
|
|
|
"Scrape for multiple scenes"
|
|
|
|
scrapeMultiScenes(
|
|
|
|
source: ScraperSourceInput!
|
|
|
|
input: ScrapeMultiScenesInput!
|
|
|
|
): [[ScrapedScene!]!]!
|
|
|
|
|
2023-07-30 23:50:24 +00:00
|
|
|
"Scrape for a single studio"
|
|
|
|
scrapeSingleStudio(
|
|
|
|
source: ScraperSourceInput!
|
|
|
|
input: ScrapeSingleStudioInput!
|
|
|
|
): [ScrapedStudio!]!
|
|
|
|
|
2023-07-28 00:36:00 +00:00
|
|
|
"Scrape for a single performer"
|
|
|
|
scrapeSinglePerformer(
|
|
|
|
source: ScraperSourceInput!
|
|
|
|
input: ScrapeSinglePerformerInput!
|
|
|
|
): [ScrapedPerformer!]!
|
|
|
|
"Scrape for multiple performers"
|
|
|
|
scrapeMultiPerformers(
|
|
|
|
source: ScraperSourceInput!
|
|
|
|
input: ScrapeMultiPerformersInput!
|
|
|
|
): [[ScrapedPerformer!]!]!
|
|
|
|
|
|
|
|
"Scrape for a single gallery"
|
|
|
|
scrapeSingleGallery(
|
|
|
|
source: ScraperSourceInput!
|
|
|
|
input: ScrapeSingleGalleryInput!
|
|
|
|
): [ScrapedGallery!]!
|
|
|
|
|
|
|
|
"Scrape for a single movie"
|
|
|
|
scrapeSingleMovie(
|
|
|
|
source: ScraperSourceInput!
|
|
|
|
input: ScrapeSingleMovieInput!
|
|
|
|
): [ScrapedMovie!]!
|
2021-09-07 01:54:22 +00:00
|
|
|
|
Refactor scraper top half (#1893)
* Simplify scraper listing
Introduce an enum, scraper.Kind, which explains what we are looking
for. Make it possible to match this from a scraper struct.
Use the enum to rewrite all the listing code to use the same code path.
* Use a map, nitpick ScrapePerformerList
Let the cache store a map from ID of a scraper to the scraper. This
improves lookups when there are many scrapers, making it practically
O(1) rather than O(n). If many scrapers are stored, this is faster.
Since range expressions work unchanged, we don't have to change much,
and things will still work.
make Kind a Stringer
Rename ScraperPerformerList -> ScraperPerformerQuery since that name
is used in the other scrapers, and we value consistency.
Tune ScraperPerformerQuery:
* Return static errors
* Use the new functionality
* When loading scrapers, do so directly
Rather than first walking the directory structure to obtain file paths,
fold the load directly in the the filepath walk. This makes the code
for more direct.
* Use static ErrNotFound
If a scraper isn't found, return one static error. This paves the way
for eventually doing our own error-presenter in gqlgen.
* Store the cache in the Resolver state
Putting the scraperCache directly in the resolver avoids the need to
call manager.GetInstance() all over the place to get access to the
scraper cache. The cache is stored by pointer, so it should be safe,
since the cache will just update its internal state rather than being
overwritten.
We can now utilize the resolver state to grab the cache where needed.
While here, pass context.Context from the resolver down into a function,
which removes a context.TODO()
* Introduce ScrapedContent
Create a union in the GraphQL schema for all scraped content. This
simplifies the internal implementation because we get variance on
the output content type.
Introduce a new type ScrapedContentType which signifies the scraped
content you want as a caller.
Use these to generalize the List interface and the URL scraping
interface.
* Simplify the scraper API
Introduce a new interface for scraping. This interface is then
used in the upper half of the scraper code, to make the code use one
code flow rather than multiple code flows. Variance is currently at
the old scraper structure.
Add extending interfaces for the different ways of invoking scrapes.
Use interface conversions to convert a scraper from the cache to a
scraper supporting the extra methods.
The return path returns models.ScrapedContent.
Write a general postProcess function in the scraper, handling all
ScrapedContent via type switching. This consolidates all postprocessing
code flows.
Introduce marhsallers in the resolver code for converting ScrapedContent
into the underlying concrete types. Use this to plug the existing
fields in the Query resolver, so everything still works.
* ScrapedContent: add more marshalling functions
Handle all marshalling of ScrapedContent through marhsalling functions.
Removes some hand-rolled early variants of it, and replaces it with
a canonical code flow.
* Support loadByName via scraper_s
In order to temporarily plug a hole in the current implementation, we
use the older implementation as a hook to get the newer implementation
to run.
Later on, this can serve as a guide for how to implement the lower level
bits inside the scrapers themselves. For now, it just enables support.
* Plug the remaining scraper functions for now
Since we would like to have a scraper which works in between refactors,
plug the lower level parts of the scraper for now. It avoids us having
to tackle this part just yet.
* Move postprocessing to its own file
There's enough postprocessing to clutter the main scrapers.go file.
Move all of this into a new file, postprocessing to make the API
simpler. It now lives in scrapers.go.
* Scraper: Invoke API consistency
scraper.Cache.ScrapeByName -> ScrapeName
* Fix scraping scenes by URL
Simple typo. While here, also make a single marshaller nil-aware.
* Introduce scraper groups, consolidate loadByURL
Rename `scraper_s` into `group`. A group is a group of scrapers with
the same identity. This corresponds to a single YAML file for a scraper
configuration. It defines a group which supports different types of
scraping contexts.
Move config into the group, and lift txnManager and globalConfig to
the group.
Because we now return models.ScrapedContent we can use interfaces to
get variance from the different underlying scrapers. Use a type
switch for the URL matcher candidates. And then again for the scrapers.
This consolidates all URL scraping paths into one.
While here, remove the urlMatcher interface which isn't needed. Also
clean up the remaining interfaces for url scraping and delete code
which has no purpose anymore.
* Consolidate fragment scraping in one code path
While here, abide the linters checks.
* Refactor loadByFragment
Give it the same treatment as loadByURL:
Step 1: find a scraperActionImpl which works for the data.
Step 2: use that to scrape
Most of this is simple analysis on the data at hand. It can be pushed
down further in a later commit, but for now we leave it here.
* Remove configScraper, autotag is a scraper
Remove the remains of the configScraper struct. It now lives on in the
group struct. Kill the remaining interfaces from the old implementation
while here.
Remove group.specification since it can now be handled by a simple
func call to spec().
Work through the autotag scraper. It now implements the scraper
interface, so it can be used as a scraper. This also simplifies the
autotag scraper quite a bit since it doens't have to implement a number
of unsupported func calls.
* Simplify the fragment scraper flow
* Pass the context
Eliminate a round of context.TODO() in the scraper code by passing
the calling context down into the subsystem. This will gracefully
allow for termination of remote calls if the client goes away for some
reason in GraphQL requests.
* Improve listScrapers in the schema
Support lists of types we accept.
* Be graceful on nil values in conversion
Supporting nil-values make the API more robust in the
case of partial results in a multi-scrape situation.
* Improve listScrapers: output at-most-once
Use the ID of a scraper to reduce the output set. If a scraper has
been included, don't include it again.
* Consolidate all API level errors into resolver.go
* Reorder files and functions:
scrapers.go -> cache.go:
It almost contains nothing but the cache code.
Move errors into scraper.go from here because
It is a better place to have them living right now
group.go:
All of the group structure. This can now go from
scraper.go, making it more lean. Move group create
from config_scraper to here.
config.go:
Move the `(c config) spec()` call to here.
config_scraper.go:
Empty file by now
* Name-update the scraper interfaces
Use 'via' rather than 'loadBy'.
The scrape happens via a given scrape method, so I think this is a nice
name for it.
* Rename scrapers for consistency.
While here, improve the error formatting, so different errors come
back differently.
* Nuke the freeones field from the GraphQL schema
* Fix autotag interfacing, refactor
The autotag scraper uses a pointer receiver, but the rest of the code
we use for scraping doesn't expect a pointer-receiver. Hence, to fix
the autotag scraper, we change it to be a value receiver, like the
rest of the code.
Fix: viaScene, and viaGallery.
While here, remove a couple of pointer-receiver methods which can be
trivially rewritten into plain functions.
* Protect against pointer interfaces
The underlying code can be a bit inconsistent in what it returns.
Introduce pointer-types in the postprocessing layer and handle them
accordingly for now. Once a better understanding of the lower levels
are understood, we can lift this.
* Move ErrConversion into the models package.
The conversion error pertains to the logic of converting models.
Because of this, it should move there, so it is centralized.
* Be consistent in scraper resolver error handling
If we have a static error
Err = errors.New(..)
Then use it wrapped at the start:
fmt.Errorf("%w: ...context...", Err)
This reads better.
While here, avoid using the underlying Atoi errors: they are verbose,
and like 99% of the time, the user know what is wrong from the input
string, so just give that back.
Also, remove the scraper id from the error contexts: it is implicit,
and the error wouldn't change if we used a different scraper, which
the error message would imply.
* Mark the list*Scrapers() API as deprecated
The same functionality is now present in listScrapers.
* Improve error formatting
Think about how each error is going to be used and tweak them to be
nicer.
* Return a sorted list of scrapers
This helps testing, it's closer to what we had, caches like stable data,
and it is easier for humans. It also makes the output stable, because
map iteration is randomized.
* Fix listScrapers calls to return in ID-order
Since we need the ordering to be by ID in all situations, it is easier
to just generalize the cache listScrapers call to support multiple
scraper types.
This avoids a de-dupe map up the chain, since every scraper is only
considered once. Sorting now happens in the cache listScrapers call.
Use this generalized function in all resolvers, which are now simple
passthroughs.
* Remove UpdateConfig from the scraper cache.
This isn't needed, so get rid of it.
* Pull a context into identify
Scraping scenes in the identify tasks now use a context from up the
call chain.
* Do not store the scraper cache in the resolver.
Scraper caches are updated through
manager.singleton•RefreshScraperCache, so we can't keep a pointer to
it in the resolver. Instead, solve this by adding a fetcher method to
the resolver type. This keeps it local to the resolver, while handling
the problem of updating caches in the configuration.
2021-11-18 23:55:34 +00:00
|
|
|
"Scrapes content based on a URL"
|
|
|
|
scrapeURL(url: String!, ty: ScrapeContentType!): ScrapedContent
|
|
|
|
|
2023-07-28 00:36:00 +00:00
|
|
|
"Scrapes a complete performer record based on a URL"
|
2019-11-19 02:49:05 +00:00
|
|
|
scrapePerformerURL(url: String!): ScrapedPerformer
|
2023-07-28 00:36:00 +00:00
|
|
|
"Scrapes a complete scene record based on a URL"
|
2019-12-16 01:35:34 +00:00
|
|
|
scrapeSceneURL(url: String!): ScrapedScene
|
2023-07-28 00:36:00 +00:00
|
|
|
"Scrapes a complete gallery record based on a URL"
|
2020-10-20 22:24:32 +00:00
|
|
|
scrapeGalleryURL(url: String!): ScrapedGallery
|
2023-07-28 00:36:00 +00:00
|
|
|
"Scrapes a complete movie record based on a URL"
|
2020-08-10 05:34:15 +00:00
|
|
|
scrapeMovieURL(url: String!): ScrapedMovie
|
2019-11-19 02:49:05 +00:00
|
|
|
|
2020-08-08 02:05:35 +00:00
|
|
|
# Plugins
|
2023-07-28 00:36:00 +00:00
|
|
|
"List loaded plugins"
|
2020-08-08 02:05:35 +00:00
|
|
|
plugins: [Plugin!]
|
2023-07-28 00:36:00 +00:00
|
|
|
"List available plugin operations"
|
2020-08-08 02:05:35 +00:00
|
|
|
pluginTasks: [PluginTask!]
|
|
|
|
|
2023-11-21 23:01:11 +00:00
|
|
|
# Packages
|
|
|
|
"List installed packages"
|
|
|
|
installedPackages(type: PackageType!): [Package!]!
|
|
|
|
"List available packages"
|
|
|
|
availablePackages(type: PackageType!, source: String!): [Package!]!
|
|
|
|
|
2019-03-26 15:35:06 +00:00
|
|
|
# Config
|
2023-07-28 00:36:00 +00:00
|
|
|
"Returns the current, complete configuration"
|
2019-03-26 15:35:06 +00:00
|
|
|
configuration: ConfigResult!
|
2023-07-28 00:36:00 +00:00
|
|
|
"Returns an array of paths for the given path"
|
2021-10-14 04:16:45 +00:00
|
|
|
directory(
|
|
|
|
"The directory path to list"
|
2023-07-28 00:36:00 +00:00
|
|
|
path: String
|
2021-10-14 04:16:45 +00:00
|
|
|
"Desired collation locale. Determines the order of the directory result. eg. 'en-US', 'pt-BR', ..."
|
|
|
|
locale: String = "en"
|
|
|
|
): Directory!
|
2022-01-04 03:20:31 +00:00
|
|
|
validateStashBoxCredentials(input: StashBoxInput!): StashBoxValidationResult!
|
2019-03-26 15:35:06 +00:00
|
|
|
|
2021-05-24 04:24:18 +00:00
|
|
|
# System status
|
2021-04-11 23:31:33 +00:00
|
|
|
systemStatus: SystemStatus!
|
2021-05-24 04:24:18 +00:00
|
|
|
|
|
|
|
# Job status
|
|
|
|
jobQueue: [Job!]
|
|
|
|
findJob(input: FindJobInput!): Job
|
2019-11-07 04:35:04 +00:00
|
|
|
|
2021-05-20 06:58:43 +00:00
|
|
|
dlnaStatus: DLNAStatus!
|
|
|
|
|
2019-03-26 15:35:06 +00:00
|
|
|
# Get everything
|
|
|
|
|
2024-02-18 23:25:08 +00:00
|
|
|
allScenes: [Scene!]! @deprecated(reason: "Use findScenes instead")
|
2023-02-19 22:24:47 +00:00
|
|
|
allSceneMarkers: [SceneMarker!]!
|
2024-02-18 23:25:08 +00:00
|
|
|
@deprecated(reason: "Use findSceneMarkers instead")
|
|
|
|
allImages: [Image!]! @deprecated(reason: "Use findImages instead")
|
|
|
|
allGalleries: [Gallery!]! @deprecated(reason: "Use findGalleries instead")
|
2019-08-21 04:47:48 +00:00
|
|
|
|
2024-02-18 23:25:08 +00:00
|
|
|
allPerformers: [Performer!]!
|
2024-01-24 09:24:13 +00:00
|
|
|
allTags: [Tag!]! @deprecated(reason: "Use findTags instead")
|
2024-02-06 00:26:16 +00:00
|
|
|
allStudios: [Studio!]! @deprecated(reason: "Use findStudios instead")
|
2024-02-18 23:25:08 +00:00
|
|
|
allMovies: [Movie!]! @deprecated(reason: "Use findMovies instead")
|
2023-08-24 01:15:49 +00:00
|
|
|
|
2020-04-19 02:03:51 +00:00
|
|
|
# Get everything with minimal metadata
|
|
|
|
|
2019-08-21 04:47:48 +00:00
|
|
|
# Version
|
|
|
|
version: Version!
|
2021-10-14 04:16:45 +00:00
|
|
|
|
2020-01-07 22:21:23 +00:00
|
|
|
# LatestVersion
|
2023-02-08 04:02:23 +00:00
|
|
|
latestversion: LatestVersion!
|
2019-03-26 15:35:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type Mutation {
|
2021-04-11 23:31:33 +00:00
|
|
|
setup(input: SetupInput!): Boolean!
|
|
|
|
migrate(input: MigrateInput!): Boolean!
|
|
|
|
|
2022-11-14 05:35:09 +00:00
|
|
|
sceneCreate(input: SceneCreateInput!): Scene
|
2019-03-26 15:35:06 +00:00
|
|
|
sceneUpdate(input: SceneUpdateInput!): Scene
|
2022-11-14 05:35:09 +00:00
|
|
|
sceneMerge(input: SceneMergeInput!): Scene
|
2019-10-27 13:05:31 +00:00
|
|
|
bulkSceneUpdate(input: BulkSceneUpdateInput!): [Scene!]
|
2019-08-15 07:32:57 +00:00
|
|
|
sceneDestroy(input: SceneDestroyInput!): Boolean!
|
2020-06-23 00:40:11 +00:00
|
|
|
scenesDestroy(input: ScenesDestroyInput!): Boolean!
|
2019-10-30 13:37:21 +00:00
|
|
|
scenesUpdate(input: [SceneUpdateInput!]!): [Scene]
|
2019-03-26 15:35:06 +00:00
|
|
|
|
2023-07-28 00:36:00 +00:00
|
|
|
"Increments the o-counter for a scene. Returns the new value"
|
2024-02-22 00:28:18 +00:00
|
|
|
sceneIncrementO(id: ID!): Int! @deprecated(reason: "Use sceneAddO instead")
|
2023-07-28 00:36:00 +00:00
|
|
|
"Decrements the o-counter for a scene. Returns the new value"
|
2024-02-22 00:28:18 +00:00
|
|
|
sceneDecrementO(id: ID!): Int! @deprecated(reason: "Use sceneRemoveO instead")
|
|
|
|
|
|
|
|
"Increments the o-counter for a scene. Uses the current time if none provided."
|
|
|
|
sceneAddO(id: ID!, times: [Timestamp!]): HistoryMutationResult!
|
|
|
|
"Decrements the o-counter for a scene, removing the last recorded time if specific time not provided. Returns the new value"
|
|
|
|
sceneDeleteO(id: ID!, times: [Timestamp!]): HistoryMutationResult!
|
|
|
|
|
2023-07-28 00:36:00 +00:00
|
|
|
"Resets the o-counter for a scene to 0. Returns the new value"
|
2020-02-03 00:17:28 +00:00
|
|
|
sceneResetO(id: ID!): Int!
|
|
|
|
|
2023-07-28 00:36:00 +00:00
|
|
|
"Sets the resume time point (if provided) and adds the provided duration to the scene's play duration"
|
2022-11-21 01:55:15 +00:00
|
|
|
sceneSaveActivity(id: ID!, resume_time: Float, playDuration: Float): Boolean!
|
|
|
|
|
2023-07-28 00:36:00 +00:00
|
|
|
"Increments the play count for the scene. Returns the new play count value."
|
2022-11-21 01:55:15 +00:00
|
|
|
sceneIncrementPlayCount(id: ID!): Int!
|
2024-02-22 00:28:18 +00:00
|
|
|
@deprecated(reason: "Use sceneAddPlay instead")
|
|
|
|
|
|
|
|
"Increments the play count for the scene. Uses the current time if none provided."
|
|
|
|
sceneAddPlay(id: ID!, times: [Timestamp!]): HistoryMutationResult!
|
|
|
|
"Decrements the play count for the scene, removing the specific times or the last recorded time if not provided."
|
|
|
|
sceneDeletePlay(id: ID!, times: [Timestamp!]): HistoryMutationResult!
|
|
|
|
"Resets the play count for a scene to 0. Returns the new play count value."
|
|
|
|
sceneResetPlayCount(id: ID!): Int!
|
2022-11-21 01:55:15 +00:00
|
|
|
|
2023-07-28 00:36:00 +00:00
|
|
|
"Generates screenshot at specified time in seconds. Leave empty to generate default screenshot"
|
2020-03-11 21:34:04 +00:00
|
|
|
sceneGenerateScreenshot(id: ID!, at: Float): String!
|
|
|
|
|
2019-03-26 15:35:06 +00:00
|
|
|
sceneMarkerCreate(input: SceneMarkerCreateInput!): SceneMarker
|
|
|
|
sceneMarkerUpdate(input: SceneMarkerUpdateInput!): SceneMarker
|
|
|
|
sceneMarkerDestroy(id: ID!): Boolean!
|
|
|
|
|
2022-11-14 05:35:09 +00:00
|
|
|
sceneAssignFile(input: AssignSceneFileInput!): Boolean!
|
|
|
|
|
2020-10-12 23:12:46 +00:00
|
|
|
imageUpdate(input: ImageUpdateInput!): Image
|
|
|
|
bulkImageUpdate(input: BulkImageUpdateInput!): [Image!]
|
|
|
|
imageDestroy(input: ImageDestroyInput!): Boolean!
|
|
|
|
imagesDestroy(input: ImagesDestroyInput!): Boolean!
|
|
|
|
imagesUpdate(input: [ImageUpdateInput!]!): [Image]
|
|
|
|
|
2023-07-28 00:36:00 +00:00
|
|
|
"Increments the o-counter for an image. Returns the new value"
|
2020-10-12 23:12:46 +00:00
|
|
|
imageIncrementO(id: ID!): Int!
|
2023-07-28 00:36:00 +00:00
|
|
|
"Decrements the o-counter for an image. Returns the new value"
|
2020-10-12 23:12:46 +00:00
|
|
|
imageDecrementO(id: ID!): Int!
|
2023-07-28 00:36:00 +00:00
|
|
|
"Resets the o-counter for a image to 0. Returns the new value"
|
2020-10-12 23:12:46 +00:00
|
|
|
imageResetO(id: ID!): Int!
|
|
|
|
|
|
|
|
galleryCreate(input: GalleryCreateInput!): Gallery
|
|
|
|
galleryUpdate(input: GalleryUpdateInput!): Gallery
|
|
|
|
bulkGalleryUpdate(input: BulkGalleryUpdateInput!): [Gallery!]
|
|
|
|
galleryDestroy(input: GalleryDestroyInput!): Boolean!
|
|
|
|
galleriesUpdate(input: [GalleryUpdateInput!]!): [Gallery]
|
|
|
|
|
|
|
|
addGalleryImages(input: GalleryAddInput!): Boolean!
|
|
|
|
removeGalleryImages(input: GalleryRemoveInput!): Boolean!
|
|
|
|
|
2023-03-16 04:04:54 +00:00
|
|
|
galleryChapterCreate(input: GalleryChapterCreateInput!): GalleryChapter
|
|
|
|
galleryChapterUpdate(input: GalleryChapterUpdateInput!): GalleryChapter
|
|
|
|
galleryChapterDestroy(id: ID!): Boolean!
|
|
|
|
|
2019-03-26 15:35:06 +00:00
|
|
|
performerCreate(input: PerformerCreateInput!): Performer
|
|
|
|
performerUpdate(input: PerformerUpdateInput!): Performer
|
2019-08-14 21:40:51 +00:00
|
|
|
performerDestroy(input: PerformerDestroyInput!): Boolean!
|
2021-01-13 00:57:53 +00:00
|
|
|
performersDestroy(ids: [ID!]!): Boolean!
|
2021-03-10 01:25:51 +00:00
|
|
|
bulkPerformerUpdate(input: BulkPerformerUpdateInput!): [Performer!]
|
2019-03-26 15:35:06 +00:00
|
|
|
|
|
|
|
studioCreate(input: StudioCreateInput!): Studio
|
|
|
|
studioUpdate(input: StudioUpdateInput!): Studio
|
2019-08-14 21:40:51 +00:00
|
|
|
studioDestroy(input: StudioDestroyInput!): Boolean!
|
2021-01-13 00:57:53 +00:00
|
|
|
studiosDestroy(ids: [ID!]!): Boolean!
|
2019-03-26 15:35:06 +00:00
|
|
|
|
2020-03-10 03:28:15 +00:00
|
|
|
movieCreate(input: MovieCreateInput!): Movie
|
|
|
|
movieUpdate(input: MovieUpdateInput!): Movie
|
|
|
|
movieDestroy(input: MovieDestroyInput!): Boolean!
|
2021-01-13 00:57:53 +00:00
|
|
|
moviesDestroy(ids: [ID!]!): Boolean!
|
2022-02-16 02:03:57 +00:00
|
|
|
bulkMovieUpdate(input: BulkMovieUpdateInput!): [Movie!]
|
2020-03-10 03:28:15 +00:00
|
|
|
|
2019-03-26 15:35:06 +00:00
|
|
|
tagCreate(input: TagCreateInput!): Tag
|
|
|
|
tagUpdate(input: TagUpdateInput!): Tag
|
|
|
|
tagDestroy(input: TagDestroyInput!): Boolean!
|
2021-01-13 00:57:53 +00:00
|
|
|
tagsDestroy(ids: [ID!]!): Boolean!
|
2021-06-16 04:33:54 +00:00
|
|
|
tagsMerge(input: TagsMergeInput!): Tag
|
2019-03-26 15:35:06 +00:00
|
|
|
|
2023-07-28 00:36:00 +00:00
|
|
|
"""
|
|
|
|
Moves the given files to the given destination. Returns true if successful.
|
|
|
|
Either the destination_folder or destination_folder_id must be provided.
|
|
|
|
If both are provided, the destination_folder_id takes precedence.
|
2023-03-21 20:57:26 +00:00
|
|
|
Destination folder must be a subfolder of one of the stash library paths.
|
|
|
|
If provided, destination_basename must be a valid filename with an extension that
|
|
|
|
matches one of the media extensions.
|
|
|
|
Creates folder hierarchy if needed.
|
|
|
|
"""
|
|
|
|
moveFiles(input: MoveFilesInput!): Boolean!
|
2022-10-06 03:50:06 +00:00
|
|
|
deleteFiles(ids: [ID!]!): Boolean!
|
|
|
|
|
2023-12-22 03:07:10 +00:00
|
|
|
fileSetFingerprints(input: FileSetFingerprintsInput!): Boolean!
|
|
|
|
|
2021-06-16 04:53:32 +00:00
|
|
|
# Saved filters
|
|
|
|
saveFilter(input: SaveFilterInput!): SavedFilter!
|
|
|
|
destroySavedFilter(input: DestroyFilterInput!): Boolean!
|
|
|
|
setDefaultFilter(input: SetDefaultFilterInput!): Boolean!
|
|
|
|
|
2023-07-28 00:36:00 +00:00
|
|
|
"Change general configuration options"
|
2019-03-26 15:35:06 +00:00
|
|
|
configureGeneral(input: ConfigGeneralInput!): ConfigGeneralResult!
|
2019-08-22 22:24:14 +00:00
|
|
|
configureInterface(input: ConfigInterfaceInput!): ConfigInterfaceResult!
|
2021-05-20 06:58:43 +00:00
|
|
|
configureDLNA(input: ConfigDLNAInput!): ConfigDLNAResult!
|
2021-08-04 02:32:58 +00:00
|
|
|
configureScraping(input: ConfigScrapingInput!): ConfigScrapingResult!
|
2023-07-28 00:36:00 +00:00
|
|
|
configureDefaults(
|
|
|
|
input: ConfigDefaultSettingsInput!
|
|
|
|
): ConfigDefaultSettingsResult!
|
2020-03-11 21:34:04 +00:00
|
|
|
|
2023-10-18 03:09:13 +00:00
|
|
|
# overwrites the entire plugin configuration for the given plugin
|
|
|
|
configurePlugin(plugin_id: ID!, input: Map!): Map!
|
|
|
|
|
2024-03-13 21:25:16 +00:00
|
|
|
# overwrites the UI configuration
|
|
|
|
# if input is provided, then the entire UI configuration is replaced
|
|
|
|
# if partial is provided, then the partial UI configuration is merged into the existing UI configuration
|
|
|
|
configureUI(input: Map, partial: Map): Map!
|
2022-06-14 00:34:04 +00:00
|
|
|
# sets a single UI key value
|
2024-03-13 21:25:16 +00:00
|
|
|
# key is a dot separated path to the value
|
2022-06-14 00:34:04 +00:00
|
|
|
configureUISetting(key: String!, value: Any): Map!
|
|
|
|
|
2023-07-28 00:36:00 +00:00
|
|
|
"Generate and set (or clear) API key"
|
2021-03-31 05:08:52 +00:00
|
|
|
generateAPIKey(input: GenerateAPIKeyInput!): String!
|
|
|
|
|
2023-07-28 00:36:00 +00:00
|
|
|
"Returns a link to download the result"
|
2020-09-15 07:28:53 +00:00
|
|
|
exportObjects(input: ExportObjectsInput!): String
|
|
|
|
|
2023-07-28 00:36:00 +00:00
|
|
|
"Performs an incremental import. Returns the job ID"
|
2021-05-24 04:24:18 +00:00
|
|
|
importObjects(input: ImportObjectsInput!): ID!
|
2020-09-20 08:36:02 +00:00
|
|
|
|
2023-07-28 00:36:00 +00:00
|
|
|
"Start an full import. Completely wipes the database and imports from the metadata directory. Returns the job ID"
|
2021-05-24 04:24:18 +00:00
|
|
|
metadataImport: ID!
|
2023-07-28 00:36:00 +00:00
|
|
|
"Start a full export. Outputs to the metadata directory. Returns the job ID"
|
2021-05-24 04:24:18 +00:00
|
|
|
metadataExport: ID!
|
2023-07-28 00:36:00 +00:00
|
|
|
"Start a scan. Returns the job ID"
|
2021-05-24 04:24:18 +00:00
|
|
|
metadataScan(input: ScanMetadataInput!): ID!
|
2023-07-28 00:36:00 +00:00
|
|
|
"Start generating content. Returns the job ID"
|
2021-05-24 04:24:18 +00:00
|
|
|
metadataGenerate(input: GenerateMetadataInput!): ID!
|
2023-07-28 00:36:00 +00:00
|
|
|
"Start auto-tagging. Returns the job ID"
|
2021-05-24 04:24:18 +00:00
|
|
|
metadataAutoTag(input: AutoTagMetadataInput!): ID!
|
2023-07-28 00:36:00 +00:00
|
|
|
"Clean metadata. Returns the job ID"
|
2021-05-24 04:24:18 +00:00
|
|
|
metadataClean(input: CleanMetadataInput!): ID!
|
2024-02-23 04:56:00 +00:00
|
|
|
"Clean generated files. Returns the job ID"
|
|
|
|
metadataCleanGenerated(input: CleanGeneratedInput!): ID!
|
2023-07-28 00:36:00 +00:00
|
|
|
"Identifies scenes using scrapers. Returns the job ID"
|
2021-10-28 03:25:17 +00:00
|
|
|
metadataIdentify(input: IdentifyMetadataInput!): ID!
|
2023-05-03 05:01:59 +00:00
|
|
|
|
2023-07-28 00:36:00 +00:00
|
|
|
"Migrate generated files for the current hash naming"
|
2021-05-24 04:24:18 +00:00
|
|
|
migrateHashNaming: ID!
|
2023-07-28 00:36:00 +00:00
|
|
|
"Migrates legacy scene screenshot files into the blob storage"
|
2023-03-16 23:52:49 +00:00
|
|
|
migrateSceneScreenshots(input: MigrateSceneScreenshotsInput!): ID!
|
2023-07-28 00:36:00 +00:00
|
|
|
"Migrates blobs from the old storage system to the current one"
|
2023-03-16 23:52:49 +00:00
|
|
|
migrateBlobs(input: MigrateBlobsInput!): ID!
|
2023-05-03 05:01:59 +00:00
|
|
|
|
2023-07-28 00:36:00 +00:00
|
|
|
"Anonymise the database in a separate file. Optionally returns a link to download the database file"
|
2022-12-22 22:15:27 +00:00
|
|
|
anonymiseDatabase(input: AnonymiseDatabaseInput!): String
|
2020-03-11 21:34:04 +00:00
|
|
|
|
2023-07-28 01:23:18 +00:00
|
|
|
"Optimises the database. Returns the job ID"
|
|
|
|
optimiseDatabase: ID!
|
|
|
|
|
2023-07-28 00:36:00 +00:00
|
|
|
"Reload scrapers"
|
2020-06-10 03:43:17 +00:00
|
|
|
reloadScrapers: Boolean!
|
|
|
|
|
2023-10-16 05:15:12 +00:00
|
|
|
"""
|
|
|
|
Enable/disable plugins - enabledMap is a map of plugin IDs to enabled booleans.
|
|
|
|
Plugins not in the map are not affected.
|
|
|
|
"""
|
|
|
|
setPluginsEnabled(enabledMap: BoolMap!): Boolean!
|
|
|
|
|
2024-02-22 00:20:21 +00:00
|
|
|
"""
|
|
|
|
Run a plugin task.
|
|
|
|
If task_name is provided, then the task must exist in the plugin config and the tasks configuration
|
|
|
|
will be used to run the plugin.
|
|
|
|
If no task_name is provided, then the plugin will be executed with the arguments provided only.
|
|
|
|
Returns the job ID
|
|
|
|
"""
|
2023-07-28 00:36:00 +00:00
|
|
|
runPluginTask(
|
|
|
|
plugin_id: ID!
|
2024-02-22 00:20:21 +00:00
|
|
|
"if provided, then the default args will be applied"
|
|
|
|
task_name: String
|
|
|
|
"displayed in the task queue"
|
|
|
|
description: String
|
|
|
|
args: [PluginArgInput!] @deprecated(reason: "Use args_map instead")
|
|
|
|
args_map: Map
|
2023-07-28 00:36:00 +00:00
|
|
|
): ID!
|
2024-02-22 00:20:21 +00:00
|
|
|
|
|
|
|
"""
|
|
|
|
Runs a plugin operation. The operation is run immediately and does not use the job queue.
|
|
|
|
Returns a map of the result.
|
|
|
|
"""
|
|
|
|
runPluginOperation(plugin_id: ID!, args: Map): Any
|
|
|
|
|
2020-08-08 02:05:35 +00:00
|
|
|
reloadPlugins: Boolean!
|
|
|
|
|
2023-11-21 23:01:11 +00:00
|
|
|
"""
|
|
|
|
Installs the given packages.
|
|
|
|
If a package is already installed, it will be updated if needed..
|
|
|
|
If an error occurs when installing a package, the job will continue to install the remaining packages.
|
|
|
|
Returns the job ID
|
|
|
|
"""
|
|
|
|
installPackages(type: PackageType!, packages: [PackageSpecInput!]!): ID!
|
|
|
|
"""
|
|
|
|
Updates the given packages.
|
|
|
|
If a package is not installed, it will not be installed.
|
|
|
|
If a package does not need to be updated, it will not be updated.
|
|
|
|
If no packages are provided, all packages of the given type will be updated.
|
|
|
|
If an error occurs when updating a package, the job will continue to update the remaining packages.
|
|
|
|
Returns the job ID.
|
|
|
|
"""
|
|
|
|
updatePackages(type: PackageType!, packages: [PackageSpecInput!]): ID!
|
|
|
|
"""
|
|
|
|
Uninstalls the given packages.
|
|
|
|
If an error occurs when uninstalling a package, the job will continue to uninstall the remaining packages.
|
|
|
|
Returns the job ID
|
|
|
|
"""
|
|
|
|
uninstallPackages(type: PackageType!, packages: [PackageSpecInput!]!): ID!
|
|
|
|
|
2021-05-24 04:24:18 +00:00
|
|
|
stopJob(job_id: ID!): Boolean!
|
|
|
|
stopAllJobs: Boolean!
|
2020-10-24 03:31:39 +00:00
|
|
|
|
2023-07-28 00:36:00 +00:00
|
|
|
"Submit fingerprints to stash-box instance"
|
|
|
|
submitStashBoxFingerprints(
|
|
|
|
input: StashBoxFingerprintSubmissionInput!
|
|
|
|
): Boolean!
|
2021-01-21 11:02:09 +00:00
|
|
|
|
2023-07-28 00:36:00 +00:00
|
|
|
"Submit scene as draft to stash-box instance"
|
2022-02-01 04:06:51 +00:00
|
|
|
submitStashBoxSceneDraft(input: StashBoxDraftSubmissionInput!): ID
|
2023-07-28 00:36:00 +00:00
|
|
|
"Submit performer as draft to stash-box instance"
|
2022-02-01 04:06:51 +00:00
|
|
|
submitStashBoxPerformerDraft(input: StashBoxDraftSubmissionInput!): ID
|
|
|
|
|
2023-07-28 00:36:00 +00:00
|
|
|
"Backup the database. Optionally returns a link to download the database file"
|
2021-01-21 11:02:09 +00:00
|
|
|
backupDatabase(input: BackupDatabaseInput!): String
|
2021-05-03 04:21:20 +00:00
|
|
|
|
2023-07-28 00:36:00 +00:00
|
|
|
"DANGEROUS: Execute an arbitrary SQL statement that returns rows."
|
2023-07-26 03:54:33 +00:00
|
|
|
querySQL(sql: String!, args: [Any]): SQLQueryResult!
|
|
|
|
|
2023-07-28 00:36:00 +00:00
|
|
|
"DANGEROUS: Execute an arbitrary SQL statement without returning any rows."
|
2023-07-26 03:54:33 +00:00
|
|
|
execSQL(sql: String!, args: [Any]): SQLExecResult!
|
|
|
|
|
2023-07-28 00:36:00 +00:00
|
|
|
"Run batch performer tag task. Returns the job ID."
|
2023-07-30 23:50:24 +00:00
|
|
|
stashBoxBatchPerformerTag(input: StashBoxBatchTagInput!): String!
|
|
|
|
"Run batch studio tag task. Returns the job ID."
|
|
|
|
stashBoxBatchStudioTag(input: StashBoxBatchTagInput!): String!
|
2021-10-14 04:16:45 +00:00
|
|
|
|
2023-07-28 00:36:00 +00:00
|
|
|
"Enables DLNA for an optional duration. Has no effect if DLNA is enabled by default"
|
2021-05-20 06:58:43 +00:00
|
|
|
enableDLNA(input: EnableDLNAInput!): Boolean!
|
2023-07-28 00:36:00 +00:00
|
|
|
"Disables DLNA for an optional duration. Has no effect if DLNA is disabled by default"
|
2021-05-20 06:58:43 +00:00
|
|
|
disableDLNA(input: DisableDLNAInput!): Boolean!
|
2023-07-28 00:36:00 +00:00
|
|
|
"Enables an IP address for DLNA for an optional duration"
|
2021-05-20 06:58:43 +00:00
|
|
|
addTempDLNAIP(input: AddTempDLNAIPInput!): Boolean!
|
2023-07-28 00:36:00 +00:00
|
|
|
"Removes an IP address from the temporary DLNA whitelist"
|
2021-05-20 06:58:43 +00:00
|
|
|
removeTempDLNAIP(input: RemoveTempDLNAIPInput!): Boolean!
|
2019-03-26 15:35:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type Subscription {
|
2023-07-28 00:36:00 +00:00
|
|
|
"Update from the metadata manager"
|
2021-05-24 04:24:18 +00:00
|
|
|
jobsSubscribe: JobStatusUpdate!
|
2019-10-24 23:07:07 +00:00
|
|
|
|
|
|
|
loggingSubscribe: [LogEntry!]!
|
2021-05-24 04:24:18 +00:00
|
|
|
|
|
|
|
scanCompleteSubscribe: Boolean!
|
2019-03-26 15:35:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
schema {
|
|
|
|
query: Query
|
|
|
|
mutation: Mutation
|
|
|
|
subscription: Subscription
|
2020-01-07 22:21:23 +00:00
|
|
|
}
|