mirror of
https://github.com/Omni-guides/Jackify.git
synced 2026-06-08 03:47:44 +02:00
Sync from development - prepare for v0.3.0
This commit is contained in:
167
jackify/backend/handlers/progress_state_metrics.py
Normal file
167
jackify/backend/handlers/progress_state_metrics.py
Normal file
@@ -0,0 +1,167 @@
|
||||
"""Metrics and synthetic entry methods for ProgressStateManager (Mixin)."""
|
||||
|
||||
import logging
|
||||
import re
|
||||
import time
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from jackify.shared.progress_models import FileProgress, OperationType, InstallationPhase
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from jackify.backend.handlers.progress_parser import ParsedLine
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ProgressStateMetricsMixin:
|
||||
"""Mixin providing metrics augmentation methods."""
|
||||
|
||||
def _augment_file_metrics(self, file_progress: FileProgress) -> None:
|
||||
"""Populate size/speed info to improve UI accuracy."""
|
||||
now = time.time()
|
||||
history = self._file_history.get(file_progress.filename)
|
||||
|
||||
total_size = file_progress.total_size or (history.get('total') if history else None)
|
||||
if total_size and file_progress.percent and not file_progress.current_size:
|
||||
file_progress.current_size = int((file_progress.percent / 100.0) * total_size)
|
||||
elif file_progress.current_size and not total_size and file_progress.total_size:
|
||||
total_size = file_progress.total_size
|
||||
|
||||
if total_size and not file_progress.total_size:
|
||||
file_progress.total_size = total_size
|
||||
|
||||
current_size = file_progress.current_size or 0
|
||||
|
||||
computed_speed = 0.0
|
||||
if file_progress.speed < 0:
|
||||
computed_speed = 0.0
|
||||
if history and current_size:
|
||||
prev_bytes = history.get('bytes', 0)
|
||||
prev_time = history.get('time', now)
|
||||
delta_bytes = current_size - prev_bytes
|
||||
delta_time = now - prev_time
|
||||
|
||||
if delta_bytes >= 0 and delta_time >= 1.0:
|
||||
computed_speed = delta_bytes / delta_time
|
||||
elif history.get('computed_speed'):
|
||||
computed_speed = history.get('computed_speed', 0.0)
|
||||
|
||||
file_progress.speed = computed_speed
|
||||
else:
|
||||
computed_speed = file_progress.speed
|
||||
|
||||
if current_size or total_size:
|
||||
self._file_history[file_progress.filename] = {
|
||||
'bytes': current_size,
|
||||
'time': now,
|
||||
'total': total_size or (history.get('total') if history else None),
|
||||
'computed_speed': computed_speed,
|
||||
}
|
||||
elif history:
|
||||
self._file_history[file_progress.filename] = history
|
||||
|
||||
def _maybe_add_wabbajack_progress(self, parsed: "ParsedLine") -> bool:
|
||||
"""Create a synthetic file entry for .wabbajack archive download."""
|
||||
if not parsed.data_info:
|
||||
return False
|
||||
if not parsed.data_info:
|
||||
return False
|
||||
|
||||
current_bytes, total_bytes = parsed.data_info
|
||||
if total_bytes <= 0:
|
||||
return False
|
||||
|
||||
for fp in self.state.active_files:
|
||||
if fp.filename.lower().endswith('.wabbajack'):
|
||||
synthetic_entry = fp
|
||||
if getattr(fp, self._synthetic_flag, False):
|
||||
percent = (current_bytes / total_bytes) * 100.0
|
||||
synthetic_entry.percent = percent
|
||||
synthetic_entry.current_size = current_bytes
|
||||
synthetic_entry.total_size = total_bytes
|
||||
synthetic_entry.last_update = time.time()
|
||||
self._augment_file_metrics(synthetic_entry)
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
synthetic_entry = None
|
||||
for fp in self.state.active_files:
|
||||
if getattr(fp, self._synthetic_flag, False):
|
||||
synthetic_entry = fp
|
||||
break
|
||||
|
||||
message = (parsed.message or "")
|
||||
phase_name = (parsed.phase_name or "").lower()
|
||||
should_force = 'wabbajack' in message.lower() or 'wabbajack' in phase_name
|
||||
|
||||
if not synthetic_entry:
|
||||
if self._has_real_download_activity() and not should_force:
|
||||
return False
|
||||
if self.state.phase not in (InstallationPhase.INITIALIZATION, InstallationPhase.DOWNLOAD) and not should_force:
|
||||
return False
|
||||
|
||||
percent = (current_bytes / total_bytes) * 100.0
|
||||
if not self._wabbajack_entry_name:
|
||||
filename_match = re.search(r'([A-Za-z0-9_\-\.]+\.wabbajack)', message, re.IGNORECASE)
|
||||
if filename_match:
|
||||
self._wabbajack_entry_name = filename_match.group(1)
|
||||
if not self._wabbajack_entry_name:
|
||||
self._wabbajack_entry_name = "Downloading .wabbajack file"
|
||||
entry_name = self._wabbajack_entry_name
|
||||
|
||||
if synthetic_entry:
|
||||
synthetic_entry.percent = percent
|
||||
synthetic_entry.current_size = current_bytes
|
||||
synthetic_entry.total_size = total_bytes
|
||||
synthetic_entry.last_update = time.time()
|
||||
self._augment_file_metrics(synthetic_entry)
|
||||
else:
|
||||
special_file = FileProgress(
|
||||
filename=entry_name,
|
||||
operation=OperationType.DOWNLOAD,
|
||||
percent=percent,
|
||||
current_size=current_bytes,
|
||||
total_size=total_bytes
|
||||
)
|
||||
special_file.last_update = time.time()
|
||||
setattr(special_file, self._synthetic_flag, True)
|
||||
self._augment_file_metrics(special_file)
|
||||
self.state.add_file(special_file)
|
||||
return True
|
||||
|
||||
def _has_real_download_activity(self) -> bool:
|
||||
"""Check if there are real download entries already visible."""
|
||||
for fp in self.state.active_files:
|
||||
if getattr(fp, self._synthetic_flag, False):
|
||||
continue
|
||||
if fp.operation == OperationType.DOWNLOAD:
|
||||
return True
|
||||
return False
|
||||
|
||||
def _remove_synthetic_wabbajack(self) -> None:
|
||||
"""Remove any synthetic .wabbajack entries once real files appear."""
|
||||
remaining = []
|
||||
removed = False
|
||||
for fp in self.state.active_files:
|
||||
if getattr(fp, self._synthetic_flag, False):
|
||||
removed = True
|
||||
self._file_history.pop(fp.filename, None)
|
||||
continue
|
||||
remaining.append(fp)
|
||||
if removed:
|
||||
self.state.active_files = remaining
|
||||
|
||||
def _remove_all_wabbajack_entries(self) -> None:
|
||||
"""Remove ALL .wabbajack entries when archive download phase starts."""
|
||||
remaining = []
|
||||
removed = False
|
||||
for fp in self.state.active_files:
|
||||
if fp.filename.lower().endswith('.wabbajack') or 'wabbajack' in fp.filename.lower():
|
||||
removed = True
|
||||
self._file_history.pop(fp.filename, None)
|
||||
continue
|
||||
remaining.append(fp)
|
||||
if removed:
|
||||
self.state.active_files = remaining
|
||||
self._wabbajack_entry_name = None
|
||||
Reference in New Issue
Block a user