mirror of
https://github.com/stashapp/stash.git
synced 2025-12-06 16:34:02 +01:00
Support multiple content folders. Closes #2
This commit is contained in:
parent
d959d61e6c
commit
ae9bbf237f
17 changed files with 1449 additions and 886 deletions
10
README.md
10
README.md
|
|
@ -15,6 +15,10 @@ Simply run the executable (double click the exe on windows or run `./stash-osx`
|
||||||
|
|
||||||
*Note for Windows users:* Running the app might present a security prompt since the binary isn't signed yet. Just click more info and then the run anyway button.
|
*Note for Windows users:* Running the app might present a security prompt since the binary isn't signed yet. Just click more info and then the run anyway button.
|
||||||
|
|
||||||
|
## Slack
|
||||||
|
|
||||||
|
I created a Slack channel to discuss the project. [Click here to join.](https://join.slack.com/stash-project/shared_invite/MTc2Nzg0NjAyNzg4LTE0OTM1ODU4MTgtNDcwODRiMGIwYQ)
|
||||||
|
|
||||||
#### FFMPEG
|
#### FFMPEG
|
||||||
|
|
||||||
If stash is unable to find or download FFMPEG then download it yourself from the link for your platform:
|
If stash is unable to find or download FFMPEG then download it yourself from the link for your platform:
|
||||||
|
|
@ -27,9 +31,7 @@ The `ffmpeg(.exe)` and `ffprobe(.exe)` files should be placed in `~/.stash` on m
|
||||||
|
|
||||||
# FAQ
|
# FAQ
|
||||||
|
|
||||||
> Does stash support multiple folders?
|
TODO
|
||||||
|
|
||||||
Not yet, but this will come in the future.
|
|
||||||
|
|
||||||
# Development
|
# Development
|
||||||
|
|
||||||
|
|
@ -66,7 +68,7 @@ TODO
|
||||||
|
|
||||||
## Cross compiling
|
## Cross compiling
|
||||||
|
|
||||||
This project makes use of [this](https://github.com/bep/dockerfiles/tree/master/ci-goreleaser) docker container to create an environment
|
This project uses a modification of [this](https://github.com/bep/dockerfiles/tree/master/ci-goreleaser) docker container to create an environment
|
||||||
where the app can be cross compiled. This process is kicked off by CI via the `scripts/cross-compile.sh` script. Run the following
|
where the app can be cross compiled. This process is kicked off by CI via the `scripts/cross-compile.sh` script. Run the following
|
||||||
command to open a bash shell to the container to poke around:
|
command to open a bash shell to the container to poke around:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"github.com/stashapp/stash/pkg/utils"
|
"github.com/stashapp/stash/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r *queryResolver) ConfigureGeneral(ctx context.Context, input *models.ConfigGeneralInput) (models.ConfigGeneralResult, error) {
|
func (r *mutationResolver) ConfigureGeneral(ctx context.Context, input *models.ConfigGeneralInput) (models.ConfigGeneralResult, error) {
|
||||||
if input == nil {
|
if input == nil {
|
||||||
return makeConfigGeneralResult(), fmt.Errorf("nil input")
|
return makeConfigGeneralResult(), fmt.Errorf("nil input")
|
||||||
}
|
}
|
||||||
|
|
@ -29,9 +29,3 @@ func (r *queryResolver) ConfigureGeneral(ctx context.Context, input *models.Conf
|
||||||
|
|
||||||
return makeConfigGeneralResult(), nil
|
return makeConfigGeneralResult(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeConfigGeneralResult() models.ConfigGeneralResult {
|
|
||||||
return models.ConfigGeneralResult{
|
|
||||||
Stashes: config.GetStashPaths(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
32
pkg/api/resolver_query_configuration.go
Normal file
32
pkg/api/resolver_query_configuration.go
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/stashapp/stash/pkg/manager/config"
|
||||||
|
"github.com/stashapp/stash/pkg/models"
|
||||||
|
"github.com/stashapp/stash/pkg/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (r *queryResolver) Configuration(ctx context.Context) (models.ConfigResult, error) {
|
||||||
|
return makeConfigResult(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *queryResolver) Directories(ctx context.Context, path *string) ([]string, error) {
|
||||||
|
var dirPath = ""
|
||||||
|
if path != nil {
|
||||||
|
dirPath = *path
|
||||||
|
}
|
||||||
|
return utils.ListDir(dirPath), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeConfigResult() models.ConfigResult {
|
||||||
|
return models.ConfigResult{
|
||||||
|
General: makeConfigGeneralResult(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeConfigGeneralResult() models.ConfigGeneralResult {
|
||||||
|
return models.ConfigGeneralResult{
|
||||||
|
Stashes: config.GetStashPaths(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -54,13 +54,6 @@ func initConfig() {
|
||||||
viper.AddConfigPath("$HOME/.stash") // Look for the config in the home directory
|
viper.AddConfigPath("$HOME/.stash") // Look for the config in the home directory
|
||||||
viper.AddConfigPath(".") // Look for config in the working directory
|
viper.AddConfigPath(".") // Look for config in the working directory
|
||||||
|
|
||||||
viper.SetDefault(config.Database, paths.GetDefaultDatabaseFilePath())
|
|
||||||
|
|
||||||
// Set generated to the metadata path for backwards compat
|
|
||||||
if !viper.IsSet(config.Generated) {
|
|
||||||
viper.SetDefault(config.Generated, viper.GetString(config.Metadata))
|
|
||||||
}
|
|
||||||
|
|
||||||
err := viper.ReadInConfig() // Find and read the config file
|
err := viper.ReadInConfig() // Find and read the config file
|
||||||
if err != nil { // Handle errors reading the config file
|
if err != nil { // Handle errors reading the config file
|
||||||
_ = utils.Touch(paths.GetDefaultConfigFilePath())
|
_ = utils.Touch(paths.GetDefaultConfigFilePath())
|
||||||
|
|
@ -69,6 +62,11 @@ func initConfig() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
viper.SetDefault(config.Database, paths.GetDefaultDatabaseFilePath())
|
||||||
|
|
||||||
|
// Set generated to the metadata path for backwards compat
|
||||||
|
viper.SetDefault(config.Generated, viper.GetString(config.Metadata))
|
||||||
|
|
||||||
// Watch for changes
|
// Watch for changes
|
||||||
viper.WatchConfig()
|
viper.WatchConfig()
|
||||||
viper.OnConfigChange(func(e fsnotify.Event) {
|
viper.OnConfigChange(func(e fsnotify.Event) {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package paths
|
package paths
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os/user"
|
"github.com/stashapp/stash/pkg/utils"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -25,16 +25,8 @@ func NewPaths() *Paths {
|
||||||
return &p
|
return &p
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetHomeDirectory() string {
|
|
||||||
currentUser, err := user.Current()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return currentUser.HomeDir
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetConfigDirectory() string {
|
func GetConfigDirectory() string {
|
||||||
return filepath.Join(GetHomeDirectory(), ".stash")
|
return filepath.Join(utils.GetHomeDirectory(), ".stash")
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetDefaultDatabaseFilePath() string {
|
func GetDefaultDatabaseFilePath() string {
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,10 @@ type ComplexityRoot struct {
|
||||||
Stashes func(childComplexity int) int
|
Stashes func(childComplexity int) int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ConfigResult struct {
|
||||||
|
General func(childComplexity int) int
|
||||||
|
}
|
||||||
|
|
||||||
FindGalleriesResultType struct {
|
FindGalleriesResultType struct {
|
||||||
Count func(childComplexity int) int
|
Count func(childComplexity int) int
|
||||||
Galleries func(childComplexity int) int
|
Galleries func(childComplexity int) int
|
||||||
|
|
@ -108,6 +112,7 @@ type ComplexityRoot struct {
|
||||||
TagCreate func(childComplexity int, input TagCreateInput) int
|
TagCreate func(childComplexity int, input TagCreateInput) int
|
||||||
TagUpdate func(childComplexity int, input TagUpdateInput) int
|
TagUpdate func(childComplexity int, input TagUpdateInput) int
|
||||||
TagDestroy func(childComplexity int, input TagDestroyInput) int
|
TagDestroy func(childComplexity int, input TagDestroyInput) int
|
||||||
|
ConfigureGeneral func(childComplexity int, input *ConfigGeneralInput) int
|
||||||
}
|
}
|
||||||
|
|
||||||
Performer struct {
|
Performer struct {
|
||||||
|
|
@ -153,7 +158,8 @@ type ComplexityRoot struct {
|
||||||
SceneMarkerTags func(childComplexity int, scene_id string) int
|
SceneMarkerTags func(childComplexity int, scene_id string) int
|
||||||
ScrapeFreeones func(childComplexity int, performer_name string) int
|
ScrapeFreeones func(childComplexity int, performer_name string) int
|
||||||
ScrapeFreeonesPerformerList func(childComplexity int, query string) int
|
ScrapeFreeonesPerformerList func(childComplexity int, query string) int
|
||||||
ConfigureGeneral func(childComplexity int, input *ConfigGeneralInput) int
|
Configuration func(childComplexity int) int
|
||||||
|
Directories func(childComplexity int, path *string) int
|
||||||
MetadataImport func(childComplexity int) int
|
MetadataImport func(childComplexity int) int
|
||||||
MetadataExport func(childComplexity int) int
|
MetadataExport func(childComplexity int) int
|
||||||
MetadataScan func(childComplexity int) int
|
MetadataScan func(childComplexity int) int
|
||||||
|
|
@ -284,6 +290,7 @@ type MutationResolver interface {
|
||||||
TagCreate(ctx context.Context, input TagCreateInput) (*Tag, error)
|
TagCreate(ctx context.Context, input TagCreateInput) (*Tag, error)
|
||||||
TagUpdate(ctx context.Context, input TagUpdateInput) (*Tag, error)
|
TagUpdate(ctx context.Context, input TagUpdateInput) (*Tag, error)
|
||||||
TagDestroy(ctx context.Context, input TagDestroyInput) (bool, error)
|
TagDestroy(ctx context.Context, input TagDestroyInput) (bool, error)
|
||||||
|
ConfigureGeneral(ctx context.Context, input *ConfigGeneralInput) (ConfigGeneralResult, error)
|
||||||
}
|
}
|
||||||
type PerformerResolver interface {
|
type PerformerResolver interface {
|
||||||
ID(ctx context.Context, obj *Performer) (string, error)
|
ID(ctx context.Context, obj *Performer) (string, error)
|
||||||
|
|
@ -327,7 +334,8 @@ type QueryResolver interface {
|
||||||
SceneMarkerTags(ctx context.Context, scene_id string) ([]SceneMarkerTag, error)
|
SceneMarkerTags(ctx context.Context, scene_id string) ([]SceneMarkerTag, error)
|
||||||
ScrapeFreeones(ctx context.Context, performer_name string) (*ScrapedPerformer, error)
|
ScrapeFreeones(ctx context.Context, performer_name string) (*ScrapedPerformer, error)
|
||||||
ScrapeFreeonesPerformerList(ctx context.Context, query string) ([]string, error)
|
ScrapeFreeonesPerformerList(ctx context.Context, query string) ([]string, error)
|
||||||
ConfigureGeneral(ctx context.Context, input *ConfigGeneralInput) (ConfigGeneralResult, error)
|
Configuration(ctx context.Context) (ConfigResult, error)
|
||||||
|
Directories(ctx context.Context, path *string) ([]string, error)
|
||||||
MetadataImport(ctx context.Context) (string, error)
|
MetadataImport(ctx context.Context) (string, error)
|
||||||
MetadataExport(ctx context.Context) (string, error)
|
MetadataExport(ctx context.Context) (string, error)
|
||||||
MetadataScan(ctx context.Context) (string, error)
|
MetadataScan(ctx context.Context) (string, error)
|
||||||
|
|
@ -607,6 +615,34 @@ func (e *executableSchema) field_Mutation_tagDestroy_args(ctx context.Context, r
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *executableSchema) field_Mutation_configureGeneral_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
||||||
|
args := map[string]interface{}{}
|
||||||
|
var arg0 *ConfigGeneralInput
|
||||||
|
if tmp, ok := rawArgs["input"]; ok {
|
||||||
|
var err error
|
||||||
|
var ptr1 ConfigGeneralInput
|
||||||
|
if tmp != nil {
|
||||||
|
ptr1, err = UnmarshalConfigGeneralInput(tmp)
|
||||||
|
arg0 = &ptr1
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if arg0 != nil {
|
||||||
|
var err error
|
||||||
|
arg0, err = e.ConfigGeneralInputMiddleware(ctx, arg0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
args["input"] = arg0
|
||||||
|
return args, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func (e *executableSchema) field_Query_findScene_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
func (e *executableSchema) field_Query_findScene_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
||||||
args := map[string]interface{}{}
|
args := map[string]interface{}{}
|
||||||
var arg0 *string
|
var arg0 *string
|
||||||
|
|
@ -1066,30 +1102,22 @@ func (e *executableSchema) field_Query_scrapeFreeonesPerformerList_args(ctx cont
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *executableSchema) field_Query_configureGeneral_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
func (e *executableSchema) field_Query_directories_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
||||||
args := map[string]interface{}{}
|
args := map[string]interface{}{}
|
||||||
var arg0 *ConfigGeneralInput
|
var arg0 *string
|
||||||
if tmp, ok := rawArgs["input"]; ok {
|
if tmp, ok := rawArgs["path"]; ok {
|
||||||
var err error
|
var err error
|
||||||
var ptr1 ConfigGeneralInput
|
var ptr1 string
|
||||||
if tmp != nil {
|
if tmp != nil {
|
||||||
ptr1, err = UnmarshalConfigGeneralInput(tmp)
|
ptr1, err = graphql.UnmarshalString(tmp)
|
||||||
arg0 = &ptr1
|
arg0 = &ptr1
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if arg0 != nil {
|
|
||||||
var err error
|
|
||||||
arg0, err = e.ConfigGeneralInputMiddleware(ctx, arg0)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
}
|
args["path"] = arg0
|
||||||
}
|
|
||||||
args["input"] = arg0
|
|
||||||
return args, nil
|
return args, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1159,6 +1187,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||||
|
|
||||||
return e.complexity.ConfigGeneralResult.Stashes(childComplexity), true
|
return e.complexity.ConfigGeneralResult.Stashes(childComplexity), true
|
||||||
|
|
||||||
|
case "ConfigResult.general":
|
||||||
|
if e.complexity.ConfigResult.General == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.complexity.ConfigResult.General(childComplexity), true
|
||||||
|
|
||||||
case "FindGalleriesResultType.count":
|
case "FindGalleriesResultType.count":
|
||||||
if e.complexity.FindGalleriesResultType.Count == nil {
|
if e.complexity.FindGalleriesResultType.Count == nil {
|
||||||
break
|
break
|
||||||
|
|
@ -1438,6 +1473,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||||
|
|
||||||
return e.complexity.Mutation.TagDestroy(childComplexity, args["input"].(TagDestroyInput)), true
|
return e.complexity.Mutation.TagDestroy(childComplexity, args["input"].(TagDestroyInput)), true
|
||||||
|
|
||||||
|
case "Mutation.configureGeneral":
|
||||||
|
if e.complexity.Mutation.ConfigureGeneral == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
args, err := e.field_Mutation_configureGeneral_args(context.TODO(), rawArgs)
|
||||||
|
if err != nil {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.complexity.Mutation.ConfigureGeneral(childComplexity, args["input"].(*ConfigGeneralInput)), true
|
||||||
|
|
||||||
case "Performer.id":
|
case "Performer.id":
|
||||||
if e.complexity.Performer.Id == nil {
|
if e.complexity.Performer.Id == nil {
|
||||||
break
|
break
|
||||||
|
|
@ -1796,17 +1843,24 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||||
|
|
||||||
return e.complexity.Query.ScrapeFreeonesPerformerList(childComplexity, args["query"].(string)), true
|
return e.complexity.Query.ScrapeFreeonesPerformerList(childComplexity, args["query"].(string)), true
|
||||||
|
|
||||||
case "Query.configureGeneral":
|
case "Query.configuration":
|
||||||
if e.complexity.Query.ConfigureGeneral == nil {
|
if e.complexity.Query.Configuration == nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
args, err := e.field_Query_configureGeneral_args(context.TODO(), rawArgs)
|
return e.complexity.Query.Configuration(childComplexity), true
|
||||||
|
|
||||||
|
case "Query.directories":
|
||||||
|
if e.complexity.Query.Directories == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
args, err := e.field_Query_directories_args(context.TODO(), rawArgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, false
|
return 0, false
|
||||||
}
|
}
|
||||||
|
|
||||||
return e.complexity.Query.ConfigureGeneral(childComplexity, args["input"].(*ConfigGeneralInput)), true
|
return e.complexity.Query.Directories(childComplexity, args["path"].(*string)), true
|
||||||
|
|
||||||
case "Query.metadataImport":
|
case "Query.metadataImport":
|
||||||
if e.complexity.Query.MetadataImport == nil {
|
if e.complexity.Query.MetadataImport == nil {
|
||||||
|
|
@ -2494,6 +2548,62 @@ func (ec *executionContext) _ConfigGeneralResult_stashes(ctx context.Context, fi
|
||||||
return arr1
|
return arr1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var configResultImplementors = []string{"ConfigResult"}
|
||||||
|
|
||||||
|
// nolint: gocyclo, errcheck, gas, goconst
|
||||||
|
func (ec *executionContext) _ConfigResult(ctx context.Context, sel ast.SelectionSet, obj *ConfigResult) graphql.Marshaler {
|
||||||
|
fields := graphql.CollectFields(ctx, sel, configResultImplementors)
|
||||||
|
|
||||||
|
out := graphql.NewFieldSet(fields)
|
||||||
|
invalid := false
|
||||||
|
for i, field := range fields {
|
||||||
|
switch field.Name {
|
||||||
|
case "__typename":
|
||||||
|
out.Values[i] = graphql.MarshalString("ConfigResult")
|
||||||
|
case "general":
|
||||||
|
out.Values[i] = ec._ConfigResult_general(ctx, field, obj)
|
||||||
|
if out.Values[i] == graphql.Null {
|
||||||
|
invalid = true
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
panic("unknown field " + strconv.Quote(field.Name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out.Dispatch()
|
||||||
|
if invalid {
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// nolint: vetshadow
|
||||||
|
func (ec *executionContext) _ConfigResult_general(ctx context.Context, field graphql.CollectedField, obj *ConfigResult) graphql.Marshaler {
|
||||||
|
ctx = ec.Tracer.StartFieldExecution(ctx, field)
|
||||||
|
defer func() { ec.Tracer.EndFieldExecution(ctx) }()
|
||||||
|
rctx := &graphql.ResolverContext{
|
||||||
|
Object: "ConfigResult",
|
||||||
|
Field: field,
|
||||||
|
Args: nil,
|
||||||
|
}
|
||||||
|
ctx = graphql.WithResolverContext(ctx, rctx)
|
||||||
|
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
|
||||||
|
resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) {
|
||||||
|
ctx = rctx // use context from middleware stack in children
|
||||||
|
return obj.General, nil
|
||||||
|
})
|
||||||
|
if resTmp == nil {
|
||||||
|
if !ec.HasError(rctx) {
|
||||||
|
ec.Errorf(ctx, "must not be null")
|
||||||
|
}
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
res := resTmp.(ConfigGeneralResult)
|
||||||
|
rctx.Result = res
|
||||||
|
ctx = ec.Tracer.StartFieldChildExecution(ctx)
|
||||||
|
|
||||||
|
return ec._ConfigGeneralResult(ctx, field.Selections, &res)
|
||||||
|
}
|
||||||
|
|
||||||
var findGalleriesResultTypeImplementors = []string{"FindGalleriesResultType"}
|
var findGalleriesResultTypeImplementors = []string{"FindGalleriesResultType"}
|
||||||
|
|
||||||
// nolint: gocyclo, errcheck, gas, goconst
|
// nolint: gocyclo, errcheck, gas, goconst
|
||||||
|
|
@ -3598,6 +3708,11 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet)
|
||||||
if out.Values[i] == graphql.Null {
|
if out.Values[i] == graphql.Null {
|
||||||
invalid = true
|
invalid = true
|
||||||
}
|
}
|
||||||
|
case "configureGeneral":
|
||||||
|
out.Values[i] = ec._Mutation_configureGeneral(ctx, field)
|
||||||
|
if out.Values[i] == graphql.Null {
|
||||||
|
invalid = true
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
panic("unknown field " + strconv.Quote(field.Name))
|
panic("unknown field " + strconv.Quote(field.Name))
|
||||||
}
|
}
|
||||||
|
|
@ -4001,6 +4116,41 @@ func (ec *executionContext) _Mutation_tagDestroy(ctx context.Context, field grap
|
||||||
return graphql.MarshalBoolean(res)
|
return graphql.MarshalBoolean(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint: vetshadow
|
||||||
|
func (ec *executionContext) _Mutation_configureGeneral(ctx context.Context, field graphql.CollectedField) graphql.Marshaler {
|
||||||
|
ctx = ec.Tracer.StartFieldExecution(ctx, field)
|
||||||
|
defer func() { ec.Tracer.EndFieldExecution(ctx) }()
|
||||||
|
rctx := &graphql.ResolverContext{
|
||||||
|
Object: "Mutation",
|
||||||
|
Field: field,
|
||||||
|
Args: nil,
|
||||||
|
}
|
||||||
|
ctx = graphql.WithResolverContext(ctx, rctx)
|
||||||
|
rawArgs := field.ArgumentMap(ec.Variables)
|
||||||
|
args, err := ec.field_Mutation_configureGeneral_args(ctx, rawArgs)
|
||||||
|
if err != nil {
|
||||||
|
ec.Error(ctx, err)
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
rctx.Args = args
|
||||||
|
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
|
||||||
|
resTmp := ec.FieldMiddleware(ctx, nil, func(rctx context.Context) (interface{}, error) {
|
||||||
|
ctx = rctx // use context from middleware stack in children
|
||||||
|
return ec.resolvers.Mutation().ConfigureGeneral(rctx, args["input"].(*ConfigGeneralInput))
|
||||||
|
})
|
||||||
|
if resTmp == nil {
|
||||||
|
if !ec.HasError(rctx) {
|
||||||
|
ec.Errorf(ctx, "must not be null")
|
||||||
|
}
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
res := resTmp.(ConfigGeneralResult)
|
||||||
|
rctx.Result = res
|
||||||
|
ctx = ec.Tracer.StartFieldChildExecution(ctx)
|
||||||
|
|
||||||
|
return ec._ConfigGeneralResult(ctx, field.Selections, &res)
|
||||||
|
}
|
||||||
|
|
||||||
var performerImplementors = []string{"Performer"}
|
var performerImplementors = []string{"Performer"}
|
||||||
|
|
||||||
// nolint: gocyclo, errcheck, gas, goconst
|
// nolint: gocyclo, errcheck, gas, goconst
|
||||||
|
|
@ -4935,10 +5085,19 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
})
|
})
|
||||||
case "configureGeneral":
|
case "configuration":
|
||||||
field := field
|
field := field
|
||||||
out.Concurrently(i, func() (res graphql.Marshaler) {
|
out.Concurrently(i, func() (res graphql.Marshaler) {
|
||||||
res = ec._Query_configureGeneral(ctx, field)
|
res = ec._Query_configuration(ctx, field)
|
||||||
|
if res == graphql.Null {
|
||||||
|
invalid = true
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
})
|
||||||
|
case "directories":
|
||||||
|
field := field
|
||||||
|
out.Concurrently(i, func() (res graphql.Marshaler) {
|
||||||
|
res = ec._Query_directories(ctx, field)
|
||||||
if res == graphql.Null {
|
if res == graphql.Null {
|
||||||
invalid = true
|
invalid = true
|
||||||
}
|
}
|
||||||
|
|
@ -5833,7 +5992,35 @@ func (ec *executionContext) _Query_scrapeFreeonesPerformerList(ctx context.Conte
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint: vetshadow
|
// nolint: vetshadow
|
||||||
func (ec *executionContext) _Query_configureGeneral(ctx context.Context, field graphql.CollectedField) graphql.Marshaler {
|
func (ec *executionContext) _Query_configuration(ctx context.Context, field graphql.CollectedField) graphql.Marshaler {
|
||||||
|
ctx = ec.Tracer.StartFieldExecution(ctx, field)
|
||||||
|
defer func() { ec.Tracer.EndFieldExecution(ctx) }()
|
||||||
|
rctx := &graphql.ResolverContext{
|
||||||
|
Object: "Query",
|
||||||
|
Field: field,
|
||||||
|
Args: nil,
|
||||||
|
}
|
||||||
|
ctx = graphql.WithResolverContext(ctx, rctx)
|
||||||
|
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
|
||||||
|
resTmp := ec.FieldMiddleware(ctx, nil, func(rctx context.Context) (interface{}, error) {
|
||||||
|
ctx = rctx // use context from middleware stack in children
|
||||||
|
return ec.resolvers.Query().Configuration(rctx)
|
||||||
|
})
|
||||||
|
if resTmp == nil {
|
||||||
|
if !ec.HasError(rctx) {
|
||||||
|
ec.Errorf(ctx, "must not be null")
|
||||||
|
}
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
res := resTmp.(ConfigResult)
|
||||||
|
rctx.Result = res
|
||||||
|
ctx = ec.Tracer.StartFieldChildExecution(ctx)
|
||||||
|
|
||||||
|
return ec._ConfigResult(ctx, field.Selections, &res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// nolint: vetshadow
|
||||||
|
func (ec *executionContext) _Query_directories(ctx context.Context, field graphql.CollectedField) graphql.Marshaler {
|
||||||
ctx = ec.Tracer.StartFieldExecution(ctx, field)
|
ctx = ec.Tracer.StartFieldExecution(ctx, field)
|
||||||
defer func() { ec.Tracer.EndFieldExecution(ctx) }()
|
defer func() { ec.Tracer.EndFieldExecution(ctx) }()
|
||||||
rctx := &graphql.ResolverContext{
|
rctx := &graphql.ResolverContext{
|
||||||
|
|
@ -5843,7 +6030,7 @@ func (ec *executionContext) _Query_configureGeneral(ctx context.Context, field g
|
||||||
}
|
}
|
||||||
ctx = graphql.WithResolverContext(ctx, rctx)
|
ctx = graphql.WithResolverContext(ctx, rctx)
|
||||||
rawArgs := field.ArgumentMap(ec.Variables)
|
rawArgs := field.ArgumentMap(ec.Variables)
|
||||||
args, err := ec.field_Query_configureGeneral_args(ctx, rawArgs)
|
args, err := ec.field_Query_directories_args(ctx, rawArgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ec.Error(ctx, err)
|
ec.Error(ctx, err)
|
||||||
return graphql.Null
|
return graphql.Null
|
||||||
|
|
@ -5852,7 +6039,7 @@ func (ec *executionContext) _Query_configureGeneral(ctx context.Context, field g
|
||||||
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
|
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
|
||||||
resTmp := ec.FieldMiddleware(ctx, nil, func(rctx context.Context) (interface{}, error) {
|
resTmp := ec.FieldMiddleware(ctx, nil, func(rctx context.Context) (interface{}, error) {
|
||||||
ctx = rctx // use context from middleware stack in children
|
ctx = rctx // use context from middleware stack in children
|
||||||
return ec.resolvers.Query().ConfigureGeneral(rctx, args["input"].(*ConfigGeneralInput))
|
return ec.resolvers.Query().Directories(rctx, args["path"].(*string))
|
||||||
})
|
})
|
||||||
if resTmp == nil {
|
if resTmp == nil {
|
||||||
if !ec.HasError(rctx) {
|
if !ec.HasError(rctx) {
|
||||||
|
|
@ -5860,11 +6047,19 @@ func (ec *executionContext) _Query_configureGeneral(ctx context.Context, field g
|
||||||
}
|
}
|
||||||
return graphql.Null
|
return graphql.Null
|
||||||
}
|
}
|
||||||
res := resTmp.(ConfigGeneralResult)
|
res := resTmp.([]string)
|
||||||
rctx.Result = res
|
rctx.Result = res
|
||||||
ctx = ec.Tracer.StartFieldChildExecution(ctx)
|
ctx = ec.Tracer.StartFieldChildExecution(ctx)
|
||||||
|
|
||||||
return ec._ConfigGeneralResult(ctx, field.Selections, &res)
|
arr1 := make(graphql.Array, len(res))
|
||||||
|
|
||||||
|
for idx1 := range res {
|
||||||
|
arr1[idx1] = func() graphql.Marshaler {
|
||||||
|
return graphql.MarshalString(res[idx1])
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr1
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint: vetshadow
|
// nolint: vetshadow
|
||||||
|
|
@ -11960,6 +12155,11 @@ type ConfigGeneralResult {
|
||||||
stashes: [String!]
|
stashes: [String!]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"""All configuration settings"""
|
||||||
|
type ConfigResult {
|
||||||
|
general: ConfigGeneralResult!
|
||||||
|
}
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# Root Schema
|
# Root Schema
|
||||||
#############
|
#############
|
||||||
|
|
@ -12011,7 +12211,10 @@ type Query {
|
||||||
scrapeFreeonesPerformerList(query: String!): [String!]!
|
scrapeFreeonesPerformerList(query: String!): [String!]!
|
||||||
|
|
||||||
# Config
|
# Config
|
||||||
configureGeneral(input: ConfigGeneralInput): ConfigGeneralResult!
|
"""Returns the current, complete configuration"""
|
||||||
|
configuration: ConfigResult!
|
||||||
|
"""Returns an array of paths for the given path"""
|
||||||
|
directories(path: String): [String!]!
|
||||||
|
|
||||||
# Metadata
|
# Metadata
|
||||||
|
|
||||||
|
|
@ -12049,10 +12252,13 @@ type Mutation {
|
||||||
tagCreate(input: TagCreateInput!): Tag
|
tagCreate(input: TagCreateInput!): Tag
|
||||||
tagUpdate(input: TagUpdateInput!): Tag
|
tagUpdate(input: TagUpdateInput!): Tag
|
||||||
tagDestroy(input: TagDestroyInput!): Boolean!
|
tagDestroy(input: TagDestroyInput!): Boolean!
|
||||||
|
|
||||||
|
"""Change general configuration options"""
|
||||||
|
configureGeneral(input: ConfigGeneralInput): ConfigGeneralResult!
|
||||||
}
|
}
|
||||||
|
|
||||||
type Subscription {
|
type Subscription {
|
||||||
"""Update from the meatadata manager"""
|
"""Update from the metadata manager"""
|
||||||
metadataUpdate: String!
|
metadataUpdate: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,11 @@ type ConfigGeneralResult struct {
|
||||||
Stashes []string `json:"stashes"`
|
Stashes []string `json:"stashes"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// All configuration settings
|
||||||
|
type ConfigResult struct {
|
||||||
|
General ConfigGeneralResult `json:"general"`
|
||||||
|
}
|
||||||
|
|
||||||
type FindFilterType struct {
|
type FindFilterType struct {
|
||||||
Q *string `json:"q"`
|
Q *string `json:"q"`
|
||||||
Page *int `json:"page"`
|
Page *int `json:"page"`
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,9 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/h2non/filetype"
|
"github.com/h2non/filetype"
|
||||||
"github.com/h2non/filetype/types"
|
"github.com/h2non/filetype/types"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"os/user"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -84,3 +86,41 @@ func EmptyDir(path string) error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ListDir(path string) []string {
|
||||||
|
if path == "" {
|
||||||
|
path = GetHomeDirectory()
|
||||||
|
}
|
||||||
|
|
||||||
|
absolutePath, err := filepath.Abs(path)
|
||||||
|
if err == nil {
|
||||||
|
path = absolutePath
|
||||||
|
}
|
||||||
|
|
||||||
|
files, err := ioutil.ReadDir(path)
|
||||||
|
if err != nil {
|
||||||
|
path = filepath.Dir(path)
|
||||||
|
files, err = ioutil.ReadDir(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
var dirPaths []string
|
||||||
|
for _, file := range files {
|
||||||
|
if !file.IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
abs, err := filepath.Abs(path)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
dirPaths = append(dirPaths, filepath.Join(abs, file.Name()))
|
||||||
|
}
|
||||||
|
return dirPaths
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetHomeDirectory() string {
|
||||||
|
currentUser, err := user.Current()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return currentUser.HomeDir
|
||||||
|
}
|
||||||
|
|
|
||||||
9
schema/documents/data/config.graphql
Normal file
9
schema/documents/data/config.graphql
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
fragment ConfigGeneralData on ConfigGeneralResult {
|
||||||
|
stashes
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment ConfigData on ConfigResult {
|
||||||
|
general {
|
||||||
|
...ConfigGeneralData
|
||||||
|
}
|
||||||
|
}
|
||||||
5
schema/documents/mutations/config.graphql
Normal file
5
schema/documents/mutations/config.graphql
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
mutation ConfigureGeneral($input: ConfigGeneralInput!) {
|
||||||
|
configureGeneral(input: $input) {
|
||||||
|
...ConfigGeneralData
|
||||||
|
}
|
||||||
|
}
|
||||||
9
schema/documents/queries/settings/config.graphql
Normal file
9
schema/documents/queries/settings/config.graphql
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
query Configuration {
|
||||||
|
configuration {
|
||||||
|
...ConfigData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
query Directories($path: String) {
|
||||||
|
directories(path: $path)
|
||||||
|
}
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
query ConfigureGeneral($input: ConfigGeneralInput!) {
|
|
||||||
configureGeneral(input: $input)
|
|
||||||
}
|
|
||||||
|
|
@ -386,6 +386,11 @@ type ConfigGeneralResult {
|
||||||
stashes: [String!]
|
stashes: [String!]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"""All configuration settings"""
|
||||||
|
type ConfigResult {
|
||||||
|
general: ConfigGeneralResult!
|
||||||
|
}
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# Root Schema
|
# Root Schema
|
||||||
#############
|
#############
|
||||||
|
|
@ -437,7 +442,10 @@ type Query {
|
||||||
scrapeFreeonesPerformerList(query: String!): [String!]!
|
scrapeFreeonesPerformerList(query: String!): [String!]!
|
||||||
|
|
||||||
# Config
|
# Config
|
||||||
configureGeneral(input: ConfigGeneralInput): ConfigGeneralResult!
|
"""Returns the current, complete configuration"""
|
||||||
|
configuration: ConfigResult!
|
||||||
|
"""Returns an array of paths for the given path"""
|
||||||
|
directories(path: String): [String!]!
|
||||||
|
|
||||||
# Metadata
|
# Metadata
|
||||||
|
|
||||||
|
|
@ -475,10 +483,13 @@ type Mutation {
|
||||||
tagCreate(input: TagCreateInput!): Tag
|
tagCreate(input: TagCreateInput!): Tag
|
||||||
tagUpdate(input: TagUpdateInput!): Tag
|
tagUpdate(input: TagUpdateInput!): Tag
|
||||||
tagDestroy(input: TagDestroyInput!): Boolean!
|
tagDestroy(input: TagDestroyInput!): Boolean!
|
||||||
|
|
||||||
|
"""Change general configuration options"""
|
||||||
|
configureGeneral(input: ConfigGeneralInput): ConfigGeneralResult!
|
||||||
}
|
}
|
||||||
|
|
||||||
type Subscription {
|
type Subscription {
|
||||||
"""Update from the meatadata manager"""
|
"""Update from the metadata manager"""
|
||||||
metadataUpdate: String!
|
metadataUpdate: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,73 @@
|
||||||
import {
|
import {
|
||||||
|
Button,
|
||||||
|
Divider,
|
||||||
|
FormGroup,
|
||||||
H1,
|
H1,
|
||||||
H4,
|
H4,
|
||||||
H6,
|
H6,
|
||||||
|
Spinner,
|
||||||
Tag,
|
Tag,
|
||||||
} from "@blueprintjs/core";
|
} from "@blueprintjs/core";
|
||||||
import React, { FunctionComponent } from "react";
|
import React, { FunctionComponent, useEffect, useState } from "react";
|
||||||
import * as GQL from "../../core/generated-graphql";
|
import * as GQL from "../../core/generated-graphql";
|
||||||
|
import { StashService } from "../../core/StashService";
|
||||||
|
import { ErrorUtils } from "../../utils/errors";
|
||||||
import { TextUtils } from "../../utils/text";
|
import { TextUtils } from "../../utils/text";
|
||||||
|
import { ToastUtils } from "../../utils/toasts";
|
||||||
|
import { FolderSelect } from "../Shared/FolderSelect/FolderSelect";
|
||||||
|
|
||||||
interface IProps {}
|
interface IProps {}
|
||||||
|
|
||||||
export const SettingsConfigurationPanel: FunctionComponent<IProps> = (props: IProps) => {
|
export const SettingsConfigurationPanel: FunctionComponent<IProps> = (props: IProps) => {
|
||||||
|
// Editing config state
|
||||||
|
const [stashes, setStashes] = useState<string[]>([]);
|
||||||
|
|
||||||
|
// const [config, setConfig] = useState<Partial<GQL.ConfigDataFragment>>({});
|
||||||
|
const { data, error, loading } = StashService.useConfiguration();
|
||||||
|
|
||||||
|
const updateGeneralConfig = StashService.useConfigureGeneral({
|
||||||
|
stashes,
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!data || !data.configuration || !!error) { return; }
|
||||||
|
const conf = StashService.nullToUndefined(data.configuration) as GQL.ConfigDataFragment;
|
||||||
|
if (!!conf.general) {
|
||||||
|
setStashes(conf.general.stashes || []);
|
||||||
|
}
|
||||||
|
// setConfig(conf);
|
||||||
|
}, [data]);
|
||||||
|
|
||||||
|
function onStashesChanged(directories: string[]) {
|
||||||
|
setStashes(directories);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onSave() {
|
||||||
|
try {
|
||||||
|
const result = await updateGeneralConfig();
|
||||||
|
console.log(result);
|
||||||
|
ToastUtils.success("Updated config");
|
||||||
|
} catch (e) {
|
||||||
|
ErrorUtils.handle(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
Configuration
|
{!!error ? error : undefined}
|
||||||
|
{(!data || !data.configuration || loading) ? <Spinner size={Spinner.SIZE_LARGE} /> : undefined}
|
||||||
|
<H4>Library</H4>
|
||||||
|
<FormGroup
|
||||||
|
label="Stashes"
|
||||||
|
helperText="Directory locations to your content"
|
||||||
|
>
|
||||||
|
<FolderSelect
|
||||||
|
directories={stashes}
|
||||||
|
onDirectoriesChanged={onStashesChanged}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
<Divider />
|
||||||
|
<Button intent="primary" onClick={() => onSave()}>Save</Button>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
85
ui/v2/src/components/Shared/FolderSelect/FolderSelect.tsx
Normal file
85
ui/v2/src/components/Shared/FolderSelect/FolderSelect.tsx
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Classes,
|
||||||
|
Dialog,
|
||||||
|
InputGroup,
|
||||||
|
Spinner,
|
||||||
|
} from "@blueprintjs/core";
|
||||||
|
import _ from "lodash";
|
||||||
|
import React, { FunctionComponent, useEffect, useState } from "react";
|
||||||
|
import { StashService } from "../../../core/StashService";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
directories: string[];
|
||||||
|
onDirectoriesChanged: (directories: string[]) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FolderSelect: FunctionComponent<IProps> = (props: IProps) => {
|
||||||
|
const [currentDirectory, setCurrentDirectory] = useState<string>("");
|
||||||
|
const [isDisplayingDialog, setIsDisplayingDialog] = useState<boolean>(false);
|
||||||
|
const [selectableDirectories, setSelectableDirectories] = useState<string[]>([]);
|
||||||
|
const [selectedDirectories, setSelectedDirectories] = useState<string[]>([]);
|
||||||
|
const { data, error, loading } = StashService.useDirectories(currentDirectory);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setSelectedDirectories(props.directories);
|
||||||
|
}, [props.directories]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!data || !data.directories || !!error) { return; }
|
||||||
|
setSelectableDirectories(StashService.nullToUndefined(data.directories));
|
||||||
|
}, [data]);
|
||||||
|
|
||||||
|
function onSelectDirectory() {
|
||||||
|
selectedDirectories.push(currentDirectory);
|
||||||
|
setSelectedDirectories(selectedDirectories);
|
||||||
|
setCurrentDirectory("");
|
||||||
|
setIsDisplayingDialog(false);
|
||||||
|
props.onDirectoriesChanged(selectedDirectories);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onRemoveDirectory(directory: string) {
|
||||||
|
const newSelectedDirectories = selectedDirectories.filter((dir) => dir !== directory);
|
||||||
|
setSelectedDirectories(newSelectedDirectories);
|
||||||
|
props.onDirectoriesChanged(newSelectedDirectories);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderDialog() {
|
||||||
|
return (
|
||||||
|
<Dialog
|
||||||
|
isOpen={isDisplayingDialog}
|
||||||
|
onClose={() => setIsDisplayingDialog(false)}
|
||||||
|
title="Select Directory"
|
||||||
|
>
|
||||||
|
<div className="dialog-content">
|
||||||
|
<InputGroup
|
||||||
|
large={true}
|
||||||
|
placeholder="File path"
|
||||||
|
onChange={(e: any) => setCurrentDirectory(e.target.value)}
|
||||||
|
value={currentDirectory}
|
||||||
|
rightElement={(!data || !data.directories || loading) ? <Spinner size={Spinner.SIZE_SMALL} /> : undefined}
|
||||||
|
/>
|
||||||
|
{selectableDirectories.map((path) => {
|
||||||
|
return <div key={path} onClick={() => setCurrentDirectory(path)}>{path}</div>;
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
<div className={Classes.DIALOG_FOOTER}>
|
||||||
|
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
|
||||||
|
<Button onClick={() => onSelectDirectory()}>Add</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{!!error ? error : undefined}
|
||||||
|
{renderDialog()}
|
||||||
|
{selectedDirectories.map((path) => {
|
||||||
|
return <div key={path}>{path} <a onClick={() => onRemoveDirectory(path)}>Remove</a></div>;
|
||||||
|
})}
|
||||||
|
<Button small={true} onClick={() => setIsDisplayingDialog(true)}>Add Directory</Button>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -118,6 +118,9 @@ export class StashService {
|
||||||
}
|
}
|
||||||
public static useStats() { return GQL.useStats(); }
|
public static useStats() { return GQL.useStats(); }
|
||||||
|
|
||||||
|
public static useConfiguration() { return GQL.useConfiguration(); }
|
||||||
|
public static useDirectories(path?: string) { return GQL.useDirectories({ variables: { path }}); }
|
||||||
|
|
||||||
public static usePerformerCreate(input: GQL.PerformerCreateInput) {
|
public static usePerformerCreate(input: GQL.PerformerCreateInput) {
|
||||||
return GQL.usePerformerCreate({ variables: input });
|
return GQL.usePerformerCreate({ variables: input });
|
||||||
}
|
}
|
||||||
|
|
@ -143,6 +146,10 @@ export class StashService {
|
||||||
return GQL.useTagUpdate({ variables: input, refetchQueries: ["AllTags"] });
|
return GQL.useTagUpdate({ variables: input, refetchQueries: ["AllTags"] });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static useConfigureGeneral(input: GQL.ConfigGeneralInput) {
|
||||||
|
return GQL.useConfigureGeneral({ variables: { input }, refetchQueries: ["Configuration"] });
|
||||||
|
}
|
||||||
|
|
||||||
public static queryScrapeFreeones(performerName: string) {
|
public static queryScrapeFreeones(performerName: string) {
|
||||||
return StashService.client.query<GQL.ScrapeFreeonesQuery>({
|
return StashService.client.query<GQL.ScrapeFreeonesQuery>({
|
||||||
query: GQL.ScrapeFreeonesDocument,
|
query: GQL.ScrapeFreeonesDocument,
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue