Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions internal/mediasort/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,6 @@ func NewSorter(ctx context.Context, opts ...Option) (Sorter, error) {
sourceDirectory: *cfg.sourceDirectory,

extVisitorFunc: visitors.NewMediaExtAliases(ctx),
progressTracker: &progressTracker{
currentMediaIndex: 0,
totalMediaFiles: 0,
logThreshold: 0,
logNextThreshold: 0,
},
fileHandler: &metadataFileHandler{
useInputMagicSignature: cfg.useInputMagicSignature,
detectDuplicates: cfg.detectDuplicates,
Expand Down
8 changes: 4 additions & 4 deletions internal/mediasort/file_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ type metadataFileHandler struct {
mediaMetadataVisitorFunc mediatype.VisitorFunc[visitors.MediaMetadata]
}

// handle takes in a source media file, and will move it a computed output file
// handleMediaFile takes in a source media file, and will move it a computed output file
// based on media metadata
func (s *metadataFileHandler) handle(ctx context.Context, srcMedia mediatype.Format) error {
visitor := mediatype.FormatWithVisitor[string](srcMedia)
srcPath, err := visitor.Accept(ctx, visitors.NewMediaPath(ctx))
func (s *metadataFileHandler) handleMediaFile(ctx context.Context, srcMedia mediatype.Format) error {
mediaVisitor := mediatype.FormatWithVisitor[string](srcMedia)
srcPath, err := mediaVisitor.Accept(ctx, visitors.NewMediaPath(ctx))
if err != nil {
return err
}
Expand Down
4 changes: 2 additions & 2 deletions internal/mediasort/progress_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ type progressTracker struct {
totalMediaFiles int
}

// handle is used to track progress of a scan run. It requires traversing
// recordMediaVisit is used to track progress of a scan run. It requires traversing
// through a folder once to accumulate a count the total number of files that
// will be handled. Then on second pass it will log occasionally the overall
// progress.
func (s *progressTracker) handle(ctx context.Context, isAccumulating bool) {
func (s *progressTracker) recordMediaVisit(ctx context.Context, isAccumulating bool) {
if isAccumulating {
s.totalMediaFiles++
return
Expand Down
55 changes: 30 additions & 25 deletions internal/mediasort/traverse.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,28 +20,46 @@ type traverser struct {
blocklist []*regexp.Regexp
useInputMagicSignature bool

fileHandler *metadataFileHandler
progressTracker *progressTracker
extVisitorFunc mediatype.VisitorFunc[map[string]struct{}]
fileHandler *metadataFileHandler
extVisitorFunc mediatype.VisitorFunc[map[string]struct{}]
}

// innerTraverseFunc is a function that is called for each file that is traversed.
type innerTraverseFunc = func(logger *zap.Logger, srcMedia mediatype.Format) error

// Run implements Sorter
func (t *traverser) Run(ctx context.Context) error {
progressTracker := &progressTracker{}
preScanRunFunc := func(logger *zap.Logger, srcMedia mediatype.Format) error {
progressTracker.recordMediaVisit(ctx, true)
return nil
}
sortMediaRunFunc := func(logger *zap.Logger, srcMedia mediatype.Format) error {
progressTracker.recordMediaVisit(ctx, false)
if err := t.fileHandler.handleMediaFile(ctx, srcMedia); err != nil {
logger.Warn("Failed to handle file.", zap.Error(err))
if t.stopWalkOnError {
return err
}
}
return nil
}

ilog.FromContext(ctx).Info("Performing pre-scan for media files...", zap.String("directory", t.sourceDirectory))
if err := filepath.WalkDir(t.sourceDirectory, t.traverseFunc(ctx, true)); err != nil {
if err := filepath.WalkDir(t.sourceDirectory, t.traverseFunc(ctx, preScanRunFunc)); err != nil {
return err
}

ilog.FromContext(ctx).Info("Sorting media files in directory...", zap.String("directory", t.sourceDirectory))
if err := filepath.WalkDir(t.sourceDirectory, t.traverseFunc(ctx, false)); err != nil {
if err := filepath.WalkDir(t.sourceDirectory, t.traverseFunc(ctx, sortMediaRunFunc)); err != nil {
return err
}

ilog.FromContext(ctx).Info("Succesfully sorted media files.")
ilog.FromContext(ctx).Info("Successfully sorted media files.")
return nil
}

func (t *traverser) traverseFunc(ctx context.Context, isPreRun bool) fs.WalkDirFunc {
func (t *traverser) traverseFunc(ctx context.Context, innerCallbackFunc innerTraverseFunc) fs.WalkDirFunc {
return func(path string, info fs.DirEntry, err error) error {
if err != nil {
return err
Expand All @@ -50,14 +68,14 @@ func (t *traverser) traverseFunc(ctx context.Context, isPreRun bool) fs.WalkDirF

if info.IsDir() {
if t.skipDir(path) {
logger.Debug("Directory matches blocklist, so skipping entire directory...")
logger.Debug("Directory matches blocklist, so skipping entire directory.")
return fs.SkipDir
}
return nil
}

if t.skipDir(path) {
logger.Debug("Path in blocklist, so skipping...")
logger.Debug("Path in blocklist, so skipping.")
return fs.SkipDir
}

Expand All @@ -74,26 +92,13 @@ func (t *traverser) traverseFunc(ctx context.Context, isPreRun bool) fs.WalkDirF
return nil
}

logger.Debug("Checking file.")
logger.Debug("Checking file...")
if t.skipFile(aliases) {
logger.Debug("File did not matchfile types allowlist, so skipping...")
logger.Debug("File did not matchfile types allowlist, so skipping.")
return nil
}

if isPreRun {
t.progressTracker.handle(ctx, true)
return nil
}

t.progressTracker.handle(ctx, false)
if err := t.fileHandler.handle(ctx, srcMedia); err != nil {
logger.Warn("Failed to handle file.", zap.Error(err))
if t.stopWalkOnError {
return err
}
}

return nil
return innerCallbackFunc(logger, srcMedia)
}
}

Expand Down
43 changes: 4 additions & 39 deletions internal/visitors/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@ package visitors
import (
"context"
"os"
"path/filepath"
"strconv"
"strings"
"time"

"github.com/dtrejod/goexif/internal/exifdata"
Expand All @@ -19,16 +16,12 @@ const (
)

type mediaMetadataFilename struct {
outDir *string
useLastModifiedDate bool
timestampAsFilename bool
useOutputMagicSignature bool
useLastModifiedDate bool
timestampAsFilename bool
}

// MediaMetadata is the return type from the MediaMetadataFilename visitor
type MediaMetadata struct {
// OutPath is an appropriate new output filename for the provided mediatype format.
OutPath string
Timestamp time.Time
}

Expand All @@ -46,10 +39,8 @@ func NewMediaMetadataFilename(
useOutputMagicSignature bool,
) mediatype.VisitorFunc[MediaMetadata] {
return &mediaMetadataFilename{
outDir: outDir,
useLastModifiedDate: useLastModifiedDate,
timestampAsFilename: timestampAsFilename,
useOutputMagicSignature: useOutputMagicSignature,
useLastModifiedDate: useLastModifiedDate,
timestampAsFilename: timestampAsFilename,
}
}

Expand Down Expand Up @@ -105,38 +96,12 @@ func (e *mediaMetadataFilename) getTimeMetadataWithFunc(
return MediaMetadata{}, err
}
}
outFile, err := e.getOutputFile(ctx, srcPath, cleanEXT, ts.UTC())
if err != nil {
return MediaMetadata{}, err
}

return MediaMetadata{
OutPath: outFile,
Timestamp: ts,
}, nil
}

func (e *mediaMetadataFilename) getOutputFile(_ context.Context, srcPath, cleanExt string, ts time.Time) (string, error) {
srcDir := filepath.Dir(srcPath)
outDir := filepath.Join(srcDir, ts.Format(outPathDateFormat))
if e.outDir != nil {
outDir = filepath.Join(*e.outDir, ts.Format(outPathDateFormat))
}

ext := filepath.Ext(srcPath)
if e.useOutputMagicSignature {
ext = cleanExt
}
outFilename := strings.TrimSuffix(filepath.Base(srcPath), filepath.Ext(srcPath))
if e.timestampAsFilename {
outFilename = strconv.FormatInt(ts.Unix(), 10)
}

outFilename = outFilename + ext
return filepath.Join(outDir, outFilename), nil

}

func (e *mediaMetadataFilename) fallbackToModTime(srcPath string, origErr error) (time.Time, error) {
// on error, fallback to lastmodified if the option was specified
if e.useLastModifiedDate {
Expand Down
70 changes: 70 additions & 0 deletions internal/visitors/rename_media.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package visitors

import "github.com/dtrejod/goexif/internal/mediatype"

type renameMedia struct {
outDir *string
useOutputMagicSignature bool

mediaDaterVisitor mediatype.VisitorFunc[MediaMetadata]
}

// NewRenameMedia is a visitor that will rename a media file based on the metadata
// contained within the file.
func NewRenameMedia(
outDir *string,
useOutputMagicSignature bool,
mediaDaterVisitor mediatype.VisitorFunc[MediaMetadata],
) mediatype.VisitorFunc[MediaMetadata] {
return &renameMedia{
outDir: outDir,
useOutputMagicSignature: useOutputMagicSignature,
mediaDaterVisitor: mediaDaterVisitor,
}
}

func (e *renameMedia) VisitJPEG(ctx context.Context, image mediatype.JPEG) (MediaMetadata, error) {
return e.renameMedia(ctx, image.Path, image.Ext())
}

func (e *renameMedia) VisitPNG(ctx context.Context, image mediatype.PNG) (MediaMetadata, error) {
return e.renameMedia(ctx, image.Path, image.Ext())
}


func (e *renameMedia) getOutputFileName(
ctx context.Context,
srcPath string,
cleanEXT string,
) (string, error) {
// Get the metadata from the media file

return.getOutputFile(ctx, srcPath, cleanEXT, metadata.TimeStamp.UTC())

Check failure on line 42 in internal/visitors/rename_media.go

View workflow job for this annotation

GitHub Actions / test

syntax error: unexpected ., expected expression


func (e *renameMedia) getOutputFile(_ context.Context, srcPath, cleanExt string, ts time.Time) (string, error) {

Check failure on line 45 in internal/visitors/rename_media.go

View workflow job for this annotation

GitHub Actions / test

syntax error: unexpected name context in argument list; possibly missing comma or )
metadata, err := e.mediaDaterVisitor(ctx, srcPath)
if err != nil {
return "", err
}

srcDir := filepath.Dir(srcPath)
outDir := filepath.Join(srcDir, ts.Format(outPathDateFormat))
if e.outDir != nil {
outDir = filepath.Join(*e.outDir, ts.Format(outPathDateFormat))
}

ext := filepath.Ext(srcPath)
if e.useOutputMagicSignature {
ext = cleanExt
}
outFilename := strings.TrimSuffix(filepath.Base(srcPath), filepath.Ext(srcPath))
if e.timestampAsFilename {
outFilename = strconv.FormatInt(ts.Unix(), 10)
}

outFilename = outFilename + ext
return filepath.Join(outDir, outFilename), nil

}

Loading