This commit is contained in:
2026-01-11 10:59:50 -08:00
parent 5f8f49c530
commit 234f7aca5c
9 changed files with 112 additions and 53 deletions

View File

@@ -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()

View File

@@ -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)

View File

@@ -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]:

View File

@@ -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]:

View File

@@ -3946,7 +3946,7 @@ 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}")

View File

@@ -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,

View File

@@ -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)

View File

@@ -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",

View File

@@ -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'