From 234f7aca5c827ced2712d3b74846211baf71a644 Mon Sep 17 00:00:00 2001 From: Nose Date: Sun, 11 Jan 2026 10:59:50 -0800 Subject: [PATCH] jh --- Provider/podcastindex.py | 4 ++-- Provider/soulseek.py | 34 ++++++++++++++++++++------- SYS/config.py | 15 ++++++++---- cmdlet/_shared.py | 48 ++++++++++++------------------------- cmdlet/download_file.py | 4 ++-- cmdlet/get_file.py | 2 +- cmdlet/screen_shot.py | 5 ++-- cmdlet/trim_file.py | 2 +- scripts/bootstrap.py | 51 ++++++++++++++++++++++++++++++++++++++++ 9 files changed, 112 insertions(+), 53 deletions(-) diff --git a/Provider/podcastindex.py b/Provider/podcastindex.py index 6e71c50..3d02cf3 100644 --- a/Provider/podcastindex.py +++ b/Provider/podcastindex.py @@ -292,10 +292,10 @@ class PodcastIndex(Provider): try: from SYS.config import resolve_output_dir - output_dir = resolve_output_dir(self.config or {}) except Exception: - output_dir = Path.home() / "Downloads" + import tempfile + output_dir = Path(tempfile.gettempdir()) try: output_dir = Path(output_dir).expanduser() diff --git a/Provider/soulseek.py b/Provider/soulseek.py index 7b2333b..ec38226 100644 --- a/Provider/soulseek.py +++ b/Provider/soulseek.py @@ -286,7 +286,7 @@ class Soulseek(Provider): # NOTE: These defaults preserve existing behavior. USERNAME = "asjhkjljhkjfdsd334" PASSWORD = "khhhg" - DOWNLOAD_DIR = "./downloads" + DOWNLOAD_DIR = None MAX_WAIT_TRANSFER = 1200 def __init__(self, config: Optional[Dict[str, Any]] = None): @@ -325,13 +325,17 @@ class Soulseek(Provider): ) return None - # Use tempfile directory as default if '.' or generic placeholder was passed - # by a caller that didn't know better. - target_dir = Path(output_dir) - if str(target_dir) == "." or str(target_dir) == "downloads": + # Use tempfile directory as default if generic path elements were passed or None. + if output_dir is None: import tempfile target_dir = Path(tempfile.gettempdir()) / "Medios" / "Soulseek" - target_dir.mkdir(parents=True, exist_ok=True) + else: + target_dir = Path(output_dir) + if str(target_dir) in (".", "downloads", "Downloads"): + import tempfile + target_dir = Path(tempfile.gettempdir()) / "Medios" / "Soulseek" + + target_dir.mkdir(parents=True, exist_ok=True) # This cmdlet stack is synchronous; use asyncio.run for clarity. return asyncio.run( @@ -348,12 +352,22 @@ class Soulseek(Provider): # dedicated loop in this thread. loop = asyncio.new_event_loop() try: + # Re-resolve target_dir inside rescue block just in case + if output_dir is None: + import tempfile + target_dir = Path(tempfile.gettempdir()) / "Medios" / "Soulseek" + else: + target_dir = Path(output_dir) + if str(target_dir) in (".", "downloads", "Downloads"): + import tempfile + target_dir = Path(tempfile.gettempdir()) / "Medios" / "Soulseek" + asyncio.set_event_loop(loop) return loop.run_until_complete( download_soulseek_file( username=username, filename=filename, - output_dir=output_dir, + output_dir=target_dir, timeout=self.MAX_WAIT_TRANSFER, ) ) @@ -642,7 +656,7 @@ class Soulseek(Provider): async def download_soulseek_file( username: str, filename: str, - output_dir: Path = Path("./downloads"), + output_dir: Optional[Path] = None, timeout: int = 1200, *, client_username: Optional[str] = None, @@ -656,6 +670,10 @@ async def download_soulseek_file( from aioslsk.transfer.model import Transfer, TransferDirection from aioslsk.transfer.state import TransferState + if output_dir is None: + import tempfile + output_dir = Path(tempfile.gettempdir()) / "Medios" / "Soulseek" + output_dir = Path(output_dir) output_dir.mkdir(parents=True, exist_ok=True) diff --git a/SYS/config.py b/SYS/config.py index dc930d1..1c408c7 100644 --- a/SYS/config.py +++ b/SYS/config.py @@ -3,6 +3,7 @@ from __future__ import annotations import re +import tempfile from pathlib import Path from typing import Any, Dict, Optional from SYS.logger import log @@ -22,6 +23,12 @@ def global_config() -> List[Dict[str, Any]]: "label": "Debug Output", "default": "false", "choices": ["true", "false"] + }, + { + "key": "auto_update", + "label": "Auto-Update", + "default": "true", + "choices": ["true", "false"] } ] @@ -185,7 +192,7 @@ def parse_conf_text(text: str, *, base: Optional[Dict[str, Any]] = None) -> Dict """Parse a lightweight .conf format into the app's config dict. Supported patterns: - - Top-level key/value: temp="C:\\Users\\Me\\Downloads" + - Top-level key/value: temp="./temp" - Sections: [store=folder] + name/path lines - Sections: [store=hydrusnetwork] + name/access key/url lines - Sections: [provider=OpenLibrary] + email/password lines @@ -447,7 +454,7 @@ def resolve_output_dir(config: Dict[str, Any]) -> Path: Priority: 1. config["temp"] - explicitly set temp/output directory 2. config["outfile"] - fallback to outfile setting - 3. Home/Videos - safe user directory fallback + 3. System Temp - default fallback directory Returns: Path to output directory @@ -471,8 +478,8 @@ def resolve_output_dir(config: Dict[str, Any]) -> Path: except Exception: pass - # Fallback to user's Videos directory - return Path.home() / "Videos" + # Fallback to system temp directory + return Path(tempfile.gettempdir()) def get_local_storage_path(config: Dict[str, Any]) -> Optional[Path]: diff --git a/cmdlet/_shared.py b/cmdlet/_shared.py index 645b2c2..4989d96 100644 --- a/cmdlet/_shared.py +++ b/cmdlet/_shared.py @@ -65,7 +65,7 @@ class CmdletArg: Example: # For STORAGE arg with a handler - storage_path = SharedArgs.STORAGE.resolve('local') # Returns Path.home() / "Videos" + storage_path = SharedArgs.STORAGE.resolve('local') # Returns Path(tempfile.gettempdir()) """ if self.handler is not None and callable(self.handler): return self.handler(value) @@ -354,47 +354,29 @@ class SharedArgs: ) -> Path: """Resolve a storage location name to a filesystem Path. - Maps storage identifiers (hydrus, local, ftp) to their actual - filesystem paths. This is the single source of truth for storage location resolution. - Note: 0x0.st is now accessed via file providers (-provider 0x0), not storage. + Maps storage identifiers to their actual filesystem paths. + This project has been refactored to use system temporary directories + for all staging/downloads by default. Args: - storage_value: One of 'hydrus', 'local', 'ftp', or None - default: Path to return if storage_value is None (defaults to Videos) + storage_value: One of 'hydrus', 'local', 'ftp', or None (currently unified to temp) + default: Path to return if storage_value is None (defaults to temp directory) Returns: - Resolved Path object for the storage location - - Raises: - ValueError: If storage_value is not a recognized storage type + Resolved Path object for the storage location (typically system temp) Example: # In a cmdlet: - storage_path = SharedArgs.resolve_storage(parsed.storage) - - # With defaults: - path = SharedArgs.resolve_storage(None) # Returns home/Videos - path = SharedArgs.resolve_storage('local') # Returns home/Videos - path = SharedArgs.resolve_storage('hydrus') # Returns home/.hydrus/client_files + storage_path = SharedArgs.resolve_storage(parsed.get('storage')) + # Returns Path(tempfile.gettempdir()) """ - storage_map = { - "local": Path.home() / "Videos", - "hydrus": Path.home() / ".hydrus" / "client_files", - "ftp": Path.home() / "FTP", - "matrix": Path.home() / "Matrix", # Placeholder, not used for upload path - } + # We no longer maintain a hardcoded map for 'hydrus' (~/.hydrus) or 'local' (~/Videos). + # Everything defaults to the system temp directory unless a specific default is provided. + # This ensures environment independence. + if default is not None: + return default - if storage_value is None: - return default or (Path.home() / "Videos") - - storage_lower = storage_value.lower() - if storage_lower not in storage_map: - raise ValueError( - f"Unknown storage location '{storage_value}'. " - f"Must be one of: {', '.join(storage_map.keys())}" - ) - - return storage_map[storage_lower] + return Path(tempfile.gettempdir()) @classmethod def get(cls, name: str) -> Optional[CmdletArg]: diff --git a/cmdlet/download_file.py b/cmdlet/download_file.py index c9e200e..4ff3e17 100644 --- a/cmdlet/download_file.py +++ b/cmdlet/download_file.py @@ -3946,8 +3946,8 @@ class Download_File(Cmdlet): final_output_dir = resolve_output_dir(config) except Exception: import tempfile - final_output_dir = Path(tempfile.gettempdir()) / "Medios-Macina" - + final_output_dir = Path(tempfile.gettempdir()) + debug(f"Using default directory: {final_output_dir}") # Ensure directory exists diff --git a/cmdlet/get_file.py b/cmdlet/get_file.py index 158dfb9..16ac77f 100644 --- a/cmdlet/get_file.py +++ b/cmdlet/get_file.py @@ -30,7 +30,7 @@ class Get_File(sh.Cmdlet): super().__init__( name="get-file", summary="Export file to local path", - usage="@1 | get-file -path C:\\Downloads", + usage="@1 | get-file -path ./output", arg=[ sh.SharedArgs.QUERY, sh.SharedArgs.STORE, diff --git a/cmdlet/screen_shot.py b/cmdlet/screen_shot.py index 32f5bf2..681effb 100644 --- a/cmdlet/screen_shot.py +++ b/cmdlet/screen_shot.py @@ -8,6 +8,7 @@ from __future__ import annotations import hashlib import sys +import tempfile import time from datetime import datetime import httpx @@ -948,9 +949,9 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int: except Exception: pass - # Default: User's Videos directory + # Default: system temp directory if screenshot_dir is None: - screenshot_dir = Path.home() / "Videos" + screenshot_dir = Path(tempfile.gettempdir()) debug(f"[screen_shot] Using default directory: {screenshot_dir}") ensure_directory(screenshot_dir) diff --git a/cmdlet/trim_file.py b/cmdlet/trim_file.py index c54da54..c7df51c 100644 --- a/cmdlet/trim_file.py +++ b/cmdlet/trim_file.py @@ -47,7 +47,7 @@ CMDLET = Cmdlet( CmdletArg( "-outdir", description= - "Output directory for the clip (defaults to source folder for local files; otherwise uses config temp/videos).", + "Output directory for the clip (defaults to source folder for local files; otherwise uses system temp).", ), CmdletArg( "-delete", diff --git a/scripts/bootstrap.py b/scripts/bootstrap.py index cf354b4..fb335b5 100644 --- a/scripts/bootstrap.py +++ b/scripts/bootstrap.py @@ -1094,6 +1094,26 @@ def main() -> int: ps1_text = r"""Param([Parameter(ValueFromRemainingArguments=$true)] $args) $scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path $repo = (Resolve-Path (Join-Path $scriptDir "..")).Path + +# Automatically check for updates if this is a git repository +if (Test-Path (Join-Path $repo ".git")) { + try { + if (-not $env:MM_NO_UPDATE) { + $conf = Join-Path $repo "config.conf" + $skip = $false + if (Test-Path $conf) { + if ((Get-Content $conf | Select-String "auto_update\s*=\s*(false|no|off|0)") -ne $null) { + $skip = $true + } + } + if (-not $skip) { + Write-Host "Checking for updates..." -ForegroundColor Gray + git -C "$repo" pull --ff-only --quiet + } + } + } catch {} +} + $venv = Join-Path $repo '.venv' $py = Join-Path $venv 'Scripts\python.exe' if (Test-Path $py) { @@ -1139,6 +1159,22 @@ if (Test-Path (Join-Path $repo 'CLI.py')) { "@echo off\n" "setlocal enabledelayedexpansion\n" f'set "REPO={repo_bat_str}"\n' + "\n" + "# Automatically check for updates if this is a git repository\n" + "if not defined MM_NO_UPDATE (\n" + " if exist \"!REPO!\\.git\" (\n" + " set \"AUTO_UPDATE=true\"\n" + " if exist \"!REPO!\\config.conf\" (\n" + " findstr /i /r \"auto_update.*=.*false auto_update.*=.*no auto_update.*=.*off auto_update.*=.*0\" \"!REPO!\\config.conf\" >nul 2>&1\n" + " if !errorlevel! == 0 set \"AUTO_UPDATE=false\"\n" + " )\n" + " if \"!AUTO_UPDATE!\" == \"true\" (\n" + " echo Checking for updates...\n" + " git -C \"!REPO!\" pull --ff-only --quiet\n" + " )\n" + " )\n" + ")\n" + "\n" "set \"VENV=!REPO!\\.venv\"\n" "set \"PY=!VENV!\\Scripts\\python.exe\"\n" "set \"ENTRY=!REPO!\\scripts\\cli_entry.py\"\n" @@ -1299,6 +1335,21 @@ if (Test-Path (Join-Path $repo 'CLI.py')) { " done\n" ' echo "MM_DEBUG: end diagnostics" >&2\n' "fi\n" + "\n" + "# Automatically check for updates if this is a git repository\n" + 'if [ -z "${MM_NO_UPDATE:-}" ] && [ -d "$REPO/.git" ] && command -v git >/dev/null 2>&1; then\n' + ' AUTO_UPDATE="true"\n' + ' if [ -f "$REPO/config.conf" ]; then\n' + ' if grep -qiE \'auto_update\s*=\s*(false|no|off|0)\' "$REPO/config.conf"; then\n' + ' AUTO_UPDATE="false"\n' + ' fi\n' + ' fi\n' + ' if [ "$AUTO_UPDATE" = "true" ]; then\n' + ' echo "Checking for updates..."\n' + ' git -C "$REPO" pull --ff-only --quiet || true\n' + ' fi\n' + "fi\n" + "\n" "# Use -m scripts.cli_entry directly instead of pip-generated wrapper to avoid entry point issues\n" "# Prefer venv's python3, then venv's python\n" 'if [ -x "$VENV/bin/python3" ]; then\n'