Sync from development - prepare for v0.2.0.6

This commit is contained in:
Omni
2025-12-28 18:52:07 +00:00
parent a813236e51
commit 99fb369d5e
50 changed files with 512 additions and 244 deletions
+13
View File
@@ -1,5 +1,18 @@
# Jackify Changelog # Jackify Changelog
## v0.2.0.6 - Premium Detection and Engine Update
**Release Date:** 2025-12-28
### Engine Updates
- **jackify-engine 0.4.4**: Latest engine version with improvements
### Critical Bug Fixes
- **Premium Detection**: Fixed false Premium errors caused by overly-broad detection pattern triggering on jackify-engine 0.4.3's userinfo JSON output
- **Custom Data Directory**: Fixed AppImage always creating ~/Jackify on startup, even when user configured a custom jackify_data_dir
- **Proton Auto-Selection**: Fixed auto-selection writing invalid "auto" string to config on detection failure
---
## v0.2.0.5 - Emergency OAuth Fix ## v0.2.0.5 - Emergency OAuth Fix
**Release Date:** 2025-12-24 **Release Date:** 2025-12-24
+1 -1
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.2.0.5" __version__ = "0.2.0.6"
@@ -142,6 +142,11 @@ class OAuthTokenHandler:
""" """
try: try:
from Crypto.Cipher import AES from Crypto.Cipher import AES
# Check if MODE_GCM is available (pycryptodome has it, old pycrypto doesn't)
if not hasattr(AES, 'MODE_GCM'):
logger.error("pycryptodome required for token decryption (pycrypto doesn't support MODE_GCM)")
return None
# Derive 32-byte AES key from encryption_key # Derive 32-byte AES key from encryption_key
key = base64.urlsafe_b64decode(self._encryption_key) key = base64.urlsafe_b64decode(self._encryption_key)
@@ -163,6 +168,9 @@ class OAuthTokenHandler:
except ImportError: except ImportError:
logger.error("pycryptodome package not available for token decryption") logger.error("pycryptodome package not available for token decryption")
return None return None
except AttributeError:
logger.error("pycryptodome required for token decryption (pycrypto doesn't support MODE_GCM)")
return None
except Exception as e: except Exception as e:
logger.error(f"Failed to decrypt data: {e}") logger.error(f"Failed to decrypt data: {e}")
return None return None
+7 -4
View File
@@ -48,6 +48,9 @@ def get_clean_subprocess_env(extra_env=None):
env = os.environ.copy() env = os.environ.copy()
# Save APPDIR before removing it (we need it to find bundled tools)
appdir = env.get('APPDIR')
# Remove AppImage-specific variables that can confuse subprocess calls # Remove AppImage-specific variables that can confuse subprocess calls
# These variables cause subprocesses to be interpreted as new AppImage launches # These variables cause subprocesses to be interpreted as new AppImage launches
for key in ['APPIMAGE', 'APPDIR', 'ARGV0', 'OWD']: for key in ['APPIMAGE', 'APPDIR', 'ARGV0', 'OWD']:
@@ -57,10 +60,10 @@ def get_clean_subprocess_env(extra_env=None):
for k in list(env): for k in list(env):
if k.startswith('_MEIPASS'): if k.startswith('_MEIPASS'):
del env[k] del env[k]
# Get current PATH - ensure we preserve system paths # Get current PATH - ensure we preserve system paths
current_path = env.get('PATH', '') current_path = env.get('PATH', '')
# Ensure common system directories are in PATH if not already present # Ensure common system directories are in PATH if not already present
# This is critical for tools like lz4 that might be in /usr/bin, /usr/local/bin, etc. # This is critical for tools like lz4 that might be in /usr/bin, /usr/local/bin, etc.
system_paths = ['/usr/bin', '/usr/local/bin', '/bin', '/sbin', '/usr/sbin'] system_paths = ['/usr/bin', '/usr/local/bin', '/bin', '/sbin', '/usr/sbin']
@@ -68,10 +71,10 @@ def get_clean_subprocess_env(extra_env=None):
for sys_path in system_paths: for sys_path in system_paths:
if sys_path not in path_parts and os.path.isdir(sys_path): if sys_path not in path_parts and os.path.isdir(sys_path):
path_parts.append(sys_path) path_parts.append(sys_path)
# Add bundled tools directory to PATH if running as AppImage # Add bundled tools directory to PATH if running as AppImage
# This ensures lz4, unzip, xz, etc. are available to subprocesses # This ensures lz4, unzip, xz, etc. are available to subprocesses
appdir = env.get('APPDIR') # Note: appdir was saved before env cleanup above
tools_dir = None tools_dir = None
if appdir: if appdir:
@@ -34,9 +34,6 @@ def is_non_premium_indicator(line: str) -> bool:
if phrase in normalized: if phrase in normalized:
return True return True
if "nexus" in normalized and "premium" in normalized:
return True
# Manual download + Nexus URL implies premium requirement in current workflows. # Manual download + Nexus URL implies premium requirement in current workflows.
if "manual download" in normalized and ("nexusmods.com" in normalized or "nexus mods" in normalized): if "manual download" in normalized and ("nexusmods.com" in normalized or "nexus mods" in normalized):
return True return True
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+225 -225
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.4.3": { "jackify-engine/0.4.4": {
"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.4.3", "Wabbajack.CLI.Builder": "0.4.4",
"Wabbajack.Downloaders.Bethesda": "0.4.3", "Wabbajack.Downloaders.Bethesda": "0.4.4",
"Wabbajack.Downloaders.Dispatcher": "0.4.3", "Wabbajack.Downloaders.Dispatcher": "0.4.4",
"Wabbajack.Hashing.xxHash64": "0.4.3", "Wabbajack.Hashing.xxHash64": "0.4.4",
"Wabbajack.Networking.Discord": "0.4.3", "Wabbajack.Networking.Discord": "0.4.4",
"Wabbajack.Networking.GitHub": "0.4.3", "Wabbajack.Networking.GitHub": "0.4.4",
"Wabbajack.Paths.IO": "0.4.3", "Wabbajack.Paths.IO": "0.4.4",
"Wabbajack.Server.Lib": "0.4.3", "Wabbajack.Server.Lib": "0.4.4",
"Wabbajack.Services.OSIntegrated": "0.4.3", "Wabbajack.Services.OSIntegrated": "0.4.4",
"Wabbajack.VFS": "0.4.3", "Wabbajack.VFS": "0.4.4",
"MegaApiClient": "1.0.0.0", "MegaApiClient": "1.0.0.0",
"runtimepack.Microsoft.NETCore.App.Runtime.linux-x64": "8.0.22" "runtimepack.Microsoft.NETCore.App.Runtime.linux-x64": "8.0.22"
}, },
@@ -1781,7 +1781,7 @@
} }
} }
}, },
"Wabbajack.CLI.Builder/0.4.3": { "Wabbajack.CLI.Builder/0.4.4": {
"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.4.3" "Wabbajack.Paths": "0.4.4"
}, },
"runtime": { "runtime": {
"Wabbajack.CLI.Builder.dll": {} "Wabbajack.CLI.Builder.dll": {}
} }
}, },
"Wabbajack.Common/0.4.3": { "Wabbajack.Common/0.4.4": {
"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.4.3", "Wabbajack.DTOs": "0.4.4",
"Wabbajack.Networking.Http": "0.4.3", "Wabbajack.Networking.Http": "0.4.4",
"Wabbajack.Paths.IO": "0.4.3" "Wabbajack.Paths.IO": "0.4.4"
}, },
"runtime": { "runtime": {
"Wabbajack.Common.dll": {} "Wabbajack.Common.dll": {}
} }
}, },
"Wabbajack.Compiler/0.4.3": { "Wabbajack.Compiler/0.4.4": {
"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.4.3", "Wabbajack.Downloaders.Dispatcher": "0.4.4",
"Wabbajack.Installer": "0.4.3", "Wabbajack.Installer": "0.4.4",
"Wabbajack.VFS": "0.4.3", "Wabbajack.VFS": "0.4.4",
"ini-parser-netstandard": "2.5.2" "ini-parser-netstandard": "2.5.2"
}, },
"runtime": { "runtime": {
"Wabbajack.Compiler.dll": {} "Wabbajack.Compiler.dll": {}
} }
}, },
"Wabbajack.Compression.BSA/0.4.3": { "Wabbajack.Compression.BSA/0.4.4": {
"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.4.3", "Wabbajack.Common": "0.4.4",
"Wabbajack.DTOs": "0.4.3" "Wabbajack.DTOs": "0.4.4"
}, },
"runtime": { "runtime": {
"Wabbajack.Compression.BSA.dll": {} "Wabbajack.Compression.BSA.dll": {}
} }
}, },
"Wabbajack.Compression.Zip/0.4.3": { "Wabbajack.Compression.Zip/0.4.4": {
"dependencies": { "dependencies": {
"Wabbajack.IO.Async": "0.4.3" "Wabbajack.IO.Async": "0.4.4"
}, },
"runtime": { "runtime": {
"Wabbajack.Compression.Zip.dll": {} "Wabbajack.Compression.Zip.dll": {}
} }
}, },
"Wabbajack.Configuration/0.4.3": { "Wabbajack.Configuration/0.4.4": {
"runtime": { "runtime": {
"Wabbajack.Configuration.dll": {} "Wabbajack.Configuration.dll": {}
} }
}, },
"Wabbajack.Downloaders.Bethesda/0.4.3": { "Wabbajack.Downloaders.Bethesda/0.4.4": {
"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.4.3", "Wabbajack.Common": "0.4.4",
"Wabbajack.Downloaders.Interfaces": "0.4.3", "Wabbajack.Downloaders.Interfaces": "0.4.4",
"Wabbajack.Networking.BethesdaNet": "0.4.3" "Wabbajack.Networking.BethesdaNet": "0.4.4"
}, },
"runtime": { "runtime": {
"Wabbajack.Downloaders.Bethesda.dll": {} "Wabbajack.Downloaders.Bethesda.dll": {}
} }
}, },
"Wabbajack.Downloaders.Dispatcher/0.4.3": { "Wabbajack.Downloaders.Dispatcher/0.4.4": {
"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.4.3", "Wabbajack.Downloaders.Bethesda": "0.4.4",
"Wabbajack.Downloaders.GameFile": "0.4.3", "Wabbajack.Downloaders.GameFile": "0.4.4",
"Wabbajack.Downloaders.GoogleDrive": "0.4.3", "Wabbajack.Downloaders.GoogleDrive": "0.4.4",
"Wabbajack.Downloaders.Http": "0.4.3", "Wabbajack.Downloaders.Http": "0.4.4",
"Wabbajack.Downloaders.IPS4OAuth2Downloader": "0.4.3", "Wabbajack.Downloaders.IPS4OAuth2Downloader": "0.4.4",
"Wabbajack.Downloaders.Interfaces": "0.4.3", "Wabbajack.Downloaders.Interfaces": "0.4.4",
"Wabbajack.Downloaders.Manual": "0.4.3", "Wabbajack.Downloaders.Manual": "0.4.4",
"Wabbajack.Downloaders.MediaFire": "0.4.3", "Wabbajack.Downloaders.MediaFire": "0.4.4",
"Wabbajack.Downloaders.Mega": "0.4.3", "Wabbajack.Downloaders.Mega": "0.4.4",
"Wabbajack.Downloaders.ModDB": "0.4.3", "Wabbajack.Downloaders.ModDB": "0.4.4",
"Wabbajack.Downloaders.Nexus": "0.4.3", "Wabbajack.Downloaders.Nexus": "0.4.4",
"Wabbajack.Downloaders.VerificationCache": "0.4.3", "Wabbajack.Downloaders.VerificationCache": "0.4.4",
"Wabbajack.Downloaders.WabbajackCDN": "0.4.3", "Wabbajack.Downloaders.WabbajackCDN": "0.4.4",
"Wabbajack.Networking.WabbajackClientApi": "0.4.3" "Wabbajack.Networking.WabbajackClientApi": "0.4.4"
}, },
"runtime": { "runtime": {
"Wabbajack.Downloaders.Dispatcher.dll": {} "Wabbajack.Downloaders.Dispatcher.dll": {}
} }
}, },
"Wabbajack.Downloaders.GameFile/0.4.3": { "Wabbajack.Downloaders.GameFile/0.4.4": {
"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.4.3", "Wabbajack.Downloaders.Interfaces": "0.4.4",
"Wabbajack.VFS": "0.4.3" "Wabbajack.VFS": "0.4.4"
}, },
"runtime": { "runtime": {
"Wabbajack.Downloaders.GameFile.dll": {} "Wabbajack.Downloaders.GameFile.dll": {}
} }
}, },
"Wabbajack.Downloaders.GoogleDrive/0.4.3": { "Wabbajack.Downloaders.GoogleDrive/0.4.4": {
"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.4.3", "Wabbajack.Common": "0.4.4",
"Wabbajack.DTOs": "0.4.3", "Wabbajack.DTOs": "0.4.4",
"Wabbajack.Downloaders.Interfaces": "0.4.3", "Wabbajack.Downloaders.Interfaces": "0.4.4",
"Wabbajack.Networking.Http": "0.4.3", "Wabbajack.Networking.Http": "0.4.4",
"Wabbajack.Networking.Http.Interfaces": "0.4.3" "Wabbajack.Networking.Http.Interfaces": "0.4.4"
}, },
"runtime": { "runtime": {
"Wabbajack.Downloaders.GoogleDrive.dll": {} "Wabbajack.Downloaders.GoogleDrive.dll": {}
} }
}, },
"Wabbajack.Downloaders.Http/0.4.3": { "Wabbajack.Downloaders.Http/0.4.4": {
"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.4.3", "Wabbajack.Common": "0.4.4",
"Wabbajack.DTOs": "0.4.3", "Wabbajack.DTOs": "0.4.4",
"Wabbajack.Downloaders.Interfaces": "0.4.3", "Wabbajack.Downloaders.Interfaces": "0.4.4",
"Wabbajack.Networking.BethesdaNet": "0.4.3", "Wabbajack.Networking.BethesdaNet": "0.4.4",
"Wabbajack.Networking.Http.Interfaces": "0.4.3", "Wabbajack.Networking.Http.Interfaces": "0.4.4",
"Wabbajack.Paths.IO": "0.4.3" "Wabbajack.Paths.IO": "0.4.4"
}, },
"runtime": { "runtime": {
"Wabbajack.Downloaders.Http.dll": {} "Wabbajack.Downloaders.Http.dll": {}
} }
}, },
"Wabbajack.Downloaders.Interfaces/0.4.3": { "Wabbajack.Downloaders.Interfaces/0.4.4": {
"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.4.3", "Wabbajack.Compression.Zip": "0.4.4",
"Wabbajack.DTOs": "0.4.3", "Wabbajack.DTOs": "0.4.4",
"Wabbajack.Paths.IO": "0.4.3" "Wabbajack.Paths.IO": "0.4.4"
}, },
"runtime": { "runtime": {
"Wabbajack.Downloaders.Interfaces.dll": {} "Wabbajack.Downloaders.Interfaces.dll": {}
} }
}, },
"Wabbajack.Downloaders.IPS4OAuth2Downloader/0.4.3": { "Wabbajack.Downloaders.IPS4OAuth2Downloader/0.4.4": {
"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.4.3", "Wabbajack.Common": "0.4.4",
"Wabbajack.Downloaders.Interfaces": "0.4.3", "Wabbajack.Downloaders.Interfaces": "0.4.4",
"Wabbajack.Networking.Http": "0.4.3", "Wabbajack.Networking.Http": "0.4.4",
"Wabbajack.Networking.Http.Interfaces": "0.4.3" "Wabbajack.Networking.Http.Interfaces": "0.4.4"
}, },
"runtime": { "runtime": {
"Wabbajack.Downloaders.IPS4OAuth2Downloader.dll": {} "Wabbajack.Downloaders.IPS4OAuth2Downloader.dll": {}
} }
}, },
"Wabbajack.Downloaders.Manual/0.4.3": { "Wabbajack.Downloaders.Manual/0.4.4": {
"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.4.3", "Wabbajack.Common": "0.4.4",
"Wabbajack.Downloaders.Interfaces": "0.4.3" "Wabbajack.Downloaders.Interfaces": "0.4.4"
}, },
"runtime": { "runtime": {
"Wabbajack.Downloaders.Manual.dll": {} "Wabbajack.Downloaders.Manual.dll": {}
} }
}, },
"Wabbajack.Downloaders.MediaFire/0.4.3": { "Wabbajack.Downloaders.MediaFire/0.4.4": {
"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.4.3", "Wabbajack.Common": "0.4.4",
"Wabbajack.Downloaders.Interfaces": "0.4.3", "Wabbajack.Downloaders.Interfaces": "0.4.4",
"Wabbajack.Networking.Http.Interfaces": "0.4.3" "Wabbajack.Networking.Http.Interfaces": "0.4.4"
}, },
"runtime": { "runtime": {
"Wabbajack.Downloaders.MediaFire.dll": {} "Wabbajack.Downloaders.MediaFire.dll": {}
} }
}, },
"Wabbajack.Downloaders.Mega/0.4.3": { "Wabbajack.Downloaders.Mega/0.4.4": {
"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.4.3", "Wabbajack.Common": "0.4.4",
"Wabbajack.Downloaders.Interfaces": "0.4.3", "Wabbajack.Downloaders.Interfaces": "0.4.4",
"Wabbajack.Paths.IO": "0.4.3" "Wabbajack.Paths.IO": "0.4.4"
}, },
"runtime": { "runtime": {
"Wabbajack.Downloaders.Mega.dll": {} "Wabbajack.Downloaders.Mega.dll": {}
} }
}, },
"Wabbajack.Downloaders.ModDB/0.4.3": { "Wabbajack.Downloaders.ModDB/0.4.4": {
"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.4.3", "Wabbajack.Common": "0.4.4",
"Wabbajack.Downloaders.Interfaces": "0.4.3", "Wabbajack.Downloaders.Interfaces": "0.4.4",
"Wabbajack.Networking.Http": "0.4.3", "Wabbajack.Networking.Http": "0.4.4",
"Wabbajack.Networking.Http.Interfaces": "0.4.3" "Wabbajack.Networking.Http.Interfaces": "0.4.4"
}, },
"runtime": { "runtime": {
"Wabbajack.Downloaders.ModDB.dll": {} "Wabbajack.Downloaders.ModDB.dll": {}
} }
}, },
"Wabbajack.Downloaders.Nexus/0.4.3": { "Wabbajack.Downloaders.Nexus/0.4.4": {
"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.4.3", "Wabbajack.DTOs": "0.4.4",
"Wabbajack.Downloaders.Interfaces": "0.4.3", "Wabbajack.Downloaders.Interfaces": "0.4.4",
"Wabbajack.Hashing.xxHash64": "0.4.3", "Wabbajack.Hashing.xxHash64": "0.4.4",
"Wabbajack.Networking.Http": "0.4.3", "Wabbajack.Networking.Http": "0.4.4",
"Wabbajack.Networking.Http.Interfaces": "0.4.3", "Wabbajack.Networking.Http.Interfaces": "0.4.4",
"Wabbajack.Networking.NexusApi": "0.4.3", "Wabbajack.Networking.NexusApi": "0.4.4",
"Wabbajack.Paths": "0.4.3" "Wabbajack.Paths": "0.4.4"
}, },
"runtime": { "runtime": {
"Wabbajack.Downloaders.Nexus.dll": {} "Wabbajack.Downloaders.Nexus.dll": {}
} }
}, },
"Wabbajack.Downloaders.VerificationCache/0.4.3": { "Wabbajack.Downloaders.VerificationCache/0.4.4": {
"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.4.3", "Wabbajack.DTOs": "0.4.4",
"Wabbajack.Paths.IO": "0.4.3" "Wabbajack.Paths.IO": "0.4.4"
}, },
"runtime": { "runtime": {
"Wabbajack.Downloaders.VerificationCache.dll": {} "Wabbajack.Downloaders.VerificationCache.dll": {}
} }
}, },
"Wabbajack.Downloaders.WabbajackCDN/0.4.3": { "Wabbajack.Downloaders.WabbajackCDN/0.4.4": {
"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.4.3", "Wabbajack.Common": "0.4.4",
"Wabbajack.Downloaders.Interfaces": "0.4.3", "Wabbajack.Downloaders.Interfaces": "0.4.4",
"Wabbajack.Networking.Http": "0.4.3", "Wabbajack.Networking.Http": "0.4.4",
"Wabbajack.RateLimiter": "0.4.3" "Wabbajack.RateLimiter": "0.4.4"
}, },
"runtime": { "runtime": {
"Wabbajack.Downloaders.WabbajackCDN.dll": {} "Wabbajack.Downloaders.WabbajackCDN.dll": {}
} }
}, },
"Wabbajack.DTOs/0.4.3": { "Wabbajack.DTOs/0.4.4": {
"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.4.3", "Wabbajack.Hashing.xxHash64": "0.4.4",
"Wabbajack.Paths": "0.4.3" "Wabbajack.Paths": "0.4.4"
}, },
"runtime": { "runtime": {
"Wabbajack.DTOs.dll": {} "Wabbajack.DTOs.dll": {}
} }
}, },
"Wabbajack.FileExtractor/0.4.3": { "Wabbajack.FileExtractor/0.4.4": {
"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.4.3", "Wabbajack.Common": "0.4.4",
"Wabbajack.Compression.BSA": "0.4.3", "Wabbajack.Compression.BSA": "0.4.4",
"Wabbajack.Hashing.PHash": "0.4.3", "Wabbajack.Hashing.PHash": "0.4.4",
"Wabbajack.Paths": "0.4.3" "Wabbajack.Paths": "0.4.4"
}, },
"runtime": { "runtime": {
"Wabbajack.FileExtractor.dll": {} "Wabbajack.FileExtractor.dll": {}
} }
}, },
"Wabbajack.Hashing.PHash/0.4.3": { "Wabbajack.Hashing.PHash/0.4.4": {
"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.4.3", "Wabbajack.Common": "0.4.4",
"Wabbajack.DTOs": "0.4.3", "Wabbajack.DTOs": "0.4.4",
"Wabbajack.Paths": "0.4.3", "Wabbajack.Paths": "0.4.4",
"Wabbajack.Paths.IO": "0.4.3" "Wabbajack.Paths.IO": "0.4.4"
}, },
"runtime": { "runtime": {
"Wabbajack.Hashing.PHash.dll": {} "Wabbajack.Hashing.PHash.dll": {}
} }
}, },
"Wabbajack.Hashing.xxHash64/0.4.3": { "Wabbajack.Hashing.xxHash64/0.4.4": {
"dependencies": { "dependencies": {
"Wabbajack.Paths": "0.4.3", "Wabbajack.Paths": "0.4.4",
"Wabbajack.RateLimiter": "0.4.3" "Wabbajack.RateLimiter": "0.4.4"
}, },
"runtime": { "runtime": {
"Wabbajack.Hashing.xxHash64.dll": {} "Wabbajack.Hashing.xxHash64.dll": {}
} }
}, },
"Wabbajack.Installer/0.4.3": { "Wabbajack.Installer/0.4.4": {
"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.4.3", "Wabbajack.DTOs": "0.4.4",
"Wabbajack.Downloaders.Dispatcher": "0.4.3", "Wabbajack.Downloaders.Dispatcher": "0.4.4",
"Wabbajack.Downloaders.GameFile": "0.4.3", "Wabbajack.Downloaders.GameFile": "0.4.4",
"Wabbajack.FileExtractor": "0.4.3", "Wabbajack.FileExtractor": "0.4.4",
"Wabbajack.Networking.WabbajackClientApi": "0.4.3", "Wabbajack.Networking.WabbajackClientApi": "0.4.4",
"Wabbajack.Paths": "0.4.3", "Wabbajack.Paths": "0.4.4",
"Wabbajack.Paths.IO": "0.4.3", "Wabbajack.Paths.IO": "0.4.4",
"Wabbajack.VFS": "0.4.3", "Wabbajack.VFS": "0.4.4",
"ini-parser-netstandard": "2.5.2" "ini-parser-netstandard": "2.5.2"
}, },
"runtime": { "runtime": {
"Wabbajack.Installer.dll": {} "Wabbajack.Installer.dll": {}
} }
}, },
"Wabbajack.IO.Async/0.4.3": { "Wabbajack.IO.Async/0.4.4": {
"runtime": { "runtime": {
"Wabbajack.IO.Async.dll": {} "Wabbajack.IO.Async.dll": {}
} }
}, },
"Wabbajack.Networking.BethesdaNet/0.4.3": { "Wabbajack.Networking.BethesdaNet/0.4.4": {
"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.4.3", "Wabbajack.DTOs": "0.4.4",
"Wabbajack.Networking.Http": "0.4.3", "Wabbajack.Networking.Http": "0.4.4",
"Wabbajack.Networking.Http.Interfaces": "0.4.3" "Wabbajack.Networking.Http.Interfaces": "0.4.4"
}, },
"runtime": { "runtime": {
"Wabbajack.Networking.BethesdaNet.dll": {} "Wabbajack.Networking.BethesdaNet.dll": {}
} }
}, },
"Wabbajack.Networking.Discord/0.4.3": { "Wabbajack.Networking.Discord/0.4.4": {
"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.4.3" "Wabbajack.Networking.Http.Interfaces": "0.4.4"
}, },
"runtime": { "runtime": {
"Wabbajack.Networking.Discord.dll": {} "Wabbajack.Networking.Discord.dll": {}
} }
}, },
"Wabbajack.Networking.GitHub/0.4.3": { "Wabbajack.Networking.GitHub/0.4.4": {
"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.4.3", "Wabbajack.DTOs": "0.4.4",
"Wabbajack.Networking.Http.Interfaces": "0.4.3" "Wabbajack.Networking.Http.Interfaces": "0.4.4"
}, },
"runtime": { "runtime": {
"Wabbajack.Networking.GitHub.dll": {} "Wabbajack.Networking.GitHub.dll": {}
} }
}, },
"Wabbajack.Networking.Http/0.4.3": { "Wabbajack.Networking.Http/0.4.4": {
"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.4.3", "Wabbajack.Configuration": "0.4.4",
"Wabbajack.Downloaders.Interfaces": "0.4.3", "Wabbajack.Downloaders.Interfaces": "0.4.4",
"Wabbajack.Hashing.xxHash64": "0.4.3", "Wabbajack.Hashing.xxHash64": "0.4.4",
"Wabbajack.Networking.Http.Interfaces": "0.4.3", "Wabbajack.Networking.Http.Interfaces": "0.4.4",
"Wabbajack.Paths": "0.4.3", "Wabbajack.Paths": "0.4.4",
"Wabbajack.Paths.IO": "0.4.3" "Wabbajack.Paths.IO": "0.4.4"
}, },
"runtime": { "runtime": {
"Wabbajack.Networking.Http.dll": {} "Wabbajack.Networking.Http.dll": {}
} }
}, },
"Wabbajack.Networking.Http.Interfaces/0.4.3": { "Wabbajack.Networking.Http.Interfaces/0.4.4": {
"dependencies": { "dependencies": {
"Wabbajack.Hashing.xxHash64": "0.4.3" "Wabbajack.Hashing.xxHash64": "0.4.4"
}, },
"runtime": { "runtime": {
"Wabbajack.Networking.Http.Interfaces.dll": {} "Wabbajack.Networking.Http.Interfaces.dll": {}
} }
}, },
"Wabbajack.Networking.NexusApi/0.4.3": { "Wabbajack.Networking.NexusApi/0.4.4": {
"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.4.3", "Wabbajack.DTOs": "0.4.4",
"Wabbajack.Networking.Http": "0.4.3", "Wabbajack.Networking.Http": "0.4.4",
"Wabbajack.Networking.Http.Interfaces": "0.4.3", "Wabbajack.Networking.Http.Interfaces": "0.4.4",
"Wabbajack.Networking.WabbajackClientApi": "0.4.3" "Wabbajack.Networking.WabbajackClientApi": "0.4.4"
}, },
"runtime": { "runtime": {
"Wabbajack.Networking.NexusApi.dll": {} "Wabbajack.Networking.NexusApi.dll": {}
} }
}, },
"Wabbajack.Networking.WabbajackClientApi/0.4.3": { "Wabbajack.Networking.WabbajackClientApi/0.4.4": {
"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.4.3", "Wabbajack.Common": "0.4.4",
"Wabbajack.DTOs": "0.4.3", "Wabbajack.DTOs": "0.4.4",
"Wabbajack.Paths.IO": "0.4.3", "Wabbajack.Paths.IO": "0.4.4",
"Wabbajack.VFS.Interfaces": "0.4.3", "Wabbajack.VFS.Interfaces": "0.4.4",
"YamlDotNet": "16.3.0" "YamlDotNet": "16.3.0"
}, },
"runtime": { "runtime": {
"Wabbajack.Networking.WabbajackClientApi.dll": {} "Wabbajack.Networking.WabbajackClientApi.dll": {}
} }
}, },
"Wabbajack.Paths/0.4.3": { "Wabbajack.Paths/0.4.4": {
"runtime": { "runtime": {
"Wabbajack.Paths.dll": {} "Wabbajack.Paths.dll": {}
} }
}, },
"Wabbajack.Paths.IO/0.4.3": { "Wabbajack.Paths.IO/0.4.4": {
"dependencies": { "dependencies": {
"Wabbajack.Paths": "0.4.3", "Wabbajack.Paths": "0.4.4",
"shortid": "4.0.0" "shortid": "4.0.0"
}, },
"runtime": { "runtime": {
"Wabbajack.Paths.IO.dll": {} "Wabbajack.Paths.IO.dll": {}
} }
}, },
"Wabbajack.RateLimiter/0.4.3": { "Wabbajack.RateLimiter/0.4.4": {
"runtime": { "runtime": {
"Wabbajack.RateLimiter.dll": {} "Wabbajack.RateLimiter.dll": {}
} }
}, },
"Wabbajack.Server.Lib/0.4.3": { "Wabbajack.Server.Lib/0.4.4": {
"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.4.3", "Wabbajack.Common": "0.4.4",
"Wabbajack.Networking.Http.Interfaces": "0.4.3", "Wabbajack.Networking.Http.Interfaces": "0.4.4",
"Wabbajack.Services.OSIntegrated": "0.4.3" "Wabbajack.Services.OSIntegrated": "0.4.4"
}, },
"runtime": { "runtime": {
"Wabbajack.Server.Lib.dll": {} "Wabbajack.Server.Lib.dll": {}
} }
}, },
"Wabbajack.Services.OSIntegrated/0.4.3": { "Wabbajack.Services.OSIntegrated/0.4.4": {
"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.4.3", "Wabbajack.Compiler": "0.4.4",
"Wabbajack.Downloaders.Dispatcher": "0.4.3", "Wabbajack.Downloaders.Dispatcher": "0.4.4",
"Wabbajack.Installer": "0.4.3", "Wabbajack.Installer": "0.4.4",
"Wabbajack.Networking.BethesdaNet": "0.4.3", "Wabbajack.Networking.BethesdaNet": "0.4.4",
"Wabbajack.Networking.Discord": "0.4.3", "Wabbajack.Networking.Discord": "0.4.4",
"Wabbajack.VFS": "0.4.3" "Wabbajack.VFS": "0.4.4"
}, },
"runtime": { "runtime": {
"Wabbajack.Services.OSIntegrated.dll": {} "Wabbajack.Services.OSIntegrated.dll": {}
} }
}, },
"Wabbajack.VFS/0.4.3": { "Wabbajack.VFS/0.4.4": {
"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.4.3", "Wabbajack.Common": "0.4.4",
"Wabbajack.FileExtractor": "0.4.3", "Wabbajack.FileExtractor": "0.4.4",
"Wabbajack.Hashing.PHash": "0.4.3", "Wabbajack.Hashing.PHash": "0.4.4",
"Wabbajack.Hashing.xxHash64": "0.4.3", "Wabbajack.Hashing.xxHash64": "0.4.4",
"Wabbajack.Paths": "0.4.3", "Wabbajack.Paths": "0.4.4",
"Wabbajack.Paths.IO": "0.4.3", "Wabbajack.Paths.IO": "0.4.4",
"Wabbajack.VFS.Interfaces": "0.4.3" "Wabbajack.VFS.Interfaces": "0.4.4"
}, },
"runtime": { "runtime": {
"Wabbajack.VFS.dll": {} "Wabbajack.VFS.dll": {}
} }
}, },
"Wabbajack.VFS.Interfaces/0.4.3": { "Wabbajack.VFS.Interfaces/0.4.4": {
"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.4.3", "Wabbajack.DTOs": "0.4.4",
"Wabbajack.Hashing.xxHash64": "0.4.3", "Wabbajack.Hashing.xxHash64": "0.4.4",
"Wabbajack.Paths": "0.4.3" "Wabbajack.Paths": "0.4.4"
}, },
"runtime": { "runtime": {
"Wabbajack.VFS.Interfaces.dll": {} "Wabbajack.VFS.Interfaces.dll": {}
@@ -2332,7 +2332,7 @@
} }
}, },
"libraries": { "libraries": {
"jackify-engine/0.4.3": { "jackify-engine/0.4.4": {
"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.4.3": { "Wabbajack.CLI.Builder/0.4.4": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Common/0.4.3": { "Wabbajack.Common/0.4.4": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Compiler/0.4.3": { "Wabbajack.Compiler/0.4.4": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Compression.BSA/0.4.3": { "Wabbajack.Compression.BSA/0.4.4": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Compression.Zip/0.4.3": { "Wabbajack.Compression.Zip/0.4.4": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Configuration/0.4.3": { "Wabbajack.Configuration/0.4.4": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Downloaders.Bethesda/0.4.3": { "Wabbajack.Downloaders.Bethesda/0.4.4": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Downloaders.Dispatcher/0.4.3": { "Wabbajack.Downloaders.Dispatcher/0.4.4": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Downloaders.GameFile/0.4.3": { "Wabbajack.Downloaders.GameFile/0.4.4": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Downloaders.GoogleDrive/0.4.3": { "Wabbajack.Downloaders.GoogleDrive/0.4.4": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Downloaders.Http/0.4.3": { "Wabbajack.Downloaders.Http/0.4.4": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Downloaders.Interfaces/0.4.3": { "Wabbajack.Downloaders.Interfaces/0.4.4": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Downloaders.IPS4OAuth2Downloader/0.4.3": { "Wabbajack.Downloaders.IPS4OAuth2Downloader/0.4.4": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Downloaders.Manual/0.4.3": { "Wabbajack.Downloaders.Manual/0.4.4": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Downloaders.MediaFire/0.4.3": { "Wabbajack.Downloaders.MediaFire/0.4.4": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Downloaders.Mega/0.4.3": { "Wabbajack.Downloaders.Mega/0.4.4": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Downloaders.ModDB/0.4.3": { "Wabbajack.Downloaders.ModDB/0.4.4": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Downloaders.Nexus/0.4.3": { "Wabbajack.Downloaders.Nexus/0.4.4": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Downloaders.VerificationCache/0.4.3": { "Wabbajack.Downloaders.VerificationCache/0.4.4": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Downloaders.WabbajackCDN/0.4.3": { "Wabbajack.Downloaders.WabbajackCDN/0.4.4": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.DTOs/0.4.3": { "Wabbajack.DTOs/0.4.4": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.FileExtractor/0.4.3": { "Wabbajack.FileExtractor/0.4.4": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Hashing.PHash/0.4.3": { "Wabbajack.Hashing.PHash/0.4.4": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Hashing.xxHash64/0.4.3": { "Wabbajack.Hashing.xxHash64/0.4.4": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Installer/0.4.3": { "Wabbajack.Installer/0.4.4": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.IO.Async/0.4.3": { "Wabbajack.IO.Async/0.4.4": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Networking.BethesdaNet/0.4.3": { "Wabbajack.Networking.BethesdaNet/0.4.4": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Networking.Discord/0.4.3": { "Wabbajack.Networking.Discord/0.4.4": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Networking.GitHub/0.4.3": { "Wabbajack.Networking.GitHub/0.4.4": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Networking.Http/0.4.3": { "Wabbajack.Networking.Http/0.4.4": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Networking.Http.Interfaces/0.4.3": { "Wabbajack.Networking.Http.Interfaces/0.4.4": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Networking.NexusApi/0.4.3": { "Wabbajack.Networking.NexusApi/0.4.4": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Networking.WabbajackClientApi/0.4.3": { "Wabbajack.Networking.WabbajackClientApi/0.4.4": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Paths/0.4.3": { "Wabbajack.Paths/0.4.4": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Paths.IO/0.4.3": { "Wabbajack.Paths.IO/0.4.4": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.RateLimiter/0.4.3": { "Wabbajack.RateLimiter/0.4.4": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Server.Lib/0.4.3": { "Wabbajack.Server.Lib/0.4.4": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.Services.OSIntegrated/0.4.3": { "Wabbajack.Services.OSIntegrated/0.4.4": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.VFS/0.4.3": { "Wabbajack.VFS/0.4.4": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Wabbajack.VFS.Interfaces/0.4.3": { "Wabbajack.VFS.Interfaces/0.4.4": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
Binary file not shown.
+66 -9
View File
@@ -904,20 +904,22 @@ class SettingsDialog(QDialog):
if best_proton: if best_proton:
resolved_install_path = str(best_proton['path']) resolved_install_path = str(best_proton['path'])
resolved_install_version = best_proton['name'] resolved_install_version = best_proton['name']
self.config_handler.set("proton_path", resolved_install_path)
self.config_handler.set("proton_version", resolved_install_version)
else: else:
resolved_install_path = "auto" # No Proton found - don't write anything, let engine auto-detect
resolved_install_version = "auto" logger.warning("Auto Proton selection failed: No Proton versions found")
except: # Don't modify existing config values
resolved_install_path = "auto" except Exception as e:
resolved_install_version = "auto" # Exception during detection - log it and don't write anything
logger.error(f"Auto Proton selection failed with exception: {e}", exc_info=True)
# Don't modify existing config values
else: else:
# User selected specific Proton version # User selected specific Proton version
resolved_install_path = selected_install_proton_path resolved_install_path = selected_install_proton_path
# Extract version from dropdown text
resolved_install_version = self.install_proton_dropdown.currentText() resolved_install_version = self.install_proton_dropdown.currentText()
self.config_handler.set("proton_path", resolved_install_path)
self.config_handler.set("proton_path", resolved_install_path) self.config_handler.set("proton_version", resolved_install_version)
self.config_handler.set("proton_version", resolved_install_version)
# Save Game Proton selection # Save Game Proton selection
selected_game_proton_path = self.game_proton_dropdown.currentData() selected_game_proton_path = self.game_proton_dropdown.currentData()
@@ -1038,6 +1040,10 @@ class JackifyMainWindow(QMainWindow):
self._details_extra_height = 360 self._details_extra_height = 360
self._initial_show_adjusted = False self._initial_show_adjusted = False
# Track open dialogs to prevent duplicates
self._settings_dialog = None
self._about_dialog = None
# Ensure GNOME/Ubuntu exposes full set of window controls (avoid hidden buttons) # Ensure GNOME/Ubuntu exposes full set of window controls (avoid hidden buttons)
self._apply_standard_window_flags() self._apply_standard_window_flags()
try: try:
@@ -1606,23 +1612,74 @@ class JackifyMainWindow(QMainWindow):
event.accept() event.accept()
def open_settings_dialog(self): def open_settings_dialog(self):
"""Open settings dialog, preventing duplicate instances"""
try: try:
# Check if dialog already exists and is visible
if self._settings_dialog is not None:
try:
if self._settings_dialog.isVisible():
# Dialog is already open - raise it to front
self._settings_dialog.raise_()
self._settings_dialog.activateWindow()
return
else:
# Dialog exists but is closed - clean up reference
self._settings_dialog = None
except RuntimeError:
# Dialog was deleted - clean up reference
self._settings_dialog = None
# Create new dialog
dlg = SettingsDialog(self) dlg = SettingsDialog(self)
self._settings_dialog = dlg
# Clean up reference when dialog is closed
def on_dialog_finished():
self._settings_dialog = None
dlg.finished.connect(on_dialog_finished)
dlg.exec() dlg.exec()
except Exception as e: except Exception as e:
print(f"[ERROR] Exception in open_settings_dialog: {e}") print(f"[ERROR] Exception in open_settings_dialog: {e}")
import traceback import traceback
traceback.print_exc() traceback.print_exc()
self._settings_dialog = None
def open_about_dialog(self): def open_about_dialog(self):
"""Open about dialog, preventing duplicate instances"""
try: try:
from jackify.frontends.gui.dialogs.about_dialog import AboutDialog from jackify.frontends.gui.dialogs.about_dialog import AboutDialog
# Check if dialog already exists and is visible
if self._about_dialog is not None:
try:
if self._about_dialog.isVisible():
# Dialog is already open - raise it to front
self._about_dialog.raise_()
self._about_dialog.activateWindow()
return
else:
# Dialog exists but is closed - clean up reference
self._about_dialog = None
except RuntimeError:
# Dialog was deleted - clean up reference
self._about_dialog = None
# Create new dialog
dlg = AboutDialog(self.system_info, self) dlg = AboutDialog(self.system_info, self)
self._about_dialog = dlg
# Clean up reference when dialog is closed
def on_dialog_finished():
self._about_dialog = None
dlg.finished.connect(on_dialog_finished)
dlg.exec() dlg.exec()
except Exception as e: except Exception as e:
print(f"[ERROR] Exception in open_about_dialog: {e}") print(f"[ERROR] Exception in open_about_dialog: {e}")
import traceback import traceback
traceback.print_exc() traceback.print_exc()
self._about_dialog = None
def _open_url(self, url: str): def _open_url(self, url: str):
"""Open URL with clean environment to avoid AppImage library conflicts.""" """Open URL with clean environment to avoid AppImage library conflicts."""
@@ -833,6 +833,8 @@ class ModlistGalleryDialog(QDialog):
self._validation_update_timer = None # Timer for background validation updates self._validation_update_timer = None # Timer for background validation updates
self._setup_ui() self._setup_ui()
# Disable filter controls during initial load to prevent race conditions
self._set_filter_controls_enabled(False)
# Lazy load - fetch modlists when dialog is shown # Lazy load - fetch modlists when dialog is shown
def _apply_initial_size(self): def _apply_initial_size(self):
@@ -1168,6 +1170,9 @@ class ModlistGalleryDialog(QDialog):
# Reconnect filter handler # Reconnect filter handler
self.game_combo.currentIndexChanged.connect(self._apply_filters) self.game_combo.currentIndexChanged.connect(self._apply_filters)
# Enable filter controls now that data is loaded
self._set_filter_controls_enabled(True)
# Apply filters (will show all modlists for selected game initially) # Apply filters (will show all modlists for selected game initially)
self._apply_filters() self._apply_filters()
@@ -1389,8 +1394,23 @@ class ModlistGalleryDialog(QDialog):
self._filter_mods_list() # Refresh mod list based on NSFW state self._filter_mods_list() # Refresh mod list based on NSFW state
self._apply_filters() # Apply all filters self._apply_filters() # Apply all filters
def _set_filter_controls_enabled(self, enabled: bool):
"""Enable or disable all filter controls"""
self.search_box.setEnabled(enabled)
self.game_combo.setEnabled(enabled)
self.show_official_only.setEnabled(enabled)
self.show_nsfw.setEnabled(enabled)
self.hide_unavailable.setEnabled(enabled)
self.tags_list.setEnabled(enabled)
self.mod_search.setEnabled(enabled)
self.mods_list.setEnabled(enabled)
def _apply_filters(self): def _apply_filters(self):
"""Apply current filters to modlist display""" """Apply current filters to modlist display"""
# CRITICAL: Guard against race condition - don't filter if modlists aren't loaded yet
if not self.all_modlists:
return
filtered = self.all_modlists filtered = self.all_modlists
# Search filter # Search filter
@@ -1480,15 +1500,25 @@ class ModlistGalleryDialog(QDialog):
def _update_grid(self): def _update_grid(self):
"""Update grid by removing all cards and re-adding only visible ones""" """Update grid by removing all cards and re-adding only visible ones"""
# CRITICAL: Guard against race condition - don't update if cards aren't ready yet
if not self.all_cards:
return
# Disable updates during grid update # Disable updates during grid update
self.grid_widget.setUpdatesEnabled(False) self.grid_widget.setUpdatesEnabled(False)
try: try:
# Remove all cards from layout # Remove all cards from layout
# CRITICAL FIX: Properly remove widgets to prevent overlapping and orphaned windows
# We need to explicitly remove widgets from the layout before taking items
# to ensure they're fully cleaned up, but we don't setParent(None) because
# widgets are immediately re-added to the grid (Qt will reparent them).
while self.grid_layout.count(): while self.grid_layout.count():
item = self.grid_layout.takeAt(0) item = self.grid_layout.takeAt(0)
if item.widget(): widget = item.widget() if item else None
item.widget().setParent(None) if widget:
# Explicitly remove widget from layout to prevent overlapping
self.grid_layout.removeWidget(widget)
del item del item
# Calculate number of columns based on available width # Calculate number of columns based on available width
@@ -1528,6 +1558,16 @@ class ModlistGalleryDialog(QDialog):
card = self.all_cards.get(modlist.machineURL) card = self.all_cards.get(modlist.machineURL)
if card: if card:
# Ensure widget is not already in the layout (prevent overlapping)
# If it is, remove it first (shouldn't happen after takeAt, but safety check)
if card.parent() == self.grid_widget:
# Widget is already a child of grid_widget, check if it's in layout
for i in range(self.grid_layout.count()):
item = self.grid_layout.itemAt(i)
if item and item.widget() == card:
# Already in layout, remove it first
self.grid_layout.removeWidget(card)
break
self.grid_layout.addWidget(card, row, col) self.grid_layout.addWidget(card, row, col)
# Set column stretch - don't stretch card columns, but add a spacer column # Set column stretch - don't stretch card columns, but add a spacer column
@@ -20,6 +20,13 @@ from PySide6.QtGui import QFont
from jackify.shared.progress_models import FileProgress, OperationType from jackify.shared.progress_models import FileProgress, OperationType
from ..shared_theme import JACKIFY_COLOR_BLUE from ..shared_theme import JACKIFY_COLOR_BLUE
def _debug_log(message):
"""Log message only if debug mode is enabled"""
from jackify.backend.handlers.config_handler import ConfigHandler
config_handler = ConfigHandler()
if config_handler.get('debug_mode', False):
print(message)
class SummaryProgressWidget(QWidget): class SummaryProgressWidget(QWidget):
"""Widget showing summary progress for phases like Installing.""" """Widget showing summary progress for phases like Installing."""
@@ -484,7 +491,28 @@ class FileProgressList(QWidget):
return return
# Widget doesn't exist - create it (only clear when creating new widget) # Widget doesn't exist - create it (only clear when creating new widget)
# CRITICAL FIX: Remove all item widgets before clear() to prevent orphaned widgets
_debug_log(f"[WIDGET_FIX] About to clear list_widget for summary widget - count={self.list_widget.count()}")
for i in range(self.list_widget.count()):
item = self.list_widget.item(i)
if item:
widget = self.list_widget.itemWidget(item)
if widget:
_debug_log(f"[WIDGET_FIX] Removing widget before clear (summary) - widget={widget}, parent={widget.parent()}, isWindow()={widget.isWindow()}")
self.list_widget.removeItemWidget(item)
if widget.isWindow():
print(f"[WIDGET_FIX] ERROR: Widget became top-level window after removeItemWidget() before clear()!")
import traceback
traceback.print_stack()
self.list_widget.clear() self.list_widget.clear()
# Check widgets in _file_items dict after clear
for key, widget in list(self._file_items.items()):
if widget:
_debug_log(f"[WIDGET_FIX] Widget in _file_items after clear - key={key}, widget={widget}, parent={widget.parent()}, isWindow()={widget.isWindow()}")
if widget.isWindow():
print(f"[WIDGET_FIX] ERROR: Widget in _file_items is a top-level window after clear()! This is the bug!")
import traceback
traceback.print_stack()
self._file_items.clear() self._file_items.clear()
# Create new summary widget # Create new summary widget
@@ -510,7 +538,22 @@ class FileProgressList(QWidget):
for i in range(self.list_widget.count()): for i in range(self.list_widget.count()):
item = self.list_widget.item(i) item = self.list_widget.item(i)
if item and item.data(Qt.UserRole) == "__summary__": if item and item.data(Qt.UserRole) == "__summary__":
# CRITICAL FIX: Call removeItemWidget() before takeItem() to prevent orphaned widgets
widget = self.list_widget.itemWidget(item)
if widget:
_debug_log(f"[WIDGET_FIX] Removing summary widget - widget={widget}, parent={widget.parent()}, isWindow()={widget.isWindow()}")
self.list_widget.removeItemWidget(item)
if widget.isWindow():
print(f"[WIDGET_FIX] ERROR: Summary widget became top-level window after removeItemWidget()!")
import traceback
traceback.print_stack()
self.list_widget.takeItem(i) self.list_widget.takeItem(i)
if widget:
_debug_log(f"[WIDGET_FIX] After takeItem (summary) - widget.parent()={widget.parent()}, isWindow()={widget.isWindow()}")
if widget.isWindow():
print(f"[WIDGET_FIX] ERROR: Summary widget is still a top-level window after takeItem()!")
import traceback
traceback.print_stack()
break break
self._summary_widget = None self._summary_widget = None
else: else:
@@ -522,7 +565,22 @@ class FileProgressList(QWidget):
for i in range(self.list_widget.count()): for i in range(self.list_widget.count()):
item = self.list_widget.item(i) item = self.list_widget.item(i)
if item and item.data(Qt.UserRole) == "__transition__": if item and item.data(Qt.UserRole) == "__transition__":
# CRITICAL FIX: Call removeItemWidget() before takeItem() to prevent orphaned widgets
widget = self.list_widget.itemWidget(item)
if widget:
_debug_log(f"[WIDGET_FIX] Removing transition label - widget={widget}, parent={widget.parent()}, isWindow()={widget.isWindow()}")
self.list_widget.removeItemWidget(item)
if widget.isWindow():
print(f"[WIDGET_FIX] ERROR: Transition label became top-level window after removeItemWidget()!")
import traceback
traceback.print_stack()
self.list_widget.takeItem(i) self.list_widget.takeItem(i)
if widget:
_debug_log(f"[WIDGET_FIX] After takeItem (transition) - widget.parent()={widget.parent()}, isWindow()={widget.isWindow()}")
if widget.isWindow():
print(f"[WIDGET_FIX] ERROR: Transition label is still a top-level window after takeItem()!")
import traceback
traceback.print_stack()
break break
self._transition_label = None self._transition_label = None
@@ -533,7 +591,28 @@ class FileProgressList(QWidget):
self._show_transition_message(current_phase) self._show_transition_message(current_phase)
else: else:
# Show empty state but keep header stable # Show empty state but keep header stable
# CRITICAL FIX: Remove all item widgets before clear() to prevent orphaned widgets
_debug_log(f"[WIDGET_FIX] About to clear list_widget (empty state) - count={self.list_widget.count()}")
for i in range(self.list_widget.count()):
item = self.list_widget.item(i)
if item:
widget = self.list_widget.itemWidget(item)
if widget:
_debug_log(f"[WIDGET_FIX] Removing widget before clear (empty) - widget={widget}, parent={widget.parent()}, isWindow()={widget.isWindow()}")
self.list_widget.removeItemWidget(item)
if widget.isWindow():
print(f"[WIDGET_FIX] ERROR: Widget became top-level window after removeItemWidget() before clear()!")
import traceback
traceback.print_stack()
self.list_widget.clear() self.list_widget.clear()
# Check widgets in _file_items dict after clear
for key, widget in list(self._file_items.items()):
if widget:
_debug_log(f"[WIDGET_FIX] Widget in _file_items after clear (empty) - key={key}, widget={widget}, parent={widget.parent()}, isWindow()={widget.isWindow()}")
if widget.isWindow():
print(f"[WIDGET_FIX] ERROR: Widget in _file_items is a top-level window after clear()! This is the bug!")
import traceback
traceback.print_stack()
self._file_items.clear() self._file_items.clear()
# Update last phase tracker # Update last phase tracker
@@ -579,7 +658,24 @@ class FileProgressList(QWidget):
for i in range(self.list_widget.count()): for i in range(self.list_widget.count()):
item = self.list_widget.item(i) item = self.list_widget.item(i)
if item and item.data(Qt.UserRole) == item_key: if item and item.data(Qt.UserRole) == item_key:
# CRITICAL FIX: Call removeItemWidget() before takeItem() to prevent orphaned widgets
widget = self.list_widget.itemWidget(item)
if widget:
_debug_log(f"[WIDGET_FIX] Removing widget for item_key={item_key} - widget={widget}, parent={widget.parent()}, isWindow()={widget.isWindow()}")
self.list_widget.removeItemWidget(item)
# Check if widget became orphaned after removal
if widget.isWindow():
print(f"[WIDGET_FIX] ERROR: Widget became top-level window after removeItemWidget()! widget={widget}")
import traceback
traceback.print_stack()
self.list_widget.takeItem(i) self.list_widget.takeItem(i)
# Final check after takeItem
if widget:
_debug_log(f"[WIDGET_FIX] After takeItem - widget.parent()={widget.parent()}, isWindow()={widget.isWindow()}")
if widget.isWindow():
print(f"[WIDGET_FIX] ERROR: Widget is still a top-level window after takeItem()! This is the bug!")
import traceback
traceback.print_stack()
break break
del self._file_items[item_key] del self._file_items[item_key]
@@ -638,7 +734,28 @@ class FileProgressList(QWidget):
def _show_transition_message(self, new_phase: str): def _show_transition_message(self, new_phase: str):
"""Show a brief 'Preparing...' message during phase transitions.""" """Show a brief 'Preparing...' message during phase transitions."""
# CRITICAL FIX: Remove all item widgets before clear() to prevent orphaned widgets
_debug_log(f"[WIDGET_FIX] About to clear list_widget (transition) - count={self.list_widget.count()}")
for i in range(self.list_widget.count()):
item = self.list_widget.item(i)
if item:
widget = self.list_widget.itemWidget(item)
if widget:
_debug_log(f"[WIDGET_FIX] Removing widget before clear (transition) - widget={widget}, parent={widget.parent()}, isWindow()={widget.isWindow()}")
self.list_widget.removeItemWidget(item)
if widget.isWindow():
print(f"[WIDGET_FIX] ERROR: Widget became top-level window after removeItemWidget() before clear()!")
import traceback
traceback.print_stack()
self.list_widget.clear() self.list_widget.clear()
# Check widgets in _file_items dict after clear
for key, widget in list(self._file_items.items()):
if widget:
_debug_log(f"[WIDGET_FIX] Widget in _file_items after clear (transition) - key={key}, widget={widget}, parent={widget.parent()}, isWindow()={widget.isWindow()}")
if widget.isWindow():
print(f"[WIDGET_FIX] ERROR: Widget in _file_items is a top-level window after clear()! This is the bug!")
import traceback
traceback.print_stack()
self._file_items.clear() self._file_items.clear()
# Header removed - tab label provides context # Header removed - tab label provides context
@@ -663,9 +780,42 @@ class FileProgressList(QWidget):
def clear(self): def clear(self):
"""Clear all file items.""" """Clear all file items."""
# CRITICAL FIX: Remove all item widgets before clear() to prevent orphaned widgets
_debug_log(f"[WIDGET_FIX] clear() called - count={self.list_widget.count()}")
for i in range(self.list_widget.count()):
item = self.list_widget.item(i)
if item:
widget = self.list_widget.itemWidget(item)
if widget:
_debug_log(f"[WIDGET_FIX] Removing widget before clear() - widget={widget}, parent={widget.parent()}, isWindow()={widget.isWindow()}")
self.list_widget.removeItemWidget(item)
if widget.isWindow():
print(f"[WIDGET_FIX] ERROR: Widget became top-level window after removeItemWidget() before clear()!")
import traceback
traceback.print_stack()
self.list_widget.clear() self.list_widget.clear()
# Check widgets in _file_items dict after clear
for key, widget in list(self._file_items.items()):
if widget:
_debug_log(f"[WIDGET_FIX] Widget in _file_items after clear() - key={key}, widget={widget}, parent={widget.parent()}, isWindow()={widget.isWindow()}")
if widget.isWindow():
print(f"[WIDGET_FIX] ERROR: Widget in _file_items is a top-level window after clear()! This is the bug!")
import traceback
traceback.print_stack()
self._file_items.clear() self._file_items.clear()
if self._summary_widget:
_debug_log(f"[WIDGET_FIX] Clearing summary_widget - widget={self._summary_widget}, parent={self._summary_widget.parent()}, isWindow()={self._summary_widget.isWindow()}")
if self._summary_widget.isWindow():
print(f"[WIDGET_FIX] ERROR: Summary widget is a top-level window in clear()!")
import traceback
traceback.print_stack()
self._summary_widget = None self._summary_widget = None
if self._transition_label:
_debug_log(f"[WIDGET_FIX] Clearing transition_label - widget={self._transition_label}, parent={self._transition_label.parent()}, isWindow()={self._transition_label.isWindow()}")
if self._transition_label.isWindow():
print(f"[WIDGET_FIX] ERROR: Transition label is a top-level window in clear()!")
import traceback
traceback.print_stack()
self._transition_label = None self._transition_label = None
self._last_phase = None self._last_phase = None
# Header removed - tab label provides context # Header removed - tab label provides context