iCloudPy

repository·main·Indexed 42 days ago

https://github.com/mandarons/icloudpy

A Python wrapper for iCloud web services that enables programmatic interaction with iCloud data, including Find My iPhone, Calendar, Contacts, iCloud Drive, and the iCloud Photo Library. It provides the ICloudPyService class for authentication and supports 2SA/2FA flows, system keyring password management, and file operations within iCloud Drive.

Tokens
2.4K
Snippets
12
Records
13
Agent score
5%

What's inside iCloudPy

  1. Handle Two-Step and Two-Factor Authentication (2SA/2FA)

    main

    If your account has security enabled, you must handle the authentication flow by checking api.requires_2fa or api.requires_2sa.

    For 2FA:

    1. Check api.requires_2fa.
    2. Prompt the user for the code received on an approved device.
    3. Call api.validate_2fa_code(code).
    4. If the session is not trusted, call api.trust_session().

    For 2SA:

    1. Check api.requires_2sa.
    2. Iterate through api.trusted_devices to let the user pick a device.
    3. Call api.send_verification_code(device).
    4. Prompt for the code and call api.validate_verification_code(device, code).
  2. Iterate through photo assets in an album

    main

    Once you have a PhotoAlbum object, you can iterate over it to access individual PhotoAsset objects. Each asset contains metadata such as filename.

    >>> for photo in api.photos.albums['Screenshots']:
            print photo, photo.filename
    <PhotoAsset: id=AVbLPCGkp798nTb9KZozCXtO7jds> IMG_6045.JPG
  3. List and access Apple devices

    main

    Use the devices property to get a dictionary of all devices associated with the account. You can access a specific device using its index or its unique ID. If there is only one device, you can use the iphone shorthand property.

    # List all devices
    api.devices
    
    # Access by index
    api.devices[0]
    
    # Access by ID
    api.devices['unique_device_id']
    
    # Shorthand for the first device (often the iPhone)
    api.iphone
  4. Access the iCloud Photo Library

    main

    You can access the entire iCloud Photo Library via the api.photos.all property. Individual albums can be accessed using the api.photos.albums dictionary, where the key is the album name.

    • api.photos.all: Returns the 'All Photos' album. This album is sorted by added_date (most recent first).
    • api.photos.albums['Album Name']: Returns a specific album. These are sorted by asset_date (EXIF date).
    >>> api.photos.all
    <PhotoAlbum: 'All Photos'>
    
    >>> api.photos.albums['Screenshots']
    <PhotoAlbum: 'Screenshots'>
  5. Download specific photo versions

    main

    Each PhotoAsset has a versions property that provides access to different resolutions or formats (e.g., 'medium', 'original', 'thumb'). To download a specific version, pass the version key to the .download() method.

    >>> photo.versions.keys()
    [u'medium', u'original', u'thumb']
    
    >>> download = photo.download('thumb')
    >>> with open(photo.versions['thumb']['filename'], 'wb') as thumb_file:
            thumb_file.write(download.raw.read())
  6. Manage Calendar events

    main

    The api.calendar service allows you to fetch events. You can fetch all events for the current month, events within a specific date range, or details for a single event.

    from datetime import datetime
    
    # Current month's events
    api.calendar.events()
    
    # Events in a date range
    api.calendar.events(datetime(2012, 1, 1), datetime(2012, 1, 31))
    
    # Single event details
    api.calendar.get_event_detail('CALENDAR', 'EVENT_ID')
  7. Use Find My iPhone features

    main

    Once authenticated, you can interact with Find My iPhone features via the api.iphone object:

    • Location: Get the last known location using api.iphone.location().
    • Status: Get a subset of device properties (battery, name, etc.) using api.iphone.status(). You can pass a list of specific property names to status() to get more data.
    • Play Sound: Trigger a ringtone on the device using api.iphone.play_sound(). You can pass a custom subject via the subject argument.
    • Lost Mode: Enable lost mode using api.iphone.lost_device(phone_number, message). This allows callers to reach a specific number without a passcode and displays a custom message.
    # Get location
    api.iphone.location()
    
    # Get status with specific properties
    api.iphone.status(['batteryLevel', 'deviceStatus'])
    
    # Play sound
    api.iphone.play_sound()
    
    # Enable lost mode
    api.iphone.lost_device('555-373-383', 'Thief! Return my phone immediately.')
  8. Authenticate with ICloudPyService

    main

    To connect to iCloud, instantiate the ICloudPyService class with your username and password. If you are using an account in the China region, you must provide the specific home_endpoint and setup_endpoint.

    from icloudpy import ICloudPyService
    
    # Standard authentication
    api = ICloudPyService('jappleseed@apple.com', 'password')
    
    # Authentication for China region
    api = ICloudPyService(
        'jappleseed@apple.com', 
        'password', 
        home_endpoint="https://www.icloud.com.cn",
        setup_endpoint="https://setup.icloud.com.cn/setup/ws/1"
    )
  9. Access App Data in iCloud Drive

    main

    Individual apps store related documents in nodes that may not appear in a standard api.drive.dir() call. Use api.drive.get_app_node(app_id) to retrieve these nodes. Once retrieved, the node can be used with standard methods like mkdir, rename, and delete.

    # Retrieve an app-specific node
    node = api.drive.get_app_node("XXXXXXXXXX.com.apple.iMovie")
    
    # Use the node like a directory
    node.mkdir('2020')
    node.delete()
  10. Manage iCloud Drive files and folders

    main

    Interact with iCloud Drive via the api.drive property. The drive behaves like a file system where you can navigate directories and perform file operations.

    Navigation and Operations:

    • Access directories: api.drive['folder_name'].
    • List directory contents: api.drive['folder_name'].dir().
    • Create directory: api.drive['folder_name'].mkdir('new_folder').
    • Rename: api.drive['folder_name'].rename('new_name').
    • Delete: api.drive['folder_name'].delete().

    File Handling:

    • Get file metadata: file_node.name, file_node.size, file_node.date_modified.
    • Upload a file: api.drive['folder_name'].upload(file_handle). (Use binary mode for file handles).
    • Download/Read a file: Use file_node.open(stream=True) to get a response object for reading contents.
    from shutil import copyfileobj
    
    # Navigate and list
    api.drive.dir()
    api.drive['Holiday Photos']['2013'].dir()
    
    # Upload
    with open('Vacation.jpeg', 'rb') as f:
        api.drive['Holiday Photos'].upload(f)
    
    # Download/Copy
    drive_file = api.drive['Holiday Photos']['image.jpg']
    with drive_file.open(stream=True) as response:
        with open('local_copy.jpg', 'wb') as f_out:
            copyfileobj(response.raw, f_out)
  11. Access iCloud Contacts

    main

    Access the iCloud address book via the api.contacts property. Use api.contacts.all() to iterate through contacts. Note that this only includes contacts stored in iCloud, not federated contacts (like Facebook).

    for c in api.contacts.all():
        print(c.get('firstName'), c.get('phones'))
  12. Download a photo asset

    main

    To download a photo, call the .download() method on a PhotoAsset. This returns a requests.Response object with stream=True.

    Important: Because the response is streamed, you should use a buffered strategy (like shutil.copyfileobj or similar) to avoid loading the entire file into memory before writing to disk.

    >>> photo = next(iter(api.photos.albums['Screenshots']), None)
    >>> download = photo.download()
    >>> with open(photo.filename, 'wb') as opened_file:
            opened_file.write(download.raw.read())