Last active
March 5, 2022 15:38
-
-
Save adisbladis/dac5943d29f12ab08cd9002dcc263968 to your computer and use it in GitHub Desktop.
Download all photos from an iCloud account
This file contains hidden or 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
#!/usr/bin/env python | |
'''Download all iCloud albums in parallel | |
Should work on python 2.7/3.x with the following installed: | |
https://pypi.python.org/pypi/pyicloud/ | |
https://pypi.python.org/pypi/pycurl/ | |
https://pypi.python.org/pypi/futures/ (included in Python 3) | |
''' | |
from concurrent.futures import ThreadPoolExecutor | |
from getpass import getpass | |
import pyicloud | |
import os.path | |
import pycurl | |
import sys | |
import os | |
if sys.version_info[0] == 2: | |
input = raw_input # noqa | |
# pyicloud requires some monkey-patching to work with python3 | |
if sys.version_info[0] == 3: | |
import urllib | |
from urllib.parse import unquote | |
urllib.unquote = unquote | |
class PhotoAlbum(pyicloud.services.photos.PhotoAlbum): | |
def _parse_binary_feed(self, *args): | |
return list(super()._parse_binary_feed(*args)) | |
pyicloud.services.photos.PhotoAlbum = PhotoAlbum | |
def download_photo(url, outfile, dl_size): | |
try: | |
stat = os.stat(outfile) | |
except OSError: | |
pass | |
else: | |
if stat.st_size != dl_size: | |
print('Size mismatch for "{}": {} != {}'.format( | |
outfile, stat.st_size, dl_size)) | |
else: | |
print("Already have {}, skipping".format(outfile)) | |
return | |
with open(outfile, 'wb') as f: | |
c = pycurl.Curl() | |
c.setopt(c.URL, url) | |
c.setopt(c.WRITEDATA, f) | |
c.perform() | |
c.close() | |
print("Saved {}".format(outfile)) | |
if __name__ == '__main__': | |
username = input('Username: ') | |
password = getpass() | |
api = pyicloud.PyiCloudService(username, password) | |
with ThreadPoolExecutor(max_workers=8) as e: | |
try: | |
os.mkdir('icloud-data') | |
except OSError: | |
pass | |
for photo in api.photos.all: | |
outfile = os.path.join('icloud-data', photo.filename) | |
url = photo.versions['original']['url'] | |
dl_size = int(photo.versions['original']['size']) | |
e.submit(download_photo, url, outfile, dl_size) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment