aboutsummaryrefslogtreecommitdiffstats
path: root/SoundboardUpdater.py
diff options
context:
space:
mode:
Diffstat (limited to 'SoundboardUpdater.py')
-rw-r--r--SoundboardUpdater.py176
1 files changed, 176 insertions, 0 deletions
diff --git a/SoundboardUpdater.py b/SoundboardUpdater.py
new file mode 100644
index 0000000..51674b6
--- /dev/null
+++ b/SoundboardUpdater.py
@@ -0,0 +1,176 @@
+# Soundboard Engine - customizable soundboard
+# Copyright (C) 2026 Vidhu Kant Sharma <vidhukant@vidhukant.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+import shutil
+import sys
+import zipfile
+from pathlib import Path
+
+import requests
+from PySide6.QtCore import Qt, QThread, Signal
+from PySide6.QtWidgets import QLabel, QPushButton, QVBoxLayout, QWidget
+
+from Soundboard import Soundboard
+
+
+class SoundboardUpdaterWindow(QWidget):
+ start_soundboard = Signal()
+
+ def __init__(self, soundboard: Soundboard):
+ super().__init__()
+
+ self.soundboard = soundboard
+
+ if soundboard.update_url.strip() != "":
+ self.worker = SoundboardUpdaterWorker(self.soundboard)
+ self.worker.start()
+ self.init_ui()
+ else:
+ self.start_soundboard.emit()
+
+ def init_ui(self):
+ self.setWindowTitle("Soundboard Updater")
+ self.setFixedSize(300, 180)
+
+ self.layout = QVBoxLayout(self)
+ self.status_label = QLabel("Updater coming soon")
+ self.layout.addWidget(self.status_label)
+
+ self.btn_layout = QVBoxLayout()
+
+ self.continue_btn = QPushButton("Continue Without Updating")
+ self.continue_btn.clicked.connect(self._start_soundboard)
+ self.btn_layout.addWidget(self.continue_btn)
+
+ self.layout.addLayout(self.btn_layout)
+
+ self.worker.no_update_found.connect(self._start_soundboard)
+ self.worker.update_found.connect(self._on_update_found)
+ self.worker.extracting.connect(
+ lambda: self.status_label.setText("Extracting update...")
+ )
+ self.worker.extracting.connect(
+ lambda: self.status_label.setText("Copying files...")
+ )
+ self.worker.update_completed.connect(self._on_update_completed)
+
+ def _on_update_found(self, size):
+ self.status_label.setText(f"Update found ({size})")
+
+ self.update_btn = QPushButton("Download Update")
+ self.update_btn.clicked.connect(self._on_download_update)
+
+ self.btn_layout.addWidget(self.update_btn)
+
+ def _on_download_update(self):
+ self.update_btn.setEnabled(False)
+ self.continue_btn.setEnabled(False)
+ self.status_label.setText("Downloading update...")
+ self.worker.start()
+
+ def _on_update_completed(self):
+ self.status_label.setText("Update completed! Please restart soundboard")
+ self.update_btn.setVisible(False)
+ self.continue_btn.setVisible(False)
+
+ def _start_soundboard(self):
+ self.start_soundboard.emit()
+ self.deleteLater()
+
+
+class SoundboardUpdaterWorker(QThread):
+ update_found = Signal(str)
+ no_update_found = Signal()
+ extracting = Signal()
+ copying = Signal()
+ update_completed = Signal()
+
+ def __init__(self, soundboard: Soundboard):
+ super().__init__()
+
+ self.soundboard = soundboard
+ self.mode = "CHECKING_UPDATE"
+
+ def run(self):
+ if self.mode == "CHECKING_UPDATE":
+ self._check_for_update()
+ else:
+ self._download_and_extract()
+
+ def _check_for_update(self):
+ try:
+ res = requests.get(self.soundboard.update_url + "SOUNDBOARD_VERSION")
+ remote_version = res.text.strip()
+
+ self.download_url = self.soundboard.update_url + remote_version + ".zip"
+
+ if remote_version != self.soundboard.soundboard_version:
+ head = requests.head(self.download_url, timeout=5)
+ update_size = self.format_size(
+ int(head.headers.get("content-length", 0))
+ )
+ self.mode = "FOUND_UPDATE"
+ self.update_found.emit(update_size)
+ else:
+ self.no_update_found.emit()
+ except Exception as e:
+ print(e)
+ sys.exit(1)
+
+ def _download_and_extract(self):
+ download_path = Path("temp_update.zip")
+ if download_path.exists():
+ download_path.unlink()
+
+ try:
+ res = requests.get(self.download_url)
+
+ with open(download_path, "wb") as f:
+ f.write(res.content)
+
+ self.extracting.emit()
+
+ with zipfile.ZipFile(download_path, "r") as zip_ref:
+ zip_ref.extractall(Path.cwd())
+
+ if download_path.exists():
+ download_path.unlink()
+
+ self.copying.emit()
+
+ extracted_path = Path("temp_update")
+
+ shutil.copy2(
+ extracted_path / "soundboard.json", self.soundboard.soundboard_path
+ )
+
+ shutil.rmtree(Path.cwd() / "assets")
+ shutil.copytree(extracted_path / "assets", Path.cwd() / "assets")
+
+ if extracted_path.exists():
+ shutil.rmtree(extracted_path)
+
+ self.update_completed.emit()
+ except Exception as e:
+ print(e)
+ sys.exit(1)
+
+ def format_size(self, bytes_size):
+ if bytes_size >= 1024 * 1024:
+ return f"{bytes_size / (1024 * 1024):.1f} MB"
+ elif bytes_size >= 1024:
+ return f"{bytes_size / 1024:.1f} KB"
+ return f"{bytes_size} B"