Last active
February 6, 2025 00:47
-
-
Save bmitchinson/265160cf18872fef9ddd789665ed10b4 to your computer and use it in GitHub Desktop.
Photo Copy Script
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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