#!/usr/bin/env python3 """Full sort of gdrive root — classifies every loose file and moves it to the right folder.""" import json, subprocess, sys REMOTE = "gdrive:" BASE = ["rclone"] # Folders that should never be touched (already organised or system folders) KEEP_DIRS = {"Google AI Studio", "Photos", "Untitled folder", "Videos", "Music", "Software", "Archives", "Bin", "Docs", "Config", "Models", "ZIM", "SPH"} def run(*args): cmd = list(args) r = subprocess.run(cmd, capture_output=True, text=True) if r.returncode != 0: print(f" ERROR: {r.stderr.strip()[:120]}") return r.returncode == 0 def classify(name: str, mime: str) -> str: ext = name.rsplit(".", 1)[-1].lower() if "." in name else "" if mime.startswith("video/") or ext in ("mp4","mkv","avi","mov","webm","flv"): return "Videos" if mime.startswith("audio/") or ext in ("mp3","flac","wav","ogg","m4a","aac"): return "Music" if mime.startswith("image/") or ext in ("jpg","jpeg","png","gif","webp","bmp","heic","heif"): return "Photos" if ext in ("exe","msi","deb","rpm","dmg","appimage"): return "Software" if ext in ("zip","rar","7z","tar","gz","bz2","xz","zst"): return "Archives" if ext == "gguf": return "Models" if ext in ("safetensors","pt","ckpt","pth"): return "Models" if ext == "zim": return "ZIM" if ext in ("conf","ovpn","cfg","ini","toml"): return "Config" if ext in ("pdf","doc","docx","txt","md","csv","xlsx","pptx","json"): return "Docs" if ext == "bin": return "Bin" # Google Docs / Sheets / Slides (no extension, mime-based) gdoc_mimes = ("application/vnd.google-apps.document", "application/vnd.google-apps.spreadsheet", "application/vnd.google-apps.presentation", "application/vnd.google-apps.form", "application/vnd.openxmlformats") if any(mime.startswith(m) for m in gdoc_mimes): return "Docs" return "Other" print("Listing root (depth 0)...") r = subprocess.run( BASE + ["lsjson", REMOTE, "--max-depth", "0"], capture_output=True, text=True ) items = json.loads(r.stdout) loose_files = [f for f in items if not f["IsDir"]] dirs = [f["Name"] for f in items if f["IsDir"]] print(f"Found {len(loose_files)} loose files, {len(dirs)} folders\n") groups: dict[str, list] = {} for f in loose_files: folder = classify(f["Name"], f["MimeType"]) groups.setdefault(folder, []).append(f["Name"]) print("=== Move plan ===") for folder, names in sorted(groups.items()): print(f" {folder}/ ← {len(names)} files") print("\n=== Executing ===") created: set = set() ok = err = 0 for folder, names in sorted(groups.items()): if folder not in created: run("rclone", "mkdir", f"{REMOTE}{folder}") created.add(folder) for name in names: print(f" → {folder}/{name[:65]}") if run("rclone", "moveto", f"{REMOTE}{name}", f"{REMOTE}{folder}/{name}"): ok += 1 else: err += 1 print(f"\n=== Done: {ok} moved, {err} errors ===")