Sync from development - prepare for v0.1.5.2

This commit is contained in:
Omni
2025-10-01 22:11:14 +01:00
parent 8661f8963e
commit 80914bc76f
56 changed files with 666 additions and 524 deletions

2
.gitignore vendored
View File

@@ -35,7 +35,7 @@ Thumbs.db
docs/ docs/
testing/ testing/
# PyInstaller build files (development only) # Build files (development only)
*.spec *.spec
hook-*.py hook-*.py
requirements-packaging.txt requirements-packaging.txt

View File

@@ -1,5 +1,26 @@
# Jackify Changelog # Jackify Changelog
## v0.1.5.2 - Proton Configuration & Engine Updates
**Release Date:** September 30, 2025
### Critical Bug Fixes
- **Fixed Proton Version Selection**: Wine component installation now properly honors user-selected Proton version from Settings dialog
- Previously, changing from GE-Proton to Proton Experimental in settings would still use the old version for component installation
- Fixed ConfigHandler to reload fresh configuration from disk instead of using stale cache
- Updated all Proton path retrieval across codebase to use fresh-reading methods
### Engine Updates
- **jackify-engine v0.3.16**: Updated to latest engine version with important reliability improvements
- **Sanity Check Fallback**: Added Proton 7z.exe fallback for case sensitivity extraction failures
- **Enhanced Error Messages**: Improved texconv/texdiag error messages to include original texture file names and conversion parameters
### Technical Improvements
- Enhanced configuration system reliability for multi-instance scenarios
- Improved error diagnostics for texture processing operations
- Fix Qt platform plugin discovery in AppImage distribution for improved compatibility
---
## v0.1.5.1 - Bug Fixes ## v0.1.5.1 - Bug Fixes
**Release Date:** September 28, 2025 **Release Date:** September 28, 2025

View File

@@ -5,4 +5,4 @@ This package provides both CLI and GUI interfaces for managing
Wabbajack modlists natively on Linux systems. Wabbajack modlists natively on Linux systems.
""" """
__version__ = "0.1.5.1" __version__ = "0.1.5.2"

View File

@@ -30,7 +30,7 @@ def _get_user_proton_version():
from jackify.backend.handlers.wine_utils import WineUtils from jackify.backend.handlers.wine_utils import WineUtils
config_handler = ConfigHandler() config_handler = ConfigHandler()
user_proton_path = config_handler.get('proton_path', 'auto') user_proton_path = config_handler.get_proton_path()
if user_proton_path == 'auto': if user_proton_path == 'auto':
# Use enhanced fallback logic with GE-Proton preference # Use enhanced fallback logic with GE-Proton preference

View File

@@ -496,6 +496,42 @@ class ConfigHandler:
logger.error(f"Error saving modlist downloads base directory: {e}") logger.error(f"Error saving modlist downloads base directory: {e}")
return False return False
def get_proton_path(self):
"""
Retrieve the saved Proton path from configuration
Always reads fresh from disk to pick up changes from Settings dialog
Returns:
str: Saved Proton path or 'auto' if not saved
"""
try:
# Reload config from disk to pick up changes from Settings dialog
self._load_config()
proton_path = self.settings.get("proton_path", "auto")
logger.debug(f"Retrieved fresh proton_path from config: {proton_path}")
return proton_path
except Exception as e:
logger.error(f"Error retrieving proton_path: {e}")
return "auto"
def get_proton_version(self):
"""
Retrieve the saved Proton version from configuration
Always reads fresh from disk to pick up changes from Settings dialog
Returns:
str: Saved Proton version or 'auto' if not saved
"""
try:
# Reload config from disk to pick up changes from Settings dialog
self._load_config()
proton_version = self.settings.get("proton_version", "auto")
logger.debug(f"Retrieved fresh proton_version from config: {proton_version}")
return proton_version
except Exception as e:
logger.error(f"Error retrieving proton_version: {e}")
return "auto"
def _auto_detect_proton(self): def _auto_detect_proton(self):
"""Auto-detect and set best Proton version (includes GE-Proton and Valve Proton)""" """Auto-detect and set best Proton version (includes GE-Proton and Valve Proton)"""
try: try:

View File

@@ -692,10 +692,32 @@ class ModlistHandler:
print("Error: Could not determine wine prefix location.") print("Error: Could not determine wine prefix location.")
return False return False
if not self.winetricks_handler.install_wine_components(wineprefix, self.game_var_full, specific_components=components): # Try winetricks first (preferred method with current fix)
self.logger.error("Failed to install Wine components. Configuration aborted.") winetricks_success = False
print("Error: Failed to install necessary Wine components.") try:
return False # Abort on failure self.logger.info("Attempting Wine component installation using winetricks...")
winetricks_success = self.winetricks_handler.install_wine_components(wineprefix, self.game_var_full, specific_components=components)
if winetricks_success:
self.logger.info("Winetricks installation completed successfully")
except Exception as e:
self.logger.warning(f"Winetricks installation failed with exception: {e}")
winetricks_success = False
# Fallback to protontricks if winetricks failed
if not winetricks_success:
self.logger.warning("Winetricks failed, falling back to protontricks for Wine component installation...")
try:
protontricks_success = self.protontricks_handler.install_wine_components(target_appid, self.game_var_full, specific_components=components)
if protontricks_success:
self.logger.info("Protontricks fallback installation completed successfully")
else:
self.logger.error("Both winetricks and protontricks failed to install Wine components.")
print("Error: Failed to install necessary Wine components using both winetricks and protontricks.")
return False
except Exception as e:
self.logger.error(f"Protontricks fallback also failed with exception: {e}")
print("Error: Failed to install necessary Wine components using both winetricks and protontricks.")
return False
self.logger.info("Step 4: Installing Wine components... Done") self.logger.info("Step 4: Installing Wine components... Done")
# Step 5: Ensure permissions of Modlist directory # Step 5: Ensure permissions of Modlist directory

View File

