DEV Community

Cover image for How I Built a Hybrid Cloud-to-Local Photo Workflow Using Google Sheets and Python
Ray Basweti
Ray Basweti

Posted on

How I Built a Hybrid Cloud-to-Local Photo Workflow Using Google Sheets and Python

Bridging the gap between mobile culling and local file management without uploading 1TB of data.

As a developer and photographer, I faced a unique bottleneck. I wanted to select my best photos while sitting on the couch using my phone, but my 50MB RAW files lived on a massive hard drive in my office. I didn't want to pay for expensive cloud culling software, and I certainly didn't want to upload terabytes of data just to select 50 images.

My solution? A Hybrid Workflow using Google Sheets as a lightweight frontend and Python as the heavy-lifting backend.

The Architecture

1. The Cloud Interface (Google Apps Script)

I utilized Google Sheets not as a spreadsheet, but as a hosting platform for a custom HTML5 Web App.

Why Sheets? It handles authentication and hosting for free.

The UI: I built a responsive, mobile-first grid using the IBM Plex Sans design system. It allows for "Start/End" range input (e.g., IMG_8100 to IMG_8500) and renders clickable tiles.

The selection page where a user marks the photos chosen

The Output: Instead of saving data to a database, it generates a "Recipe" โ€” a PDF file containing only the IDs of the selected photos.

2. The Local Engine (Python & Flask)

Once the "Recipe PDF" is generated, the local work begins.

The Stack: I wrapped a Python script in a Flask web server to provide a modern UI, but used Tkinter for native Windows folder dialogs (because browser file pickers are too restrictive).

The Logic:

The app parses the PDF using pypdf to extract IDs (e.g., 8104).

It scans a local source directory for matches, handling multiple file extensions (.CR2, .JPG, .NEF).

The local page where the user loads the generated PDF and selects the source and destination folders

It performs a safe copy (not cut) to a destination folder.

Technical Challenges & Solutions

The "WinError 32" Lock

The Problem: On Windows, reading a PDF via Flask kept the file handle open. When I tried to move or delete the file later, the app crashed with PermissionDenied.

The Fix: I implemented a strict file.seek(0) and explicit stream closure pattern in Python to release the file handle immediately after parsing the text.

Mobile UX on Sheets

The Problem: Scrolling through 2,000 grid items on a phone was sluggish, and the interface felt like... well, a spreadsheet.

The Fix: I wrote custom CSS media queries to switch from a flexible grid to a strict 2-column layout on mobile, with increased touch targets and a sticky footer, making it feel like a native app.

The Result

This tool reduced a 2-hour workflow to 15 minutes. I can cull photos on my phone, AirDrop the PDF to my PC, and have Python extract the files instantly.

Future Plans

I am looking into packaging this as an Electron app for easier distribution and adding AI-based blur detection.

Built with ๐Ÿ’™ by Ray Basweti. Check out the code on my Github.

Top comments (1)

Collapse
 
martijn_assie_12a2d3b1833 profile image
Martijn Assie

This is a clever workaround for a very real pain point, nicely thought through from end to end. Using Google Sheets as a lightweight, authenticated frontend is a great example of bending familiar tools instead of overengineering ๐Ÿ‘Œ
The โ€œrecipe PDFโ€ idea is especially clean, it keeps the cloud side dumb and the local side in control, which feels right for huge RAW libraries.
Small extra thought: if you ever take this further, adding simple batching or progress feedback on the Python side could make long copy jobs feel a lot more transparent. Overall, solid engineering and very practical.