Skip to main content

Overview

The utils package provides essential utility functions for file operations, date extraction from metadata, path manipulation, and dependency checking. It includes platform-specific implementations for optimal performance on macOS, Linux, and Windows.

Date Extraction

GetFileDate

Extracts the creation date from media file metadata with multiple fallback methods.
func GetFileDate(filePath string) (time.Time, error)
filePath
string
required
Path to media file
date
time.Time
File creation date extracted from metadata
error
error
Returns an error if no valid date is found

Extraction Priority

  1. macOS mdls metadata (most reliable for RAW files)
  2. EXIF data (DateTimeOriginal, DateTime, etc.)
  3. Video metadata (creation_time from FFprobe)
  4. File modification time (validated as fallback)

Example

import "github.com/Azilone/Camera-Workflow/internal/utils"

date, err := utils.GetFileDate("/path/to/IMG_1234.jpg")
if err != nil {
    log.Printf("Failed to extract date: %v", err)
} else {
    fmt.Printf("Photo taken: %s\n", date.Format("2006-01-02"))
}

getMacOSMetadataDate

Extracts creation date using macOS mdls command (macOS only).
func getMacOSMetadataDate(filePath string) (time.Time, error)
filePath
string
required
Path to file

Platform Support

Only works on macOS (darwin). Returns error on other platforms.

getImageMetadataDate

Extracts creation date from image EXIF metadata using ImageMagick.
func getImageMetadataDate(filePath string) (time.Time, error)
filePath
string
required
Path to image file

EXIF Fields (Priority Order)

  1. EXIF:DateTimeOriginal - Camera capture time (preferred)
  2. EXIF:DateTime - File save time
  3. date:create - General creation time
  4. date:modify - Modification time

getVideoMetadataDate

Extracts creation date from video metadata using FFprobe.
func getVideoMetadataDate(filePath string) (time.Time, error)
filePath
string
required
Path to video file

isValidDate

Validates that a date is reasonable for media files.
func isValidDate(date time.Time) bool
date
time.Time
required
Date to validate

Validation Rules

  • Rejects dates in the future
  • Rejects dates before 1990 (pre-digital photography era)

File Organization

CreateDestinationPath

Generates the destination path for a media file based on date and organization settings.
func CreateDestinationPath(
    baseDir string,
    fileDate time.Time,
    mediaType string,
    organizeByDate bool,
    language string,
) string
baseDir
string
required
Base destination directory
fileDate
time.Time
required
File creation date
mediaType
string
required
Type of media (“image” or “video”)
organizeByDate
bool
required
Whether to organize by date
language
string
required
Language code for month names (“en”, “fr”, “es”, “de”)
path
string
Full destination directory path

Path Structure

With date organization:
{baseDir}/{YYYY}/{MM-MonthName}/{YYYY-MM-DD}/{mediaType}s/
Example: /dest/2024/03-March/2024-03-15/images/
Without date organization:
{baseDir}/{mediaType}s/
Example: /dest/images/

GetMonthName

Returns the localized month name for a given month number.
func GetMonthName(month int, language string) string
month
int
required
Month number (1-12)
language
string
required
Language code (“en”, “fr”, “es”, “de”)
monthName
string
Formatted as “MM-MonthName” (e.g., “03-March”)

Supported Languages

  • English (en): January, February, March, …
  • French (fr): Janvier, Fevrier, Mars, …
  • Spanish (es): Enero, Febrero, Marzo, …
  • German (de): Januar, Februar, Maerz, …

CleanFilename

Generates a clean, safe filename with date prefix.
func CleanFilename(
    filename string,
    extension string,
    date time.Time,
    counter int,
) string
filename
string
required
Original filename (without extension)
extension
string
required
File extension (without dot)
date
time.Time
required
File date
counter
int
required
Counter for uniqueness (3 digits)
cleanFilename
string
Cleaned filename in format: YYYY-MM-DD_cleanname_###.ext

Cleaning Operations

  1. Remove special characters (keep alphanumeric, ., _, -)
  2. Replace consecutive underscores with single underscore
  3. Trim leading/trailing underscores
  4. Add date prefix and counter

Example

clean := utils.CleanFilename(
    "My Photo (2024)!",
    "jpg",
    time.Date(2024, 3, 15, 0, 0, 0, 0, time.UTC),
    1,
)
// Result: "2024-03-15_My_Photo_2024_001.jpg"

