Skip to content

Commit

Permalink
Merge branch 'develop' into postgres-support-2
Browse files Browse the repository at this point in the history
  • Loading branch information
DogmaDragon authored Nov 12, 2024
2 parents 7dd8643 + 64fed35 commit 9b3ed54
Show file tree
Hide file tree
Showing 38 changed files with 1,619 additions and 828 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/golangci-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ jobs:
run: docker exec -t build /bin/bash -c "make generate-backend"

- name: Run golangci-lint
uses: golangci/golangci-lint-action@v3
uses: golangci/golangci-lint-action@v6
with:
# Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version
version: latest
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ require (
github.com/go-chi/httplog v0.3.1
github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4
github.com/gofrs/uuid/v5 v5.1.0
github.com/golang-jwt/jwt/v4 v4.5.0
github.com/golang-jwt/jwt/v4 v4.5.1
github.com/golang-migrate/migrate/v4 v4.16.2
github.com/gorilla/securecookie v1.1.1
github.com/gorilla/sessions v1.2.1
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -259,8 +259,8 @@ github.com/gofrs/uuid/v5 v5.1.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo=
github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-migrate/migrate/v4 v4.16.2 h1:8coYbMKUyInrFk1lfGfRovTLAW7PhWp8qQDT2iKfuoA=
github.com/golang-migrate/migrate/v4 v4.16.2/go.mod h1:pfcJX4nPHaVdc5nmdCikFBWtm+UBpiZjRNNsyBbp0/o=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
Expand Down
9 changes: 9 additions & 0 deletions graphql/schema/types/scene-marker.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ type SceneMarker {
id: ID!
scene: Scene!
title: String!
"The required start time of the marker (in seconds). Supports decimals."
seconds: Float!
"The optional end time of the marker (in seconds). Supports decimals."
end_seconds: Float
primary_tag: Tag!
tags: [Tag!]!
created_at: Time!
Expand All @@ -18,7 +21,10 @@ type SceneMarker {

input SceneMarkerCreateInput {
title: String!
"The required start time of the marker (in seconds). Supports decimals."
seconds: Float!
"The optional end time of the marker (in seconds). Supports decimals."
end_seconds: Float
scene_id: ID!
primary_tag_id: ID!
tag_ids: [ID!]
Expand All @@ -27,7 +33,10 @@ input SceneMarkerCreateInput {
input SceneMarkerUpdateInput {
id: ID!
title: String
"The start time of the marker (in seconds). Supports decimals."
seconds: Float
"The end time of the marker (in seconds). Supports decimals."
end_seconds: Float
scene_id: ID
primary_tag_id: ID
tag_ids: [ID!]
Expand Down
37 changes: 36 additions & 1 deletion internal/api/resolver_mutation_scene.go
Original file line number Diff line number Diff line change
Expand Up @@ -655,6 +655,13 @@ func (r *mutationResolver) SceneMarkerCreate(ctx context.Context, input SceneMar
newMarker.PrimaryTagID = primaryTagID
newMarker.SceneID = sceneID

if input.EndSeconds != nil {
if err := validateSceneMarkerEndSeconds(newMarker.Seconds, *input.EndSeconds); err != nil {
return nil, err
}
newMarker.EndSeconds = input.EndSeconds
}

tagIDs, err := stringslice.StringSliceToIntSlice(input.TagIds)
if err != nil {
return nil, fmt.Errorf("converting tag ids: %w", err)
Expand All @@ -680,6 +687,13 @@ func (r *mutationResolver) SceneMarkerCreate(ctx context.Context, input SceneMar
return r.getSceneMarker(ctx, newMarker.ID)
}

func validateSceneMarkerEndSeconds(seconds, endSeconds float64) error {
if endSeconds < seconds {
return fmt.Errorf("end_seconds (%f) must be greater than or equal to seconds (%f)", endSeconds, seconds)
}
return nil
}

func (r *mutationResolver) SceneMarkerUpdate(ctx context.Context, input SceneMarkerUpdateInput) (*models.SceneMarker, error) {
markerID, err := strconv.Atoi(input.ID)
if err != nil {
Expand All @@ -695,6 +709,7 @@ func (r *mutationResolver) SceneMarkerUpdate(ctx context.Context, input SceneMar

updatedMarker.Title = translator.optionalString(input.Title, "title")
updatedMarker.Seconds = translator.optionalFloat64(input.Seconds, "seconds")
updatedMarker.EndSeconds = translator.optionalFloat64(input.EndSeconds, "end_seconds")
updatedMarker.SceneID, err = translator.optionalIntFromString(input.SceneID, "scene_id")
if err != nil {
return nil, fmt.Errorf("converting scene id: %w", err)
Expand Down Expand Up @@ -735,6 +750,26 @@ func (r *mutationResolver) SceneMarkerUpdate(ctx context.Context, input SceneMar
return fmt.Errorf("scene marker with id %d not found", markerID)
}

// Validate end_seconds
shouldValidateEndSeconds := (updatedMarker.Seconds.Set || updatedMarker.EndSeconds.Set) && !updatedMarker.EndSeconds.Null
if shouldValidateEndSeconds {
seconds := existingMarker.Seconds
if updatedMarker.Seconds.Set {
seconds = updatedMarker.Seconds.Value
}

endSeconds := existingMarker.EndSeconds
if updatedMarker.EndSeconds.Set {
endSeconds = &updatedMarker.EndSeconds.Value
}

if endSeconds != nil {
if err := validateSceneMarkerEndSeconds(seconds, *endSeconds); err != nil {
return err
}
}
}

newMarker, err := qb.UpdatePartial(ctx, markerID, updatedMarker)
if err != nil {
return err
Expand All @@ -749,7 +784,7 @@ func (r *mutationResolver) SceneMarkerUpdate(ctx context.Context, input SceneMar
}

// remove the marker preview if the scene changed or if the timestamp was changed
if existingMarker.SceneID != newMarker.SceneID || existingMarker.Seconds != newMarker.Seconds {
if existingMarker.SceneID != newMarker.SceneID || existingMarker.Seconds != newMarker.Seconds || existingMarker.EndSeconds != newMarker.EndSeconds {
seconds := int(existingMarker.Seconds)
if err := fileDeleter.MarkMarkerFiles(existingScene, seconds); err != nil {
return err
Expand Down
4 changes: 4 additions & 0 deletions internal/manager/json_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,7 @@ func (jp *jsonUtils) saveGallery(fn string, gallery *jsonschema.Gallery) error {
func (jp *jsonUtils) saveFile(fn string, file jsonschema.DirEntry) error {
return jsonschema.SaveFileFile(filepath.Join(jp.json.Files, fn), file)
}

func (jp *jsonUtils) saveSavedFilter(fn string, savedFilter *jsonschema.SavedFilter) error {
return jsonschema.SaveSavedFilterFile(filepath.Join(jp.json.SavedFilters, fn), savedFilter)
}
61 changes: 61 additions & 0 deletions internal/manager/task_export.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/stashapp/stash/pkg/models/jsonschema"
"github.com/stashapp/stash/pkg/models/paths"
"github.com/stashapp/stash/pkg/performer"
"github.com/stashapp/stash/pkg/savedfilter"
"github.com/stashapp/stash/pkg/scene"
"github.com/stashapp/stash/pkg/sliceutil"
"github.com/stashapp/stash/pkg/sliceutil/stringslice"
Expand Down Expand Up @@ -176,6 +177,7 @@ func (t *ExportTask) Start(ctx context.Context, wg *sync.WaitGroup) {
t.ExportPerformers(ctx, workerCount)
t.ExportStudios(ctx, workerCount)
t.ExportTags(ctx, workerCount)
t.ExportSavedFilters(ctx, workerCount)

return nil
})
Expand Down Expand Up @@ -1186,3 +1188,62 @@ func (t *ExportTask) exportGroup(ctx context.Context, wg *sync.WaitGroup, jobCha
}
}
}

func (t *ExportTask) ExportSavedFilters(ctx context.Context, workers int) {
// don't export saved filters unless we're doing a full export
if !t.full {
return
}

var wg sync.WaitGroup

reader := t.repository.SavedFilter
var filters []*models.SavedFilter
var err error
filters, err = reader.All(ctx)

if err != nil {
logger.Errorf("[saved filters] failed to fetch saved filters: %v", err)
}

logger.Info("[saved filters] exporting")
startTime := time.Now()

jobCh := make(chan *models.SavedFilter, workers*2) // make a buffered channel to feed workers

for w := 0; w < workers; w++ { // create export Saved Filter workers
wg.Add(1)
go t.exportSavedFilter(ctx, &wg, jobCh)
}

for i, savedFilter := range filters {
index := i + 1
logger.Progressf("[saved filters] %d of %d", index, len(filters))

jobCh <- savedFilter // feed workers
}

close(jobCh)
wg.Wait()

logger.Infof("[saved filters] export complete in %s. %d workers used.", time.Since(startTime), workers)
}

func (t *ExportTask) exportSavedFilter(ctx context.Context, wg *sync.WaitGroup, jobChan <-chan *models.SavedFilter) {
defer wg.Done()

for thisFilter := range jobChan {
newJSON, err := savedfilter.ToJSON(ctx, thisFilter)

if err != nil {
logger.Errorf("[saved filter] <%s> error getting saved filter JSON: %v", thisFilter.Name, err)
continue
}

fn := newJSON.Filename()

if err := t.json.saveSavedFilter(fn, newJSON); err != nil {
logger.Errorf("[saved filter] <%s> failed to save json: %v", fn, err)
}
}
}
52 changes: 52 additions & 0 deletions internal/manager/task_import.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/stashapp/stash/pkg/models/jsonschema"
"github.com/stashapp/stash/pkg/models/paths"
"github.com/stashapp/stash/pkg/performer"
"github.com/stashapp/stash/pkg/savedfilter"
"github.com/stashapp/stash/pkg/scene"
"github.com/stashapp/stash/pkg/studio"
"github.com/stashapp/stash/pkg/tag"
Expand Down Expand Up @@ -124,6 +125,7 @@ func (t *ImportTask) Start(ctx context.Context) {
}
}

t.ImportSavedFilters(ctx)
t.ImportTags(ctx)
t.ImportPerformers(ctx)
t.ImportStudios(ctx)
Expand Down Expand Up @@ -779,3 +781,53 @@ func (t *ImportTask) ImportImages(ctx context.Context) {

logger.Info("[images] import complete")
}

func (t *ImportTask) ImportSavedFilters(ctx context.Context) {
logger.Info("[saved filters] importing")

path := t.json.json.SavedFilters
files, err := os.ReadDir(path)
if err != nil {
if !errors.Is(err, os.ErrNotExist) {
logger.Errorf("[saved filters] failed to read saved filters directory: %v", err)
}

return
}

r := t.repository

for i, fi := range files {
index := i + 1
savedFilterJSON, err := jsonschema.LoadSavedFilterFile(filepath.Join(path, fi.Name()))
if err != nil {
logger.Errorf("[saved filters] failed to read json: %v", err)
continue
}

logger.Progressf("[saved filters] %d of %d", index, len(files))

if err := r.WithTxn(ctx, func(ctx context.Context) error {
return t.importSavedFilter(ctx, savedFilterJSON)
}); err != nil {
logger.Errorf("[saved filters] <%s> failed to import: %v", fi.Name(), err)
continue
}
}

logger.Info("[saved filters] import complete")
}

func (t *ImportTask) importSavedFilter(ctx context.Context, savedFilterJSON *jsonschema.SavedFilter) error {
importer := &savedfilter.Importer{
ReaderWriter: t.repository.SavedFilter,
Input: *savedFilterJSON,
MissingRefBehaviour: t.MissingRefBehaviour,
}

if err := performImport(ctx, importer, t.DuplicateBehaviour); err != nil {
return err
}

return nil
}
3 changes: 2 additions & 1 deletion pkg/file/scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -1100,7 +1100,8 @@ func (s *scanJob) removeOutdatedFingerprints(existing models.File, fp models.Fin

// oshash has changed, MD5 is missing - remove MD5 from the existing fingerprints
logger.Infof("Removing outdated checksum from %s", existing.Base().Path)
existing.Base().Fingerprints.Remove(models.FingerprintTypeMD5)
b := existing.Base()
b.Fingerprints = b.Fingerprints.Remove(models.FingerprintTypeMD5)
}

// returns a file only if it was updated
Expand Down
21 changes: 18 additions & 3 deletions pkg/models/fingerprint.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,31 @@ func (f *Fingerprint) Value() string {

type Fingerprints []Fingerprint

func (f *Fingerprints) Remove(type_ string) {
func (f Fingerprints) Remove(type_ string) Fingerprints {
var ret Fingerprints

for _, ff := range *f {
for _, ff := range f {
if ff.Type != type_ {
ret = append(ret, ff)
}
}

*f = ret
return ret
}

func (f Fingerprints) Filter(types ...string) Fingerprints {
var ret Fingerprints

for _, ff := range f {
for _, t := range types {
if ff.Type == t {
ret = append(ret, ff)
break
}
}
}

return ret
}

// Equals returns true if the contents of this slice are equal to those in the other slice.
Expand Down
31 changes: 31 additions & 0 deletions pkg/models/jsonschema/load.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package jsonschema

import (
"fmt"
"os"

jsoniter "github.com/json-iterator/go"
)

func loadFile[T any](filePath string) (*T, error) {
var ret T
file, err := os.Open(filePath)
if err != nil {
return nil, err
}
defer file.Close()
var json = jsoniter.ConfigCompatibleWithStandardLibrary
jsonParser := json.NewDecoder(file)
err = jsonParser.Decode(&ret)
if err != nil {
return nil, err
}
return &ret, nil
}

func saveFile[T any](filePath string, obj *T) error {
if obj == nil {
return fmt.Errorf("object must not be nil")
}
return marshalToFile(filePath, obj)
}
27 changes: 27 additions & 0 deletions pkg/models/jsonschema/saved_filter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package jsonschema

import (
"github.com/stashapp/stash/pkg/fsutil"
"github.com/stashapp/stash/pkg/models"
)

type SavedFilter struct {
Mode models.FilterMode `db:"mode" json:"mode"`
Name string `db:"name" json:"name"`
FindFilter *models.FindFilterType `json:"find_filter"`
ObjectFilter map[string]interface{} `json:"object_filter"`
UIOptions map[string]interface{} `json:"ui_options"`
}

func (s SavedFilter) Filename() string {
ret := fsutil.SanitiseBasename(s.Name + "_" + s.Mode.String())
return ret + ".json"
}

func LoadSavedFilterFile(filePath string) (*SavedFilter, error) {
return loadFile[SavedFilter](filePath)
}

func SaveSavedFilterFile(filePath string, image *SavedFilter) error {
return saveFile[SavedFilter](filePath, image)
}
Loading

0 comments on commit 9b3ed54

Please sign in to comment.