Небольшие изменения
This commit is contained in:
parent
401cc652a2
commit
d17ea24fa5
1 changed files with 85 additions and 25 deletions
110
main.py
110
main.py
|
|
@ -6,11 +6,22 @@ import tkinter as tk
|
|||
from tkinter import filedialog, messagebox, ttk
|
||||
from pathlib import Path
|
||||
import threading
|
||||
import subprocess
|
||||
import re
|
||||
|
||||
# --- КОНФИГУРАЦИЯ ---
|
||||
SERVER_URL = "https://server1.borderban.ru/mods/"
|
||||
CONFIG_FILE = Path.home() / ".factorio_sync_config.json"
|
||||
|
||||
def get_mod_base_name(filename):
|
||||
# Пытаемся отсечь версию мода (обычно ИмяМода_1.0.0.zip)
|
||||
match = re.match(r"^(.+)_(\d+\.\d+\.\d+)\.zip$", filename)
|
||||
if match:
|
||||
return match.group(1)
|
||||
if "_" in filename and filename.endswith(".zip"):
|
||||
return filename.rsplit("_", 1)[0]
|
||||
return filename
|
||||
|
||||
def get_file_hash(filepath):
|
||||
sha256_hash = hashlib.sha256()
|
||||
with open(filepath, "rb") as f:
|
||||
|
|
@ -22,28 +33,45 @@ class SyncApp:
|
|||
def __init__(self, root):
|
||||
self.root = root
|
||||
self.root.title("Factorio Mod Sync")
|
||||
self.root.geometry("500x250")
|
||||
self.root.geometry("500x350")
|
||||
|
||||
self.config = self.load_config()
|
||||
self.setup_ui()
|
||||
|
||||
def load_config(self):
|
||||
if CONFIG_FILE.exists():
|
||||
return json.loads(CONFIG_FILE.read_text())
|
||||
return {"mods_path": ""}
|
||||
data = json.loads(CONFIG_FILE.read_text())
|
||||
return {
|
||||
"mods_path": data.get("mods_path", ""),
|
||||
"game_path": data.get("game_path", "")
|
||||
}
|
||||
return {"mods_path": "", "game_path": ""}
|
||||
|
||||
def save_config(self, mods_path):
|
||||
CONFIG_FILE.write_text(json.dumps({"mods_path": mods_path}))
|
||||
def save_config(self, mods_path=None, game_path=None):
|
||||
config = self.load_config()
|
||||
if mods_path is not None:
|
||||
config["mods_path"] = mods_path
|
||||
if game_path is not None:
|
||||
config["game_path"] = game_path
|
||||
CONFIG_FILE.write_text(json.dumps(config))
|
||||
|
||||
def setup_ui(self):
|
||||
# Выбор пути
|
||||
tk.Label(self.root, text="Путь к папке mods:").pack(pady=(10, 0))
|
||||
self.path_var = tk.StringVar(value=self.config["mods_path"])
|
||||
entry_frame = tk.Frame(self.root)
|
||||
entry_frame.pack(fill="x", padx=20, pady=5)
|
||||
entry_frame.pack(fill="x", padx=20, pady=2)
|
||||
tk.Entry(entry_frame, textvariable=self.path_var).pack(side="left", fill="x", expand=True)
|
||||
tk.Button(entry_frame, text="Обзор", command=self.select_path).pack(side="right", padx=5)
|
||||
|
||||
# Выбор пути к игре
|
||||
tk.Label(self.root, text="Путь к игре (exe):").pack(pady=(5, 0))
|
||||
self.game_path_var = tk.StringVar(value=self.config["game_path"])
|
||||
game_entry_frame = tk.Frame(self.root)
|
||||
game_entry_frame.pack(fill="x", padx=20, pady=2)
|
||||
tk.Entry(game_entry_frame, textvariable=self.game_path_var).pack(side="left", fill="x", expand=True)
|
||||
tk.Button(game_entry_frame, text="Обзор", command=self.select_game_path).pack(side="right", padx=5)
|
||||
|
||||
# Статус и Прогресс
|
||||
self.status_label = tk.Label(self.root, text="Готов к работе", fg="blue")
|
||||
self.status_label.pack(pady=(10, 0))
|
||||
|
|
@ -51,15 +79,40 @@ class SyncApp:
|
|||
self.progress = ttk.Progressbar(self.root, orient="horizontal", length=400, mode="determinate")
|
||||
self.progress.pack(pady=10)
|
||||
|
||||
self.sync_btn = tk.Button(self.root, text="СИНХРОНИЗИРОВАТЬ", command=self.start_sync_thread,
|
||||
bg="#2c3e50", fg="white", font=("Arial", 10, "bold"), height=2)
|
||||
self.sync_btn.pack(pady=10)
|
||||
# Кнопки
|
||||
btn_frame = tk.Frame(self.root)
|
||||
btn_frame.pack(pady=10)
|
||||
|
||||
self.sync_btn = tk.Button(btn_frame, text="СИНХРОНИЗИРОВАТЬ", command=self.start_sync_thread,
|
||||
bg="#2c3e50", fg="white", font=("Arial", 10, "bold"), height=2, width=18)
|
||||
self.sync_btn.pack(side="left", padx=10)
|
||||
|
||||
self.launch_btn = tk.Button(btn_frame, text="ЗАПУСТИТЬ ИГРУ", command=self.launch_game,
|
||||
bg="#27ae60", fg="white", font=("Arial", 10, "bold"), height=2, width=18)
|
||||
self.launch_btn.pack(side="right", padx=10)
|
||||
|
||||
def select_path(self):
|
||||
path = filedialog.askdirectory()
|
||||
path = filedialog.askdirectory(title="Выберите папку mods")
|
||||
if path:
|
||||
self.path_var.set(path)
|
||||
self.save_config(path)
|
||||
self.save_config(mods_path=path)
|
||||
|
||||
def select_game_path(self):
|
||||
path = filedialog.askopenfilename(title="Выберите исполняемый файл игры")
|
||||
if path:
|
||||
self.game_path_var.set(path)
|
||||
self.save_config(game_path=path)
|
||||
|
||||
def launch_game(self):
|
||||
game_path = self.game_path_var.get()
|
||||
if not game_path or not Path(game_path).exists():
|
||||
messagebox.showwarning("Внимание", "Сначала выберите правильный путь к исполняемому файлу игры!")
|
||||
return
|
||||
try:
|
||||
subprocess.Popen([game_path], cwd=os.path.dirname(game_path))
|
||||
self.update_status("Игра запущена!", 100)
|
||||
except Exception as e:
|
||||
messagebox.showerror("Ошибка", f"Не удалось запустить игру:\n{e}")
|
||||
|
||||
def update_status(self, text, value=None):
|
||||
self.status_label.config(text=text)
|
||||
|
|
@ -79,12 +132,14 @@ class SyncApp:
|
|||
return
|
||||
|
||||
self.sync_btn.config(state="disabled")
|
||||
self.launch_btn.config(state="disabled")
|
||||
try:
|
||||
# 1. Получение манифеста
|
||||
self.update_status("Получение списка модов с сервера...", 5)
|
||||
response = requests.get(f"{SERVER_URL}mods_list.json", timeout=15)
|
||||
response.raise_for_status()
|
||||
server_manifest = response.json()
|
||||
server_base_names = {get_mod_base_name(f) for f in server_manifest.keys()}
|
||||
|
||||
local_mods = Path(mods_path)
|
||||
if not local_mods.exists():
|
||||
|
|
@ -92,14 +147,17 @@ class SyncApp:
|
|||
|
||||
mod_status = {}
|
||||
|
||||
# --- ЭТАП ОЧИСТКИ (Удаление лишнего) ---
|
||||
self.update_status("Очистка старых модов...", 10)
|
||||
# --- ЭТАП ОЧИСТКИ (Удаление старых версий) ---
|
||||
self.update_status("Очистка старых версий модов...", 10)
|
||||
local_files = list(local_mods.glob("*.zip"))
|
||||
for local_file in local_files:
|
||||
if local_file.name not in server_manifest:
|
||||
print(f"Удаление лишнего файла: {local_file.name}")
|
||||
local_file.unlink()
|
||||
mod_status[local_file.name] = "удалено"
|
||||
local_base = get_mod_base_name(local_file.name)
|
||||
# Удаляем только если это старая версия мода из сборки (базовое имя совпадает)
|
||||
if local_base in server_base_names:
|
||||
print(f"Удаление старой версии: {local_file.name}")
|
||||
local_file.unlink()
|
||||
mod_status[local_file.name] = "удалено"
|
||||
# ---------------------------------------
|
||||
|
||||
# 2. Синхронизация (Загрузка нужного)
|
||||
|
|
@ -135,13 +193,8 @@ class SyncApp:
|
|||
self.update_status("Синхронизация завершена!", 100)
|
||||
|
||||
# --- СОРТИРОВКА И ВЫВОД ОТЧЕТА ---
|
||||
sort_order = {
|
||||
"без изменений": 1,
|
||||
"обновлено": 2,
|
||||
"добавлено": 3,
|
||||
"удалено": 4
|
||||
}
|
||||
sorted_mods = sorted(mod_status.items(), key=lambda item: sort_order.get(item[1], 5))
|
||||
# Сортируем просто по алфавиту для удобного git-diff стиля
|
||||
sorted_mods = sorted(mod_status.items(), key=lambda item: item[0])
|
||||
|
||||
# Безопасный вызов отрисовки UI из фонового потока
|
||||
self.root.after(0, self.show_report, sorted_mods)
|
||||
|
|
@ -151,6 +204,7 @@ class SyncApp:
|
|||
self.root.after(0, lambda: messagebox.showerror("Ошибка", str(e)))
|
||||
finally:
|
||||
self.root.after(0, lambda: self.sync_btn.config(state="normal"))
|
||||
self.root.after(0, lambda: self.launch_btn.config(state="normal"))
|
||||
|
||||
def show_report(self, sorted_mods):
|
||||
report_win = tk.Toplevel(self.root)
|
||||
|
|
@ -173,8 +227,14 @@ class SyncApp:
|
|||
|
||||
# Заполняем поле
|
||||
for mod, status in sorted_mods:
|
||||
text_widget.insert(tk.END, f"{mod}: ")
|
||||
text_widget.insert(tk.END, f"{status}\n", status)
|
||||
if status == "добавлено":
|
||||
text_widget.insert(tk.END, f"+ {mod}\n", status)
|
||||
elif status == "удалено":
|
||||
text_widget.insert(tk.END, f"- {mod}\n", status)
|
||||
elif status == "обновлено":
|
||||
text_widget.insert(tk.END, f"~ {mod}\n", status)
|
||||
elif status == "без изменений":
|
||||
text_widget.insert(tk.END, f" {mod}\n", status)
|
||||
|
||||
text_widget.config(state="disabled") # Только чтение
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue