Skip to content

Instantly share code, notes, and snippets.

@bmitchinson
Last active February 6, 2025 00:47
Show Gist options
  • Save bmitchinson/265160cf18872fef9ddd789665ed10b4 to your computer and use it in GitHub Desktop.
Save bmitchinson/265160cf18872fef9ddd789665ed10b4 to your computer and use it in GitHub Desktop.
Photo Copy Script
#!/bin/bash
# Usage: process_media_parallel.sh /path/to/source /path/to/destination
# Requirements: ImageMagick v7 (for the "magick" command)
# copies all mov/mp4/pdfs as is
# copies all images (png/jpg/tiff/heic) to jpgs with max size of 3mb
# copies images from source nested folders into top level destination folder, with src foldername in filename
# creates txt file reporting all files observed but not copied
# Iterated on w GPT o1
# ps run this to make macs stop generating thumbnails for network storage folders of images, cant view
# folders wout rainbow spin w default thumbnail gen.
# `defaults write com.apple.desktopservices DSDontWriteNetworkStores -bool true`
# `killall Finder`
if [ "$#" -ne 2 ]; then
echo "Usage: $0 source_directory destination_directory"
exit 1
fi
src="$1"
dest="$2"
if [ ! -d "$src" ]; then
echo "Source directory does not exist."
exit 1
fi
mkdir -p "$dest"
# File to log unprocessed files
unprocessed="$dest/unprocessed_files.txt"
: > "$unprocessed" # initialize log
if ! command -v magick >/dev/null 2>&1; then
echo "Error: ImageMagick 'magick' command not found. Please install ImageMagick v7 or later."
exit 1
fi
# Maximum number of parallel jobs
max_jobs=16
preserve_metadata() {
local src="$1"
local dest="$2"
touch -r "$src" "$dest"
# Copy all metadata using exiftool
if command -v exiftool >/dev/null 2>&1; then
exiftool -overwrite_original -TagsFromFile "$src" -all:all "$dest"
fi
# Optionally, preserve the creation date using SetFile (requires Xcode Command Line Tools)
if command -v SetFile >/dev/null 2>&1; then
local birth creation_date
birth=$(stat -f %B "$src")
creation_date=$(date -r "$birth" "+%m/%d/%Y %H:%M:%S")
SetFile -d "$creation_date" "$dest"
fi
}
process_file() {
local file="$1"
local ext="${file##*.}"
local ext_lower
ext_lower=$(echo "$ext" | tr '[:upper:]' '[:lower:]')
local relpath dir newname folderprefix output
# Compute relative path from the source and build the new name.
relpath="${file#$src/}"
dir=$(dirname "$relpath")
if [ "$dir" = "." ]; then
newname="$(basename "$file")"
else
folderprefix=$(echo "$dir" | tr '/' '_')
newname="${folderprefix}_$(basename "$file")"
fi
case "$ext_lower" in
jpg|jpeg|png|tif|tiff|heic|heif|gif)
output="$dest/${newname%.*}.jpg"
echo "Processing image: $file -> $output"
# Determine max file size based on extension.
ext="${file##*.}"
if [[ "$ext" =~ ^(tif|tiff)$ ]]; then
max_size=7000000 # 7mb
else
max_size=3000000 # 3mb
fi
magick "$file" -define jpeg:extent="$max_size" "$output"
preserve_metadata "$file" "$output"
;;
mov|mp4|pdf)
# Copy video/pdf files as-is.
output="$dest/$newname"
echo "Copying video or pdf: $file -> $output"
cp "$file" "$output"
;;
*)
echo "Skipping file (unprocessed format): $file"
echo "$file" >> "$unprocessed"
;;
esac
}
# Export variables for subshell access
export src dest unprocessed
# Process files in parallel using process substitution.
while IFS= read -r -d '' file; do
process_file "$file" &
# echo "$file" &
# Limit to $max_jobs concurrent jobs.
while [ "$(jobs -rp | wc -l)" -ge "$max_jobs" ]; do
sleep 0.2
done
# macOS creates hidden AppleDouble files (prefixed with ._) to store extended
# attributes and resource fork data. When you run find, it lists both the
# actual file and its corresponding ._ metadata file, which is why you might
# see duplicates.
done < <(find "$src" \( -type d \( -name "originals" -o -name "thumbs" \) -prune \) -o -type f ! -name '._*' -print0)
wait # Wait for all background jobs to finish
echo "Processing complete. Unprocessed files are listed in: $unprocessed"
echo "Files in src:"
find $src \( -type d \( -name "originals" -o -name "thumbs" \) -prune \) -o -type f | wc -l
echo "Files in destination:"
find $dest -type f | wc -l
# ddrescue /dev/sr0 /mnt/user/mitchinson_primary/ArchivalStuff/gma_feb_25/cds/9_cd.iso ~/9_cd.log
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment