From b19e59803443a3ba4abaa6386dd8f90575e33ea7 Mon Sep 17 00:00:00 2001 From: arsam Date: Fri, 18 Sep 2026 23:15:48 +0330 Subject: [PATCH] Import source: sort_root_full Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality. --- sort_root_full.py | 89 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 sort_root_full.py diff --git a/sort_root_full.py b/sort_root_full.py new file mode 100644 index 0000000..8ee3478 --- /dev/null +++ b/sort_root_full.py @@ -0,0 +1,89 @@ +#!/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 ===")