a0ef53922e
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
221 lines
8.0 KiB
Python
221 lines
8.0 KiB
Python
import os
|
|
import threading
|
|
import tkinter as tk
|
|
|
|
from core.config import ConfigManager, Profile
|
|
from core.robocopy_engine import RobocopySyncEngine
|
|
from core.sync_engine import SyncEngine
|
|
from ui import styles
|
|
from ui.path_panel import PathPanel
|
|
from ui.profile_bar import ProfileBar
|
|
from ui.status_bar import StatusBar
|
|
from ui.summary_panel import SummaryPanel
|
|
from ui.system_list import SystemList
|
|
|
|
|
|
class PocketSyncApp:
|
|
def __init__(self, root: tk.Tk, config_manager: ConfigManager):
|
|
self.root = root
|
|
self.cm = config_manager
|
|
self.engine: SyncEngine = RobocopySyncEngine()
|
|
|
|
self._build_ui()
|
|
self._load_profile(self.cm.active_profile)
|
|
|
|
# ------------------------------------------------------------------
|
|
# Construcción de la UI
|
|
# ------------------------------------------------------------------
|
|
|
|
def _build_ui(self) -> None:
|
|
self.root.title("PocketSync")
|
|
self.root.geometry(f"{styles.WINDOW_WIDTH}x{styles.WINDOW_HEIGHT}")
|
|
|
|
# --- Barra de perfiles ---
|
|
self.profile_bar = ProfileBar(self.root, on_change=self._on_profile_change)
|
|
self.profile_bar.pack(fill="x", padx=styles.PAD_X, pady=(8, 2))
|
|
self.profile_bar.set_callbacks(
|
|
on_new=self._on_new_profile,
|
|
on_rename=self._on_rename_profile,
|
|
on_delete=self._on_delete_profile,
|
|
)
|
|
self._refresh_profile_bar()
|
|
|
|
# --- Rutas de origen ---
|
|
self.src_panel = PathPanel(self.root, title="Rutas de origen")
|
|
self.src_panel.pack(fill="x", padx=styles.PAD_X, pady=styles.PAD_Y)
|
|
self.src_panel.set_roms_callback(self._on_roms_src_change)
|
|
|
|
# --- Lista de sistemas ---
|
|
self.system_list = SystemList(self.root)
|
|
self.system_list.pack(fill="both", expand=True, padx=styles.PAD_X)
|
|
|
|
# --- Rutas de destino ---
|
|
self.dst_panel = PathPanel(self.root, title="Rutas de destino")
|
|
self.dst_panel.pack(fill="x", padx=styles.PAD_X, pady=styles.PAD_Y)
|
|
|
|
# --- Botón Sync ---
|
|
tk.Button(
|
|
self.root,
|
|
text="Sync Now",
|
|
font=styles.FONT_BUTTON,
|
|
command=self._run_sync,
|
|
).pack(pady=6)
|
|
|
|
# --- Barra de estado ---
|
|
self.status_bar = StatusBar(self.root)
|
|
self.status_bar.pack(fill="x", padx=styles.PAD_X, pady=styles.PAD_Y)
|
|
|
|
# --- Panel de resumen ---
|
|
self.summary = SummaryPanel(self.root)
|
|
self.summary.pack(fill="x", padx=styles.PAD_X, pady=styles.PAD_Y)
|
|
|
|
self.root.protocol("WM_DELETE_WINDOW", self._on_close)
|
|
|
|
# ------------------------------------------------------------------
|
|
# Gestión de perfiles
|
|
# ------------------------------------------------------------------
|
|
|
|
def _refresh_profile_bar(self) -> None:
|
|
self.profile_bar.refresh(self.cm.profile_names(), self.cm.active_profile_name)
|
|
|
|
def _collect_ui_into_profile(self) -> None:
|
|
"""Escribe el estado actual de la UI en el perfil activo."""
|
|
p = self.cm.active_profile
|
|
p.esde_src = self.src_panel.get_esde()
|
|
p.roms_src = self.src_panel.get_roms()
|
|
p.esde_dst = self.dst_panel.get_esde()
|
|
p.roms_dst = self.dst_panel.get_roms()
|
|
p.selected = self.system_list.get_selected()
|
|
|
|
def _load_profile(self, profile: Profile) -> None:
|
|
self.src_panel.set_esde(profile.esde_src)
|
|
self.src_panel.set_roms(profile.roms_src)
|
|
self.dst_panel.set_esde(profile.esde_dst)
|
|
self.dst_panel.set_roms(profile.roms_dst)
|
|
|
|
if os.path.isdir(profile.roms_src):
|
|
self.system_list.populate(profile.roms_src)
|
|
else:
|
|
self.system_list.populate("")
|
|
|
|
self.system_list.set_selected(profile.selected)
|
|
|
|
def _on_profile_change(self, name: str) -> None:
|
|
self._collect_ui_into_profile()
|
|
self.cm.active_profile_name = name
|
|
self._load_profile(self.cm.active_profile)
|
|
self._refresh_profile_bar()
|
|
|
|
def _on_new_profile(self, name: str) -> bool:
|
|
if self.cm.get_profile(name) is not None:
|
|
return False
|
|
self._collect_ui_into_profile()
|
|
self.cm.add_profile(name)
|
|
self.cm.active_profile_name = name
|
|
self._load_profile(self.cm.active_profile)
|
|
self._refresh_profile_bar()
|
|
return True
|
|
|
|
def _on_rename_profile(self, old: str, new: str) -> bool:
|
|
ok = self.cm.rename_profile(old, new)
|
|
if ok:
|
|
self._refresh_profile_bar()
|
|
return ok
|
|
|
|
def _on_delete_profile(self, name: str) -> bool:
|
|
ok = self.cm.delete_profile(name)
|
|
if ok:
|
|
self._load_profile(self.cm.active_profile)
|
|
self._refresh_profile_bar()
|
|
return ok
|
|
|
|
# ------------------------------------------------------------------
|
|
# Callbacks de widgets
|
|
# ------------------------------------------------------------------
|
|
|
|
def _on_roms_src_change(self, path: str) -> None:
|
|
self.system_list.populate(path)
|
|
|
|
# ------------------------------------------------------------------
|
|
# Loop de sincronización
|
|
# ------------------------------------------------------------------
|
|
|
|
def _run_sync(self) -> None:
|
|
thread = threading.Thread(target=self._sync_thread)
|
|
thread.daemon = True
|
|
thread.start()
|
|
|
|
def _sync_thread(self) -> None:
|
|
selected = self.system_list.get_selected()
|
|
|
|
if not selected:
|
|
self.summary.append("No hay sistemas seleccionados.")
|
|
return
|
|
|
|
esde_src = self.src_panel.get_esde()
|
|
roms_src = self.src_panel.get_roms()
|
|
esde_dst = self.dst_panel.get_esde()
|
|
roms_dst = self.dst_panel.get_roms()
|
|
|
|
if not all([esde_src, roms_src, esde_dst, roms_dst]):
|
|
self.summary.append("ERROR: Debes configurar todas las rutas antes de continuar.")
|
|
return
|
|
|
|
self.summary.clear()
|
|
self.summary.append("=" * 60)
|
|
self.summary.append("INICIANDO PROCESO DE COPIA")
|
|
self.summary.append(f"Total de sistemas a procesar: {len(selected)}")
|
|
self.summary.append("=" * 60)
|
|
|
|
total = len(selected)
|
|
|
|
for idx, system in enumerate(selected, 1):
|
|
self.status_bar.set_system(f"Sistema: {idx}/{total} - {system.upper()}")
|
|
self.summary.append(f"\nSISTEMA [{idx}/{total}]: {system.upper()}")
|
|
|
|
# Fase 1: ROMs
|
|
self.status_bar.set_phase("Fase: [1/3] Copiando ROMs...")
|
|
self.summary.append(" [1/3] Copiando ROMs...")
|
|
self.engine.sync_folder(
|
|
os.path.join(roms_src, system),
|
|
os.path.join(roms_dst, system),
|
|
on_file=self.status_bar.set_file,
|
|
on_summary=self.summary.append,
|
|
)
|
|
|
|
# Fase 2: gamelists
|
|
self.status_bar.set_phase("Fase: [2/3] Copiando gamelists...")
|
|
self.summary.append(" [2/3] Copiando gamelists...")
|
|
self.engine.sync_folder(
|
|
os.path.join(esde_src, "gamelists", system),
|
|
os.path.join(esde_dst, "gamelists", system),
|
|
on_file=self.status_bar.set_file,
|
|
on_summary=self.summary.append,
|
|
)
|
|
|
|
# Fase 3: media
|
|
self.status_bar.set_phase("Fase: [3/3] Copiando media...")
|
|
self.summary.append(" [3/3] Copiando media...")
|
|
self.engine.sync_folder(
|
|
os.path.join(esde_src, "downloaded_media", system),
|
|
os.path.join(esde_dst, "downloaded_media", system),
|
|
on_file=self.status_bar.set_file,
|
|
on_summary=self.summary.append,
|
|
)
|
|
|
|
self.summary.append(f" Sistema '{system}' completado\n")
|
|
|
|
self.status_bar.reset()
|
|
self.summary.append("=" * 60)
|
|
self.summary.append("PROCESO COMPLETADO EXITOSAMENTE")
|
|
self.summary.append("=" * 60)
|
|
|
|
# ------------------------------------------------------------------
|
|
# Cierre
|
|
# ------------------------------------------------------------------
|
|
|
|
def _on_close(self) -> None:
|
|
self._collect_ui_into_profile()
|
|
self.cm.save()
|
|
self.root.destroy()
|