CLI tool to sync and clean Google Photos backups on Google Drive
Project description
📸 Google Photos → Google Drive Backup & Cleanup Tool
A unified python-based toolkit and React Native companion app to process, sync, and fix timestamps of Google Photos backups on Google Drive using rclone. This project is designed to identify and organize original-quality files that consume storage quota and manage Google Photos metadata.
⚙️ Prerequisites
To use this toolkit, you must have rclone installed and configured on your system:
- Install rclone: Follow the rclone installation instructions.
- Configure your Google Drive remote: Set up your Google Drive connection by running:
rclone configFollow the step-by-step prompts to create a new remote (e.g.gdrive:). Refer to the official rclone config command guide for more details. - Verify configuration: Make sure you can list your drive contents:
rclone lsd gdrive:
To run the React Native Android companion app, you will also need:
- Node.js (v18 or higher) and npm installed.
- Expo Go app installed on your Android device (or an Android emulator configured via Android Studio).
🚀 High-Level End-User Guide
Follow this standard sequence to backup, audit, and clean up your Google Photos storage:
-
Install Browser Userscript:
- Install the Tampermonkey browser extension.
- Install the google_photos_toolkit.user.js userscript (located in the root of this repo).
- Go to photos.google.com, filter by "Consuming" space / "Original" quality, and click Export Metadata to download a CSV file. Place it in
data/csv/.
-
Google Drive Sync (Cloud-to-Cloud):
- Run the python CLI or the companion Android app to compare your exported CSV metadata against your actual Google Drive.
- Automatically copy missing files cloud-to-cloud to your backup directory.
-
Verify & Fix Timestamps:
- Run the metadata touch tools to align Drive modification timestamps with your photo's local EXIF/CSV datetimes using
rclone touchwithout re-uploading payloads.
- Run the metadata touch tools to align Drive modification timestamps with your photo's local EXIF/CSV datetimes using
-
Pixel Re-upload & Deletion Workflow (Free Storage Sync):
- To safely clean up cloud storage and re-upload original files via Pixel phone, see the Pixel Re-upload Guide.
🏗️ Project Architecture & Code Structure
.
├── cleaner.py # Main CLI entrypoint (argparse interface)
├── google_photos_toolkit.user.js # Browser userscript for metadata export & Drive sync
├── src/ # Core python modules
│ ├── sync.py # Cloud-to-cloud file backup copying logic
│ ├── metadata.py # Timestamp comparison and Drive fixing via 'rclone touch'
│ └── process_backup.py # Parses CSV & updates local file modification times
├── android-app/ # React Native/Expo Android companion application
├── data/ # Contains inputs/caches (Ignored by Git except templates/configs)
│ ├── csv/ # Input metadata exports from Google Photos Toolkit (GPTK)
│ └── json/ # Drive file listings (drive_index_photo_backUp.json, etc.)
└── logs/ # Executables and commands logs (rclone commands lists)
📦 Installation & Packaging
You can run the tool directly as a script, install it locally as a system-wide CLI command, or compile it into a single standalone executable binary.
1. Run Directly as a Script
Run directly from the repository root:
python3 cleaner.py [command] [options]
2. Install as a Local CLI Command
You can install the tool as a system-wide CLI package (mapped to the command gp-cleaner):
# Install in editable mode
pip install -e .
# If using an externally managed environment (e.g. Homebrew on macOS)
pip install -e . --break-system-packages --user
Once installed, you can invoke the tool from any directory:
gp-cleaner [command] [options]
3. Build a Standalone macOS Executable Binary
To package the app into a single compiled binary that doesn't depend on system Python path settings:
- Install PyInstaller:
pip install pyinstaller --break-system-packages --user
- Compile the binary:
pyinstaller --onefile cleaner.py --name gp-cleaner
- Run the standalone binary from the
dist/directory:./dist/gp-cleaner [command] [options]
🔧 CLI Entry Point
All operations are run via cleaner.py (or gp-cleaner if installed) using positional commands:
1. File Synchronization (sync)
Handles synchronizing files inside Google Drive (cloud-to-cloud) or uploading missing local folders.
# Sync original-quality backup photos into year-wise folders
gp-cleaner sync backup --remote gdrive:
# Compare space-consuming CSV metadata against Google Drive index (cloud-to-cloud sync)
# This finds files present on Drive but not in photos_backUp, and copies them to the backup directory
gp-cleaner sync consuming --csv "o consuming album metadata.csv" --remote gdrive:
# Compare a local folder (e.g. GPH OP) against Drive index and upload missing files to a destination folder on Drive
# (Prevents duplicate files and folder structures)
gp-cleaner sync upload-local --dir "data/GPH OP/ALL_PHOTOS" --dest "O Consuming" --remote gdrive:
2. Metadata & Timestamps (metadata)
Identifies, verifies, and fixes timestamp discrepancies between local files, Google Drive, and CSV database reports.
# Compare local and Drive indices, and execute 'rclone touch' directly on Drive to fix mismatched times
gp-cleaner metadata fix-drive --remote gdrive:
# Fix local photo modification timestamps
gp-cleaner metadata fix-local
# Verify that local photo timestamps (EXIF & modify date) match dates in a Google Photos CSV file
gp-cleaner metadata verify-csv --csv "o consuming album metadata.csv" --dir "data/GPH OP/ALL_PHOTOS"
3. CSV Processing & Local Organizing (process)
Processes local files against GPTK metadata CSVs to extract correct timestamps and write them to the local files system (os.utime).
# Process and update timestamps for local backup photos
gp-cleaner process backup
4. Google Takeout Metadata Merger (process takeout)
Recursively walks a Google Takeout directory to find companion JSON files, extract timestamps, descriptions, and GPS tags, and embed them into the photos/videos.
# Process and merge Google Takeout directory metadata
gp-cleaner process takeout --dir <takeout_dir>
# Verify Google Takeout metadata updates
gp-cleaner metadata verify-takeout
📱 React Native Android Companion App
The project includes an Expo-based Android companion application located under the android-app/ directory. This app allows you to:
- View and index local photo directories.
- Connect to the Google Photos API to audit large storage-consuming files.
- Track and verify Google Takeout processing logs directly on your mobile device.
Running the App:
-
Install Dependencies:
cd android-app npm install
-
Start the Development Server:
npx expo start
-
Open on Device:
- Scan the QR code displayed in your terminal using the Expo Go app on your Android device.
- Alternatively, press
ain your terminal to open it in a running Android emulator.
🧠 Core System Design & Workflow
1. Data Source (Google Photos Toolkit)
Because the official Google Photos API strips GPS/EXIF metadata and hides the takesUpSpace attribute, this toolkit relies on the Google Photos Toolkit (GPTK) userscript (google_photos_toolkit.user.js in the root).
- Run the userscript in Tampermonkey on photos.google.com.
- Filter for "Consuming" space and "Original" quality.
- Click "Export Metadata" to get
metadata.csv. - Save the files under
data/csv/.
2. Google Drive Indexing
We index files on Google Drive using rclone to avoid querying the API repeatedly:
rclone lsjson -R "gdrive:photos_backUp" > data/json/drive_index_photo_backUp.json
3. Local Comparison & Cloud Execution
- The script loads the local files (
data/photos/photos_backUp) and indexes their sizes and modification times. - It compares them to the cached
drive_index_photo_backUp.json. - If timestamps mismatch (e.g., due to timezone shifts or upload bugs), the
metadata fix-drivecommand usesrclone touchto adjust the remote file's modified time without re-uploading the file payload.
4. Google Takeout Metadata Merger & Matching Logic
For a complete guide of the algorithms, code snippets, and design choices behind this feature, see LEARNING.md. Below is a summary of the core concepts:
- Fuzzy Filename Matching: Resolves Takeout quirks such as name truncations, supplemental suffixes (e.g.
.supplemental-metadata.json,.supp.json), double dots (..json), and duplicate bracket shifts (e.g.photo.jpg(1).jsonmatchingphoto(1).jpg). - Batch Exiftool Operations: Minimizes process startup overhead by writing all modifications to a single temporary JSON and importing it in one command using
exiftool -json=temp.json -@ files.txt. - Filesystem Date Correction: Updates the filesystem
mtimeusing Python'sos.utime()to match the JSON's true epoch timestamp (seconds since epoch) so that Finder and File Explorer sort photos chronologically.
⚠️ Important Rules for AI Developers
- No Standalone Scripts: Do not create new top-level
run_*.shor*.pyscripts in the root orscripts/. Always implement logic as functions inside thesrc/modules and register them as subcommands incleaner.py. - Paths: Keep paths relative to the workspace. Local raw photo directories are typically ignored by git (e.g.,
data/photos/). - Parallelism: When generating mass
rclonecommands, write the commands tologs/and execute them concurrently viarun_rclone_commandsinsrc/sync.pyto optimize speed.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file google_photo_meta_fixer-1.0.0.tar.gz.
File metadata
- Download URL: google_photo_meta_fixer-1.0.0.tar.gz
- Upload date:
- Size: 26.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f0d01d96933cbdaf9336bc13d336b541407c5986203b2fe690e6e166e45bce04
|
|
| MD5 |
629858e4fe00b6a9fe341a8b35f297d2
|
|
| BLAKE2b-256 |
ff57030cf558ac633d873a40e73abab426c15bfcdb6a94d1ccf9c556f2b7efcf
|
Provenance
The following attestation bundles were made for google_photo_meta_fixer-1.0.0.tar.gz:
Publisher:
publish.yml on hiijitesh/google-photo-meta-fixer
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
google_photo_meta_fixer-1.0.0.tar.gz -
Subject digest:
f0d01d96933cbdaf9336bc13d336b541407c5986203b2fe690e6e166e45bce04 - Sigstore transparency entry: 2131935321
- Sigstore integration time:
-
Permalink:
hiijitesh/google-photo-meta-fixer@64786b8c5255835c44d089af9a69fad5e11e19c9 -
Branch / Tag:
refs/tags/v1.0.1 - Owner: https://github.com/hiijitesh
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@64786b8c5255835c44d089af9a69fad5e11e19c9 -
Trigger Event:
release
-
Statement type:
File details
Details for the file google_photo_meta_fixer-1.0.0-py3-none-any.whl.
File metadata
- Download URL: google_photo_meta_fixer-1.0.0-py3-none-any.whl
- Upload date:
- Size: 30.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a65aa5d53b158db61396db762cdb75e5ca4434c8e7613b642eadb3b008f5be5e
|
|
| MD5 |
2c89226056b74e62ffdb3823e03105a3
|
|
| BLAKE2b-256 |
f59c26678234d7d8d398cd7e822868821c9f12821296d7db9f4cbffa1344f5f2
|
Provenance
The following attestation bundles were made for google_photo_meta_fixer-1.0.0-py3-none-any.whl:
Publisher:
publish.yml on hiijitesh/google-photo-meta-fixer
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
google_photo_meta_fixer-1.0.0-py3-none-any.whl -
Subject digest:
a65aa5d53b158db61396db762cdb75e5ca4434c8e7613b642eadb3b008f5be5e - Sigstore transparency entry: 2131935672
- Sigstore integration time:
-
Permalink:
hiijitesh/google-photo-meta-fixer@64786b8c5255835c44d089af9a69fad5e11e19c9 -
Branch / Tag:
refs/tags/v1.0.1 - Owner: https://github.com/hiijitesh
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@64786b8c5255835c44d089af9a69fad5e11e19c9 -
Trigger Event:
release
-
Statement type: