#!/usr/bin/env python3 import os import subprocess import shutil import time from pathlib import Path from urllib.parse import quote from dataclasses import dataclass from typing import Optional @dataclass class SMBConfig: """Configuration for an SMB mount.""" server: str share: str username: str password: str mount_name: str # Name for the mount folder (e.g., "GDriveNetwork") local_dest: Optional[str] = None # Optional local destination for syncing @property def mount_point(self) -> str: """Returns the full mount point path.""" return str(Path.home() / "NetworkMounts" / self.mount_name) def is_mounted(mount_point: str) -> bool: """Check if a path is currently mounted.""" return os.path.ismount(mount_point) def mount_share(config: SMBConfig) -> bool: """ Mount an SMB share using the provided configuration. Returns True if successful, False otherwise. """ mount_point = config.mount_point # Create mount point directory if it doesn't exist Path(mount_point).mkdir(parents=True, exist_ok=True) # Build the SMB URL # Format: //username:password@server/share if config.username and config.password: escaped_pass = quote(config.password, safe='') smb_url = f"//{config.username}:{escaped_pass}@{config.server}/{config.share}" else: smb_url = f"//{config.server}/{config.share}" # Mount command for macOS cmd = ["mount_smbfs", smb_url, mount_point] try: print(f"Mounting {config.share} from {config.server}...") subprocess.run(cmd, check=True, capture_output=True, text=True) print(f"Successfully mounted to {mount_point}") return True except subprocess.CalledProcessError as e: print(f"Failed to mount: {e.stderr}") return False def unmount_share(config: SMBConfig) -> bool: """Unmount an SMB share.""" mount_point = config.mount_point if not is_mounted(mount_point): print(f"{mount_point} is not mounted.") return True try: subprocess.run(["umount", mount_point], check=True, capture_output=True, text=True) print(f"Successfully unmounted {mount_point}") return True except subprocess.CalledProcessError as e: print(f"Failed to unmount: {e.stderr}") return False def ensure_mounted(config: SMBConfig) -> bool: """ Ensure a share is mounted. Mount it if not already mounted. Returns True if mounted (or already was), False if mount failed. """ mount_point = config.mount_point if is_mounted(mount_point): print(f"{config.share} is already mounted at {mount_point}") return True print(f"{config.share} not mounted. Attempting to mount...") return mount_share(config) # ----------------------------- # PREDEFINED CONFIGURATIONS # ----------------------------- # Add your different mount configurations here CONFIGS = { "gdrive": SMBConfig( server="192.168.1.68", share="GDrive", username="macos", password="dromischSYNC123987me", mount_name="GDrive", local_dest="/Users/dromisch/NetworkMounts/GDrive" ), # Example: Add more configurations as needed # "photos": SMBConfig( # server="192.168.1.68", # share="Photos", # username="photouser", # password="photopass", # mount_name="PhotosNetwork", # local_dest="/Volumes/ExtraDrive/Photos" # ), # "backup": SMBConfig( # server="192.168.1.100", # share="Backups", # username="backupuser", # password="backuppass", # mount_name="BackupNetwork" # ), } def list_configs(): """Print available configurations.""" print("Available mount configurations:") for name, config in CONFIGS.items(): status = "mounted" if is_mounted(config.mount_point) else "not mounted" print(f" {name}: //{config.server}/{config.share} ({status})") def main(): import argparse parser = argparse.ArgumentParser(description="Mount SMB network shares") parser.add_argument("config", nargs="?", help="Configuration name to mount") parser.add_argument("-l", "--list", action="store_true", help="List available configurations") parser.add_argument("-u", "--unmount", action="store_true", help="Unmount instead of mount") parser.add_argument("-a", "--all", action="store_true", help="Mount/unmount all configurations") args = parser.parse_args() if args.list: list_configs() return if args.all: for name, config in CONFIGS.items(): print(f"\n--- {name} ---") if args.unmount: unmount_share(config) else: ensure_mounted(config) return if not args.config: parser.print_help() print("\n") list_configs() return if args.config not in CONFIGS: print(f"Unknown configuration: {args.config}") list_configs() return config = CONFIGS[args.config] if args.unmount: unmount_share(config) else: ensure_mounted(config) if __name__ == "__main__": main()