mirror of
https://github.com/Omni-guides/Jackify.git
synced 2026-06-08 01:37:44 +02:00
167 lines
6.1 KiB
Python
167 lines
6.1 KiB
Python
import json
|
|
import re
|
|
from typing import Optional
|
|
from jackify.shared.errors import (
|
|
JackifyError, InstallError, OAuthError,
|
|
oauth_expired, wabbajack_install_failed, format_technical_context,
|
|
game_not_found_for_modlist,
|
|
)
|
|
|
|
|
|
def _ctx_detail(ctx: dict) -> Optional[str]:
|
|
if not ctx:
|
|
return None
|
|
return format_technical_context(context=ctx)
|
|
|
|
|
|
def _engine_error(msg: str, ctx: dict) -> InstallError:
|
|
"""Map generic engine_error payloads to user-visible, actionable InstallError variants."""
|
|
text = (msg or "").strip()
|
|
match = re.search(r"can't find game\s+([A-Za-z0-9_:-]+)", text, flags=re.IGNORECASE)
|
|
if match:
|
|
game_name = match.group(1)
|
|
return game_not_found_for_modlist(game_name, detail=text)
|
|
|
|
return InstallError(
|
|
"Install Engine Error",
|
|
text or "An install engine error occurred.",
|
|
suggestion="Review the error message and retry after correcting the reported issue.",
|
|
solutions=[
|
|
"Check the exact error message shown above and fix the prerequisite it mentions.",
|
|
"Retry the install after restarting Steam.",
|
|
"If this persists, check Modlist_Install_workflow.log for the same error text.",
|
|
],
|
|
technical=_ctx_detail(ctx),
|
|
)
|
|
|
|
|
|
_TYPE_MAP = {
|
|
"engine_error": _engine_error,
|
|
"auth_failed": lambda msg, ctx: oauth_expired(),
|
|
"premium_required": lambda msg, ctx: InstallError(
|
|
"Nexus Premium Required",
|
|
msg,
|
|
suggestion="Jackify requires a Nexus Premium account for automated installs.",
|
|
solutions=[
|
|
"Log in to Nexus Mods with a Premium account.",
|
|
"Non-premium support is planned for a future release.",
|
|
],
|
|
),
|
|
"network_error": lambda msg, ctx: InstallError(
|
|
"Network or Download Failure",
|
|
msg,
|
|
suggestion="Check your internet connection and retry.",
|
|
solutions=[
|
|
"Verify your internet connection.",
|
|
"Re-run the install — Wabbajack resumes from where it stopped.",
|
|
"Check if Nexus Mods is reachable at nexusmods.com.",
|
|
"Disable VPN or proxy if active.",
|
|
],
|
|
technical=_ctx_detail(ctx),
|
|
),
|
|
"disk_full": lambda msg, ctx: InstallError(
|
|
"Disk Full",
|
|
msg,
|
|
suggestion="Free space on the target drive and retry.",
|
|
solutions=[
|
|
"Run: df -h to see available space.",
|
|
"Delete old modlist downloads or backups.",
|
|
"Move the install to a larger drive.",
|
|
],
|
|
technical=_ctx_detail(ctx),
|
|
),
|
|
"permission_denied": lambda msg, ctx: InstallError(
|
|
"Permission Denied",
|
|
msg,
|
|
suggestion="Check write permissions on the target path.",
|
|
solutions=[
|
|
"Ensure Jackify and Steam are run as the same user.",
|
|
"Avoid install paths under /usr, /var, or /opt.",
|
|
f"Check permissions: ls -la {ctx.get('path', '<path>')}",
|
|
],
|
|
technical=_ctx_detail(ctx),
|
|
),
|
|
"archive_corrupt": lambda msg, ctx: InstallError(
|
|
"Corrupted Archive",
|
|
msg,
|
|
suggestion="Re-run the install — Wabbajack will re-download and re-verify the file.",
|
|
solutions=[
|
|
"Re-run the install.",
|
|
"Check available disk space (partial downloads appear corrupt).",
|
|
"Check Modlist_Install_workflow.log for the specific filename.",
|
|
],
|
|
technical=_ctx_detail(ctx),
|
|
),
|
|
"file_not_found": lambda msg, ctx: InstallError(
|
|
"File Not Found",
|
|
msg,
|
|
suggestion="Check the modlist URL and your game installation paths.",
|
|
solutions=[
|
|
"Verify the modlist name is correct.",
|
|
"Ensure the target game is installed.",
|
|
"Re-run — the modlist index may have been temporarily unavailable.",
|
|
],
|
|
technical=_ctx_detail(ctx),
|
|
),
|
|
"validation_failed": lambda msg, ctx: InstallError(
|
|
"Validation Failed",
|
|
msg,
|
|
suggestion="Re-run the install to re-download any failed files.",
|
|
solutions=[
|
|
"Re-run the install — Wabbajack resumes and re-validates.",
|
|
"Check available disk space.",
|
|
"Check Modlist_Install_workflow.log for specific failures.",
|
|
],
|
|
technical=_ctx_detail(ctx),
|
|
),
|
|
"download_stalled": lambda msg, ctx: InstallError(
|
|
"Downloads Stalled",
|
|
msg,
|
|
suggestion="Check your connection and OAuth status, then retry.",
|
|
solutions=[
|
|
"Check your internet connection.",
|
|
"In Settings, confirm Nexus OAuth is active.",
|
|
"Re-run the install.",
|
|
],
|
|
),
|
|
}
|
|
|
|
_EXIT_CODE_MAP = {
|
|
2: lambda d, c: _TYPE_MAP["auth_failed"](d, c or {}),
|
|
3: lambda d, c: _TYPE_MAP["network_error"](d, c or {}),
|
|
4: lambda d, c: _TYPE_MAP["disk_full"](d, c or {}),
|
|
5: lambda d, c: _TYPE_MAP["validation_failed"](d, c or {}),
|
|
6: lambda d, c: wabbajack_install_failed(format_technical_context(detail=d, context=c) or d),
|
|
}
|
|
|
|
|
|
def parse_engine_error_line(line: str) -> Optional[JackifyError]:
|
|
"""Parse one stderr line. Returns JackifyError or None."""
|
|
line = line.strip()
|
|
if not line:
|
|
return None
|
|
try:
|
|
obj = json.loads(line)
|
|
except (json.JSONDecodeError, ValueError):
|
|
return None
|
|
if obj.get("je") != "1":
|
|
return None
|
|
if obj.get("level") == "warning":
|
|
return None
|
|
error_type = obj.get("type", "engine_error")
|
|
message = obj.get("message", "An unknown engine error occurred.")
|
|
context = obj.get("context") or {}
|
|
factory = _TYPE_MAP.get(error_type)
|
|
if factory:
|
|
return factory(message, context)
|
|
return wabbajack_install_failed(f"[{error_type}] {message}")
|
|
|
|
|
|
def error_from_exit_code(exit_code: int, detail: str = "", context: Optional[dict] = None) -> Optional[JackifyError]:
|
|
"""Return a JackifyError based on exit code alone (fallback when no stderr line received)."""
|
|
factory = _EXIT_CODE_MAP.get(exit_code)
|
|
if factory:
|
|
detail_message = detail or f"Engine exited with code {exit_code}."
|
|
return factory(detail_message, context or {})
|
|
return None
|