From 2516e3eeef62cc5242135d3f41b858ea5eda86ce Mon Sep 17 00:00:00 2001 From: jenstandstad Date: Mon, 4 May 2026 20:05:12 +0200 Subject: [PATCH] Add gnommo load command to copy projects from removable media Adds the inverse of the archive command: `gnommo load -p ` inspects the configured external drive and rsyncs the project folder onto the local drive. Supports --dry-run. Also expands .gitignore to cover additional media file types and project directories. Co-Authored-By: Claude Sonnet 4.6 --- .gitignore | 23 ++++++++++++++++-- gnommo/cli.py | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 817c628..5ab29ac 100644 --- a/.gitignore +++ b/.gitignore @@ -7,16 +7,35 @@ __pycache__/ venv/ .venv/ *.egg-info/ - -Video1/* +*.pdf +*.png +*.key +*.bak +shared_assets/* +Video*/* +Illustrations # OS .DS_Store Thumbs.db +*/intermediate/* # Output **/out/ *.mp4 +*.mov +*.mp3 +*.aifc +*.wav + # Temp *.tmp .cache/ + +# Secrets +.env +.env.* + +# Sync state (local only, per-environment) +.gnommo_sync.json +.gnommo_sync.prod.json diff --git a/gnommo/cli.py b/gnommo/cli.py index 0eb4d08..41bd8a1 100644 --- a/gnommo/cli.py +++ b/gnommo/cli.py @@ -95,6 +95,7 @@ Examples: "import", "description", "archive", + "load", "extract-audio", "master", "push", @@ -269,6 +270,8 @@ Examples: return cmd_description(project_path, args.verbose) elif action == "archive": return cmd_archive(project_path, args.verbose, args.dry_run) + elif action == "load": + return cmd_load(project_path, args.verbose, args.dry_run) elif action == "extract-audio": return cmd_extract_audio( project_path, args.verbose, args.segment, args.channel, args.combined @@ -3020,6 +3023,69 @@ def cmd_archive(project_path: Path, verbose: bool, dry_run: bool) -> int: return 0 +def cmd_load(project_path: Path, verbose: bool, dry_run: bool) -> int: + """Load project files from external cache storage onto the local drive.""" + from .cache import load_cache_config + + print(f"Loading: {project_path.name}") + + # Check cache is configured + cache_base = load_cache_config() + if cache_base is None: + print("Error: Cache not configured. Create ~/.gnommo.conf with:") + print(" [cache]") + print(" path = /Volumes/YourDisk/gnommo") + return 1 + + if not cache_base.exists(): + print(f"Error: Cache path not accessible: {cache_base}") + print("Make sure the external drive is connected.") + return 1 + + # Build source path on the external drive + src_path = cache_base / project_path.name + if not src_path.exists(): + print(f"Error: Project not found on external drive: {src_path}") + return 1 + + print(f" Source: {src_path}") + print(f" Destination: {project_path}") + + # Create destination if needed + if not dry_run: + project_path.mkdir(parents=True, exist_ok=True) + + rsync_cmd = [ + "rsync", + "-av", + "--progress", + "--exclude=*.py", + "--exclude=__pycache__", + "--exclude=.git", + "--exclude=.DS_Store", + f"{src_path}/", + f"{project_path}/", + ] + + if dry_run: + rsync_cmd.insert(1, "--dry-run") + print("\n [DRY RUN] Would execute:") + print(f" {' '.join(rsync_cmd)}") + else: + print("\n Copying files...") + + if verbose: + print(f" Command: {' '.join(rsync_cmd)}") + + result = subprocess.run(rsync_cmd) + if result.returncode != 0: + print(f"Error: rsync failed with code {result.returncode}") + return 1 + + print("\nDone.") + return 0 + + # ============================================================================= # Extract Audio Command # =============================================================================