Top-level imports; catch only expected exceptions
velopack/tracerite/fastapi-vue are always available in the GUI build, so drop the in-function import guards. The update checker now catches only RuntimeError (not a Velopack install: dev/portable) and OSError (network). Build script and PyInstaller spec use platformdirs for the persistent tool cache.
This commit is contained in:
+18
-36
@@ -26,9 +26,13 @@ from pathlib import Path
|
||||
|
||||
import msgspec.structs
|
||||
import uvicorn
|
||||
import velopack
|
||||
import webview
|
||||
from fastapi_vue.logging import patch_log_config
|
||||
from fastapi_vue.startupbox import print_box
|
||||
from tracerite.html import html_traceback
|
||||
|
||||
from mediahive.config import load_config, save_config
|
||||
from mediahive.config import load_config, log_dir, save_config
|
||||
from mediahive.volume_control import get_volume, set_volume, volume_max
|
||||
|
||||
logger = logging.getLogger("mediahive.winmain")
|
||||
@@ -872,18 +876,16 @@ def _wait_for_previous_instance(log_path: Path, timeout: float = 15.0):
|
||||
|
||||
|
||||
def _setup_logging() -> Path:
|
||||
"""Redirect stdout/stderr and configure logging to a file in %APPDATA%/mediahive/.
|
||||
"""Redirect stdout/stderr and configure logging to a file in the platform log dir.
|
||||
|
||||
In a PyInstaller --windowed build there is no console, so any print() or
|
||||
unhandled exception traceback would be lost. This ensures everything ends
|
||||
up in a persistent log file the user can send for bug reports.
|
||||
Returns the path to the log file.
|
||||
"""
|
||||
from mediahive.config import config_dir
|
||||
|
||||
log_dir = config_dir()
|
||||
log_dir.mkdir(parents=True, exist_ok=True)
|
||||
log_path = log_dir / "mediahive.log"
|
||||
log_directory = log_dir()
|
||||
log_directory.mkdir(parents=True, exist_ok=True)
|
||||
log_path = log_directory / "mediahive.log"
|
||||
|
||||
try:
|
||||
log_file = _rotate_and_open_log(log_path)
|
||||
@@ -900,7 +902,7 @@ def _setup_logging() -> Path:
|
||||
log_file = _wait_for_previous_instance(log_path)
|
||||
if log_file is None:
|
||||
# Never fail startup over logging: fall back to a per-process file.
|
||||
log_path = log_dir / f"mediahive-{os.getpid()}.log"
|
||||
log_path = log_directory / f"mediahive-{os.getpid()}.log"
|
||||
with contextlib.suppress(OSError):
|
||||
fd = os.open(log_path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC)
|
||||
log_file = os.fdopen(fd, "w", encoding="utf-8", buffering=1)
|
||||
@@ -959,22 +961,14 @@ def _show_fatal_error(exc: BaseException) -> None:
|
||||
Frozen --windowed builds otherwise surface crashes only as PyInstaller's
|
||||
plain-text error dialog (or nothing at all).
|
||||
"""
|
||||
try:
|
||||
from tracerite.html import html_traceback
|
||||
|
||||
fragment = str(html_traceback(exc))
|
||||
except Exception: # noqa: BLE001 - error reporting must never raise
|
||||
return
|
||||
fragment = str(html_traceback(exc))
|
||||
page = (
|
||||
"<!DOCTYPE html><html><head><meta charset='utf-8'>"
|
||||
"<title>MediaHive — Error</title></head>"
|
||||
f"<body style='margin:1.5rem'>{fragment}</body></html>"
|
||||
)
|
||||
try:
|
||||
webview.create_window("MediaHive — Error", html=page, width=1100, height=750)
|
||||
webview.start(icon=_icon_path(), **_webview_start_kwargs())
|
||||
except Exception:
|
||||
logger.exception("Could not display the error window")
|
||||
webview.create_window("MediaHive — Error", html=page, width=1100, height=750)
|
||||
webview.start(icon=_icon_path(), **_webview_start_kwargs())
|
||||
|
||||
|
||||
def _velopack_startup() -> None:
|
||||
@@ -986,14 +980,7 @@ def _velopack_startup() -> None:
|
||||
Applies downloaded-but-pending updates. No-op in development and
|
||||
portable-ZIP runs.
|
||||
"""
|
||||
try:
|
||||
import velopack
|
||||
except ImportError:
|
||||
return
|
||||
try:
|
||||
velopack.App().run()
|
||||
except Exception:
|
||||
logger.exception("Velopack startup hook failed")
|
||||
velopack.App().run()
|
||||
|
||||
|
||||
def _check_for_updates() -> None:
|
||||
@@ -1001,11 +988,10 @@ def _check_for_updates() -> None:
|
||||
|
||||
Downloaded updates are applied automatically by Velopack on the next app
|
||||
start (via _velopack_startup), so the running session is never
|
||||
interrupted.
|
||||
interrupted. Not a Velopack install (dev/portable) and network failures
|
||||
are expected and skipped quietly.
|
||||
"""
|
||||
try:
|
||||
import velopack
|
||||
|
||||
mgr = velopack.UpdateManager(velopack.GiteaSource(VELOPACK_REPO_URL))
|
||||
info = mgr.check_for_updates()
|
||||
if info is None:
|
||||
@@ -1015,7 +1001,7 @@ def _check_for_updates() -> None:
|
||||
logger.info("Velopack: downloading update %s", version)
|
||||
mgr.download_updates(info)
|
||||
logger.info("Velopack: update %s staged, applies on next launch", version)
|
||||
except Exception as exc: # not a Velopack install (dev/portable), network, ...
|
||||
except (RuntimeError, OSError) as exc:
|
||||
logger.info("Velopack update check skipped: %s", exc)
|
||||
|
||||
|
||||
@@ -1184,7 +1170,7 @@ def _strip_mark_of_the_web() -> None:
|
||||
meipass = Path(sys._MEIPASS) # type: ignore[attr-defined]
|
||||
for dll in meipass.rglob("*.dll"):
|
||||
with contextlib.suppress(OSError):
|
||||
os.remove(f"{dll}:Zone.Identifier")
|
||||
Path(f"{dll}:Zone.Identifier").unlink()
|
||||
|
||||
|
||||
def winmain() -> None:
|
||||
@@ -1242,8 +1228,6 @@ def winmain() -> None:
|
||||
|
||||
# Startup banner, same as fastapi-vue's server.run() prints in CLI mode.
|
||||
# Goes to stderr, which frozen builds redirect to the log file.
|
||||
from fastapi_vue.startupbox import print_box
|
||||
|
||||
try:
|
||||
version = importlib.metadata.version("mediahive")
|
||||
except importlib.metadata.PackageNotFoundError:
|
||||
@@ -1254,8 +1238,6 @@ def winmain() -> None:
|
||||
# log config wires up its access-log middleware, emoji level prefixes and
|
||||
# tracerite tracebacks (colors are auto-disabled when stderr is not a tty,
|
||||
# e.g. redirected to the log file in frozen builds).
|
||||
from fastapi_vue.logging import patch_log_config
|
||||
|
||||
config = uvicorn.Config(
|
||||
"mediahive.server:app",
|
||||
host=BACKEND_HOST,
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
# Or use the build script (recommended—handles versioning and packaging):
|
||||
# uv run scripts/guibuild.py
|
||||
|
||||
import os
|
||||
import sys
|
||||
import mediahive.winmain
|
||||
import mediahive.server
|
||||
@@ -23,11 +22,9 @@ _icon_win = _pkg / "assets" / "mediahive.ico"
|
||||
_icon_mac = _pkg / "assets" / "mediahive.icns"
|
||||
# ffmpeg staging lives in the persistent build cache (same logic as
|
||||
# scripts/guibuild.py); fall back to the legacy build/ffmpeg location.
|
||||
if sys.platform == "win32":
|
||||
_cache_base = Path(os.environ.get("LOCALAPPDATA", Path.home() / "AppData" / "Local"))
|
||||
else:
|
||||
_cache_base = Path(os.environ.get("XDG_CACHE_HOME", Path.home() / ".cache"))
|
||||
_tools_dir = _cache_base / "mediahive-build" / "ffmpeg"
|
||||
from platformdirs import user_cache_path
|
||||
|
||||
_tools_dir = user_cache_path("mediahive-build") / "ffmpeg"
|
||||
if not _tools_dir.exists():
|
||||
_tools_dir = Path(SPECPATH).parent / "build" / "ffmpeg"
|
||||
_tool_names = ["ffmpeg.exe"] if sys.platform == "win32" else ["ffmpeg"]
|
||||
|
||||
+2
-5
@@ -31,6 +31,7 @@ import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
import setuptools_scm
|
||||
from platformdirs import user_cache_path
|
||||
|
||||
# BtbN automated builds always publish a 'latest' tag with this asset.
|
||||
_FFMPEG_URL = (
|
||||
@@ -46,11 +47,7 @@ _ASSETS_DIR = _REPO_ROOT / "mediahive" / "assets"
|
||||
|
||||
def _build_cache_dir() -> Path:
|
||||
"""Return the persistent cross-build cache dir for downloaded tools (CI wipes build/)."""
|
||||
if sys.platform == "win32":
|
||||
base = os.environ.get("LOCALAPPDATA") or (Path.home() / "AppData" / "Local")
|
||||
return Path(base) / "mediahive-build"
|
||||
base = os.environ.get("XDG_CACHE_HOME") or (Path.home() / ".cache")
|
||||
return Path(base) / "mediahive-build"
|
||||
return user_cache_path("mediahive-build")
|
||||
|
||||
|
||||
_FFMPEG_STAGING = _build_cache_dir() / "ffmpeg"
|
||||
|
||||
Reference in New Issue
Block a user