DEV Community

Mate Technologies
Mate Technologies

Posted on

🧹 Build a Desktop Image Metadata Cleaner in Python (MetaClean v2)

Ever shared a photo online without realizing it contains hidden data like GPS location, camera model, or timestamps?

In this tutorial, we’ll build MetaClean β€” a Python desktop app that removes EXIF metadata from images while keeping visual quality intact.

πŸ‘‰ Try MetaClean here:
https://gum.new/gum/cmki9hb0s000204l402y17n9c

By the end, you’ll have:

βœ… A drag-and-drop desktop app
βœ… Batch image processing
βœ… Metadata preview
βœ… Safe overwrite with backups
βœ… Exportable cleanup reports

Let’s get started.

πŸ›  Requirements

Install Python 3.9+ and then:

pip install pillow ttkbootstrap tkinterdnd2

What these do:

pillow β†’ image handling

tkinter β†’ GUI (built into Python)

ttkbootstrap β†’ modern UI styling

tkinterdnd2 β†’ drag & drop support

πŸ“ Project Structure

Create a new folder:

metaclean/
β”œβ”€β”€ app.py
└── logo.ico (optional)

All code goes inside app.py.

Step 1 β€” Imports & Constants

Start with basic imports and app metadata.

import os, sys, threading, json, platform, shutil
import tkinter as tk
from tkinter import filedialog, messagebox
from datetime import datetime
from PIL import Image, ExifTags, ImageOps
import ttkbootstrap as tb
Enter fullscreen mode Exit fullscreen mode

Optional drag & drop:

try:
    from tkinterdnd2 import TkinterDnD, DND_FILES
    DND_ENABLED = True
except ImportError:
    DND_ENABLED = False
Enter fullscreen mode Exit fullscreen mode

Why?

We gracefully fall back if drag & drop isn’t installed.

Step 2 β€” Create the Main App Class

Using a class keeps everything organized.

class MetaCleanApp:
    APP_NAME = "MetaClean"
    APP_VERSION = "2.0.0"
    SUPPORTED_EXT = (".png", ".jpg", ".jpeg", ".webp", ".bmp", ".tiff")
Enter fullscreen mode Exit fullscreen mode

Step 3 β€” Initialize the Window

def __init__(self):
    if DND_ENABLED:
        self.root = TkinterDnD.Tk()
    else:
        self.root = tk.Tk()

    self.style = tb.Style(theme="darkly")
    self.root.title("MetaClean v2.0")
    self.root.geometry("1000x620")
Enter fullscreen mode Exit fullscreen mode

Here we:

Create the root window

Apply dark theme

Set size and title

Step 4 β€” Logging & App Directories

We store logs and backups safely in the user’s home folder.

self.home_dir = os.path.expanduser("~")
self.log_dir = os.path.join(self.home_dir, ".metaclean")
os.makedirs(self.log_dir, exist_ok=True)
Enter fullscreen mode Exit fullscreen mode

Logging helper:

def log_error(self, msg):
    with open(self.log_file, "a") as f:
        f.write(f"{datetime.now()} {msg}\n")
Enter fullscreen mode Exit fullscreen mode

Step 5 β€” Reading Image Metadata

def read_metadata(self, path):
    img = Image.open(path)
    exif = img.getexif()

    if not exif:
        return "No metadata found."

    lines = []
    for tag, val in exif.items():
        name = ExifTags.TAGS.get(tag, tag)
        lines.append(f"{name}: {val}")

    return "\n".join(lines)
Enter fullscreen mode Exit fullscreen mode

Step 6 β€” Removing Metadata Safely

def strip_metadata(self, src, dst):
    img = Image.open(src)
    img = ImageOps.exif_transpose(img)

    data = list(img.getdata())
    clean = Image.new(img.mode, img.size)
    clean.putdata(data)

    clean.save(dst, icc_profile=img.info.get("icc_profile"))
Enter fullscreen mode Exit fullscreen mode

This guarantees all metadata is removed.

Step 7 β€” Image Selection

def add_images(self):
    files = filedialog.askopenfilenames()
    for f in files:
        self.image_listbox.insert(tk.END, f)
Enter fullscreen mode Exit fullscreen mode

Step 8 β€” Batch Cleanup Logic

def run_cleanup(self):
    files = self.image_listbox.get(0, tk.END)
    for src in files:
        dst = src + "_clean.jpg"
        self.strip_metadata(src, dst)
Enter fullscreen mode Exit fullscreen mode

Step 9 β€” Export Reports

JSON:

json.dump(self.cleanup_results, f, indent=2)
Enter fullscreen mode Exit fullscreen mode

TXT:

f.write(f"{k}: {v}\n")
Enter fullscreen mode Exit fullscreen mode

Step 10 β€” Run the App

if __name__ == "__main__":
    app = MetaCleanApp()
    app.run()
Enter fullscreen mode Exit fullscreen mode

βœ… Final Result

You now have a full desktop metadata cleaner with batch support, previews, backups, and exportable reports.

If you’d like to try the polished build of MetaClean v2:

πŸ‘‰ https://gum.new/gum/cmki9hb0s000204l402y17n9c

Top comments (0)