Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e0090fc1c3 | ||
|
|
a47b5c251c |
No files matched your search
@@ -105,6 +105,7 @@ temperature source (Tctl vs Tccd).
|
|||||||
- **Plug and play**: the service waits for the device, streams while present and keeps
|
- **Plug and play**: the service waits for the device, streams while present and keeps
|
||||||
waiting through unplug/replug; a udev rule grants access to the logged-in user
|
waiting through unplug/replug; a udev rule grants access to the logged-in user
|
||||||
- **Single instance**: launching the app again just raises the running window
|
- **Single instance**: launching the app again just raises the running window
|
||||||
|
- **Self-update from GitHub Releases** (check + one-click update)
|
||||||
- **Donate / GitHub buttons** in the app (window and tray menu)
|
- **Donate / GitHub buttons** in the app (window and tray menu)
|
||||||
- **AppImage build** script (`packaging/build_appimage.sh`, PySide6 bundled)
|
- **AppImage build** script (`packaging/build_appimage.sh`, PySide6 bundled)
|
||||||
|
|
||||||
@@ -208,6 +209,20 @@ docs/ reverse-engineering.md — full analysis (Indonesian)
|
|||||||
assets/ previews and icon
|
assets/ previews and icon
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Updates
|
||||||
|
|
||||||
|
The app keeps itself current from GitHub Releases:
|
||||||
|
|
||||||
|
- On start (when running from an AppImage) and from the **Periksa pembaruan** button or the
|
||||||
|
tray menu, it asks the GitHub API for the latest release and compares it with its version.
|
||||||
|
- When a newer release exists, a **Perbarui ke vX.Y.Z** button appears. One click downloads
|
||||||
|
the release asset, replaces the running AppImage atomically, refreshes the sender from the
|
||||||
|
new bundle and restarts the service. Restart the app to use the new version.
|
||||||
|
- Nothing is downloaded without that click, and no other files are touched.
|
||||||
|
|
||||||
|
If the check fails (offline, proxy), the message appears in the app log. `GAMEMAX_DEBUG=1`
|
||||||
|
also mirrors the log to stdout for headless runs.
|
||||||
|
|
||||||
## Support
|
## Support
|
||||||
|
|
||||||
If this project is useful to you:
|
If this project is useful to you:
|
||||||
|
|||||||
+19
-3
@@ -30,7 +30,7 @@ from PySide6.QtWidgets import (
|
|||||||
)
|
)
|
||||||
from PySide6.QtNetwork import QLocalServer, QLocalSocket
|
from PySide6.QtNetwork import QLocalServer, QLocalSocket
|
||||||
|
|
||||||
APP_VERSION = "1.2.0"
|
APP_VERSION = "1.2.2"
|
||||||
REPO = "oxpoe/Gamemax-linux-apps"
|
REPO = "oxpoe/Gamemax-linux-apps"
|
||||||
RELEASES_API = f"https://api.github.com/repos/{REPO}/releases/latest"
|
RELEASES_API = f"https://api.github.com/repos/{REPO}/releases/latest"
|
||||||
UPDATE_ASSET = "GAMEMAX-LCD-x86_64.AppImage"
|
UPDATE_ASSET = "GAMEMAX-LCD-x86_64.AppImage"
|
||||||
@@ -84,8 +84,21 @@ def version_tuple(tag):
|
|||||||
return tuple(int(p) for p in parts) or (0,)
|
return tuple(int(p) for p in parts) or (0,)
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_ca_bundle():
|
||||||
|
"""Bundel PyInstaller tidak menemukan CA sistem sendiri; arahkan OpenSSL ke sana."""
|
||||||
|
if os.environ.get("SSL_CERT_FILE"):
|
||||||
|
return
|
||||||
|
for path in ("/etc/ssl/certs/ca-certificates.crt", # Arch/Debian
|
||||||
|
"/etc/pki/tls/certs/ca-bundle.crt", # Fedora/openSUSE
|
||||||
|
"/etc/ssl/cert.pem"): # Alpine/BSD
|
||||||
|
if os.path.exists(path):
|
||||||
|
os.environ["SSL_CERT_FILE"] = path
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
def latest_release():
|
def latest_release():
|
||||||
"""(tag, url aset) dari rilis terbaru GitHub — dipanggil dari thread pekerja."""
|
"""(tag, url aset) dari rilis terbaru GitHub — dipanggil dari thread pekerja."""
|
||||||
|
ensure_ca_bundle()
|
||||||
req = urllib.request.Request(RELEASES_API, headers={
|
req = urllib.request.Request(RELEASES_API, headers={
|
||||||
"Accept": "application/vnd.github+json", "User-Agent": "gamemax-lcd"})
|
"Accept": "application/vnd.github+json", "User-Agent": "gamemax-lcd"})
|
||||||
with urllib.request.urlopen(req, timeout=10) as resp:
|
with urllib.request.urlopen(req, timeout=10) as resp:
|
||||||
@@ -487,6 +500,7 @@ class Window(QWidget):
|
|||||||
self.start_task(f"mengunduh {tag}", lambda: self.do_update(tag, url, target))
|
self.start_task(f"mengunduh {tag}", lambda: self.do_update(tag, url, target))
|
||||||
|
|
||||||
def do_update(self, tag, url, target):
|
def do_update(self, tag, url, target):
|
||||||
|
ensure_ca_bundle()
|
||||||
tmp = target + ".new"
|
tmp = target + ".new"
|
||||||
urllib.request.urlretrieve(url, tmp)
|
urllib.request.urlretrieve(url, tmp)
|
||||||
os.chmod(tmp, 0o755)
|
os.chmod(tmp, 0o755)
|
||||||
@@ -671,6 +685,8 @@ class Window(QWidget):
|
|||||||
|
|
||||||
def log(self, text):
|
def log(self, text):
|
||||||
self.logview.appendPlainText(text)
|
self.logview.appendPlainText(text)
|
||||||
|
if os.environ.get("GAMEMAX_DEBUG"):
|
||||||
|
print(text, flush=True)
|
||||||
|
|
||||||
def warn_once(self, text):
|
def warn_once(self, text):
|
||||||
if getattr(self, "_warned", None) != text:
|
if getattr(self, "_warned", None) != text:
|
||||||
@@ -882,8 +898,8 @@ def _main():
|
|||||||
("periksa pembaruan", w.do_check_update))
|
("periksa pembaruan", w.do_check_update))
|
||||||
for name, fn in steps:
|
for name, fn in steps:
|
||||||
try:
|
try:
|
||||||
fn()
|
out = fn()
|
||||||
print(f"[ OK ] {name}")
|
print(f"[ OK ] {name}" + (f" — {out}" if isinstance(out, str) and out else ""))
|
||||||
except Exception as exc: # noqa: BLE001
|
except Exception as exc: # noqa: BLE001
|
||||||
print(f"[GAGAL] {name}: {exc}")
|
print(f"[GAGAL] {name}: {exc}")
|
||||||
# jalur asinkron yang dipakai tombol: centang layar kosong + simpan
|
# jalur asinkron yang dipakai tombol: centang layar kosong + simpan
|
||||||
|
|||||||
Reference in new issue
Block a user