2022-07-13 06:30:54 +00:00
package file
import (
"context"
2023-03-21 20:57:26 +00:00
"fmt"
"path/filepath"
2023-04-19 03:06:53 +00:00
"strings"
2022-07-13 06:30:54 +00:00
"time"
2024-01-16 03:48:28 +00:00
"github.com/stashapp/stash/pkg/logger"
2023-09-01 00:39:29 +00:00
"github.com/stashapp/stash/pkg/models"
)
2023-03-21 20:57:26 +00:00
// 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
2023-09-01 00:39:29 +00:00
func GetOrCreateFolderHierarchy ( ctx context . Context , fc models . FolderFinderCreator , path string ) ( * models . Folder , error ) {
2023-03-21 20:57:26 +00:00
// get or create folder hierarchy
folder , err := fc . FindByPath ( ctx , path )
if err != nil {
return nil , err
}
if folder == nil {
parentPath := filepath . Dir ( path )
parent , err := GetOrCreateFolderHierarchy ( ctx , fc , parentPath )
if err != nil {
return nil , err
}
now := time . Now ( )
2023-09-01 00:39:29 +00:00
folder = & models . Folder {
2023-03-21 20:57:26 +00:00
Path : path ,
ParentFolderID : & parent . ID ,
2023-09-01 00:39:29 +00:00
DirEntry : models . DirEntry {
2023-03-21 20:57:26 +00:00
// leave mod time empty for now - it will be updated when the folder is scanned
} ,
CreatedAt : now ,
UpdatedAt : now ,
}
if err = fc . Create ( ctx , folder ) ; err != nil {
return nil , fmt . Errorf ( "creating folder %s: %w" , path , err )
}
}
return folder , nil
}
2023-04-19 03:06:53 +00:00
2024-01-16 03:48:28 +00:00
func transferZipHierarchy ( ctx context . Context , folderStore models . FolderReaderWriter , files models . FileFinderUpdater , zipFileID models . FileID , oldPath string , newPath string ) error {
if err := transferZipFolderHierarchy ( ctx , folderStore , zipFileID , oldPath , newPath ) ; err != nil {
return fmt . Errorf ( "moving folder hierarchy for file %s: %w" , oldPath , err )
}
if err := transferZipFileEntries ( ctx , folderStore , files , zipFileID , oldPath , newPath ) ; err != nil {
return fmt . Errorf ( "moving zip file contents for file %s: %w" , oldPath , err )
}
return nil
}
// transferZipFolderHierarchy creates the folder hierarchy for zipFileID under newPath, and removes
2023-04-19 03:06:53 +00:00
// ZipFileID from folders under oldPath.
2024-01-16 03:48:28 +00:00
func transferZipFolderHierarchy ( ctx context . Context , folderStore models . FolderReaderWriter , zipFileID models . FileID , oldPath string , newPath string ) error {
2023-04-19 03:06:53 +00:00
zipFolders , err := folderStore . FindByZipFileID ( ctx , zipFileID )
if err != nil {
return err
}
for _ , oldFolder := range zipFolders {
oldZfPath := oldFolder . Path
// sanity check - ignore folders which aren't under oldPath
if ! strings . HasPrefix ( oldZfPath , oldPath ) {
continue
}
relZfPath , err := filepath . Rel ( oldPath , oldZfPath )
if err != nil {
return err
}
newZfPath := filepath . Join ( newPath , relZfPath )
newFolder , err := GetOrCreateFolderHierarchy ( ctx , folderStore , newZfPath )
if err != nil {
return err
}
// add ZipFileID to new folder
2024-01-16 03:48:28 +00:00
logger . Debugf ( "adding zip file %s to folder %s" , zipFileID , newFolder . Path )
2023-04-19 03:06:53 +00:00
newFolder . ZipFileID = & zipFileID
if err = folderStore . Update ( ctx , newFolder ) ; err != nil {
return err
}
// remove ZipFileID from old folder
2024-01-16 03:48:28 +00:00
logger . Debugf ( "removing zip file %s from folder %s" , zipFileID , oldFolder . Path )
2023-04-19 03:06:53 +00:00
oldFolder . ZipFileID = nil
if err = folderStore . Update ( ctx , oldFolder ) ; err != nil {
return err
}
}
return nil
}
2024-01-16 03:48:28 +00:00
func transferZipFileEntries ( ctx context . Context , folders models . FolderFinderCreator , files models . FileFinderUpdater , zipFileID models . FileID , oldPath , newPath string ) error {
// move contained files if file is a zip file
zipFiles , err := files . FindByZipFileID ( ctx , zipFileID )
if err != nil {
return fmt . Errorf ( "finding contained files in file %s: %w" , oldPath , err )
}
for _ , zf := range zipFiles {
zfBase := zf . Base ( )
oldZfPath := zfBase . Path
oldZfDir := filepath . Dir ( oldZfPath )
// sanity check - ignore files which aren't under oldPath
if ! strings . HasPrefix ( oldZfPath , oldPath ) {
continue
}
relZfDir , err := filepath . Rel ( oldPath , oldZfDir )
if err != nil {
return fmt . Errorf ( "moving contained file %s: %w" , zfBase . ID , err )
}
newZfDir := filepath . Join ( newPath , relZfDir )
// folder should have been created by transferZipFolderHierarchy
newZfFolder , err := GetOrCreateFolderHierarchy ( ctx , folders , newZfDir )
if err != nil {
return fmt . Errorf ( "getting or creating folder hierarchy: %w" , err )
}
// update file parent folder
zfBase . ParentFolderID = newZfFolder . ID
logger . Debugf ( "moving %s to folder %s" , zfBase . Path , newZfFolder . Path )
if err := files . Update ( ctx , zf ) ; err != nil {
return fmt . Errorf ( "updating file %s: %w" , oldZfPath , err )
}
}
return nil
}