Files
homelab/infrastructure/graduated/ios/README.md

4.2 KiB

iOS

iPhoto Export

  1. Download all your photos in batches of 1000

  2. Extract all zip files

  3. Use find to copy all files to photo-archive

    find ./icloud -type f -not -name '*.zip' -print0 | xargs -0 -I % /bin/bash -c 'cp -v --backup=numbered "%" photo-archive/'

  4. Verify all photos were copied successfully

    find ./icloud -type f -not -name '*.zip' | wc -l find ./photo-archive -type f | wc -l

  5. Rename duplicates so they have the correct extension

    # Collect duplicates
    find photo-archive -name '*.~*' > duplicates.txt
    cat duplicates.txt | wc -l
    
    # dry run convert duplicates named *.~n~ to something else
    for item in $(cat duplicates.txt); do echo mv $item photo-archive/$(python -c "from pathlib import Path; number=Path(\"$item\").suffix ;name = Path(Path(\"$item\").stem); print(name.stem + '_' + number.replace('~','').replace('.','') + name.suffix)"); done    
    # actual rename
    for item in $(cat duplicates.txt); do mv $item photo-archive/$(python -c "from pathlib import Path; number=Path(\"$item\").suffix ;name = Path(Path(\"$item\").stem); print(name.stem + '_' + number.replace('~','').replace('.','') + name.suffix)"); done
    
    # verify
    find photo-archive -name '*.~*'
    find ./photo-archive -type f | wc -l
    
  6. Remove live photos

    Live photos show up as a .MOV files. They should be the only .MOV files in the folder. Remove them with:

    find photo-archive -name '*.MOV' -delete
    
    # verify
    find ./photo-archive -type f | wc -l
    
  7. Organize by year

    To keep your photos folder from getting too bloated you should organize your photos into subfolders by (at least) year.

    Phockup seems to be able to do this:

    podman run -v ~/Pictures:/mnt ivandokov/phockup:latest /mnt/iphone /mnt/iOS -d YYYY
    

Photos Export

https://github.com/icloud-photos-downloader/icloud_photos_downloader

podman run -it --rm --name icloudpd \
    -v $(pwd)/iPhotos:/data \
    -e TZ=America/New_York \
    icloudpd/icloudpd:latest \
    icloudpd \
        --directory /data \
        --username apple@ducoterra.net \
        --watch-with-interval 3600

You can browse all downloaded photos with feh:

pacman -S feh

feh --recursive --scale-down -d -S filename $(pwd)/iPhotos

Extensions

  • PNG
  • HEIC: iOS HDR photo
  • MOV
  • DNG: iOS raw photo
  • JPG
  • 3gp: quicktime video file
  • MP4
  • GIF
  • WEBP: Google photo container
  • CR2: Canon raw photo

Conversion

You will need imagemagick and libraw:

pacman -S imagemagick libraw

Then you can convert any image with magick:

magick input.HEIC output.png
magick input.HEIC output.jpeg

You get a list of all unique file extensions with:

find . -type f | awk -F. '!a[$NF]++{print $NF}'

Then use those file extensions to create a find command:

# -o means "or" in this case
# Parentheses are required
find . -type f -printf '%p\n' \( \
    -iname '*.jpg' \
    -o -iname '*.heic' \
    -o -iname '*.dng' \
    -o -iname '*.png' \
    -o -iname '*.WEBP' \
    -o -iname '*.cr2' \
\)

And then use xargs with magick to convert the images:

find . -type f \( \
    -iname '*.jpg' \
    -o -iname '*.heic' \
    -o -iname '*.dng' \
    -o -iname '*.png' \
    -o -iname '*.WEBP' \
    -o -iname '*.cr2' \
\) -print0 | xargs -0 -P $(nproc) -I % python -c "\
import subprocess; \
from pathlib import Path; \
to_folder = '/run/media/ducoterra/photos/'; \
p = Path(\"%\"); \
realpath = (str(p.parent) + '/' if str(p.parent) != '.' else '') + p.stem + '.jpg'; \
subprocess.run(['mkdir', '-p', to_folder + str(Path(realpath).parent)]); \
subprocess.run(['magick', \"%\", to_folder + realpath])"

And then videos:

find . -type f \( \
    -iname '*.MOV' \
    -o -iname '*.3gp' \
    -o -iname '*.MP4' \
    -o -iname '*.GIF' \
\) -print0 | xargs -0 -I % python -c "\
import subprocess; \
from pathlib import Path; \
to_folder = '/run/media/ducoterra/photos/'; \
p = Path(\"%\"); \
realpath = (str(p.parent) + '/' if str(p.parent) != '.' else '') + p.stem + '.mp4'; \
subprocess.run(['mkdir', '-p', to_folder + str(Path(realpath).parent)]); \
subprocess.run(['ffmpeg', '-i', '%', '-c', 'copy', to_folder + realpath])"