mirror of
https://github.com/Omni-guides/Jackify.git
synced 2026-01-17 19:47:00 +01:00
Sync from development - prepare for v0.1.1
This commit is contained in:
@@ -168,7 +168,7 @@ def main():
|
||||
print(f"Error: {diagnosis['error']}")
|
||||
return
|
||||
|
||||
print(f"\n📊 Diagnosis Results:")
|
||||
print(f"\nDiagnosis Results:")
|
||||
print(f" Average CPU: {diagnosis['avg_cpu']:.1f}% (Range: {diagnosis['min_cpu']:.1f}% - {diagnosis['max_cpu']:.1f}%)")
|
||||
print(f" Memory usage: {diagnosis['avg_memory_mb']:.1f}MB (Peak: {diagnosis['max_memory_mb']:.1f}MB)")
|
||||
print(f" Low CPU samples: {diagnosis['low_cpu_samples']}/{diagnosis['samples']} "
|
||||
|
||||
@@ -761,7 +761,14 @@ class ModlistHandler:
|
||||
# Conditionally update binary and working directory paths
|
||||
# Skip for jackify-engine workflows since paths are already correct
|
||||
if not getattr(self, 'engine_installed', False):
|
||||
steam_libraries = [self.steam_library] if self.steam_library else None
|
||||
# Convert steamapps/common path to library root path
|
||||
steam_libraries = None
|
||||
if self.steam_library:
|
||||
# self.steam_library is steamapps/common, need to go up 2 levels to get library root
|
||||
steam_library_root = Path(self.steam_library).parent.parent
|
||||
steam_libraries = [steam_library_root]
|
||||
self.logger.debug(f"Using Steam library root: {steam_library_root}")
|
||||
|
||||
if not self.path_handler.edit_binary_working_paths(
|
||||
modlist_ini_path=modlist_ini_path_obj,
|
||||
modlist_dir_path=modlist_dir_path_obj,
|
||||
|
||||
@@ -723,13 +723,17 @@ class ModlistInstallCLI:
|
||||
if chunk == b'\n':
|
||||
# Complete line - decode and print
|
||||
line = buffer.decode('utf-8', errors='replace')
|
||||
print(line, end='')
|
||||
# Enhance Nexus download errors with modlist context
|
||||
enhanced_line = self._enhance_nexus_error(line)
|
||||
print(enhanced_line, end='')
|
||||
buffer = b''
|
||||
last_progress_time = time.time()
|
||||
elif chunk == b'\r':
|
||||
# Carriage return - decode and print without newline
|
||||
line = buffer.decode('utf-8', errors='replace')
|
||||
print(line, end='')
|
||||
# Enhance Nexus download errors with modlist context
|
||||
enhanced_line = self._enhance_nexus_error(line)
|
||||
print(enhanced_line, end='')
|
||||
sys.stdout.flush()
|
||||
buffer = b''
|
||||
last_progress_time = time.time()
|
||||
@@ -1098,4 +1102,36 @@ class ModlistInstallCLI:
|
||||
print(f"Nexus API Key: [SET]")
|
||||
else:
|
||||
print(f"Nexus API Key: [NOT SET - WILL LIKELY FAIL]")
|
||||
print(f"{COLOR_INFO}----------------------------------------{COLOR_RESET}")
|
||||
print(f"{COLOR_INFO}----------------------------------------{COLOR_RESET}")
|
||||
|
||||
def _enhance_nexus_error(self, line: str) -> str:
|
||||
"""
|
||||
Enhance Nexus download error messages by adding the mod URL for easier troubleshooting.
|
||||
"""
|
||||
import re
|
||||
|
||||
# Pattern to match Nexus download errors with ModID and FileID
|
||||
nexus_error_pattern = r"Failed to download '[^']+' from Nexus \(Game: ([^,]+), ModID: (\d+), FileID: \d+\):"
|
||||
|
||||
match = re.search(nexus_error_pattern, line)
|
||||
if match:
|
||||
game_name = match.group(1)
|
||||
mod_id = match.group(2)
|
||||
|
||||
# Map game names to Nexus URL segments
|
||||
game_url_map = {
|
||||
'SkyrimSpecialEdition': 'skyrimspecialedition',
|
||||
'Skyrim': 'skyrim',
|
||||
'Fallout4': 'fallout4',
|
||||
'FalloutNewVegas': 'newvegas',
|
||||
'Oblivion': 'oblivion',
|
||||
'Starfield': 'starfield'
|
||||
}
|
||||
|
||||
game_url = game_url_map.get(game_name, game_name.lower())
|
||||
mod_url = f"https://www.nexusmods.com/{game_url}/mods/{mod_id}"
|
||||
|
||||
# Add URL on next line for easier debugging
|
||||
return f"{line}\n Nexus URL: {mod_url}"
|
||||
|
||||
return line
|
||||
@@ -815,11 +815,12 @@ class PathHandler:
|
||||
subpath = value_part[idx:].lstrip('/')
|
||||
correct_steam_lib = None
|
||||
for lib in steam_libraries:
|
||||
if (lib / subpath.split('/')[2]).exists():
|
||||
correct_steam_lib = lib.parent
|
||||
# Check if the actual game folder exists in this library
|
||||
if len(subpath.split('/')) > 3 and (lib / subpath.split('/')[2] / subpath.split('/')[3]).exists():
|
||||
correct_steam_lib = lib
|
||||
break
|
||||
if not correct_steam_lib and steam_libraries:
|
||||
correct_steam_lib = steam_libraries[0].parent
|
||||
correct_steam_lib = steam_libraries[0]
|
||||
if correct_steam_lib:
|
||||
new_binary_path = f"{drive_prefix}/{correct_steam_lib}/{subpath}".replace('\\', '/').replace('//', '/')
|
||||
else:
|
||||
|
||||
@@ -1,141 +0,0 @@
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import requests
|
||||
import shutil
|
||||
import tempfile
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
GITHUB_OWNER = "Omni-guides"
|
||||
GITHUB_REPO = "Jackify"
|
||||
ASSET_NAME = "jackify"
|
||||
CONFIG_DIR = os.path.expanduser("~/.config/jackify")
|
||||
TOKEN_PATH = os.path.join(CONFIG_DIR, "github_token")
|
||||
LAST_CHECK_PATH = os.path.join(CONFIG_DIR, "last_update_check.json")
|
||||
|
||||
THROTTLE_HOURS = 6
|
||||
|
||||
def get_github_token():
|
||||
if os.path.exists(TOKEN_PATH):
|
||||
with open(TOKEN_PATH, "r") as f:
|
||||
return f.read().strip()
|
||||
return None
|
||||
|
||||
def get_latest_release_info():
|
||||
url = f"https://api.github.com/repos/{GITHUB_OWNER}/{GITHUB_REPO}/releases/latest"
|
||||
headers = {}
|
||||
token = get_github_token()
|
||||
if token:
|
||||
headers["Authorization"] = f"token {token}"
|
||||
resp = requests.get(url, headers=headers, verify=True)
|
||||
if resp.status_code == 200:
|
||||
return resp.json()
|
||||
else:
|
||||
raise RuntimeError(f"Failed to fetch release info: {resp.status_code} {resp.text}")
|
||||
|
||||
def get_current_version():
|
||||
# This should match however Jackify stores its version
|
||||
try:
|
||||
from jackify import __version__
|
||||
return __version__
|
||||
except ImportError:
|
||||
return None
|
||||
|
||||
def should_check_for_update():
|
||||
try:
|
||||
if os.path.exists(LAST_CHECK_PATH):
|
||||
with open(LAST_CHECK_PATH, "r") as f:
|
||||
data = json.load(f)
|
||||
last_check = data.get("last_check", 0)
|
||||
now = int(time.time())
|
||||
if now - last_check < THROTTLE_HOURS * 3600:
|
||||
return False
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"[WARN] Could not read last update check timestamp: {e}")
|
||||
return True
|
||||
|
||||
def record_update_check():
|
||||
try:
|
||||
with open(LAST_CHECK_PATH, "w") as f:
|
||||
json.dump({"last_check": int(time.time())}, f)
|
||||
except Exception as e:
|
||||
print(f"[WARN] Could not write last update check timestamp: {e}")
|
||||
|
||||
def check_for_update():
|
||||
if not should_check_for_update():
|
||||
return False, None, None
|
||||
try:
|
||||
release = get_latest_release_info()
|
||||
latest_version = release["tag_name"].lstrip("v")
|
||||
current_version = get_current_version()
|
||||
if current_version is None:
|
||||
print("[WARN] Could not determine current version.")
|
||||
record_update_check()
|
||||
return False, None, None
|
||||
if latest_version > current_version:
|
||||
record_update_check()
|
||||
return True, latest_version, release
|
||||
record_update_check()
|
||||
return False, latest_version, release
|
||||
except Exception as e:
|
||||
print(f"[ERROR] Update check failed: {e}")
|
||||
record_update_check()
|
||||
return False, None, None
|
||||
|
||||
def download_latest_asset(release):
|
||||
token = get_github_token()
|
||||
headers = {"Accept": "application/octet-stream"}
|
||||
if token:
|
||||
headers["Authorization"] = f"token {token}"
|
||||
for asset in release["assets"]:
|
||||
if asset["name"] == ASSET_NAME:
|
||||
download_url = asset["url"]
|
||||
resp = requests.get(download_url, headers=headers, stream=True, verify=True)
|
||||
if resp.status_code == 200:
|
||||
return resp.content
|
||||
else:
|
||||
raise RuntimeError(f"Failed to download asset: {resp.status_code} {resp.text}")
|
||||
raise RuntimeError(f"Asset '{ASSET_NAME}' not found in release.")
|
||||
|
||||
def replace_current_binary(new_binary_bytes):
|
||||
current_exe = os.path.realpath(sys.argv[0])
|
||||
backup_path = current_exe + ".bak"
|
||||
try:
|
||||
# Write to a temp file first
|
||||
with tempfile.NamedTemporaryFile(delete=False, dir=os.path.dirname(current_exe)) as tmpf:
|
||||
tmpf.write(new_binary_bytes)
|
||||
tmp_path = tmpf.name
|
||||
# Backup current binary
|
||||
shutil.copy2(current_exe, backup_path)
|
||||
# Replace atomically
|
||||
os.replace(tmp_path, current_exe)
|
||||
os.chmod(current_exe, 0o755)
|
||||
print(f"[INFO] Updated binary written to {current_exe}. Backup at {backup_path}.")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"[ERROR] Failed to replace binary: {e}")
|
||||
return False
|
||||
|
||||
def main():
|
||||
if '--update' in sys.argv:
|
||||
print("Checking for updates...")
|
||||
update_available, latest_version, release = check_for_update()
|
||||
if update_available:
|
||||
print(f"A new version (v{latest_version}) is available. Downloading...")
|
||||
try:
|
||||
new_bin = download_latest_asset(release)
|
||||
if replace_current_binary(new_bin):
|
||||
print("Update complete! Please restart Jackify.")
|
||||
else:
|
||||
print("Update failed during binary replacement.")
|
||||
except Exception as e:
|
||||
print(f"[ERROR] Update failed: {e}")
|
||||
else:
|
||||
print("You are already running the latest version.")
|
||||
sys.exit(0)
|
||||
|
||||
# For direct CLI testing
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user