Automating Image Conversion and Uploads with Python: HEIC to PNG on Google Drive

Introduction

Apple’s HEIC image format, known for its efficiency, isn’t always compatible with all platforms or devices. This guide walks you through a Python script that automatically converts HEIC images to PNG, uploads them back to Google Drive, and cleans up the original files. We’ll cover everything from setting up Google credentials to running the script.

Setting Up Your Environment

1. Install Python Libraries You’ll need Python installed on your machine along with a few libraries. Open your terminal or command prompt and run:

pip install pillow pyheif google-api-python-client google-auth-httplib2 google-auth-oauthlib
  • Pillow for image processing.
  • pyheif for handling HEIC files.
  • Google Client libraries to interact with the Drive API.

2. Google Drive API and Credentials To interact with Google Drive:

To enable our Python script to interact with Google Drive for converting and managing images, we first need to set up access to the Google Drive API. This involves a few steps within the Google Cloud Platform and handling credentials correctly.

Create a Project in Google Cloud Platform (GCP)

Go to the Google Cloud Console. Sign in with your Google account. Click on the “Select a project” dropdown near the top of the page, then click “NEW PROJECT” to create a new project. Enter a project name and select a billing account as required. Click “CREATE”.

Enable the Google Drive API

With your new project selected, navigate to the “Dashboard” pane on the GCP console. Click on “Go to APIs overview” → “+ ENABLE APIS AND SERVICES”. In the API Library, search for “Google Drive API” and select it. Click the “ENABLE” button to enable the Google Drive API for your project.

Configure Consent Screen

In the sidebar under “APIs & Services”, select “OAuth consent screen”. Choose the User Type (usually “External”) and click “CREATE”. Fill in the required fields under the “App registration” section. This information will be shown to users when they first authenticate with your script.

Create Credentials

In the sidebar under “APIs & Services”, select “Credentials”. Click “+ CREATE CREDENTIALS” at the top and choose “OAuth client ID”. If prompted to set up the OAuth consent screen, fill in the required details and save. For “Application type”, select “Desktop app”, give it a name, and click “Create”. Your credentials (client ID and client secret) will now be displayed. Click “OK” to dismiss the dialog.

Download Credentials

Under “OAuth 2.0 Client IDs”, find your newly created credentials and click on the download button on the right side to download the credentials.json file. Save this file to your project directory where your Python script resides.

Initialize in Your Script Now that you have downloaded credentials.json, the script will use this file to authenticate users. Ensure the file is in the same directory as your Python script or provide the correct path in your script to locate this file.

The Script Explained

Below is the Python script broken down into sections:

Import Libraries

We start by importing necessary libraries for handling files, images, and Google Drive API interactions.

import io
import os
import pyheif
from PIL import Image
from googleapiclient.discovery import build
from googleapiclient.http import MediaIoBaseDownload, MediaFileUpload
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials

Google Authentication Flow

The SCOPES variable defines the permissions for accessing Google Drive. The get_credentials() function manages the authentication flow. This function checks for existing credentials or initiates an authorization flow if none are found.

SCOPES = ['https://www.googleapis.com/auth/drive']

def get_credentials():
    creds = None
    # The file token.json stores the user's access and refresh tokens.
    if os.path.exists('token.json'):
        creds = Credentials.from_authorized_user_file('token.json', SCOPES)
    # If there are no (valid) credentials available, let the user log in.
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(Request())
        else:
            flow = InstalledAppFlow.from_client_secrets_file('credentials.json', SCOPES, redirect_uri='https://localhost')
            auth_url, _ = flow.authorization_url(
                prompt='consent', 
                access_type='offline',
                include_granted_scopes='true'
            )
            
            print("Please go to this URL and authorize access:")
            print(auth_url)
            code = input("Enter the authorization code: ")
            flow.fetch_token(code=code)
            
            # Save the credentials for the next run
            with open('token.json', 'w') as token_file:
                token_file.write(flow.credentials.to_json())
            creds = flow.credentials
    return creds

creds = get_credentials()
service = build('drive', 'v3', credentials=creds)

Downloading and Converting Images

Each HEIC file from Drive is downloaded, converted to PNG using pyheif and Pillow, then saved locally. The new PNG files are uploaded back to Google Drive, and the original HEIC files are deleted.

# Replace FOLDER_ID with your Google Drive folder ID
FOLDER_ID = 'FOLDER_ID'

# Folder to save images
image_folder = 'images'
if not os.path.exists(image_folder):
    os.makedirs(image_folder)

# Query to get all HEIC files from the folder
query = f"parents = '{FOLDER_ID}' and mimeType = 'image/heif'"
results = service.files().list(q=query, pageSize=1000, fields="nextPageToken, files(id, name)").execute()

items = results.get('files', [])
print(f'{len(items)} images found.')

if not items:
    print("No HEIC files found.")
else:
    for item in items:
        request = service.files().get_media(fileId=item['id'])
        fh = io.BytesIO()
        downloader = MediaIoBaseDownload(fh, request)
        done = False

        while done is False:
            status, done = downloader.next_chunk()
            print(f"Download {item['name']} {int(status.progress() * 100)}%.")

        fh.seek(0)
        heif_file = pyheif.read(fh)
        image = Image.frombytes(
            heif_file.mode, 
            heif_file.size, 
            heif_file.data,
            "raw",
            heif_file.mode,
            heif_file.stride,
        )

        # Convert to PNG and save in the 'images' folder
        png_filename = item['name'].rsplit('.', 1)[0] + '.png' 
        png_filepath = os.path.join(image_folder, png_filename)
        image.save(png_filepath, format="PNG")

        # Upload PNG back to Google Drive
        file_metadata = {'name': png_filename, 'parents': [FOLDER_ID]}
        media = MediaFileUpload(png_filepath, mimetype='image/png')
        file = service.files().create(body=file_metadata, media_body=media, fields='id').execute()
        print(f"Uploaded {png_filename} with file ID {file.get('id')}")

        # Delete the original HEIC file from Google Drive
        try:
            service.files().delete(fileId=item['id']).execute()
            print(f"Deleted original file: {item['name']}")
        except Exception as error:
            print(f"An error occurred: {error}")

Running the Script

  1. Place the credentials.json file in your project directory.
  2. Adjust FOLDER_ID in the script to point to your specific Google Drive folder.
  3. Run the script. You’ll be prompted to authorize access on the first run. Copy the token in the redirected URL into your console to generate the token file.

Conclusion

This script simplifies image format management and can be modified to suit various needs. Understanding and customizing the script can provide a deeper insight into Python scripting and API interactions.

Leave a Reply

Your email address will not be published. Required fields are marked *