GetUniqueFilename

Generates a unique filename by adding counters if file already exists.
func GetUniqueFilename(
    basePath string,
    filename string,
    extension string,
) (string, error)
basePath
string
required
Directory path
filename
string
required
Base filename
extension
string
required
File extension
uniquePath
string
Full path with unique filename

Counter Limit

Returns error after 9999 attempts to prevent infinite loops.

File Type Detection

HasExtension

Checks if a file has one of the specified extensions.
func HasExtension(filename string, extensions []string) bool
filename
string
required
File path or name
extensions
[]string
required
List of valid extensions (without dots)
matches
bool
True if file has one of the specified extensions

Example

photoExts := []string{"jpg", "jpeg", "heic", "raw"}
isPhoto := utils.HasExtension("/path/to/IMG_1234.jpg", photoExts)
// Result: true

Path Operations

IsPathWithin

Checks if a path is within a parent directory.
func IsPathWithin(path string, parent string) bool
path
string
required
Path to check
parent
string
required
Parent directory
within
bool
True if path is within parent directory

Example

isWithin := utils.IsPathWithin("/dest/2024/images", "/dest")
// Result: true

isWithin := utils.IsPathWithin("/other/dir", "/dest")
// Result: false

EnsureDir

Creates a directory and all parent directories if they don’t exist.
func EnsureDir(dirPath string) error
dirPath
string
required
Directory path to create

Permissions

Creates directories with 0755 permissions.

System Entry Filtering

ShouldSkipSystemEntry

Determines if a file or directory should be skipped.
func ShouldSkipSystemEntry(name string, isDir bool) bool
name
string
required
Entry name
isDir
bool
required
Whether entry is a directory

Skipped Entries

Directories:
  • .Spotlight-V100
  • .fseventsd
  • .TemporaryItems
  • .DocumentRevisions-V100
  • System Volume Information
  • __MACOSX
  • .Trash* (any trash folder)
Files:
  • .DS_Store
  • Thumbs.db
  • desktop.ini
  • ._* (resource forks)

IsPermissionError

Checks if an error is a permission-related error.
func IsPermissionError(err error) bool
err
error
required
Error to check

Video Utilities

GetVideoDuration

Extracts video duration using FFprobe.
func GetVideoDuration(filePath string) (time.Duration, error)
filePath
string
required
Path to video file
duration
time.Duration
Video duration

Example

duration, err := utils.GetVideoDuration("/path/to/video.mp4")
if err != nil {
    log.Printf("Failed to get duration: %v", err)
} else {
    fmt.Printf("Video is %v long\n", duration)
}

Acceleration

ResolveFFmpegCommand

Finds the best FFmpeg binary for the current platform.
func ResolveFFmpegCommand() ([]string, string)
command
[]string
FFmpeg command to use (path or just “ffmpeg”)
message
string
Informational message about which FFmpeg was selected

Apple Silicon Optimization

On Apple Silicon (arm64), automatically detects and prefers native arm64 FFmpeg binaries for best performance. Checks:
  1. FFmpeg in PATH
  2. /opt/homebrew/bin/ffmpeg
  3. /opt/homebrew/opt/ffmpeg/bin/ffmpeg

CheckVideoAcceleration

Verifies hardware acceleration availability.
func CheckVideoAcceleration(ffmpegCmd []string) (bool, string)
ffmpegCmd
[]string
required
FFmpeg command from ResolveFFmpegCommand
available
bool
Whether hardware acceleration is available
message
string
Description of acceleration status

Supported Acceleration

  • macOS: VideoToolbox H.265 encoder (hevc_videotoolbox)
  • Other platforms: Not yet supported

Dependency Checking

CheckDependencies

Verifies that all required external tools are installed.
func CheckDependencies() error
error
error
Returns an error listing missing dependencies

Required Dependencies

  • ffmpeg - Video conversion
  • ffprobe - Video metadata extraction
  • magick - ImageMagick for image conversion

Example

if err := utils.CheckDependencies(); err != nil {
    log.Fatalf("Missing dependencies: %v", err)
}
  • Converter - Uses utils for file operations and metadata
  • Config - Defines supported formats used by utils
  • Security - Uses path utilities for validation

Build docs developers (and LLMs) love