Files
Jackify/jackify/backend/handlers/progress_state_processing.py
2026-02-07 18:26:54 +00:00

240 lines
9.5 KiB
Python

"""Line processing methods for ProgressStateManager (Mixin)."""
import logging
import time
from typing import TYPE_CHECKING
from jackify.shared.progress_models import (
InstallationPhase,
InstallationProgress,
FileProgress,
OperationType,
)
if TYPE_CHECKING:
from jackify.backend.handlers.progress_parser import ParsedLine
logger = logging.getLogger(__name__)
class ProgressStateProcessingMixin:
"""Mixin providing line processing methods."""
def process_line(self, line: str) -> bool:
"""
Process a line of output and update state.
Returns:
True if state was updated, False otherwise
"""
parsed = self.parser.parse_line(line)
if not parsed.has_progress:
return False
updated = False
phase_changed = False
if parsed.phase and parsed.phase != self.state.phase:
previous_phase = self.state.phase
if previous_phase == InstallationPhase.DOWNLOAD:
self._download_files_seen = {}
self._download_total_bytes = 0
self._download_processed_bytes = 0
if previous_phase == InstallationPhase.VALIDATE and not parsed.data_info:
if self.state.data_total > 0:
self.state.data_processed = 0
self.state.data_total = 0
updated = True
if previous_phase == InstallationPhase.VALIDATE:
if self.state.phase_name and 'validat' in self.state.phase_name.lower():
self.state.phase_name = ""
updated = True
phase_changed = True
self._previous_phase = self.state.phase
self.state.phase = parsed.phase
updated = True
elif parsed.phase:
self.state.phase = parsed.phase
updated = True
if parsed.phase_name:
self.state.phase_name = parsed.phase_name
updated = True
elif phase_changed:
if self.state.phase_name and self.state.phase != InstallationPhase.VALIDATE:
self.state.phase_name = ""
updated = True
if self.state.phase == InstallationPhase.DOWNLOAD:
if self.state.phase_name and 'validat' in self.state.phase_name.lower():
self.state.phase_name = ""
updated = True
if parsed.overall_percent is not None:
self.state.overall_percent = parsed.overall_percent
updated = True
if parsed.step_info:
self.state.phase_step, self.state.phase_max_steps = parsed.step_info
updated = True
if parsed.data_info:
self.state.data_processed, self.state.data_total = parsed.data_info
if self.state.data_total > 0 and self.state.overall_percent == 0.0:
self.state.overall_percent = (self.state.data_processed / self.state.data_total) * 100.0
updated = True
if parsed.file_counter:
self.state.phase_step, self.state.phase_max_steps = parsed.file_counter
updated = True
if parsed.file_progress:
if hasattr(parsed.file_progress, '_hidden') and parsed.file_progress._hidden:
return updated
if hasattr(parsed.file_progress, '_texture_counter'):
tex_current, tex_total = parsed.file_progress._texture_counter
self.state.texture_conversion_current = tex_current
self.state.texture_conversion_total = tex_total
updated = True
if hasattr(parsed.file_progress, '_bsa_counter'):
bsa_current, bsa_total = parsed.file_progress._bsa_counter
self.state.bsa_building_current = bsa_current
self.state.bsa_building_total = bsa_total
updated = True
if parsed.file_progress.filename.lower().endswith('.wabbajack'):
self._wabbajack_entry_name = parsed.file_progress.filename
self._remove_synthetic_wabbajack()
self._has_real_wabbajack = True
else:
if parsed.file_progress.operation == OperationType.DOWNLOAD:
self._remove_all_wabbajack_entries()
self._has_real_wabbajack = True
if self.state.phase == InstallationPhase.DOWNLOAD and parsed.file_progress.operation == OperationType.DOWNLOAD:
filename = parsed.file_progress.filename
total_size = parsed.file_progress.total_size or 0
current_size = parsed.file_progress.current_size or 0
if filename not in self._download_files_seen:
if total_size > 0:
self._download_total_bytes += total_size
self._download_files_seen[filename] = (total_size, current_size)
self._download_processed_bytes += current_size
else:
old_total, old_current = self._download_files_seen[filename]
if total_size > old_total:
self._download_total_bytes += (total_size - old_total)
if current_size > old_current:
self._download_processed_bytes += (current_size - old_current)
self._download_files_seen[filename] = (max(old_total, total_size), current_size)
if self.state.data_total == 0 and self._download_total_bytes > 0:
self.state.data_total = self._download_total_bytes
self.state.data_processed = self._download_processed_bytes
updated = True
self._augment_file_metrics(parsed.file_progress)
existing_file = None
for f in self.state.active_files:
if f.filename == parsed.file_progress.filename:
existing_file = f
break
if parsed.file_progress.percent >= 100.0 and not existing_file:
updated = True
elif parsed.file_progress.percent >= 100.0:
parsed.file_progress.percent = 100.0
parsed.file_progress.last_update = time.time()
self.state.add_file(parsed.file_progress)
updated = True
else:
self.state.add_file(parsed.file_progress)
updated = True
elif parsed.data_info:
phase_name_lower = (parsed.phase_name or "").lower()
message_lower = (parsed.message or "").lower()
is_archive_phase = (
'mod archives' in phase_name_lower or
'downloading mod archives' in message_lower or
(parsed.phase == InstallationPhase.DOWNLOAD and self._has_real_download_activity())
)
if is_archive_phase:
self._remove_all_wabbajack_entries()
self._has_real_wabbajack = True
if not getattr(self, '_has_real_wabbajack', False):
if self._maybe_add_wabbajack_progress(parsed):
updated = True
if parsed.completed_filename:
if not self.parser.should_display_file(parsed.completed_filename):
parsed.completed_filename = None
if parsed.completed_filename:
if self.state.phase == InstallationPhase.DOWNLOAD:
filename = parsed.completed_filename
if filename in self._download_files_seen:
old_total, old_current = self._download_files_seen[filename]
if old_current < old_total:
self._download_processed_bytes += (old_total - old_current)
self._download_files_seen[filename] = (old_total, old_total)
if self.state.data_total == 0 and self._download_total_bytes > 0:
self.state.data_total = self._download_total_bytes
self.state.data_processed = self._download_processed_bytes
updated = True
found_existing = False
for file_prog in self.state.active_files:
filename_match = (
file_prog.filename == parsed.completed_filename or
file_prog.filename.endswith(parsed.completed_filename) or
parsed.completed_filename in file_prog.filename
)
if filename_match:
file_prog.percent = 100.0
file_prog.last_update = time.time()
updated = True
found_existing = True
break
if not found_existing:
operation = OperationType.DOWNLOAD
if parsed.file_progress:
operation = parsed.file_progress.operation
completed_file = FileProgress(
filename=parsed.completed_filename,
operation=operation,
percent=100.0,
current_size=0,
total_size=0
)
completed_file.last_update = time.time()
self.state.add_file(completed_file)
updated = True
if parsed.speed_info:
operation, speed = parsed.speed_info
self.state.update_speed(operation, speed)
updated = True
if parsed.message:
self.state.message = parsed.message
if updated:
self.state.timestamp = time.time()
if updated:
self.state.remove_completed_files()
return updated