diff --git a/pkg/manager/jsonschema/performer.go b/pkg/manager/jsonschema/performer.go index bd21654f6..44aec069a 100644 --- a/pkg/manager/jsonschema/performer.go +++ b/pkg/manager/jsonschema/performer.go @@ -3,27 +3,30 @@ package jsonschema import ( "encoding/json" "fmt" + "github.com/stashapp/stash/pkg/models" "os" ) type Performer struct { - Name string `json:"name,omitempty"` - URL string `json:"url,omitempty"` - Twitter string `json:"twitter,omitempty"` - Instagram string `json:"instagram,omitempty"` - Birthdate string `json:"birthdate,omitempty"` - Ethnicity string `json:"ethnicity,omitempty"` - Country string `json:"country,omitempty"` - EyeColor string `json:"eye_color,omitempty"` - Height string `json:"height,omitempty"` - Measurements string `json:"measurements,omitempty"` - FakeTits string `json:"fake_tits,omitempty"` - CareerLength string `json:"career_length,omitempty"` - Tattoos string `json:"tattoos,omitempty"` - Piercings string `json:"piercings,omitempty"` - Aliases string `json:"aliases,omitempty"` - Favorite bool `json:"favorite,omitempty"` - Image string `json:"image,omitempty"` + Name string `json:"name,omitempty"` + URL string `json:"url,omitempty"` + Twitter string `json:"twitter,omitempty"` + Instagram string `json:"instagram,omitempty"` + Birthdate string `json:"birthdate,omitempty"` + Ethnicity string `json:"ethnicity,omitempty"` + Country string `json:"country,omitempty"` + EyeColor string `json:"eye_color,omitempty"` + Height string `json:"height,omitempty"` + Measurements string `json:"measurements,omitempty"` + FakeTits string `json:"fake_tits,omitempty"` + CareerLength string `json:"career_length,omitempty"` + Tattoos string `json:"tattoos,omitempty"` + Piercings string `json:"piercings,omitempty"` + Aliases string `json:"aliases,omitempty"` + Favorite bool `json:"favorite,omitempty"` + Image string `json:"image,omitempty"` + CreatedAt models.JSONTime `json:"created_at,omitempty"` + UpdatedAt models.JSONTime `json:"updated_at,omitempty"` } func LoadPerformerFile(filePath string) (*Performer, error) { diff --git a/pkg/manager/jsonschema/scene.go b/pkg/manager/jsonschema/scene.go index 64de6ea49..c13ad2693 100644 --- a/pkg/manager/jsonschema/scene.go +++ b/pkg/manager/jsonschema/scene.go @@ -3,14 +3,17 @@ package jsonschema import ( "encoding/json" "fmt" + "github.com/stashapp/stash/pkg/models" "os" ) type SceneMarker struct { - Title string `json:"title,omitempty"` - Seconds string `json:"seconds,omitempty"` - PrimaryTag string `json:"primary_tag,omitempty"` - Tags []string `json:"tags,omitempty"` + Title string `json:"title,omitempty"` + Seconds string `json:"seconds,omitempty"` + PrimaryTag string `json:"primary_tag,omitempty"` + Tags []string `json:"tags,omitempty"` + CreatedAt models.JSONTime `json:"created_at,omitempty"` + UpdatedAt models.JSONTime `json:"updated_at,omitempty"` } type SceneFile struct { @@ -25,17 +28,19 @@ type SceneFile struct { } type Scene struct { - Title string `json:"title,omitempty"` - Studio string `json:"studio,omitempty"` - URL string `json:"url,omitempty"` - Date string `json:"date,omitempty"` - Rating int `json:"rating,omitempty"` - Details string `json:"details,omitempty"` - Gallery string `json:"gallery,omitempty"` - Performers []string `json:"performers,omitempty"` - Tags []string `json:"tags,omitempty"` - Markers []SceneMarker `json:"markers,omitempty"` - File *SceneFile `json:"file,omitempty"` + Title string `json:"title,omitempty"` + Studio string `json:"studio,omitempty"` + URL string `json:"url,omitempty"` + Date string `json:"date,omitempty"` + Rating int `json:"rating,omitempty"` + Details string `json:"details,omitempty"` + Gallery string `json:"gallery,omitempty"` + Performers []string `json:"performers,omitempty"` + Tags []string `json:"tags,omitempty"` + Markers []SceneMarker `json:"markers,omitempty"` + File *SceneFile `json:"file,omitempty"` + CreatedAt models.JSONTime `json:"created_at,omitempty"` + UpdatedAt models.JSONTime `json:"updated_at,omitempty"` } func LoadSceneFile(filePath string) (*Scene, error) { diff --git a/pkg/manager/jsonschema/studio.go b/pkg/manager/jsonschema/studio.go index 8b89f92fe..75e50d302 100644 --- a/pkg/manager/jsonschema/studio.go +++ b/pkg/manager/jsonschema/studio.go @@ -3,13 +3,16 @@ package jsonschema import ( "encoding/json" "fmt" + "github.com/stashapp/stash/pkg/models" "os" ) type Studio struct { - Name string `json:"name,omitempty"` - URL string `json:"url,omitempty"` - Image string `json:"image,omitempty"` + Name string `json:"name,omitempty"` + URL string `json:"url,omitempty"` + Image string `json:"image,omitempty"` + CreatedAt models.JSONTime `json:"created_at,omitempty"` + UpdatedAt models.JSONTime `json:"updated_at,omitempty"` } func LoadStudioFile(filePath string) (*Studio, error) { diff --git a/pkg/manager/task_export.go b/pkg/manager/task_export.go index 38f9542aa..b7485b1b9 100644 --- a/pkg/manager/task_export.go +++ b/pkg/manager/task_export.go @@ -60,7 +60,10 @@ func (t *ExportTask) ExportScenes(ctx context.Context) { logger.Progressf("[scenes] %d of %d", index, len(scenes)) t.Mappings.Scenes = append(t.Mappings.Scenes, jsonschema.PathMapping{Path: scene.Path, Checksum: scene.Checksum}) - newSceneJSON := jsonschema.Scene{} + newSceneJSON := jsonschema.Scene{ + CreatedAt: models.JSONTime{Time: scene.CreatedAt.Timestamp}, + UpdatedAt: models.JSONTime{Time: scene.UpdatedAt.Timestamp}, + } var studioName string if scene.StudioID.Valid { @@ -129,6 +132,8 @@ func (t *ExportTask) ExportScenes(ctx context.Context) { Seconds: t.getDecimalString(sceneMarker.Seconds), PrimaryTag: primaryTag.Name, Tags: t.getTagNames(sceneMarkerTags), + CreatedAt: models.JSONTime{Time: sceneMarker.CreatedAt.Timestamp}, + UpdatedAt: models.JSONTime{Time: sceneMarker.UpdatedAt.Timestamp}, } newSceneJSON.Markers = append(newSceneJSON.Markers, sceneMarkerJSON) @@ -208,7 +213,10 @@ func (t *ExportTask) ExportPerformers(ctx context.Context) { t.Mappings.Performers = append(t.Mappings.Performers, jsonschema.NameMapping{Name: performer.Name.String, Checksum: performer.Checksum}) - newPerformerJSON := jsonschema.Performer{} + newPerformerJSON := jsonschema.Performer{ + CreatedAt: models.JSONTime{Time: performer.CreatedAt.Timestamp}, + UpdatedAt: models.JSONTime{Time: performer.UpdatedAt.Timestamp}, + } if performer.Name.Valid { newPerformerJSON.Name = performer.Name.String @@ -291,7 +299,10 @@ func (t *ExportTask) ExportStudios(ctx context.Context) { t.Mappings.Studios = append(t.Mappings.Studios, jsonschema.NameMapping{Name: studio.Name.String, Checksum: studio.Checksum}) - newStudioJSON := jsonschema.Studio{} + newStudioJSON := jsonschema.Studio{ + CreatedAt: models.JSONTime{Time: studio.CreatedAt.Timestamp}, + UpdatedAt: models.JSONTime{Time: studio.UpdatedAt.Timestamp}, + } if studio.Name.Valid { newStudioJSON.Name = studio.Name.String diff --git a/pkg/manager/task_import.go b/pkg/manager/task_import.go index aa640c5ea..e2e589612 100644 --- a/pkg/manager/task_import.go +++ b/pkg/manager/task_import.go @@ -73,13 +73,12 @@ func (t *ImportTask) ImportPerformers(ctx context.Context) { } // Populate a new performer from the input - currentTime := time.Now() newPerformer := models.Performer{ Image: imageData, Checksum: checksum, Favorite: sql.NullBool{Bool: performerJSON.Favorite, Valid: true}, - CreatedAt: models.SQLiteTimestamp{Timestamp: currentTime}, - UpdatedAt: models.SQLiteTimestamp{Timestamp: currentTime}, + CreatedAt: models.SQLiteTimestamp{Timestamp: t.getTimeFromJSONTime(performerJSON.CreatedAt)}, + UpdatedAt: models.SQLiteTimestamp{Timestamp: t.getTimeFromJSONTime(performerJSON.UpdatedAt)}, } if performerJSON.Name != "" { @@ -169,14 +168,13 @@ func (t *ImportTask) ImportStudios(ctx context.Context) { } // Populate a new studio from the input - currentTime := time.Now() newStudio := models.Studio{ Image: imageData, Checksum: checksum, Name: sql.NullString{String: studioJSON.Name, Valid: true}, URL: sql.NullString{String: studioJSON.URL, Valid: true}, - CreatedAt: models.SQLiteTimestamp{Timestamp: currentTime}, - UpdatedAt: models.SQLiteTimestamp{Timestamp: currentTime}, + CreatedAt: models.SQLiteTimestamp{Timestamp: t.getTimeFromJSONTime(studioJSON.CreatedAt)}, + UpdatedAt: models.SQLiteTimestamp{Timestamp: t.getTimeFromJSONTime(studioJSON.UpdatedAt)}, } _, err = qb.Create(newStudio, tx) @@ -308,12 +306,6 @@ func (t *ImportTask) ImportScrapedItems(ctx context.Context) { index := i + 1 logger.Progressf("[scraped sites] %d of %d", index, len(t.Mappings.Scenes)) - var updatedAt time.Time - if currentTime.Location() != nil { - updatedAt = mappingJSON.UpdatedAt.Time.In(currentTime.Location()) - } else { - updatedAt = mappingJSON.UpdatedAt.Time - } newScrapedItem := models.ScrapedItem{ Title: sql.NullString{String: mappingJSON.Title, Valid: true}, Description: sql.NullString{String: mappingJSON.Description, Valid: true}, @@ -328,7 +320,7 @@ func (t *ImportTask) ImportScrapedItems(ctx context.Context) { VideoFilename: sql.NullString{String: mappingJSON.VideoFilename, Valid: true}, VideoURL: sql.NullString{String: mappingJSON.VideoURL, Valid: true}, CreatedAt: models.SQLiteTimestamp{Timestamp: currentTime}, - UpdatedAt: models.SQLiteTimestamp{Timestamp: updatedAt}, + UpdatedAt: models.SQLiteTimestamp{Timestamp: t.getTimeFromJSONTime(mappingJSON.UpdatedAt)}, } studio, err := sqb.FindByName(mappingJSON.Studio, tx) @@ -356,7 +348,6 @@ func (t *ImportTask) ImportScenes(ctx context.Context) { tx := database.DB.MustBeginTx(ctx, nil) qb := models.NewSceneQueryBuilder() jqb := models.NewJoinsQueryBuilder() - currentTime := time.Now() for i, mappingJSON := range t.Mappings.Scenes { index := i + 1 @@ -369,10 +360,8 @@ func (t *ImportTask) ImportScenes(ctx context.Context) { logger.Progressf("[scenes] %d of %d", index, len(t.Mappings.Scenes)) newScene := models.Scene{ - Checksum: mappingJSON.Checksum, - Path: mappingJSON.Path, - CreatedAt: models.SQLiteTimestamp{Timestamp: currentTime}, - UpdatedAt: models.SQLiteTimestamp{Timestamp: currentTime}, + Checksum: mappingJSON.Checksum, + Path: mappingJSON.Path, } sceneJSON, err := instance.JSON.getScene(mappingJSON.Checksum) @@ -398,6 +387,8 @@ func (t *ImportTask) ImportScenes(ctx context.Context) { if sceneJSON.Rating != 0 { newScene.Rating = sql.NullInt64{Int64: int64(sceneJSON.Rating), Valid: true} } + newScene.CreatedAt = models.SQLiteTimestamp{Timestamp: t.getTimeFromJSONTime(sceneJSON.CreatedAt)} + newScene.UpdatedAt = models.SQLiteTimestamp{Timestamp: t.getTimeFromJSONTime(sceneJSON.UpdatedAt)} if sceneJSON.File != nil { if sceneJSON.File.Size != "" { @@ -520,8 +511,8 @@ func (t *ImportTask) ImportScenes(ctx context.Context) { Title: marker.Title, Seconds: seconds, SceneID: sql.NullInt64{Int64: int64(scene.ID), Valid: true}, - CreatedAt: models.SQLiteTimestamp{Timestamp: currentTime}, - UpdatedAt: models.SQLiteTimestamp{Timestamp: currentTime}, + CreatedAt: models.SQLiteTimestamp{Timestamp: t.getTimeFromJSONTime(marker.CreatedAt)}, + UpdatedAt: models.SQLiteTimestamp{Timestamp: t.getTimeFromJSONTime(marker.UpdatedAt)}, } primaryTag, err := tqb.FindByName(marker.PrimaryTag, tx) @@ -636,3 +627,21 @@ func (t *ImportTask) getUnique(s []string) []string { } return s[:j] } + +var currentLocation = time.Now().Location() + +func (t *ImportTask) getTimeFromJSONTime(jsonTime models.JSONTime) time.Time { + if currentLocation != nil { + if jsonTime.IsZero() { + return time.Now().In(currentLocation) + } else { + return jsonTime.Time.In(currentLocation) + } + } else { + if jsonTime.IsZero() { + return time.Now() + } else { + return jsonTime.Time + } + } +} diff --git a/pkg/utils/boolean.go b/pkg/utils/boolean.go index 51a6cfd72..f0abd02c3 100644 --- a/pkg/utils/boolean.go +++ b/pkg/utils/boolean.go @@ -1,5 +1,6 @@ package utils +// Btoi transforms a boolean to an int. 1 for true, false otherwise func Btoi(b bool) int { if b { return 1 diff --git a/pkg/utils/file.go b/pkg/utils/file.go index 29dde14f5..e029c3073 100644 --- a/pkg/utils/file.go +++ b/pkg/utils/file.go @@ -10,6 +10,7 @@ import ( "path/filepath" ) +// FileType uses the filetype package to determine the given file path's type func FileType(filePath string) (types.Type, error) { file, _ := os.Open(filePath) @@ -20,6 +21,7 @@ func FileType(filePath string) (types.Type, error) { return filetype.Match(head) } +// FileExists returns true if the given path exists func FileExists(path string) (bool, error) { _, err := os.Stat(path) if err == nil { @@ -31,6 +33,7 @@ func FileExists(path string) (bool, error) { } } +// DirExists returns true if the given path exists and is a directory func DirExists(path string) (bool, error) { exists, _ := FileExists(path) fileInfo, _ := os.Stat(path) @@ -40,6 +43,7 @@ func DirExists(path string) (bool, error) { return true, nil } +// Touch creates an empty file at the given path if it doesn't already exist func Touch(path string) error { var _, err = os.Stat(path) if os.IsNotExist(err) { @@ -52,6 +56,7 @@ func Touch(path string) error { return nil } +// EnsureDir will create a directory at the given path if it doesn't already exist func EnsureDir(path string) error { exists, err := FileExists(path) if !exists { @@ -61,10 +66,12 @@ func EnsureDir(path string) error { return err } +// RemoveDir removes the given file path along with all of its contents func RemoveDir(path string) error { return os.RemoveAll(path) } +// EmptyDir will recursively remove the contents of a directory at the given path func EmptyDir(path string) error { d, err := os.Open(path) if err != nil { @@ -87,6 +94,7 @@ func EmptyDir(path string) error { return nil } +// ListDir will return the contents of a given directory path as a string slice func ListDir(path string) []string { if path == "" { path = GetHomeDirectory() @@ -117,6 +125,7 @@ func ListDir(path string) []string { return dirPaths } +// GetHomeDirectory returns the path of the user's home directory. ~ on Unix and C:\Users\UserName on Windows func GetHomeDirectory() string { currentUser, err := user.Current() if err != nil { diff --git a/pkg/utils/image.go b/pkg/utils/image.go index 5e9e2d1a8..8a07db441 100644 --- a/pkg/utils/image.go +++ b/pkg/utils/image.go @@ -6,6 +6,8 @@ import ( "regexp" ) +// ProcessBase64Image transforms a base64 encoded string from a form post and returns the MD5 hash of the data and the +// image itself as a byte slice. func ProcessBase64Image(imageString string) (string, []byte, error) { if imageString == "" { return "", nil, fmt.Errorf("empty image string") @@ -27,10 +29,12 @@ func ProcessBase64Image(imageString string) (string, []byte, error) { return MD5FromBytes(imageData), imageData, nil } +// GetDataFromBase64String returns the given base64 encoded string as a byte slice func GetDataFromBase64String(encodedString string) ([]byte, error) { return base64.StdEncoding.DecodeString(encodedString) } +// GetBase64StringFromData returns the given byte slice as a base64 encoded string func GetBase64StringFromData(data []byte) string { return base64.StdEncoding.EncodeToString(data)