diff --git a/dist-winXP/ProBackup.exe b/dist-winXP/ProBackup.exe index 8f2cfa6..c987efb 100644 Binary files a/dist-winXP/ProBackup.exe and b/dist-winXP/ProBackup.exe differ diff --git a/license_manager.py b/license_manager.py index e2ad913..81cbe04 100644 --- a/license_manager.py +++ b/license_manager.py @@ -4,6 +4,7 @@ import subprocess import platform import os import base64 +import sys # 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. @@ -14,6 +15,35 @@ CREATE_NO_WINDOW = 0x08000000 class LicenseManager: @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(): """Gets the motherboard UUID using WMIC (Windows only).""" try: diff --git a/main.py b/main.py index cc23b7a..c28b3d2 100644 --- a/main.py +++ b/main.py @@ -278,8 +278,11 @@ class BackupApp(QMainWindow): # Ensure sql_tools folder exists 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: # Check if strict limit reached immediately @@ -1308,9 +1311,9 @@ class BackupApp(QMainWindow): def scheduled_backup_wrapper(self, job_id): # Determine if we can run if self.worker and self.worker.isRunning(): - # Concurrent jobs? For now let's skip or queue? - # Simple approach: Skip if busy. - self.log(">>> Jadwal Skip (App Sibuk) untuk Job ID: {}".format(job_id)) + # Concurrent jobs? Queue them! + self.log(">>> App Sibuk. Job ID {} masuk antrian.".format(job_id)) + self.job_queue.append(job_id) return # --- Check Trial Limit --- @@ -1370,6 +1373,13 @@ class BackupApp(QMainWindow): if hasattr(self, 'run_single_btn'): self.run_single_btn.setEnabled(True) 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): self.run_all_btn.setEnabled(True) @@ -1378,6 +1388,12 @@ class BackupApp(QMainWindow): if hasattr(self, 'run_single_btn'): self.run_single_btn.setEnabled(True) 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 --- def setup_tray(self):