@@ -630,15 +630,32 @@ class PathHandler:
# Moved _find_shortcuts_vdf here from ShortcutHandler # Moved _find_shortcuts_vdf here from ShortcutHandler
def _find_shortcuts_vdf(self) -> Optional[str]: def _find_shortcuts_vdf(self) -> Optional[str]:
"""Helper to find the active shortcuts.vdf file for a user. """Helper to find the active shortcuts.vdf file for the current Steam user.
Iterates through userdata directories and returns the path to the Uses proper multi-user detection to find the correct Steam user instead
first found shortcuts.vdf file. of just taking the first found user directory.
Returns: Returns:
Optional[str]: The full path to the shortcuts.vdf file, or None if not found. Optional[str]: The full path to the shortcuts.vdf file, or None if not found.
""" """
# This implementation was moved from ShortcutHandler try:
# Use native Steam service for proper multi-user detection
from jackify.backend.services.native_steam_service import NativeSteamService
steam_service = NativeSteamService()
shortcuts_path = steam_service.get_shortcuts_vdf_path()
if shortcuts_path:
logger.info(f"Found shortcuts.vdf using multi-user detection: {shortcuts_path}")
return str(shortcuts_path)
else:
logger.error("Could not determine shortcuts.vdf path using multi-user detection")
return None
except Exception as e:
logger.error(f"Error using multi-user detection for shortcuts.vdf: {e}")
# Fallback to legacy behavior if multi-user detection fails
logger.warning("Falling back to legacy shortcuts.vdf detection (first-found user)")
userdata_base_paths = [ userdata_base_paths = [
os.path.expanduser("~/.steam/steam/userdata"), os.path.expanduser("~/.steam/steam/userdata"),
os.path.expanduser("~/.local/share/Steam/userdata"), os.path.expanduser("~/.local/share/Steam/userdata"),

View File

@@ -222,8 +222,14 @@ class ValidationHandler:
def validate_steam_shortcut(self, app_id: str) -> Tuple[bool, str]: def validate_steam_shortcut(self, app_id: str) -> Tuple[bool, str]:
"""Validate a Steam shortcut.""" """Validate a Steam shortcut."""
try: try:
# Check if shortcuts.vdf exists # Use native Steam service to get proper shortcuts.vdf path with multi-user support
shortcuts_path = Path.home() / '.steam' / 'steam' / 'userdata' / '75424832' / 'config' / 'shortcuts.vdf' from jackify.backend.services.native_steam_service import NativeSteamService
steam_service = NativeSteamService()
shortcuts_path = steam_service.get_shortcuts_vdf_path()
if not shortcuts_path:
return False, "Could not determine shortcuts.vdf path (no active Steam user found)"
if not shortcuts_path.exists(): if not shortcuts_path.exists():
return False, "shortcuts.vdf not found" return False, "shortcuts.vdf not found"

View File

@@ -709,7 +709,7 @@ class WineUtils:
try: try:
from .config_handler import ConfigHandler from .config_handler import ConfigHandler
config = ConfigHandler() config = ConfigHandler()
fallback_path = config.get('proton_path', 'auto') fallback_path = config.get_proton_path()
if fallback_path != 'auto': if fallback_path != 'auto':
fallback_wine_bin = Path(fallback_path) / "files/bin/wine" fallback_wine_bin = Path(fallback_path) / "files/bin/wine"
if fallback_wine_bin.is_file(): if fallback_wine_bin.is_file():

View File

@@ -137,7 +137,7 @@ class WinetricksHandler:
from ..handlers.wine_utils import WineUtils from ..handlers.wine_utils import WineUtils
config = ConfigHandler() config = ConfigHandler()
user_proton_path = config.get('proton_path', 'auto') user_proton_path = config.get_proton_path()
# If user selected a specific Proton, try that first # If user selected a specific Proton, try that first
wine_binary = None wine_binary = None
@@ -181,6 +181,49 @@ class WinetricksHandler:
env['WINE'] = str(wine_binary) env['WINE'] = str(wine_binary)
self.logger.info(f"Using Proton wine binary for winetricks: {wine_binary}") self.logger.info(f"Using Proton wine binary for winetricks: {wine_binary}")
# CRITICAL: Set up protontricks-compatible environment
proton_dist_path = os.path.dirname(os.path.dirname(wine_binary)) # e.g., /path/to/proton/dist/bin/wine -> /path/to/proton/dist
self.logger.debug(f"Proton dist path: {proton_dist_path}")
# Set WINEDLLPATH like protontricks does
env['WINEDLLPATH'] = f"{proton_dist_path}/lib64/wine:{proton_dist_path}/lib/wine"
# Ensure Proton bin directory is first in PATH
env['PATH'] = f"{proton_dist_path}/bin:{env.get('PATH', '')}"
# Set DLL overrides exactly like protontricks
dll_overrides = {
"beclient": "b,n",
"beclient_x64": "b,n",
"dxgi": "n",
"d3d9": "n",
"d3d10core": "n",
"d3d11": "n",
"d3d12": "n",
"d3d12core": "n",
"nvapi": "n",
"nvapi64": "n",
"nvofapi64": "n",
"nvcuda": "b"
}
# Merge with existing overrides
existing_overrides = env.get('WINEDLLOVERRIDES', '')
if existing_overrides:
# Parse existing overrides
for override in existing_overrides.split(';'):
if '=' in override:
name, value = override.split('=', 1)
dll_overrides[name] = value
env['WINEDLLOVERRIDES'] = ';'.join(f"{name}={setting}" for name, setting in dll_overrides.items())
# Set Wine defaults from protontricks
env['WINE_LARGE_ADDRESS_AWARE'] = '1'
env['DXVK_ENABLE_NVAPI'] = '1'
self.logger.debug(f"Set protontricks environment: WINEDLLPATH={env['WINEDLLPATH']}")
except Exception as e: except Exception as e:
self.logger.error(f"Cannot run winetricks: Failed to get Proton wine binary: {e}") self.logger.error(f"Cannot run winetricks: Failed to get Proton wine binary: {e}")
return False return False
@@ -426,7 +469,8 @@ class WinetricksHandler:
except Exception as e: except Exception as e:
self.logger.warning(f"Could not read winetricks.log: {e}") self.logger.warning(f"Could not read winetricks.log: {e}")
self.logger.error(f"{component} failed (attempt {attempt}): {result.stderr.strip()[:200]}") self.logger.error(f"{component} failed (attempt {attempt}): {result.stderr.strip()}")
self.logger.debug(f"Full stdout for {component}: {result.stdout.strip()}")
except Exception as e: except Exception as e:
self.logger.error(f"Error installing {component} (attempt {attempt}): {e}") self.logger.error(f"Error installing {component} (attempt {attempt}): {e}")

View File

@@ -46,7 +46,7 @@ class AutomatedPrefixService:
from jackify.backend.handlers.wine_utils import WineUtils from jackify.backend.handlers.wine_utils import WineUtils
config_handler = ConfigHandler() config_handler = ConfigHandler()
user_proton_path = config_handler.get('proton_path', 'auto') user_proton_path = config_handler.get_proton_path()
if user_proton_path == 'auto': if user_proton_path == 'auto':
# Use enhanced fallback logic with GE-Proton preference # Use enhanced fallback logic with GE-Proton preference
@@ -2705,7 +2705,7 @@ echo Prefix creation complete.
from jackify.backend.handlers.wine_utils import WineUtils from jackify.backend.handlers.wine_utils import WineUtils
config = ConfigHandler() config = ConfigHandler()
user_proton_path = config.get('proton_path', 'auto') user_proton_path = config.get_proton_path()
# If user selected a specific Proton, try that first # If user selected a specific Proton, try that first
if user_proton_path != 'auto': if user_proton_path != 'auto':

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -7,7 +7,7 @@
"targets": { "targets": {
".NETCoreApp,Version=v8.0": {}, ".NETCoreApp,Version=v8.0": {},
".NETCoreApp,Version=v8.0/linux-x64": { ".NETCoreApp,Version=v8.0/linux-x64": {
"jackify-engine/0.3.15": { "jackify-engine/0.3.16": {
"dependencies": { "dependencies": {
"Markdig": "0.40.0", "Markdig": "0.40.0",
"Microsoft.Extensions.Configuration.Json": "9.0.1", "Microsoft.Extensions.Configuration.Json": "9.0.1",
@@ -22,16 +22,16 @@
"SixLabors.ImageSharp": "3.1.6", "SixLabors.ImageSharp": "3.1.6",
"System.CommandLine": "2.0.0-beta4.22272.1", "System.CommandLine": "2.0.0-beta4.22272.1",
"System.CommandLine.NamingConventionBinder": "2.0.0-beta4.22272.1", "System.CommandLine.NamingConventionBinder": "2.0.0-beta4.22272.1",
"Wabbajack.CLI.Builder": "0.3.15", "Wabbajack.CLI.Builder": "0.3.16",
"Wabbajack.Downloaders.Bethesda": "0.3.15", "Wabbajack.Downloaders.Bethesda": "0.3.16",
"Wabbajack.Downloaders.Dispatcher": "0.3.15", "Wabbajack.Downloaders.Dispatcher": "0.3.16",
"Wabbajack.Hashing.xxHash64": "0.3.15", "Wabbajack.Hashing.xxHash64": "0.3.16",
"Wabbajack.Networking.Discord": "0.3.15", "Wabbajack.Networking.Discord": "0.3.16",
"Wabbajack.Networking.GitHub": "0.3.15", "Wabbajack.Networking.GitHub": "0.3.16",
"Wabbajack.Paths.IO": "0.3.15", "Wabbajack.Paths.IO": "0.3.16",
"Wabbajack.Server.Lib": "0.3.15", "Wabbajack.Server.Lib": "0.3.16",
"Wabbajack.Services.OSIntegrated": "0.3.15", "Wabbajack.Services.OSIntegrated": "0.3.16",
"Wabbajack.VFS": "0.3.15", "Wabbajack.VFS": "0.3.16",
"MegaApiClient": "1.0.0.0", "MegaApiClient": "1.0.0.0",
"runtimepack.Microsoft.NETCore.App.Runtime.linux-x64": "8.0.19" "runtimepack.Microsoft.NETCore.App.Runtime.linux-x64": "8.0.19"
}, },
@@ -1781,7 +1781,7 @@
} }
} }
}, },
"Wabbajack.CLI.Builder/0.3.15": { "Wabbajack.CLI.Builder/0.3.16": {
"dependencies": { "dependencies": {
"Microsoft.Extensions.Configuration.Json": "9.0.1", "Microsoft.Extensions.Configuration.Json": "9.0.1",
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
@@ -1791,109 +1791,109 @@
"Microsoft.Extensions.Logging.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"System.CommandLine": "2.0.0-beta4.22272.1", "System.CommandLine": "2.0.0-beta4.22272.1",
"System.CommandLine.NamingConventionBinder": "2.0.0-beta4.22272.1", "System.CommandLine.NamingConventionBinder": "2.0.0-beta4.22272.1",
"Wabbajack.Paths": "0.3.15" "Wabbajack.Paths": "0.3.16"
}, },
"runtime": { "runtime": {
"Wabbajack.CLI.Builder.dll": {} "Wabbajack.CLI.Builder.dll": {}
} }
}, },
"Wabbajack.Common/0.3.15": { "Wabbajack.Common/0.3.16": {
"dependencies": { "dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"System.Reactive": "6.0.1", "System.Reactive": "6.0.1",
"Wabbajack.DTOs": "0.3.15", "Wabbajack.DTOs": "0.3.16",
"Wabbajack.Networking.Http": "0.3.15", "Wabbajack.Networking.Http": "0.3.16",
"Wabbajack.Paths.IO": "0.3.15" "Wabbajack.Paths.IO": "0.3.16"
}, },
"runtime": { "runtime": {
"Wabbajack.Common.dll": {} "Wabbajack.Common.dll": {}
} }
}, },
"Wabbajack.Compiler/0.3.15": { "Wabbajack.Compiler/0.3.16": {
"dependencies": { "dependencies": {
"F23.StringSimilarity": "6.0.0", "F23.StringSimilarity": "6.0.0",
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Newtonsoft.Json": "13.0.3", "Newtonsoft.Json": "13.0.3",
"SixLabors.ImageSharp": "3.1.6", "SixLabors.ImageSharp": "3.1.6",
"Wabbajack.Downloaders.Dispatcher": "0.3.15", "Wabbajack.Downloaders.Dispatcher": "0.3.16",
"Wabbajack.Installer": "0.3.15", "Wabbajack.Installer": "0.3.16",
"Wabbajack.VFS": "0.3.15", "Wabbajack.VFS": "0.3.16",
"ini-parser-netstandard": "2.5.2" "ini-parser-netstandard": "2.5.2"
}, },
"runtime": { "runtime": {
"Wabbajack.Compiler.dll": {} "Wabbajack.Compiler.dll": {}
} }
}, },
"Wabbajack.Compression.BSA/0.3.15": { "Wabbajack.Compression.BSA/0.3.16": {
"dependencies": { "dependencies": {
"K4os.Compression.LZ4.Streams": "1.3.8", "K4os.Compression.LZ4.Streams": "1.3.8",
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"SharpZipLib": "1.4.2", "SharpZipLib": "1.4.2",
"Wabbajack.Common": "0.3.15", "Wabbajack.Common": "0.3.16",
"Wabbajack.DTOs": "0.3.15" "Wabbajack.DTOs": "0.3.16"
}, },
"runtime": { "runtime": {
"Wabbajack.Compression.BSA.dll": {} "Wabbajack.Compression.BSA.dll": {}
} }
}, },
"Wabbajack.Compression.Zip/0.3.15": { "Wabbajack.Compression.Zip/0.3.16": {
"dependencies": { "dependencies": {
"Wabbajack.IO.Async": "0.3.15" "Wabbajack.IO.Async": "0.3.16"
}, },
"runtime": { "runtime": {
"Wabbajack.Compression.Zip.dll": {} "Wabbajack.Compression.Zip.dll": {}
} }
}, },
"Wabbajack.Configuration/0.3.15": { "Wabbajack.Configuration/0.3.16": {
"runtime": { "runtime": {
"Wabbajack.Configuration.dll": {} "Wabbajack.Configuration.dll": {}
} }
}, },
"Wabbajack.Downloaders.Bethesda/0.3.15": { "Wabbajack.Downloaders.Bethesda/0.3.16": {
"dependencies": { "dependencies": {
"LibAES-CTR": "1.1.0", "LibAES-CTR": "1.1.0",
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"SharpZipLib": "1.4.2", "SharpZipLib": "1.4.2",
"Wabbajack.Common": "0.3.15", "Wabbajack.Common": "0.3.16",
"Wabbajack.Downloaders.Interfaces": "0.3.15", "Wabbajack.Downloaders.Interfaces": "0.3.16",
"Wabbajack.Networking.BethesdaNet": "0.3.15" "Wabbajack.Networking.BethesdaNet": "0.3.16"
}, },
"runtime": { "runtime": {
"Wabbajack.Downloaders.Bethesda.dll": {} "Wabbajack.Downloaders.Bethesda.dll": {}
} }
}, },
"Wabbajack.Downloaders.Dispatcher/0.3.15": { "Wabbajack.Downloaders.Dispatcher/0.3.16": {
"dependencies": { "dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Newtonsoft.Json": "13.0.3", "Newtonsoft.Json": "13.0.3",
"SixLabors.ImageSharp": "3.1.6", "SixLabors.ImageSharp": "3.1.6",
"Wabbajack.Downloaders.Bethesda": "0.3.15", "Wabbajack.Downloaders.Bethesda": "0.3.16",
"Wabbajack.Downloaders.GameFile": "0.3.15", "Wabbajack.Downloaders.GameFile": "0.3.16",
"Wabbajack.Downloaders.GoogleDrive": "0.3.15", "Wabbajack.Downloaders.GoogleDrive": "0.3.16",
"Wabbajack.Downloaders.Http": "0.3.15", "Wabbajack.Downloaders.Http": "0.3.16",
"Wabbajack.Downloaders.IPS4OAuth2Downloader": "0.3.15", "Wabbajack.Downloaders.IPS4OAuth2Downloader": "0.3.16",
"Wabbajack.Downloaders.Interfaces": "0.3.15", "Wabbajack.Downloaders.Interfaces": "0.3.16",
"Wabbajack.Downloaders.Manual": "0.3.15", "Wabbajack.Downloaders.Manual": "0.3.16",
"Wabbajack.Downloaders.MediaFire": "0.3.15", "Wabbajack.Downloaders.MediaFire": "0.3.16",
"Wabbajack.Downloaders.Mega": "0.3.15", "Wabbajack.Downloaders.Mega": "0.3.16",
"Wabbajack.Downloaders.ModDB": "0.3.15", "Wabbajack.Downloaders.ModDB": "0.3.16",
"Wabbajack.Downloaders.Nexus": "0.3.15", "Wabbajack.Downloaders.Nexus": "0.3.16",
"Wabbajack.Downloaders.VerificationCache": "0.3.15", "Wabbajack.Downloaders.VerificationCache": "0.3.16",
"Wabbajack.Downloaders.WabbajackCDN": "0.3.15", "Wabbajack.Downloaders.WabbajackCDN": "0.3.16",
"Wabbajack.Networking.WabbajackClientApi": "0.3.15" "Wabbajack.Networking.WabbajackClientApi": "0.3.16"
}, },
"runtime": { "runtime": {
"Wabbajack.Downloaders.Dispatcher.dll": {} "Wabbajack.Downloaders.Dispatcher.dll": {}
} }
}, },
"Wabbajack.Downloaders.GameFile/0.3.15": { "Wabbajack.Downloaders.GameFile/0.3.16": {
"dependencies": { "dependencies": {
"GameFinder.StoreHandlers.EADesktop": "4.5.0", "GameFinder.StoreHandlers.EADesktop": "4.5.0",
"GameFinder.StoreHandlers.EGS": "4.5.0", "GameFinder.StoreHandlers.EGS": "4.5.0",
@@ -1903,360 +1903,360 @@
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"SixLabors.ImageSharp": "3.1.6", "SixLabors.ImageSharp": "3.1.6",
"Wabbajack.Downloaders.Interfaces": "0.3.15", "Wabbajack.Downloaders.Interfaces": "0.3.16",
"Wabbajack.VFS": "0.3.15" "Wabbajack.VFS": "0.3.16"
}, },
"runtime": { "runtime": {
"Wabbajack.Downloaders.GameFile.dll": {} "Wabbajack.Downloaders.GameFile.dll": {}
} }
}, },
"Wabbajack.Downloaders.GoogleDrive/0.3.15": { "Wabbajack.Downloaders.GoogleDrive/0.3.16": {
"dependencies": { "dependencies": {
"HtmlAgilityPack": "1.11.72", "HtmlAgilityPack": "1.11.72",
"Microsoft.AspNetCore.Http.Extensions": "2.3.0", "Microsoft.AspNetCore.Http.Extensions": "2.3.0",
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Wabbajack.Common": "0.3.15", "Wabbajack.Common": "0.3.16",
"Wabbajack.DTOs": "0.3.15", "Wabbajack.DTOs": "0.3.16",
"Wabbajack.Downloaders.Interfaces": "0.3.15", "Wabbajack.Downloaders.Interfaces": "0.3.16",
"Wabbajack.Networking.Http": "0.3.15", "Wabbajack.Networking.Http": "0.3.16",
"Wabbajack.Networking.Http.Interfaces": "0.3.15" "Wabbajack.Networking.Http.Interfaces": "0.3.16"
}, },
"runtime": { "runtime": {
"Wabbajack.Downloaders.GoogleDrive.dll": {} "Wabbajack.Downloaders.GoogleDrive.dll": {}
} }
}, },
"Wabbajack.Downloaders.Http/0.3.15": { "Wabbajack.Downloaders.Http/0.3.16": {
"dependencies": { "dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Wabbajack.Common": "0.3.15", "Wabbajack.Common": "0.3.16",
"Wabbajack.DTOs": "0.3.15", "Wabbajack.DTOs": "0.3.16",
"Wabbajack.Downloaders.Interfaces": "0.3.15", "Wabbajack.Downloaders.Interfaces": "0.3.16",
"Wabbajack.Networking.BethesdaNet": "0.3.15", "Wabbajack.Networking.BethesdaNet": "0.3.16",
"Wabbajack.Networking.Http.Interfaces": "0.3.15", "Wabbajack.Networking.Http.Interfaces": "0.3.16",
"Wabbajack.Paths.IO": "0.3.15" "Wabbajack.Paths.IO": "0.3.16"
}, },
"runtime": { "runtime": {
"Wabbajack.Downloaders.Http.dll": {} "Wabbajack.Downloaders.Http.dll": {}
} }
}, },
"Wabbajack.Downloaders.Interfaces/0.3.15": { "Wabbajack.Downloaders.Interfaces/0.3.16": {
"dependencies": { "dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Wabbajack.Compression.Zip": "0.3.15", "Wabbajack.Compression.Zip": "0.3.16",
"Wabbajack.DTOs": "0.3.15", "Wabbajack.DTOs": "0.3.16",
"Wabbajack.Paths.IO": "0.3.15" "Wabbajack.Paths.IO": "0.3.16"
}, },
"runtime": { "runtime": {
"Wabbajack.Downloaders.Interfaces.dll": {} "Wabbajack.Downloaders.Interfaces.dll": {}
} }
}, },
"Wabbajack.Downloaders.IPS4OAuth2Downloader/0.3.15": { "Wabbajack.Downloaders.IPS4OAuth2Downloader/0.3.16": {
"dependencies": { "dependencies": {
"F23.StringSimilarity": "6.0.0", "F23.StringSimilarity": "6.0.0",
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Wabbajack.Common": "0.3.15", "Wabbajack.Common": "0.3.16",
"Wabbajack.Downloaders.Interfaces": "0.3.15", "Wabbajack.Downloaders.Interfaces": "0.3.16",
"Wabbajack.Networking.Http": "0.3.15", "Wabbajack.Networking.Http": "0.3.16",
"Wabbajack.Networking.Http.Interfaces": "0.3.15" "Wabbajack.Networking.Http.Interfaces": "0.3.16"
}, },
"runtime": { "runtime": {
"Wabbajack.Downloaders.IPS4OAuth2Downloader.dll": {} "Wabbajack.Downloaders.IPS4OAuth2Downloader.dll": {}
} }
}, },
"Wabbajack.Downloaders.Manual/0.3.15": { "Wabbajack.Downloaders.Manual/0.3.16": {
"dependencies": { "dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Wabbajack.Common": "0.3.15", "Wabbajack.Common": "0.3.16",
"Wabbajack.Downloaders.Interfaces": "0.3.15" "Wabbajack.Downloaders.Interfaces": "0.3.16"
}, },
"runtime": { "runtime": {
"Wabbajack.Downloaders.Manual.dll": {} "Wabbajack.Downloaders.Manual.dll": {}
} }
}, },
"Wabbajack.Downloaders.MediaFire/0.3.15": { "Wabbajack.Downloaders.MediaFire/0.3.16": {
"dependencies": { "dependencies": {
"HtmlAgilityPack": "1.11.72", "HtmlAgilityPack": "1.11.72",
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Wabbajack.Common": "0.3.15", "Wabbajack.Common": "0.3.16",
"Wabbajack.Downloaders.Interfaces": "0.3.15", "Wabbajack.Downloaders.Interfaces": "0.3.16",
"Wabbajack.Networking.Http.Interfaces": "0.3.15" "Wabbajack.Networking.Http.Interfaces": "0.3.16"
}, },
"runtime": { "runtime": {
"Wabbajack.Downloaders.MediaFire.dll": {} "Wabbajack.Downloaders.MediaFire.dll": {}
} }
}, },
"Wabbajack.Downloaders.Mega/0.3.15": { "Wabbajack.Downloaders.Mega/0.3.16": {
"dependencies": { "dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Newtonsoft.Json": "13.0.3", "Newtonsoft.Json": "13.0.3",
"Wabbajack.Common": "0.3.15", "Wabbajack.Common": "0.3.16",
"Wabbajack.Downloaders.Interfaces": "0.3.15", "Wabbajack.Downloaders.Interfaces": "0.3.16",
"Wabbajack.Paths.IO": "0.3.15" "Wabbajack.Paths.IO": "0.3.16"
}, },
"runtime": { "runtime": {
"Wabbajack.Downloaders.Mega.dll": {} "Wabbajack.Downloaders.Mega.dll": {}
} }
}, },
"Wabbajack.Downloaders.ModDB/0.3.15": { "Wabbajack.Downloaders.ModDB/0.3.16": {
"dependencies": { "dependencies": {
"HtmlAgilityPack": "1.11.72", "HtmlAgilityPack": "1.11.72",
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Newtonsoft.Json": "13.0.3", "Newtonsoft.Json": "13.0.3",
"Wabbajack.Common": "0.3.15", "Wabbajack.Common": "0.3.16",
"Wabbajack.Downloaders.Interfaces": "0.3.15", "Wabbajack.Downloaders.Interfaces": "0.3.16",
"Wabbajack.Networking.Http": "0.3.15", "Wabbajack.Networking.Http": "0.3.16",
"Wabbajack.Networking.Http.Interfaces": "0.3.15" "Wabbajack.Networking.Http.Interfaces": "0.3.16"
}, },
"runtime": { "runtime": {
"Wabbajack.Downloaders.ModDB.dll": {} "Wabbajack.Downloaders.ModDB.dll": {}
} }
}, },
"Wabbajack.Downloaders.Nexus/0.3.15": { "Wabbajack.Downloaders.Nexus/0.3.16": {
"dependencies": { "dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Wabbajack.DTOs": "0.3.15", "Wabbajack.DTOs": "0.3.16",
"Wabbajack.Downloaders.Interfaces": "0.3.15", "Wabbajack.Downloaders.Interfaces": "0.3.16",
"Wabbajack.Hashing.xxHash64": "0.3.15", "Wabbajack.Hashing.xxHash64": "0.3.16",
"Wabbajack.Networking.Http": "0.3.15", "Wabbajack.Networking.Http": "0.3.16",
"Wabbajack.Networking.Http.Interfaces": "0.3.15", "Wabbajack.Networking.Http.Interfaces": "0.3.16",
"Wabbajack.Networking.NexusApi": "0.3.15", "Wabbajack.Networking.NexusApi": "0.3.16",
"Wabbajack.Paths": "0.3.15" "Wabbajack.Paths": "0.3.16"
}, },
"runtime": { "runtime": {
"Wabbajack.Downloaders.Nexus.dll": {} "Wabbajack.Downloaders.Nexus.dll": {}
} }
}, },
"Wabbajack.Downloaders.VerificationCache/0.3.15": { "Wabbajack.Downloaders.VerificationCache/0.3.16": {
"dependencies": { "dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Stub.System.Data.SQLite.Core.NetStandard": "1.0.119", "Stub.System.Data.SQLite.Core.NetStandard": "1.0.119",
"Wabbajack.DTOs": "0.3.15", "Wabbajack.DTOs": "0.3.16",
"Wabbajack.Paths.IO": "0.3.15" "Wabbajack.Paths.IO": "0.3.16"
}, },
"runtime": { "runtime": {
"Wabbajack.Downloaders.VerificationCache.dll": {} "Wabbajack.Downloaders.VerificationCache.dll": {}
} }
}, },
"Wabbajack.Downloaders.WabbajackCDN/0.3.15": { "Wabbajack.Downloaders.WabbajackCDN/0.3.16": {
"dependencies": { "dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Microsoft.Toolkit.HighPerformance": "7.1.2", "Microsoft.Toolkit.HighPerformance": "7.1.2",
"Wabbajack.Common": "0.3.15", "Wabbajack.Common": "0.3.16",
"Wabbajack.Downloaders.Interfaces": "0.3.15", "Wabbajack.Downloaders.Interfaces": "0.3.16",
"Wabbajack.Networking.Http": "0.3.15", "Wabbajack.Networking.Http": "0.3.16",
"Wabbajack.RateLimiter": "0.3.15" "Wabbajack.RateLimiter": "0.3.16"
}, },
"runtime": { "runtime": {
"Wabbajack.Downloaders.WabbajackCDN.dll": {} "Wabbajack.Downloaders.WabbajackCDN.dll": {}
} }
}, },
"Wabbajack.DTOs/0.3.15": { "Wabbajack.DTOs/0.3.16": {
"dependencies": { "dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Wabbajack.Hashing.xxHash64": "0.3.15", "Wabbajack.Hashing.xxHash64": "0.3.16",
"Wabbajack.Paths": "0.3.15" "Wabbajack.Paths": "0.3.16"
}, },
"runtime": { "runtime": {
"Wabbajack.DTOs.dll": {} "Wabbajack.DTOs.dll": {}
} }
}, },
"Wabbajack.FileExtractor/0.3.15": { "Wabbajack.FileExtractor/0.3.16": {
"dependencies": { "dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"OMODFramework": "3.0.1", "OMODFramework": "3.0.1",
"Wabbajack.Common": "0.3.15", "Wabbajack.Common": "0.3.16",
"Wabbajack.Compression.BSA": "0.3.15", "Wabbajack.Compression.BSA": "0.3.16",
"Wabbajack.Hashing.PHash": "0.3.15", "Wabbajack.Hashing.PHash": "0.3.16",
"Wabbajack.Paths": "0.3.15" "Wabbajack.Paths": "0.3.16"
}, },
"runtime": { "runtime": {
"Wabbajack.FileExtractor.dll": {} "Wabbajack.FileExtractor.dll": {}
} }
}, },
"Wabbajack.Hashing.PHash/0.3.15": { "Wabbajack.Hashing.PHash/0.3.16": {
"dependencies": { "dependencies": {
"BCnEncoder.Net.ImageSharp": "1.1.1", "BCnEncoder.Net.ImageSharp": "1.1.1",
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Shipwreck.Phash": "0.5.0", "Shipwreck.Phash": "0.5.0",
"SixLabors.ImageSharp": "3.1.6", "SixLabors.ImageSharp": "3.1.6",
"Wabbajack.Common": "0.3.15", "Wabbajack.Common": "0.3.16",
"Wabbajack.DTOs": "0.3.15", "Wabbajack.DTOs": "0.3.16",
"Wabbajack.Paths": "0.3.15", "Wabbajack.Paths": "0.3.16",
"Wabbajack.Paths.IO": "0.3.15" "Wabbajack.Paths.IO": "0.3.16"
}, },
"runtime": { "runtime": {
"Wabbajack.Hashing.PHash.dll": {} "Wabbajack.Hashing.PHash.dll": {}
} }
}, },
"Wabbajack.Hashing.xxHash64/0.3.15": { "Wabbajack.Hashing.xxHash64/0.3.16": {
"dependencies": { "dependencies": {
"Wabbajack.Paths": "0.3.15", "Wabbajack.Paths": "0.3.16",
"Wabbajack.RateLimiter": "0.3.15" "Wabbajack.RateLimiter": "0.3.16"
}, },
"runtime": { "runtime": {
"Wabbajack.Hashing.xxHash64.dll": {} "Wabbajack.Hashing.xxHash64.dll": {}
} }
}, },
"Wabbajack.Installer/0.3.15": { "Wabbajack.Installer/0.3.16": {
"dependencies": { "dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Newtonsoft.Json": "13.0.3", "Newtonsoft.Json": "13.0.3",
"Octopus.Octodiff": "2.0.548", "Octopus.Octodiff": "2.0.548",
"SixLabors.ImageSharp": "3.1.6", "SixLabors.ImageSharp": "3.1.6",
"Wabbajack.DTOs": "0.3.15", "Wabbajack.DTOs": "0.3.16",
"Wabbajack.Downloaders.Dispatcher": "0.3.15", "Wabbajack.Downloaders.Dispatcher": "0.3.16",
"Wabbajack.Downloaders.GameFile": "0.3.15", "Wabbajack.Downloaders.GameFile": "0.3.16",
"Wabbajack.FileExtractor": "0.3.15", "Wabbajack.FileExtractor": "0.3.16",
"Wabbajack.Networking.WabbajackClientApi": "0.3.15", "Wabbajack.Networking.WabbajackClientApi": "0.3.16",
"Wabbajack.Paths": "0.3.15", "Wabbajack.Paths": "0.3.16",
"Wabbajack.Paths.IO": "0.3.15", "Wabbajack.Paths.IO": "0.3.16",
"Wabbajack.VFS": "0.3.15", "Wabbajack.VFS": "0.3.16",
"ini-parser-netstandard": "2.5.2" "ini-parser-netstandard": "2.5.2"
}, },
"runtime": { "runtime": {
"Wabbajack.Installer.dll": {} "Wabbajack.Installer.dll": {}
} }
}, },
"Wabbajack.IO.Async/0.3.15": { "Wabbajack.IO.Async/0.3.16": {
"runtime": { "runtime": {
"Wabbajack.IO.Async.dll": {} "Wabbajack.IO.Async.dll": {}
} }
}, },
"Wabbajack.Networking.BethesdaNet/0.3.15": { "Wabbajack.Networking.BethesdaNet/0.3.16": {
"dependencies": { "dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Wabbajack.DTOs": "0.3.15", "Wabbajack.DTOs": "0.3.16",
"Wabbajack.Networking.Http": "0.3.15", "Wabbajack.Networking.Http": "0.3.16",
"Wabbajack.Networking.Http.Interfaces": "0.3.15" "Wabbajack.Networking.Http.Interfaces": "0.3.16"
}, },
"runtime": { "runtime": {
"Wabbajack.Networking.BethesdaNet.dll": {} "Wabbajack.Networking.BethesdaNet.dll": {}
} }
}, },
"Wabbajack.Networking.Discord/0.3.15": { "Wabbajack.Networking.Discord/0.3.16": {
"dependencies": { "dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Wabbajack.Networking.Http.Interfaces": "0.3.15" "Wabbajack.Networking.Http.Interfaces": "0.3.16"
}, },
"runtime": { "runtime": {
"Wabbajack.Networking.Discord.dll": {} "Wabbajack.Networking.Discord.dll": {}
} }
}, },
"Wabbajack.Networking.GitHub/0.3.15": { "Wabbajack.Networking.GitHub/0.3.16": {
"dependencies": { "dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Octokit": "14.0.0", "Octokit": "14.0.0",
"Wabbajack.DTOs": "0.3.15", "Wabbajack.DTOs": "0.3.16",
"Wabbajack.Networking.Http.Interfaces": "0.3.15" "Wabbajack.Networking.Http.Interfaces": "0.3.16"
}, },
"runtime": { "runtime": {
"Wabbajack.Networking.GitHub.dll": {} "Wabbajack.Networking.GitHub.dll": {}
} }
}, },
"Wabbajack.Networking.Http/0.3.15": { "Wabbajack.Networking.Http/0.3.16": {
"dependencies": { "dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Http": "9.0.1", "Microsoft.Extensions.Http": "9.0.1",
"Microsoft.Extensions.Logging": "9.0.1", "Microsoft.Extensions.Logging": "9.0.1",
"Wabbajack.Configuration": "0.3.15", "Wabbajack.Configuration": "0.3.16",
"Wabbajack.Downloaders.Interfaces": "0.3.15", "Wabbajack.Downloaders.Interfaces": "0.3.16",
"Wabbajack.Hashing.xxHash64": "0.3.15", "Wabbajack.Hashing.xxHash64": "0.3.16",
"Wabbajack.Networking.Http.Interfaces": "0.3.15", "Wabbajack.Networking.Http.Interfaces": "0.3.16",
"Wabbajack.Paths": "0.3.15", "Wabbajack.Paths": "0.3.16",
"Wabbajack.Paths.IO": "0.3.15" "Wabbajack.Paths.IO": "0.3.16"
}, },
"runtime": { "runtime": {
"Wabbajack.Networking.Http.dll": {} "Wabbajack.Networking.Http.dll": {}
} }
}, },
"Wabbajack.Networking.Http.Interfaces/0.3.15": { "Wabbajack.Networking.Http.Interfaces/0.3.16": {
"dependencies": { "dependencies": {
"Wabbajack.Hashing.xxHash64": "0.3.15" "Wabbajack.Hashing.xxHash64": "0.3.16"
}, },
"runtime": { "runtime": {
"Wabbajack.Networking.Http.Interfaces.dll": {} "Wabbajack.Networking.Http.Interfaces.dll": {}
} }
}, },
"Wabbajack.Networking.NexusApi/0.3.15": { "Wabbajack.Networking.NexusApi/0.3.16": {
"dependencies": { "dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Wabbajack.DTOs": "0.3.15", "Wabbajack.DTOs": "0.3.16",
"Wabbajack.Networking.Http": "0.3.15", "Wabbajack.Networking.Http": "0.3.16",
"Wabbajack.Networking.Http.Interfaces": "0.3.15", "Wabbajack.Networking.Http.Interfaces": "0.3.16",
"Wabbajack.Networking.WabbajackClientApi": "0.3.15" "Wabbajack.Networking.WabbajackClientApi": "0.3.16"
}, },
"runtime": { "runtime": {
"Wabbajack.Networking.NexusApi.dll": {} "Wabbajack.Networking.NexusApi.dll": {}
} }
}, },
"Wabbajack.Networking.WabbajackClientApi/0.3.15": { "Wabbajack.Networking.WabbajackClientApi/0.3.16": {
"dependencies": { "dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Octokit": "14.0.0", "Octokit": "14.0.0",
"Wabbajack.Common": "0.3.15", "Wabbajack.Common": "0.3.16",
"Wabbajack.DTOs": "0.3.15", "Wabbajack.DTOs": "0.3.16",
"Wabbajack.Paths.IO": "0.3.15", "Wabbajack.Paths.IO": "0.3.16",
"Wabbajack.VFS.Interfaces": "0.3.15", "Wabbajack.VFS.Interfaces": "0.3.16",
"YamlDotNet": "16.3.0" "YamlDotNet": "16.3.0"
}, },
"runtime": { "runtime": {
"Wabbajack.Networking.WabbajackClientApi.dll": {} "Wabbajack.Networking.WabbajackClientApi.dll": {}
} }
}, },
"Wabbajack.Paths/0.3.15": { "Wabbajack.Paths/0.3.16": {
"runtime": { "runtime": {
"Wabbajack.Paths.dll": {} "Wabbajack.Paths.dll": {}
} }
}, },
"Wabbajack.Paths.IO/0.3.15": { "Wabbajack.Paths.IO/0.3.16": {
"dependencies": { "dependencies": {
"Wabbajack.Paths": "0.3.15", "Wabbajack.Paths": "0.3.16",
"shortid": "4.0.0" "shortid": "4.0.0"
}, },
"runtime": { "runtime": {
"Wabbajack.Paths.IO.dll": {} "Wabbajack.Paths.IO.dll": {}
} }
}, },
"Wabbajack.RateLimiter/0.3.15": { "Wabbajack.RateLimiter/0.3.16": {
"runtime": { "runtime": {
"Wabbajack.RateLimiter.dll": {} "Wabbajack.RateLimiter.dll": {}
} }
}, },
"Wabbajack.Server.Lib/0.3.15": { "Wabbajack.Server.Lib/0.3.16": {
"dependencies": { "dependencies": {
"FluentFTP": "52.0.0", "FluentFTP": "52.0.0",
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
@@ -2264,58 +2264,58 @@
"Nettle": "3.0.0", "Nettle": "3.0.0",
"Newtonsoft.Json": "13.0.3", "Newtonsoft.Json": "13.0.3",
"SixLabors.ImageSharp": "3.1.6", "SixLabors.ImageSharp": "3.1.6",
"Wabbajack.Common": "0.3.15", "Wabbajack.Common": "0.3.16",
"Wabbajack.Networking.Http.Interfaces": "0.3.15", "Wabbajack.Networking.Http.Interfaces": "0.3.16",
"Wabbajack.Services.OSIntegrated": "0.3.15" "Wabbajack.Services.OSIntegrated": "0.3.16"
}, },
"runtime": { "runtime": {
"Wabbajack.Server.Lib.dll": {} "Wabbajack.Server.Lib.dll": {}
} }
}, },
"Wabbajack.Services.OSIntegrated/0.3.15": { "Wabbajack.Services.OSIntegrated/0.3.16": {
"dependencies": { "dependencies": {
"DeviceId": "6.8.0", "DeviceId": "6.8.0",
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Newtonsoft.Json": "13.0.3", "Newtonsoft.Json": "13.0.3",
"SixLabors.ImageSharp": "3.1.6", "SixLabors.ImageSharp": "3.1.6",
"Wabbajack.Compiler": "0.3.15", "Wabbajack.Compiler": "0.3.16",
"Wabbajack.Downloaders.Dispatcher": "0.3.15", "Wabbajack.Downloaders.Dispatcher": "0.3.16",
"Wabbajack.Installer": "0.3.15", "Wabbajack.Installer": "0.3.16",
"Wabbajack.Networking.BethesdaNet": "0.3.15", "Wabbajack.Networking.BethesdaNet": "0.3.16",
"Wabbajack.Networking.Discord": "0.3.15", "Wabbajack.Networking.Discord": "0.3.16",
"Wabbajack.VFS": "0.3.15" "Wabbajack.VFS": "0.3.16"
}, },
"runtime": { "runtime": {
"Wabbajack.Services.OSIntegrated.dll": {} "Wabbajack.Services.OSIntegrated.dll": {}
} }
}, },
"Wabbajack.VFS/0.3.15": { "Wabbajack.VFS/0.3.16": {
"dependencies": { "dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1", "Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"SixLabors.ImageSharp": "3.1.6", "SixLabors.ImageSharp": "3.1.6",
"System.Data.SQLite.Core": "1.0.119", "System.Data.SQLite.Core": "1.0.119",
"Wabbajack.Common": "0.3.15", "Wabbajack.Common": "0.3.16",
"Wabbajack.FileExtractor": "0.3.15", "Wabbajack.FileExtractor": "0.3.16",
"Wabbajack.Hashing.PHash": "0.3.15", "Wabbajack.Hashing.PHash": "0.3.16",
"Wabbajack.Hashing.xxHash64": "0.3.15", "Wabbajack.Hashing.xxHash64": "0.3.16",
"Wabbajack.Paths": "0.3.15", "Wabbajack.Paths": "0.3.16",
"Wabbajack.Paths.IO": "0.3.15", "Wabbajack.Paths.IO": "0.3.16",
"Wabbajack.VFS.Interfaces": "0.3.15" "Wabbajack.VFS.Interfaces": "0.3.16"
}, },
"runtime": { "runtime": {
"Wabbajack.VFS.dll": {} "Wabbajack.VFS.dll": {}
} }
}, },
"Wabbajack.VFS.Interfaces/0.3.15": { "Wabbajack.VFS.Interfaces/0.3.16": {
"dependencies": { "dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.1", "Microsoft.Extensions.DependencyInjection": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1", "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Wabbajack.DTOs": "0.3.15", "Wabbajack.DTOs": "0.3.16",
"Wabbajack.Hashing.xxHash64": "0.3.15", "Wabbajack.Hashing.xxHash64": "0.3.16",
"Wabbajack.Paths": "0.3.15" "Wabbajack.Paths": "0.3.16"
}, },
"runtime": { "runtime": {
"Wabbajack.VFS.Interfaces.dll": {} "Wabbajack.VFS.Interfaces.dll": {}
@@ -2332,7 +2332,7 @@
} }
}, },
"libraries": { "libraries": {
"jackify-engine/0.3.15": { "jackify-engine/0.3.16": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
@@ -3021,202 +3021,202 @@
"path": "yamldotnet/16.3.0", "path": "yamldotnet/16.3.0",
"hashPath": "yamldotnet.16.3.0.nupkg.sha512" "hashPath": "yamldotnet.16.3.0.nupkg.sha512"
}, },
"Wabbajack.CLI.Builder/0.3.15": { "Wabbajack.CLI.Builder/0.3.16": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Common/0.3.15": { "Wabbajack.Common/0.3.16": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Compiler/0.3.15": { "Wabbajack.Compiler/0.3.16": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Compression.BSA/0.3.15": { "Wabbajack.Compression.BSA/0.3.16": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Compression.Zip/0.3.15": { "Wabbajack.Compression.Zip/0.3.16": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Configuration/0.3.15": { "Wabbajack.Configuration/0.3.16": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Downloaders.Bethesda/0.3.15": { "Wabbajack.Downloaders.Bethesda/0.3.16": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Downloaders.Dispatcher/0.3.15": { "Wabbajack.Downloaders.Dispatcher/0.3.16": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Downloaders.GameFile/0.3.15": { "Wabbajack.Downloaders.GameFile/0.3.16": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Downloaders.GoogleDrive/0.3.15": { "Wabbajack.Downloaders.GoogleDrive/0.3.16": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Downloaders.Http/0.3.15": { "Wabbajack.Downloaders.Http/0.3.16": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Downloaders.Interfaces/0.3.15": { "Wabbajack.Downloaders.Interfaces/0.3.16": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Downloaders.IPS4OAuth2Downloader/0.3.15": { "Wabbajack.Downloaders.IPS4OAuth2Downloader/0.3.16": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Downloaders.Manual/0.3.15": { "Wabbajack.Downloaders.Manual/0.3.16": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Downloaders.MediaFire/0.3.15": { "Wabbajack.Downloaders.MediaFire/0.3.16": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Downloaders.Mega/0.3.15": { "Wabbajack.Downloaders.Mega/0.3.16": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Downloaders.ModDB/0.3.15": { "Wabbajack.Downloaders.ModDB/0.3.16": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Downloaders.Nexus/0.3.15": { "Wabbajack.Downloaders.Nexus/0.3.16": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Downloaders.VerificationCache/0.3.15": { "Wabbajack.Downloaders.VerificationCache/0.3.16": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Downloaders.WabbajackCDN/0.3.15": { "Wabbajack.Downloaders.WabbajackCDN/0.3.16": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.DTOs/0.3.15": { "Wabbajack.DTOs/0.3.16": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.FileExtractor/0.3.15": { "Wabbajack.FileExtractor/0.3.16": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Hashing.PHash/0.3.15": { "Wabbajack.Hashing.PHash/0.3.16": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Hashing.xxHash64/0.3.15": { "Wabbajack.Hashing.xxHash64/0.3.16": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Installer/0.3.15": { "Wabbajack.Installer/0.3.16": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.IO.Async/0.3.15": { "Wabbajack.IO.Async/0.3.16": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Networking.BethesdaNet/0.3.15": { "Wabbajack.Networking.BethesdaNet/0.3.16": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Networking.Discord/0.3.15": { "Wabbajack.Networking.Discord/0.3.16": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Networking.GitHub/0.3.15": { "Wabbajack.Networking.GitHub/0.3.16": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Networking.Http/0.3.15": { "Wabbajack.Networking.Http/0.3.16": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Networking.Http.Interfaces/0.3.15": { "Wabbajack.Networking.Http.Interfaces/0.3.16": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Networking.NexusApi/0.3.15": { "Wabbajack.Networking.NexusApi/0.3.16": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Networking.WabbajackClientApi/0.3.15": { "Wabbajack.Networking.WabbajackClientApi/0.3.16": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Paths/0.3.15": { "Wabbajack.Paths/0.3.16": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Paths.IO/0.3.15": { "Wabbajack.Paths.IO/0.3.16": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.RateLimiter/0.3.15": { "Wabbajack.RateLimiter/0.3.16": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Server.Lib/0.3.15": { "Wabbajack.Server.Lib/0.3.16": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Services.OSIntegrated/0.3.15": { "Wabbajack.Services.OSIntegrated/0.3.16": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.VFS/0.3.15": { "Wabbajack.VFS/0.3.16": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.VFS.Interfaces/0.3.15": { "Wabbajack.VFS.Interfaces/0.3.16": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""

Binary file not shown.

View File

@@ -102,7 +102,7 @@ sys.path.insert(0, str(src_dir))
from PySide6.QtWidgets import ( from PySide6.QtWidgets import (
QApplication, QMainWindow, QWidget, QLabel, QVBoxLayout, QPushButton, QApplication, QMainWindow, QWidget, QLabel, QVBoxLayout, QPushButton,
QStackedWidget, QHBoxLayout, QDialog, QFormLayout, QLineEdit, QCheckBox, QSpinBox, QMessageBox, QGroupBox, QGridLayout, QFileDialog, QToolButton, QStyle, QComboBox QStackedWidget, QHBoxLayout, QDialog, QFormLayout, QLineEdit, QCheckBox, QSpinBox, QMessageBox, QGroupBox, QGridLayout, QFileDialog, QToolButton, QStyle, QComboBox, QTabWidget
) )
from PySide6.QtCore import Qt, QEvent from PySide6.QtCore import Qt, QEvent
from PySide6.QtGui import QIcon from PySide6.QtGui import QIcon
@@ -167,171 +167,61 @@ class SettingsDialog(QDialog):
self._original_debug_mode = self.config_handler.get('debug_mode', False) self._original_debug_mode = self.config_handler.get('debug_mode', False)
self.setWindowTitle("Settings") self.setWindowTitle("Settings")
self.setModal(True) self.setModal(True)
self.setMinimumWidth(750) self.setMinimumWidth(650) # Reduced width for Steam Deck compatibility
self.setMaximumWidth(800) # Maximum width to prevent excessive stretching
self.setStyleSheet("QDialog { background-color: #232323; color: #eee; } QPushButton:hover { background-color: #333; }") self.setStyleSheet("QDialog { background-color: #232323; color: #eee; } QPushButton:hover { background-color: #333; }")
main_layout = QVBoxLayout() main_layout = QVBoxLayout()
self.setLayout(main_layout) self.setLayout(main_layout)
# --- Resource Limits Section --- # Create tab widget
resource_group = QGroupBox("Resource Limits") self.tab_widget = QTabWidget()
resource_group.setStyleSheet("QGroupBox { border: 1px solid #555; border-radius: 6px; margin-top: 8px; padding: 8px; background: #23282d; } QGroupBox:title { subcontrol-origin: margin; left: 10px; padding: 0 3px 0 3px; font-weight: bold; color: #fff; }") self.tab_widget.setStyleSheet("""
resource_layout = QGridLayout() QTabWidget::pane { border: 1px solid #555; background: #232323; }
resource_group.setLayout(resource_layout) QTabBar::tab { background: #333; color: #eee; padding: 8px 16px; margin: 2px; }
resource_layout.setVerticalSpacing(4) QTabBar::tab:selected { background: #555; }
resource_layout.setHorizontalSpacing(8) QTabBar::tab:hover { background: #444; }
resource_layout.addWidget(self._bold_label("Resource"), 0, 0, 1, 1, Qt.AlignLeft) """)
resource_layout.addWidget(self._bold_label("Max Tasks"), 0, 1, 1, 1, Qt.AlignLeft) main_layout.addWidget(self.tab_widget)
self.resource_settings_path = os.path.expanduser("~/.config/jackify/resource_settings.json")
self.resource_settings = self._load_json(self.resource_settings_path)
self.resource_edits = {}
resource_row_index = 0
for resource_row_index, (k, v) in enumerate(self.resource_settings.items(), start=1):
try:
# Create resource label with optional inline checkbox for File Extractor
if k == "File Extractor":
# Create horizontal layout for File Extractor with inline checkbox
resource_row = QHBoxLayout()
resource_label = QLabel(f"{k}:", parent=self)
resource_row.addWidget(resource_label)
resource_row.addSpacing(10) # Add some spacing
multithreading_checkbox = QCheckBox("Multithreading (Experimental)") # Create tabs
multithreading_checkbox.setChecked(v.get('_7zzMultiThread', 'off') == 'on') self._create_general_tab()
multithreading_checkbox.setToolTip("Enables multithreaded file extraction using 7-Zip. May improve extraction speed on multi-core systems but could be less stable.") self._create_advanced_tab()
multithreading_checkbox.setStyleSheet("color: #fff;")
resource_row.addWidget(multithreading_checkbox)
resource_row.addStretch() # Push checkbox to the left
# Add the horizontal layout to the grid # --- Save/Close/Help Buttons ---
resource_layout.addLayout(resource_row, resource_row_index, 0) btn_layout = QHBoxLayout()
else: self.help_btn = QPushButton("Help")
resource_layout.addWidget(QLabel(f"{k}:", parent=self), resource_row_index, 0, 1, 1, Qt.AlignLeft) self.help_btn.setToolTip("Help/documentation coming soon!")
self.help_btn.clicked.connect(self._show_help)
btn_layout.addWidget(self.help_btn)
btn_layout.addStretch(1)
save_btn = QPushButton("Save")
close_btn = QPushButton("Close")
save_btn.clicked.connect(self._save)
close_btn.clicked.connect(self.reject)
btn_layout.addWidget(save_btn)
btn_layout.addWidget(close_btn)
max_tasks_spin = QSpinBox() # Add error label for validation messages
max_tasks_spin.setMinimum(1) self.error_label = QLabel("")
max_tasks_spin.setMaximum(128) self.error_label.setStyleSheet("QLabel { color: #ff6b6b; }")
max_tasks_spin.setValue(v.get('MaxTasks', 16)) main_layout.addWidget(self.error_label)
max_tasks_spin.setToolTip("Maximum number of concurrent tasks for this resource.")
max_tasks_spin.setFixedWidth(160) main_layout.addSpacing(10)
resource_layout.addWidget(max_tasks_spin, resource_row_index, 1) main_layout.addLayout(btn_layout)
# Store the widgets (checkbox for File Extractor, None for others)
if k == "File Extractor":
self.resource_edits[k] = (multithreading_checkbox, max_tasks_spin)
else:
self.resource_edits[k] = (None, max_tasks_spin)
except Exception as e: except Exception as e:
print(f"[ERROR] Failed to create widgets for resource '{k}': {e}") print(f"[ERROR] Exception in SettingsDialog.__init__: {e}")
continue import traceback
traceback.print_exc()
# If no resources exist, show helpful message def _create_general_tab(self):
if not self.resource_edits: """Create the General settings tab"""
info_label = QLabel("Resource Limit settings will be generated once a modlist install action is performed") general_tab = QWidget()
info_label.setStyleSheet("color: #aaa; font-style: italic; padding: 20px; font-size: 11pt;") general_layout = QVBoxLayout(general_tab)
info_label.setWordWrap(True)
info_label.setAlignment(Qt.AlignCenter)
info_label.setMinimumHeight(60) # Ensure enough height to prevent cutoff
resource_layout.addWidget(info_label, 1, 0, 3, 2) # Span more rows for better space
# Bandwidth limiter row (only show if Downloads resource exists) # --- Directory Paths Section (moved to top as most essential) ---
if "Downloads" in self.resource_settings: dir_group = QGroupBox("Directory Paths")
downloads_throughput = self.resource_settings["Downloads"].get("MaxThroughput", 0)
self.bandwidth_spin = QSpinBox()
self.bandwidth_spin.setMinimum(0)
self.bandwidth_spin.setMaximum(1000000)
self.bandwidth_spin.setValue(downloads_throughput)
self.bandwidth_spin.setSuffix(" KB/s")
self.bandwidth_spin.setFixedWidth(160)
self.bandwidth_spin.setToolTip("Set the maximum download speed for modlist downloads. 0 = unlimited.")
bandwidth_note = QLabel("(0 = unlimited)")
bandwidth_note.setStyleSheet("color: #aaa; font-size: 10pt;")
# Create horizontal layout for bandwidth row
bandwidth_row = QHBoxLayout()
bandwidth_row.addWidget(self.bandwidth_spin)
bandwidth_row.addWidget(bandwidth_note)
bandwidth_row.addStretch() # Push to the left
resource_layout.addWidget(QLabel("Bandwidth Limit:", parent=self), resource_row_index+1, 0, 1, 1, Qt.AlignLeft)
resource_layout.addLayout(bandwidth_row, resource_row_index+1, 1)
else:
self.bandwidth_spin = None # No bandwidth UI if Downloads resource doesn't exist
main_layout.addWidget(resource_group)
main_layout.addSpacing(12)
# --- Debug & Diagnostics Section ---
debug_group = QGroupBox("Debug & Diagnostics")
debug_group.setStyleSheet("QGroupBox { border: 1px solid #555; border-radius: 6px; margin-top: 8px; padding: 8px; background: #23282d; } QGroupBox:title { subcontrol-origin: margin; left: 10px; padding: 0 3px 0 3px; font-weight: bold; color: #fff; }")
debug_layout = QVBoxLayout()
debug_group.setLayout(debug_layout)
self.debug_checkbox = QCheckBox("Enable debug mode (requires restart)")
# Load debug_mode from config
self.debug_checkbox.setChecked(self.config_handler.get('debug_mode', False))
self.debug_checkbox.setToolTip("Enable verbose debug logging. Requires Jackify restart to take effect.")
self.debug_checkbox.setStyleSheet("color: #fff;")
debug_layout.addWidget(self.debug_checkbox)
main_layout.addWidget(debug_group)
main_layout.addSpacing(12)
# --- Nexus API Key Section ---
api_group = QGroupBox("Nexus API Key")
api_group.setStyleSheet("QGroupBox { border: 1px solid #555; border-radius: 6px; margin-top: 8px; padding: 8px; background: #23282d; } QGroupBox:title { subcontrol-origin: margin; left: 10px; padding: 0 3px 0 3px; font-weight: bold; color: #fff; }")
api_layout = QHBoxLayout()
api_group.setLayout(api_layout)
self.api_key_edit = QLineEdit()
self.api_key_edit.setEchoMode(QLineEdit.Password)
api_key = self.config_handler.get_api_key()
if api_key:
self.api_key_edit.setText(api_key)
else:
self.api_key_edit.setText("")
self.api_key_edit.setToolTip("Your Nexus API Key (obfuscated by default, click Show to reveal)")
# Connect for immediate saving when text changes
self.api_key_edit.textChanged.connect(self._on_api_key_changed)
self.api_show_btn = QToolButton()
self.api_show_btn.setCheckable(True)
self.api_show_btn.setIcon(QIcon.fromTheme("view-visible"))
self.api_show_btn.setToolTip("Show or hide your API key")
self.api_show_btn.toggled.connect(self._toggle_api_key_visibility)
self.api_show_btn.setStyleSheet("")
clear_api_btn = QPushButton("Clear API Key")
clear_api_btn.clicked.connect(self._clear_api_key)
api_layout.addWidget(QLabel("Nexus API Key:"))
api_layout.addWidget(self.api_key_edit)
api_layout.addWidget(self.api_show_btn)
api_layout.addWidget(clear_api_btn)
main_layout.addWidget(api_group)
main_layout.addSpacing(12)
# --- Proton Version Section ---
proton_group = QGroupBox("Proton Version")
proton_group.setStyleSheet("QGroupBox { border: 1px solid #555; border-radius: 6px; margin-top: 8px; padding: 8px; background: #23282d; } QGroupBox:title { subcontrol-origin: margin; left: 10px; padding: 0 3px 0 3px; font-weight: bold; color: #fff; }")
proton_layout = QHBoxLayout()
proton_group.setLayout(proton_layout)
self.proton_dropdown = QComboBox()
self.proton_dropdown.setToolTip("Select Proton version for shortcut creation and texture processing")
self.proton_dropdown.setMinimumWidth(200)
# Populate Proton dropdown
self._populate_proton_dropdown()
# Refresh button for Proton detection
refresh_btn = QPushButton("")
refresh_btn.setFixedSize(30, 30)
refresh_btn.setToolTip("Refresh Proton version list")
refresh_btn.clicked.connect(self._refresh_proton_dropdown)
proton_layout.addWidget(QLabel("Proton Version:"))
proton_layout.addWidget(self.proton_dropdown)
proton_layout.addWidget(refresh_btn)
proton_layout.addStretch()
main_layout.addWidget(proton_group)
main_layout.addSpacing(12)
# --- Directories & Paths Section ---
dir_group = QGroupBox("Directories & Paths")
dir_group.setStyleSheet("QGroupBox { border: 1px solid #555; border-radius: 6px; margin-top: 8px; padding: 8px; background: #23282d; } QGroupBox:title { subcontrol-origin: margin; left: 10px; padding: 0 3px 0 3px; font-weight: bold; color: #fff; }") dir_group.setStyleSheet("QGroupBox { border: 1px solid #555; border-radius: 6px; margin-top: 8px; padding: 8px; background: #23282d; } QGroupBox:title { subcontrol-origin: margin; left: 10px; padding: 0 3px 0 3px; font-weight: bold; color: #fff; }")
dir_layout = QFormLayout() dir_layout = QFormLayout()
dir_group.setLayout(dir_layout) dir_group.setLayout(dir_layout)
@@ -380,52 +270,155 @@ class SettingsDialog(QDialog):
jackify_data_dir_row.addWidget(reset_jackify_dir_btn) jackify_data_dir_row.addWidget(reset_jackify_dir_btn)
dir_layout.addRow(QLabel("Jackify Data Dir:"), jackify_data_dir_row) dir_layout.addRow(QLabel("Jackify Data Dir:"), jackify_data_dir_row)
main_layout.addWidget(dir_group) general_layout.addWidget(dir_group)
main_layout.addSpacing(12) general_layout.addSpacing(12)
# --- Save/Close/Help Buttons --- # --- Nexus API Key Section ---
btn_layout = QHBoxLayout() api_group = QGroupBox("Nexus API Key")
self.help_btn = QPushButton("Help") api_group.setStyleSheet("QGroupBox { border: 1px solid #555; border-radius: 6px; margin-top: 8px; padding: 8px; background: #23282d; } QGroupBox:title { subcontrol-origin: margin; left: 10px; padding: 0 3px 0 3px; font-weight: bold; color: #fff; }")
self.help_btn.setToolTip("Help/documentation coming soon!") api_layout = QHBoxLayout()
self.help_btn.clicked.connect(self._show_help) api_group.setLayout(api_layout)
btn_layout.addWidget(self.help_btn) self.api_key_edit = QLineEdit()
btn_layout.addStretch(1) self.api_key_edit.setEchoMode(QLineEdit.Password)
save_btn = QPushButton("Save") api_key = self.config_handler.get_api_key()
close_btn = QPushButton("Close") if api_key:
save_btn.clicked.connect(self._save) self.api_key_edit.setText(api_key)
close_btn.clicked.connect(self.reject) else:
btn_layout.addWidget(save_btn) self.api_key_edit.setText("")
btn_layout.addWidget(close_btn) self.api_key_edit.setToolTip("Your Nexus API Key (obfuscated by default, click Show to reveal)")
main_layout.addSpacing(10) # Connect for immediate saving when text changes
main_layout.addLayout(btn_layout) self.api_key_edit.textChanged.connect(self._on_api_key_changed)
self.api_show_btn = QToolButton()
self.api_show_btn.setCheckable(True)
self.api_show_btn.setIcon(QIcon.fromTheme("view-visible"))
self.api_show_btn.setToolTip("Show or hide your API key")
self.api_show_btn.toggled.connect(self._toggle_api_key_visibility)
self.api_show_btn.setStyleSheet("")
clear_api_btn = QPushButton("Clear API Key")
clear_api_btn.clicked.connect(self._clear_api_key)
api_layout.addWidget(QLabel("Nexus API Key:"))
api_layout.addWidget(self.api_key_edit)
api_layout.addWidget(self.api_show_btn)
api_layout.addWidget(clear_api_btn)
general_layout.addWidget(api_group)
general_layout.addSpacing(12)
# Set tab order for accessibility # --- Default Proton Version Section ---
# Get the first resource's widgets if any exist proton_group = QGroupBox("Default Proton Version")
if self.resource_edits: proton_group.setStyleSheet("QGroupBox { border: 1px solid #555; border-radius: 6px; margin-top: 8px; padding: 8px; background: #23282d; } QGroupBox:title { subcontrol-origin: margin; left: 10px; padding: 0 3px 0 3px; font-weight: bold; color: #fff; }")
first_resource_key = list(self.resource_edits.keys())[0] proton_layout = QHBoxLayout()
first_multithreading, first_max_tasks = self.resource_edits[first_resource_key] proton_group.setLayout(proton_layout)
# Set tab order starting with the first max tasks spinner
self.setTabOrder(first_max_tasks, self.bandwidth_spin)
# Continue with bandwidth spinner regardless of resources
self.setTabOrder(self.bandwidth_spin, self.debug_checkbox)
self.setTabOrder(self.debug_checkbox, self.api_key_edit)
self.setTabOrder(self.api_key_edit, self.api_show_btn)
self.setTabOrder(self.api_show_btn, clear_api_btn)
self.setTabOrder(clear_api_btn, self.install_dir_edit)
self.setTabOrder(self.install_dir_edit, self.install_dir_btn)
self.setTabOrder(self.install_dir_btn, self.download_dir_edit)
self.setTabOrder(self.download_dir_edit, self.download_dir_btn)
self.setTabOrder(self.download_dir_btn, save_btn)
self.setTabOrder(save_btn, close_btn)
self.error_label = QLabel("") self.proton_dropdown = QComboBox()
self.error_label.setStyleSheet("color: #f55; font-weight: bold;") self.proton_dropdown.setToolTip("Select default Proton version for shortcut creation and texture processing")
main_layout.insertWidget(0, self.error_label) self.proton_dropdown.setMinimumWidth(200)
# Populate Proton dropdown
self._populate_proton_dropdown()
# Refresh button for Proton detection
refresh_btn = QPushButton("")
refresh_btn.setFixedSize(30, 30)
refresh_btn.setToolTip("Refresh Proton version list")
refresh_btn.clicked.connect(self._refresh_proton_dropdown)
proton_layout.addWidget(QLabel("Proton Version:"))
proton_layout.addWidget(self.proton_dropdown)
proton_layout.addWidget(refresh_btn)
proton_layout.addStretch()
general_layout.addWidget(proton_group)
general_layout.addSpacing(12)
# --- Enable Debug Section (moved to bottom as advanced option) ---
debug_group = QGroupBox("Enable Debug")
debug_group.setStyleSheet("QGroupBox { border: 1px solid #555; border-radius: 6px; margin-top: 8px; padding: 8px; background: #23282d; } QGroupBox:title { subcontrol-origin: margin; left: 10px; padding: 0 3px 0 3px; font-weight: bold; color: #fff; }")
debug_layout = QVBoxLayout()
debug_group.setLayout(debug_layout)
self.debug_checkbox = QCheckBox("Enable debug mode (requires restart)")
# Load debug_mode from config
self.debug_checkbox.setChecked(self.config_handler.get('debug_mode', False))
self.debug_checkbox.setToolTip("Enable verbose debug logging. Requires Jackify restart to take effect.")
self.debug_checkbox.setStyleSheet("color: #fff;")
debug_layout.addWidget(self.debug_checkbox)
general_layout.addWidget(debug_group)
general_layout.addStretch() # Add stretch to push content to top
self.tab_widget.addTab(general_tab, "General")
def _create_advanced_tab(self):
"""Create the Advanced settings tab"""
advanced_tab = QWidget()
advanced_layout = QVBoxLayout(advanced_tab)
resource_group = QGroupBox("Resource Limits")
resource_group.setStyleSheet("QGroupBox { border: 1px solid #555; border-radius: 6px; margin-top: 8px; padding: 8px; background: #23282d; } QGroupBox:title { subcontrol-origin: margin; left: 10px; padding: 0 3px 0 3px; font-weight: bold; color: #fff; }")
resource_layout = QGridLayout()
resource_group.setLayout(resource_layout)
resource_layout.setVerticalSpacing(4)
resource_layout.setHorizontalSpacing(8)
resource_layout.addWidget(self._bold_label("Resource"), 0, 0, 1, 1, Qt.AlignLeft)
resource_layout.addWidget(self._bold_label("Max Tasks"), 0, 1, 1, 1, Qt.AlignLeft)
self.resource_settings_path = os.path.expanduser("~/.config/jackify/resource_settings.json")
self.resource_settings = self._load_json(self.resource_settings_path)
self.resource_edits = {}
resource_row_index = 0
for resource_row_index, (k, v) in enumerate(self.resource_settings.items(), start=1):
try:
# Create resource label
resource_layout.addWidget(QLabel(f"{k}:", parent=self), resource_row_index, 0, 1, 1, Qt.AlignLeft)
max_tasks_spin = QSpinBox()
max_tasks_spin.setMinimum(1)
max_tasks_spin.setMaximum(128)
max_tasks_spin.setValue(v.get('MaxTasks', 16))
max_tasks_spin.setToolTip("Maximum number of concurrent tasks for this resource.")
max_tasks_spin.setFixedWidth(160)
resource_layout.addWidget(max_tasks_spin, resource_row_index, 1)
# Store the widgets
self.resource_edits[k] = (None, max_tasks_spin)
except Exception as e: except Exception as e:
print(f"[ERROR] Exception in SettingsDialog __init__: {e}") print(f"[ERROR] Failed to create widgets for resource '{k}': {e}")
import traceback continue
traceback.print_exc()
raise # If no resources exist, show helpful message
if not self.resource_edits:
info_label = QLabel("Resource Limit settings will be generated once a modlist install action is performed")
info_label.setStyleSheet("color: #aaa; font-style: italic; padding: 20px; font-size: 11pt;")
info_label.setWordWrap(True)
info_label.setAlignment(Qt.AlignCenter)
info_label.setMinimumHeight(60) # Ensure enough height to prevent cutoff
resource_layout.addWidget(info_label, 1, 0, 3, 2) # Span more rows for better space
# Bandwidth limiter row (only show if Downloads resource exists)
if "Downloads" in self.resource_settings:
downloads_throughput = self.resource_settings["Downloads"].get("MaxThroughput", 0)
self.bandwidth_spin = QSpinBox()
self.bandwidth_spin.setMinimum(0)
self.bandwidth_spin.setMaximum(1000000)
self.bandwidth_spin.setValue(downloads_throughput)
self.bandwidth_spin.setSuffix(" KB/s")
self.bandwidth_spin.setFixedWidth(160)
self.bandwidth_spin.setToolTip("Set the maximum download speed for modlist downloads. 0 = unlimited.")
bandwidth_note = QLabel("(0 = unlimited)")
bandwidth_note.setStyleSheet("color: #aaa; font-size: 10pt;")
# Create horizontal layout for bandwidth row
bandwidth_row = QHBoxLayout()
bandwidth_row.addWidget(self.bandwidth_spin)
bandwidth_row.addWidget(bandwidth_note)
bandwidth_row.addStretch() # Push to the left
resource_layout.addWidget(QLabel("Bandwidth Limit:", parent=self), resource_row_index+1, 0, 1, 1, Qt.AlignLeft)
resource_layout.addLayout(bandwidth_row, resource_row_index+1, 1)
else:
self.bandwidth_spin = None # No bandwidth UI if Downloads resource doesn't exist
advanced_layout.addWidget(resource_group)
advanced_layout.addStretch() # Add stretch to push content to top
self.tab_widget.addTab(advanced_tab, "Advanced")
def _toggle_api_key_visibility(self, checked): def _toggle_api_key_visibility(self, checked):
# Always use the same eyeball icon, only change color when toggled # Always use the same eyeball icon, only change color when toggled
@@ -572,13 +565,6 @@ class SettingsDialog(QDialog):
for k, (multithreading_checkbox, max_tasks_spin) in self.resource_edits.items(): for k, (multithreading_checkbox, max_tasks_spin) in self.resource_edits.items():
resource_data = self.resource_settings.get(k, {}) resource_data = self.resource_settings.get(k, {})
resource_data['MaxTasks'] = max_tasks_spin.value() resource_data['MaxTasks'] = max_tasks_spin.value()
# Only add multithreading setting for File Extractor
if k == "File Extractor" and multithreading_checkbox:
if multithreading_checkbox.isChecked():
resource_data['_7zzMultiThread'] = 'on'
else:
# Remove the setting if unchecked (don't add 'off')
resource_data.pop('_7zzMultiThread', None)
self.resource_settings[k] = resource_data self.resource_settings[k] = resource_data
# Save bandwidth limit to Downloads resource MaxThroughput (only if bandwidth UI exists) # Save bandwidth limit to Downloads resource MaxThroughput (only if bandwidth UI exists)

View File

@@ -1757,6 +1757,10 @@ class InstallModlistScreen(QWidget):
MessageService.critical(self, "Steam Restart Failed", "Failed to restart Steam automatically. Please restart Steam manually, then try again.") MessageService.critical(self, "Steam Restart Failed", "Failed to restart Steam automatically. Please restart Steam manually, then try again.")
def start_automated_prefix_workflow(self): def start_automated_prefix_workflow(self):
# Ensure _current_resolution is always set before starting workflow
if not hasattr(self, '_current_resolution') or self._current_resolution is None:
resolution = self.resolution_combo.currentText() if hasattr(self, 'resolution_combo') else None
self._current_resolution = resolution.split()[0] if resolution and resolution != "Leave unchanged" else None
"""Start the automated prefix creation workflow""" """Start the automated prefix creation workflow"""
try: try:
# Disable controls during installation # Disable controls during installation

View File

@@ -222,8 +222,14 @@ class ValidationHandler:
def validate_steam_shortcut(self, app_id: str) -> Tuple[bool, str]: def validate_steam_shortcut(self, app_id: str) -> Tuple[bool, str]:
"""Validate a Steam shortcut.""" """Validate a Steam shortcut."""
try: try:
# Check if shortcuts.vdf exists # Use native Steam service to get proper shortcuts.vdf path with multi-user support
shortcuts_path = Path.home() / '.steam' / 'steam' / 'userdata' / '75424832' / 'config' / 'shortcuts.vdf' from jackify.backend.services.native_steam_service import NativeSteamService
steam_service = NativeSteamService()
shortcuts_path = steam_service.get_shortcuts_vdf_path()
if not shortcuts_path:
return False, "Could not determine shortcuts.vdf path (no active Steam user found)"
if not shortcuts_path.exists(): if not shortcuts_path.exists():
return False, "shortcuts.vdf not found" return False, "shortcuts.vdf not found"