This commit is contained in:
2026-01-09 13:41:18 -08:00
parent 1deddfda5c
commit 94aca4d3d4
2 changed files with 124 additions and 32 deletions

View File

@@ -136,11 +136,16 @@ def playwright_package_installed() -> bool:
def _build_playwright_install_cmd(browsers: str | None) -> list[str]:
"""Return the command to install Playwright browsers.
- If browsers is None or empty: default to install Chromium only.
- If browsers is None or empty: default to install Chromium only (headless).
- If browsers contains 'all': install all engines by running 'playwright install' with no extra args.
- Otherwise, validate entries and return a command that installs the named engines.
The --with-deps flag is NOT used because:
1. The project already includes ffmpeg (in MPV/ffmpeg)
2. Most system dependencies should already be available
"""
# Use --skip-browsers to just install deps without browsers, then install specific browsers
base = [sys.executable, "-m", "playwright", "install"]
if not browsers:
return base + ["chromium"]
@@ -293,7 +298,10 @@ def main() -> int:
)
args = parser.parse_args()
repo_root = Path(__file__).resolve().parent.parent
# Ensure repo_root is always the project root, not the current working directory
# This prevents issues when bootstrap.py is run from different directories
script_dir = Path(__file__).resolve().parent
repo_root = script_dir.parent
# Helpers for interactive menu and uninstall detection
def _venv_python_path(p: Path) -> Path | None:
@@ -795,15 +803,19 @@ def main() -> int:
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$repo = (Resolve-Path (Join-Path $scriptDir "..")).Path
$venv = Join-Path $repo '.venv'
$py = Join-Path $venv 'Scripts\python.exe'
if (Test-Path $py) {
& $py -m scripts.cli_entry @args; exit $LASTEXITCODE
}
# Ensure venv Scripts dir is on PATH for provider discovery
$venvScripts = Join-Path $venv 'Scripts'
if (Test-Path $venvScripts) { $env:PATH = $venvScripts + ';' + $env:PATH }
$py = Join-Path $venv 'Scripts\python.exe'
$cli = Join-Path $repo 'CLI.py'
if (Test-Path $py) { & $py -m scripts.cli_entry @args; exit $LASTEXITCODE }
if (Test-Path $cli) { & python $cli @args; exit $LASTEXITCODE }
# fallback
python -m scripts.cli_entry @args
# Fallback to system python if venv doesn't exist
if (Test-Path (Join-Path $repo 'CLI.py')) {
python -m scripts.cli_entry @args
} else {
python -m scripts.cli_entry @args
}
"""
try:
ps1.write_text(ps1_text, encoding="utf-8")
@@ -828,16 +840,16 @@ python -m scripts.cli_entry @args
"Param([Parameter(ValueFromRemainingArguments=$true)] $args)\n"
f'$repo = "{repo}"\n'
"$venv = Join-Path $repo '.venv'\n"
"$exe = Join-Path $venv 'Scripts\\mm.exe'\n"
"if (Test-Path $exe) { & $exe @args; exit $LASTEXITCODE }\n"
"$py = Join-Path $venv 'Scripts\\python.exe'\n"
"if (Test-Path $py) {\n"
" if ($env:MM_DEBUG) {\n"
' Write-Host "MM_DEBUG: diagnostics" -ForegroundColor Yellow\n'
" & $py -c \"import sys,importlib,importlib.util,traceback; print('sys.executable:', sys.executable); print('sys.path (first 8):', sys.path[:8]);\"\n"
' Write-Host "MM_DEBUG: using venv python at $py" -ForegroundColor Yellow\n'
" & $py -c \"import sys; print('sys.executable:', sys.executable)\"\n"
" }\n"
" & $py -m scripts.cli_entry @args; exit $LASTEXITCODE\n"
"}\n"
"# Fallback to system python if venv doesn't exist\n"
"if ($env:MM_DEBUG) { Write-Host 'MM_DEBUG: venv python not found, trying system python' -ForegroundColor Yellow }\n"
"python -m scripts.cli_entry @args\n"
)
if mm_ps1.exists():
@@ -845,29 +857,71 @@ python -m scripts.cli_entry @args
mm_ps1.replace(bak)
mm_ps1.write_text(ps1_text, encoding="utf-8")
# Attempt to add user_bin to the user's PATH if it's not present.
# Write mm.bat (CMD shim for better compatibility)
mm_bat = user_bin / "mm.bat"
bat_text = (
"@echo off\n"
"setlocal enabledelayedexpansion\n"
f'set "REPO={repo}"\n'
"set \"VENV=!REPO!\\.venv\"\n"
"set \"PY=!VENV!\\Scripts\\python.exe\"\n"
"if exist \"!PY!\" (\n"
" if defined MM_DEBUG (\n"
" echo MM_DEBUG: using venv python at !PY!\n"
" \"!PY!\" -c \"import sys; print('sys.executable:', sys.executable)\"\n"
" )\n"
" \"!PY!\" -m scripts.cli_entry %*\n"
" exit /b !ERRORLEVEL!\n"
")\n"
"echo MM: venv not found at !VENV!, trying system python\n"
"if defined MM_DEBUG echo MM_DEBUG: venv python not found, trying system python\n"
"python -m scripts.cli_entry %*\n"
"exit /b !ERRORLEVEL!\n"
)
if mm_bat.exists():
bak = mm_bat.with_suffix(f".bak{int(time.time())}")
mm_bat.replace(bak)
mm_bat.write_text(bat_text, encoding="utf-8")
# Add user_bin to PATH for current and future sessions
str_bin = str(user_bin)
cur_path = os.environ.get("PATH", "")
# Update current session PATH if not already present
if str_bin not in cur_path:
os.environ["PATH"] = str_bin + ";" + cur_path
if not args.quiet:
print(f"Added {user_bin} to current session PATH")
# Persist to user's Windows registry PATH for future sessions
try:
cur = os.environ.get("PATH", "")
str_bin = str(user_bin)
if str_bin not in cur:
ps_cmd = (
"$bin = '{bin}';"
"$cur = [Environment]::GetEnvironmentVariable('PATH','User');"
"if ($cur -notlike \"*$bin*\") {[Environment]::SetEnvironmentVariable('PATH', ($bin + ';' + ($cur -ne $null ? $cur : '')), 'User')}"
).format(bin=str_bin.replace("\\",
"\\\\"))
subprocess.run(
["powershell",
"-NoProfile",
"-Command",
ps_cmd],
check=False
)
except Exception:
pass
ps_cmd = (
"$bin = '{bin}';"
"$cur = [Environment]::GetEnvironmentVariable('PATH','User');"
"if ($cur -notlike \"*$bin*\") {{"
"[Environment]::SetEnvironmentVariable('PATH', ($bin + ';' + ($cur -ne $null ? $cur : '')), 'User');"
"Write-Host 'Added {bin} to User PATH in registry' -ForegroundColor Green"
"}}"
).format(bin=str_bin.replace("\\", "\\\\"))
subprocess.run(
["powershell",
"-NoProfile",
"-Command",
ps_cmd],
check=False,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL
)
except Exception as e:
if not args.quiet:
print(f"Note: Could not persist PATH to registry (this is non-critical): {e}", file=sys.stderr)
if not args.quiet:
print(f"Installed global launchers to: {user_bin}")
print(f"\nInstalled global launchers to: {user_bin}")
print(f"✓ mm.ps1 (PowerShell)")
print(f"✓ mm.bat (Command Prompt)")
print(f"\nYou can now run 'mm' from any terminal window.")
print(f"To use in the current terminal, reload your profile or run: $env:PATH = '{str_bin};' + $env:PATH")
else:
# POSIX

View File

@@ -1,7 +1,9 @@
from __future__ import annotations
import contextlib
import os
import re
import shutil
import tempfile
import traceback
from dataclasses import dataclass
@@ -74,6 +76,7 @@ class PlaywrightDefaults:
viewport_height: int = 1080
navigation_timeout_ms: int = 90_000
ignore_https_errors: bool = True
ffmpeg_path: Optional[str] = None # Path to ffmpeg executable; auto-detected if None
@dataclass(slots=True)
@@ -101,6 +104,7 @@ class PlaywrightTool:
- sync_playwright start/stop
- browser launch/context creation
- user-agent/viewport defaults
- ffmpeg path resolution (for video recording)
Config overrides (top-level keys):
- playwright.browser="chromium"
@@ -110,6 +114,13 @@ class PlaywrightTool:
- playwright.viewport_height=1200
- playwright.navigation_timeout_ms=90000
- playwright.ignore_https_errors=true
- playwright.ffmpeg_path="/path/to/ffmpeg" (auto-detected if not set)
FFmpeg resolution (in order):
1. Config key: playwright.ffmpeg_path
2. Environment variable: PLAYWRIGHT_FFMPEG_PATH
3. Project bundled: MPV/ffmpeg/bin/ffmpeg[.exe]
4. System PATH: which ffmpeg
"""
def __init__(self, config: Optional[Dict[str, Any]] = None) -> None:
@@ -162,6 +173,32 @@ class PlaywrightTool:
ignore_https = bool(_get("ignore_https_errors", defaults.ignore_https_errors))
# Try to find ffmpeg: config override, environment variable, bundled, then system
ffmpeg_path: Optional[str] = None
config_ffmpeg = _get("ffmpeg_path", None)
if config_ffmpeg:
ffmpeg_path = str(config_ffmpeg).strip()
else:
# Check environment variable (supports project ffmpeg)
env_ffmpeg = os.environ.get("PLAYWRIGHT_FFMPEG_PATH")
if env_ffmpeg:
ffmpeg_path = env_ffmpeg
else:
# Try to find bundled ffmpeg in the project (if available)
try:
repo_root = Path(__file__).resolve().parent.parent
bundled_ffmpeg = repo_root / "MPV" / "ffmpeg" / "bin"
if bundled_ffmpeg.exists():
ffmpeg_exe = bundled_ffmpeg / ("ffmpeg.exe" if os.name == "nt" else "ffmpeg")
if ffmpeg_exe.exists():
ffmpeg_path = str(ffmpeg_exe)
except Exception:
pass
# Try system ffmpeg if bundled not found
if not ffmpeg_path:
ffmpeg_path = shutil.which("ffmpeg")
return PlaywrightDefaults(
browser=browser,
headless=headless,
@@ -170,6 +207,7 @@ class PlaywrightTool:
viewport_height=vh,
navigation_timeout_ms=nav_timeout,
ignore_https_errors=ignore_https,
ffmpeg_path=ffmpeg_path,
)
def require(self) -> None: