2023-09-01 00:39:29 +00:00
|
|
|
package file
|
2022-08-30 02:17:15 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"path/filepath"
|
|
|
|
"time"
|
|
|
|
|
2023-09-01 00:39:29 +00:00
|
|
|
"github.com/stashapp/stash/pkg/models"
|
2022-08-30 02:17:15 +00:00
|
|
|
"github.com/stashapp/stash/pkg/models/jsonschema"
|
|
|
|
)
|
|
|
|
|
2023-09-01 00:39:29 +00:00
|
|
|
var ErrZipFileNotExist = errors.New("zip file does not exist")
|
2022-08-30 02:17:15 +00:00
|
|
|
|
2023-09-01 00:39:29 +00:00
|
|
|
type Importer struct {
|
|
|
|
ReaderWriter models.FileFinderCreator
|
|
|
|
FolderStore models.FolderFinderCreator
|
2022-08-30 02:17:15 +00:00
|
|
|
Input jsonschema.DirEntry
|
|
|
|
|
2023-09-01 00:39:29 +00:00
|
|
|
file models.File
|
|
|
|
folder *models.Folder
|
2022-08-30 02:17:15 +00:00
|
|
|
}
|
|
|
|
|
2023-09-01 00:39:29 +00:00
|
|
|
func (i *Importer) PreImport(ctx context.Context) error {
|
2022-08-30 02:17:15 +00:00
|
|
|
var err error
|
|
|
|
|
|
|
|
switch ff := i.Input.(type) {
|
|
|
|
case *jsonschema.BaseDirEntry:
|
|
|
|
i.folder, err = i.folderJSONToFolder(ctx, ff)
|
|
|
|
default:
|
|
|
|
i.file, err = i.fileJSONToFile(ctx, i.Input)
|
|
|
|
}
|
|
|
|
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-09-01 00:39:29 +00:00
|
|
|
func (i *Importer) folderJSONToFolder(ctx context.Context, baseJSON *jsonschema.BaseDirEntry) (*models.Folder, error) {
|
|
|
|
ret := models.Folder{
|
|
|
|
DirEntry: models.DirEntry{
|
2022-08-30 02:17:15 +00:00
|
|
|
ModTime: baseJSON.ModTime.GetTime(),
|
|
|
|
},
|
2022-09-01 07:54:34 +00:00
|
|
|
Path: baseJSON.Path,
|
2022-08-30 02:17:15 +00:00
|
|
|
CreatedAt: baseJSON.CreatedAt.GetTime(),
|
|
|
|
UpdatedAt: baseJSON.CreatedAt.GetTime(),
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := i.populateZipFileID(ctx, &ret.DirEntry); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// set parent folder id during the creation process
|
|
|
|
|
|
|
|
return &ret, nil
|
|
|
|
}
|
|
|
|
|
2023-09-01 00:39:29 +00:00
|
|
|
func (i *Importer) fileJSONToFile(ctx context.Context, fileJSON jsonschema.DirEntry) (models.File, error) {
|
2022-08-30 02:17:15 +00:00
|
|
|
switch ff := fileJSON.(type) {
|
|
|
|
case *jsonschema.VideoFile:
|
|
|
|
baseFile, err := i.baseFileJSONToBaseFile(ctx, ff.BaseFile)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2023-09-01 00:39:29 +00:00
|
|
|
return &models.VideoFile{
|
2022-08-30 02:17:15 +00:00
|
|
|
BaseFile: baseFile,
|
|
|
|
Format: ff.Format,
|
|
|
|
Width: ff.Width,
|
|
|
|
Height: ff.Height,
|
|
|
|
Duration: ff.Duration,
|
|
|
|
VideoCodec: ff.VideoCodec,
|
|
|
|
AudioCodec: ff.AudioCodec,
|
|
|
|
FrameRate: ff.FrameRate,
|
|
|
|
BitRate: ff.BitRate,
|
|
|
|
Interactive: ff.Interactive,
|
|
|
|
InteractiveSpeed: ff.InteractiveSpeed,
|
|
|
|
}, nil
|
|
|
|
case *jsonschema.ImageFile:
|
|
|
|
baseFile, err := i.baseFileJSONToBaseFile(ctx, ff.BaseFile)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2023-09-01 00:39:29 +00:00
|
|
|
return &models.ImageFile{
|
2022-08-30 02:17:15 +00:00
|
|
|
BaseFile: baseFile,
|
|
|
|
Format: ff.Format,
|
|
|
|
Width: ff.Width,
|
|
|
|
Height: ff.Height,
|
|
|
|
}, nil
|
|
|
|
case *jsonschema.BaseFile:
|
|
|
|
return i.baseFileJSONToBaseFile(ctx, ff)
|
|
|
|
}
|
|
|
|
|
2024-08-28 04:45:57 +00:00
|
|
|
return nil, errors.New("unknown file type")
|
2022-08-30 02:17:15 +00:00
|
|
|
}
|
|
|
|
|
2023-09-01 00:39:29 +00:00
|
|
|
func (i *Importer) baseFileJSONToBaseFile(ctx context.Context, baseJSON *jsonschema.BaseFile) (*models.BaseFile, error) {
|
|
|
|
baseFile := models.BaseFile{
|
|
|
|
DirEntry: models.DirEntry{
|
2022-08-30 02:17:15 +00:00
|
|
|
ModTime: baseJSON.ModTime.GetTime(),
|
|
|
|
},
|
2022-09-01 07:54:34 +00:00
|
|
|
Basename: filepath.Base(baseJSON.Path),
|
2022-08-30 02:17:15 +00:00
|
|
|
Size: baseJSON.Size,
|
|
|
|
CreatedAt: baseJSON.CreatedAt.GetTime(),
|
|
|
|
UpdatedAt: baseJSON.CreatedAt.GetTime(),
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, fp := range baseJSON.Fingerprints {
|
2023-09-01 00:39:29 +00:00
|
|
|
baseFile.Fingerprints = append(baseFile.Fingerprints, models.Fingerprint{
|
2022-08-30 02:17:15 +00:00
|
|
|
Type: fp.Type,
|
|
|
|
Fingerprint: fp.Fingerprint,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := i.populateZipFileID(ctx, &baseFile.DirEntry); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &baseFile, nil
|
|
|
|
}
|
|
|
|
|
2023-09-01 00:39:29 +00:00
|
|
|
func (i *Importer) populateZipFileID(ctx context.Context, f *models.DirEntry) error {
|
2022-09-01 07:54:34 +00:00
|
|
|
zipFilePath := i.Input.DirEntry().ZipFile
|
2022-08-30 02:17:15 +00:00
|
|
|
if zipFilePath != "" {
|
|
|
|
zf, err := i.ReaderWriter.FindByPath(ctx, zipFilePath)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error finding file by path %q: %v", zipFilePath, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if zf == nil {
|
2023-09-01 00:39:29 +00:00
|
|
|
return ErrZipFileNotExist
|
2022-08-30 02:17:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
id := zf.Base().ID
|
|
|
|
f.ZipFileID = &id
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-09-01 00:39:29 +00:00
|
|
|
func (i *Importer) PostImport(ctx context.Context, id int) error {
|
2022-08-30 02:17:15 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-09-01 00:39:29 +00:00
|
|
|
func (i *Importer) Name() string {
|
2022-09-01 07:54:34 +00:00
|
|
|
return i.Input.DirEntry().Path
|
2022-08-30 02:17:15 +00:00
|
|
|
}
|
|
|
|
|
2023-09-01 00:39:29 +00:00
|
|
|
func (i *Importer) FindExistingID(ctx context.Context) (*int, error) {
|
2022-09-01 07:54:34 +00:00
|
|
|
path := i.Input.DirEntry().Path
|
2022-08-30 02:17:15 +00:00
|
|
|
existing, err := i.ReaderWriter.FindByPath(ctx, path)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if existing != nil {
|
|
|
|
id := int(existing.Base().ID)
|
|
|
|
return &id, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2023-09-01 00:39:29 +00:00
|
|
|
func (i *Importer) createFolderHierarchy(ctx context.Context, p string) (*models.Folder, error) {
|
2022-08-30 02:17:15 +00:00
|
|
|
parentPath := filepath.Dir(p)
|
|
|
|
|
2022-09-06 01:26:10 +00:00
|
|
|
if parentPath == p {
|
2022-08-30 02:17:15 +00:00
|
|
|
// get or create this folder
|
|
|
|
return i.getOrCreateFolder(ctx, p, nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
parent, err := i.createFolderHierarchy(ctx, parentPath)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return i.getOrCreateFolder(ctx, p, parent)
|
|
|
|
}
|
|
|
|
|
2023-09-01 00:39:29 +00:00
|
|
|
func (i *Importer) getOrCreateFolder(ctx context.Context, path string, parent *models.Folder) (*models.Folder, error) {
|
2022-08-30 02:17:15 +00:00
|
|
|
folder, err := i.FolderStore.FindByPath(ctx, path)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if folder != nil {
|
|
|
|
return folder, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
now := time.Now()
|
|
|
|
|
2023-09-01 00:39:29 +00:00
|
|
|
folder = &models.Folder{
|
2022-08-30 02:17:15 +00:00
|
|
|
Path: path,
|
|
|
|
CreatedAt: now,
|
|
|
|
UpdatedAt: now,
|
|
|
|
}
|
|
|
|
|
|
|
|
if parent != nil {
|
|
|
|
folder.ZipFileID = parent.ZipFileID
|
|
|
|
folder.ParentFolderID = &parent.ID
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := i.FolderStore.Create(ctx, folder); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return folder, nil
|
|
|
|
}
|
|
|
|
|
2023-09-01 00:39:29 +00:00
|
|
|
func (i *Importer) Create(ctx context.Context) (*int, error) {
|
2022-08-30 02:17:15 +00:00
|
|
|
// create folder hierarchy and set parent folder id
|
2022-09-01 07:54:34 +00:00
|
|
|
path := i.Input.DirEntry().Path
|
2022-08-30 02:17:15 +00:00
|
|
|
path = filepath.Dir(path)
|
|
|
|
folder, err := i.createFolderHierarchy(ctx, path)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("creating folder hierarchy for %q: %w", path, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if i.folder != nil {
|
|
|
|
return i.createFolder(ctx, folder)
|
|
|
|
}
|
|
|
|
|
|
|
|
return i.createFile(ctx, folder)
|
|
|
|
}
|
|
|
|
|
2023-09-01 00:39:29 +00:00
|
|
|
func (i *Importer) createFile(ctx context.Context, parentFolder *models.Folder) (*int, error) {
|
2022-08-30 02:17:15 +00:00
|
|
|
if parentFolder != nil {
|
|
|
|
i.file.Base().ParentFolderID = parentFolder.ID
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := i.ReaderWriter.Create(ctx, i.file); err != nil {
|
|
|
|
return nil, fmt.Errorf("error creating file: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
id := int(i.file.Base().ID)
|
|
|
|
return &id, nil
|
|
|
|
}
|
|
|
|
|
2023-09-01 00:39:29 +00:00
|
|
|
func (i *Importer) createFolder(ctx context.Context, parentFolder *models.Folder) (*int, error) {
|
2022-08-30 02:17:15 +00:00
|
|
|
if parentFolder != nil {
|
|
|
|
i.folder.ParentFolderID = &parentFolder.ID
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := i.FolderStore.Create(ctx, i.folder); err != nil {
|
|
|
|
return nil, fmt.Errorf("error creating folder: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
id := int(i.folder.ID)
|
|
|
|
return &id, nil
|
|
|
|
}
|
|
|
|
|
2023-09-01 00:39:29 +00:00
|
|
|
func (i *Importer) Update(ctx context.Context, id int) error {
|
2022-08-30 02:17:15 +00:00
|
|
|
// update not supported
|
|
|
|
return nil
|
|
|
|
}
|