mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 04:14:39 +03:00
Model refactor (#3915)
* Add mockery config file * Move basic file/folder structs to models * Fix hack due to import loop * Move file interfaces to models * Move folder interfaces to models * Move scene interfaces to models * Move scene marker interfaces to models * Move image interfaces to models * Move gallery interfaces to models * Move gallery chapter interfaces to models * Move studio interfaces to models * Move movie interfaces to models * Move performer interfaces to models * Move tag interfaces to models * Move autotag interfaces to models * Regenerate mocks
This commit is contained in:
@@ -16,7 +16,6 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/stashapp/stash/pkg/file"
|
||||
"github.com/stashapp/stash/pkg/fsutil"
|
||||
"github.com/stashapp/stash/pkg/logger"
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
@@ -51,7 +50,7 @@ const (
|
||||
type StreamType struct {
|
||||
Name string
|
||||
SegmentType *SegmentType
|
||||
ServeManifest func(sm *StreamManager, w http.ResponseWriter, r *http.Request, vf *file.VideoFile, resolution string)
|
||||
ServeManifest func(sm *StreamManager, w http.ResponseWriter, r *http.Request, vf *models.VideoFile, resolution string)
|
||||
Args func(codec VideoCodec, segment int, videoFilter VideoFilter, videoOnly bool, outputDir string) Args
|
||||
}
|
||||
|
||||
@@ -250,7 +249,7 @@ var ErrInvalidSegment = errors.New("invalid segment")
|
||||
|
||||
type StreamOptions struct {
|
||||
StreamType *StreamType
|
||||
VideoFile *file.VideoFile
|
||||
VideoFile *models.VideoFile
|
||||
Resolution string
|
||||
Hash string
|
||||
Segment string
|
||||
@@ -279,7 +278,7 @@ type waitingSegment struct {
|
||||
type runningStream struct {
|
||||
dir string
|
||||
streamType *StreamType
|
||||
vf *file.VideoFile
|
||||
vf *models.VideoFile
|
||||
maxTranscodeSize int
|
||||
outputDir string
|
||||
|
||||
@@ -394,7 +393,7 @@ func (tp *transcodeProcess) checkSegments() {
|
||||
}
|
||||
}
|
||||
|
||||
func lastSegment(vf *file.VideoFile) int {
|
||||
func lastSegment(vf *models.VideoFile) int {
|
||||
return int(math.Ceil(vf.Duration/segmentLength)) - 1
|
||||
}
|
||||
|
||||
@@ -405,7 +404,7 @@ func segmentExists(path string) bool {
|
||||
|
||||
// serveHLSManifest serves a generated HLS playlist. The URLs for the segments
|
||||
// are of the form {r.URL}/%d.ts{?urlQuery} where %d is the segment index.
|
||||
func serveHLSManifest(sm *StreamManager, w http.ResponseWriter, r *http.Request, vf *file.VideoFile, resolution string) {
|
||||
func serveHLSManifest(sm *StreamManager, w http.ResponseWriter, r *http.Request, vf *models.VideoFile, resolution string) {
|
||||
if sm.cacheDir == "" {
|
||||
logger.Error("[transcode] cannot live transcode with HLS because cache dir is unset")
|
||||
http.Error(w, "cannot live transcode with HLS because cache dir is unset", http.StatusServiceUnavailable)
|
||||
@@ -460,7 +459,7 @@ func serveHLSManifest(sm *StreamManager, w http.ResponseWriter, r *http.Request,
|
||||
}
|
||||
|
||||
// serveDASHManifest serves a generated DASH manifest.
|
||||
func serveDASHManifest(sm *StreamManager, w http.ResponseWriter, r *http.Request, vf *file.VideoFile, resolution string) {
|
||||
func serveDASHManifest(sm *StreamManager, w http.ResponseWriter, r *http.Request, vf *models.VideoFile, resolution string) {
|
||||
if sm.cacheDir == "" {
|
||||
logger.Error("[transcode] cannot live transcode with DASH because cache dir is unset")
|
||||
http.Error(w, "cannot live transcode files with DASH because cache dir is unset", http.StatusServiceUnavailable)
|
||||
@@ -550,7 +549,7 @@ func serveDASHManifest(sm *StreamManager, w http.ResponseWriter, r *http.Request
|
||||
utils.ServeStaticContent(w, r, buf.Bytes())
|
||||
}
|
||||
|
||||
func (sm *StreamManager) ServeManifest(w http.ResponseWriter, r *http.Request, streamType *StreamType, vf *file.VideoFile, resolution string) {
|
||||
func (sm *StreamManager) ServeManifest(w http.ResponseWriter, r *http.Request, streamType *StreamType, vf *models.VideoFile, resolution string) {
|
||||
streamType.ServeManifest(sm, w, r, vf, resolution)
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/stashapp/stash/pkg/file"
|
||||
"github.com/stashapp/stash/pkg/fsutil"
|
||||
"github.com/stashapp/stash/pkg/logger"
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
@@ -134,7 +133,7 @@ var (
|
||||
|
||||
type TranscodeOptions struct {
|
||||
StreamType StreamFormat
|
||||
VideoFile *file.VideoFile
|
||||
VideoFile *models.VideoFile
|
||||
Resolution string
|
||||
StartTime float64
|
||||
}
|
||||
|
||||
@@ -10,12 +10,13 @@ import (
|
||||
|
||||
"github.com/stashapp/stash/pkg/job"
|
||||
"github.com/stashapp/stash/pkg/logger"
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
"github.com/stashapp/stash/pkg/txn"
|
||||
)
|
||||
|
||||
// Cleaner scans through stored file and folder instances and removes those that are no longer present on disk.
|
||||
type Cleaner struct {
|
||||
FS FS
|
||||
FS models.FS
|
||||
Repository Repository
|
||||
|
||||
Handlers []CleanHandler
|
||||
@@ -55,44 +56,44 @@ func (s *Cleaner) Clean(ctx context.Context, options CleanOptions, progress *job
|
||||
}
|
||||
|
||||
type fileOrFolder struct {
|
||||
fileID ID
|
||||
folderID FolderID
|
||||
fileID models.FileID
|
||||
folderID models.FolderID
|
||||
}
|
||||
|
||||
type deleteSet struct {
|
||||
orderedList []fileOrFolder
|
||||
fileIDSet map[ID]string
|
||||
fileIDSet map[models.FileID]string
|
||||
|
||||
folderIDSet map[FolderID]string
|
||||
folderIDSet map[models.FolderID]string
|
||||
}
|
||||
|
||||
func newDeleteSet() deleteSet {
|
||||
return deleteSet{
|
||||
fileIDSet: make(map[ID]string),
|
||||
folderIDSet: make(map[FolderID]string),
|
||||
fileIDSet: make(map[models.FileID]string),
|
||||
folderIDSet: make(map[models.FolderID]string),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *deleteSet) add(id ID, path string) {
|
||||
func (s *deleteSet) add(id models.FileID, path string) {
|
||||
if _, ok := s.fileIDSet[id]; !ok {
|
||||
s.orderedList = append(s.orderedList, fileOrFolder{fileID: id})
|
||||
s.fileIDSet[id] = path
|
||||
}
|
||||
}
|
||||
|
||||
func (s *deleteSet) has(id ID) bool {
|
||||
func (s *deleteSet) has(id models.FileID) bool {
|
||||
_, ok := s.fileIDSet[id]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (s *deleteSet) addFolder(id FolderID, path string) {
|
||||
func (s *deleteSet) addFolder(id models.FolderID, path string) {
|
||||
if _, ok := s.folderIDSet[id]; !ok {
|
||||
s.orderedList = append(s.orderedList, fileOrFolder{folderID: id})
|
||||
s.folderIDSet[id] = path
|
||||
}
|
||||
}
|
||||
|
||||
func (s *deleteSet) hasFolder(id FolderID) bool {
|
||||
func (s *deleteSet) hasFolder(id models.FolderID) bool {
|
||||
_, ok := s.folderIDSet[id]
|
||||
return ok
|
||||
}
|
||||
@@ -113,7 +114,7 @@ func (j *cleanJob) execute(ctx context.Context) error {
|
||||
|
||||
if err := txn.WithReadTxn(ctx, j.Repository, func(ctx context.Context) error {
|
||||
var err error
|
||||
fileCount, err = j.Repository.CountAllInPaths(ctx, j.options.Paths)
|
||||
fileCount, err = j.Repository.FileStore.CountAllInPaths(ctx, j.options.Paths)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -177,7 +178,7 @@ func (j *cleanJob) assessFiles(ctx context.Context, toDelete *deleteSet) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
files, err := j.Repository.FindAllInPaths(ctx, j.options.Paths, batchSize, offset)
|
||||
files, err := j.Repository.FileStore.FindAllInPaths(ctx, j.options.Paths, batchSize, offset)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error querying for files: %w", err)
|
||||
}
|
||||
@@ -221,9 +222,9 @@ func (j *cleanJob) assessFiles(ctx context.Context, toDelete *deleteSet) error {
|
||||
}
|
||||
|
||||
// flagFolderForDelete adds folders to the toDelete set, with the leaf folders added first
|
||||
func (j *cleanJob) flagFileForDelete(ctx context.Context, toDelete *deleteSet, f File) error {
|
||||
func (j *cleanJob) flagFileForDelete(ctx context.Context, toDelete *deleteSet, f models.File) error {
|
||||
// add contained files first
|
||||
containedFiles, err := j.Repository.FindByZipFileID(ctx, f.Base().ID)
|
||||
containedFiles, err := j.Repository.FileStore.FindByZipFileID(ctx, f.Base().ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error finding contained files for %q: %w", f.Base().Path, err)
|
||||
}
|
||||
@@ -306,7 +307,7 @@ func (j *cleanJob) assessFolders(ctx context.Context, toDelete *deleteSet) error
|
||||
return nil
|
||||
}
|
||||
|
||||
func (j *cleanJob) flagFolderForDelete(ctx context.Context, toDelete *deleteSet, folder *Folder) error {
|
||||
func (j *cleanJob) flagFolderForDelete(ctx context.Context, toDelete *deleteSet, folder *models.Folder) error {
|
||||
// it is possible that child folders may be included while parent folders are not
|
||||
// so we need to check child folders separately
|
||||
toDelete.addFolder(folder.ID, folder.Path)
|
||||
@@ -314,7 +315,7 @@ func (j *cleanJob) flagFolderForDelete(ctx context.Context, toDelete *deleteSet,
|
||||
return nil
|
||||
}
|
||||
|
||||
func (j *cleanJob) shouldClean(ctx context.Context, f File) bool {
|
||||
func (j *cleanJob) shouldClean(ctx context.Context, f models.File) bool {
|
||||
path := f.Base().Path
|
||||
|
||||
info, err := f.Base().Info(j.FS)
|
||||
@@ -336,7 +337,7 @@ func (j *cleanJob) shouldClean(ctx context.Context, f File) bool {
|
||||
return !filter.Accept(ctx, path, info)
|
||||
}
|
||||
|
||||
func (j *cleanJob) shouldCleanFolder(ctx context.Context, f *Folder) bool {
|
||||
func (j *cleanJob) shouldCleanFolder(ctx context.Context, f *models.Folder) bool {
|
||||
path := f.Path
|
||||
|
||||
info, err := f.Info(j.FS)
|
||||
@@ -376,7 +377,7 @@ func (j *cleanJob) shouldCleanFolder(ctx context.Context, f *Folder) bool {
|
||||
return !filter.Accept(ctx, path, info)
|
||||
}
|
||||
|
||||
func (j *cleanJob) deleteFile(ctx context.Context, fileID ID, fn string) {
|
||||
func (j *cleanJob) deleteFile(ctx context.Context, fileID models.FileID, fn string) {
|
||||
// delete associated objects
|
||||
fileDeleter := NewDeleter()
|
||||
if err := txn.WithTxn(ctx, j.Repository, func(ctx context.Context) error {
|
||||
@@ -386,14 +387,14 @@ func (j *cleanJob) deleteFile(ctx context.Context, fileID ID, fn string) {
|
||||
return err
|
||||
}
|
||||
|
||||
return j.Repository.Destroy(ctx, fileID)
|
||||
return j.Repository.FileStore.Destroy(ctx, fileID)
|
||||
}); err != nil {
|
||||
logger.Errorf("Error deleting file %q from database: %s", fn, err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (j *cleanJob) deleteFolder(ctx context.Context, folderID FolderID, fn string) {
|
||||
func (j *cleanJob) deleteFolder(ctx context.Context, folderID models.FolderID, fn string) {
|
||||
// delete associated objects
|
||||
fileDeleter := NewDeleter()
|
||||
if err := txn.WithTxn(ctx, j.Repository, func(ctx context.Context) error {
|
||||
@@ -410,7 +411,7 @@ func (j *cleanJob) deleteFolder(ctx context.Context, folderID FolderID, fn strin
|
||||
}
|
||||
}
|
||||
|
||||
func (j *cleanJob) fireHandlers(ctx context.Context, fileDeleter *Deleter, fileID ID) error {
|
||||
func (j *cleanJob) fireHandlers(ctx context.Context, fileDeleter *Deleter, fileID models.FileID) error {
|
||||
for _, h := range j.Handlers {
|
||||
if err := h.HandleFile(ctx, fileDeleter, fileID); err != nil {
|
||||
return err
|
||||
@@ -420,7 +421,7 @@ func (j *cleanJob) fireHandlers(ctx context.Context, fileDeleter *Deleter, fileI
|
||||
return nil
|
||||
}
|
||||
|
||||
func (j *cleanJob) fireFolderHandlers(ctx context.Context, fileDeleter *Deleter, folderID FolderID) error {
|
||||
func (j *cleanJob) fireFolderHandlers(ctx context.Context, fileDeleter *Deleter, folderID models.FolderID) error {
|
||||
for _, h := range j.Handlers {
|
||||
if err := h.HandleFolder(ctx, fileDeleter, folderID); err != nil {
|
||||
return err
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
|
||||
"github.com/stashapp/stash/pkg/fsutil"
|
||||
"github.com/stashapp/stash/pkg/logger"
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
"github.com/stashapp/stash/pkg/txn"
|
||||
)
|
||||
|
||||
@@ -179,7 +180,7 @@ func (d *Deleter) renameForRestore(path string) error {
|
||||
return d.RenamerRemover.Rename(path+deleteFileSuffix, path)
|
||||
}
|
||||
|
||||
func Destroy(ctx context.Context, destroyer Destroyer, f File, fileDeleter *Deleter, deleteFile bool) error {
|
||||
func Destroy(ctx context.Context, destroyer models.FileDestroyer, f models.File, fileDeleter *Deleter, deleteFile bool) error {
|
||||
if err := destroyer.Destroy(ctx, f.Base().ID); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -195,11 +196,11 @@ func Destroy(ctx context.Context, destroyer Destroyer, f File, fileDeleter *Dele
|
||||
}
|
||||
|
||||
type ZipDestroyer struct {
|
||||
FileDestroyer GetterDestroyer
|
||||
FolderDestroyer FolderGetterDestroyer
|
||||
FileDestroyer models.FileFinderDestroyer
|
||||
FolderDestroyer models.FolderFinderDestroyer
|
||||
}
|
||||
|
||||
func (d *ZipDestroyer) DestroyZip(ctx context.Context, f File, fileDeleter *Deleter, deleteFile bool) error {
|
||||
func (d *ZipDestroyer) DestroyZip(ctx context.Context, f models.File, fileDeleter *Deleter, deleteFile bool) error {
|
||||
// destroy contained files
|
||||
files, err := d.FileDestroyer.FindByZipFileID(ctx, f.Base().ID)
|
||||
if err != nil {
|
||||
|
||||
226
pkg/file/file.go
226
pkg/file/file.go
@@ -1,225 +1,15 @@
|
||||
package file
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
"github.com/stashapp/stash/pkg/txn"
|
||||
)
|
||||
|
||||
// ID represents an ID of a file.
|
||||
type ID int32
|
||||
// Repository provides access to storage methods for files and folders.
|
||||
type Repository struct {
|
||||
txn.Manager
|
||||
txn.DatabaseProvider
|
||||
|
||||
func (i ID) String() string {
|
||||
return strconv.Itoa(int(i))
|
||||
}
|
||||
|
||||
// DirEntry represents a file or directory in the file system.
|
||||
type DirEntry struct {
|
||||
ZipFileID *ID `json:"zip_file_id"`
|
||||
|
||||
// transient - not persisted
|
||||
// only guaranteed to have id, path and basename set
|
||||
ZipFile File
|
||||
|
||||
ModTime time.Time `json:"mod_time"`
|
||||
}
|
||||
|
||||
func (e *DirEntry) info(fs FS, path string) (fs.FileInfo, error) {
|
||||
if e.ZipFile != nil {
|
||||
zipPath := e.ZipFile.Base().Path
|
||||
zfs, err := fs.OpenZip(zipPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer zfs.Close()
|
||||
fs = zfs
|
||||
}
|
||||
// else assume os file
|
||||
|
||||
ret, err := fs.Lstat(path)
|
||||
return ret, err
|
||||
}
|
||||
|
||||
// File represents a file in the file system.
|
||||
type File interface {
|
||||
Base() *BaseFile
|
||||
SetFingerprints(fp Fingerprints)
|
||||
Open(fs FS) (io.ReadCloser, error)
|
||||
}
|
||||
|
||||
// BaseFile represents a file in the file system.
|
||||
type BaseFile struct {
|
||||
ID ID `json:"id"`
|
||||
|
||||
DirEntry
|
||||
|
||||
// resolved from parent folder and basename only - not stored in DB
|
||||
Path string `json:"path"`
|
||||
|
||||
Basename string `json:"basename"`
|
||||
ParentFolderID FolderID `json:"parent_folder_id"`
|
||||
|
||||
Fingerprints Fingerprints `json:"fingerprints"`
|
||||
|
||||
Size int64 `json:"size"`
|
||||
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
// SetFingerprints sets the fingerprints of the file.
|
||||
// If a fingerprint of the same type already exists, it is overwritten.
|
||||
func (f *BaseFile) SetFingerprints(fp Fingerprints) {
|
||||
for _, v := range fp {
|
||||
f.SetFingerprint(v)
|
||||
}
|
||||
}
|
||||
|
||||
// SetFingerprint sets the fingerprint of the file.
|
||||
// If a fingerprint of the same type already exists, it is overwritten.
|
||||
func (f *BaseFile) SetFingerprint(fp Fingerprint) {
|
||||
for i, existing := range f.Fingerprints {
|
||||
if existing.Type == fp.Type {
|
||||
f.Fingerprints[i] = fp
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
f.Fingerprints = append(f.Fingerprints, fp)
|
||||
}
|
||||
|
||||
// Base is used to fulfil the File interface.
|
||||
func (f *BaseFile) Base() *BaseFile {
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *BaseFile) Open(fs FS) (io.ReadCloser, error) {
|
||||
if f.ZipFile != nil {
|
||||
zipPath := f.ZipFile.Base().Path
|
||||
zfs, err := fs.OpenZip(zipPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return zfs.OpenOnly(f.Path)
|
||||
}
|
||||
|
||||
return fs.Open(f.Path)
|
||||
}
|
||||
|
||||
func (f *BaseFile) Info(fs FS) (fs.FileInfo, error) {
|
||||
return f.info(fs, f.Path)
|
||||
}
|
||||
|
||||
func (f *BaseFile) Serve(fs FS, w http.ResponseWriter, r *http.Request) error {
|
||||
reader, err := f.Open(fs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer reader.Close()
|
||||
|
||||
content, ok := reader.(io.ReadSeeker)
|
||||
if !ok {
|
||||
data, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
content = bytes.NewReader(data)
|
||||
}
|
||||
|
||||
if r.URL.Query().Has("t") {
|
||||
w.Header().Set("Cache-Control", "private, max-age=31536000, immutable")
|
||||
} else {
|
||||
w.Header().Set("Cache-Control", "no-cache")
|
||||
}
|
||||
http.ServeContent(w, r, f.Basename, f.ModTime, content)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type Finder interface {
|
||||
Find(ctx context.Context, id ...ID) ([]File, error)
|
||||
}
|
||||
|
||||
// Getter provides methods to find Files.
|
||||
type Getter interface {
|
||||
Finder
|
||||
FindByPath(ctx context.Context, path string) (File, error)
|
||||
FindAllByPath(ctx context.Context, path string) ([]File, error)
|
||||
FindByFingerprint(ctx context.Context, fp Fingerprint) ([]File, error)
|
||||
FindByZipFileID(ctx context.Context, zipFileID ID) ([]File, error)
|
||||
FindAllInPaths(ctx context.Context, p []string, limit, offset int) ([]File, error)
|
||||
FindByFileInfo(ctx context.Context, info fs.FileInfo, size int64) ([]File, error)
|
||||
}
|
||||
|
||||
type Counter interface {
|
||||
CountAllInPaths(ctx context.Context, p []string) (int, error)
|
||||
CountByFolderID(ctx context.Context, folderID FolderID) (int, error)
|
||||
}
|
||||
|
||||
// Creator provides methods to create Files.
|
||||
type Creator interface {
|
||||
Create(ctx context.Context, f File) error
|
||||
}
|
||||
|
||||
// Updater provides methods to update Files.
|
||||
type Updater interface {
|
||||
Update(ctx context.Context, f File) error
|
||||
}
|
||||
|
||||
type Destroyer interface {
|
||||
Destroy(ctx context.Context, id ID) error
|
||||
}
|
||||
|
||||
type GetterUpdater interface {
|
||||
Getter
|
||||
Updater
|
||||
}
|
||||
|
||||
type GetterDestroyer interface {
|
||||
Getter
|
||||
Destroyer
|
||||
}
|
||||
|
||||
// Store provides methods to find, create and update Files.
|
||||
type Store interface {
|
||||
Getter
|
||||
Counter
|
||||
Creator
|
||||
Updater
|
||||
Destroyer
|
||||
|
||||
IsPrimary(ctx context.Context, fileID ID) (bool, error)
|
||||
}
|
||||
|
||||
// Decorator wraps the Decorate method to add additional functionality while scanning files.
|
||||
type Decorator interface {
|
||||
Decorate(ctx context.Context, fs FS, f File) (File, error)
|
||||
IsMissingMetadata(ctx context.Context, fs FS, f File) bool
|
||||
}
|
||||
|
||||
type FilteredDecorator struct {
|
||||
Decorator
|
||||
Filter
|
||||
}
|
||||
|
||||
// Decorate runs the decorator if the filter accepts the file.
|
||||
func (d *FilteredDecorator) Decorate(ctx context.Context, fs FS, f File) (File, error) {
|
||||
if d.Accept(ctx, f) {
|
||||
return d.Decorator.Decorate(ctx, fs, f)
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func (d *FilteredDecorator) IsMissingMetadata(ctx context.Context, fs FS, f File) bool {
|
||||
if d.Accept(ctx, f) {
|
||||
return d.Decorator.IsMissingMetadata(ctx, fs, f)
|
||||
}
|
||||
|
||||
return false
|
||||
FileStore models.FileReaderWriter
|
||||
FolderStore models.FolderReaderWriter
|
||||
}
|
||||
|
||||
@@ -3,94 +3,16 @@ package file
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
)
|
||||
|
||||
// FolderID represents an ID of a folder.
|
||||
type FolderID int32
|
||||
|
||||
// String converts the ID to a string.
|
||||
func (i FolderID) String() string {
|
||||
return strconv.Itoa(int(i))
|
||||
}
|
||||
|
||||
// Folder represents a folder in the file system.
|
||||
type Folder struct {
|
||||
ID FolderID `json:"id"`
|
||||
DirEntry
|
||||
Path string `json:"path"`
|
||||
ParentFolderID *FolderID `json:"parent_folder_id"`
|
||||
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
func (f *Folder) Info(fs FS) (fs.FileInfo, error) {
|
||||
return f.info(fs, f.Path)
|
||||
}
|
||||
|
||||
type FolderFinder interface {
|
||||
Find(ctx context.Context, id FolderID) (*Folder, error)
|
||||
}
|
||||
|
||||
// FolderPathFinder finds Folders by their path.
|
||||
type FolderPathFinder interface {
|
||||
FindByPath(ctx context.Context, path string) (*Folder, error)
|
||||
}
|
||||
|
||||
// FolderGetter provides methods to find Folders.
|
||||
type FolderGetter interface {
|
||||
FolderFinder
|
||||
FolderPathFinder
|
||||
FindByZipFileID(ctx context.Context, zipFileID ID) ([]*Folder, error)
|
||||
FindAllInPaths(ctx context.Context, p []string, limit, offset int) ([]*Folder, error)
|
||||
FindByParentFolderID(ctx context.Context, parentFolderID FolderID) ([]*Folder, error)
|
||||
}
|
||||
|
||||
type FolderCounter interface {
|
||||
CountAllInPaths(ctx context.Context, p []string) (int, error)
|
||||
}
|
||||
|
||||
// FolderCreator provides methods to create Folders.
|
||||
type FolderCreator interface {
|
||||
Create(ctx context.Context, f *Folder) error
|
||||
}
|
||||
|
||||
type FolderFinderCreator interface {
|
||||
FolderPathFinder
|
||||
FolderCreator
|
||||
}
|
||||
|
||||
// FolderUpdater provides methods to update Folders.
|
||||
type FolderUpdater interface {
|
||||
Update(ctx context.Context, f *Folder) error
|
||||
}
|
||||
|
||||
type FolderDestroyer interface {
|
||||
Destroy(ctx context.Context, id FolderID) error
|
||||
}
|
||||
|
||||
type FolderGetterDestroyer interface {
|
||||
FolderGetter
|
||||
FolderDestroyer
|
||||
}
|
||||
|
||||
// FolderStore provides methods to find, create and update Folders.
|
||||
type FolderStore interface {
|
||||
FolderGetter
|
||||
FolderCounter
|
||||
FolderCreator
|
||||
FolderUpdater
|
||||
FolderDestroyer
|
||||
}
|
||||
|
||||
// GetOrCreateFolderHierarchy gets the folder for the given path, or creates a folder hierarchy for the given path if one if no existing folder is found.
|
||||
// Does not create any folders in the file system
|
||||
func GetOrCreateFolderHierarchy(ctx context.Context, fc FolderFinderCreator, path string) (*Folder, error) {
|
||||
func GetOrCreateFolderHierarchy(ctx context.Context, fc models.FolderFinderCreator, path string) (*models.Folder, error) {
|
||||
// get or create folder hierarchy
|
||||
folder, err := fc.FindByPath(ctx, path)
|
||||
if err != nil {
|
||||
@@ -106,10 +28,10 @@ func GetOrCreateFolderHierarchy(ctx context.Context, fc FolderFinderCreator, pat
|
||||
|
||||
now := time.Now()
|
||||
|
||||
folder = &Folder{
|
||||
folder = &models.Folder{
|
||||
Path: path,
|
||||
ParentFolderID: &parent.ID,
|
||||
DirEntry: DirEntry{
|
||||
DirEntry: models.DirEntry{
|
||||
// leave mod time empty for now - it will be updated when the folder is scanned
|
||||
},
|
||||
CreatedAt: now,
|
||||
@@ -126,7 +48,7 @@ func GetOrCreateFolderHierarchy(ctx context.Context, fc FolderFinderCreator, pat
|
||||
|
||||
// TransferZipFolderHierarchy creates the folder hierarchy for zipFileID under newPath, and removes
|
||||
// ZipFileID from folders under oldPath.
|
||||
func TransferZipFolderHierarchy(ctx context.Context, folderStore FolderStore, zipFileID ID, oldPath string, newPath string) error {
|
||||
func TransferZipFolderHierarchy(ctx context.Context, folderStore models.FolderReaderWriter, zipFileID models.FileID, oldPath string, newPath string) error {
|
||||
zipFolders, err := folderStore.FindByZipFileID(ctx, zipFileID)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -7,27 +7,28 @@ import (
|
||||
"io/fs"
|
||||
|
||||
"github.com/stashapp/stash/pkg/logger"
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
)
|
||||
|
||||
type folderRenameCandidate struct {
|
||||
folder *Folder
|
||||
folder *models.Folder
|
||||
found int
|
||||
files int
|
||||
}
|
||||
|
||||
type folderRenameDetector struct {
|
||||
// candidates is a map of folder id to the number of files that match
|
||||
candidates map[FolderID]folderRenameCandidate
|
||||
candidates map[models.FolderID]folderRenameCandidate
|
||||
// rejects is a set of folder ids which were found to still exist
|
||||
rejects map[FolderID]struct{}
|
||||
rejects map[models.FolderID]struct{}
|
||||
}
|
||||
|
||||
func (d *folderRenameDetector) isReject(id FolderID) bool {
|
||||
func (d *folderRenameDetector) isReject(id models.FolderID) bool {
|
||||
_, ok := d.rejects[id]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (d *folderRenameDetector) getCandidate(id FolderID) *folderRenameCandidate {
|
||||
func (d *folderRenameDetector) getCandidate(id models.FolderID) *folderRenameCandidate {
|
||||
c, ok := d.candidates[id]
|
||||
if !ok {
|
||||
return nil
|
||||
@@ -40,14 +41,14 @@ func (d *folderRenameDetector) setCandidate(c folderRenameCandidate) {
|
||||
d.candidates[c.folder.ID] = c
|
||||
}
|
||||
|
||||
func (d *folderRenameDetector) reject(id FolderID) {
|
||||
func (d *folderRenameDetector) reject(id models.FolderID) {
|
||||
d.rejects[id] = struct{}{}
|
||||
}
|
||||
|
||||
// bestCandidate returns the folder that is the best candidate for a rename.
|
||||
// This is the folder that has the largest number of its original files that
|
||||
// are still present in the new location.
|
||||
func (d *folderRenameDetector) bestCandidate() *Folder {
|
||||
func (d *folderRenameDetector) bestCandidate() *models.Folder {
|
||||
if len(d.candidates) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -74,14 +75,14 @@ func (d *folderRenameDetector) bestCandidate() *Folder {
|
||||
return best.folder
|
||||
}
|
||||
|
||||
func (s *scanJob) detectFolderMove(ctx context.Context, file scanFile) (*Folder, error) {
|
||||
func (s *scanJob) detectFolderMove(ctx context.Context, file scanFile) (*models.Folder, error) {
|
||||
// in order for a folder to be considered moved, the existing folder must be
|
||||
// missing, and the majority of the old folder's files must be present, unchanged,
|
||||
// in the new folder.
|
||||
|
||||
detector := folderRenameDetector{
|
||||
candidates: make(map[FolderID]folderRenameCandidate),
|
||||
rejects: make(map[FolderID]struct{}),
|
||||
candidates: make(map[models.FolderID]folderRenameCandidate),
|
||||
rejects: make(map[models.FolderID]struct{}),
|
||||
}
|
||||
// rejects is a set of folder ids which were found to still exist
|
||||
|
||||
@@ -117,7 +118,7 @@ func (s *scanJob) detectFolderMove(ctx context.Context, file scanFile) (*Folder,
|
||||
}
|
||||
|
||||
// check if the file exists in the database based on basename, size and mod time
|
||||
existing, err := s.Repository.Store.FindByFileInfo(ctx, info, size)
|
||||
existing, err := s.Repository.FileStore.FindByFileInfo(ctx, info, size)
|
||||
if err != nil {
|
||||
return fmt.Errorf("checking for existing file %q: %w", path, err)
|
||||
}
|
||||
@@ -163,7 +164,7 @@ func (s *scanJob) detectFolderMove(ctx context.Context, file scanFile) (*Folder,
|
||||
|
||||
// parent folder is missing, possible candidate
|
||||
// count the total number of files in the existing folder
|
||||
count, err := s.Repository.Store.CountByFolderID(ctx, parentFolderID)
|
||||
count, err := s.Repository.FileStore.CountByFolderID(ctx, parentFolderID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("counting files in folder %d: %w", parentFolderID, err)
|
||||
}
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
package file
|
||||
|
||||
// VisualFile is an interface for files that have a width and height.
|
||||
type VisualFile interface {
|
||||
File
|
||||
GetWidth() int
|
||||
GetHeight() int
|
||||
GetFormat() string
|
||||
}
|
||||
|
||||
func GetMinResolution(f VisualFile) int {
|
||||
w := f.GetWidth()
|
||||
h := f.GetHeight()
|
||||
|
||||
if w < h {
|
||||
return w
|
||||
}
|
||||
|
||||
return h
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"os"
|
||||
|
||||
"github.com/stashapp/stash/pkg/fsutil"
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
)
|
||||
|
||||
// Opener provides an interface to open a file.
|
||||
@@ -14,7 +15,7 @@ type Opener interface {
|
||||
}
|
||||
|
||||
type fsOpener struct {
|
||||
fs FS
|
||||
fs models.FS
|
||||
name string
|
||||
}
|
||||
|
||||
@@ -22,15 +23,6 @@ func (o *fsOpener) Open() (io.ReadCloser, error) {
|
||||
return o.fs.Open(o.name)
|
||||
}
|
||||
|
||||
// FS represents a file system.
|
||||
type FS interface {
|
||||
Stat(name string) (fs.FileInfo, error)
|
||||
Lstat(name string) (fs.FileInfo, error)
|
||||
Open(name string) (fs.ReadDirFile, error)
|
||||
OpenZip(name string) (*ZipFS, error)
|
||||
IsPathCaseSensitive(path string) (bool, error)
|
||||
}
|
||||
|
||||
// OsFS is a file system backed by the OS.
|
||||
type OsFS struct{}
|
||||
|
||||
@@ -66,7 +58,7 @@ func (f *OsFS) Open(name string) (fs.ReadDirFile, error) {
|
||||
return os.Open(name)
|
||||
}
|
||||
|
||||
func (f *OsFS) OpenZip(name string) (*ZipFS, error) {
|
||||
func (f *OsFS) OpenZip(name string) (models.ZipFS, error) {
|
||||
info, err := f.Lstat(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -3,6 +3,8 @@ package file
|
||||
import (
|
||||
"context"
|
||||
"io/fs"
|
||||
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
)
|
||||
|
||||
// PathFilter provides a filter function for paths.
|
||||
@@ -18,18 +20,18 @@ func (pff PathFilterFunc) Accept(path string) bool {
|
||||
|
||||
// Filter provides a filter function for Files.
|
||||
type Filter interface {
|
||||
Accept(ctx context.Context, f File) bool
|
||||
Accept(ctx context.Context, f models.File) bool
|
||||
}
|
||||
|
||||
type FilterFunc func(ctx context.Context, f File) bool
|
||||
type FilterFunc func(ctx context.Context, f models.File) bool
|
||||
|
||||
func (ff FilterFunc) Accept(ctx context.Context, f File) bool {
|
||||
func (ff FilterFunc) Accept(ctx context.Context, f models.File) bool {
|
||||
return ff(ctx, f)
|
||||
}
|
||||
|
||||
// Handler provides a handler for Files.
|
||||
type Handler interface {
|
||||
Handle(ctx context.Context, f File, oldFile File) error
|
||||
Handle(ctx context.Context, f models.File, oldFile models.File) error
|
||||
}
|
||||
|
||||
// FilteredHandler is a Handler runs only if the filter accepts the file.
|
||||
@@ -39,7 +41,7 @@ type FilteredHandler struct {
|
||||
}
|
||||
|
||||
// Handle runs the handler if the filter accepts the file.
|
||||
func (h *FilteredHandler) Handle(ctx context.Context, f File, oldFile File) error {
|
||||
func (h *FilteredHandler) Handle(ctx context.Context, f models.File, oldFile models.File) error {
|
||||
if h.Accept(ctx, f) {
|
||||
return h.Handler.Handle(ctx, f, oldFile)
|
||||
}
|
||||
@@ -48,6 +50,6 @@ func (h *FilteredHandler) Handle(ctx context.Context, f File, oldFile File) erro
|
||||
|
||||
// CleanHandler provides a handler for cleaning Files and Folders.
|
||||
type CleanHandler interface {
|
||||
HandleFile(ctx context.Context, fileDeleter *Deleter, fileID ID) error
|
||||
HandleFolder(ctx context.Context, fileDeleter *Deleter, folderID FolderID) error
|
||||
HandleFile(ctx context.Context, fileDeleter *Deleter, fileID models.FileID) error
|
||||
HandleFolder(ctx context.Context, fileDeleter *Deleter, folderID models.FolderID) error
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/stashapp/stash/pkg/file"
|
||||
"github.com/stashapp/stash/pkg/file/video"
|
||||
"github.com/stashapp/stash/pkg/logger"
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
_ "golang.org/x/image/webp"
|
||||
)
|
||||
|
||||
@@ -21,10 +22,10 @@ type Decorator struct {
|
||||
FFProbe ffmpeg.FFProbe
|
||||
}
|
||||
|
||||
func (d *Decorator) Decorate(ctx context.Context, fs file.FS, f file.File) (file.File, error) {
|
||||
func (d *Decorator) Decorate(ctx context.Context, fs models.FS, f models.File) (models.File, error) {
|
||||
base := f.Base()
|
||||
|
||||
decorateFallback := func() (file.File, error) {
|
||||
decorateFallback := func() (models.File, error) {
|
||||
r, err := fs.Open(base.Path)
|
||||
if err != nil {
|
||||
return f, fmt.Errorf("reading image file %q: %w", base.Path, err)
|
||||
@@ -35,7 +36,7 @@ func (d *Decorator) Decorate(ctx context.Context, fs file.FS, f file.File) (file
|
||||
if err != nil {
|
||||
return f, fmt.Errorf("decoding image file %q: %w", base.Path, err)
|
||||
}
|
||||
return &file.ImageFile{
|
||||
return &models.ImageFile{
|
||||
BaseFile: base,
|
||||
Format: format,
|
||||
Width: c.Width,
|
||||
@@ -58,7 +59,7 @@ func (d *Decorator) Decorate(ctx context.Context, fs file.FS, f file.File) (file
|
||||
|
||||
// Fallback to catch non-animated avif images that FFProbe detects as video files
|
||||
if probe.Bitrate == 0 && probe.VideoCodec == "av1" {
|
||||
return &file.ImageFile{
|
||||
return &models.ImageFile{
|
||||
BaseFile: base,
|
||||
Format: "avif",
|
||||
Width: probe.Width,
|
||||
@@ -78,7 +79,7 @@ func (d *Decorator) Decorate(ctx context.Context, fs file.FS, f file.File) (file
|
||||
return videoFileDecorator.Decorate(ctx, fs, f)
|
||||
}
|
||||
|
||||
return &file.ImageFile{
|
||||
return &models.ImageFile{
|
||||
BaseFile: base,
|
||||
Format: probe.VideoCodec,
|
||||
Width: probe.Width,
|
||||
@@ -86,14 +87,14 @@ func (d *Decorator) Decorate(ctx context.Context, fs file.FS, f file.File) (file
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *Decorator) IsMissingMetadata(ctx context.Context, fs file.FS, f file.File) bool {
|
||||
func (d *Decorator) IsMissingMetadata(ctx context.Context, fs models.FS, f models.File) bool {
|
||||
const (
|
||||
unsetString = "unset"
|
||||
unsetNumber = -1
|
||||
)
|
||||
|
||||
imf, isImage := f.(*file.ImageFile)
|
||||
vf, isVideo := f.(*file.VideoFile)
|
||||
imf, isImage := f.(*models.ImageFile)
|
||||
vf, isVideo := f.(*models.VideoFile)
|
||||
|
||||
switch {
|
||||
case isImage:
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
package file
|
||||
|
||||
// ImageFile is an extension of BaseFile to represent image files.
|
||||
type ImageFile struct {
|
||||
*BaseFile
|
||||
Format string `json:"format"`
|
||||
Width int `json:"width"`
|
||||
Height int `json:"height"`
|
||||
}
|
||||
|
||||
func (f ImageFile) GetWidth() int {
|
||||
return f.Width
|
||||
}
|
||||
|
||||
func (f ImageFile) GetHeight() int {
|
||||
return f.Height
|
||||
}
|
||||
|
||||
func (f ImageFile) GetFormat() string {
|
||||
return f.Format
|
||||
}
|
||||
253
pkg/file/import.go
Normal file
253
pkg/file/import.go
Normal file
@@ -0,0 +1,253 @@
|
||||
package file
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
"github.com/stashapp/stash/pkg/models/jsonschema"
|
||||
)
|
||||
|
||||
var ErrZipFileNotExist = errors.New("zip file does not exist")
|
||||
|
||||
type Importer struct {
|
||||
ReaderWriter models.FileFinderCreator
|
||||
FolderStore models.FolderFinderCreator
|
||||
Input jsonschema.DirEntry
|
||||
|
||||
file models.File
|
||||
folder *models.Folder
|
||||
}
|
||||
|
||||
func (i *Importer) PreImport(ctx context.Context) error {
|
||||
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
|
||||
}
|
||||
|
||||
func (i *Importer) folderJSONToFolder(ctx context.Context, baseJSON *jsonschema.BaseDirEntry) (*models.Folder, error) {
|
||||
ret := models.Folder{
|
||||
DirEntry: models.DirEntry{
|
||||
ModTime: baseJSON.ModTime.GetTime(),
|
||||
},
|
||||
Path: baseJSON.Path,
|
||||
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
|
||||
}
|
||||
|
||||
func (i *Importer) fileJSONToFile(ctx context.Context, fileJSON jsonschema.DirEntry) (models.File, error) {
|
||||
switch ff := fileJSON.(type) {
|
||||
case *jsonschema.VideoFile:
|
||||
baseFile, err := i.baseFileJSONToBaseFile(ctx, ff.BaseFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &models.VideoFile{
|
||||
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
|
||||
}
|
||||
return &models.ImageFile{
|
||||
BaseFile: baseFile,
|
||||
Format: ff.Format,
|
||||
Width: ff.Width,
|
||||
Height: ff.Height,
|
||||
}, nil
|
||||
case *jsonschema.BaseFile:
|
||||
return i.baseFileJSONToBaseFile(ctx, ff)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unknown file type")
|
||||
}
|
||||
|
||||
func (i *Importer) baseFileJSONToBaseFile(ctx context.Context, baseJSON *jsonschema.BaseFile) (*models.BaseFile, error) {
|
||||
baseFile := models.BaseFile{
|
||||
DirEntry: models.DirEntry{
|
||||
ModTime: baseJSON.ModTime.GetTime(),
|
||||
},
|
||||
Basename: filepath.Base(baseJSON.Path),
|
||||
Size: baseJSON.Size,
|
||||
CreatedAt: baseJSON.CreatedAt.GetTime(),
|
||||
UpdatedAt: baseJSON.CreatedAt.GetTime(),
|
||||
}
|
||||
|
||||
for _, fp := range baseJSON.Fingerprints {
|
||||
baseFile.Fingerprints = append(baseFile.Fingerprints, models.Fingerprint{
|
||||
Type: fp.Type,
|
||||
Fingerprint: fp.Fingerprint,
|
||||
})
|
||||
}
|
||||
|
||||
if err := i.populateZipFileID(ctx, &baseFile.DirEntry); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &baseFile, nil
|
||||
}
|
||||
|
||||
func (i *Importer) populateZipFileID(ctx context.Context, f *models.DirEntry) error {
|
||||
zipFilePath := i.Input.DirEntry().ZipFile
|
||||
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 {
|
||||
return ErrZipFileNotExist
|
||||
}
|
||||
|
||||
id := zf.Base().ID
|
||||
f.ZipFileID = &id
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Importer) PostImport(ctx context.Context, id int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Importer) Name() string {
|
||||
return i.Input.DirEntry().Path
|
||||
}
|
||||
|
||||
func (i *Importer) FindExistingID(ctx context.Context) (*int, error) {
|
||||
path := i.Input.DirEntry().Path
|
||||
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
|
||||
}
|
||||
|
||||
func (i *Importer) createFolderHierarchy(ctx context.Context, p string) (*models.Folder, error) {
|
||||
parentPath := filepath.Dir(p)
|
||||
|
||||
if parentPath == p {
|
||||
// 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)
|
||||
}
|
||||
|
||||
func (i *Importer) getOrCreateFolder(ctx context.Context, path string, parent *models.Folder) (*models.Folder, error) {
|
||||
folder, err := i.FolderStore.FindByPath(ctx, path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if folder != nil {
|
||||
return folder, nil
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
|
||||
folder = &models.Folder{
|
||||
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
|
||||
}
|
||||
|
||||
func (i *Importer) Create(ctx context.Context) (*int, error) {
|
||||
// create folder hierarchy and set parent folder id
|
||||
path := i.Input.DirEntry().Path
|
||||
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)
|
||||
}
|
||||
|
||||
func (i *Importer) createFile(ctx context.Context, parentFolder *models.Folder) (*int, error) {
|
||||
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
|
||||
}
|
||||
|
||||
func (i *Importer) createFolder(ctx context.Context, parentFolder *models.Folder) (*int, error) {
|
||||
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
|
||||
}
|
||||
|
||||
func (i *Importer) Update(ctx context.Context, id int) error {
|
||||
// update not supported
|
||||
return nil
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/stashapp/stash/pkg/logger"
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
"github.com/stashapp/stash/pkg/txn"
|
||||
)
|
||||
|
||||
@@ -40,14 +41,14 @@ func (r folderCreatorStatRenamerImpl) Mkdir(name string, perm os.FileMode) error
|
||||
|
||||
type Mover struct {
|
||||
Renamer DirMakerStatRenamer
|
||||
Files GetterUpdater
|
||||
Folders FolderStore
|
||||
Files models.FileFinderUpdater
|
||||
Folders models.FolderReaderWriter
|
||||
|
||||
moved map[string]string
|
||||
foldersCreated []string
|
||||
}
|
||||
|
||||
func NewMover(fileStore GetterUpdater, folderStore FolderStore) *Mover {
|
||||
func NewMover(fileStore models.FileFinderUpdater, folderStore models.FolderReaderWriter) *Mover {
|
||||
return &Mover{
|
||||
Files: fileStore,
|
||||
Folders: folderStore,
|
||||
@@ -60,7 +61,7 @@ func NewMover(fileStore GetterUpdater, folderStore FolderStore) *Mover {
|
||||
|
||||
// Move moves the file to the given folder and basename. If basename is empty, then the existing basename is used.
|
||||
// Assumes that the parent folder exists in the filesystem.
|
||||
func (m *Mover) Move(ctx context.Context, f File, folder *Folder, basename string) error {
|
||||
func (m *Mover) Move(ctx context.Context, f models.File, folder *models.Folder, basename string) error {
|
||||
fBase := f.Base()
|
||||
|
||||
// don't allow moving files in zip files
|
||||
|
||||
130
pkg/file/scan.go
130
pkg/file/scan.go
@@ -13,6 +13,7 @@ import (
|
||||
|
||||
"github.com/remeh/sizedwaitgroup"
|
||||
"github.com/stashapp/stash/pkg/logger"
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
"github.com/stashapp/stash/pkg/txn"
|
||||
"github.com/stashapp/stash/pkg/utils"
|
||||
)
|
||||
@@ -24,15 +25,6 @@ const (
|
||||
maxRetries = -1
|
||||
)
|
||||
|
||||
// Repository provides access to storage methods for files and folders.
|
||||
type Repository struct {
|
||||
txn.Manager
|
||||
txn.DatabaseProvider
|
||||
Store
|
||||
|
||||
FolderStore FolderStore
|
||||
}
|
||||
|
||||
// Scanner scans files into the database.
|
||||
//
|
||||
// The scan process works using two goroutines. The first walks through the provided paths
|
||||
@@ -59,7 +51,7 @@ type Repository struct {
|
||||
// If the file is not a renamed file, then the decorators are fired and the file is created, then
|
||||
// the applicable handlers are fired.
|
||||
type Scanner struct {
|
||||
FS FS
|
||||
FS models.FS
|
||||
Repository Repository
|
||||
FingerprintCalculator FingerprintCalculator
|
||||
|
||||
@@ -67,6 +59,38 @@ type Scanner struct {
|
||||
FileDecorators []Decorator
|
||||
}
|
||||
|
||||
// FingerprintCalculator calculates a fingerprint for the provided file.
|
||||
type FingerprintCalculator interface {
|
||||
CalculateFingerprints(f *models.BaseFile, o Opener, useExisting bool) ([]models.Fingerprint, error)
|
||||
}
|
||||
|
||||
// Decorator wraps the Decorate method to add additional functionality while scanning files.
|
||||
type Decorator interface {
|
||||
Decorate(ctx context.Context, fs models.FS, f models.File) (models.File, error)
|
||||
IsMissingMetadata(ctx context.Context, fs models.FS, f models.File) bool
|
||||
}
|
||||
|
||||
type FilteredDecorator struct {
|
||||
Decorator
|
||||
Filter
|
||||
}
|
||||
|
||||
// Decorate runs the decorator if the filter accepts the file.
|
||||
func (d *FilteredDecorator) Decorate(ctx context.Context, fs models.FS, f models.File) (models.File, error) {
|
||||
if d.Accept(ctx, f) {
|
||||
return d.Decorator.Decorate(ctx, fs, f)
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func (d *FilteredDecorator) IsMissingMetadata(ctx context.Context, fs models.FS, f models.File) bool {
|
||||
if d.Accept(ctx, f) {
|
||||
return d.Decorator.IsMissingMetadata(ctx, fs, f)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// ProgressReporter is used to report progress of the scan.
|
||||
type ProgressReporter interface {
|
||||
AddTotal(total int)
|
||||
@@ -129,8 +153,8 @@ func (s *Scanner) Scan(ctx context.Context, handlers []Handler, options ScanOpti
|
||||
}
|
||||
|
||||
type scanFile struct {
|
||||
*BaseFile
|
||||
fs FS
|
||||
*models.BaseFile
|
||||
fs models.FS
|
||||
info fs.FileInfo
|
||||
}
|
||||
|
||||
@@ -198,7 +222,7 @@ func (s *scanJob) queueFiles(ctx context.Context, paths []string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *scanJob) queueFileFunc(ctx context.Context, f FS, zipFile *scanFile) fs.WalkDirFunc {
|
||||
func (s *scanJob) queueFileFunc(ctx context.Context, f models.FS, zipFile *scanFile) fs.WalkDirFunc {
|
||||
return func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
// don't let errors prevent scanning
|
||||
@@ -229,8 +253,8 @@ func (s *scanJob) queueFileFunc(ctx context.Context, f FS, zipFile *scanFile) fs
|
||||
}
|
||||
|
||||
ff := scanFile{
|
||||
BaseFile: &BaseFile{
|
||||
DirEntry: DirEntry{
|
||||
BaseFile: &models.BaseFile{
|
||||
DirEntry: models.DirEntry{
|
||||
ModTime: modTime(info),
|
||||
},
|
||||
Path: path,
|
||||
@@ -286,7 +310,7 @@ func (s *scanJob) queueFileFunc(ctx context.Context, f FS, zipFile *scanFile) fs
|
||||
}
|
||||
}
|
||||
|
||||
func getFileSize(f FS, path string, info fs.FileInfo) (int64, error) {
|
||||
func getFileSize(f models.FS, path string, info fs.FileInfo) (int64, error) {
|
||||
// #2196/#3042 - replace size with target size if file is a symlink
|
||||
if info.Mode()&os.ModeSymlink == os.ModeSymlink {
|
||||
targetInfo, err := f.Stat(path)
|
||||
@@ -408,10 +432,10 @@ func (s *scanJob) processQueueItem(ctx context.Context, f scanFile) {
|
||||
})
|
||||
}
|
||||
|
||||
func (s *scanJob) getFolderID(ctx context.Context, path string) (*FolderID, error) {
|
||||
func (s *scanJob) getFolderID(ctx context.Context, path string) (*models.FolderID, error) {
|
||||
// check the folder cache first
|
||||
if f, ok := s.folderPathToID.Load(path); ok {
|
||||
v := f.(FolderID)
|
||||
v := f.(models.FolderID)
|
||||
return &v, nil
|
||||
}
|
||||
|
||||
@@ -428,7 +452,7 @@ func (s *scanJob) getFolderID(ctx context.Context, path string) (*FolderID, erro
|
||||
return &ret.ID, nil
|
||||
}
|
||||
|
||||
func (s *scanJob) getZipFileID(ctx context.Context, zipFile *scanFile) (*ID, error) {
|
||||
func (s *scanJob) getZipFileID(ctx context.Context, zipFile *scanFile) (*models.FileID, error) {
|
||||
if zipFile == nil {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -441,11 +465,11 @@ func (s *scanJob) getZipFileID(ctx context.Context, zipFile *scanFile) (*ID, err
|
||||
|
||||
// check the folder cache first
|
||||
if f, ok := s.zipPathToID.Load(path); ok {
|
||||
v := f.(ID)
|
||||
v := f.(models.FileID)
|
||||
return &v, nil
|
||||
}
|
||||
|
||||
ret, err := s.Repository.FindByPath(ctx, path)
|
||||
ret, err := s.Repository.FileStore.FindByPath(ctx, path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting zip file ID for %q: %w", path, err)
|
||||
}
|
||||
@@ -489,7 +513,7 @@ func (s *scanJob) handleFolder(ctx context.Context, file scanFile) error {
|
||||
})
|
||||
}
|
||||
|
||||
func (s *scanJob) onNewFolder(ctx context.Context, file scanFile) (*Folder, error) {
|
||||
func (s *scanJob) onNewFolder(ctx context.Context, file scanFile) (*models.Folder, error) {
|
||||
renamed, err := s.handleFolderRename(ctx, file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -501,7 +525,7 @@ func (s *scanJob) onNewFolder(ctx context.Context, file scanFile) (*Folder, erro
|
||||
|
||||
now := time.Now()
|
||||
|
||||
toCreate := &Folder{
|
||||
toCreate := &models.Folder{
|
||||
DirEntry: file.DirEntry,
|
||||
Path: file.Path,
|
||||
CreatedAt: now,
|
||||
@@ -536,7 +560,7 @@ func (s *scanJob) onNewFolder(ctx context.Context, file scanFile) (*Folder, erro
|
||||
return toCreate, nil
|
||||
}
|
||||
|
||||
func (s *scanJob) handleFolderRename(ctx context.Context, file scanFile) (*Folder, error) {
|
||||
func (s *scanJob) handleFolderRename(ctx context.Context, file scanFile) (*models.Folder, error) {
|
||||
// ignore folders in zip files
|
||||
if file.ZipFileID != nil {
|
||||
return nil, nil
|
||||
@@ -572,7 +596,7 @@ func (s *scanJob) handleFolderRename(ctx context.Context, file scanFile) (*Folde
|
||||
return renamedFrom, nil
|
||||
}
|
||||
|
||||
func (s *scanJob) onExistingFolder(ctx context.Context, f scanFile, existing *Folder) (*Folder, error) {
|
||||
func (s *scanJob) onExistingFolder(ctx context.Context, f scanFile, existing *models.Folder) (*models.Folder, error) {
|
||||
update := false
|
||||
|
||||
// update if mod time is changed
|
||||
@@ -613,12 +637,12 @@ func modTime(info fs.FileInfo) time.Time {
|
||||
func (s *scanJob) handleFile(ctx context.Context, f scanFile) error {
|
||||
defer s.incrementProgress(f)
|
||||
|
||||
var ff File
|
||||
var ff models.File
|
||||
// don't use a transaction to check if new or existing
|
||||
if err := s.withDB(ctx, func(ctx context.Context) error {
|
||||
// determine if file already exists in data store
|
||||
var err error
|
||||
ff, err = s.Repository.FindByPath(ctx, f.Path)
|
||||
ff, err = s.Repository.FileStore.FindByPath(ctx, f.Path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("checking for existing file %q: %w", f.Path, err)
|
||||
}
|
||||
@@ -661,7 +685,7 @@ func (s *scanJob) isZipFile(path string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *scanJob) onNewFile(ctx context.Context, f scanFile) (File, error) {
|
||||
func (s *scanJob) onNewFile(ctx context.Context, f scanFile) (models.File, error) {
|
||||
now := time.Now()
|
||||
|
||||
baseFile := f.BaseFile
|
||||
@@ -716,7 +740,7 @@ func (s *scanJob) onNewFile(ctx context.Context, f scanFile) (File, error) {
|
||||
|
||||
// if not renamed, queue file for creation
|
||||
if err := s.withTxn(ctx, func(ctx context.Context) error {
|
||||
if err := s.Repository.Create(ctx, file); err != nil {
|
||||
if err := s.Repository.FileStore.Create(ctx, file); err != nil {
|
||||
return fmt.Errorf("creating file %q: %w", path, err)
|
||||
}
|
||||
|
||||
@@ -732,7 +756,7 @@ func (s *scanJob) onNewFile(ctx context.Context, f scanFile) (File, error) {
|
||||
return file, nil
|
||||
}
|
||||
|
||||
func (s *scanJob) fireDecorators(ctx context.Context, fs FS, f File) (File, error) {
|
||||
func (s *scanJob) fireDecorators(ctx context.Context, fs models.FS, f models.File) (models.File, error) {
|
||||
for _, h := range s.FileDecorators {
|
||||
var err error
|
||||
f, err = h.Decorate(ctx, fs, f)
|
||||
@@ -744,7 +768,7 @@ func (s *scanJob) fireDecorators(ctx context.Context, fs FS, f File) (File, erro
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func (s *scanJob) fireHandlers(ctx context.Context, f File, oldFile File) error {
|
||||
func (s *scanJob) fireHandlers(ctx context.Context, f models.File, oldFile models.File) error {
|
||||
for _, h := range s.handlers {
|
||||
if err := h.Handle(ctx, f, oldFile); err != nil {
|
||||
return err
|
||||
@@ -754,7 +778,7 @@ func (s *scanJob) fireHandlers(ctx context.Context, f File, oldFile File) error
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *scanJob) calculateFingerprints(fs FS, f *BaseFile, path string, useExisting bool) (Fingerprints, error) {
|
||||
func (s *scanJob) calculateFingerprints(fs models.FS, f *models.BaseFile, path string, useExisting bool) (models.Fingerprints, error) {
|
||||
// only log if we're (re)calculating fingerprints
|
||||
if !useExisting {
|
||||
logger.Infof("Calculating fingerprints for %s ...", path)
|
||||
@@ -772,7 +796,7 @@ func (s *scanJob) calculateFingerprints(fs FS, f *BaseFile, path string, useExis
|
||||
return fp, nil
|
||||
}
|
||||
|
||||
func appendFileUnique(v []File, toAdd []File) []File {
|
||||
func appendFileUnique(v []models.File, toAdd []models.File) []models.File {
|
||||
for _, f := range toAdd {
|
||||
found := false
|
||||
id := f.Base().ID
|
||||
@@ -791,7 +815,7 @@ func appendFileUnique(v []File, toAdd []File) []File {
|
||||
return v
|
||||
}
|
||||
|
||||
func (s *scanJob) getFileFS(f *BaseFile) (FS, error) {
|
||||
func (s *scanJob) getFileFS(f *models.BaseFile) (models.FS, error) {
|
||||
if f.ZipFile == nil {
|
||||
return s.FS, nil
|
||||
}
|
||||
@@ -805,11 +829,11 @@ func (s *scanJob) getFileFS(f *BaseFile) (FS, error) {
|
||||
return fs.OpenZip(zipPath)
|
||||
}
|
||||
|
||||
func (s *scanJob) handleRename(ctx context.Context, f File, fp []Fingerprint) (File, error) {
|
||||
var others []File
|
||||
func (s *scanJob) handleRename(ctx context.Context, f models.File, fp []models.Fingerprint) (models.File, error) {
|
||||
var others []models.File
|
||||
|
||||
for _, tfp := range fp {
|
||||
thisOthers, err := s.Repository.FindByFingerprint(ctx, tfp)
|
||||
thisOthers, err := s.Repository.FileStore.FindByFingerprint(ctx, tfp)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting files by fingerprint %v: %w", tfp, err)
|
||||
}
|
||||
@@ -817,7 +841,7 @@ func (s *scanJob) handleRename(ctx context.Context, f File, fp []Fingerprint) (F
|
||||
others = appendFileUnique(others, thisOthers)
|
||||
}
|
||||
|
||||
var missing []File
|
||||
var missing []models.File
|
||||
|
||||
fZipID := f.Base().ZipFileID
|
||||
for _, other := range others {
|
||||
@@ -867,7 +891,7 @@ func (s *scanJob) handleRename(ctx context.Context, f File, fp []Fingerprint) (F
|
||||
fBase.Fingerprints = otherBase.Fingerprints
|
||||
|
||||
if err := s.withTxn(ctx, func(ctx context.Context) error {
|
||||
if err := s.Repository.Update(ctx, f); err != nil {
|
||||
if err := s.Repository.FileStore.Update(ctx, f); err != nil {
|
||||
return fmt.Errorf("updating file for rename %q: %w", fBase.Path, err)
|
||||
}
|
||||
|
||||
@@ -889,7 +913,7 @@ func (s *scanJob) handleRename(ctx context.Context, f File, fp []Fingerprint) (F
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func (s *scanJob) isHandlerRequired(ctx context.Context, f File) bool {
|
||||
func (s *scanJob) isHandlerRequired(ctx context.Context, f models.File) bool {
|
||||
accept := len(s.options.HandlerRequiredFilters) == 0
|
||||
for _, filter := range s.options.HandlerRequiredFilters {
|
||||
// accept if any filter accepts the file
|
||||
@@ -910,7 +934,7 @@ func (s *scanJob) isHandlerRequired(ctx context.Context, f File) bool {
|
||||
// - file size
|
||||
// - image format, width or height
|
||||
// - video codec, audio codec, format, width, height, framerate or bitrate
|
||||
func (s *scanJob) isMissingMetadata(ctx context.Context, f scanFile, existing File) bool {
|
||||
func (s *scanJob) isMissingMetadata(ctx context.Context, f scanFile, existing models.File) bool {
|
||||
for _, h := range s.FileDecorators {
|
||||
if h.IsMissingMetadata(ctx, f.fs, existing) {
|
||||
return true
|
||||
@@ -920,7 +944,7 @@ func (s *scanJob) isMissingMetadata(ctx context.Context, f scanFile, existing Fi
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *scanJob) setMissingMetadata(ctx context.Context, f scanFile, existing File) (File, error) {
|
||||
func (s *scanJob) setMissingMetadata(ctx context.Context, f scanFile, existing models.File) (models.File, error) {
|
||||
path := existing.Base().Path
|
||||
logger.Infof("Updating metadata for %s", path)
|
||||
|
||||
@@ -934,7 +958,7 @@ func (s *scanJob) setMissingMetadata(ctx context.Context, f scanFile, existing F
|
||||
|
||||
// queue file for update
|
||||
if err := s.withTxn(ctx, func(ctx context.Context) error {
|
||||
if err := s.Repository.Update(ctx, existing); err != nil {
|
||||
if err := s.Repository.FileStore.Update(ctx, existing); err != nil {
|
||||
return fmt.Errorf("updating file %q: %w", path, err)
|
||||
}
|
||||
|
||||
@@ -946,7 +970,7 @@ func (s *scanJob) setMissingMetadata(ctx context.Context, f scanFile, existing F
|
||||
return existing, nil
|
||||
}
|
||||
|
||||
func (s *scanJob) setMissingFingerprints(ctx context.Context, f scanFile, existing File) (File, error) {
|
||||
func (s *scanJob) setMissingFingerprints(ctx context.Context, f scanFile, existing models.File) (models.File, error) {
|
||||
const useExisting = true
|
||||
fp, err := s.calculateFingerprints(f.fs, existing.Base(), f.Path, useExisting)
|
||||
if err != nil {
|
||||
@@ -957,7 +981,7 @@ func (s *scanJob) setMissingFingerprints(ctx context.Context, f scanFile, existi
|
||||
existing.SetFingerprints(fp)
|
||||
|
||||
if err := s.withTxn(ctx, func(ctx context.Context) error {
|
||||
if err := s.Repository.Update(ctx, existing); err != nil {
|
||||
if err := s.Repository.FileStore.Update(ctx, existing); err != nil {
|
||||
return fmt.Errorf("updating file %q: %w", f.Path, err)
|
||||
}
|
||||
|
||||
@@ -971,7 +995,7 @@ func (s *scanJob) setMissingFingerprints(ctx context.Context, f scanFile, existi
|
||||
}
|
||||
|
||||
// returns a file only if it was updated
|
||||
func (s *scanJob) onExistingFile(ctx context.Context, f scanFile, existing File) (File, error) {
|
||||
func (s *scanJob) onExistingFile(ctx context.Context, f scanFile, existing models.File) (models.File, error) {
|
||||
base := existing.Base()
|
||||
path := base.Path
|
||||
|
||||
@@ -1006,7 +1030,7 @@ func (s *scanJob) onExistingFile(ctx context.Context, f scanFile, existing File)
|
||||
|
||||
// queue file for update
|
||||
if err := s.withTxn(ctx, func(ctx context.Context) error {
|
||||
if err := s.Repository.Update(ctx, existing); err != nil {
|
||||
if err := s.Repository.FileStore.Update(ctx, existing); err != nil {
|
||||
return fmt.Errorf("updating file %q: %w", path, err)
|
||||
}
|
||||
|
||||
@@ -1022,21 +1046,21 @@ func (s *scanJob) onExistingFile(ctx context.Context, f scanFile, existing File)
|
||||
return existing, nil
|
||||
}
|
||||
|
||||
func (s *scanJob) removeOutdatedFingerprints(existing File, fp Fingerprints) {
|
||||
func (s *scanJob) removeOutdatedFingerprints(existing models.File, fp models.Fingerprints) {
|
||||
// HACK - if no MD5 fingerprint was returned, and the oshash is changed
|
||||
// then remove the MD5 fingerprint
|
||||
oshash := fp.For(FingerprintTypeOshash)
|
||||
oshash := fp.For(models.FingerprintTypeOshash)
|
||||
if oshash == nil {
|
||||
return
|
||||
}
|
||||
|
||||
existingOshash := existing.Base().Fingerprints.For(FingerprintTypeOshash)
|
||||
existingOshash := existing.Base().Fingerprints.For(models.FingerprintTypeOshash)
|
||||
if existingOshash == nil || *existingOshash == *oshash {
|
||||
// missing oshash or same oshash - nothing to do
|
||||
return
|
||||
}
|
||||
|
||||
md5 := fp.For(FingerprintTypeMD5)
|
||||
md5 := fp.For(models.FingerprintTypeMD5)
|
||||
|
||||
if md5 != nil {
|
||||
// nothing to do
|
||||
@@ -1045,11 +1069,11 @@ func (s *scanJob) removeOutdatedFingerprints(existing File, fp Fingerprints) {
|
||||
|
||||
// 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(FingerprintTypeMD5)
|
||||
existing.Base().Fingerprints.Remove(models.FingerprintTypeMD5)
|
||||
}
|
||||
|
||||
// returns a file only if it was updated
|
||||
func (s *scanJob) onUnchangedFile(ctx context.Context, f scanFile, existing File) (File, error) {
|
||||
func (s *scanJob) onUnchangedFile(ctx context.Context, f scanFile, existing models.File) (models.File, error) {
|
||||
var err error
|
||||
|
||||
isMissingMetdata := s.isMissingMetadata(ctx, f, existing)
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/asticode/go-astisub"
|
||||
"github.com/stashapp/stash/pkg/file"
|
||||
"github.com/stashapp/stash/pkg/logger"
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
"github.com/stashapp/stash/pkg/txn"
|
||||
@@ -87,12 +86,12 @@ func getCaptionsLangFromPath(captionPath string) string {
|
||||
}
|
||||
|
||||
type CaptionUpdater interface {
|
||||
GetCaptions(ctx context.Context, fileID file.ID) ([]*models.VideoCaption, error)
|
||||
UpdateCaptions(ctx context.Context, fileID file.ID, captions []*models.VideoCaption) error
|
||||
GetCaptions(ctx context.Context, fileID models.FileID) ([]*models.VideoCaption, error)
|
||||
UpdateCaptions(ctx context.Context, fileID models.FileID, captions []*models.VideoCaption) error
|
||||
}
|
||||
|
||||
// associates captions to scene/s with the same basename
|
||||
func AssociateCaptions(ctx context.Context, captionPath string, txnMgr txn.Manager, fqb file.Getter, w CaptionUpdater) {
|
||||
func AssociateCaptions(ctx context.Context, captionPath string, txnMgr txn.Manager, fqb models.FileFinder, w CaptionUpdater) {
|
||||
captionLang := getCaptionsLangFromPath(captionPath)
|
||||
|
||||
captionPrefix := getCaptionPrefix(captionPath)
|
||||
@@ -108,7 +107,7 @@ func AssociateCaptions(ctx context.Context, captionPath string, txnMgr txn.Manag
|
||||
// found some files
|
||||
// filter out non video files
|
||||
switch f.(type) {
|
||||
case *file.VideoFile:
|
||||
case *models.VideoFile:
|
||||
break
|
||||
default:
|
||||
continue
|
||||
@@ -143,7 +142,7 @@ func AssociateCaptions(ctx context.Context, captionPath string, txnMgr txn.Manag
|
||||
}
|
||||
|
||||
// CleanCaptions removes non existent/accessible language codes from captions
|
||||
func CleanCaptions(ctx context.Context, f *file.VideoFile, txnMgr txn.Manager, w CaptionUpdater) error {
|
||||
func CleanCaptions(ctx context.Context, f *models.VideoFile, txnMgr txn.Manager, w CaptionUpdater) error {
|
||||
captions, err := w.GetCaptions(ctx, f.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting captions for file %s: %w", f.Path, err)
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/stashapp/stash/pkg/ffmpeg"
|
||||
"github.com/stashapp/stash/pkg/file"
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
)
|
||||
|
||||
// Decorator adds video specific fields to a File.
|
||||
@@ -14,7 +15,7 @@ type Decorator struct {
|
||||
FFProbe ffmpeg.FFProbe
|
||||
}
|
||||
|
||||
func (d *Decorator) Decorate(ctx context.Context, fs file.FS, f file.File) (file.File, error) {
|
||||
func (d *Decorator) Decorate(ctx context.Context, fs models.FS, f models.File) (models.File, error) {
|
||||
if d.FFProbe == "" {
|
||||
return f, errors.New("ffprobe not configured")
|
||||
}
|
||||
@@ -42,7 +43,7 @@ func (d *Decorator) Decorate(ctx context.Context, fs file.FS, f file.File) (file
|
||||
interactive = true
|
||||
}
|
||||
|
||||
return &file.VideoFile{
|
||||
return &models.VideoFile{
|
||||
BaseFile: base,
|
||||
Format: string(container),
|
||||
VideoCodec: videoFile.VideoCodec,
|
||||
@@ -56,13 +57,13 @@ func (d *Decorator) Decorate(ctx context.Context, fs file.FS, f file.File) (file
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *Decorator) IsMissingMetadata(ctx context.Context, fs file.FS, f file.File) bool {
|
||||
func (d *Decorator) IsMissingMetadata(ctx context.Context, fs models.FS, f models.File) bool {
|
||||
const (
|
||||
unsetString = "unset"
|
||||
unsetNumber = -1
|
||||
)
|
||||
|
||||
vf, ok := f.(*file.VideoFile)
|
||||
vf, ok := f.(*models.VideoFile)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
package file
|
||||
|
||||
// VideoFile is an extension of BaseFile to represent video files.
|
||||
type VideoFile struct {
|
||||
*BaseFile
|
||||
Format string `json:"format"`
|
||||
Width int `json:"width"`
|
||||
Height int `json:"height"`
|
||||
Duration float64 `json:"duration"`
|
||||
VideoCodec string `json:"video_codec"`
|
||||
AudioCodec string `json:"audio_codec"`
|
||||
FrameRate float64 `json:"frame_rate"`
|
||||
BitRate int64 `json:"bitrate"`
|
||||
|
||||
Interactive bool `json:"interactive"`
|
||||
InteractiveSpeed *int `json:"interactive_speed"`
|
||||
}
|
||||
|
||||
func (f VideoFile) GetWidth() int {
|
||||
return f.Width
|
||||
}
|
||||
|
||||
func (f VideoFile) GetHeight() int {
|
||||
return f.Height
|
||||
}
|
||||
|
||||
func (f VideoFile) GetFormat() string {
|
||||
return f.Format
|
||||
}
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
)
|
||||
|
||||
// Modified from github.com/facebookgo/symwalk
|
||||
@@ -48,7 +50,7 @@ import (
|
||||
//
|
||||
// Note that symwalk.Walk does not terminate if there are any non-terminating loops in
|
||||
// the file structure.
|
||||
func walkSym(f FS, filename string, linkDirname string, walkFn fs.WalkDirFunc) error {
|
||||
func walkSym(f models.FS, filename string, linkDirname string, walkFn fs.WalkDirFunc) error {
|
||||
symWalkFunc := func(path string, info fs.DirEntry, err error) error {
|
||||
|
||||
if fname, err := filepath.Rel(filename, path); err == nil {
|
||||
@@ -80,7 +82,7 @@ func walkSym(f FS, filename string, linkDirname string, walkFn fs.WalkDirFunc) e
|
||||
}
|
||||
|
||||
// symWalk extends filepath.Walk to also follow symlinks
|
||||
func symWalk(fs FS, path string, walkFn fs.WalkDirFunc) error {
|
||||
func symWalk(fs models.FS, path string, walkFn fs.WalkDirFunc) error {
|
||||
return walkSym(fs, path, path, walkFn)
|
||||
}
|
||||
|
||||
@@ -93,7 +95,7 @@ func (d *statDirEntry) IsDir() bool { return d.info.IsDir() }
|
||||
func (d *statDirEntry) Type() fs.FileMode { return d.info.Mode().Type() }
|
||||
func (d *statDirEntry) Info() (fs.FileInfo, error) { return d.info, nil }
|
||||
|
||||
func fsWalk(f FS, root string, fn fs.WalkDirFunc) error {
|
||||
func fsWalk(f models.FS, root string, fn fs.WalkDirFunc) error {
|
||||
info, err := f.Lstat(root)
|
||||
if err != nil {
|
||||
err = fn(root, nil, err)
|
||||
@@ -106,7 +108,7 @@ func fsWalk(f FS, root string, fn fs.WalkDirFunc) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func walkDir(f FS, path string, d fs.DirEntry, walkDirFn fs.WalkDirFunc) error {
|
||||
func walkDir(f models.FS, path string, d fs.DirEntry, walkDirFn fs.WalkDirFunc) error {
|
||||
if err := walkDirFn(path, d, nil); err != nil || !d.IsDir() {
|
||||
if errors.Is(err, fs.SkipDir) && d.IsDir() {
|
||||
// Successfully skipped directory.
|
||||
@@ -143,7 +145,7 @@ func walkDir(f FS, path string, d fs.DirEntry, walkDirFn fs.WalkDirFunc) error {
|
||||
|
||||
// readDir reads the directory named by dirname and returns
|
||||
// a sorted list of directory entries.
|
||||
func readDir(fs FS, dirname string) ([]fs.DirEntry, error) {
|
||||
func readDir(fs models.FS, dirname string) ([]fs.DirEntry, error) {
|
||||
f, err := fs.Open(dirname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/stashapp/stash/pkg/logger"
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
"github.com/xWTF/chardet"
|
||||
|
||||
"golang.org/x/net/html/charset"
|
||||
@@ -22,14 +23,14 @@ var (
|
||||
)
|
||||
|
||||
// ZipFS is a file system backed by a zip file.
|
||||
type ZipFS struct {
|
||||
type zipFS struct {
|
||||
*zip.Reader
|
||||
zipFileCloser io.Closer
|
||||
zipInfo fs.FileInfo
|
||||
zipPath string
|
||||
}
|
||||
|
||||
func newZipFS(fs FS, path string, info fs.FileInfo) (*ZipFS, error) {
|
||||
func newZipFS(fs models.FS, path string, info fs.FileInfo) (*zipFS, error) {
|
||||
reader, err := fs.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -85,7 +86,7 @@ func newZipFS(fs FS, path string, info fs.FileInfo) (*ZipFS, error) {
|
||||
}
|
||||
}
|
||||
|
||||
return &ZipFS{
|
||||
return &zipFS{
|
||||
Reader: zipReader,
|
||||
zipFileCloser: reader,
|
||||
zipInfo: info,
|
||||
@@ -93,7 +94,7 @@ func newZipFS(fs FS, path string, info fs.FileInfo) (*ZipFS, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (f *ZipFS) rel(name string) (string, error) {
|
||||
func (f *zipFS) rel(name string) (string, error) {
|
||||
if f.zipPath == name {
|
||||
return ".", nil
|
||||
}
|
||||
@@ -110,7 +111,7 @@ func (f *ZipFS) rel(name string) (string, error) {
|
||||
return relName, nil
|
||||
}
|
||||
|
||||
func (f *ZipFS) Stat(name string) (fs.FileInfo, error) {
|
||||
func (f *zipFS) Stat(name string) (fs.FileInfo, error) {
|
||||
reader, err := f.Open(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -120,15 +121,15 @@ func (f *ZipFS) Stat(name string) (fs.FileInfo, error) {
|
||||
return reader.Stat()
|
||||
}
|
||||
|
||||
func (f *ZipFS) Lstat(name string) (fs.FileInfo, error) {
|
||||
func (f *zipFS) Lstat(name string) (fs.FileInfo, error) {
|
||||
return f.Stat(name)
|
||||
}
|
||||
|
||||
func (f *ZipFS) OpenZip(name string) (*ZipFS, error) {
|
||||
func (f *zipFS) OpenZip(name string) (models.ZipFS, error) {
|
||||
return nil, errZipFSOpenZip
|
||||
}
|
||||
|
||||
func (f *ZipFS) IsPathCaseSensitive(path string) (bool, error) {
|
||||
func (f *zipFS) IsPathCaseSensitive(path string) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
@@ -145,7 +146,7 @@ func (f *zipReadDirFile) ReadDir(n int) ([]fs.DirEntry, error) {
|
||||
return asReadDirFile.ReadDir(n)
|
||||
}
|
||||
|
||||
func (f *ZipFS) Open(name string) (fs.ReadDirFile, error) {
|
||||
func (f *zipFS) Open(name string) (fs.ReadDirFile, error) {
|
||||
relName, err := f.rel(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -161,12 +162,12 @@ func (f *ZipFS) Open(name string) (fs.ReadDirFile, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (f *ZipFS) Close() error {
|
||||
func (f *zipFS) Close() error {
|
||||
return f.zipFileCloser.Close()
|
||||
}
|
||||
|
||||
// openOnly returns a ReadCloser where calling Close will close the zip fs as well.
|
||||
func (f *ZipFS) OpenOnly(name string) (io.ReadCloser, error) {
|
||||
func (f *zipFS) OpenOnly(name string) (io.ReadCloser, error) {
|
||||
r, err := f.Open(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -8,15 +8,14 @@ import (
|
||||
"github.com/stashapp/stash/pkg/models/jsonschema"
|
||||
)
|
||||
|
||||
type ChapterCreatorUpdater interface {
|
||||
Create(ctx context.Context, newGalleryChapter *models.GalleryChapter) error
|
||||
Update(ctx context.Context, updatedGalleryChapter *models.GalleryChapter) error
|
||||
type ChapterImporterReaderWriter interface {
|
||||
models.GalleryChapterCreatorUpdater
|
||||
FindByGalleryID(ctx context.Context, galleryID int) ([]*models.GalleryChapter, error)
|
||||
}
|
||||
|
||||
type ChapterImporter struct {
|
||||
GalleryID int
|
||||
ReaderWriter ChapterCreatorUpdater
|
||||
ReaderWriter ChapterImporterReaderWriter
|
||||
Input jsonschema.GalleryChapter
|
||||
MissingRefBehaviour models.ImportMissingRefEnum
|
||||
|
||||
|
||||
@@ -41,12 +41,7 @@ func (s *Service) Destroy(ctx context.Context, i *models.Gallery, fileDeleter *i
|
||||
return imgsDestroyed, nil
|
||||
}
|
||||
|
||||
type ChapterDestroyer interface {
|
||||
FindByGalleryID(ctx context.Context, galleryID int) ([]*models.GalleryChapter, error)
|
||||
Destroy(ctx context.Context, id int) error
|
||||
}
|
||||
|
||||
func DestroyChapter(ctx context.Context, galleryChapter *models.GalleryChapter, qb ChapterDestroyer) error {
|
||||
func DestroyChapter(ctx context.Context, galleryChapter *models.GalleryChapter, qb models.GalleryChapterDestroyer) error {
|
||||
return qb.Destroy(ctx, galleryChapter.ID)
|
||||
}
|
||||
|
||||
|
||||
@@ -7,13 +7,8 @@ import (
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
"github.com/stashapp/stash/pkg/models/json"
|
||||
"github.com/stashapp/stash/pkg/models/jsonschema"
|
||||
"github.com/stashapp/stash/pkg/studio"
|
||||
)
|
||||
|
||||
type ChapterFinder interface {
|
||||
FindByGalleryID(ctx context.Context, galleryID int) ([]*models.GalleryChapter, error)
|
||||
}
|
||||
|
||||
// ToBasicJSON converts a gallery object into its JSON object equivalent. It
|
||||
// does not convert the relationships to other objects.
|
||||
func ToBasicJSON(gallery *models.Gallery) (*jsonschema.Gallery, error) {
|
||||
@@ -48,7 +43,7 @@ func ToBasicJSON(gallery *models.Gallery) (*jsonschema.Gallery, error) {
|
||||
|
||||
// GetStudioName returns the name of the provided gallery's studio. It returns an
|
||||
// empty string if there is no studio assigned to the gallery.
|
||||
func GetStudioName(ctx context.Context, reader studio.Finder, gallery *models.Gallery) (string, error) {
|
||||
func GetStudioName(ctx context.Context, reader models.StudioGetter, gallery *models.Gallery) (string, error) {
|
||||
if gallery.StudioID != nil {
|
||||
studio, err := reader.Find(ctx, *gallery.StudioID)
|
||||
if err != nil {
|
||||
@@ -65,7 +60,7 @@ func GetStudioName(ctx context.Context, reader studio.Finder, gallery *models.Ga
|
||||
|
||||
// GetGalleryChaptersJSON returns a slice of GalleryChapter JSON representation
|
||||
// objects corresponding to the provided gallery's chapters.
|
||||
func GetGalleryChaptersJSON(ctx context.Context, chapterReader ChapterFinder, gallery *models.Gallery) ([]jsonschema.GalleryChapter, error) {
|
||||
func GetGalleryChaptersJSON(ctx context.Context, chapterReader models.GalleryChapterFinder, gallery *models.Gallery) ([]jsonschema.GalleryChapter, error) {
|
||||
galleryChapters, err := chapterReader.FindByGalleryID(ctx, gallery.ID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting gallery chapters: %v", err)
|
||||
|
||||
@@ -3,7 +3,6 @@ package gallery
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/stashapp/stash/pkg/file"
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
"github.com/stashapp/stash/pkg/models/json"
|
||||
"github.com/stashapp/stash/pkg/models/jsonschema"
|
||||
@@ -50,8 +49,8 @@ var (
|
||||
func createFullGallery(id int) models.Gallery {
|
||||
return models.Gallery{
|
||||
ID: id,
|
||||
Files: models.NewRelatedFiles([]file.File{
|
||||
&file.BaseFile{
|
||||
Files: models.NewRelatedFiles([]models.File{
|
||||
&models.BaseFile{
|
||||
Path: path,
|
||||
},
|
||||
}),
|
||||
@@ -69,8 +68,8 @@ func createFullGallery(id int) models.Gallery {
|
||||
func createEmptyGallery(id int) models.Gallery {
|
||||
return models.Gallery{
|
||||
ID: id,
|
||||
Files: models.NewRelatedFiles([]file.File{
|
||||
&file.BaseFile{
|
||||
Files: models.NewRelatedFiles([]models.File{
|
||||
&models.BaseFile{
|
||||
Path: path,
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -5,22 +5,25 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/stashapp/stash/pkg/file"
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
"github.com/stashapp/stash/pkg/models/jsonschema"
|
||||
"github.com/stashapp/stash/pkg/performer"
|
||||
"github.com/stashapp/stash/pkg/sliceutil/stringslice"
|
||||
"github.com/stashapp/stash/pkg/studio"
|
||||
"github.com/stashapp/stash/pkg/tag"
|
||||
)
|
||||
|
||||
type ImporterReaderWriter interface {
|
||||
models.GalleryCreatorUpdater
|
||||
FindByFileID(ctx context.Context, fileID models.FileID) ([]*models.Gallery, error)
|
||||
FindByFolderID(ctx context.Context, folderID models.FolderID) ([]*models.Gallery, error)
|
||||
FindUserGalleryByTitle(ctx context.Context, title string) ([]*models.Gallery, error)
|
||||
}
|
||||
|
||||
type Importer struct {
|
||||
ReaderWriter FullCreatorUpdater
|
||||
StudioWriter studio.NameFinderCreator
|
||||
PerformerWriter performer.NameFinderCreator
|
||||
TagWriter tag.NameFinderCreator
|
||||
FileFinder file.Getter
|
||||
FolderFinder file.FolderGetter
|
||||
ReaderWriter ImporterReaderWriter
|
||||
StudioWriter models.StudioFinderCreator
|
||||
PerformerWriter models.PerformerFinderCreator
|
||||
TagWriter models.TagFinderCreator
|
||||
FileFinder models.FileFinder
|
||||
FolderFinder models.FolderFinder
|
||||
Input jsonschema.Gallery
|
||||
MissingRefBehaviour models.ImportMissingRefEnum
|
||||
|
||||
@@ -28,11 +31,6 @@ type Importer struct {
|
||||
gallery models.Gallery
|
||||
}
|
||||
|
||||
type FullCreatorUpdater interface {
|
||||
FinderCreatorUpdater
|
||||
Update(ctx context.Context, updatedGallery *models.Gallery) error
|
||||
}
|
||||
|
||||
func (i *Importer) PreImport(ctx context.Context) error {
|
||||
i.gallery = i.galleryJSONToGallery(i.Input)
|
||||
|
||||
@@ -251,7 +249,7 @@ func (i *Importer) createTags(ctx context.Context, names []string) ([]*models.Ta
|
||||
}
|
||||
|
||||
func (i *Importer) populateFilesFolder(ctx context.Context) error {
|
||||
files := make([]file.File, 0)
|
||||
files := make([]models.File, 0)
|
||||
|
||||
for _, ref := range i.Input.ZipFiles {
|
||||
path := ref
|
||||
@@ -340,7 +338,7 @@ func (i *Importer) FindExistingID(ctx context.Context) (*int, error) {
|
||||
}
|
||||
|
||||
func (i *Importer) Create(ctx context.Context) (*int, error) {
|
||||
var fileIDs []file.ID
|
||||
var fileIDs []models.FileID
|
||||
for _, f := range i.gallery.Files.List() {
|
||||
fileIDs = append(fileIDs, f.Base().ID)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stashapp/stash/pkg/file"
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
"github.com/stashapp/stash/pkg/models/json"
|
||||
"github.com/stashapp/stash/pkg/models/jsonschema"
|
||||
@@ -68,7 +67,7 @@ func TestImporterPreImport(t *testing.T) {
|
||||
Rating: &rating,
|
||||
Organized: organized,
|
||||
URL: url,
|
||||
Files: models.NewRelatedFiles([]file.File{}),
|
||||
Files: models.NewRelatedFiles([]models.File{}),
|
||||
TagIDs: models.NewRelatedIDs([]int{}),
|
||||
PerformerIDs: models.NewRelatedIDs([]int{}),
|
||||
CreatedAt: createdAt,
|
||||
|
||||
@@ -4,27 +4,10 @@ import (
|
||||
"context"
|
||||
"strconv"
|
||||
|
||||
"github.com/stashapp/stash/pkg/file"
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
)
|
||||
|
||||
type Queryer interface {
|
||||
Query(ctx context.Context, galleryFilter *models.GalleryFilterType, findFilter *models.FindFilterType) ([]*models.Gallery, int, error)
|
||||
}
|
||||
|
||||
type CountQueryer interface {
|
||||
QueryCount(ctx context.Context, galleryFilter *models.GalleryFilterType, findFilter *models.FindFilterType) (int, error)
|
||||
}
|
||||
|
||||
type Finder interface {
|
||||
FindByPath(ctx context.Context, p string) ([]*models.Gallery, error)
|
||||
FindUserGalleryByTitle(ctx context.Context, title string) ([]*models.Gallery, error)
|
||||
FindByFolderID(ctx context.Context, folderID file.FolderID) ([]*models.Gallery, error)
|
||||
FindByFileID(ctx context.Context, fileID file.ID) ([]*models.Gallery, error)
|
||||
FindByFingerprints(ctx context.Context, fp []file.Fingerprint) ([]*models.Gallery, error)
|
||||
}
|
||||
|
||||
func CountByPerformerID(ctx context.Context, r CountQueryer, id int) (int, error) {
|
||||
func CountByPerformerID(ctx context.Context, r models.GalleryQueryer, id int) (int, error) {
|
||||
filter := &models.GalleryFilterType{
|
||||
Performers: &models.MultiCriterionInput{
|
||||
Value: []string{strconv.Itoa(id)},
|
||||
@@ -35,7 +18,7 @@ func CountByPerformerID(ctx context.Context, r CountQueryer, id int) (int, error
|
||||
return r.QueryCount(ctx, filter, nil)
|
||||
}
|
||||
|
||||
func CountByStudioID(ctx context.Context, r CountQueryer, id int, depth *int) (int, error) {
|
||||
func CountByStudioID(ctx context.Context, r models.GalleryQueryer, id int, depth *int) (int, error) {
|
||||
filter := &models.GalleryFilterType{
|
||||
Studios: &models.HierarchicalMultiCriterionInput{
|
||||
Value: []string{strconv.Itoa(id)},
|
||||
@@ -47,7 +30,7 @@ func CountByStudioID(ctx context.Context, r CountQueryer, id int, depth *int) (i
|
||||
return r.QueryCount(ctx, filter, nil)
|
||||
}
|
||||
|
||||
func CountByTagID(ctx context.Context, r CountQueryer, id int, depth *int) (int, error) {
|
||||
func CountByTagID(ctx context.Context, r models.GalleryQueryer, id int, depth *int) (int, error) {
|
||||
filter := &models.GalleryFilterType{
|
||||
Tags: &models.HierarchicalMultiCriterionInput{
|
||||
Value: []string{strconv.Itoa(id)},
|
||||
|
||||
@@ -7,39 +7,40 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/stashapp/stash/pkg/file"
|
||||
"github.com/stashapp/stash/pkg/logger"
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
"github.com/stashapp/stash/pkg/plugin"
|
||||
)
|
||||
|
||||
type FinderCreatorUpdater interface {
|
||||
Finder
|
||||
Create(ctx context.Context, newGallery *models.Gallery, fileIDs []file.ID) error
|
||||
type ScanCreatorUpdater interface {
|
||||
FindByFileID(ctx context.Context, fileID models.FileID) ([]*models.Gallery, error)
|
||||
FindByFingerprints(ctx context.Context, fp []models.Fingerprint) ([]*models.Gallery, error)
|
||||
GetFiles(ctx context.Context, relatedID int) ([]models.File, error)
|
||||
|
||||
Create(ctx context.Context, newGallery *models.Gallery, fileIDs []models.FileID) error
|
||||
UpdatePartial(ctx context.Context, id int, updatedGallery models.GalleryPartial) (*models.Gallery, error)
|
||||
AddFileID(ctx context.Context, id int, fileID file.ID) error
|
||||
models.FileLoader
|
||||
AddFileID(ctx context.Context, id int, fileID models.FileID) error
|
||||
}
|
||||
|
||||
type SceneFinderUpdater interface {
|
||||
type ScanSceneFinderUpdater interface {
|
||||
FindByPath(ctx context.Context, p string) ([]*models.Scene, error)
|
||||
Update(ctx context.Context, updatedScene *models.Scene) error
|
||||
AddGalleryIDs(ctx context.Context, sceneID int, galleryIDs []int) error
|
||||
}
|
||||
|
||||
type ImageFinderUpdater interface {
|
||||
FindByZipFileID(ctx context.Context, zipFileID file.ID) ([]*models.Image, error)
|
||||
type ScanImageFinderUpdater interface {
|
||||
FindByZipFileID(ctx context.Context, zipFileID models.FileID) ([]*models.Image, error)
|
||||
UpdatePartial(ctx context.Context, id int, partial models.ImagePartial) (*models.Image, error)
|
||||
}
|
||||
|
||||
type ScanHandler struct {
|
||||
CreatorUpdater FullCreatorUpdater
|
||||
SceneFinderUpdater SceneFinderUpdater
|
||||
ImageFinderUpdater ImageFinderUpdater
|
||||
CreatorUpdater ScanCreatorUpdater
|
||||
SceneFinderUpdater ScanSceneFinderUpdater
|
||||
ImageFinderUpdater ScanImageFinderUpdater
|
||||
PluginCache *plugin.Cache
|
||||
}
|
||||
|
||||
func (h *ScanHandler) Handle(ctx context.Context, f file.File, oldFile file.File) error {
|
||||
func (h *ScanHandler) Handle(ctx context.Context, f models.File, oldFile models.File) error {
|
||||
baseFile := f.Base()
|
||||
|
||||
// try to match the file to a gallery
|
||||
@@ -83,7 +84,7 @@ func (h *ScanHandler) Handle(ctx context.Context, f file.File, oldFile file.File
|
||||
|
||||
logger.Infof("%s doesn't exist. Creating new gallery...", f.Base().Path)
|
||||
|
||||
if err := h.CreatorUpdater.Create(ctx, newGallery, []file.ID{baseFile.ID}); err != nil {
|
||||
if err := h.CreatorUpdater.Create(ctx, newGallery, []models.FileID{baseFile.ID}); err != nil {
|
||||
return fmt.Errorf("creating new gallery: %w", err)
|
||||
}
|
||||
|
||||
@@ -112,7 +113,7 @@ func (h *ScanHandler) Handle(ctx context.Context, f file.File, oldFile file.File
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *ScanHandler) associateExisting(ctx context.Context, existing []*models.Gallery, f file.File, updateExisting bool) error {
|
||||
func (h *ScanHandler) associateExisting(ctx context.Context, existing []*models.Gallery, f models.File, updateExisting bool) error {
|
||||
for _, i := range existing {
|
||||
if err := i.LoadFiles(ctx, h.CreatorUpdater); err != nil {
|
||||
return err
|
||||
@@ -146,7 +147,7 @@ func (h *ScanHandler) associateExisting(ctx context.Context, existing []*models.
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *ScanHandler) associateScene(ctx context.Context, existing []*models.Gallery, f file.File) error {
|
||||
func (h *ScanHandler) associateScene(ctx context.Context, existing []*models.Gallery, f models.File) error {
|
||||
galleryIDs := make([]int, len(existing))
|
||||
for i, g := range existing {
|
||||
galleryIDs[i] = g.ID
|
||||
|
||||
@@ -3,50 +3,25 @@ package gallery
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/stashapp/stash/pkg/file"
|
||||
"github.com/stashapp/stash/pkg/image"
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
)
|
||||
|
||||
type FinderByFile interface {
|
||||
FindByFileID(ctx context.Context, fileID file.ID) ([]*models.Gallery, error)
|
||||
}
|
||||
|
||||
type Repository interface {
|
||||
models.GalleryFinder
|
||||
FinderByFile
|
||||
Destroy(ctx context.Context, id int) error
|
||||
models.FileLoader
|
||||
ImageUpdater
|
||||
PartialUpdater
|
||||
}
|
||||
|
||||
type PartialUpdater interface {
|
||||
UpdatePartial(ctx context.Context, id int, updatedGallery models.GalleryPartial) (*models.Gallery, error)
|
||||
}
|
||||
|
||||
type ImageFinder interface {
|
||||
FindByFolderID(ctx context.Context, folder file.FolderID) ([]*models.Image, error)
|
||||
FindByZipFileID(ctx context.Context, zipFileID file.ID) ([]*models.Image, error)
|
||||
FindByFolderID(ctx context.Context, folder models.FolderID) ([]*models.Image, error)
|
||||
FindByZipFileID(ctx context.Context, zipFileID models.FileID) ([]*models.Image, error)
|
||||
models.GalleryIDLoader
|
||||
}
|
||||
|
||||
type ImageService interface {
|
||||
Destroy(ctx context.Context, i *models.Image, fileDeleter *image.FileDeleter, deleteGenerated, deleteFile bool) error
|
||||
DestroyZipImages(ctx context.Context, zipFile file.File, fileDeleter *image.FileDeleter, deleteGenerated bool) ([]*models.Image, error)
|
||||
}
|
||||
|
||||
type ChapterRepository interface {
|
||||
ChapterFinder
|
||||
ChapterDestroyer
|
||||
|
||||
Update(ctx context.Context, updatedObject models.GalleryChapter) (*models.GalleryChapter, error)
|
||||
DestroyZipImages(ctx context.Context, zipFile models.File, fileDeleter *image.FileDeleter, deleteGenerated bool) ([]*models.Image, error)
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
Repository Repository
|
||||
Repository models.GalleryReaderWriter
|
||||
ImageFinder ImageFinder
|
||||
ImageService ImageService
|
||||
File file.Store
|
||||
Folder file.FolderStore
|
||||
File models.FileReaderWriter
|
||||
Folder models.FolderReaderWriter
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ func (s *Service) RemoveImages(ctx context.Context, g *models.Gallery, toRemove
|
||||
return s.Updated(ctx, g.ID)
|
||||
}
|
||||
|
||||
func AddPerformer(ctx context.Context, qb PartialUpdater, o *models.Gallery, performerID int) error {
|
||||
func AddPerformer(ctx context.Context, qb models.GalleryUpdater, o *models.Gallery, performerID int) error {
|
||||
_, err := qb.UpdatePartial(ctx, o.ID, models.GalleryPartial{
|
||||
PerformerIDs: &models.UpdateIDs{
|
||||
IDs: []int{performerID},
|
||||
@@ -64,7 +64,7 @@ func AddPerformer(ctx context.Context, qb PartialUpdater, o *models.Gallery, per
|
||||
return err
|
||||
}
|
||||
|
||||
func AddTag(ctx context.Context, qb PartialUpdater, o *models.Gallery, tagID int) error {
|
||||
func AddTag(ctx context.Context, qb models.GalleryUpdater, o *models.Gallery, tagID int) error {
|
||||
_, err := qb.UpdatePartial(ctx, o.ID, models.GalleryPartial{
|
||||
TagIDs: &models.UpdateIDs{
|
||||
IDs: []int{tagID},
|
||||
|
||||
@@ -13,8 +13,8 @@ import (
|
||||
|
||||
"github.com/stashapp/stash/pkg/ffmpeg"
|
||||
"github.com/stashapp/stash/pkg/ffmpeg/transcoder"
|
||||
"github.com/stashapp/stash/pkg/file"
|
||||
"github.com/stashapp/stash/pkg/logger"
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -23,7 +23,7 @@ const (
|
||||
rows = 5
|
||||
)
|
||||
|
||||
func Generate(encoder *ffmpeg.FFMpeg, videoFile *file.VideoFile) (*uint64, error) {
|
||||
func Generate(encoder *ffmpeg.FFMpeg, videoFile *models.VideoFile) (*uint64, error) {
|
||||
sprite, err := generateSprite(encoder, videoFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -76,7 +76,7 @@ func combineImages(images []image.Image) image.Image {
|
||||
return montage
|
||||
}
|
||||
|
||||
func generateSprite(encoder *ffmpeg.FFMpeg, videoFile *file.VideoFile) (image.Image, error) {
|
||||
func generateSprite(encoder *ffmpeg.FFMpeg, videoFile *models.VideoFile) (image.Image, error) {
|
||||
logger.Infof("[generator] generating phash sprite for %s", videoFile.Path)
|
||||
|
||||
// Generate sprite image offset by 5% on each end to avoid intro/outros
|
||||
|
||||
@@ -10,10 +10,6 @@ import (
|
||||
"github.com/stashapp/stash/pkg/models/paths"
|
||||
)
|
||||
|
||||
type Destroyer interface {
|
||||
Destroy(ctx context.Context, id int) error
|
||||
}
|
||||
|
||||
// FileDeleter is an extension of file.Deleter that handles deletion of image files.
|
||||
type FileDeleter struct {
|
||||
*file.Deleter
|
||||
@@ -45,7 +41,7 @@ func (s *Service) Destroy(ctx context.Context, i *models.Image, fileDeleter *Fil
|
||||
|
||||
// DestroyZipImages destroys all images in zip, optionally marking the files and generated files for deletion.
|
||||
// Returns a slice of images that were destroyed.
|
||||
func (s *Service) DestroyZipImages(ctx context.Context, zipFile file.File, fileDeleter *FileDeleter, deleteGenerated bool) ([]*models.Image, error) {
|
||||
func (s *Service) DestroyZipImages(ctx context.Context, zipFile models.File, fileDeleter *FileDeleter, deleteGenerated bool) ([]*models.Image, error) {
|
||||
var imgsDestroyed []*models.Image
|
||||
|
||||
imgs, err := s.Repository.FindByZipFileID(ctx, zipFile.Base().ID)
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
"github.com/stashapp/stash/pkg/models/json"
|
||||
"github.com/stashapp/stash/pkg/models/jsonschema"
|
||||
"github.com/stashapp/stash/pkg/studio"
|
||||
)
|
||||
|
||||
// ToBasicJSON converts a image object into its JSON object equivalent. It
|
||||
@@ -53,7 +52,7 @@ func ToBasicJSON(image *models.Image) *jsonschema.Image {
|
||||
|
||||
// GetStudioName returns the name of the provided image's studio. It returns an
|
||||
// empty string if there is no studio assigned to the image.
|
||||
func GetStudioName(ctx context.Context, reader studio.Finder, image *models.Image) (string, error) {
|
||||
func GetStudioName(ctx context.Context, reader models.StudioGetter, image *models.Image) (string, error) {
|
||||
if image.StudioID != nil {
|
||||
studio, err := reader.Find(ctx, *image.StudioID)
|
||||
if err != nil {
|
||||
|
||||
@@ -3,7 +3,6 @@ package image
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/stashapp/stash/pkg/file"
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
"github.com/stashapp/stash/pkg/models/json"
|
||||
"github.com/stashapp/stash/pkg/models/jsonschema"
|
||||
@@ -45,8 +44,8 @@ var (
|
||||
func createFullImage(id int) models.Image {
|
||||
return models.Image{
|
||||
ID: id,
|
||||
Files: models.NewRelatedFiles([]file.File{
|
||||
&file.BaseFile{
|
||||
Files: models.NewRelatedFiles([]models.File{
|
||||
&models.BaseFile{
|
||||
Path: path,
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -5,13 +5,9 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/stashapp/stash/pkg/file"
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
"github.com/stashapp/stash/pkg/models/jsonschema"
|
||||
"github.com/stashapp/stash/pkg/performer"
|
||||
"github.com/stashapp/stash/pkg/sliceutil/stringslice"
|
||||
"github.com/stashapp/stash/pkg/studio"
|
||||
"github.com/stashapp/stash/pkg/tag"
|
||||
)
|
||||
|
||||
type GalleryFinder interface {
|
||||
@@ -19,18 +15,18 @@ type GalleryFinder interface {
|
||||
FindUserGalleryByTitle(ctx context.Context, title string) ([]*models.Gallery, error)
|
||||
}
|
||||
|
||||
type FullCreatorUpdater interface {
|
||||
FinderCreatorUpdater
|
||||
Update(ctx context.Context, updatedImage *models.Image) error
|
||||
type ImporterReaderWriter interface {
|
||||
models.ImageCreatorUpdater
|
||||
FindByFileID(ctx context.Context, fileID models.FileID) ([]*models.Image, error)
|
||||
}
|
||||
|
||||
type Importer struct {
|
||||
ReaderWriter FullCreatorUpdater
|
||||
FileFinder file.Getter
|
||||
StudioWriter studio.NameFinderCreator
|
||||
ReaderWriter ImporterReaderWriter
|
||||
FileFinder models.FileFinder
|
||||
StudioWriter models.StudioFinderCreator
|
||||
GalleryFinder GalleryFinder
|
||||
PerformerWriter performer.NameFinderCreator
|
||||
TagWriter tag.NameFinderCreator
|
||||
PerformerWriter models.PerformerFinderCreator
|
||||
TagWriter models.TagFinderCreator
|
||||
Input jsonschema.Image
|
||||
MissingRefBehaviour models.ImportMissingRefEnum
|
||||
|
||||
@@ -99,7 +95,7 @@ func (i *Importer) imageJSONToImage(imageJSON jsonschema.Image) models.Image {
|
||||
}
|
||||
|
||||
func (i *Importer) populateFiles(ctx context.Context) error {
|
||||
files := make([]file.File, 0)
|
||||
files := make([]models.File, 0)
|
||||
|
||||
for _, ref := range i.Input.Files {
|
||||
path := ref
|
||||
@@ -330,7 +326,7 @@ func (i *Importer) FindExistingID(ctx context.Context) (*int, error) {
|
||||
}
|
||||
|
||||
func (i *Importer) Create(ctx context.Context) (*int, error) {
|
||||
var fileIDs []file.ID
|
||||
var fileIDs []models.FileID
|
||||
for _, f := range i.image.Files.List() {
|
||||
fileIDs = append(fileIDs, f.Base().ID)
|
||||
}
|
||||
@@ -360,7 +356,7 @@ func (i *Importer) Update(ctx context.Context, id int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func importTags(ctx context.Context, tagWriter tag.NameFinderCreator, names []string, missingRefBehaviour models.ImportMissingRefEnum) ([]*models.Tag, error) {
|
||||
func importTags(ctx context.Context, tagWriter models.TagFinderCreator, names []string, missingRefBehaviour models.ImportMissingRefEnum) ([]*models.Tag, error) {
|
||||
tags, err := tagWriter.FindByNames(ctx, names, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -395,7 +391,7 @@ func importTags(ctx context.Context, tagWriter tag.NameFinderCreator, names []st
|
||||
return tags, nil
|
||||
}
|
||||
|
||||
func createTags(ctx context.Context, tagWriter tag.NameFinderCreator, names []string) ([]*models.Tag, error) {
|
||||
func createTags(ctx context.Context, tagWriter models.TagCreator, names []string) ([]*models.Tag, error) {
|
||||
var ret []*models.Tag
|
||||
for _, name := range names {
|
||||
newTag := models.NewTag(name)
|
||||
|
||||
@@ -7,14 +7,6 @@ import (
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
)
|
||||
|
||||
type Queryer interface {
|
||||
Query(ctx context.Context, options models.ImageQueryOptions) (*models.ImageQueryResult, error)
|
||||
}
|
||||
|
||||
type CountQueryer interface {
|
||||
QueryCount(ctx context.Context, imageFilter *models.ImageFilterType, findFilter *models.FindFilterType) (int, error)
|
||||
}
|
||||
|
||||
// QueryOptions returns a ImageQueryResult populated with the provided filters.
|
||||
func QueryOptions(imageFilter *models.ImageFilterType, findFilter *models.FindFilterType, count bool) models.ImageQueryOptions {
|
||||
return models.ImageQueryOptions{
|
||||
@@ -27,7 +19,7 @@ func QueryOptions(imageFilter *models.ImageFilterType, findFilter *models.FindFi
|
||||
}
|
||||
|
||||
// Query queries for images using the provided filters.
|
||||
func Query(ctx context.Context, qb Queryer, imageFilter *models.ImageFilterType, findFilter *models.FindFilterType) ([]*models.Image, error) {
|
||||
func Query(ctx context.Context, qb models.ImageQueryer, imageFilter *models.ImageFilterType, findFilter *models.FindFilterType) ([]*models.Image, error) {
|
||||
result, err := qb.Query(ctx, QueryOptions(imageFilter, findFilter, false))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -41,7 +33,7 @@ func Query(ctx context.Context, qb Queryer, imageFilter *models.ImageFilterType,
|
||||
return images, nil
|
||||
}
|
||||
|
||||
func CountByPerformerID(ctx context.Context, r CountQueryer, id int) (int, error) {
|
||||
func CountByPerformerID(ctx context.Context, r models.ImageQueryer, id int) (int, error) {
|
||||
filter := &models.ImageFilterType{
|
||||
Performers: &models.MultiCriterionInput{
|
||||
Value: []string{strconv.Itoa(id)},
|
||||
@@ -52,7 +44,7 @@ func CountByPerformerID(ctx context.Context, r CountQueryer, id int) (int, error
|
||||
return r.QueryCount(ctx, filter, nil)
|
||||
}
|
||||
|
||||
func CountByStudioID(ctx context.Context, r CountQueryer, id int, depth *int) (int, error) {
|
||||
func CountByStudioID(ctx context.Context, r models.ImageQueryer, id int, depth *int) (int, error) {
|
||||
filter := &models.ImageFilterType{
|
||||
Studios: &models.HierarchicalMultiCriterionInput{
|
||||
Value: []string{strconv.Itoa(id)},
|
||||
@@ -64,7 +56,7 @@ func CountByStudioID(ctx context.Context, r CountQueryer, id int, depth *int) (i
|
||||
return r.QueryCount(ctx, filter, nil)
|
||||
}
|
||||
|
||||
func CountByTagID(ctx context.Context, r CountQueryer, id int, depth *int) (int, error) {
|
||||
func CountByTagID(ctx context.Context, r models.ImageQueryer, id int, depth *int) (int, error) {
|
||||
filter := &models.ImageFilterType{
|
||||
Tags: &models.HierarchicalMultiCriterionInput{
|
||||
Value: []string{strconv.Itoa(id)},
|
||||
@@ -76,7 +68,7 @@ func CountByTagID(ctx context.Context, r CountQueryer, id int, depth *int) (int,
|
||||
return r.QueryCount(ctx, filter, nil)
|
||||
}
|
||||
|
||||
func FindByGalleryID(ctx context.Context, r Queryer, galleryID int, sortBy string, sortDir models.SortDirectionEnum) ([]*models.Image, error) {
|
||||
func FindByGalleryID(ctx context.Context, r models.ImageQueryer, galleryID int, sortBy string, sortDir models.SortDirectionEnum) ([]*models.Image, error) {
|
||||
perPage := -1
|
||||
|
||||
findFilter := models.FindFilterType{
|
||||
@@ -99,7 +91,7 @@ func FindByGalleryID(ctx context.Context, r Queryer, galleryID int, sortBy strin
|
||||
}, &findFilter)
|
||||
}
|
||||
|
||||
func FindGalleryCover(ctx context.Context, r Queryer, galleryID int, galleryCoverRegex string) (*models.Image, error) {
|
||||
func FindGalleryCover(ctx context.Context, r models.ImageQueryer, galleryID int, galleryCoverRegex string) (*models.Image, error) {
|
||||
const useCoverJpg = true
|
||||
img, err := findGalleryCover(ctx, r, galleryID, useCoverJpg, galleryCoverRegex)
|
||||
if err != nil {
|
||||
@@ -114,7 +106,7 @@ func FindGalleryCover(ctx context.Context, r Queryer, galleryID int, galleryCove
|
||||
return findGalleryCover(ctx, r, galleryID, !useCoverJpg, galleryCoverRegex)
|
||||
}
|
||||
|
||||
func findGalleryCover(ctx context.Context, r Queryer, galleryID int, useCoverJpg bool, galleryCoverRegex string) (*models.Image, error) {
|
||||
func findGalleryCover(ctx context.Context, r models.ImageQueryer, galleryID int, useCoverJpg bool, galleryCoverRegex string) (*models.Image, error) {
|
||||
// try to find cover.jpg in the gallery
|
||||
perPage := 1
|
||||
sortBy := "path"
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/stashapp/stash/pkg/file"
|
||||
"github.com/stashapp/stash/pkg/logger"
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
"github.com/stashapp/stash/pkg/models/paths"
|
||||
@@ -21,21 +20,22 @@ var (
|
||||
ErrNotImageFile = errors.New("not an image file")
|
||||
)
|
||||
|
||||
type FinderCreatorUpdater interface {
|
||||
FindByFileID(ctx context.Context, fileID file.ID) ([]*models.Image, error)
|
||||
FindByFolderID(ctx context.Context, folderID file.FolderID) ([]*models.Image, error)
|
||||
FindByFingerprints(ctx context.Context, fp []file.Fingerprint) ([]*models.Image, error)
|
||||
type ScanCreatorUpdater interface {
|
||||
FindByFileID(ctx context.Context, fileID models.FileID) ([]*models.Image, error)
|
||||
FindByFolderID(ctx context.Context, folderID models.FolderID) ([]*models.Image, error)
|
||||
FindByFingerprints(ctx context.Context, fp []models.Fingerprint) ([]*models.Image, error)
|
||||
GetFiles(ctx context.Context, relatedID int) ([]models.File, error)
|
||||
GetGalleryIDs(ctx context.Context, relatedID int) ([]int, error)
|
||||
|
||||
Create(ctx context.Context, newImage *models.ImageCreateInput) error
|
||||
UpdatePartial(ctx context.Context, id int, updatedImage models.ImagePartial) (*models.Image, error)
|
||||
AddFileID(ctx context.Context, id int, fileID file.ID) error
|
||||
models.GalleryIDLoader
|
||||
models.FileLoader
|
||||
AddFileID(ctx context.Context, id int, fileID models.FileID) error
|
||||
}
|
||||
|
||||
type GalleryFinderCreator interface {
|
||||
FindByFileID(ctx context.Context, fileID file.ID) ([]*models.Gallery, error)
|
||||
FindByFolderID(ctx context.Context, folderID file.FolderID) ([]*models.Gallery, error)
|
||||
Create(ctx context.Context, newObject *models.Gallery, fileIDs []file.ID) error
|
||||
FindByFileID(ctx context.Context, fileID models.FileID) ([]*models.Gallery, error)
|
||||
FindByFolderID(ctx context.Context, folderID models.FolderID) ([]*models.Gallery, error)
|
||||
Create(ctx context.Context, newObject *models.Gallery, fileIDs []models.FileID) error
|
||||
UpdatePartial(ctx context.Context, id int, updatedGallery models.GalleryPartial) (*models.Gallery, error)
|
||||
}
|
||||
|
||||
@@ -44,11 +44,11 @@ type ScanConfig interface {
|
||||
}
|
||||
|
||||
type ScanGenerator interface {
|
||||
Generate(ctx context.Context, i *models.Image, f file.File) error
|
||||
Generate(ctx context.Context, i *models.Image, f models.File) error
|
||||
}
|
||||
|
||||
type ScanHandler struct {
|
||||
CreatorUpdater FinderCreatorUpdater
|
||||
CreatorUpdater ScanCreatorUpdater
|
||||
GalleryFinder GalleryFinderCreator
|
||||
|
||||
ScanGenerator ScanGenerator
|
||||
@@ -80,7 +80,7 @@ func (h *ScanHandler) validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *ScanHandler) Handle(ctx context.Context, f file.File, oldFile file.File) error {
|
||||
func (h *ScanHandler) Handle(ctx context.Context, f models.File, oldFile models.File) error {
|
||||
if err := h.validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -130,7 +130,7 @@ func (h *ScanHandler) Handle(ctx context.Context, f file.File, oldFile file.File
|
||||
|
||||
if err := h.CreatorUpdater.Create(ctx, &models.ImageCreateInput{
|
||||
Image: newImage,
|
||||
FileIDs: []file.ID{imageFile.ID},
|
||||
FileIDs: []models.FileID{imageFile.ID},
|
||||
}); err != nil {
|
||||
return fmt.Errorf("creating new image: %w", err)
|
||||
}
|
||||
@@ -151,8 +151,8 @@ func (h *ScanHandler) Handle(ctx context.Context, f file.File, oldFile file.File
|
||||
|
||||
// remove the old thumbnail if the checksum changed - we'll regenerate it
|
||||
if oldFile != nil {
|
||||
oldHash := oldFile.Base().Fingerprints.GetString(file.FingerprintTypeMD5)
|
||||
newHash := f.Base().Fingerprints.GetString(file.FingerprintTypeMD5)
|
||||
oldHash := oldFile.Base().Fingerprints.GetString(models.FingerprintTypeMD5)
|
||||
newHash := f.Base().Fingerprints.GetString(models.FingerprintTypeMD5)
|
||||
|
||||
if oldHash != "" && newHash != "" && oldHash != newHash {
|
||||
// remove cache dir of gallery
|
||||
@@ -173,7 +173,7 @@ func (h *ScanHandler) Handle(ctx context.Context, f file.File, oldFile file.File
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *ScanHandler) associateExisting(ctx context.Context, existing []*models.Image, f *file.BaseFile, updateExisting bool) error {
|
||||
func (h *ScanHandler) associateExisting(ctx context.Context, existing []*models.Image, f *models.BaseFile, updateExisting bool) error {
|
||||
for _, i := range existing {
|
||||
if err := i.LoadFiles(ctx, h.CreatorUpdater); err != nil {
|
||||
return err
|
||||
@@ -239,7 +239,7 @@ func (h *ScanHandler) associateExisting(ctx context.Context, existing []*models.
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *ScanHandler) getOrCreateFolderBasedGallery(ctx context.Context, f file.File) (*models.Gallery, error) {
|
||||
func (h *ScanHandler) getOrCreateFolderBasedGallery(ctx context.Context, f models.File) (*models.Gallery, error) {
|
||||
folderID := f.Base().ParentFolderID
|
||||
g, err := h.GalleryFinder.FindByFolderID(ctx, folderID)
|
||||
if err != nil {
|
||||
@@ -299,7 +299,7 @@ func (h *ScanHandler) associateFolderImages(ctx context.Context, g *models.Galle
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *ScanHandler) getOrCreateZipBasedGallery(ctx context.Context, zipFile file.File) (*models.Gallery, error) {
|
||||
func (h *ScanHandler) getOrCreateZipBasedGallery(ctx context.Context, zipFile models.File) (*models.Gallery, error) {
|
||||
g, err := h.GalleryFinder.FindByFileID(ctx, zipFile.Base().ID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("finding zip based gallery: %w", err)
|
||||
@@ -319,7 +319,7 @@ func (h *ScanHandler) getOrCreateZipBasedGallery(ctx context.Context, zipFile fi
|
||||
|
||||
logger.Infof("%s doesn't exist. Creating new gallery...", zipFile.Base().Path)
|
||||
|
||||
if err := h.GalleryFinder.Create(ctx, newGallery, []file.ID{zipFile.Base().ID}); err != nil {
|
||||
if err := h.GalleryFinder.Create(ctx, newGallery, []models.FileID{zipFile.Base().ID}); err != nil {
|
||||
return nil, fmt.Errorf("creating zip-based gallery: %w", err)
|
||||
}
|
||||
|
||||
@@ -328,7 +328,7 @@ func (h *ScanHandler) getOrCreateZipBasedGallery(ctx context.Context, zipFile fi
|
||||
return newGallery, nil
|
||||
}
|
||||
|
||||
func (h *ScanHandler) getOrCreateGallery(ctx context.Context, f file.File) (*models.Gallery, error) {
|
||||
func (h *ScanHandler) getOrCreateGallery(ctx context.Context, f models.File) (*models.Gallery, error) {
|
||||
// don't create folder-based galleries for files in zip file
|
||||
if f.Base().ZipFile != nil {
|
||||
return h.getOrCreateZipBasedGallery(ctx, f.Base().ZipFile)
|
||||
@@ -357,7 +357,7 @@ func (h *ScanHandler) getOrCreateGallery(ctx context.Context, f file.File) (*mod
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (h *ScanHandler) getGalleryToAssociate(ctx context.Context, newImage *models.Image, f file.File) (*models.Gallery, error) {
|
||||
func (h *ScanHandler) getGalleryToAssociate(ctx context.Context, newImage *models.Image, f models.File) (*models.Gallery, error) {
|
||||
g, err := h.getOrCreateGallery(ctx, f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -1,24 +1,10 @@
|
||||
package image
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/stashapp/stash/pkg/file"
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
)
|
||||
|
||||
type FinderByFile interface {
|
||||
FindByFileID(ctx context.Context, fileID file.ID) ([]*models.Image, error)
|
||||
FindByZipFileID(ctx context.Context, zipFileID file.ID) ([]*models.Image, error)
|
||||
}
|
||||
|
||||
type Repository interface {
|
||||
FinderByFile
|
||||
Destroyer
|
||||
models.FileLoader
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
File file.Store
|
||||
Repository Repository
|
||||
File models.FileReaderWriter
|
||||
Repository models.ImageReaderWriter
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/stashapp/stash/pkg/ffmpeg/transcoder"
|
||||
"github.com/stashapp/stash/pkg/file"
|
||||
"github.com/stashapp/stash/pkg/fsutil"
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
)
|
||||
|
||||
const ffmpegImageQuality = 5
|
||||
@@ -68,7 +69,7 @@ func NewThumbnailEncoder(ffmpegEncoder *ffmpeg.FFMpeg, ffProbe ffmpeg.FFProbe, c
|
||||
// the provided max size. It resizes based on the largest X/Y direction.
|
||||
// It returns nil and an error if an error occurs reading, decoding or encoding
|
||||
// the image, or if the image is not suitable for thumbnails.
|
||||
func (e *ThumbnailEncoder) GetThumbnail(f file.File, maxSize int) ([]byte, error) {
|
||||
func (e *ThumbnailEncoder) GetThumbnail(f models.File, maxSize int) ([]byte, error) {
|
||||
reader, err := f.Open(&file.OsFS{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -82,7 +83,7 @@ func (e *ThumbnailEncoder) GetThumbnail(f file.File, maxSize int) ([]byte, error
|
||||
|
||||
data := buf.Bytes()
|
||||
|
||||
if imageFile, ok := f.(*file.ImageFile); ok {
|
||||
if imageFile, ok := f.(*models.ImageFile); ok {
|
||||
format := imageFile.Format
|
||||
animated := imageFile.Format == formatGif
|
||||
|
||||
@@ -98,7 +99,7 @@ func (e *ThumbnailEncoder) GetThumbnail(f file.File, maxSize int) ([]byte, error
|
||||
}
|
||||
|
||||
// Videofiles can only be thumbnailed with ffmpeg
|
||||
if _, ok := f.(*file.VideoFile); ok {
|
||||
if _, ok := f.(*models.VideoFile); ok {
|
||||
return e.ffmpegImageThumbnail(buf, maxSize)
|
||||
}
|
||||
|
||||
|
||||
@@ -6,11 +6,7 @@ import (
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
)
|
||||
|
||||
type PartialUpdater interface {
|
||||
UpdatePartial(ctx context.Context, id int, partial models.ImagePartial) (*models.Image, error)
|
||||
}
|
||||
|
||||
func AddPerformer(ctx context.Context, qb PartialUpdater, i *models.Image, performerID int) error {
|
||||
func AddPerformer(ctx context.Context, qb models.ImageUpdater, i *models.Image, performerID int) error {
|
||||
_, err := qb.UpdatePartial(ctx, i.ID, models.ImagePartial{
|
||||
PerformerIDs: &models.UpdateIDs{
|
||||
IDs: []int{performerID},
|
||||
@@ -21,7 +17,7 @@ func AddPerformer(ctx context.Context, qb PartialUpdater, i *models.Image, perfo
|
||||
return err
|
||||
}
|
||||
|
||||
func AddTag(ctx context.Context, qb PartialUpdater, i *models.Image, tagID int) error {
|
||||
func AddTag(ctx context.Context, qb models.ImageUpdater, i *models.Image, tagID int) error {
|
||||
_, err := qb.UpdatePartial(ctx, i.ID, models.ImagePartial{
|
||||
TagIDs: &models.UpdateIDs{
|
||||
IDs: []int{tagID},
|
||||
|
||||
@@ -20,7 +20,7 @@ type Cache struct {
|
||||
// against. This means that performers with single-letter words in their names could potentially
|
||||
// be missed.
|
||||
// This query is expensive, so it's queried once and cached, if the cache if provided.
|
||||
func getSingleLetterPerformers(ctx context.Context, c *Cache, reader PerformerAutoTagQueryer) ([]*models.Performer, error) {
|
||||
func getSingleLetterPerformers(ctx context.Context, c *Cache, reader models.PerformerAutoTagQueryer) ([]*models.Performer, error) {
|
||||
if c == nil {
|
||||
c = &Cache{}
|
||||
}
|
||||
@@ -53,7 +53,7 @@ func getSingleLetterPerformers(ctx context.Context, c *Cache, reader PerformerAu
|
||||
|
||||
// getSingleLetterStudios returns all studios with names that start with single character words.
|
||||
// See getSingleLetterPerformers for details.
|
||||
func getSingleLetterStudios(ctx context.Context, c *Cache, reader StudioAutoTagQueryer) ([]*models.Studio, error) {
|
||||
func getSingleLetterStudios(ctx context.Context, c *Cache, reader models.StudioAutoTagQueryer) ([]*models.Studio, error) {
|
||||
if c == nil {
|
||||
c = &Cache{}
|
||||
}
|
||||
@@ -86,7 +86,7 @@ func getSingleLetterStudios(ctx context.Context, c *Cache, reader StudioAutoTagQ
|
||||
|
||||
// getSingleLetterTags returns all tags with names that start with single character words.
|
||||
// See getSingleLetterPerformers for details.
|
||||
func getSingleLetterTags(ctx context.Context, c *Cache, reader TagAutoTagQueryer) ([]*models.Tag, error) {
|
||||
func getSingleLetterTags(ctx context.Context, c *Cache, reader models.TagAutoTagQueryer) ([]*models.Tag, error) {
|
||||
if c == nil {
|
||||
c = &Cache{}
|
||||
}
|
||||
|
||||
@@ -14,8 +14,6 @@ import (
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
"github.com/stashapp/stash/pkg/scene"
|
||||
"github.com/stashapp/stash/pkg/sliceutil/stringslice"
|
||||
"github.com/stashapp/stash/pkg/studio"
|
||||
"github.com/stashapp/stash/pkg/tag"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -28,24 +26,6 @@ const (
|
||||
|
||||
var separatorRE = regexp.MustCompile(separatorPattern)
|
||||
|
||||
type PerformerAutoTagQueryer interface {
|
||||
Query(ctx context.Context, performerFilter *models.PerformerFilterType, findFilter *models.FindFilterType) ([]*models.Performer, int, error)
|
||||
QueryForAutoTag(ctx context.Context, words []string) ([]*models.Performer, error)
|
||||
models.AliasLoader
|
||||
}
|
||||
|
||||
type StudioAutoTagQueryer interface {
|
||||
QueryForAutoTag(ctx context.Context, words []string) ([]*models.Studio, error)
|
||||
studio.Queryer
|
||||
GetAliases(ctx context.Context, studioID int) ([]string, error)
|
||||
}
|
||||
|
||||
type TagAutoTagQueryer interface {
|
||||
QueryForAutoTag(ctx context.Context, words []string) ([]*models.Tag, error)
|
||||
tag.Queryer
|
||||
GetAliases(ctx context.Context, tagID int) ([]string, error)
|
||||
}
|
||||
|
||||
func getPathQueryRegex(name string) string {
|
||||
// escape specific regex characters
|
||||
name = regexp.QuoteMeta(name)
|
||||
@@ -146,7 +126,7 @@ func regexpMatchesPath(r *regexp.Regexp, path string) int {
|
||||
return found[len(found)-1][0]
|
||||
}
|
||||
|
||||
func getPerformers(ctx context.Context, words []string, performerReader PerformerAutoTagQueryer, cache *Cache) ([]*models.Performer, error) {
|
||||
func getPerformers(ctx context.Context, words []string, performerReader models.PerformerAutoTagQueryer, cache *Cache) ([]*models.Performer, error) {
|
||||
performers, err := performerReader.QueryForAutoTag(ctx, words)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -160,7 +140,7 @@ func getPerformers(ctx context.Context, words []string, performerReader Performe
|
||||
return append(performers, swPerformers...), nil
|
||||
}
|
||||
|
||||
func PathToPerformers(ctx context.Context, path string, reader PerformerAutoTagQueryer, cache *Cache, trimExt bool) ([]*models.Performer, error) {
|
||||
func PathToPerformers(ctx context.Context, path string, reader models.PerformerAutoTagQueryer, cache *Cache, trimExt bool) ([]*models.Performer, error) {
|
||||
words := getPathWords(path, trimExt)
|
||||
|
||||
performers, err := getPerformers(ctx, words, reader, cache)
|
||||
@@ -198,7 +178,7 @@ func PathToPerformers(ctx context.Context, path string, reader PerformerAutoTagQ
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func getStudios(ctx context.Context, words []string, reader StudioAutoTagQueryer, cache *Cache) ([]*models.Studio, error) {
|
||||
func getStudios(ctx context.Context, words []string, reader models.StudioAutoTagQueryer, cache *Cache) ([]*models.Studio, error) {
|
||||
studios, err := reader.QueryForAutoTag(ctx, words)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -215,7 +195,7 @@ func getStudios(ctx context.Context, words []string, reader StudioAutoTagQueryer
|
||||
// PathToStudio returns the Studio that matches the given path.
|
||||
// Where multiple matching studios are found, the one that matches the latest
|
||||
// position in the path is returned.
|
||||
func PathToStudio(ctx context.Context, path string, reader StudioAutoTagQueryer, cache *Cache, trimExt bool) (*models.Studio, error) {
|
||||
func PathToStudio(ctx context.Context, path string, reader models.StudioAutoTagQueryer, cache *Cache, trimExt bool) (*models.Studio, error) {
|
||||
words := getPathWords(path, trimExt)
|
||||
candidates, err := getStudios(ctx, words, reader, cache)
|
||||
|
||||
@@ -249,7 +229,7 @@ func PathToStudio(ctx context.Context, path string, reader StudioAutoTagQueryer,
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func getTags(ctx context.Context, words []string, reader TagAutoTagQueryer, cache *Cache) ([]*models.Tag, error) {
|
||||
func getTags(ctx context.Context, words []string, reader models.TagAutoTagQueryer, cache *Cache) ([]*models.Tag, error) {
|
||||
tags, err := reader.QueryForAutoTag(ctx, words)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -263,7 +243,7 @@ func getTags(ctx context.Context, words []string, reader TagAutoTagQueryer, cach
|
||||
return append(tags, swTags...), nil
|
||||
}
|
||||
|
||||
func PathToTags(ctx context.Context, path string, reader TagAutoTagQueryer, cache *Cache, trimExt bool) ([]*models.Tag, error) {
|
||||
func PathToTags(ctx context.Context, path string, reader models.TagAutoTagQueryer, cache *Cache, trimExt bool) ([]*models.Tag, error) {
|
||||
words := getPathWords(path, trimExt)
|
||||
tags, err := getTags(ctx, words, reader, cache)
|
||||
|
||||
@@ -299,7 +279,7 @@ func PathToTags(ctx context.Context, path string, reader TagAutoTagQueryer, cach
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func PathToScenesFn(ctx context.Context, name string, paths []string, sceneReader scene.Queryer, fn func(ctx context.Context, scene *models.Scene) error) error {
|
||||
func PathToScenesFn(ctx context.Context, name string, paths []string, sceneReader models.SceneQueryer, fn func(ctx context.Context, scene *models.Scene) error) error {
|
||||
regex := getPathQueryRegex(name)
|
||||
organized := false
|
||||
filter := models.SceneFilterType{
|
||||
@@ -358,7 +338,7 @@ func PathToScenesFn(ctx context.Context, name string, paths []string, sceneReade
|
||||
return nil
|
||||
}
|
||||
|
||||
func PathToImagesFn(ctx context.Context, name string, paths []string, imageReader image.Queryer, fn func(ctx context.Context, scene *models.Image) error) error {
|
||||
func PathToImagesFn(ctx context.Context, name string, paths []string, imageReader models.ImageQueryer, fn func(ctx context.Context, scene *models.Image) error) error {
|
||||
regex := getPathQueryRegex(name)
|
||||
organized := false
|
||||
filter := models.ImageFilterType{
|
||||
@@ -417,7 +397,7 @@ func PathToImagesFn(ctx context.Context, name string, paths []string, imageReade
|
||||
return nil
|
||||
}
|
||||
|
||||
func PathToGalleriesFn(ctx context.Context, name string, paths []string, galleryReader gallery.Queryer, fn func(ctx context.Context, scene *models.Gallery) error) error {
|
||||
func PathToGalleriesFn(ctx context.Context, name string, paths []string, galleryReader models.GalleryQueryer, fn func(ctx context.Context, scene *models.Gallery) error) error {
|
||||
regex := getPathQueryRegex(name)
|
||||
organized := false
|
||||
filter := models.GalleryFilterType{
|
||||
|
||||
@@ -58,7 +58,7 @@ func ScrapedPerformer(ctx context.Context, qb PerformerFinder, p *models.Scraped
|
||||
}
|
||||
|
||||
type StudioFinder interface {
|
||||
studio.Queryer
|
||||
models.StudioQueryer
|
||||
FindByStashID(ctx context.Context, stashID models.StashID) ([]*models.Studio, error)
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ func ScrapedMovie(ctx context.Context, qb MovieNamesFinder, m *models.ScrapedMov
|
||||
|
||||
// ScrapedTag matches the provided tag with the tags
|
||||
// in the database and sets the ID field if one is found.
|
||||
func ScrapedTag(ctx context.Context, qb tag.Queryer, s *models.ScrapedTag) error {
|
||||
func ScrapedTag(ctx context.Context, qb models.TagQueryer, s *models.ScrapedTag) error {
|
||||
if s.StoredID != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@ import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/stashapp/stash/pkg/file"
|
||||
)
|
||||
|
||||
type FileQueryOptions struct {
|
||||
@@ -57,24 +55,24 @@ func PathsFileFilter(paths []string) *FileFilterType {
|
||||
type FileQueryResult struct {
|
||||
// can't use QueryResult because id type is wrong
|
||||
|
||||
IDs []file.ID
|
||||
IDs []FileID
|
||||
Count int
|
||||
|
||||
finder file.Finder
|
||||
files []file.File
|
||||
getter FileGetter
|
||||
files []File
|
||||
resolveErr error
|
||||
}
|
||||
|
||||
func NewFileQueryResult(finder file.Finder) *FileQueryResult {
|
||||
func NewFileQueryResult(fileGetter FileGetter) *FileQueryResult {
|
||||
return &FileQueryResult{
|
||||
finder: finder,
|
||||
getter: fileGetter,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *FileQueryResult) Resolve(ctx context.Context) ([]file.File, error) {
|
||||
func (r *FileQueryResult) Resolve(ctx context.Context) ([]File, error) {
|
||||
// cache results
|
||||
if r.files == nil && r.resolveErr == nil {
|
||||
r.files, r.resolveErr = r.finder.Find(ctx, r.IDs...)
|
||||
r.files, r.resolveErr = r.getter.Find(ctx, r.IDs...)
|
||||
}
|
||||
return r.files, r.resolveErr
|
||||
}
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
package file
|
||||
package models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var (
|
||||
FingerprintTypeOshash = "oshash"
|
||||
@@ -12,6 +17,15 @@ type Fingerprint struct {
|
||||
Fingerprint interface{}
|
||||
}
|
||||
|
||||
func (f *Fingerprint) Value() string {
|
||||
switch v := f.Fingerprint.(type) {
|
||||
case int64:
|
||||
return strconv.FormatUint(uint64(v), 16)
|
||||
default:
|
||||
return fmt.Sprintf("%v", f.Fingerprint)
|
||||
}
|
||||
}
|
||||
|
||||
type Fingerprints []Fingerprint
|
||||
|
||||
func (f *Fingerprints) Remove(type_ string) {
|
||||
@@ -114,8 +128,3 @@ func (f Fingerprints) AppendUnique(o Fingerprint) Fingerprints {
|
||||
|
||||
return append(f, o)
|
||||
}
|
||||
|
||||
// FingerprintCalculator calculates a fingerprint for the provided file.
|
||||
type FingerprintCalculator interface {
|
||||
CalculateFingerprints(f *BaseFile, o Opener, useExisting bool) ([]Fingerprint, error)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package file
|
||||
package models
|
||||
|
||||
import "testing"
|
||||
|
||||
27
pkg/models/fs.go
Normal file
27
pkg/models/fs.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/fs"
|
||||
)
|
||||
|
||||
// FileOpener provides an interface to open a file.
|
||||
type FileOpener interface {
|
||||
Open() (io.ReadCloser, error)
|
||||
}
|
||||
|
||||
// FS represents a file system.
|
||||
type FS interface {
|
||||
Stat(name string) (fs.FileInfo, error)
|
||||
Lstat(name string) (fs.FileInfo, error)
|
||||
Open(name string) (fs.ReadDirFile, error)
|
||||
OpenZip(name string) (ZipFS, error)
|
||||
IsPathCaseSensitive(path string) (bool, error)
|
||||
}
|
||||
|
||||
// ZipFS represents a zip file system.
|
||||
type ZipFS interface {
|
||||
FS
|
||||
io.Closer
|
||||
OpenOnly(name string) (io.ReadCloser, error)
|
||||
}
|
||||
@@ -1,11 +1,5 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/stashapp/stash/pkg/file"
|
||||
)
|
||||
|
||||
type GalleryFilterType struct {
|
||||
And *GalleryFilterType `json:"AND"`
|
||||
Or *GalleryFilterType `json:"OR"`
|
||||
@@ -86,40 +80,3 @@ type GalleryDestroyInput struct {
|
||||
DeleteFile *bool `json:"delete_file"`
|
||||
DeleteGenerated *bool `json:"delete_generated"`
|
||||
}
|
||||
|
||||
type GalleryFinder interface {
|
||||
FindMany(ctx context.Context, ids []int) ([]*Gallery, error)
|
||||
}
|
||||
|
||||
type GalleryReader interface {
|
||||
Find(ctx context.Context, id int) (*Gallery, error)
|
||||
GalleryFinder
|
||||
FindByChecksum(ctx context.Context, checksum string) ([]*Gallery, error)
|
||||
FindByChecksums(ctx context.Context, checksums []string) ([]*Gallery, error)
|
||||
FindByPath(ctx context.Context, path string) ([]*Gallery, error)
|
||||
FindBySceneID(ctx context.Context, sceneID int) ([]*Gallery, error)
|
||||
FindByImageID(ctx context.Context, imageID int) ([]*Gallery, error)
|
||||
|
||||
SceneIDLoader
|
||||
PerformerIDLoader
|
||||
TagIDLoader
|
||||
|
||||
Count(ctx context.Context) (int, error)
|
||||
All(ctx context.Context) ([]*Gallery, error)
|
||||
Query(ctx context.Context, galleryFilter *GalleryFilterType, findFilter *FindFilterType) ([]*Gallery, int, error)
|
||||
QueryCount(ctx context.Context, galleryFilter *GalleryFilterType, findFilter *FindFilterType) (int, error)
|
||||
GetImageIDs(ctx context.Context, galleryID int) ([]int, error)
|
||||
}
|
||||
|
||||
type GalleryWriter interface {
|
||||
Create(ctx context.Context, newGallery *Gallery, fileIDs []file.ID) error
|
||||
Update(ctx context.Context, updatedGallery *Gallery) error
|
||||
UpdatePartial(ctx context.Context, id int, updatedGallery GalleryPartial) (*Gallery, error)
|
||||
Destroy(ctx context.Context, id int) error
|
||||
UpdateImages(ctx context.Context, galleryID int, imageIDs []int) error
|
||||
}
|
||||
|
||||
type GalleryReaderWriter interface {
|
||||
GalleryReader
|
||||
GalleryWriter
|
||||
}
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
package models
|
||||
|
||||
import "context"
|
||||
|
||||
type GalleryChapterReader interface {
|
||||
Find(ctx context.Context, id int) (*GalleryChapter, error)
|
||||
FindMany(ctx context.Context, ids []int) ([]*GalleryChapter, error)
|
||||
FindByGalleryID(ctx context.Context, galleryID int) ([]*GalleryChapter, error)
|
||||
}
|
||||
|
||||
type GalleryChapterWriter interface {
|
||||
Create(ctx context.Context, newGalleryChapter *GalleryChapter) error
|
||||
Update(ctx context.Context, updatedGalleryChapter *GalleryChapter) error
|
||||
UpdatePartial(ctx context.Context, id int, updatedGalleryChapter GalleryChapterPartial) (*GalleryChapter, error)
|
||||
Destroy(ctx context.Context, id int) error
|
||||
}
|
||||
|
||||
type GalleryChapterReaderWriter interface {
|
||||
GalleryChapterReader
|
||||
GalleryChapterWriter
|
||||
}
|
||||
@@ -77,60 +77,21 @@ type ImageQueryResult struct {
|
||||
Megapixels float64
|
||||
TotalSize float64
|
||||
|
||||
finder ImageFinder
|
||||
getter ImageGetter
|
||||
images []*Image
|
||||
resolveErr error
|
||||
}
|
||||
|
||||
func NewImageQueryResult(finder ImageFinder) *ImageQueryResult {
|
||||
func NewImageQueryResult(getter ImageGetter) *ImageQueryResult {
|
||||
return &ImageQueryResult{
|
||||
finder: finder,
|
||||
getter: getter,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *ImageQueryResult) Resolve(ctx context.Context) ([]*Image, error) {
|
||||
// cache results
|
||||
if r.images == nil && r.resolveErr == nil {
|
||||
r.images, r.resolveErr = r.finder.FindMany(ctx, r.IDs)
|
||||
r.images, r.resolveErr = r.getter.FindMany(ctx, r.IDs)
|
||||
}
|
||||
return r.images, r.resolveErr
|
||||
}
|
||||
|
||||
type ImageFinder interface {
|
||||
// TODO - rename to Find and remove existing method
|
||||
FindMany(ctx context.Context, ids []int) ([]*Image, error)
|
||||
}
|
||||
|
||||
type ImageReader interface {
|
||||
ImageFinder
|
||||
// TODO - remove this in another PR
|
||||
Find(ctx context.Context, id int) (*Image, error)
|
||||
FindByChecksum(ctx context.Context, checksum string) ([]*Image, error)
|
||||
FindByGalleryID(ctx context.Context, galleryID int) ([]*Image, error)
|
||||
CountByGalleryID(ctx context.Context, galleryID int) (int, error)
|
||||
OCountByPerformerID(ctx context.Context, performerID int) (int, error)
|
||||
Count(ctx context.Context) (int, error)
|
||||
Size(ctx context.Context) (float64, error)
|
||||
All(ctx context.Context) ([]*Image, error)
|
||||
Query(ctx context.Context, options ImageQueryOptions) (*ImageQueryResult, error)
|
||||
QueryCount(ctx context.Context, imageFilter *ImageFilterType, findFilter *FindFilterType) (int, error)
|
||||
|
||||
GalleryIDLoader
|
||||
PerformerIDLoader
|
||||
TagIDLoader
|
||||
}
|
||||
|
||||
type ImageWriter interface {
|
||||
Create(ctx context.Context, newImage *ImageCreateInput) error
|
||||
Update(ctx context.Context, updatedImage *Image) error
|
||||
UpdatePartial(ctx context.Context, id int, partial ImagePartial) (*Image, error)
|
||||
IncrementOCounter(ctx context.Context, id int) (int, error)
|
||||
DecrementOCounter(ctx context.Context, id int) (int, error)
|
||||
ResetOCounter(ctx context.Context, id int) (int, error)
|
||||
Destroy(ctx context.Context, id int) error
|
||||
}
|
||||
|
||||
type ImageReaderWriter interface {
|
||||
ImageReader
|
||||
ImageWriter
|
||||
}
|
||||
|
||||
350
pkg/models/mocks/FileReaderWriter.go
Normal file
350
pkg/models/mocks/FileReaderWriter.go
Normal file
@@ -0,0 +1,350 @@
|
||||
// Code generated by mockery v2.10.0. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
context "context"
|
||||
fs "io/fs"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
models "github.com/stashapp/stash/pkg/models"
|
||||
)
|
||||
|
||||
// FileReaderWriter is an autogenerated mock type for the FileReaderWriter type
|
||||
type FileReaderWriter struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// CountAllInPaths provides a mock function with given fields: ctx, p
|
||||
func (_m *FileReaderWriter) CountAllInPaths(ctx context.Context, p []string) (int, error) {
|
||||
ret := _m.Called(ctx, p)
|
||||
|
||||
var r0 int
|
||||
if rf, ok := ret.Get(0).(func(context.Context, []string) int); ok {
|
||||
r0 = rf(ctx, p)
|
||||
} else {
|
||||
r0 = ret.Get(0).(int)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, []string) error); ok {
|
||||
r1 = rf(ctx, p)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// CountByFolderID provides a mock function with given fields: ctx, folderID
|
||||
func (_m *FileReaderWriter) CountByFolderID(ctx context.Context, folderID models.FolderID) (int, error) {
|
||||
ret := _m.Called(ctx, folderID)
|
||||
|
||||
var r0 int
|
||||
if rf, ok := ret.Get(0).(func(context.Context, models.FolderID) int); ok {
|
||||
r0 = rf(ctx, folderID)
|
||||
} else {
|
||||
r0 = ret.Get(0).(int)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, models.FolderID) error); ok {
|
||||
r1 = rf(ctx, folderID)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Create provides a mock function with given fields: ctx, f
|
||||
func (_m *FileReaderWriter) Create(ctx context.Context, f models.File) error {
|
||||
ret := _m.Called(ctx, f)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, models.File) error); ok {
|
||||
r0 = rf(ctx, f)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Destroy provides a mock function with given fields: ctx, id
|
||||
func (_m *FileReaderWriter) Destroy(ctx context.Context, id models.FileID) error {
|
||||
ret := _m.Called(ctx, id)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, models.FileID) error); ok {
|
||||
r0 = rf(ctx, id)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Find provides a mock function with given fields: ctx, id
|
||||
func (_m *FileReaderWriter) Find(ctx context.Context, id ...models.FileID) ([]models.File, error) {
|
||||
_va := make([]interface{}, len(id))
|
||||
for _i := range id {
|
||||
_va[_i] = id[_i]
|
||||
}
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, ctx)
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _m.Called(_ca...)
|
||||
|
||||
var r0 []models.File
|
||||
if rf, ok := ret.Get(0).(func(context.Context, ...models.FileID) []models.File); ok {
|
||||
r0 = rf(ctx, id...)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]models.File)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, ...models.FileID) error); ok {
|
||||
r1 = rf(ctx, id...)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// FindAllByPath provides a mock function with given fields: ctx, path
|
||||
func (_m *FileReaderWriter) FindAllByPath(ctx context.Context, path string) ([]models.File, error) {
|
||||
ret := _m.Called(ctx, path)
|
||||
|
||||
var r0 []models.File
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) []models.File); ok {
|
||||
r0 = rf(ctx, path)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]models.File)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
|
||||
r1 = rf(ctx, path)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// FindAllInPaths provides a mock function with given fields: ctx, p, limit, offset
|
||||
func (_m *FileReaderWriter) FindAllInPaths(ctx context.Context, p []string, limit int, offset int) ([]models.File, error) {
|
||||
ret := _m.Called(ctx, p, limit, offset)
|
||||
|
||||
var r0 []models.File
|
||||
if rf, ok := ret.Get(0).(func(context.Context, []string, int, int) []models.File); ok {
|
||||
r0 = rf(ctx, p, limit, offset)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]models.File)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, []string, int, int) error); ok {
|
||||
r1 = rf(ctx, p, limit, offset)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// FindByFileInfo provides a mock function with given fields: ctx, info, size
|
||||
func (_m *FileReaderWriter) FindByFileInfo(ctx context.Context, info fs.FileInfo, size int64) ([]models.File, error) {
|
||||
ret := _m.Called(ctx, info, size)
|
||||
|
||||
var r0 []models.File
|
||||
if rf, ok := ret.Get(0).(func(context.Context, fs.FileInfo, int64) []models.File); ok {
|
||||
r0 = rf(ctx, info, size)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]models.File)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, fs.FileInfo, int64) error); ok {
|
||||
r1 = rf(ctx, info, size)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// FindByFingerprint provides a mock function with given fields: ctx, fp
|
||||
func (_m *FileReaderWriter) FindByFingerprint(ctx context.Context, fp models.Fingerprint) ([]models.File, error) {
|
||||
ret := _m.Called(ctx, fp)
|
||||
|
||||
var r0 []models.File
|
||||
if rf, ok := ret.Get(0).(func(context.Context, models.Fingerprint) []models.File); ok {
|
||||
r0 = rf(ctx, fp)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]models.File)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, models.Fingerprint) error); ok {
|
||||
r1 = rf(ctx, fp)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// FindByPath provides a mock function with given fields: ctx, path
|
||||
func (_m *FileReaderWriter) FindByPath(ctx context.Context, path string) (models.File, error) {
|
||||
ret := _m.Called(ctx, path)
|
||||
|
||||
var r0 models.File
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) models.File); ok {
|
||||
r0 = rf(ctx, path)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(models.File)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
|
||||
r1 = rf(ctx, path)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// FindByZipFileID provides a mock function with given fields: ctx, zipFileID
|
||||
func (_m *FileReaderWriter) FindByZipFileID(ctx context.Context, zipFileID models.FileID) ([]models.File, error) {
|
||||
ret := _m.Called(ctx, zipFileID)
|
||||
|
||||
var r0 []models.File
|
||||
if rf, ok := ret.Get(0).(func(context.Context, models.FileID) []models.File); ok {
|
||||
r0 = rf(ctx, zipFileID)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]models.File)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, models.FileID) error); ok {
|
||||
r1 = rf(ctx, zipFileID)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetCaptions provides a mock function with given fields: ctx, fileID
|
||||
func (_m *FileReaderWriter) GetCaptions(ctx context.Context, fileID models.FileID) ([]*models.VideoCaption, error) {
|
||||
ret := _m.Called(ctx, fileID)
|
||||
|
||||
var r0 []*models.VideoCaption
|
||||
if rf, ok := ret.Get(0).(func(context.Context, models.FileID) []*models.VideoCaption); ok {
|
||||
r0 = rf(ctx, fileID)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*models.VideoCaption)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, models.FileID) error); ok {
|
||||
r1 = rf(ctx, fileID)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// IsPrimary provides a mock function with given fields: ctx, fileID
|
||||
func (_m *FileReaderWriter) IsPrimary(ctx context.Context, fileID models.FileID) (bool, error) {
|
||||
ret := _m.Called(ctx, fileID)
|
||||
|
||||
var r0 bool
|
||||
if rf, ok := ret.Get(0).(func(context.Context, models.FileID) bool); ok {
|
||||
r0 = rf(ctx, fileID)
|
||||
} else {
|
||||
r0 = ret.Get(0).(bool)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, models.FileID) error); ok {
|
||||
r1 = rf(ctx, fileID)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Query provides a mock function with given fields: ctx, options
|
||||
func (_m *FileReaderWriter) Query(ctx context.Context, options models.FileQueryOptions) (*models.FileQueryResult, error) {
|
||||
ret := _m.Called(ctx, options)
|
||||
|
||||
var r0 *models.FileQueryResult
|
||||
if rf, ok := ret.Get(0).(func(context.Context, models.FileQueryOptions) *models.FileQueryResult); ok {
|
||||
r0 = rf(ctx, options)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*models.FileQueryResult)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, models.FileQueryOptions) error); ok {
|
||||
r1 = rf(ctx, options)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Update provides a mock function with given fields: ctx, f
|
||||
func (_m *FileReaderWriter) Update(ctx context.Context, f models.File) error {
|
||||
ret := _m.Called(ctx, f)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, models.File) error); ok {
|
||||
r0 = rf(ctx, f)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// UpdateCaptions provides a mock function with given fields: ctx, fileID, captions
|
||||
func (_m *FileReaderWriter) UpdateCaptions(ctx context.Context, fileID models.FileID, captions []*models.VideoCaption) error {
|
||||
ret := _m.Called(ctx, fileID, captions)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, models.FileID, []*models.VideoCaption) error); ok {
|
||||
r0 = rf(ctx, fileID, captions)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
193
pkg/models/mocks/FolderReaderWriter.go
Normal file
193
pkg/models/mocks/FolderReaderWriter.go
Normal file
@@ -0,0 +1,193 @@
|
||||
// Code generated by mockery v2.10.0. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
models "github.com/stashapp/stash/pkg/models"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// FolderReaderWriter is an autogenerated mock type for the FolderReaderWriter type
|
||||
type FolderReaderWriter struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// CountAllInPaths provides a mock function with given fields: ctx, p
|
||||
func (_m *FolderReaderWriter) CountAllInPaths(ctx context.Context, p []string) (int, error) {
|
||||
ret := _m.Called(ctx, p)
|
||||
|
||||
var r0 int
|
||||
if rf, ok := ret.Get(0).(func(context.Context, []string) int); ok {
|
||||
r0 = rf(ctx, p)
|
||||
} else {
|
||||
r0 = ret.Get(0).(int)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, []string) error); ok {
|
||||
r1 = rf(ctx, p)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Create provides a mock function with given fields: ctx, f
|
||||
func (_m *FolderReaderWriter) Create(ctx context.Context, f *models.Folder) error {
|
||||
ret := _m.Called(ctx, f)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *models.Folder) error); ok {
|
||||
r0 = rf(ctx, f)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Destroy provides a mock function with given fields: ctx, id
|
||||
func (_m *FolderReaderWriter) Destroy(ctx context.Context, id models.FolderID) error {
|
||||
ret := _m.Called(ctx, id)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, models.FolderID) error); ok {
|
||||
r0 = rf(ctx, id)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Find provides a mock function with given fields: ctx, id
|
||||
func (_m *FolderReaderWriter) Find(ctx context.Context, id models.FolderID) (*models.Folder, error) {
|
||||
ret := _m.Called(ctx, id)
|
||||
|
||||
var r0 *models.Folder
|
||||
if rf, ok := ret.Get(0).(func(context.Context, models.FolderID) *models.Folder); ok {
|
||||
r0 = rf(ctx, id)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*models.Folder)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, models.FolderID) error); ok {
|
||||
r1 = rf(ctx, id)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// FindAllInPaths provides a mock function with given fields: ctx, p, limit, offset
|
||||
func (_m *FolderReaderWriter) FindAllInPaths(ctx context.Context, p []string, limit int, offset int) ([]*models.Folder, error) {
|
||||
ret := _m.Called(ctx, p, limit, offset)
|
||||
|
||||
var r0 []*models.Folder
|
||||
if rf, ok := ret.Get(0).(func(context.Context, []string, int, int) []*models.Folder); ok {
|
||||
r0 = rf(ctx, p, limit, offset)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*models.Folder)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, []string, int, int) error); ok {
|
||||
r1 = rf(ctx, p, limit, offset)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// FindByParentFolderID provides a mock function with given fields: ctx, parentFolderID
|
||||
func (_m *FolderReaderWriter) FindByParentFolderID(ctx context.Context, parentFolderID models.FolderID) ([]*models.Folder, error) {
|
||||
ret := _m.Called(ctx, parentFolderID)
|
||||
|
||||
var r0 []*models.Folder
|
||||
if rf, ok := ret.Get(0).(func(context.Context, models.FolderID) []*models.Folder); ok {
|
||||
r0 = rf(ctx, parentFolderID)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*models.Folder)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, models.FolderID) error); ok {
|
||||
r1 = rf(ctx, parentFolderID)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// FindByPath provides a mock function with given fields: ctx, path
|
||||
func (_m *FolderReaderWriter) FindByPath(ctx context.Context, path string) (*models.Folder, error) {
|
||||
ret := _m.Called(ctx, path)
|
||||
|
||||
var r0 *models.Folder
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) *models.Folder); ok {
|
||||
r0 = rf(ctx, path)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*models.Folder)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
|
||||
r1 = rf(ctx, path)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// FindByZipFileID provides a mock function with given fields: ctx, zipFileID
|
||||
func (_m *FolderReaderWriter) FindByZipFileID(ctx context.Context, zipFileID models.FileID) ([]*models.Folder, error) {
|
||||
ret := _m.Called(ctx, zipFileID)
|
||||
|
||||
var r0 []*models.Folder
|
||||
if rf, ok := ret.Get(0).(func(context.Context, models.FileID) []*models.Folder); ok {
|
||||
r0 = rf(ctx, zipFileID)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*models.Folder)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, models.FileID) error); ok {
|
||||
r1 = rf(ctx, zipFileID)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Update provides a mock function with given fields: ctx, f
|
||||
func (_m *FolderReaderWriter) Update(ctx context.Context, f *models.Folder) error {
|
||||
ret := _m.Called(ctx, f)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *models.Folder) error); ok {
|
||||
r0 = rf(ctx, f)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
@@ -5,10 +5,8 @@ package mocks
|
||||
import (
|
||||
context "context"
|
||||
|
||||
file "github.com/stashapp/stash/pkg/file"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
models "github.com/stashapp/stash/pkg/models"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// GalleryReaderWriter is an autogenerated mock type for the GalleryReaderWriter type
|
||||
@@ -16,6 +14,41 @@ type GalleryReaderWriter struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// AddFileID provides a mock function with given fields: ctx, id, fileID
|
||||
func (_m *GalleryReaderWriter) AddFileID(ctx context.Context, id int, fileID models.FileID) error {
|
||||
ret := _m.Called(ctx, id, fileID)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int, models.FileID) error); ok {
|
||||
r0 = rf(ctx, id, fileID)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// AddImages provides a mock function with given fields: ctx, galleryID, imageIDs
|
||||
func (_m *GalleryReaderWriter) AddImages(ctx context.Context, galleryID int, imageIDs ...int) error {
|
||||
_va := make([]interface{}, len(imageIDs))
|
||||
for _i := range imageIDs {
|
||||
_va[_i] = imageIDs[_i]
|
||||
}
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, ctx, galleryID)
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _m.Called(_ca...)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int, ...int) error); ok {
|
||||
r0 = rf(ctx, galleryID, imageIDs...)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// All provides a mock function with given fields: ctx
|
||||
func (_m *GalleryReaderWriter) All(ctx context.Context) ([]*models.Gallery, error) {
|
||||
ret := _m.Called(ctx)
|
||||
@@ -60,12 +93,33 @@ func (_m *GalleryReaderWriter) Count(ctx context.Context) (int, error) {
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// CountByFileID provides a mock function with given fields: ctx, fileID
|
||||
func (_m *GalleryReaderWriter) CountByFileID(ctx context.Context, fileID models.FileID) (int, error) {
|
||||
ret := _m.Called(ctx, fileID)
|
||||
|
||||
var r0 int
|
||||
if rf, ok := ret.Get(0).(func(context.Context, models.FileID) int); ok {
|
||||
r0 = rf(ctx, fileID)
|
||||
} else {
|
||||
r0 = ret.Get(0).(int)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, models.FileID) error); ok {
|
||||
r1 = rf(ctx, fileID)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Create provides a mock function with given fields: ctx, newGallery, fileIDs
|
||||
func (_m *GalleryReaderWriter) Create(ctx context.Context, newGallery *models.Gallery, fileIDs []file.ID) error {
|
||||
func (_m *GalleryReaderWriter) Create(ctx context.Context, newGallery *models.Gallery, fileIDs []models.FileID) error {
|
||||
ret := _m.Called(ctx, newGallery, fileIDs)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *models.Gallery, []file.ID) error); ok {
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *models.Gallery, []models.FileID) error); ok {
|
||||
r0 = rf(ctx, newGallery, fileIDs)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
@@ -157,6 +211,75 @@ func (_m *GalleryReaderWriter) FindByChecksums(ctx context.Context, checksums []
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// FindByFileID provides a mock function with given fields: ctx, fileID
|
||||
func (_m *GalleryReaderWriter) FindByFileID(ctx context.Context, fileID models.FileID) ([]*models.Gallery, error) {
|
||||
ret := _m.Called(ctx, fileID)
|
||||
|
||||
var r0 []*models.Gallery
|
||||
if rf, ok := ret.Get(0).(func(context.Context, models.FileID) []*models.Gallery); ok {
|
||||
r0 = rf(ctx, fileID)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*models.Gallery)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, models.FileID) error); ok {
|
||||
r1 = rf(ctx, fileID)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// FindByFingerprints provides a mock function with given fields: ctx, fp
|
||||
func (_m *GalleryReaderWriter) FindByFingerprints(ctx context.Context, fp []models.Fingerprint) ([]*models.Gallery, error) {
|
||||
ret := _m.Called(ctx, fp)
|
||||
|
||||
var r0 []*models.Gallery
|
||||
if rf, ok := ret.Get(0).(func(context.Context, []models.Fingerprint) []*models.Gallery); ok {
|
||||
r0 = rf(ctx, fp)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*models.Gallery)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, []models.Fingerprint) error); ok {
|
||||
r1 = rf(ctx, fp)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// FindByFolderID provides a mock function with given fields: ctx, folderID
|
||||
func (_m *GalleryReaderWriter) FindByFolderID(ctx context.Context, folderID models.FolderID) ([]*models.Gallery, error) {
|
||||
ret := _m.Called(ctx, folderID)
|
||||
|
||||
var r0 []*models.Gallery
|
||||
if rf, ok := ret.Get(0).(func(context.Context, models.FolderID) []*models.Gallery); ok {
|
||||
r0 = rf(ctx, folderID)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*models.Gallery)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, models.FolderID) error); ok {
|
||||
r1 = rf(ctx, folderID)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// FindByImageID provides a mock function with given fields: ctx, imageID
|
||||
func (_m *GalleryReaderWriter) FindByImageID(ctx context.Context, imageID int) ([]*models.Gallery, error) {
|
||||
ret := _m.Called(ctx, imageID)
|
||||
@@ -249,13 +372,59 @@ func (_m *GalleryReaderWriter) FindMany(ctx context.Context, ids []int) ([]*mode
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetImageIDs provides a mock function with given fields: ctx, galleryID
|
||||
func (_m *GalleryReaderWriter) GetImageIDs(ctx context.Context, galleryID int) ([]int, error) {
|
||||
ret := _m.Called(ctx, galleryID)
|
||||
// FindUserGalleryByTitle provides a mock function with given fields: ctx, title
|
||||
func (_m *GalleryReaderWriter) FindUserGalleryByTitle(ctx context.Context, title string) ([]*models.Gallery, error) {
|
||||
ret := _m.Called(ctx, title)
|
||||
|
||||
var r0 []*models.Gallery
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) []*models.Gallery); ok {
|
||||
r0 = rf(ctx, title)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*models.Gallery)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
|
||||
r1 = rf(ctx, title)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetFiles provides a mock function with given fields: ctx, relatedID
|
||||
func (_m *GalleryReaderWriter) GetFiles(ctx context.Context, relatedID int) ([]models.File, error) {
|
||||
ret := _m.Called(ctx, relatedID)
|
||||
|
||||
var r0 []models.File
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int) []models.File); ok {
|
||||
r0 = rf(ctx, relatedID)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]models.File)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, int) error); ok {
|
||||
r1 = rf(ctx, relatedID)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetImageIDs provides a mock function with given fields: ctx, relatedID
|
||||
func (_m *GalleryReaderWriter) GetImageIDs(ctx context.Context, relatedID int) ([]int, error) {
|
||||
ret := _m.Called(ctx, relatedID)
|
||||
|
||||
var r0 []int
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int) []int); ok {
|
||||
r0 = rf(ctx, galleryID)
|
||||
r0 = rf(ctx, relatedID)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]int)
|
||||
@@ -264,7 +433,30 @@ func (_m *GalleryReaderWriter) GetImageIDs(ctx context.Context, galleryID int) (
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, int) error); ok {
|
||||
r1 = rf(ctx, galleryID)
|
||||
r1 = rf(ctx, relatedID)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetManyFileIDs provides a mock function with given fields: ctx, ids
|
||||
func (_m *GalleryReaderWriter) GetManyFileIDs(ctx context.Context, ids []int) ([][]models.FileID, error) {
|
||||
ret := _m.Called(ctx, ids)
|
||||
|
||||
var r0 [][]models.FileID
|
||||
if rf, ok := ret.Get(0).(func(context.Context, []int) [][]models.FileID); ok {
|
||||
r0 = rf(ctx, ids)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([][]models.FileID)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, []int) error); ok {
|
||||
r1 = rf(ctx, ids)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
@@ -392,6 +584,27 @@ func (_m *GalleryReaderWriter) QueryCount(ctx context.Context, galleryFilter *mo
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// RemoveImages provides a mock function with given fields: ctx, galleryID, imageIDs
|
||||
func (_m *GalleryReaderWriter) RemoveImages(ctx context.Context, galleryID int, imageIDs ...int) error {
|
||||
_va := make([]interface{}, len(imageIDs))
|
||||
for _i := range imageIDs {
|
||||
_va[_i] = imageIDs[_i]
|
||||
}
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, ctx, galleryID)
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _m.Called(_ca...)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int, ...int) error); ok {
|
||||
r0 = rf(ctx, galleryID, imageIDs...)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Update provides a mock function with given fields: ctx, updatedGallery
|
||||
func (_m *GalleryReaderWriter) Update(ctx context.Context, updatedGallery *models.Gallery) error {
|
||||
ret := _m.Called(ctx, updatedGallery)
|
||||
|
||||
@@ -14,6 +14,20 @@ type ImageReaderWriter struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// AddFileID provides a mock function with given fields: ctx, id, fileID
|
||||
func (_m *ImageReaderWriter) AddFileID(ctx context.Context, id int, fileID models.FileID) error {
|
||||
ret := _m.Called(ctx, id, fileID)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int, models.FileID) error); ok {
|
||||
r0 = rf(ctx, id, fileID)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// All provides a mock function with given fields: ctx
|
||||
func (_m *ImageReaderWriter) All(ctx context.Context) ([]*models.Image, error) {
|
||||
ret := _m.Called(ctx)
|
||||
@@ -58,6 +72,27 @@ func (_m *ImageReaderWriter) Count(ctx context.Context) (int, error) {
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// CountByFileID provides a mock function with given fields: ctx, fileID
|
||||
func (_m *ImageReaderWriter) CountByFileID(ctx context.Context, fileID models.FileID) (int, error) {
|
||||
ret := _m.Called(ctx, fileID)
|
||||
|
||||
var r0 int
|
||||
if rf, ok := ret.Get(0).(func(context.Context, models.FileID) int); ok {
|
||||
r0 = rf(ctx, fileID)
|
||||
} else {
|
||||
r0 = ret.Get(0).(int)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, models.FileID) error); ok {
|
||||
r1 = rf(ctx, fileID)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// CountByGalleryID provides a mock function with given fields: ctx, galleryID
|
||||
func (_m *ImageReaderWriter) CountByGalleryID(ctx context.Context, galleryID int) (int, error) {
|
||||
ret := _m.Called(ctx, galleryID)
|
||||
@@ -174,6 +209,75 @@ func (_m *ImageReaderWriter) FindByChecksum(ctx context.Context, checksum string
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// FindByFileID provides a mock function with given fields: ctx, fileID
|
||||
func (_m *ImageReaderWriter) FindByFileID(ctx context.Context, fileID models.FileID) ([]*models.Image, error) {
|
||||
ret := _m.Called(ctx, fileID)
|
||||
|
||||
var r0 []*models.Image
|
||||
if rf, ok := ret.Get(0).(func(context.Context, models.FileID) []*models.Image); ok {
|
||||
r0 = rf(ctx, fileID)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*models.Image)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, models.FileID) error); ok {
|
||||
r1 = rf(ctx, fileID)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// FindByFingerprints provides a mock function with given fields: ctx, fp
|
||||
func (_m *ImageReaderWriter) FindByFingerprints(ctx context.Context, fp []models.Fingerprint) ([]*models.Image, error) {
|
||||
ret := _m.Called(ctx, fp)
|
||||
|
||||
var r0 []*models.Image
|
||||
if rf, ok := ret.Get(0).(func(context.Context, []models.Fingerprint) []*models.Image); ok {
|
||||
r0 = rf(ctx, fp)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*models.Image)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, []models.Fingerprint) error); ok {
|
||||
r1 = rf(ctx, fp)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// FindByFolderID provides a mock function with given fields: ctx, fileID
|
||||
func (_m *ImageReaderWriter) FindByFolderID(ctx context.Context, fileID models.FolderID) ([]*models.Image, error) {
|
||||
ret := _m.Called(ctx, fileID)
|
||||
|
||||
var r0 []*models.Image
|
||||
if rf, ok := ret.Get(0).(func(context.Context, models.FolderID) []*models.Image); ok {
|
||||
r0 = rf(ctx, fileID)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*models.Image)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, models.FolderID) error); ok {
|
||||
r1 = rf(ctx, fileID)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// FindByGalleryID provides a mock function with given fields: ctx, galleryID
|
||||
func (_m *ImageReaderWriter) FindByGalleryID(ctx context.Context, galleryID int) ([]*models.Image, error) {
|
||||
ret := _m.Called(ctx, galleryID)
|
||||
@@ -197,6 +301,29 @@ func (_m *ImageReaderWriter) FindByGalleryID(ctx context.Context, galleryID int)
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// FindByZipFileID provides a mock function with given fields: ctx, zipFileID
|
||||
func (_m *ImageReaderWriter) FindByZipFileID(ctx context.Context, zipFileID models.FileID) ([]*models.Image, error) {
|
||||
ret := _m.Called(ctx, zipFileID)
|
||||
|
||||
var r0 []*models.Image
|
||||
if rf, ok := ret.Get(0).(func(context.Context, models.FileID) []*models.Image); ok {
|
||||
r0 = rf(ctx, zipFileID)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*models.Image)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, models.FileID) error); ok {
|
||||
r1 = rf(ctx, zipFileID)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// FindMany provides a mock function with given fields: ctx, ids
|
||||
func (_m *ImageReaderWriter) FindMany(ctx context.Context, ids []int) ([]*models.Image, error) {
|
||||
ret := _m.Called(ctx, ids)
|
||||
@@ -220,6 +347,29 @@ func (_m *ImageReaderWriter) FindMany(ctx context.Context, ids []int) ([]*models
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetFiles provides a mock function with given fields: ctx, relatedID
|
||||
func (_m *ImageReaderWriter) GetFiles(ctx context.Context, relatedID int) ([]models.File, error) {
|
||||
ret := _m.Called(ctx, relatedID)
|
||||
|
||||
var r0 []models.File
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int) []models.File); ok {
|
||||
r0 = rf(ctx, relatedID)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]models.File)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, int) error); ok {
|
||||
r1 = rf(ctx, relatedID)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetGalleryIDs provides a mock function with given fields: ctx, relatedID
|
||||
func (_m *ImageReaderWriter) GetGalleryIDs(ctx context.Context, relatedID int) ([]int, error) {
|
||||
ret := _m.Called(ctx, relatedID)
|
||||
@@ -243,6 +393,29 @@ func (_m *ImageReaderWriter) GetGalleryIDs(ctx context.Context, relatedID int) (
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetManyFileIDs provides a mock function with given fields: ctx, ids
|
||||
func (_m *ImageReaderWriter) GetManyFileIDs(ctx context.Context, ids []int) ([][]models.FileID, error) {
|
||||
ret := _m.Called(ctx, ids)
|
||||
|
||||
var r0 [][]models.FileID
|
||||
if rf, ok := ret.Get(0).(func(context.Context, []int) [][]models.FileID); ok {
|
||||
r0 = rf(ctx, ids)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([][]models.FileID)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, []int) error); ok {
|
||||
r1 = rf(ctx, ids)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetPerformerIDs provides a mock function with given fields: ctx, relatedID
|
||||
func (_m *ImageReaderWriter) GetPerformerIDs(ctx context.Context, relatedID int) ([]int, error) {
|
||||
ret := _m.Called(ctx, relatedID)
|
||||
@@ -453,3 +626,31 @@ func (_m *ImageReaderWriter) UpdatePartial(ctx context.Context, id int, partial
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// UpdatePerformers provides a mock function with given fields: ctx, imageID, performerIDs
|
||||
func (_m *ImageReaderWriter) UpdatePerformers(ctx context.Context, imageID int, performerIDs []int) error {
|
||||
ret := _m.Called(ctx, imageID, performerIDs)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int, []int) error); ok {
|
||||
r0 = rf(ctx, imageID, performerIDs)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// UpdateTags provides a mock function with given fields: ctx, imageID, tagIDs
|
||||
func (_m *ImageReaderWriter) UpdateTags(ctx context.Context, imageID int, tagIDs []int) error {
|
||||
ret := _m.Called(ctx, imageID, tagIDs)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int, []int) error); ok {
|
||||
r0 = rf(ctx, imageID, tagIDs)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
@@ -199,13 +199,13 @@ func (_m *SceneMarkerReaderWriter) GetMarkerStrings(ctx context.Context, q *stri
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetTagIDs provides a mock function with given fields: ctx, imageID
|
||||
func (_m *SceneMarkerReaderWriter) GetTagIDs(ctx context.Context, imageID int) ([]int, error) {
|
||||
ret := _m.Called(ctx, imageID)
|
||||
// GetTagIDs provides a mock function with given fields: ctx, relatedID
|
||||
func (_m *SceneMarkerReaderWriter) GetTagIDs(ctx context.Context, relatedID int) ([]int, error) {
|
||||
ret := _m.Called(ctx, relatedID)
|
||||
|
||||
var r0 []int
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int) []int); ok {
|
||||
r0 = rf(ctx, imageID)
|
||||
r0 = rf(ctx, relatedID)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]int)
|
||||
@@ -214,7 +214,7 @@ func (_m *SceneMarkerReaderWriter) GetTagIDs(ctx context.Context, imageID int) (
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, int) error); ok {
|
||||
r1 = rf(ctx, imageID)
|
||||
r1 = rf(ctx, relatedID)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
@@ -5,10 +5,8 @@ package mocks
|
||||
import (
|
||||
context "context"
|
||||
|
||||
file "github.com/stashapp/stash/pkg/file"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
models "github.com/stashapp/stash/pkg/models"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// SceneReaderWriter is an autogenerated mock type for the SceneReaderWriter type
|
||||
@@ -16,6 +14,34 @@ type SceneReaderWriter struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// AddFileID provides a mock function with given fields: ctx, id, fileID
|
||||
func (_m *SceneReaderWriter) AddFileID(ctx context.Context, id int, fileID models.FileID) error {
|
||||
ret := _m.Called(ctx, id, fileID)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int, models.FileID) error); ok {
|
||||
r0 = rf(ctx, id, fileID)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// AddGalleryIDs provides a mock function with given fields: ctx, sceneID, galleryIDs
|
||||
func (_m *SceneReaderWriter) AddGalleryIDs(ctx context.Context, sceneID int, galleryIDs []int) error {
|
||||
ret := _m.Called(ctx, sceneID, galleryIDs)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int, []int) error); ok {
|
||||
r0 = rf(ctx, sceneID, galleryIDs)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// All provides a mock function with given fields: ctx
|
||||
func (_m *SceneReaderWriter) All(ctx context.Context) ([]*models.Scene, error) {
|
||||
ret := _m.Called(ctx)
|
||||
@@ -39,6 +65,20 @@ func (_m *SceneReaderWriter) All(ctx context.Context) ([]*models.Scene, error) {
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// AssignFiles provides a mock function with given fields: ctx, sceneID, fileID
|
||||
func (_m *SceneReaderWriter) AssignFiles(ctx context.Context, sceneID int, fileID []models.FileID) error {
|
||||
ret := _m.Called(ctx, sceneID, fileID)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int, []models.FileID) error); ok {
|
||||
r0 = rf(ctx, sceneID, fileID)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Count provides a mock function with given fields: ctx
|
||||
func (_m *SceneReaderWriter) Count(ctx context.Context) (int, error) {
|
||||
ret := _m.Called(ctx)
|
||||
@@ -60,6 +100,27 @@ func (_m *SceneReaderWriter) Count(ctx context.Context) (int, error) {
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// CountByFileID provides a mock function with given fields: ctx, fileID
|
||||
func (_m *SceneReaderWriter) CountByFileID(ctx context.Context, fileID models.FileID) (int, error) {
|
||||
ret := _m.Called(ctx, fileID)
|
||||
|
||||
var r0 int
|
||||
if rf, ok := ret.Get(0).(func(context.Context, models.FileID) int); ok {
|
||||
r0 = rf(ctx, fileID)
|
||||
} else {
|
||||
r0 = ret.Get(0).(int)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, models.FileID) error); ok {
|
||||
r1 = rf(ctx, fileID)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// CountByMovieID provides a mock function with given fields: ctx, movieID
|
||||
func (_m *SceneReaderWriter) CountByMovieID(ctx context.Context, movieID int) (int, error) {
|
||||
ret := _m.Called(ctx, movieID)
|
||||
@@ -187,11 +248,11 @@ func (_m *SceneReaderWriter) CountMissingOSHash(ctx context.Context) (int, error
|
||||
}
|
||||
|
||||
// Create provides a mock function with given fields: ctx, newScene, fileIDs
|
||||
func (_m *SceneReaderWriter) Create(ctx context.Context, newScene *models.Scene, fileIDs []file.ID) error {
|
||||
func (_m *SceneReaderWriter) Create(ctx context.Context, newScene *models.Scene, fileIDs []models.FileID) error {
|
||||
ret := _m.Called(ctx, newScene, fileIDs)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *models.Scene, []file.ID) error); ok {
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *models.Scene, []models.FileID) error); ok {
|
||||
r0 = rf(ctx, newScene, fileIDs)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
@@ -302,6 +363,52 @@ func (_m *SceneReaderWriter) FindByChecksum(ctx context.Context, checksum string
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// FindByFileID provides a mock function with given fields: ctx, fileID
|
||||
func (_m *SceneReaderWriter) FindByFileID(ctx context.Context, fileID models.FileID) ([]*models.Scene, error) {
|
||||
ret := _m.Called(ctx, fileID)
|
||||
|
||||
var r0 []*models.Scene
|
||||
if rf, ok := ret.Get(0).(func(context.Context, models.FileID) []*models.Scene); ok {
|
||||
r0 = rf(ctx, fileID)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*models.Scene)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, models.FileID) error); ok {
|
||||
r1 = rf(ctx, fileID)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// FindByFingerprints provides a mock function with given fields: ctx, fp
|
||||
func (_m *SceneReaderWriter) FindByFingerprints(ctx context.Context, fp []models.Fingerprint) ([]*models.Scene, error) {
|
||||
ret := _m.Called(ctx, fp)
|
||||
|
||||
var r0 []*models.Scene
|
||||
if rf, ok := ret.Get(0).(func(context.Context, []models.Fingerprint) []*models.Scene); ok {
|
||||
r0 = rf(ctx, fp)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*models.Scene)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, []models.Fingerprint) error); ok {
|
||||
r1 = rf(ctx, fp)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// FindByGalleryID provides a mock function with given fields: ctx, performerID
|
||||
func (_m *SceneReaderWriter) FindByGalleryID(ctx context.Context, performerID int) ([]*models.Scene, error) {
|
||||
ret := _m.Called(ctx, performerID)
|
||||
@@ -417,6 +524,29 @@ func (_m *SceneReaderWriter) FindByPerformerID(ctx context.Context, performerID
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// FindByPrimaryFileID provides a mock function with given fields: ctx, fileID
|
||||
func (_m *SceneReaderWriter) FindByPrimaryFileID(ctx context.Context, fileID models.FileID) ([]*models.Scene, error) {
|
||||
ret := _m.Called(ctx, fileID)
|
||||
|
||||
var r0 []*models.Scene
|
||||
if rf, ok := ret.Get(0).(func(context.Context, models.FileID) []*models.Scene); ok {
|
||||
r0 = rf(ctx, fileID)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*models.Scene)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, models.FileID) error); ok {
|
||||
r1 = rf(ctx, fileID)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// FindDuplicates provides a mock function with given fields: ctx, distance, durationDiff
|
||||
func (_m *SceneReaderWriter) FindDuplicates(ctx context.Context, distance int, durationDiff float64) ([][]*models.Scene, error) {
|
||||
ret := _m.Called(ctx, distance, durationDiff)
|
||||
@@ -487,15 +617,15 @@ func (_m *SceneReaderWriter) GetCover(ctx context.Context, sceneID int) ([]byte,
|
||||
}
|
||||
|
||||
// GetFiles provides a mock function with given fields: ctx, relatedID
|
||||
func (_m *SceneReaderWriter) GetFiles(ctx context.Context, relatedID int) ([]*file.VideoFile, error) {
|
||||
func (_m *SceneReaderWriter) GetFiles(ctx context.Context, relatedID int) ([]*models.VideoFile, error) {
|
||||
ret := _m.Called(ctx, relatedID)
|
||||
|
||||
var r0 []*file.VideoFile
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int) []*file.VideoFile); ok {
|
||||
var r0 []*models.VideoFile
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int) []*models.VideoFile); ok {
|
||||
r0 = rf(ctx, relatedID)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*file.VideoFile)
|
||||
r0 = ret.Get(0).([]*models.VideoFile)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -532,6 +662,29 @@ func (_m *SceneReaderWriter) GetGalleryIDs(ctx context.Context, relatedID int) (
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetManyFileIDs provides a mock function with given fields: ctx, ids
|
||||
func (_m *SceneReaderWriter) GetManyFileIDs(ctx context.Context, ids []int) ([][]models.FileID, error) {
|
||||
ret := _m.Called(ctx, ids)
|
||||
|
||||
var r0 [][]models.FileID
|
||||
if rf, ok := ret.Get(0).(func(context.Context, []int) [][]models.FileID); ok {
|
||||
r0 = rf(ctx, ids)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([][]models.FileID)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, []int) error); ok {
|
||||
r1 = rf(ctx, ids)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetMovies provides a mock function with given fields: ctx, id
|
||||
func (_m *SceneReaderWriter) GetMovies(ctx context.Context, id int) ([]models.MoviesScenes, error) {
|
||||
ret := _m.Called(ctx, id)
|
||||
@@ -689,20 +842,20 @@ func (_m *SceneReaderWriter) IncrementOCounter(ctx context.Context, id int) (int
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// IncrementWatchCount provides a mock function with given fields: ctx, id
|
||||
func (_m *SceneReaderWriter) IncrementWatchCount(ctx context.Context, id int) (int, error) {
|
||||
ret := _m.Called(ctx, id)
|
||||
// IncrementWatchCount provides a mock function with given fields: ctx, sceneID
|
||||
func (_m *SceneReaderWriter) IncrementWatchCount(ctx context.Context, sceneID int) (int, error) {
|
||||
ret := _m.Called(ctx, sceneID)
|
||||
|
||||
var r0 int
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int) int); ok {
|
||||
r0 = rf(ctx, id)
|
||||
r0 = rf(ctx, sceneID)
|
||||
} else {
|
||||
r0 = ret.Get(0).(int)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, int) error); ok {
|
||||
r1 = rf(ctx, id)
|
||||
r1 = rf(ctx, sceneID)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
@@ -859,20 +1012,20 @@ func (_m *SceneReaderWriter) ResetOCounter(ctx context.Context, id int) (int, er
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// SaveActivity provides a mock function with given fields: ctx, id, resumeTime, playDuration
|
||||
func (_m *SceneReaderWriter) SaveActivity(ctx context.Context, id int, resumeTime *float64, playDuration *float64) (bool, error) {
|
||||
ret := _m.Called(ctx, id, resumeTime, playDuration)
|
||||
// SaveActivity provides a mock function with given fields: ctx, sceneID, resumeTime, playDuration
|
||||
func (_m *SceneReaderWriter) SaveActivity(ctx context.Context, sceneID int, resumeTime *float64, playDuration *float64) (bool, error) {
|
||||
ret := _m.Called(ctx, sceneID, resumeTime, playDuration)
|
||||
|
||||
var r0 bool
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int, *float64, *float64) bool); ok {
|
||||
r0 = rf(ctx, id, resumeTime, playDuration)
|
||||
r0 = rf(ctx, sceneID, resumeTime, playDuration)
|
||||
} else {
|
||||
r0 = ret.Get(0).(bool)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, int, *float64, *float64) error); ok {
|
||||
r1 = rf(ctx, id, resumeTime, playDuration)
|
||||
r1 = rf(ctx, sceneID, resumeTime, playDuration)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
@@ -58,13 +58,13 @@ func (_m *StudioReaderWriter) Count(ctx context.Context) (int, error) {
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Create provides a mock function with given fields: ctx, input
|
||||
func (_m *StudioReaderWriter) Create(ctx context.Context, input *models.Studio) error {
|
||||
ret := _m.Called(ctx, input)
|
||||
// Create provides a mock function with given fields: ctx, newStudio
|
||||
func (_m *StudioReaderWriter) Create(ctx context.Context, newStudio *models.Studio) error {
|
||||
ret := _m.Called(ctx, newStudio)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *models.Studio) error); ok {
|
||||
r0 = rf(ctx, input)
|
||||
r0 = rf(ctx, newStudio)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
@@ -132,6 +132,29 @@ func (_m *StudioReaderWriter) FindByName(ctx context.Context, name string, nocas
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// FindBySceneID provides a mock function with given fields: ctx, sceneID
|
||||
func (_m *StudioReaderWriter) FindBySceneID(ctx context.Context, sceneID int) (*models.Studio, error) {
|
||||
ret := _m.Called(ctx, sceneID)
|
||||
|
||||
var r0 *models.Studio
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int) *models.Studio); ok {
|
||||
r0 = rf(ctx, sceneID)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*models.Studio)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, int) error); ok {
|
||||
r1 = rf(ctx, sceneID)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// FindByStashID provides a mock function with given fields: ctx, stashID
|
||||
func (_m *StudioReaderWriter) FindByStashID(ctx context.Context, stashID models.StashID) ([]*models.Studio, error) {
|
||||
ret := _m.Called(ctx, stashID)
|
||||
@@ -395,13 +418,13 @@ func (_m *StudioReaderWriter) UpdateImage(ctx context.Context, studioID int, ima
|
||||
return r0
|
||||
}
|
||||
|
||||
// UpdatePartial provides a mock function with given fields: ctx, input
|
||||
func (_m *StudioReaderWriter) UpdatePartial(ctx context.Context, input models.StudioPartial) (*models.Studio, error) {
|
||||
ret := _m.Called(ctx, input)
|
||||
// UpdatePartial provides a mock function with given fields: ctx, updatedStudio
|
||||
func (_m *StudioReaderWriter) UpdatePartial(ctx context.Context, updatedStudio models.StudioPartial) (*models.Studio, error) {
|
||||
ret := _m.Called(ctx, updatedStudio)
|
||||
|
||||
var r0 *models.Studio
|
||||
if rf, ok := ret.Get(0).(func(context.Context, models.StudioPartial) *models.Studio); ok {
|
||||
r0 = rf(ctx, input)
|
||||
r0 = rf(ctx, updatedStudio)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*models.Studio)
|
||||
@@ -410,7 +433,7 @@ func (_m *StudioReaderWriter) UpdatePartial(ctx context.Context, input models.St
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, models.StudioPartial) error); ok {
|
||||
r1 = rf(ctx, input)
|
||||
r1 = rf(ctx, updatedStudio)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
@@ -385,13 +385,13 @@ func (_m *TagReaderWriter) FindMany(ctx context.Context, ids []int) ([]*models.T
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetAliases provides a mock function with given fields: ctx, tagID
|
||||
func (_m *TagReaderWriter) GetAliases(ctx context.Context, tagID int) ([]string, error) {
|
||||
ret := _m.Called(ctx, tagID)
|
||||
// GetAliases provides a mock function with given fields: ctx, relatedID
|
||||
func (_m *TagReaderWriter) GetAliases(ctx context.Context, relatedID int) ([]string, error) {
|
||||
ret := _m.Called(ctx, relatedID)
|
||||
|
||||
var r0 []string
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int) []string); ok {
|
||||
r0 = rf(ctx, tagID)
|
||||
r0 = rf(ctx, relatedID)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]string)
|
||||
@@ -400,7 +400,7 @@ func (_m *TagReaderWriter) GetAliases(ctx context.Context, tagID int) ([]string,
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, int) error); ok {
|
||||
r1 = rf(ctx, tagID)
|
||||
r1 = rf(ctx, relatedID)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
@@ -31,6 +31,10 @@ type imageResolver struct {
|
||||
images []*models.Image
|
||||
}
|
||||
|
||||
func (s *imageResolver) Find(ctx context.Context, id int) (*models.Image, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (s *imageResolver) FindMany(ctx context.Context, ids []int) ([]*models.Image, error) {
|
||||
return s.images, nil
|
||||
}
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"math"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
type HashAlgorithm string
|
||||
@@ -47,3 +52,244 @@ func (e *HashAlgorithm) UnmarshalGQL(v interface{}) error {
|
||||
func (e HashAlgorithm) MarshalGQL(w io.Writer) {
|
||||
fmt.Fprint(w, strconv.Quote(e.String()))
|
||||
}
|
||||
|
||||
// ID represents an ID of a file.
|
||||
type FileID int32
|
||||
|
||||
func (i FileID) String() string {
|
||||
return strconv.Itoa(int(i))
|
||||
}
|
||||
|
||||
func (i *FileID) UnmarshalGQL(v interface{}) (err error) {
|
||||
switch v := v.(type) {
|
||||
case string:
|
||||
var id int
|
||||
id, err = strconv.Atoi(v)
|
||||
*i = FileID(id)
|
||||
return err
|
||||
case int:
|
||||
*i = FileID(v)
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("%T is not an int", v)
|
||||
}
|
||||
}
|
||||
|
||||
func (i FileID) MarshalGQL(w io.Writer) {
|
||||
fmt.Fprint(w, strconv.Quote(i.String()))
|
||||
}
|
||||
|
||||
// DirEntry represents a file or directory in the file system.
|
||||
type DirEntry struct {
|
||||
ZipFileID *FileID `json:"zip_file_id"`
|
||||
|
||||
// transient - not persisted
|
||||
// only guaranteed to have id, path and basename set
|
||||
ZipFile File
|
||||
|
||||
ModTime time.Time `json:"mod_time"`
|
||||
}
|
||||
|
||||
func (e *DirEntry) info(fs FS, path string) (fs.FileInfo, error) {
|
||||
if e.ZipFile != nil {
|
||||
zipPath := e.ZipFile.Base().Path
|
||||
zfs, err := fs.OpenZip(zipPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer zfs.Close()
|
||||
fs = zfs
|
||||
}
|
||||
// else assume os file
|
||||
|
||||
ret, err := fs.Lstat(path)
|
||||
return ret, err
|
||||
}
|
||||
|
||||
// File represents a file in the file system.
|
||||
type File interface {
|
||||
Base() *BaseFile
|
||||
SetFingerprints(fp Fingerprints)
|
||||
Open(fs FS) (io.ReadCloser, error)
|
||||
}
|
||||
|
||||
// BaseFile represents a file in the file system.
|
||||
type BaseFile struct {
|
||||
ID FileID `json:"id"`
|
||||
|
||||
DirEntry
|
||||
|
||||
// resolved from parent folder and basename only - not stored in DB
|
||||
Path string `json:"path"`
|
||||
|
||||
Basename string `json:"basename"`
|
||||
ParentFolderID FolderID `json:"parent_folder_id"`
|
||||
|
||||
Fingerprints Fingerprints `json:"fingerprints"`
|
||||
|
||||
Size int64 `json:"size"`
|
||||
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
func (f *BaseFile) FingerprintSlice() []Fingerprint {
|
||||
return f.Fingerprints
|
||||
}
|
||||
|
||||
// SetFingerprints sets the fingerprints of the file.
|
||||
// If a fingerprint of the same type already exists, it is overwritten.
|
||||
func (f *BaseFile) SetFingerprints(fp Fingerprints) {
|
||||
for _, v := range fp {
|
||||
f.SetFingerprint(v)
|
||||
}
|
||||
}
|
||||
|
||||
// SetFingerprint sets the fingerprint of the file.
|
||||
// If a fingerprint of the same type already exists, it is overwritten.
|
||||
func (f *BaseFile) SetFingerprint(fp Fingerprint) {
|
||||
for i, existing := range f.Fingerprints {
|
||||
if existing.Type == fp.Type {
|
||||
f.Fingerprints[i] = fp
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
f.Fingerprints = append(f.Fingerprints, fp)
|
||||
}
|
||||
|
||||
// Base is used to fulfil the File interface.
|
||||
func (f *BaseFile) Base() *BaseFile {
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *BaseFile) Open(fs FS) (io.ReadCloser, error) {
|
||||
if f.ZipFile != nil {
|
||||
zipPath := f.ZipFile.Base().Path
|
||||
zfs, err := fs.OpenZip(zipPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return zfs.OpenOnly(f.Path)
|
||||
}
|
||||
|
||||
return fs.Open(f.Path)
|
||||
}
|
||||
|
||||
func (f *BaseFile) Info(fs FS) (fs.FileInfo, error) {
|
||||
return f.info(fs, f.Path)
|
||||
}
|
||||
|
||||
func (f *BaseFile) Serve(fs FS, w http.ResponseWriter, r *http.Request) error {
|
||||
reader, err := f.Open(fs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer reader.Close()
|
||||
|
||||
content, ok := reader.(io.ReadSeeker)
|
||||
if !ok {
|
||||
data, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
content = bytes.NewReader(data)
|
||||
}
|
||||
|
||||
if r.URL.Query().Has("t") {
|
||||
w.Header().Set("Cache-Control", "private, max-age=31536000, immutable")
|
||||
} else {
|
||||
w.Header().Set("Cache-Control", "no-cache")
|
||||
}
|
||||
http.ServeContent(w, r, f.Basename, f.ModTime, content)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// VisualFile is an interface for files that have a width and height.
|
||||
type VisualFile interface {
|
||||
File
|
||||
GetWidth() int
|
||||
GetHeight() int
|
||||
GetFormat() string
|
||||
}
|
||||
|
||||
func GetMinResolution(f VisualFile) int {
|
||||
w := f.GetWidth()
|
||||
h := f.GetHeight()
|
||||
|
||||
if w < h {
|
||||
return w
|
||||
}
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
// ImageFile is an extension of BaseFile to represent image files.
|
||||
type ImageFile struct {
|
||||
*BaseFile
|
||||
Format string `json:"format"`
|
||||
Width int `json:"width"`
|
||||
Height int `json:"height"`
|
||||
}
|
||||
|
||||
func (f ImageFile) GetWidth() int {
|
||||
return f.Width
|
||||
}
|
||||
|
||||
func (f ImageFile) GetHeight() int {
|
||||
return f.Height
|
||||
}
|
||||
|
||||
func (f ImageFile) GetFormat() string {
|
||||
return f.Format
|
||||
}
|
||||
|
||||
// VideoFile is an extension of BaseFile to represent video files.
|
||||
type VideoFile struct {
|
||||
*BaseFile
|
||||
Format string `json:"format"`
|
||||
Width int `json:"width"`
|
||||
Height int `json:"height"`
|
||||
Duration float64 `json:"duration"`
|
||||
VideoCodec string `json:"video_codec"`
|
||||
AudioCodec string `json:"audio_codec"`
|
||||
FrameRate float64 `json:"frame_rate"`
|
||||
BitRate int64 `json:"bitrate"`
|
||||
|
||||
Interactive bool `json:"interactive"`
|
||||
InteractiveSpeed *int `json:"interactive_speed"`
|
||||
}
|
||||
|
||||
func (f VideoFile) GetWidth() int {
|
||||
return f.Width
|
||||
}
|
||||
|
||||
func (f VideoFile) GetHeight() int {
|
||||
return f.Height
|
||||
}
|
||||
|
||||
func (f VideoFile) GetFormat() string {
|
||||
return f.Format
|
||||
}
|
||||
|
||||
// #1572 - Inf and NaN values cause the JSON marshaller to fail
|
||||
// Replace these values with 0 rather than erroring
|
||||
|
||||
func (f VideoFile) DurationFinite() float64 {
|
||||
ret := f.Duration
|
||||
if math.IsInf(ret, 0) || math.IsNaN(ret) {
|
||||
return 0
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (f VideoFile) FrameRateFinite() float64 {
|
||||
ret := f.FrameRate
|
||||
if math.IsInf(ret, 0) || math.IsNaN(ret) {
|
||||
return 0
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
51
pkg/models/model_folder.go
Normal file
51
pkg/models/model_folder.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// FolderID represents an ID of a folder.
|
||||
type FolderID int32
|
||||
|
||||
// String converts the ID to a string.
|
||||
func (i FolderID) String() string {
|
||||
return strconv.Itoa(int(i))
|
||||
}
|
||||
|
||||
func (i *FolderID) UnmarshalGQL(v interface{}) (err error) {
|
||||
switch v := v.(type) {
|
||||
case string:
|
||||
var id int
|
||||
id, err = strconv.Atoi(v)
|
||||
*i = FolderID(id)
|
||||
return err
|
||||
case int:
|
||||
*i = FolderID(v)
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("%T is not an int", v)
|
||||
}
|
||||
}
|
||||
|
||||
func (i FolderID) MarshalGQL(w io.Writer) {
|
||||
fmt.Fprint(w, strconv.Quote(i.String()))
|
||||
}
|
||||
|
||||
// Folder represents a folder in the file system.
|
||||
type Folder struct {
|
||||
ID FolderID `json:"id"`
|
||||
DirEntry
|
||||
Path string `json:"path"`
|
||||
ParentFolderID *FolderID `json:"parent_folder_id"`
|
||||
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
func (f *Folder) Info(fs FS) (fs.FileInfo, error) {
|
||||
return f.info(fs, f.Path)
|
||||
}
|
||||
@@ -5,8 +5,6 @@ import (
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/stashapp/stash/pkg/file"
|
||||
)
|
||||
|
||||
type Gallery struct {
|
||||
@@ -24,11 +22,11 @@ type Gallery struct {
|
||||
// transient - not persisted
|
||||
Files RelatedFiles
|
||||
// transient - not persisted
|
||||
PrimaryFileID *file.ID
|
||||
PrimaryFileID *FileID
|
||||
// transient - path of primary file or folder
|
||||
Path string
|
||||
|
||||
FolderID *file.FolderID `json:"folder_id"`
|
||||
FolderID *FolderID `json:"folder_id"`
|
||||
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
@@ -45,13 +43,13 @@ func (g *Gallery) IsUserCreated() bool {
|
||||
}
|
||||
|
||||
func (g *Gallery) LoadFiles(ctx context.Context, l FileLoader) error {
|
||||
return g.Files.load(func() ([]file.File, error) {
|
||||
return g.Files.load(func() ([]File, error) {
|
||||
return l.GetFiles(ctx, g.ID)
|
||||
})
|
||||
}
|
||||
|
||||
func (g *Gallery) LoadPrimaryFile(ctx context.Context, l file.Finder) error {
|
||||
return g.Files.loadPrimary(func() (file.File, error) {
|
||||
func (g *Gallery) LoadPrimaryFile(ctx context.Context, l FileGetter) error {
|
||||
return g.Files.loadPrimary(func() (File, error) {
|
||||
if g.PrimaryFileID == nil {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -89,7 +87,7 @@ func (g *Gallery) LoadTagIDs(ctx context.Context, l TagIDLoader) error {
|
||||
func (g Gallery) PrimaryChecksum() string {
|
||||
// renamed from Checksum to prevent gqlgen from using it in the resolver
|
||||
if p := g.Files.Primary(); p != nil {
|
||||
v := p.Base().Fingerprints.Get(file.FingerprintTypeMD5)
|
||||
v := p.Base().Fingerprints.Get(FingerprintTypeMD5)
|
||||
if v == nil {
|
||||
return ""
|
||||
}
|
||||
@@ -120,7 +118,7 @@ type GalleryPartial struct {
|
||||
SceneIDs *UpdateIDs
|
||||
TagIDs *UpdateIDs
|
||||
PerformerIDs *UpdateIDs
|
||||
PrimaryFileID *file.ID
|
||||
PrimaryFileID *FileID
|
||||
}
|
||||
|
||||
func NewGalleryPartial() GalleryPartial {
|
||||
|
||||
@@ -5,8 +5,6 @@ import (
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/stashapp/stash/pkg/file"
|
||||
)
|
||||
|
||||
// Image stores the metadata for a single image.
|
||||
@@ -24,7 +22,7 @@ type Image struct {
|
||||
|
||||
// transient - not persisted
|
||||
Files RelatedFiles
|
||||
PrimaryFileID *file.ID
|
||||
PrimaryFileID *FileID
|
||||
// transient - path of primary file - empty if no files
|
||||
Path string
|
||||
// transient - checksum of primary file - empty if no files
|
||||
@@ -39,13 +37,13 @@ type Image struct {
|
||||
}
|
||||
|
||||
func (i *Image) LoadFiles(ctx context.Context, l FileLoader) error {
|
||||
return i.Files.load(func() ([]file.File, error) {
|
||||
return i.Files.load(func() ([]File, error) {
|
||||
return l.GetFiles(ctx, i.ID)
|
||||
})
|
||||
}
|
||||
|
||||
func (i *Image) LoadPrimaryFile(ctx context.Context, l file.Finder) error {
|
||||
return i.Files.loadPrimary(func() (file.File, error) {
|
||||
func (i *Image) LoadPrimaryFile(ctx context.Context, l FileGetter) error {
|
||||
return i.Files.loadPrimary(func() (File, error) {
|
||||
if i.PrimaryFileID == nil {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -107,7 +105,7 @@ func (i Image) DisplayName() string {
|
||||
|
||||
type ImageCreateInput struct {
|
||||
*Image
|
||||
FileIDs []file.ID
|
||||
FileIDs []FileID
|
||||
}
|
||||
|
||||
type ImagePartial struct {
|
||||
@@ -125,7 +123,7 @@ type ImagePartial struct {
|
||||
GalleryIDs *UpdateIDs
|
||||
TagIDs *UpdateIDs
|
||||
PerformerIDs *UpdateIDs
|
||||
PrimaryFileID *file.ID
|
||||
PrimaryFileID *FileID
|
||||
}
|
||||
|
||||
func NewImagePartial() ImagePartial {
|
||||
|
||||
@@ -6,8 +6,6 @@ import (
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/stashapp/stash/pkg/file"
|
||||
)
|
||||
|
||||
// Scene stores the metadata for a single video scene.
|
||||
@@ -26,7 +24,7 @@ type Scene struct {
|
||||
|
||||
// transient - not persisted
|
||||
Files RelatedVideoFiles
|
||||
PrimaryFileID *file.ID
|
||||
PrimaryFileID *FileID
|
||||
// transient - path of primary file - empty if no files
|
||||
Path string
|
||||
// transient - oshash of primary file - empty if no files
|
||||
@@ -57,13 +55,13 @@ func (s *Scene) LoadURLs(ctx context.Context, l URLLoader) error {
|
||||
}
|
||||
|
||||
func (s *Scene) LoadFiles(ctx context.Context, l VideoFileLoader) error {
|
||||
return s.Files.load(func() ([]*file.VideoFile, error) {
|
||||
return s.Files.load(func() ([]*VideoFile, error) {
|
||||
return l.GetFiles(ctx, s.ID)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Scene) LoadPrimaryFile(ctx context.Context, l file.Finder) error {
|
||||
return s.Files.loadPrimary(func() (*file.VideoFile, error) {
|
||||
func (s *Scene) LoadPrimaryFile(ctx context.Context, l FileGetter) error {
|
||||
return s.Files.loadPrimary(func() (*VideoFile, error) {
|
||||
if s.PrimaryFileID == nil {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -73,10 +71,10 @@ func (s *Scene) LoadPrimaryFile(ctx context.Context, l file.Finder) error {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var vf *file.VideoFile
|
||||
var vf *VideoFile
|
||||
if len(f) > 0 {
|
||||
var ok bool
|
||||
vf, ok = f[0].(*file.VideoFile)
|
||||
vf, ok = f[0].(*VideoFile)
|
||||
if !ok {
|
||||
return nil, errors.New("not a video file")
|
||||
}
|
||||
@@ -173,7 +171,7 @@ type ScenePartial struct {
|
||||
PerformerIDs *UpdateIDs
|
||||
MovieIDs *UpdateMovieIDs
|
||||
StashIDs *UpdateStashIDs
|
||||
PrimaryFileID *file.ID
|
||||
PrimaryFileID *FileID
|
||||
}
|
||||
|
||||
func NewScenePartial() ScenePartial {
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package models
|
||||
|
||||
import "context"
|
||||
|
||||
type MovieFilterType struct {
|
||||
Name *StringCriterionInput `json:"name"`
|
||||
Director *StringCriterionInput `json:"director"`
|
||||
@@ -27,37 +25,3 @@ type MovieFilterType struct {
|
||||
// Filter by updated at
|
||||
UpdatedAt *TimestampCriterionInput `json:"updated_at"`
|
||||
}
|
||||
|
||||
type MovieReader interface {
|
||||
Find(ctx context.Context, id int) (*Movie, error)
|
||||
FindMany(ctx context.Context, ids []int) ([]*Movie, error)
|
||||
// FindBySceneID(sceneID int) ([]*Movie, error)
|
||||
FindByName(ctx context.Context, name string, nocase bool) (*Movie, error)
|
||||
FindByNames(ctx context.Context, names []string, nocase bool) ([]*Movie, error)
|
||||
All(ctx context.Context) ([]*Movie, error)
|
||||
Count(ctx context.Context) (int, error)
|
||||
Query(ctx context.Context, movieFilter *MovieFilterType, findFilter *FindFilterType) ([]*Movie, int, error)
|
||||
QueryCount(ctx context.Context, movieFilter *MovieFilterType, findFilter *FindFilterType) (int, error)
|
||||
GetFrontImage(ctx context.Context, movieID int) ([]byte, error)
|
||||
HasFrontImage(ctx context.Context, movieID int) (bool, error)
|
||||
GetBackImage(ctx context.Context, movieID int) ([]byte, error)
|
||||
HasBackImage(ctx context.Context, movieID int) (bool, error)
|
||||
FindByPerformerID(ctx context.Context, performerID int) ([]*Movie, error)
|
||||
CountByPerformerID(ctx context.Context, performerID int) (int, error)
|
||||
FindByStudioID(ctx context.Context, studioID int) ([]*Movie, error)
|
||||
CountByStudioID(ctx context.Context, studioID int) (int, error)
|
||||
}
|
||||
|
||||
type MovieWriter interface {
|
||||
Create(ctx context.Context, newMovie *Movie) error
|
||||
UpdatePartial(ctx context.Context, id int, updatedMovie MoviePartial) (*Movie, error)
|
||||
Update(ctx context.Context, updatedMovie *Movie) error
|
||||
Destroy(ctx context.Context, id int) error
|
||||
UpdateFrontImage(ctx context.Context, movieID int, frontImage []byte) error
|
||||
UpdateBackImage(ctx context.Context, movieID int, backImage []byte) error
|
||||
}
|
||||
|
||||
type MovieReaderWriter interface {
|
||||
MovieReader
|
||||
MovieWriter
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
@@ -193,44 +192,3 @@ type PerformerFilterType struct {
|
||||
// Filter by updated at
|
||||
UpdatedAt *TimestampCriterionInput `json:"updated_at"`
|
||||
}
|
||||
|
||||
type PerformerFinder interface {
|
||||
FindMany(ctx context.Context, ids []int) ([]*Performer, error)
|
||||
}
|
||||
|
||||
type PerformerReader interface {
|
||||
Find(ctx context.Context, id int) (*Performer, error)
|
||||
PerformerFinder
|
||||
FindBySceneID(ctx context.Context, sceneID int) ([]*Performer, error)
|
||||
FindByImageID(ctx context.Context, imageID int) ([]*Performer, error)
|
||||
FindByGalleryID(ctx context.Context, galleryID int) ([]*Performer, error)
|
||||
FindByNames(ctx context.Context, names []string, nocase bool) ([]*Performer, error)
|
||||
FindByStashID(ctx context.Context, stashID StashID) ([]*Performer, error)
|
||||
FindByStashIDStatus(ctx context.Context, hasStashID bool, stashboxEndpoint string) ([]*Performer, error)
|
||||
CountByTagID(ctx context.Context, tagID int) (int, error)
|
||||
Count(ctx context.Context) (int, error)
|
||||
All(ctx context.Context) ([]*Performer, error)
|
||||
// TODO - this interface is temporary until the filter schema can fully
|
||||
// support the query needed
|
||||
QueryForAutoTag(ctx context.Context, words []string) ([]*Performer, error)
|
||||
Query(ctx context.Context, performerFilter *PerformerFilterType, findFilter *FindFilterType) ([]*Performer, int, error)
|
||||
QueryCount(ctx context.Context, galleryFilter *PerformerFilterType, findFilter *FindFilterType) (int, error)
|
||||
AliasLoader
|
||||
GetImage(ctx context.Context, performerID int) ([]byte, error)
|
||||
HasImage(ctx context.Context, performerID int) (bool, error)
|
||||
StashIDLoader
|
||||
TagIDLoader
|
||||
}
|
||||
|
||||
type PerformerWriter interface {
|
||||
Create(ctx context.Context, newPerformer *Performer) error
|
||||
UpdatePartial(ctx context.Context, id int, updatedPerformer PerformerPartial) (*Performer, error)
|
||||
Update(ctx context.Context, updatedPerformer *Performer) error
|
||||
Destroy(ctx context.Context, id int) error
|
||||
UpdateImage(ctx context.Context, performerID int, image []byte) error
|
||||
}
|
||||
|
||||
type PerformerReaderWriter interface {
|
||||
PerformerReader
|
||||
PerformerWriter
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/stashapp/stash/pkg/file"
|
||||
)
|
||||
import "context"
|
||||
|
||||
type SceneIDLoader interface {
|
||||
GetSceneIDs(ctx context.Context, relatedID int) ([]int, error)
|
||||
}
|
||||
|
||||
type ImageIDLoader interface {
|
||||
GetImageIDs(ctx context.Context, relatedID int) ([]int, error)
|
||||
}
|
||||
|
||||
type GalleryIDLoader interface {
|
||||
GetGalleryIDs(ctx context.Context, relatedID int) ([]int, error)
|
||||
}
|
||||
@@ -22,6 +22,10 @@ type TagIDLoader interface {
|
||||
GetTagIDs(ctx context.Context, relatedID int) ([]int, error)
|
||||
}
|
||||
|
||||
type FileIDLoader interface {
|
||||
GetManyFileIDs(ctx context.Context, ids []int) ([][]FileID, error)
|
||||
}
|
||||
|
||||
type SceneMovieLoader interface {
|
||||
GetMovies(ctx context.Context, id int) ([]MoviesScenes, error)
|
||||
}
|
||||
@@ -31,11 +35,11 @@ type StashIDLoader interface {
|
||||
}
|
||||
|
||||
type VideoFileLoader interface {
|
||||
GetFiles(ctx context.Context, relatedID int) ([]*file.VideoFile, error)
|
||||
GetFiles(ctx context.Context, relatedID int) ([]*VideoFile, error)
|
||||
}
|
||||
|
||||
type FileLoader interface {
|
||||
GetFiles(ctx context.Context, relatedID int) ([]file.File, error)
|
||||
GetFiles(ctx context.Context, relatedID int) ([]File, error)
|
||||
}
|
||||
|
||||
type AliasLoader interface {
|
||||
@@ -224,12 +228,12 @@ func (r *RelatedStashIDs) load(fn func() ([]StashID, error)) error {
|
||||
}
|
||||
|
||||
type RelatedVideoFiles struct {
|
||||
primaryFile *file.VideoFile
|
||||
files []*file.VideoFile
|
||||
primaryFile *VideoFile
|
||||
files []*VideoFile
|
||||
primaryLoaded bool
|
||||
}
|
||||
|
||||
func NewRelatedVideoFiles(files []*file.VideoFile) RelatedVideoFiles {
|
||||
func NewRelatedVideoFiles(files []*VideoFile) RelatedVideoFiles {
|
||||
ret := RelatedVideoFiles{
|
||||
files: files,
|
||||
primaryLoaded: true,
|
||||
@@ -242,12 +246,12 @@ func NewRelatedVideoFiles(files []*file.VideoFile) RelatedVideoFiles {
|
||||
return ret
|
||||
}
|
||||
|
||||
func (r *RelatedVideoFiles) SetPrimary(f *file.VideoFile) {
|
||||
func (r *RelatedVideoFiles) SetPrimary(f *VideoFile) {
|
||||
r.primaryFile = f
|
||||
r.primaryLoaded = true
|
||||
}
|
||||
|
||||
func (r *RelatedVideoFiles) Set(f []*file.VideoFile) {
|
||||
func (r *RelatedVideoFiles) Set(f []*VideoFile) {
|
||||
r.files = f
|
||||
if len(r.files) > 0 {
|
||||
r.primaryFile = r.files[0]
|
||||
@@ -267,7 +271,7 @@ func (r RelatedVideoFiles) PrimaryLoaded() bool {
|
||||
}
|
||||
|
||||
// List returns the related files. Panics if the relationship has not been loaded.
|
||||
func (r RelatedVideoFiles) List() []*file.VideoFile {
|
||||
func (r RelatedVideoFiles) List() []*VideoFile {
|
||||
if !r.Loaded() {
|
||||
panic("relationship has not been loaded")
|
||||
}
|
||||
@@ -276,7 +280,7 @@ func (r RelatedVideoFiles) List() []*file.VideoFile {
|
||||
}
|
||||
|
||||
// Primary returns the primary file. Panics if the relationship has not been loaded.
|
||||
func (r RelatedVideoFiles) Primary() *file.VideoFile {
|
||||
func (r RelatedVideoFiles) Primary() *VideoFile {
|
||||
if !r.PrimaryLoaded() {
|
||||
panic("relationship has not been loaded")
|
||||
}
|
||||
@@ -284,7 +288,7 @@ func (r RelatedVideoFiles) Primary() *file.VideoFile {
|
||||
return r.primaryFile
|
||||
}
|
||||
|
||||
func (r *RelatedVideoFiles) load(fn func() ([]*file.VideoFile, error)) error {
|
||||
func (r *RelatedVideoFiles) load(fn func() ([]*VideoFile, error)) error {
|
||||
if r.Loaded() {
|
||||
return nil
|
||||
}
|
||||
@@ -304,7 +308,7 @@ func (r *RelatedVideoFiles) load(fn func() ([]*file.VideoFile, error)) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *RelatedVideoFiles) loadPrimary(fn func() (*file.VideoFile, error)) error {
|
||||
func (r *RelatedVideoFiles) loadPrimary(fn func() (*VideoFile, error)) error {
|
||||
if r.PrimaryLoaded() {
|
||||
return nil
|
||||
}
|
||||
@@ -321,12 +325,12 @@ func (r *RelatedVideoFiles) loadPrimary(fn func() (*file.VideoFile, error)) erro
|
||||
}
|
||||
|
||||
type RelatedFiles struct {
|
||||
primaryFile file.File
|
||||
files []file.File
|
||||
primaryFile File
|
||||
files []File
|
||||
primaryLoaded bool
|
||||
}
|
||||
|
||||
func NewRelatedFiles(files []file.File) RelatedFiles {
|
||||
func NewRelatedFiles(files []File) RelatedFiles {
|
||||
ret := RelatedFiles{
|
||||
files: files,
|
||||
primaryLoaded: true,
|
||||
@@ -350,7 +354,7 @@ func (r RelatedFiles) PrimaryLoaded() bool {
|
||||
}
|
||||
|
||||
// List returns the related files. Panics if the relationship has not been loaded.
|
||||
func (r RelatedFiles) List() []file.File {
|
||||
func (r RelatedFiles) List() []File {
|
||||
if !r.Loaded() {
|
||||
panic("relationship has not been loaded")
|
||||
}
|
||||
@@ -359,7 +363,7 @@ func (r RelatedFiles) List() []file.File {
|
||||
}
|
||||
|
||||
// Primary returns the primary file. Panics if the relationship has not been loaded.
|
||||
func (r RelatedFiles) Primary() file.File {
|
||||
func (r RelatedFiles) Primary() File {
|
||||
if !r.PrimaryLoaded() {
|
||||
panic("relationship has not been loaded")
|
||||
}
|
||||
@@ -367,7 +371,7 @@ func (r RelatedFiles) Primary() file.File {
|
||||
return r.primaryFile
|
||||
}
|
||||
|
||||
func (r *RelatedFiles) load(fn func() ([]file.File, error)) error {
|
||||
func (r *RelatedFiles) load(fn func() ([]File, error)) error {
|
||||
if r.Loaded() {
|
||||
return nil
|
||||
}
|
||||
@@ -387,7 +391,7 @@ func (r *RelatedFiles) load(fn func() ([]file.File, error)) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *RelatedFiles) loadPrimary(fn func() (file.File, error)) error {
|
||||
func (r *RelatedFiles) loadPrimary(fn func() (File, error)) error {
|
||||
if r.PrimaryLoaded() {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/stashapp/stash/pkg/file"
|
||||
"github.com/stashapp/stash/pkg/txn"
|
||||
)
|
||||
|
||||
@@ -14,8 +13,8 @@ type TxnManager interface {
|
||||
type Repository struct {
|
||||
TxnManager
|
||||
|
||||
File file.Store
|
||||
Folder file.FolderStore
|
||||
File FileReaderWriter
|
||||
Folder FolderReaderWriter
|
||||
Gallery GalleryReaderWriter
|
||||
GalleryChapter GalleryChapterReaderWriter
|
||||
Image ImageReaderWriter
|
||||
|
||||
88
pkg/models/repository_file.go
Normal file
88
pkg/models/repository_file.go
Normal file
@@ -0,0 +1,88 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/fs"
|
||||
)
|
||||
|
||||
// FileGetter provides methods to get files by ID.
|
||||
type FileGetter interface {
|
||||
Find(ctx context.Context, id ...FileID) ([]File, error)
|
||||
}
|
||||
|
||||
// FileFinder provides methods to find files.
|
||||
type FileFinder interface {
|
||||
FileGetter
|
||||
FindAllByPath(ctx context.Context, path string) ([]File, error)
|
||||
FindAllInPaths(ctx context.Context, p []string, limit, offset int) ([]File, error)
|
||||
FindByPath(ctx context.Context, path string) (File, error)
|
||||
FindByFingerprint(ctx context.Context, fp Fingerprint) ([]File, error)
|
||||
FindByZipFileID(ctx context.Context, zipFileID FileID) ([]File, error)
|
||||
FindByFileInfo(ctx context.Context, info fs.FileInfo, size int64) ([]File, error)
|
||||
}
|
||||
|
||||
// FileQueryer provides methods to query files.
|
||||
type FileQueryer interface {
|
||||
Query(ctx context.Context, options FileQueryOptions) (*FileQueryResult, error)
|
||||
}
|
||||
|
||||
// FileCounter provides methods to count files.
|
||||
type FileCounter interface {
|
||||
CountAllInPaths(ctx context.Context, p []string) (int, error)
|
||||
CountByFolderID(ctx context.Context, folderID FolderID) (int, error)
|
||||
}
|
||||
|
||||
// FileCreator provides methods to create files.
|
||||
type FileCreator interface {
|
||||
Create(ctx context.Context, f File) error
|
||||
}
|
||||
|
||||
// FileUpdater provides methods to update files.
|
||||
type FileUpdater interface {
|
||||
Update(ctx context.Context, f File) error
|
||||
}
|
||||
|
||||
// FileDestroyer provides methods to destroy files.
|
||||
type FileDestroyer interface {
|
||||
Destroy(ctx context.Context, id FileID) error
|
||||
}
|
||||
|
||||
type FileFinderCreator interface {
|
||||
FileFinder
|
||||
FileCreator
|
||||
}
|
||||
|
||||
type FileFinderUpdater interface {
|
||||
FileFinder
|
||||
FileUpdater
|
||||
}
|
||||
|
||||
type FileFinderDestroyer interface {
|
||||
FileFinder
|
||||
FileDestroyer
|
||||
}
|
||||
|
||||
// FileReader provides all methods to read files.
|
||||
type FileReader interface {
|
||||
FileFinder
|
||||
FileQueryer
|
||||
FileCounter
|
||||
|
||||
GetCaptions(ctx context.Context, fileID FileID) ([]*VideoCaption, error)
|
||||
IsPrimary(ctx context.Context, fileID FileID) (bool, error)
|
||||
}
|
||||
|
||||
// FileWriter provides all methods to modify files.
|
||||
type FileWriter interface {
|
||||
FileCreator
|
||||
FileUpdater
|
||||
FileDestroyer
|
||||
|
||||
UpdateCaptions(ctx context.Context, fileID FileID, captions []*VideoCaption) error
|
||||
}
|
||||
|
||||
// FileReaderWriter provides all file methods.
|
||||
type FileReaderWriter interface {
|
||||
FileReader
|
||||
FileWriter
|
||||
}
|
||||
64
pkg/models/repository_folder.go
Normal file
64
pkg/models/repository_folder.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package models
|
||||
|
||||
import "context"
|
||||
|
||||
// FolderGetter provides methods to get folders by ID.
|
||||
type FolderGetter interface {
|
||||
Find(ctx context.Context, id FolderID) (*Folder, error)
|
||||
}
|
||||
|
||||
// FolderFinder provides methods to find folders.
|
||||
type FolderFinder interface {
|
||||
FolderGetter
|
||||
FindAllInPaths(ctx context.Context, p []string, limit, offset int) ([]*Folder, error)
|
||||
FindByPath(ctx context.Context, path string) (*Folder, error)
|
||||
FindByZipFileID(ctx context.Context, zipFileID FileID) ([]*Folder, error)
|
||||
FindByParentFolderID(ctx context.Context, parentFolderID FolderID) ([]*Folder, error)
|
||||
}
|
||||
|
||||
type FolderCounter interface {
|
||||
CountAllInPaths(ctx context.Context, p []string) (int, error)
|
||||
}
|
||||
|
||||
// FolderCreator provides methods to create folders.
|
||||
type FolderCreator interface {
|
||||
Create(ctx context.Context, f *Folder) error
|
||||
}
|
||||
|
||||
// FolderUpdater provides methods to update folders.
|
||||
type FolderUpdater interface {
|
||||
Update(ctx context.Context, f *Folder) error
|
||||
}
|
||||
|
||||
type FolderDestroyer interface {
|
||||
Destroy(ctx context.Context, id FolderID) error
|
||||
}
|
||||
|
||||
type FolderFinderCreator interface {
|
||||
FolderFinder
|
||||
FolderCreator
|
||||
}
|
||||
|
||||
type FolderFinderDestroyer interface {
|
||||
FolderFinder
|
||||
FolderDestroyer
|
||||
}
|
||||
|
||||
// FolderReader provides all methods to read folders.
|
||||
type FolderReader interface {
|
||||
FolderFinder
|
||||
FolderCounter
|
||||
}
|
||||
|
||||
// FolderWriter provides all methods to modify folders.
|
||||
type FolderWriter interface {
|
||||
FolderCreator
|
||||
FolderUpdater
|
||||
FolderDestroyer
|
||||
}
|
||||
|
||||
// FolderReaderWriter provides all folder methods.
|
||||
type FolderReaderWriter interface {
|
||||
FolderReader
|
||||
FolderWriter
|
||||
}
|
||||
91
pkg/models/repository_gallery.go
Normal file
91
pkg/models/repository_gallery.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package models
|
||||
|
||||
import "context"
|
||||
|
||||
// GalleryGetter provides methods to get galleries by ID.
|
||||
type GalleryGetter interface {
|
||||
// TODO - rename this to Find and remove existing method
|
||||
FindMany(ctx context.Context, ids []int) ([]*Gallery, error)
|
||||
Find(ctx context.Context, id int) (*Gallery, error)
|
||||
}
|
||||
|
||||
// GalleryFinder provides methods to find galleries.
|
||||
type GalleryFinder interface {
|
||||
GalleryGetter
|
||||
FindByFingerprints(ctx context.Context, fp []Fingerprint) ([]*Gallery, error)
|
||||
FindByChecksum(ctx context.Context, checksum string) ([]*Gallery, error)
|
||||
FindByChecksums(ctx context.Context, checksums []string) ([]*Gallery, error)
|
||||
FindByPath(ctx context.Context, path string) ([]*Gallery, error)
|
||||
FindByFileID(ctx context.Context, fileID FileID) ([]*Gallery, error)
|
||||
FindByFolderID(ctx context.Context, folderID FolderID) ([]*Gallery, error)
|
||||
FindBySceneID(ctx context.Context, sceneID int) ([]*Gallery, error)
|
||||
FindByImageID(ctx context.Context, imageID int) ([]*Gallery, error)
|
||||
FindUserGalleryByTitle(ctx context.Context, title string) ([]*Gallery, error)
|
||||
}
|
||||
|
||||
// GalleryQueryer provides methods to query galleries.
|
||||
type GalleryQueryer interface {
|
||||
Query(ctx context.Context, galleryFilter *GalleryFilterType, findFilter *FindFilterType) ([]*Gallery, int, error)
|
||||
QueryCount(ctx context.Context, galleryFilter *GalleryFilterType, findFilter *FindFilterType) (int, error)
|
||||
}
|
||||
|
||||
// GalleryCounter provides methods to count galleries.
|
||||
type GalleryCounter interface {
|
||||
Count(ctx context.Context) (int, error)
|
||||
CountByFileID(ctx context.Context, fileID FileID) (int, error)
|
||||
}
|
||||
|
||||
// GalleryCreator provides methods to create galleries.
|
||||
type GalleryCreator interface {
|
||||
Create(ctx context.Context, newGallery *Gallery, fileIDs []FileID) error
|
||||
}
|
||||
|
||||
// GalleryUpdater provides methods to update galleries.
|
||||
type GalleryUpdater interface {
|
||||
Update(ctx context.Context, updatedGallery *Gallery) error
|
||||
UpdatePartial(ctx context.Context, id int, updatedGallery GalleryPartial) (*Gallery, error)
|
||||
UpdateImages(ctx context.Context, galleryID int, imageIDs []int) error
|
||||
}
|
||||
|
||||
// GalleryDestroyer provides methods to destroy galleries.
|
||||
type GalleryDestroyer interface {
|
||||
Destroy(ctx context.Context, id int) error
|
||||
}
|
||||
|
||||
type GalleryCreatorUpdater interface {
|
||||
GalleryCreator
|
||||
GalleryUpdater
|
||||
}
|
||||
|
||||
// GalleryReader provides all methods to read galleries.
|
||||
type GalleryReader interface {
|
||||
GalleryFinder
|
||||
GalleryQueryer
|
||||
GalleryCounter
|
||||
|
||||
FileIDLoader
|
||||
ImageIDLoader
|
||||
SceneIDLoader
|
||||
PerformerIDLoader
|
||||
TagIDLoader
|
||||
FileLoader
|
||||
|
||||
All(ctx context.Context) ([]*Gallery, error)
|
||||
}
|
||||
|
||||
// GalleryWriter provides all methods to modify galleries.
|
||||
type GalleryWriter interface {
|
||||
GalleryCreator
|
||||
GalleryUpdater
|
||||
GalleryDestroyer
|
||||
|
||||
AddFileID(ctx context.Context, id int, fileID FileID) error
|
||||
AddImages(ctx context.Context, galleryID int, imageIDs ...int) error
|
||||
RemoveImages(ctx context.Context, galleryID int, imageIDs ...int) error
|
||||
}
|
||||
|
||||
// GalleryReaderWriter provides all gallery methods.
|
||||
type GalleryReaderWriter interface {
|
||||
GalleryReader
|
||||
GalleryWriter
|
||||
}
|
||||
55
pkg/models/repository_gallery_chapter.go
Normal file
55
pkg/models/repository_gallery_chapter.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package models
|
||||
|
||||
import "context"
|
||||
|
||||
// GalleryChapterGetter provides methods to get gallery chapters by ID.
|
||||
type GalleryChapterGetter interface {
|
||||
// TODO - rename this to Find and remove existing method
|
||||
FindMany(ctx context.Context, ids []int) ([]*GalleryChapter, error)
|
||||
Find(ctx context.Context, id int) (*GalleryChapter, error)
|
||||
}
|
||||
|
||||
// GalleryChapterFinder provides methods to find gallery chapters.
|
||||
type GalleryChapterFinder interface {
|
||||
GalleryChapterGetter
|
||||
FindByGalleryID(ctx context.Context, galleryID int) ([]*GalleryChapter, error)
|
||||
}
|
||||
|
||||
// GalleryChapterCreator provides methods to create gallery chapters.
|
||||
type GalleryChapterCreator interface {
|
||||
Create(ctx context.Context, newGalleryChapter *GalleryChapter) error
|
||||
}
|
||||
|
||||
// GalleryChapterUpdater provides methods to update gallery chapters.
|
||||
type GalleryChapterUpdater interface {
|
||||
Update(ctx context.Context, updatedGalleryChapter *GalleryChapter) error
|
||||
UpdatePartial(ctx context.Context, id int, updatedGalleryChapter GalleryChapterPartial) (*GalleryChapter, error)
|
||||
}
|
||||
|
||||
// GalleryChapterDestroyer provides methods to destroy gallery chapters.
|
||||
type GalleryChapterDestroyer interface {
|
||||
Destroy(ctx context.Context, id int) error
|
||||
}
|
||||
|
||||
type GalleryChapterCreatorUpdater interface {
|
||||
GalleryChapterCreator
|
||||
GalleryChapterUpdater
|
||||
}
|
||||
|
||||
// GalleryChapterReader provides all methods to read gallery chapters.
|
||||
type GalleryChapterReader interface {
|
||||
GalleryChapterFinder
|
||||
}
|
||||
|
||||
// GalleryChapterWriter provides all methods to modify gallery chapters.
|
||||
type GalleryChapterWriter interface {
|
||||
GalleryChapterCreator
|
||||
GalleryChapterUpdater
|
||||
GalleryChapterDestroyer
|
||||
}
|
||||
|
||||
// GalleryChapterReaderWriter provides all gallery chapter methods.
|
||||
type GalleryChapterReaderWriter interface {
|
||||
GalleryChapterReader
|
||||
GalleryChapterWriter
|
||||
}
|
||||
92
pkg/models/repository_image.go
Normal file
92
pkg/models/repository_image.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package models
|
||||
|
||||
import "context"
|
||||
|
||||
// ImageGetter provides methods to get images by ID.
|
||||
type ImageGetter interface {
|
||||
// TODO - rename this to Find and remove existing method
|
||||
FindMany(ctx context.Context, ids []int) ([]*Image, error)
|
||||
Find(ctx context.Context, id int) (*Image, error)
|
||||
}
|
||||
|
||||
// ImageFinder provides methods to find images.
|
||||
type ImageFinder interface {
|
||||
ImageGetter
|
||||
FindByFingerprints(ctx context.Context, fp []Fingerprint) ([]*Image, error)
|
||||
FindByChecksum(ctx context.Context, checksum string) ([]*Image, error)
|
||||
FindByFileID(ctx context.Context, fileID FileID) ([]*Image, error)
|
||||
FindByFolderID(ctx context.Context, fileID FolderID) ([]*Image, error)
|
||||
FindByZipFileID(ctx context.Context, zipFileID FileID) ([]*Image, error)
|
||||
FindByGalleryID(ctx context.Context, galleryID int) ([]*Image, error)
|
||||
}
|
||||
|
||||
// ImageQueryer provides methods to query images.
|
||||
type ImageQueryer interface {
|
||||
Query(ctx context.Context, options ImageQueryOptions) (*ImageQueryResult, error)
|
||||
QueryCount(ctx context.Context, imageFilter *ImageFilterType, findFilter *FindFilterType) (int, error)
|
||||
}
|
||||
|
||||
// ImageCounter provides methods to count images.
|
||||
type ImageCounter interface {
|
||||
Count(ctx context.Context) (int, error)
|
||||
CountByFileID(ctx context.Context, fileID FileID) (int, error)
|
||||
CountByGalleryID(ctx context.Context, galleryID int) (int, error)
|
||||
OCountByPerformerID(ctx context.Context, performerID int) (int, error)
|
||||
}
|
||||
|
||||
// ImageCreator provides methods to create images.
|
||||
type ImageCreator interface {
|
||||
Create(ctx context.Context, newImage *ImageCreateInput) error
|
||||
}
|
||||
|
||||
// ImageUpdater provides methods to update images.
|
||||
type ImageUpdater interface {
|
||||
Update(ctx context.Context, updatedImage *Image) error
|
||||
UpdatePartial(ctx context.Context, id int, partial ImagePartial) (*Image, error)
|
||||
UpdatePerformers(ctx context.Context, imageID int, performerIDs []int) error
|
||||
UpdateTags(ctx context.Context, imageID int, tagIDs []int) error
|
||||
}
|
||||
|
||||
// ImageDestroyer provides methods to destroy images.
|
||||
type ImageDestroyer interface {
|
||||
Destroy(ctx context.Context, id int) error
|
||||
}
|
||||
|
||||
type ImageCreatorUpdater interface {
|
||||
ImageCreator
|
||||
ImageUpdater
|
||||
}
|
||||
|
||||
// ImageReader provides all methods to read images.
|
||||
type ImageReader interface {
|
||||
ImageFinder
|
||||
ImageQueryer
|
||||
ImageCounter
|
||||
|
||||
FileIDLoader
|
||||
GalleryIDLoader
|
||||
PerformerIDLoader
|
||||
TagIDLoader
|
||||
FileLoader
|
||||
|
||||
All(ctx context.Context) ([]*Image, error)
|
||||
Size(ctx context.Context) (float64, error)
|
||||
}
|
||||
|
||||
// ImageWriter provides all methods to modify images.
|
||||
type ImageWriter interface {
|
||||
ImageCreator
|
||||
ImageUpdater
|
||||
ImageDestroyer
|
||||
|
||||
AddFileID(ctx context.Context, id int, fileID FileID) error
|
||||
IncrementOCounter(ctx context.Context, id int) (int, error)
|
||||
DecrementOCounter(ctx context.Context, id int) (int, error)
|
||||
ResetOCounter(ctx context.Context, id int) (int, error)
|
||||
}
|
||||
|
||||
// ImageReaderWriter provides all image methods.
|
||||
type ImageReaderWriter interface {
|
||||
ImageReader
|
||||
ImageWriter
|
||||
}
|
||||
86
pkg/models/repository_movie.go
Normal file
86
pkg/models/repository_movie.go
Normal file
@@ -0,0 +1,86 @@
|
||||
package models
|
||||
|
||||
import "context"
|
||||
|
||||
// MovieGetter provides methods to get movies by ID.
|
||||
type MovieGetter interface {
|
||||
// TODO - rename this to Find and remove existing method
|
||||
FindMany(ctx context.Context, ids []int) ([]*Movie, error)
|
||||
Find(ctx context.Context, id int) (*Movie, error)
|
||||
}
|
||||
|
||||
// MovieFinder provides methods to find movies.
|
||||
type MovieFinder interface {
|
||||
MovieGetter
|
||||
FindByPerformerID(ctx context.Context, performerID int) ([]*Movie, error)
|
||||
FindByStudioID(ctx context.Context, studioID int) ([]*Movie, error)
|
||||
FindByName(ctx context.Context, name string, nocase bool) (*Movie, error)
|
||||
FindByNames(ctx context.Context, names []string, nocase bool) ([]*Movie, error)
|
||||
}
|
||||
|
||||
// MovieQueryer provides methods to query movies.
|
||||
type MovieQueryer interface {
|
||||
Query(ctx context.Context, movieFilter *MovieFilterType, findFilter *FindFilterType) ([]*Movie, int, error)
|
||||
QueryCount(ctx context.Context, movieFilter *MovieFilterType, findFilter *FindFilterType) (int, error)
|
||||
}
|
||||
|
||||
// MovieCounter provides methods to count movies.
|
||||
type MovieCounter interface {
|
||||
Count(ctx context.Context) (int, error)
|
||||
CountByPerformerID(ctx context.Context, performerID int) (int, error)
|
||||
CountByStudioID(ctx context.Context, studioID int) (int, error)
|
||||
}
|
||||
|
||||
// MovieCreator provides methods to create movies.
|
||||
type MovieCreator interface {
|
||||
Create(ctx context.Context, newMovie *Movie) error
|
||||
}
|
||||
|
||||
// MovieUpdater provides methods to update movies.
|
||||
type MovieUpdater interface {
|
||||
Update(ctx context.Context, updatedMovie *Movie) error
|
||||
UpdatePartial(ctx context.Context, id int, updatedMovie MoviePartial) (*Movie, error)
|
||||
UpdateFrontImage(ctx context.Context, movieID int, frontImage []byte) error
|
||||
UpdateBackImage(ctx context.Context, movieID int, backImage []byte) error
|
||||
}
|
||||
|
||||
// MovieDestroyer provides methods to destroy movies.
|
||||
type MovieDestroyer interface {
|
||||
Destroy(ctx context.Context, id int) error
|
||||
}
|
||||
|
||||
type MovieCreatorUpdater interface {
|
||||
MovieCreator
|
||||
MovieUpdater
|
||||
}
|
||||
|
||||
type MovieFinderCreator interface {
|
||||
MovieFinder
|
||||
MovieCreator
|
||||
}
|
||||
|
||||
// MovieReader provides all methods to read movies.
|
||||
type MovieReader interface {
|
||||
MovieFinder
|
||||
MovieQueryer
|
||||
MovieCounter
|
||||
|
||||
All(ctx context.Context) ([]*Movie, error)
|
||||
GetFrontImage(ctx context.Context, movieID int) ([]byte, error)
|
||||
HasFrontImage(ctx context.Context, movieID int) (bool, error)
|
||||
GetBackImage(ctx context.Context, movieID int) ([]byte, error)
|
||||
HasBackImage(ctx context.Context, movieID int) (bool, error)
|
||||
}
|
||||
|
||||
// MovieWriter provides all methods to modify movies.
|
||||
type MovieWriter interface {
|
||||
MovieCreator
|
||||
MovieUpdater
|
||||
MovieDestroyer
|
||||
}
|
||||
|
||||
// MovieReaderWriter provides all movie methods.
|
||||
type MovieReaderWriter interface {
|
||||
MovieReader
|
||||
MovieWriter
|
||||
}
|
||||
98
pkg/models/repository_performer.go
Normal file
98
pkg/models/repository_performer.go
Normal file
@@ -0,0 +1,98 @@
|
||||
package models
|
||||
|
||||
import "context"
|
||||
|
||||
// PerformerGetter provides methods to get performers by ID.
|
||||
type PerformerGetter interface {
|
||||
// TODO - rename this to Find and remove existing method
|
||||
FindMany(ctx context.Context, ids []int) ([]*Performer, error)
|
||||
Find(ctx context.Context, id int) (*Performer, error)
|
||||
}
|
||||
|
||||
// PerformerFinder provides methods to find performers.
|
||||
type PerformerFinder interface {
|
||||
PerformerGetter
|
||||
FindBySceneID(ctx context.Context, sceneID int) ([]*Performer, error)
|
||||
FindByImageID(ctx context.Context, imageID int) ([]*Performer, error)
|
||||
FindByGalleryID(ctx context.Context, galleryID int) ([]*Performer, error)
|
||||
FindByStashID(ctx context.Context, stashID StashID) ([]*Performer, error)
|
||||
FindByStashIDStatus(ctx context.Context, hasStashID bool, stashboxEndpoint string) ([]*Performer, error)
|
||||
FindByNames(ctx context.Context, names []string, nocase bool) ([]*Performer, error)
|
||||
}
|
||||
|
||||
// PerformerQueryer provides methods to query performers.
|
||||
type PerformerQueryer interface {
|
||||
Query(ctx context.Context, performerFilter *PerformerFilterType, findFilter *FindFilterType) ([]*Performer, int, error)
|
||||
QueryCount(ctx context.Context, galleryFilter *PerformerFilterType, findFilter *FindFilterType) (int, error)
|
||||
}
|
||||
|
||||
type PerformerAutoTagQueryer interface {
|
||||
PerformerQueryer
|
||||
AliasLoader
|
||||
|
||||
// TODO - this interface is temporary until the filter schema can fully
|
||||
// support the query needed
|
||||
QueryForAutoTag(ctx context.Context, words []string) ([]*Performer, error)
|
||||
}
|
||||
|
||||
// PerformerCounter provides methods to count performers.
|
||||
type PerformerCounter interface {
|
||||
Count(ctx context.Context) (int, error)
|
||||
CountByTagID(ctx context.Context, tagID int) (int, error)
|
||||
}
|
||||
|
||||
// PerformerCreator provides methods to create performers.
|
||||
type PerformerCreator interface {
|
||||
Create(ctx context.Context, newPerformer *Performer) error
|
||||
}
|
||||
|
||||
// PerformerUpdater provides methods to update performers.
|
||||
type PerformerUpdater interface {
|
||||
Update(ctx context.Context, updatedPerformer *Performer) error
|
||||
UpdatePartial(ctx context.Context, id int, updatedPerformer PerformerPartial) (*Performer, error)
|
||||
UpdateImage(ctx context.Context, performerID int, image []byte) error
|
||||
}
|
||||
|
||||
// PerformerDestroyer provides methods to destroy performers.
|
||||
type PerformerDestroyer interface {
|
||||
Destroy(ctx context.Context, id int) error
|
||||
}
|
||||
|
||||
type PerformerFinderCreator interface {
|
||||
PerformerFinder
|
||||
PerformerCreator
|
||||
}
|
||||
|
||||
type PerformerCreatorUpdater interface {
|
||||
PerformerCreator
|
||||
PerformerUpdater
|
||||
}
|
||||
|
||||
// PerformerReader provides all methods to read performers.
|
||||
type PerformerReader interface {
|
||||
PerformerFinder
|
||||
PerformerQueryer
|
||||
PerformerAutoTagQueryer
|
||||
PerformerCounter
|
||||
|
||||
AliasLoader
|
||||
StashIDLoader
|
||||
TagIDLoader
|
||||
|
||||
All(ctx context.Context) ([]*Performer, error)
|
||||
GetImage(ctx context.Context, performerID int) ([]byte, error)
|
||||
HasImage(ctx context.Context, performerID int) (bool, error)
|
||||
}
|
||||
|
||||
// PerformerWriter provides all methods to modify performers.
|
||||
type PerformerWriter interface {
|
||||
PerformerCreator
|
||||
PerformerUpdater
|
||||
PerformerDestroyer
|
||||
}
|
||||
|
||||
// PerformerReaderWriter provides all performer methods.
|
||||
type PerformerReaderWriter interface {
|
||||
PerformerReader
|
||||
PerformerWriter
|
||||
}
|
||||
115
pkg/models/repository_scene.go
Normal file
115
pkg/models/repository_scene.go
Normal file
@@ -0,0 +1,115 @@
|
||||
package models
|
||||
|
||||
import "context"
|
||||
|
||||
// SceneGetter provides methods to get scenes by ID.
|
||||
type SceneGetter interface {
|
||||
// TODO - rename this to Find and remove existing method
|
||||
FindMany(ctx context.Context, ids []int) ([]*Scene, error)
|
||||
Find(ctx context.Context, id int) (*Scene, error)
|
||||
}
|
||||
|
||||
// SceneFinder provides methods to find scenes.
|
||||
type SceneFinder interface {
|
||||
SceneGetter
|
||||
FindByFingerprints(ctx context.Context, fp []Fingerprint) ([]*Scene, error)
|
||||
FindByChecksum(ctx context.Context, checksum string) ([]*Scene, error)
|
||||
FindByOSHash(ctx context.Context, oshash string) ([]*Scene, error)
|
||||
FindByPath(ctx context.Context, path string) ([]*Scene, error)
|
||||
FindByFileID(ctx context.Context, fileID FileID) ([]*Scene, error)
|
||||
FindByPrimaryFileID(ctx context.Context, fileID FileID) ([]*Scene, error)
|
||||
FindByPerformerID(ctx context.Context, performerID int) ([]*Scene, error)
|
||||
FindByGalleryID(ctx context.Context, performerID int) ([]*Scene, error)
|
||||
FindByMovieID(ctx context.Context, movieID int) ([]*Scene, error)
|
||||
FindDuplicates(ctx context.Context, distance int, durationDiff float64) ([][]*Scene, error)
|
||||
}
|
||||
|
||||
// SceneQueryer provides methods to query scenes.
|
||||
type SceneQueryer interface {
|
||||
Query(ctx context.Context, options SceneQueryOptions) (*SceneQueryResult, error)
|
||||
QueryCount(ctx context.Context, sceneFilter *SceneFilterType, findFilter *FindFilterType) (int, error)
|
||||
}
|
||||
|
||||
// SceneCounter provides methods to count scenes.
|
||||
type SceneCounter interface {
|
||||
Count(ctx context.Context) (int, error)
|
||||
CountByPerformerID(ctx context.Context, performerID int) (int, error)
|
||||
CountByMovieID(ctx context.Context, movieID int) (int, error)
|
||||
CountByFileID(ctx context.Context, fileID FileID) (int, error)
|
||||
CountByStudioID(ctx context.Context, studioID int) (int, error)
|
||||
CountByTagID(ctx context.Context, tagID int) (int, error)
|
||||
CountMissingChecksum(ctx context.Context) (int, error)
|
||||
CountMissingOSHash(ctx context.Context) (int, error)
|
||||
OCount(ctx context.Context) (int, error)
|
||||
OCountByPerformerID(ctx context.Context, performerID int) (int, error)
|
||||
PlayCount(ctx context.Context) (int, error)
|
||||
UniqueScenePlayCount(ctx context.Context) (int, error)
|
||||
}
|
||||
|
||||
// SceneCreator provides methods to create scenes.
|
||||
type SceneCreator interface {
|
||||
Create(ctx context.Context, newScene *Scene, fileIDs []FileID) error
|
||||
}
|
||||
|
||||
// SceneUpdater provides methods to update scenes.
|
||||
type SceneUpdater interface {
|
||||
Update(ctx context.Context, updatedScene *Scene) error
|
||||
UpdatePartial(ctx context.Context, id int, updatedScene ScenePartial) (*Scene, error)
|
||||
UpdateCover(ctx context.Context, sceneID int, cover []byte) error
|
||||
}
|
||||
|
||||
// SceneDestroyer provides methods to destroy scenes.
|
||||
type SceneDestroyer interface {
|
||||
Destroy(ctx context.Context, id int) error
|
||||
}
|
||||
|
||||
type SceneCreatorUpdater interface {
|
||||
SceneCreator
|
||||
SceneUpdater
|
||||
}
|
||||
|
||||
// SceneReader provides all methods to read scenes.
|
||||
type SceneReader interface {
|
||||
SceneFinder
|
||||
SceneQueryer
|
||||
SceneCounter
|
||||
|
||||
URLLoader
|
||||
FileIDLoader
|
||||
GalleryIDLoader
|
||||
PerformerIDLoader
|
||||
TagIDLoader
|
||||
SceneMovieLoader
|
||||
StashIDLoader
|
||||
VideoFileLoader
|
||||
|
||||
All(ctx context.Context) ([]*Scene, error)
|
||||
Wall(ctx context.Context, q *string) ([]*Scene, error)
|
||||
Size(ctx context.Context) (float64, error)
|
||||
Duration(ctx context.Context) (float64, error)
|
||||
PlayDuration(ctx context.Context) (float64, error)
|
||||
GetCover(ctx context.Context, sceneID int) ([]byte, error)
|
||||
HasCover(ctx context.Context, sceneID int) (bool, error)
|
||||
}
|
||||
|
||||
// SceneWriter provides all methods to modify scenes.
|
||||
type SceneWriter interface {
|
||||
SceneCreator
|
||||
SceneUpdater
|
||||
SceneDestroyer
|
||||
|
||||
AddFileID(ctx context.Context, id int, fileID FileID) error
|
||||
AddGalleryIDs(ctx context.Context, sceneID int, galleryIDs []int) error
|
||||
AssignFiles(ctx context.Context, sceneID int, fileID []FileID) error
|
||||
IncrementOCounter(ctx context.Context, id int) (int, error)
|
||||
DecrementOCounter(ctx context.Context, id int) (int, error)
|
||||
ResetOCounter(ctx context.Context, id int) (int, error)
|
||||
SaveActivity(ctx context.Context, sceneID int, resumeTime *float64, playDuration *float64) (bool, error)
|
||||
IncrementWatchCount(ctx context.Context, sceneID int) (int, error)
|
||||
}
|
||||
|
||||
// SceneReaderWriter provides all scene methods.
|
||||
type SceneReaderWriter interface {
|
||||
SceneReader
|
||||
SceneWriter
|
||||
}
|
||||
76
pkg/models/repository_scene_marker.go
Normal file
76
pkg/models/repository_scene_marker.go
Normal file
@@ -0,0 +1,76 @@
|
||||
package models
|
||||
|
||||
import "context"
|
||||
|
||||
// SceneMarkerGetter provides methods to get scene markers by ID.
|
||||
type SceneMarkerGetter interface {
|
||||
// TODO - rename this to Find and remove existing method
|
||||
FindMany(ctx context.Context, ids []int) ([]*SceneMarker, error)
|
||||
Find(ctx context.Context, id int) (*SceneMarker, error)
|
||||
}
|
||||
|
||||
// SceneMarkerFinder provides methods to find scene markers.
|
||||
type SceneMarkerFinder interface {
|
||||
SceneMarkerGetter
|
||||
FindBySceneID(ctx context.Context, sceneID int) ([]*SceneMarker, error)
|
||||
}
|
||||
|
||||
// SceneMarkerQueryer provides methods to query scene markers.
|
||||
type SceneMarkerQueryer interface {
|
||||
Query(ctx context.Context, sceneMarkerFilter *SceneMarkerFilterType, findFilter *FindFilterType) ([]*SceneMarker, int, error)
|
||||
QueryCount(ctx context.Context, sceneMarkerFilter *SceneMarkerFilterType, findFilter *FindFilterType) (int, error)
|
||||
}
|
||||
|
||||
// SceneMarkerCounter provides methods to count scene markers.
|
||||
type SceneMarkerCounter interface {
|
||||
Count(ctx context.Context) (int, error)
|
||||
CountByTagID(ctx context.Context, tagID int) (int, error)
|
||||
}
|
||||
|
||||
// SceneMarkerCreator provides methods to create scene markers.
|
||||
type SceneMarkerCreator interface {
|
||||
Create(ctx context.Context, newSceneMarker *SceneMarker) error
|
||||
}
|
||||
|
||||
// SceneMarkerUpdater provides methods to update scene markers.
|
||||
type SceneMarkerUpdater interface {
|
||||
Update(ctx context.Context, updatedSceneMarker *SceneMarker) error
|
||||
UpdatePartial(ctx context.Context, id int, updatedSceneMarker SceneMarkerPartial) (*SceneMarker, error)
|
||||
UpdateTags(ctx context.Context, markerID int, tagIDs []int) error
|
||||
}
|
||||
|
||||
// SceneMarkerDestroyer provides methods to destroy scene markers.
|
||||
type SceneMarkerDestroyer interface {
|
||||
Destroy(ctx context.Context, id int) error
|
||||
}
|
||||
|
||||
type SceneMarkerCreatorUpdater interface {
|
||||
SceneMarkerCreator
|
||||
SceneMarkerUpdater
|
||||
}
|
||||
|
||||
// SceneMarkerReader provides all methods to read scene markers.
|
||||
type SceneMarkerReader interface {
|
||||
SceneMarkerFinder
|
||||
SceneMarkerQueryer
|
||||
SceneMarkerCounter
|
||||
|
||||
TagIDLoader
|
||||
|
||||
All(ctx context.Context) ([]*SceneMarker, error)
|
||||
Wall(ctx context.Context, q *string) ([]*SceneMarker, error)
|
||||
GetMarkerStrings(ctx context.Context, q *string, sort *string) ([]*MarkerStringsResultType, error)
|
||||
}
|
||||
|
||||
// SceneMarkerWriter provides all methods to modify scene markers.
|
||||
type SceneMarkerWriter interface {
|
||||
SceneMarkerCreator
|
||||
SceneMarkerUpdater
|
||||
SceneMarkerDestroyer
|
||||
}
|
||||
|
||||
// SceneMarkerReaderWriter provides all scene marker methods.
|
||||
type SceneMarkerReaderWriter interface {
|
||||
SceneMarkerReader
|
||||
SceneMarkerWriter
|
||||
}
|
||||
94
pkg/models/repository_studio.go
Normal file
94
pkg/models/repository_studio.go
Normal file
@@ -0,0 +1,94 @@
|
||||
package models
|
||||
|
||||
import "context"
|
||||
|
||||
// StudioGetter provides methods to get studios by ID.
|
||||
type StudioGetter interface {
|
||||
// TODO - rename this to Find and remove existing method
|
||||
FindMany(ctx context.Context, ids []int) ([]*Studio, error)
|
||||
Find(ctx context.Context, id int) (*Studio, error)
|
||||
}
|
||||
|
||||
// StudioFinder provides methods to find studios.
|
||||
type StudioFinder interface {
|
||||
StudioGetter
|
||||
FindChildren(ctx context.Context, id int) ([]*Studio, error)
|
||||
FindBySceneID(ctx context.Context, sceneID int) (*Studio, error)
|
||||
FindByStashID(ctx context.Context, stashID StashID) ([]*Studio, error)
|
||||
FindByStashIDStatus(ctx context.Context, hasStashID bool, stashboxEndpoint string) ([]*Studio, error)
|
||||
FindByName(ctx context.Context, name string, nocase bool) (*Studio, error)
|
||||
}
|
||||
|
||||
// StudioQueryer provides methods to query studios.
|
||||
type StudioQueryer interface {
|
||||
Query(ctx context.Context, studioFilter *StudioFilterType, findFilter *FindFilterType) ([]*Studio, int, error)
|
||||
}
|
||||
|
||||
type StudioAutoTagQueryer interface {
|
||||
StudioQueryer
|
||||
AliasLoader
|
||||
|
||||
// TODO - this interface is temporary until the filter schema can fully
|
||||
// support the query needed
|
||||
QueryForAutoTag(ctx context.Context, words []string) ([]*Studio, error)
|
||||
}
|
||||
|
||||
// StudioCounter provides methods to count studios.
|
||||
type StudioCounter interface {
|
||||
Count(ctx context.Context) (int, error)
|
||||
}
|
||||
|
||||
// StudioCreator provides methods to create studios.
|
||||
type StudioCreator interface {
|
||||
Create(ctx context.Context, newStudio *Studio) error
|
||||
}
|
||||
|
||||
// StudioUpdater provides methods to update studios.
|
||||
type StudioUpdater interface {
|
||||
Update(ctx context.Context, updatedStudio *Studio) error
|
||||
UpdatePartial(ctx context.Context, updatedStudio StudioPartial) (*Studio, error)
|
||||
UpdateImage(ctx context.Context, studioID int, image []byte) error
|
||||
}
|
||||
|
||||
// StudioDestroyer provides methods to destroy studios.
|
||||
type StudioDestroyer interface {
|
||||
Destroy(ctx context.Context, id int) error
|
||||
}
|
||||
|
||||
type StudioFinderCreator interface {
|
||||
StudioFinder
|
||||
StudioCreator
|
||||
}
|
||||
|
||||
type StudioCreatorUpdater interface {
|
||||
StudioCreator
|
||||
StudioUpdater
|
||||
}
|
||||
|
||||
// StudioReader provides all methods to read studios.
|
||||
type StudioReader interface {
|
||||
StudioFinder
|
||||
StudioQueryer
|
||||
StudioAutoTagQueryer
|
||||
StudioCounter
|
||||
|
||||
AliasLoader
|
||||
StashIDLoader
|
||||
|
||||
All(ctx context.Context) ([]*Studio, error)
|
||||
GetImage(ctx context.Context, studioID int) ([]byte, error)
|
||||
HasImage(ctx context.Context, studioID int) (bool, error)
|
||||
}
|
||||
|
||||
// StudioWriter provides all methods to modify studios.
|
||||
type StudioWriter interface {
|
||||
StudioCreator
|
||||
StudioUpdater
|
||||
StudioDestroyer
|
||||
}
|
||||
|
||||
// StudioReaderWriter provides all studio methods.
|
||||
type StudioReaderWriter interface {
|
||||
StudioReader
|
||||
StudioWriter
|
||||
}
|
||||
104
pkg/models/repository_tag.go
Normal file
104
pkg/models/repository_tag.go
Normal file
@@ -0,0 +1,104 @@
|
||||
package models
|
||||
|
||||
import "context"
|
||||
|
||||
// TagGetter provides methods to get tags by ID.
|
||||
type TagGetter interface {
|
||||
// TODO - rename this to Find and remove existing method
|
||||
FindMany(ctx context.Context, ids []int) ([]*Tag, error)
|
||||
Find(ctx context.Context, id int) (*Tag, error)
|
||||
}
|
||||
|
||||
// TagFinder provides methods to find tags.
|
||||
type TagFinder interface {
|
||||
TagGetter
|
||||
FindAllAncestors(ctx context.Context, tagID int, excludeIDs []int) ([]*TagPath, error)
|
||||
FindAllDescendants(ctx context.Context, tagID int, excludeIDs []int) ([]*TagPath, error)
|
||||
FindByParentTagID(ctx context.Context, parentID int) ([]*Tag, error)
|
||||
FindByChildTagID(ctx context.Context, childID int) ([]*Tag, error)
|
||||
FindBySceneID(ctx context.Context, sceneID int) ([]*Tag, error)
|
||||
FindByImageID(ctx context.Context, imageID int) ([]*Tag, error)
|
||||
FindByGalleryID(ctx context.Context, galleryID int) ([]*Tag, error)
|
||||
FindByPerformerID(ctx context.Context, performerID int) ([]*Tag, error)
|
||||
FindBySceneMarkerID(ctx context.Context, sceneMarkerID int) ([]*Tag, error)
|
||||
FindByName(ctx context.Context, name string, nocase bool) (*Tag, error)
|
||||
FindByNames(ctx context.Context, names []string, nocase bool) ([]*Tag, error)
|
||||
}
|
||||
|
||||
// TagQueryer provides methods to query tags.
|
||||
type TagQueryer interface {
|
||||
Query(ctx context.Context, tagFilter *TagFilterType, findFilter *FindFilterType) ([]*Tag, int, error)
|
||||
}
|
||||
|
||||
type TagAutoTagQueryer interface {
|
||||
TagQueryer
|
||||
AliasLoader
|
||||
|
||||
// TODO - this interface is temporary until the filter schema can fully
|
||||
// support the query needed
|
||||
QueryForAutoTag(ctx context.Context, words []string) ([]*Tag, error)
|
||||
}
|
||||
|
||||
// TagCounter provides methods to count tags.
|
||||
type TagCounter interface {
|
||||
Count(ctx context.Context) (int, error)
|
||||
}
|
||||
|
||||
// TagCreator provides methods to create tags.
|
||||
type TagCreator interface {
|
||||
Create(ctx context.Context, newTag *Tag) error
|
||||
}
|
||||
|
||||
// TagUpdater provides methods to update tags.
|
||||
type TagUpdater interface {
|
||||
Update(ctx context.Context, updatedTag *Tag) error
|
||||
UpdatePartial(ctx context.Context, id int, updateTag TagPartial) (*Tag, error)
|
||||
UpdateAliases(ctx context.Context, tagID int, aliases []string) error
|
||||
UpdateImage(ctx context.Context, tagID int, image []byte) error
|
||||
UpdateParentTags(ctx context.Context, tagID int, parentIDs []int) error
|
||||
UpdateChildTags(ctx context.Context, tagID int, parentIDs []int) error
|
||||
}
|
||||
|
||||
// TagDestroyer provides methods to destroy tags.
|
||||
type TagDestroyer interface {
|
||||
Destroy(ctx context.Context, id int) error
|
||||
}
|
||||
|
||||
type TagFinderCreator interface {
|
||||
TagFinder
|
||||
TagCreator
|
||||
}
|
||||
|
||||
type TagCreatorUpdater interface {
|
||||
TagCreator
|
||||
TagUpdater
|
||||
}
|
||||
|
||||
// TagReader provides all methods to read tags.
|
||||
type TagReader interface {
|
||||
TagFinder
|
||||
TagQueryer
|
||||
TagAutoTagQueryer
|
||||
TagCounter
|
||||
|
||||
AliasLoader
|
||||
|
||||
All(ctx context.Context) ([]*Tag, error)
|
||||
GetImage(ctx context.Context, tagID int) ([]byte, error)
|
||||
HasImage(ctx context.Context, tagID int) (bool, error)
|
||||
}
|
||||
|
||||
// TagWriter provides all methods to modify tags.
|
||||
type TagWriter interface {
|
||||
TagCreator
|
||||
TagUpdater
|
||||
TagDestroyer
|
||||
|
||||
Merge(ctx context.Context, source []int, destination int) error
|
||||
}
|
||||
|
||||
// TagReaderWriter provides all tags methods.
|
||||
type TagReaderWriter interface {
|
||||
TagReader
|
||||
TagWriter
|
||||
}
|
||||
@@ -1,10 +1,6 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/stashapp/stash/pkg/file"
|
||||
)
|
||||
import "context"
|
||||
|
||||
type PHashDuplicationCriterionInput struct {
|
||||
Duplicated *bool `json:"duplicated"`
|
||||
@@ -112,7 +108,7 @@ type SceneQueryResult struct {
|
||||
TotalDuration float64
|
||||
TotalSize float64
|
||||
|
||||
finder SceneFinder
|
||||
getter SceneGetter
|
||||
scenes []*Scene
|
||||
resolveErr error
|
||||
}
|
||||
@@ -129,83 +125,16 @@ type ScenesDestroyInput struct {
|
||||
DeleteGenerated *bool `json:"delete_generated"`
|
||||
}
|
||||
|
||||
func NewSceneQueryResult(finder SceneFinder) *SceneQueryResult {
|
||||
func NewSceneQueryResult(getter SceneGetter) *SceneQueryResult {
|
||||
return &SceneQueryResult{
|
||||
finder: finder,
|
||||
getter: getter,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *SceneQueryResult) Resolve(ctx context.Context) ([]*Scene, error) {
|
||||
// cache results
|
||||
if r.scenes == nil && r.resolveErr == nil {
|
||||
r.scenes, r.resolveErr = r.finder.FindMany(ctx, r.IDs)
|
||||
r.scenes, r.resolveErr = r.getter.FindMany(ctx, r.IDs)
|
||||
}
|
||||
return r.scenes, r.resolveErr
|
||||
}
|
||||
|
||||
type SceneFinder interface {
|
||||
// TODO - rename this to Find and remove existing method
|
||||
FindMany(ctx context.Context, ids []int) ([]*Scene, error)
|
||||
}
|
||||
|
||||
type SceneReader interface {
|
||||
SceneFinder
|
||||
// TODO - remove this in another PR
|
||||
Find(ctx context.Context, id int) (*Scene, error)
|
||||
FindByChecksum(ctx context.Context, checksum string) ([]*Scene, error)
|
||||
FindByOSHash(ctx context.Context, oshash string) ([]*Scene, error)
|
||||
FindByPath(ctx context.Context, path string) ([]*Scene, error)
|
||||
FindByPerformerID(ctx context.Context, performerID int) ([]*Scene, error)
|
||||
FindByGalleryID(ctx context.Context, performerID int) ([]*Scene, error)
|
||||
FindDuplicates(ctx context.Context, distance int, durationDiff float64) ([][]*Scene, error)
|
||||
|
||||
URLLoader
|
||||
GalleryIDLoader
|
||||
PerformerIDLoader
|
||||
TagIDLoader
|
||||
SceneMovieLoader
|
||||
StashIDLoader
|
||||
VideoFileLoader
|
||||
|
||||
CountByPerformerID(ctx context.Context, performerID int) (int, error)
|
||||
OCountByPerformerID(ctx context.Context, performerID int) (int, error)
|
||||
OCount(ctx context.Context) (int, error)
|
||||
// FindByStudioID(studioID int) ([]*Scene, error)
|
||||
FindByMovieID(ctx context.Context, movieID int) ([]*Scene, error)
|
||||
CountByMovieID(ctx context.Context, movieID int) (int, error)
|
||||
Count(ctx context.Context) (int, error)
|
||||
PlayCount(ctx context.Context) (int, error)
|
||||
UniqueScenePlayCount(ctx context.Context) (int, error)
|
||||
Size(ctx context.Context) (float64, error)
|
||||
Duration(ctx context.Context) (float64, error)
|
||||
PlayDuration(ctx context.Context) (float64, error)
|
||||
// SizeCount() (string, error)
|
||||
CountByStudioID(ctx context.Context, studioID int) (int, error)
|
||||
CountByTagID(ctx context.Context, tagID int) (int, error)
|
||||
CountMissingChecksum(ctx context.Context) (int, error)
|
||||
CountMissingOSHash(ctx context.Context) (int, error)
|
||||
Wall(ctx context.Context, q *string) ([]*Scene, error)
|
||||
All(ctx context.Context) ([]*Scene, error)
|
||||
Query(ctx context.Context, options SceneQueryOptions) (*SceneQueryResult, error)
|
||||
QueryCount(ctx context.Context, sceneFilter *SceneFilterType, findFilter *FindFilterType) (int, error)
|
||||
GetCover(ctx context.Context, sceneID int) ([]byte, error)
|
||||
HasCover(ctx context.Context, sceneID int) (bool, error)
|
||||
}
|
||||
|
||||
type SceneWriter interface {
|
||||
Create(ctx context.Context, newScene *Scene, fileIDs []file.ID) error
|
||||
Update(ctx context.Context, updatedScene *Scene) error
|
||||
UpdatePartial(ctx context.Context, id int, updatedScene ScenePartial) (*Scene, error)
|
||||
IncrementOCounter(ctx context.Context, id int) (int, error)
|
||||
DecrementOCounter(ctx context.Context, id int) (int, error)
|
||||
ResetOCounter(ctx context.Context, id int) (int, error)
|
||||
SaveActivity(ctx context.Context, id int, resumeTime *float64, playDuration *float64) (bool, error)
|
||||
IncrementWatchCount(ctx context.Context, id int) (int, error)
|
||||
Destroy(ctx context.Context, id int) error
|
||||
UpdateCover(ctx context.Context, sceneID int, cover []byte) error
|
||||
}
|
||||
|
||||
type SceneReaderWriter interface {
|
||||
SceneReader
|
||||
SceneWriter
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package models
|
||||
|
||||
import "context"
|
||||
|
||||
type SceneMarkerFilterType struct {
|
||||
// Filter to only include scene markers with this tag
|
||||
TagID *string `json:"tag_id"`
|
||||
@@ -28,30 +26,3 @@ type MarkerStringsResultType struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
}
|
||||
|
||||
type SceneMarkerReader interface {
|
||||
Find(ctx context.Context, id int) (*SceneMarker, error)
|
||||
FindMany(ctx context.Context, ids []int) ([]*SceneMarker, error)
|
||||
FindBySceneID(ctx context.Context, sceneID int) ([]*SceneMarker, error)
|
||||
CountByTagID(ctx context.Context, tagID int) (int, error)
|
||||
GetMarkerStrings(ctx context.Context, q *string, sort *string) ([]*MarkerStringsResultType, error)
|
||||
Wall(ctx context.Context, q *string) ([]*SceneMarker, error)
|
||||
Count(ctx context.Context) (int, error)
|
||||
All(ctx context.Context) ([]*SceneMarker, error)
|
||||
Query(ctx context.Context, sceneMarkerFilter *SceneMarkerFilterType, findFilter *FindFilterType) ([]*SceneMarker, int, error)
|
||||
QueryCount(ctx context.Context, sceneMarkerFilter *SceneMarkerFilterType, findFilter *FindFilterType) (int, error)
|
||||
GetTagIDs(ctx context.Context, imageID int) ([]int, error)
|
||||
}
|
||||
|
||||
type SceneMarkerWriter interface {
|
||||
Create(ctx context.Context, newSceneMarker *SceneMarker) error
|
||||
Update(ctx context.Context, updatedSceneMarker *SceneMarker) error
|
||||
UpdatePartial(ctx context.Context, id int, updatedSceneMarker SceneMarkerPartial) (*SceneMarker, error)
|
||||
Destroy(ctx context.Context, id int) error
|
||||
UpdateTags(ctx context.Context, markerID int, tagIDs []int) error
|
||||
}
|
||||
|
||||
type SceneMarkerReaderWriter interface {
|
||||
SceneMarkerReader
|
||||
SceneMarkerWriter
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package models
|
||||
|
||||
import "context"
|
||||
|
||||
type StudioFilterType struct {
|
||||
And *StudioFilterType `json:"AND"`
|
||||
Or *StudioFilterType `json:"OR"`
|
||||
@@ -37,39 +35,3 @@ type StudioFilterType struct {
|
||||
// Filter by updated at
|
||||
UpdatedAt *TimestampCriterionInput `json:"updated_at"`
|
||||
}
|
||||
|
||||
type StudioFinder interface {
|
||||
FindMany(ctx context.Context, ids []int) ([]*Studio, error)
|
||||
}
|
||||
|
||||
type StudioReader interface {
|
||||
Find(ctx context.Context, id int) (*Studio, error)
|
||||
StudioFinder
|
||||
FindChildren(ctx context.Context, id int) ([]*Studio, error)
|
||||
FindByName(ctx context.Context, name string, nocase bool) (*Studio, error)
|
||||
FindByStashID(ctx context.Context, stashID StashID) ([]*Studio, error)
|
||||
FindByStashIDStatus(ctx context.Context, hasStashID bool, stashboxEndpoint string) ([]*Studio, error)
|
||||
Count(ctx context.Context) (int, error)
|
||||
All(ctx context.Context) ([]*Studio, error)
|
||||
// TODO - this interface is temporary until the filter schema can fully
|
||||
// support the query needed
|
||||
QueryForAutoTag(ctx context.Context, words []string) ([]*Studio, error)
|
||||
Query(ctx context.Context, studioFilter *StudioFilterType, findFilter *FindFilterType) ([]*Studio, int, error)
|
||||
GetImage(ctx context.Context, studioID int) ([]byte, error)
|
||||
HasImage(ctx context.Context, studioID int) (bool, error)
|
||||
AliasLoader
|
||||
StashIDLoader
|
||||
}
|
||||
|
||||
type StudioWriter interface {
|
||||
Create(ctx context.Context, newStudio *Studio) error
|
||||
UpdatePartial(ctx context.Context, input StudioPartial) (*Studio, error)
|
||||
Update(ctx context.Context, updatedStudio *Studio) error
|
||||
Destroy(ctx context.Context, id int) error
|
||||
UpdateImage(ctx context.Context, studioID int, image []byte) error
|
||||
}
|
||||
|
||||
type StudioReaderWriter interface {
|
||||
StudioReader
|
||||
StudioWriter
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package models
|
||||
|
||||
import "context"
|
||||
|
||||
type TagFilterType struct {
|
||||
And *TagFilterType `json:"AND"`
|
||||
Or *TagFilterType `json:"OR"`
|
||||
@@ -39,49 +37,3 @@ type TagFilterType struct {
|
||||
// Filter by updated at
|
||||
UpdatedAt *TimestampCriterionInput `json:"updated_at"`
|
||||
}
|
||||
|
||||
type TagFinder interface {
|
||||
FindMany(ctx context.Context, ids []int) ([]*Tag, error)
|
||||
}
|
||||
|
||||
type TagReader interface {
|
||||
Find(ctx context.Context, id int) (*Tag, error)
|
||||
TagFinder
|
||||
FindBySceneID(ctx context.Context, sceneID int) ([]*Tag, error)
|
||||
FindByPerformerID(ctx context.Context, performerID int) ([]*Tag, error)
|
||||
FindBySceneMarkerID(ctx context.Context, sceneMarkerID int) ([]*Tag, error)
|
||||
FindByImageID(ctx context.Context, imageID int) ([]*Tag, error)
|
||||
FindByGalleryID(ctx context.Context, galleryID int) ([]*Tag, error)
|
||||
FindByName(ctx context.Context, name string, nocase bool) (*Tag, error)
|
||||
FindByNames(ctx context.Context, names []string, nocase bool) ([]*Tag, error)
|
||||
FindByParentTagID(ctx context.Context, parentID int) ([]*Tag, error)
|
||||
FindByChildTagID(ctx context.Context, childID int) ([]*Tag, error)
|
||||
Count(ctx context.Context) (int, error)
|
||||
All(ctx context.Context) ([]*Tag, error)
|
||||
// TODO - this interface is temporary until the filter schema can fully
|
||||
// support the query needed
|
||||
QueryForAutoTag(ctx context.Context, words []string) ([]*Tag, error)
|
||||
Query(ctx context.Context, tagFilter *TagFilterType, findFilter *FindFilterType) ([]*Tag, int, error)
|
||||
GetImage(ctx context.Context, tagID int) ([]byte, error)
|
||||
HasImage(ctx context.Context, tagID int) (bool, error)
|
||||
GetAliases(ctx context.Context, tagID int) ([]string, error)
|
||||
FindAllAncestors(ctx context.Context, tagID int, excludeIDs []int) ([]*TagPath, error)
|
||||
FindAllDescendants(ctx context.Context, tagID int, excludeIDs []int) ([]*TagPath, error)
|
||||
}
|
||||
|
||||
type TagWriter interface {
|
||||
Create(ctx context.Context, newTag *Tag) error
|
||||
UpdatePartial(ctx context.Context, id int, updateTag TagPartial) (*Tag, error)
|
||||
Update(ctx context.Context, updatedTag *Tag) error
|
||||
Destroy(ctx context.Context, id int) error
|
||||
UpdateImage(ctx context.Context, tagID int, image []byte) error
|
||||
UpdateAliases(ctx context.Context, tagID int, aliases []string) error
|
||||
Merge(ctx context.Context, source []int, destination int) error
|
||||
UpdateParentTags(ctx context.Context, tagID int, parentIDs []int) error
|
||||
UpdateChildTags(ctx context.Context, tagID int, parentIDs []int) error
|
||||
}
|
||||
|
||||
type TagReaderWriter interface {
|
||||
TagReader
|
||||
TagWriter
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
"github.com/stashapp/stash/pkg/models/json"
|
||||
"github.com/stashapp/stash/pkg/models/jsonschema"
|
||||
"github.com/stashapp/stash/pkg/studio"
|
||||
"github.com/stashapp/stash/pkg/utils"
|
||||
)
|
||||
|
||||
@@ -18,7 +17,7 @@ type ImageGetter interface {
|
||||
}
|
||||
|
||||
// ToJSON converts a Movie into its JSON equivalent.
|
||||
func ToJSON(ctx context.Context, reader ImageGetter, studioReader studio.Finder, movie *models.Movie) (*jsonschema.Movie, error) {
|
||||
func ToJSON(ctx context.Context, reader ImageGetter, studioReader models.StudioGetter, movie *models.Movie) (*jsonschema.Movie, error) {
|
||||
newMovieJSON := jsonschema.Movie{
|
||||
Name: movie.Name,
|
||||
Aliases: movie.Aliases,
|
||||
|
||||
@@ -6,24 +6,17 @@ import (
|
||||
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
"github.com/stashapp/stash/pkg/models/jsonschema"
|
||||
"github.com/stashapp/stash/pkg/studio"
|
||||
"github.com/stashapp/stash/pkg/utils"
|
||||
)
|
||||
|
||||
type ImageUpdater interface {
|
||||
UpdateFrontImage(ctx context.Context, movieID int, frontImage []byte) error
|
||||
UpdateBackImage(ctx context.Context, movieID int, backImage []byte) error
|
||||
}
|
||||
|
||||
type NameFinderCreatorUpdater interface {
|
||||
NameFinderCreator
|
||||
Update(ctx context.Context, updatedMovie *models.Movie) error
|
||||
ImageUpdater
|
||||
type ImporterReaderWriter interface {
|
||||
models.MovieCreatorUpdater
|
||||
FindByName(ctx context.Context, name string, nocase bool) (*models.Movie, error)
|
||||
}
|
||||
|
||||
type Importer struct {
|
||||
ReaderWriter NameFinderCreatorUpdater
|
||||
StudioWriter studio.NameFinderCreator
|
||||
ReaderWriter ImporterReaderWriter
|
||||
StudioWriter models.StudioFinderCreator
|
||||
Input jsonschema.Movie
|
||||
MissingRefBehaviour models.ImportMissingRefEnum
|
||||
|
||||
|
||||
@@ -7,15 +7,7 @@ import (
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
)
|
||||
|
||||
type Queryer interface {
|
||||
Query(ctx context.Context, movieFilter *models.MovieFilterType, findFilter *models.FindFilterType) ([]*models.Movie, int, error)
|
||||
}
|
||||
|
||||
type CountQueryer interface {
|
||||
QueryCount(ctx context.Context, movieFilter *models.MovieFilterType, findFilter *models.FindFilterType) (int, error)
|
||||
}
|
||||
|
||||
func CountByStudioID(ctx context.Context, r CountQueryer, id int, depth *int) (int, error) {
|
||||
func CountByStudioID(ctx context.Context, r models.MovieQueryer, id int, depth *int) (int, error) {
|
||||
filter := &models.MovieFilterType{
|
||||
Studios: &models.HierarchicalMultiCriterionInput{
|
||||
Value: []string{strconv.Itoa(id)},
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
package movie
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
)
|
||||
|
||||
type NameFinderCreator interface {
|
||||
FindByName(ctx context.Context, name string, nocase bool) (*models.Movie, error)
|
||||
Create(ctx context.Context, newMovie *models.Movie) error
|
||||
}
|
||||
@@ -10,19 +10,17 @@ import (
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
"github.com/stashapp/stash/pkg/models/jsonschema"
|
||||
"github.com/stashapp/stash/pkg/sliceutil/stringslice"
|
||||
"github.com/stashapp/stash/pkg/tag"
|
||||
"github.com/stashapp/stash/pkg/utils"
|
||||
)
|
||||
|
||||
type NameFinderCreatorUpdater interface {
|
||||
NameFinderCreator
|
||||
Update(ctx context.Context, updatedPerformer *models.Performer) error
|
||||
UpdateImage(ctx context.Context, performerID int, image []byte) error
|
||||
type ImporterReaderWriter interface {
|
||||
models.PerformerCreatorUpdater
|
||||
models.PerformerQueryer
|
||||
}
|
||||
|
||||
type Importer struct {
|
||||
ReaderWriter NameFinderCreatorUpdater
|
||||
TagWriter tag.NameFinderCreator
|
||||
ReaderWriter ImporterReaderWriter
|
||||
TagWriter models.TagFinderCreator
|
||||
Input jsonschema.Performer
|
||||
MissingRefBehaviour models.ImportMissingRefEnum
|
||||
|
||||
@@ -65,7 +63,7 @@ func (i *Importer) populateTags(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func importTags(ctx context.Context, tagWriter tag.NameFinderCreator, names []string, missingRefBehaviour models.ImportMissingRefEnum) ([]*models.Tag, error) {
|
||||
func importTags(ctx context.Context, tagWriter models.TagFinderCreator, names []string, missingRefBehaviour models.ImportMissingRefEnum) ([]*models.Tag, error) {
|
||||
tags, err := tagWriter.FindByNames(ctx, names, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -100,7 +98,7 @@ func importTags(ctx context.Context, tagWriter tag.NameFinderCreator, names []st
|
||||
return tags, nil
|
||||
}
|
||||
|
||||
func createTags(ctx context.Context, tagWriter tag.NameFinderCreator, names []string) ([]*models.Tag, error) {
|
||||
func createTags(ctx context.Context, tagWriter models.TagFinderCreator, names []string) ([]*models.Tag, error) {
|
||||
var ret []*models.Tag
|
||||
for _, name := range names {
|
||||
newTag := models.NewTag(name)
|
||||
|
||||
@@ -7,15 +7,7 @@ import (
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
)
|
||||
|
||||
type Queryer interface {
|
||||
Query(ctx context.Context, performerFilter *models.PerformerFilterType, findFilter *models.FindFilterType) ([]*models.Performer, int, error)
|
||||
}
|
||||
|
||||
type CountQueryer interface {
|
||||
QueryCount(ctx context.Context, galleryFilter *models.PerformerFilterType, findFilter *models.FindFilterType) (int, error)
|
||||
}
|
||||
|
||||
func CountByStudioID(ctx context.Context, r CountQueryer, id int, depth *int) (int, error) {
|
||||
func CountByStudioID(ctx context.Context, r models.PerformerQueryer, id int, depth *int) (int, error) {
|
||||
filter := &models.PerformerFilterType{
|
||||
Studios: &models.HierarchicalMultiCriterionInput{
|
||||
Value: []string{strconv.Itoa(id)},
|
||||
@@ -27,7 +19,7 @@ func CountByStudioID(ctx context.Context, r CountQueryer, id int, depth *int) (i
|
||||
return r.QueryCount(ctx, filter, nil)
|
||||
}
|
||||
|
||||
func CountByTagID(ctx context.Context, r CountQueryer, id int, depth *int) (int, error) {
|
||||
func CountByTagID(ctx context.Context, r models.PerformerQueryer, id int, depth *int) (int, error) {
|
||||
filter := &models.PerformerFilterType{
|
||||
Tags: &models.HierarchicalMultiCriterionInput{
|
||||
Value: []string{strconv.Itoa(id)},
|
||||
@@ -39,7 +31,7 @@ func CountByTagID(ctx context.Context, r CountQueryer, id int, depth *int) (int,
|
||||
return r.QueryCount(ctx, filter, nil)
|
||||
}
|
||||
|
||||
func CountByAppearsWith(ctx context.Context, r CountQueryer, id int) (int, error) {
|
||||
func CountByAppearsWith(ctx context.Context, r models.PerformerQueryer, id int) (int, error) {
|
||||
filter := &models.PerformerFilterType{
|
||||
Performers: &models.MultiCriterionInput{
|
||||
Value: []string{strconv.Itoa(id)},
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
package performer
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
)
|
||||
|
||||
type NameFinderCreator interface {
|
||||
FindByNames(ctx context.Context, names []string, nocase bool) ([]*models.Performer, error)
|
||||
Query(ctx context.Context, performerFilter *models.PerformerFilterType, findFilter *models.FindFilterType) ([]*models.Performer, int, error)
|
||||
Create(ctx context.Context, newPerformer *models.Performer) error
|
||||
}
|
||||
@@ -6,12 +6,11 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/stashapp/stash/pkg/file"
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
"github.com/stashapp/stash/pkg/plugin"
|
||||
)
|
||||
|
||||
func (s *Service) Create(ctx context.Context, input *models.Scene, fileIDs []file.ID, coverImage []byte) (*models.Scene, error) {
|
||||
func (s *Service) Create(ctx context.Context, input *models.Scene, fileIDs []models.FileID, coverImage []byte) (*models.Scene, error) {
|
||||
// title must be set if no files are provided
|
||||
if input.Title == "" && len(fileIDs) == 0 {
|
||||
return nil, errors.New("title must be set if scene has no files")
|
||||
|
||||
@@ -105,15 +105,6 @@ func (d *FileDeleter) MarkMarkerFiles(scene *models.Scene, seconds int) error {
|
||||
return d.Files(files)
|
||||
}
|
||||
|
||||
type Destroyer interface {
|
||||
Destroy(ctx context.Context, id int) error
|
||||
}
|
||||
|
||||
type MarkerDestroyer interface {
|
||||
FindBySceneID(ctx context.Context, sceneID int) ([]*models.SceneMarker, error)
|
||||
Destroy(ctx context.Context, id int) error
|
||||
}
|
||||
|
||||
// Destroy deletes a scene and its associated relationships from the
|
||||
// database.
|
||||
func (s *Service) Destroy(ctx context.Context, scene *models.Scene, fileDeleter *FileDeleter, deleteGenerated, deleteFile bool) error {
|
||||
@@ -190,7 +181,7 @@ func (s *Service) deleteFiles(ctx context.Context, scene *models.Scene, fileDele
|
||||
// DestroyMarker deletes the scene marker from the database and returns a
|
||||
// function that removes the generated files, to be executed after the
|
||||
// transaction is successfully committed.
|
||||
func DestroyMarker(ctx context.Context, scene *models.Scene, sceneMarker *models.SceneMarker, qb MarkerDestroyer, fileDeleter *FileDeleter) error {
|
||||
func DestroyMarker(ctx context.Context, scene *models.Scene, sceneMarker *models.SceneMarker, qb models.SceneMarkerDestroyer, fileDeleter *FileDeleter) error {
|
||||
if err := qb.Destroy(ctx, sceneMarker.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -11,8 +11,6 @@ import (
|
||||
"github.com/stashapp/stash/pkg/models/json"
|
||||
"github.com/stashapp/stash/pkg/models/jsonschema"
|
||||
"github.com/stashapp/stash/pkg/sliceutil/intslice"
|
||||
"github.com/stashapp/stash/pkg/studio"
|
||||
"github.com/stashapp/stash/pkg/tag"
|
||||
"github.com/stashapp/stash/pkg/utils"
|
||||
)
|
||||
|
||||
@@ -20,18 +18,10 @@ type CoverGetter interface {
|
||||
GetCover(ctx context.Context, sceneID int) ([]byte, error)
|
||||
}
|
||||
|
||||
type MarkerTagFinder interface {
|
||||
tag.Finder
|
||||
TagFinder
|
||||
FindBySceneMarkerID(ctx context.Context, sceneMarkerID int) ([]*models.Tag, error)
|
||||
}
|
||||
|
||||
type MarkerFinder interface {
|
||||
FindBySceneID(ctx context.Context, sceneID int) ([]*models.SceneMarker, error)
|
||||
}
|
||||
|
||||
type TagFinder interface {
|
||||
models.TagGetter
|
||||
FindBySceneID(ctx context.Context, sceneID int) ([]*models.Tag, error)
|
||||
FindBySceneMarkerID(ctx context.Context, sceneMarkerID int) ([]*models.Tag, error)
|
||||
}
|
||||
|
||||
// ToBasicJSON converts a scene object into its JSON object equivalent. It
|
||||
@@ -88,7 +78,7 @@ func ToBasicJSON(ctx context.Context, reader CoverGetter, scene *models.Scene) (
|
||||
|
||||
// GetStudioName returns the name of the provided scene's studio. It returns an
|
||||
// empty string if there is no studio assigned to the scene.
|
||||
func GetStudioName(ctx context.Context, reader studio.Finder, scene *models.Scene) (string, error) {
|
||||
func GetStudioName(ctx context.Context, reader models.StudioGetter, scene *models.Scene) (string, error) {
|
||||
if scene.StudioID != nil {
|
||||
studio, err := reader.Find(ctx, *scene.StudioID)
|
||||
if err != nil {
|
||||
@@ -126,7 +116,7 @@ func getTagNames(tags []*models.Tag) []string {
|
||||
}
|
||||
|
||||
// GetDependentTagIDs returns a slice of unique tag IDs that this scene references.
|
||||
func GetDependentTagIDs(ctx context.Context, tags MarkerTagFinder, markerReader MarkerFinder, scene *models.Scene) ([]int, error) {
|
||||
func GetDependentTagIDs(ctx context.Context, tags TagFinder, markerReader models.SceneMarkerFinder, scene *models.Scene) ([]int, error) {
|
||||
var ret []int
|
||||
|
||||
t, err := tags.FindBySceneID(ctx, scene.ID)
|
||||
@@ -158,13 +148,9 @@ func GetDependentTagIDs(ctx context.Context, tags MarkerTagFinder, markerReader
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
type MovieFinder interface {
|
||||
Find(ctx context.Context, id int) (*models.Movie, error)
|
||||
}
|
||||
|
||||
// GetSceneMoviesJSON returns a slice of SceneMovie JSON representation objects
|
||||
// corresponding to the provided scene's scene movie relationships.
|
||||
func GetSceneMoviesJSON(ctx context.Context, movieReader MovieFinder, scene *models.Scene) ([]jsonschema.SceneMovie, error) {
|
||||
func GetSceneMoviesJSON(ctx context.Context, movieReader models.MovieGetter, scene *models.Scene) ([]jsonschema.SceneMovie, error) {
|
||||
sceneMovies := scene.Movies.List()
|
||||
|
||||
var results []jsonschema.SceneMovie
|
||||
@@ -202,7 +188,7 @@ func GetDependentMovieIDs(ctx context.Context, scene *models.Scene) ([]int, erro
|
||||
|
||||
// GetSceneMarkersJSON returns a slice of SceneMarker JSON representation
|
||||
// objects corresponding to the provided scene's markers.
|
||||
func GetSceneMarkersJSON(ctx context.Context, markerReader MarkerFinder, tagReader MarkerTagFinder, scene *models.Scene) ([]jsonschema.SceneMarker, error) {
|
||||
func GetSceneMarkersJSON(ctx context.Context, markerReader models.SceneMarkerFinder, tagReader TagFinder, scene *models.Scene) ([]jsonschema.SceneMarker, error) {
|
||||
sceneMarkers, err := markerReader.FindBySceneID(ctx, scene.ID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting scene markers: %v", err)
|
||||
|
||||
@@ -3,7 +3,6 @@ package scene
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/stashapp/stash/pkg/file"
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
"github.com/stashapp/stash/pkg/models/json"
|
||||
"github.com/stashapp/stash/pkg/models/jsonschema"
|
||||
@@ -93,9 +92,9 @@ func createFullScene(id int) models.Scene {
|
||||
Rating: &rating,
|
||||
Organized: organized,
|
||||
URLs: models.NewRelatedStrings([]string{url}),
|
||||
Files: models.NewRelatedVideoFiles([]*file.VideoFile{
|
||||
Files: models.NewRelatedVideoFiles([]*models.VideoFile{
|
||||
{
|
||||
BaseFile: &file.BaseFile{
|
||||
BaseFile: &models.BaseFile{
|
||||
Path: path,
|
||||
},
|
||||
},
|
||||
@@ -111,9 +110,9 @@ func createFullScene(id int) models.Scene {
|
||||
func createEmptyScene(id int) models.Scene {
|
||||
return models.Scene{
|
||||
ID: id,
|
||||
Files: models.NewRelatedVideoFiles([]*file.VideoFile{
|
||||
Files: models.NewRelatedVideoFiles([]*models.VideoFile{
|
||||
{
|
||||
BaseFile: &file.BaseFile{
|
||||
BaseFile: &models.BaseFile{
|
||||
Path: path,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -450,11 +450,11 @@ func (p *FilenameParser) initWhiteSpaceRegex() {
|
||||
}
|
||||
|
||||
type FilenameParserRepository struct {
|
||||
Scene Queryer
|
||||
Scene models.SceneQueryer
|
||||
Performer PerformerNamesFinder
|
||||
Studio studio.Queryer
|
||||
Studio models.StudioQueryer
|
||||
Movie MovieNameFinder
|
||||
Tag tag.Queryer
|
||||
Tag models.TagQueryer
|
||||
}
|
||||
|
||||
func (p *FilenameParser) Parse(ctx context.Context, repo FilenameParserRepository) ([]*models.SceneParserResult, int, error) {
|
||||
@@ -544,7 +544,7 @@ func (p *FilenameParser) queryPerformer(ctx context.Context, qb PerformerNamesFi
|
||||
return ret
|
||||
}
|
||||
|
||||
func (p *FilenameParser) queryStudio(ctx context.Context, qb studio.Queryer, studioName string) *models.Studio {
|
||||
func (p *FilenameParser) queryStudio(ctx context.Context, qb models.StudioQueryer, studioName string) *models.Studio {
|
||||
// massage the performer name
|
||||
studioName = delimiterRE.ReplaceAllString(studioName, " ")
|
||||
|
||||
@@ -587,7 +587,7 @@ func (p *FilenameParser) queryMovie(ctx context.Context, qb MovieNameFinder, mov
|
||||
return ret
|
||||
}
|
||||
|
||||
func (p *FilenameParser) queryTag(ctx context.Context, qb tag.Queryer, tagName string) *models.Tag {
|
||||
func (p *FilenameParser) queryTag(ctx context.Context, qb models.TagQueryer, tagName string) *models.Tag {
|
||||
// massage the tag name
|
||||
tagName = delimiterRE.ReplaceAllString(tagName, " ")
|
||||
|
||||
@@ -626,7 +626,7 @@ func (p *FilenameParser) setPerformers(ctx context.Context, qb PerformerNamesFin
|
||||
}
|
||||
}
|
||||
|
||||
func (p *FilenameParser) setTags(ctx context.Context, qb tag.Queryer, h sceneHolder, result *models.SceneParserResult) {
|
||||
func (p *FilenameParser) setTags(ctx context.Context, qb models.TagQueryer, h sceneHolder, result *models.SceneParserResult) {
|
||||
// query for each performer
|
||||
tagsSet := make(map[int]bool)
|
||||
for _, tagName := range h.tags {
|
||||
@@ -642,7 +642,7 @@ func (p *FilenameParser) setTags(ctx context.Context, qb tag.Queryer, h sceneHol
|
||||
}
|
||||
}
|
||||
|
||||
func (p *FilenameParser) setStudio(ctx context.Context, qb studio.Queryer, h sceneHolder, result *models.SceneParserResult) {
|
||||
func (p *FilenameParser) setStudio(ctx context.Context, qb models.StudioQueryer, h sceneHolder, result *models.SceneParserResult) {
|
||||
// query for each performer
|
||||
if h.studio != "" {
|
||||
studio := p.queryStudio(ctx, qb, h.studio)
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
package scene
|
||||
|
||||
import (
|
||||
"github.com/stashapp/stash/pkg/file"
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
)
|
||||
|
||||
// GetHash returns the hash of the file, based on the hash algorithm provided. If
|
||||
// hash algorithm is MD5, then Checksum is returned. Otherwise, OSHash is returned.
|
||||
func GetHash(f file.File, hashAlgorithm models.HashAlgorithm) string {
|
||||
func GetHash(f models.File, hashAlgorithm models.HashAlgorithm) string {
|
||||
switch hashAlgorithm {
|
||||
case models.HashAlgorithmMd5:
|
||||
return f.Base().Fingerprints.GetString(file.FingerprintTypeMD5)
|
||||
return f.Base().Fingerprints.GetString(models.FingerprintTypeMD5)
|
||||
case models.HashAlgorithmOshash:
|
||||
return f.Base().Fingerprints.GetString(file.FingerprintTypeOshash)
|
||||
return f.Base().Fingerprints.GetString(models.FingerprintTypeOshash)
|
||||
default:
|
||||
panic("unknown hash algorithm")
|
||||
}
|
||||
|
||||
@@ -5,32 +5,25 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/stashapp/stash/pkg/file"
|
||||
"github.com/stashapp/stash/pkg/gallery"
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
"github.com/stashapp/stash/pkg/models/jsonschema"
|
||||
"github.com/stashapp/stash/pkg/movie"
|
||||
"github.com/stashapp/stash/pkg/performer"
|
||||
"github.com/stashapp/stash/pkg/sliceutil/stringslice"
|
||||
"github.com/stashapp/stash/pkg/studio"
|
||||
"github.com/stashapp/stash/pkg/tag"
|
||||
"github.com/stashapp/stash/pkg/utils"
|
||||
)
|
||||
|
||||
type FullCreatorUpdater interface {
|
||||
CreatorUpdater
|
||||
Update(ctx context.Context, updatedScene *models.Scene) error
|
||||
Updater
|
||||
type ImporterReaderWriter interface {
|
||||
models.SceneCreatorUpdater
|
||||
FindByFileID(ctx context.Context, fileID models.FileID) ([]*models.Scene, error)
|
||||
}
|
||||
|
||||
type Importer struct {
|
||||
ReaderWriter FullCreatorUpdater
|
||||
FileFinder file.Getter
|
||||
StudioWriter studio.NameFinderCreator
|
||||
GalleryFinder gallery.Finder
|
||||
PerformerWriter performer.NameFinderCreator
|
||||
MovieWriter movie.NameFinderCreator
|
||||
TagWriter tag.NameFinderCreator
|
||||
ReaderWriter ImporterReaderWriter
|
||||
FileFinder models.FileFinder
|
||||
StudioWriter models.StudioFinderCreator
|
||||
GalleryFinder models.GalleryFinder
|
||||
PerformerWriter models.PerformerFinderCreator
|
||||
MovieWriter models.MovieFinderCreator
|
||||
TagWriter models.TagFinderCreator
|
||||
Input jsonschema.Scene
|
||||
MissingRefBehaviour models.ImportMissingRefEnum
|
||||
FileNamingAlgorithm models.HashAlgorithm
|
||||
@@ -123,7 +116,7 @@ func (i *Importer) sceneJSONToScene(sceneJSON jsonschema.Scene) models.Scene {
|
||||
}
|
||||
|
||||
func (i *Importer) populateFiles(ctx context.Context) error {
|
||||
files := make([]*file.VideoFile, 0)
|
||||
files := make([]*models.VideoFile, 0)
|
||||
|
||||
for _, ref := range i.Input.Files {
|
||||
path := ref
|
||||
@@ -135,7 +128,7 @@ func (i *Importer) populateFiles(ctx context.Context) error {
|
||||
if f == nil {
|
||||
return fmt.Errorf("scene file '%s' not found", path)
|
||||
} else {
|
||||
files = append(files, f.(*file.VideoFile))
|
||||
files = append(files, f.(*models.VideoFile))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -413,7 +406,7 @@ func (i *Importer) FindExistingID(ctx context.Context) (*int, error) {
|
||||
}
|
||||
|
||||
func (i *Importer) Create(ctx context.Context) (*int, error) {
|
||||
var fileIDs []file.ID
|
||||
var fileIDs []models.FileID
|
||||
for _, f := range i.scene.Files.List() {
|
||||
fileIDs = append(fileIDs, f.Base().ID)
|
||||
}
|
||||
@@ -437,7 +430,7 @@ func (i *Importer) Update(ctx context.Context, id int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func importTags(ctx context.Context, tagWriter tag.NameFinderCreator, names []string, missingRefBehaviour models.ImportMissingRefEnum) ([]*models.Tag, error) {
|
||||
func importTags(ctx context.Context, tagWriter models.TagFinderCreator, names []string, missingRefBehaviour models.ImportMissingRefEnum) ([]*models.Tag, error) {
|
||||
tags, err := tagWriter.FindByNames(ctx, names, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -472,7 +465,7 @@ func importTags(ctx context.Context, tagWriter tag.NameFinderCreator, names []st
|
||||
return tags, nil
|
||||
}
|
||||
|
||||
func createTags(ctx context.Context, tagWriter tag.NameFinderCreator, names []string) ([]*models.Tag, error) {
|
||||
func createTags(ctx context.Context, tagWriter models.TagCreator, names []string) ([]*models.Tag, error) {
|
||||
var ret []*models.Tag
|
||||
for _, name := range names {
|
||||
newTag := models.NewTag(name)
|
||||
|
||||
@@ -7,20 +7,17 @@ import (
|
||||
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
"github.com/stashapp/stash/pkg/models/jsonschema"
|
||||
"github.com/stashapp/stash/pkg/tag"
|
||||
)
|
||||
|
||||
type MarkerCreatorUpdater interface {
|
||||
Create(ctx context.Context, newSceneMarker *models.SceneMarker) error
|
||||
Update(ctx context.Context, updatedSceneMarker *models.SceneMarker) error
|
||||
models.SceneMarkerCreatorUpdater
|
||||
FindBySceneID(ctx context.Context, sceneID int) ([]*models.SceneMarker, error)
|
||||
UpdateTags(ctx context.Context, markerID int, tagIDs []int) error
|
||||
}
|
||||
|
||||
type MarkerImporter struct {
|
||||
SceneID int
|
||||
ReaderWriter MarkerCreatorUpdater
|
||||
TagWriter tag.NameFinderCreator
|
||||
TagWriter models.TagFinderCreator
|
||||
Input jsonschema.SceneMarker
|
||||
MissingRefBehaviour models.ImportMissingRefEnum
|
||||
|
||||
|
||||
@@ -7,15 +7,7 @@ import (
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
)
|
||||
|
||||
type MarkerQueryer interface {
|
||||
Query(ctx context.Context, sceneMarkerFilter *models.SceneMarkerFilterType, findFilter *models.FindFilterType) ([]*models.SceneMarker, int, error)
|
||||
}
|
||||
|
||||
type MarkerCountQueryer interface {
|
||||
QueryCount(ctx context.Context, sceneMarkerFilter *models.SceneMarkerFilterType, findFilter *models.FindFilterType) (int, error)
|
||||
}
|
||||
|
||||
func MarkerCountByTagID(ctx context.Context, r MarkerCountQueryer, id int, depth *int) (int, error) {
|
||||
func MarkerCountByTagID(ctx context.Context, r models.SceneMarkerQueryer, id int, depth *int) (int, error) {
|
||||
filter := &models.SceneMarkerFilterType{
|
||||
Tags: &models.HierarchicalMultiCriterionInput{
|
||||
Value: []string{strconv.Itoa(id)},
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/stashapp/stash/pkg/file"
|
||||
"github.com/stashapp/stash/pkg/fsutil"
|
||||
"github.com/stashapp/stash/pkg/logger"
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
@@ -33,7 +32,7 @@ func (s *Service) Merge(ctx context.Context, sourceIDs []int, destinationID int,
|
||||
return fmt.Errorf("finding source scenes: %w", err)
|
||||
}
|
||||
|
||||
var fileIDs []file.ID
|
||||
var fileIDs []models.FileID
|
||||
|
||||
for _, src := range sources {
|
||||
// TODO - delete generated files as needed
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user