Files
TG-GDrive/sort_root_full.py
arsam e8a94ff9be Import source: sort_root_full
Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
2026-09-18 23:15:17 +03:30

90 lines
3.1 KiB
Python

#!/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 ===")