feat: Add ProBackup.exe executable for Windows XP distribution.
This commit is contained in:
Binary file not shown.
@@ -4,6 +4,7 @@ import subprocess
|
|||||||
import platform
|
import platform
|
||||||
import os
|
import os
|
||||||
import base64
|
import base64
|
||||||
|
import sys
|
||||||
|
|
||||||
# Simple Secret Salt - In a real app, obfuscate this or use asymmetric keys (RSA)
|
# Simple Secret Salt - In a real app, obfuscate this or use asymmetric keys (RSA)
|
||||||
# For this level, a symmetric secret is "good enough" for basic protection.
|
# For this level, a symmetric secret is "good enough" for basic protection.
|
||||||
@@ -14,6 +15,35 @@ CREATE_NO_WINDOW = 0x08000000
|
|||||||
|
|
||||||
class LicenseManager:
|
class LicenseManager:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
def _get_base_dir():
|
||||||
|
if getattr(sys, 'frozen', False):
|
||||||
|
return os.path.dirname(sys.executable)
|
||||||
|
return os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _get_license_path():
|
||||||
|
return os.path.join(LicenseManager._get_base_dir(), "license.dat")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def save_license(key):
|
||||||
|
try:
|
||||||
|
with open(LicenseManager._get_license_path(), "w") as f:
|
||||||
|
f.write(key.strip())
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def load_license():
|
||||||
|
path = LicenseManager._get_license_path()
|
||||||
|
if not os.path.exists(path):
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
with open(path, "r") as f:
|
||||||
|
return f.read().strip()
|
||||||
|
except:
|
||||||
|
return None
|
||||||
|
@staticmethod
|
||||||
def get_motherboard_id():
|
def get_motherboard_id():
|
||||||
"""Gets the motherboard UUID using WMIC (Windows only)."""
|
"""Gets the motherboard UUID using WMIC (Windows only)."""
|
||||||
try:
|
try:
|
||||||
|
|||||||
24
main.py
24
main.py
@@ -278,8 +278,11 @@ class BackupApp(QMainWindow):
|
|||||||
|
|
||||||
# Ensure sql_tools folder exists
|
# Ensure sql_tools folder exists
|
||||||
self.ensure_sql_tools_folder()
|
self.ensure_sql_tools_folder()
|
||||||
|
self.settings = self.load_settings()
|
||||||
|
self.trial_limit = 10 # Max uses in trial mode
|
||||||
|
|
||||||
self.load_settings()
|
# Job Queue System
|
||||||
|
self.job_queue = []
|
||||||
|
|
||||||
if self.trial_mode:
|
if self.trial_mode:
|
||||||
# Check if strict limit reached immediately
|
# Check if strict limit reached immediately
|
||||||
@@ -1308,9 +1311,9 @@ class BackupApp(QMainWindow):
|
|||||||
def scheduled_backup_wrapper(self, job_id):
|
def scheduled_backup_wrapper(self, job_id):
|
||||||
# Determine if we can run
|
# Determine if we can run
|
||||||
if self.worker and self.worker.isRunning():
|
if self.worker and self.worker.isRunning():
|
||||||
# Concurrent jobs? For now let's skip or queue?
|
# Concurrent jobs? Queue them!
|
||||||
# Simple approach: Skip if busy.
|
self.log(">>> App Sibuk. Job ID {} masuk antrian.".format(job_id))
|
||||||
self.log(">>> Jadwal Skip (App Sibuk) untuk Job ID: {}".format(job_id))
|
self.job_queue.append(job_id)
|
||||||
return
|
return
|
||||||
|
|
||||||
# --- Check Trial Limit ---
|
# --- Check Trial Limit ---
|
||||||
@@ -1371,6 +1374,13 @@ class BackupApp(QMainWindow):
|
|||||||
self.run_single_btn.setEnabled(True)
|
self.run_single_btn.setEnabled(True)
|
||||||
self.log(">>> Eksekusi Selesai.")
|
self.log(">>> Eksekusi Selesai.")
|
||||||
|
|
||||||
|
# Check Queue
|
||||||
|
if self.job_queue:
|
||||||
|
next_job_id = self.job_queue.pop(0)
|
||||||
|
self.log(">>> Memproses antrian: Job ID {}".format(next_job_id))
|
||||||
|
# Use QTimer to schedule it on main thread to be safe, though we are likely already on main thread slot
|
||||||
|
QTimer.singleShot(100, lambda: self.scheduled_backup_wrapper(next_job_id))
|
||||||
|
|
||||||
def processing_error(self, error_msg):
|
def processing_error(self, error_msg):
|
||||||
self.run_all_btn.setEnabled(True)
|
self.run_all_btn.setEnabled(True)
|
||||||
self.cancel_btn.setEnabled(False)
|
self.cancel_btn.setEnabled(False)
|
||||||
@@ -1379,6 +1389,12 @@ class BackupApp(QMainWindow):
|
|||||||
self.run_single_btn.setEnabled(True)
|
self.run_single_btn.setEnabled(True)
|
||||||
self.log("!!! Kesalahan Thread: {}".format(error_msg))
|
self.log("!!! Kesalahan Thread: {}".format(error_msg))
|
||||||
|
|
||||||
|
# Check Queue even on error
|
||||||
|
if self.job_queue:
|
||||||
|
next_job_id = self.job_queue.pop(0)
|
||||||
|
self.log(">>> Memproses antrian (setelah error): Job ID {}".format(next_job_id))
|
||||||
|
QTimer.singleShot(100, lambda: self.scheduled_backup_wrapper(next_job_id))
|
||||||
|
|
||||||
# --- Tray ---
|
# --- Tray ---
|
||||||
def setup_tray(self):
|
def setup_tray(self):
|
||||||
self.tray_icon = QSystemTrayIcon(self)
|
self.tray_icon = QSystemTrayIcon(self)
|
||||||
|
|||||||
Reference in New Issue
Block a user