Ad – 728Γ—90
πŸ“ Projects

Python File Organizer – Automation Project

Sort files into categorized folders automatically. Features dry-run preview, conflict handling, and a move log for undo.

⏱️ 20 min read🎯 ProjectsπŸ“… Updated 2026

Complete Implementation

Python
#!/usr/bin/env python3
import shutil, json, argparse
from pathlib import Path
from datetime import datetime

CATEGORIES = {
    "Images":    [".jpg", ".jpeg", ".png", ".gif", ".webp"],
    "Videos":    [".mp4", ".mkv", ".avi", ".mov"],
    "Audio":     [".mp3", ".wav", ".flac", ".aac"],
    "Documents": [".pdf", ".doc", ".docx", ".txt", ".md"],
    "Code":      [".py", ".js", ".ts", ".html", ".css"],
    "Archives":  [".zip", ".tar", ".gz", ".rar"],
}

def get_category(ext):
    for cat, exts in CATEGORIES.items():
        if ext.lower() in exts:
            return cat
    return "Other"

def organize(source, dest, dry_run=False):
    moves = []
    for file in source.iterdir():
        if not file.is_file() or file.name.startswith("."):
            continue
        cat = get_category(file.suffix)
        target_dir = dest / cat
        target = target_dir / file.name
        if target.exists():
            ts = datetime.now().strftime("%Y%m%d_%H%M%S")
            target = target_dir / f"{file.stem}_{ts}{file.suffix}"
        moves.append({"src": str(file), "dst": str(target), "category": cat})
        if not dry_run:
            target_dir.mkdir(parents=True, exist_ok=True)
            shutil.move(str(file), target)
    return moves

parser = argparse.ArgumentParser()
parser.add_argument("source")
parser.add_argument("-d", "--dest")
parser.add_argument("--dry-run", action="store_true")
args = parser.parse_args()

src = Path(args.source).resolve()
dst = Path(args.dest).resolve() if args.dest else src
moves = organize(src, dst, args.dry_run)

by_cat = {}
for m in moves:
    by_cat.setdefault(m["category"], []).append(m)

for cat, items in sorted(by_cat.items()):
    action = "Would move" if args.dry_run else "Moved"
    print(f"\n{cat} ({len(items)} files)")
    for item in items:
        print(f"  {action}: {Path(item['src']).name}")

if not args.dry_run and moves:
    log = dst / ".organizer_log.json"
    log.write_text(json.dumps(moves, indent=2))
    print(f"\n{len(moves)} files moved. Log: {log}")
Bash
python organizer.py ~/Downloads --dry-run
python organizer.py ~/Downloads -d ~/Organized
Tip: The --dry-run flag is essential for any file manipulation tool. Always let users preview before committing.