1 #!/bin/bash
  2 
  3 #############################################################################
  4 # Back up photos from an iPhone
  5 #############################################################################
  6 # Includes mutation .plist files saved as .AAE files
  7 # and their corresponding edited jpegs
  8 
  9 # This script only works if the full resolution ORIGINAL images are stored
 10 # on the iPhone and not on iCloud. I don't think this is the default
 11 # if you have iCloud Photos enabled, so check your settings!
 12 #
 13 #
 14 # Usage: Modify the directory variables where indicated below
 15 # then run the script without any arguments.
 16 #
 17 # Pay attention to any warnings generated.
 18 # You can safely ignore any skip messages genreated while processing
 19 # the Adjustments directories.  It's just letting you know that
 20 # no edits were found for a particular photo.
 21 #
 22 #############################################################################
 23 # This script makes the following assumptions, which are true
 24 # as of iOS version 12.  If Apple changes any of these things,
 25 # the script may behave unexpectedly.
 26 #############################################################################
 27 # 1: The DCIM and PhotoData directories are accessible from the iPhone's root
 28 # 2: The DCIM directory has subfolders in the format of ###APPLE
 29 #    and all the photos we want synced are inside that
 30 # 3: The PhotoData directory structure is as follows:
 31 #    (iphone)/Photodata/Mutations/DCIM/
 32 #    with subfolders in the same ###APPLE format
 33 # 4: Within these PhotoData directories we expect to find
 34 #    an Adjustments subfolder and some files within that:
 35 #    an Adjustments.plist file and either a FullSizeRender.jpg
 36 #    or an IMG_####.jpg for slowmo's and timelapses.
 37 #############################################################################
 38 # made with <3 by ~inthreedee ( https://tilde.town/~inthreedee/ )
 39 #############################################################################
 40 
 41 
 42 #############################################################################
 43 ######################### Edit these directories! ###########################
 44 #############################################################################
 45 # Source MUST be the root iPhone directory that has
 46 # the subfolders DCIM and PhotoData within it.
 47 # If auto-mounted by GNOME, it will look something like this:
 48 # src="/run/user/1000/gvfs/afc:host=295ad715b7ff8ec030151d433d446db89e576df99"
 49 # example dest="/mnt/backups/pictures/example"
 50 source="/run/user/1000/gvfs/afc:host=######"
 51 dest="/mnt/backups/Pictures/example"
 52 
 53 # Which of the ###APPLE directories do we want to sync?
 54 # This array MUST be formatted as a list of numbers separated by spaces.
 55 # {#..#} can be used for an inclusive sequence of numbers. For example:
 56 # =(111)
 57 # =(111 112 114)
 58 # =({111..115} 118 121)
 59 processdirs=(111..121)
 60 
 61 #############################################################################
 62 ########################### No touchy beyond here! ##########################
 63 #############################################################################
 64 
 65 # Append APPLE to each of the array elements so we get the full
 66 # folder name structure of ####APPLE
 67 processdirs=( "${processdirs[@]/%/APPLE}" )
 68 
 69 # These directories should be left alone unless Apple
 70 # changes their iOS directory structure
 71 photos="$source/DCIM"
 72 photodata="$source/PhotoData/Mutations/DCIM"
 73 
 74 if [ ! -d "$source" ] || [ ! -d "$photos" ] || [ ! -d "$photodata" ]; then
 75     echo "Source directory does not exist, exiting."
 76 elif [ ! -d "$dest" ]; then
 77     echo "Destination directory does not exist, exiting."
 78 elif [ ! -x "$(command -v rsync)" ]; then
 79      echo "This script requires rsync"
 80 else
 81     # First, sync the photo originals
 82     echo "Syncing photos..."
 83 
 84     # loop through all the ###APPLE directories
 85     for appledir1 in "$photos"/*; do
 86         # make sure it is a directory, not some random file
 87         if [ -d "$appledir1" ]; then
 88             appledirname="$( echo "$appledir1" | grep -o "........$")"
 89             # Is the current directory one we want to process?
 90             if echo "${processdirs[@]}" | grep -q -w "$appledirname"; then
 91                 # back up everything we find here
 92                 rsync -ah --info=progress2 "$photos"/"$appledirname"/ "$dest"
 93             else
 94                 echo "Skipping photos in $appledirname"
 95             fi
 96         else
 97             echo "WARNING Skipping invalid directory: $appledir1"
 98         fi
 99     done
100     echo "Done."
101 
102 
103     # The photo originals are synced, now sync the edits
104     echo "Syncing photo data..."
105 
106     # loop through all the ###APPLE directories
107     for appledir2 in "$photodata"/*; do
108         # make sure it is a directory, not some random file
109         if [ -d "$appledir2" ]; then
110             appledirname="$( echo "$appledir2" | grep -o "........$")"
111             # Is the current directory one we want to process?
112             if echo "${processdirs[@]}" | grep -q -w "$appledirname"; then
113                 # loop through all the IMG_#### directories
114                 for imgdir in "$photodata"/"$appledirname"/*; do
115                     if [ -d "$imgdir" ]; then
116                         iname="$(echo "$imgdir" | grep -o "........$")"
117 
118                         if [ -d "$imgdir/Adjustments" ]; then
119                             adjustsdir="$imgdir/Adjustments"
120 
121                             # Adjustments.plist files get renamed and saved as *.AAE
122                             if [ -f "$adjustsdir"/Adjustments.plist ]; then
123                                 rsync -a --no-relative --no-implied-dirs "$adjustsdir"/Adjustments.plist "$dest"/"$iname".AAE
124                             else
125                                 echo "No Adjustments.plist found for $iname, skipping."
126                             fi
127 
128                             # Save the edited image alongside the original as *e.jpg
129                             if [ -f "$adjustsdir"/FullSizeRender.jpg ]; then
130                                 rsync -a --no-relative --no-implied-dirs "$adjustsdir"/FullSizeRender.jpg "$dest"/"$iname"e.jpg
131                                 # NOTE there may be a lower resolution IMG_####.jpg here
132                                 # Not sure why. Might be a lower quality version if the full
133                                 # quality version is stored only on iCloud?
134                                 # Obviously the full quality version must be on the phone for backups
135 
136                             # Slowmos and timelapses won't have a FullSizeRender.jpg, but they will
137                             # have thumbnail screencaps already named IMG_####.jpg. Back these up too
138                             elif [ -f "$adjustsdir"/"$iname".jpg ]; then
139                                 rsync -a --no-relative --no-implied-dirs "$adjustsdir"/"$iname".jpg "$dest"/"$iname".jpg
140 
141                             else
142                                 echo "No jpg image found for $iname, skipping."
143                             fi
144                         else
145                             echo "No Adjustments directory found for $iname, skipping."
146                         fi
147                     else
148                         echo "WARNING Skipping invalid directory: $imgdir"
149                     fi
150                 done
151             else
152                 echo "Skipping photos in $appledirname"
153             fi
154         else
155             echo "WARNING Skipping invalid directory: $appledir2"
156         fi
157     done
158     echo "Done."
159     echo "Photo backup complete."
160 fi