160 lines
5.7 KiB
Python
160 lines
5.7 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
Updater Application
|
|
Downloads and extracts updates for the Handover System.
|
|
"""
|
|
|
|
import sys
|
|
import os
|
|
import time
|
|
import zipfile
|
|
import argparse
|
|
import shutil
|
|
import subprocess
|
|
import requests
|
|
from pathlib import Path
|
|
from PySide6.QtWidgets import (
|
|
QApplication, QWidget, QVBoxLayout, QLabel, QProgressBar, QMessageBox
|
|
)
|
|
from PySide6.QtCore import Qt, QThread, Signal
|
|
|
|
class UpdateWorker(QThread):
|
|
progress_changed = Signal(int)
|
|
status_changed = Signal(str)
|
|
finished = Signal(bool, str)
|
|
|
|
def __init__(self, download_url, target_dir, restart_exe):
|
|
super().__init__()
|
|
self.download_url = download_url
|
|
self.target_dir = Path(target_dir)
|
|
self.restart_exe = restart_exe
|
|
|
|
def run(self):
|
|
try:
|
|
# 1. Wait for main application to close
|
|
self.status_changed.emit("메인 애플리케이션 종료 대기 중...")
|
|
time.sleep(2) # Give it a moment
|
|
|
|
# Simple check: try to rename the executable. If it fails, it's still running.
|
|
# A more robust way would be checking process list, but this is simple and effective.
|
|
exe_path = self.target_dir / self.restart_exe
|
|
max_retries = 10
|
|
for i in range(max_retries):
|
|
try:
|
|
if exe_path.exists():
|
|
# Try to open for exclusive access
|
|
with open(exe_path, 'ab'):
|
|
pass
|
|
break
|
|
except IOError:
|
|
if i == max_retries - 1:
|
|
raise Exception("애플리케이션이 종료되지 않았습니다. 수동으로 종료해주세요.")
|
|
time.sleep(1)
|
|
|
|
# 2. Download Update
|
|
self.status_changed.emit("업데이트 다운로드 중...")
|
|
local_zip = self.target_dir / "update.zip"
|
|
|
|
response = requests.get(self.download_url, stream=True)
|
|
response.raise_for_status()
|
|
|
|
total_size = int(response.headers.get('content-length', 0))
|
|
block_size = 8192
|
|
downloaded = 0
|
|
|
|
with open(local_zip, 'wb') as f:
|
|
for chunk in response.iter_content(chunk_size=block_size):
|
|
if chunk:
|
|
f.write(chunk)
|
|
downloaded += len(chunk)
|
|
if total_size > 0:
|
|
percent = int((downloaded / total_size) * 50) # First 50% is download
|
|
self.progress_changed.emit(percent)
|
|
|
|
# 3. Extract Update
|
|
self.status_changed.emit("업데이트 설치 중...")
|
|
with zipfile.ZipFile(local_zip, 'r') as zip_ref:
|
|
file_list = zip_ref.namelist()
|
|
total_files = len(file_list)
|
|
|
|
for idx, file in enumerate(file_list):
|
|
zip_ref.extract(file, self.target_dir)
|
|
percent = 50 + int((idx / total_files) * 50) # Last 50% is extraction
|
|
self.progress_changed.emit(percent)
|
|
|
|
# Cleanup
|
|
try:
|
|
os.remove(local_zip)
|
|
except:
|
|
pass
|
|
|
|
self.progress_changed.emit(100)
|
|
self.status_changed.emit("업데이트 완료!")
|
|
self.finished.emit(True, "성공")
|
|
|
|
except Exception as e:
|
|
self.finished.emit(False, str(e))
|
|
|
|
class UpdaterWindow(QWidget):
|
|
def __init__(self, download_url, target_dir, restart_exe):
|
|
super().__init__()
|
|
self.download_url = download_url
|
|
self.target_dir = target_dir
|
|
self.restart_exe = restart_exe
|
|
|
|
self.init_ui()
|
|
self.start_update()
|
|
|
|
def init_ui(self):
|
|
self.setWindowTitle("소프트웨어 업데이트")
|
|
self.setFixedSize(400, 150)
|
|
|
|
layout = QVBoxLayout()
|
|
|
|
self.status_label = QLabel("준비 중...")
|
|
self.status_label.setAlignment(Qt.AlignCenter)
|
|
layout.addWidget(self.status_label)
|
|
|
|
self.progress_bar = QProgressBar()
|
|
self.progress_bar.setRange(0, 100)
|
|
layout.addWidget(self.progress_bar)
|
|
|
|
self.setLayout(layout)
|
|
|
|
def start_update(self):
|
|
self.worker = UpdateWorker(self.download_url, self.target_dir, self.restart_exe)
|
|
self.worker.progress_changed.connect(self.progress_bar.setValue)
|
|
self.worker.status_changed.connect(self.status_label.setText)
|
|
self.worker.finished.connect(self.on_finished)
|
|
self.worker.start()
|
|
|
|
def on_finished(self, success, message):
|
|
if success:
|
|
# Restart application
|
|
exe_path = Path(self.target_dir) / self.restart_exe
|
|
if exe_path.exists():
|
|
subprocess.Popen([str(exe_path)])
|
|
else:
|
|
QMessageBox.warning(self, "오류", f"실행 파일을 찾을 수 없습니다:\n{exe_path}")
|
|
|
|
self.close()
|
|
else:
|
|
QMessageBox.critical(self, "업데이트 실패", f"오류가 발생했습니다:\n{message}")
|
|
self.close()
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description='Handover System Updater')
|
|
parser.add_argument('--url', required=True, help='Download URL for the update')
|
|
parser.add_argument('--target', required=True, help='Target installation directory')
|
|
parser.add_argument('--restart', required=True, help='Executable name to restart')
|
|
|
|
args = parser.parse_args()
|
|
|
|
app = QApplication(sys.argv)
|
|
window = UpdaterWindow(args.url, args.target, args.restart)
|
|
window.show()
|
|
sys.exit(app.exec())
|
|
|
|
if __name__ == "__main__":
|
|
main()
|