Compare commits

...

65 Commits
0.11.5 ... main

Author SHA1 Message Date
github-actions[bot]
ba8072c669 Update version.json with commit count
Some checks failed
Bleeding Update version / update-version (push) Has been cancelled
Version Change Action / version-release (push) Has been cancelled
2025-03-03 04:12:50 +00:00
Nix
785e9be9f9
Update version.json 2025-03-03 01:12:37 -03:00
github-actions[bot]
0cbd000be4 Update version.json with commit count 2025-03-03 04:11:40 +00:00
Nixietab
52b635285e Moved the health checks to a OOP method 2025-03-03 01:10:53 -03:00
github-actions[bot]
67a16c008a Update version.json with commit count
Some checks failed
Bleeding Update version / update-version (push) Has been cancelled
Version Change Action / version-release (push) Has been cancelled
2025-03-01 08:34:21 +00:00
Nix
a4bd707461
Delete healtcheck.py 2025-03-01 05:34:12 -03:00
github-actions[bot]
1b27fffc96 Update version.json with commit count 2025-03-01 08:34:00 +00:00
Nix
fade5f86b7
Update picodulce.py 2025-03-01 05:33:51 -03:00
github-actions[bot]
823b438840 Update version.json with commit count 2025-03-01 08:33:36 +00:00
Nix
9a8c3f44d0
Update version.json 2025-03-01 05:33:25 -03:00
github-actions[bot]
6b65fb0d1e Update version.json with commit count 2025-03-01 08:33:18 +00:00
Nix
8247009d60
Delete locales directory 2025-03-01 05:33:07 -03:00
github-actions[bot]
e5c395d031 Update version.json with commit count 2025-03-01 08:06:58 +00:00
Nix
263e6eae07
Added rudimentary translations (#11)
* Update picodulce.py

* Update picodulce.py

* Update picodulce.py

* Update picodulce.py

* Create healtcheck.py

* Update picodulce.py

* Update version.json

* Create locales-go-here

* Add files via upload

* Update version.json

* Update picodulce.py

* Update healtcheck.py

* Update picodulce.py

* Added more locales

* Update version.json

* Update picodulce.py
2025-03-01 05:06:48 -03:00
github-actions[bot]
ec99488326 Update version.json with commit count
Some checks failed
Bleeding Update version / update-version (push) Has been cancelled
Version Change Action / version-release (push) Has been cancelled
2025-02-23 04:26:44 +00:00
Nix
61cd427beb
Delete locales directory 2025-02-23 01:26:35 -03:00
github-actions[bot]
cb2f5b52b3 Update version.json with commit count 2025-02-23 04:26:29 +00:00
Nix
ba40354a5d
Create bogosbinted.json 2025-02-23 01:26:19 -03:00
github-actions[bot]
0c151b058e Update version.json with commit count
Some checks failed
Bleeding Update version / update-version (push) Has been cancelled
Version Change Action / version-release (push) Has been cancelled
2025-02-12 19:13:11 +00:00
Nix
fc7f47d273
put the loading theme background in a separate function 2025-02-12 16:12:59 -03:00
github-actions[bot]
4f4ff35ee5 Update version.json with commit count
Some checks failed
Bleeding Update version / update-version (push) Has been cancelled
Version Change Action / version-release (push) Has been cancelled
2025-02-11 11:52:38 +00:00
Nix
8b9827b422
Update version.json 2025-02-11 08:52:27 -03:00
github-actions[bot]
892cbc4d07 Update version.json with commit count 2025-02-11 11:51:51 +00:00
Nix
f61f15fe7e
Update version.json 2025-02-11 08:51:39 -03:00
github-actions[bot]
d077a922c0 Update version.json with commit count 2025-02-11 11:50:38 +00:00
Nix
9b70503d26
auth done right 2025-02-11 08:50:27 -03:00
github-actions[bot]
ae9f25a7a8 Update version.json with commit count 2025-02-11 11:49:55 +00:00
Nix
00ed5f97b9
authentication done right 2025-02-11 08:49:44 -03:00
github-actions[bot]
5dbbfd5d87 Update version.json with commit count 2025-02-11 05:45:27 +00:00
Nix
37a1c5b0df
Update version.json 2025-02-11 02:45:16 -03:00
github-actions[bot]
f2a1989993 Update version.json with commit count 2025-02-11 05:38:13 +00:00
Nix
3d40ce7df3
fixxed the need of re-opening the settings menu to refresh the themes list 2025-02-11 02:38:02 -03:00
github-actions[bot]
36ff8896ef Update version.json with commit count
Some checks failed
Bleeding Update version / update-version (push) Has been cancelled
Version Change Action / version-release (push) Has been cancelled
2025-02-04 16:42:58 +00:00
Nix
5f59acf0b4
Merge pull request #10 from refrigerador67/main
Removed popup when checking for updates on startup when in latest version
2025-02-04 13:42:46 -03:00
github-actions[bot]
c48a193d9a Update version.json with commit count 2025-02-04 16:39:08 +00:00
refrigerador67
47a843c669
Removed popup at latest version when checking updates at startup 2025-02-04 13:38:57 -03:00
github-actions[bot]
52be28bb6c Update version.json with commit count 2025-02-04 16:30:53 +00:00
github-actions[bot]
6522b70066 Update version.json with commit count
Some checks failed
Bleeding Update version / update-version (push) Has been cancelled
Version Change Action / version-release (push) Has been cancelled
2025-01-29 02:03:17 +00:00
Nix
8d486a9af2
Update version.json 2025-01-28 23:03:07 -03:00
github-actions[bot]
97393e4ae7 Update version.json with commit count 2025-01-29 02:02:18 +00:00
Nix
e35120bb36
fixxed bleeding edge 2025-01-28 23:02:07 -03:00
github-actions[bot]
f2cfb3ceb3 Update version.json with commit count 2025-01-29 01:47:18 +00:00
Nix
15246cd535
Create Bleeding-Job.yaml 2025-01-28 22:47:06 -03:00
Nix
3edcd10c12
Update Build.yml 2025-01-28 22:43:44 -03:00
Nix
874e513b47
Update requirements.txt 2025-01-21 10:05:08 -03:00
Nix
a10318e00d
Update picodulce.py 2025-01-20 15:11:04 -03:00
Nix
7608b647fe
minnor fix to the instance stuff
Some checks failed
Version Change Action / version-release (push) Has been cancelled
2025-01-14 02:22:32 -03:00
Nix
60d16326b0
Update picodulce.py 2025-01-14 02:21:55 -03:00
Nix
0d300f0435
Merge pull request #9 from refrigerador67/main
Update README.md
2025-01-14 01:56:33 -03:00
refrigerador67
3123ed30cf
Update README.md 2025-01-14 04:50:05 +00:00
Nix
db41858aae
Update picodulce.py 2025-01-14 01:07:50 -03:00
Nix
8c0a794202
Merge pull request #8 from refrigerador67/main
Added PKGBUILD
2025-01-14 00:29:43 -03:00
refrigerador67
514f6427ab
Update README.md 2025-01-14 03:25:54 +00:00
refrigerador67
9920636b9c
Merge branch 'nixietab:main' into main 2025-01-14 03:22:20 +00:00
refrigerador67
5182e42f81
Added PKGBUILD 2025-01-14 03:21:48 +00:00
Nix
d2f3aa6a49
Update version.json 2025-01-14 00:15:39 -03:00
Nix
8167462838
added animated themes support! 2025-01-13 20:19:25 -03:00
Nix
51dc126c8a
minnor fixes 2025-01-09 23:20:35 -03:00
Nix
5202f8fb25
Add files via upload 2025-01-08 22:36:18 -03:00
Nix
46d053d952
minor changes 2025-01-06 16:09:57 -03:00
Nix
82f44105b5
Update version.json
Some checks failed
Version Change Action / version-release (push) Has been cancelled
2025-01-06 06:40:03 -03:00
Nix
fd2e57ffb3
Update picodulce.py 2025-01-06 06:38:11 -03:00
Nix
372075132f
Update picodulce.py 2025-01-06 00:54:35 -03:00
Nix
9c66a3eeb8
theme previews 2025-01-05 06:49:40 -03:00
refrigerador67
a19b8a1545
Minor UI changes 2024-12-18 23:11:00 -03:00
9 changed files with 827 additions and 226 deletions

36
.github/workflows/Bleeding-Job.yaml vendored Normal file
View File

@ -0,0 +1,36 @@
name: Bleeding Update version
on:
push:
branches:
- main
jobs:
update-version:
runs-on: ubuntu-latest
steps:
- name: Check out the repository
uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.x'
- name: Update version.json
run: |
git fetch --prune --unshallow
commit_count=$(git rev-list --count HEAD)
version=$(jq -r '.version' version.json)
jq --arg versionBleeding "$version-$commit_count" '. + {versionBleeding: $versionBleeding}' version.json > version.tmp && mv version.tmp version.json
- name: Commit and push changes
run: |
git config --global user.name 'github-actions[bot]'
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
git add version.json
git commit -m "Update version.json with commit count"
git push
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -9,6 +9,8 @@ jobs:
version-release:
runs-on: windows-latest # Use Windows 10 runner
if: github.actor != 'github-actions[bot]' # Only run if the actor is not the GitHub Actions bot
steps:
- name: Checkout repository
uses: actions/checkout@v3
@ -36,7 +38,7 @@ jobs:
run: |
dir actions-temp
dir
- name: Get version and name from version.json
id: version_info
run: |

76
PKGBUILD Normal file
View File

@ -0,0 +1,76 @@
pkgname=picodulce
pkgver=0.11.7
pkgrel=1
pkgdesc="Launcher for Minecraft based on the picomc library"
arch=('x86_64')
OPTIONS=(!strip !docs libtool emptydirs)
url="https://github.com/nixietab/picodulce"
license=('MIT') # Replace with your project's license
depends=('python' 'python-virtualenv' 'xdg-utils')
makedepends=('git')
source=("git+https://github.com/nixietab/picodulce.git")
sha256sums=('SKIP')
package() {
cd "$srcdir/$pkgname"
# Create a directory for the application in the user's home directory
install -dm755 "$pkgdir/usr/share/$pkgname"
# Copy all project files to the created directory
cp -r . "$pkgdir/usr/share/$pkgname"
# Create a virtual environment
python -m venv "$pkgdir/usr/share/$pkgname/venv"
# Activate the virtual environment and install dependencies
source "$pkgdir/usr/share/$pkgname/venv/bin/activate"
pip install -r requirements.txt
# Create a run.sh script
install -Dm755 /dev/stdin "$pkgdir/usr/share/$pkgname/run.sh" <<EOF
#!/bin/bash
if [ ! -d "venv" ]; then
echo "venv folder does not exist. Creating virtual environment..."
python3 -m venv venv
source venv/bin/activate
echo "Installing required packages..."
pip install -r requirements.txt
else
source venv/bin/activate
fi
python picodulce.py
EOF
# Make the run.sh script executable
chmod +x "$pkgdir/usr/share/$pkgname/run.sh"
# Create a desktop entry for the application
install -Dm644 /dev/stdin "$pkgdir/usr/share/applications/$pkgname.desktop" <<EOF
[Desktop Entry]
Name=Picodulce
Exec=/usr/share/picodulce/run.sh
Icon=/usr/share/picodulce/launcher_icon.ico
Terminal=true
Type=Application
Comment=Picodulce Launcher
Categories=Game;
EOF
# Ensure the normal user has permission to write to the picodulce folder
chown -R "$USER:$USER" "$pkgdir/usr/share/$pkgname"
chmod -R u+w "$pkgdir/usr/share/$pkgname"
#Install into bin
install -Dm755 /dev/stdin "$pkgdir/usr/bin/picodulce" <<EOF
#!/bin/bash
cd /usr/share/picodulce/
exec ./run.sh
EOF
}
# vim:set ts=2 sw=2 et:

View File

@ -40,7 +40,21 @@
- **Custom Theme Support**: Create and apply personalized themes with ease. A dedicated repository and guide are [available to help you get started.](https://github.com/nixietab/picodulce-themes)
# Installation
If you are on windows you may be more interested in a [installer](https://github.com/nixietab/picodulce/releases/latest)
## Windows
For Windows systems using the [installer](https://github.com/nixietab/picodulce/releases/latest) is recommended
## Arch Linux
The package is available in the [AUR](https://aur.archlinux.org/packages/picodulce) as ```picodulce```
For installing on Arch without using an AUR helper a PKGBUILD is provided
```
git clone https://aur.archlinux.org/picodulce.git
cd picodulce
makepkg -si
```
## Other OS
### 1. Clone the repository

244
authser.py Normal file
View File

@ -0,0 +1,244 @@
import sys
import subprocess
import re
from PyQt5.QtWidgets import (QApplication, QDialog, QLabel, QVBoxLayout,
QPushButton, QLineEdit, QMessageBox)
from PyQt5.QtCore import QThread, pyqtSignal, Qt, QUrl, QObject
from PyQt5.QtGui import QDesktopServices
class AuthenticationParser:
@staticmethod
def clean_ansi(text):
ansi_clean = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')
printable_clean = re.compile(r'[^\x20-\x7E\n]')
text = ansi_clean.sub('', text)
text = printable_clean.sub('', text)
return text.strip()
@staticmethod
def is_auth_error(output):
cleaned_output = AuthenticationParser.clean_ansi(output)
return "AADSTS70016" in cleaned_output and "not yet been authorized" in cleaned_output
@staticmethod
def parse_auth_output(output):
cleaned_output = AuthenticationParser.clean_ansi(output)
if AuthenticationParser.is_auth_error(cleaned_output):
return None
pattern = r"https://[^\s]+"
code_pattern = r"code\s+([A-Z0-9]+)"
url_match = re.search(pattern, cleaned_output)
code_match = re.search(code_pattern, cleaned_output, re.IGNORECASE)
if url_match and code_match:
return {
'url': url_match.group(0),
'code': code_match.group(1)
}
return None
class AuthDialog(QDialog):
def __init__(self, url, code, parent=None, error_mode=False):
super().__init__(parent)
self.setWindowTitle("Microsoft Authentication")
self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint)
self.setModal(True)
self.setup_ui(url, code, error_mode)
def setup_ui(self, url, code, error_mode):
layout = QVBoxLayout(self)
if error_mode:
error_label = QLabel("Error in Login - Please try again")
error_label.setStyleSheet("QLabel { color: red; font-weight: bold; }")
layout.addWidget(error_label)
instructions = QLabel(
"To authenticate your Microsoft Account:\n\n"
"1. Click 'Open Authentication Page' or visit:\n"
"2. Copy the code below\n"
"3. Paste the code on the Microsoft website\n"
"4. After completing authentication, click 'I've Completed Authentication'"
)
instructions.setWordWrap(True)
layout.addWidget(instructions)
url_label = QLabel(url)
url_label.setTextInteractionFlags(Qt.TextSelectableByMouse)
url_label.setWordWrap(True)
layout.addWidget(url_label)
self.code_input = QLineEdit(code)
self.code_input.setReadOnly(True)
self.code_input.setAlignment(Qt.AlignCenter)
self.code_input.setStyleSheet("""
QLineEdit {
font-size: 16pt;
font-weight: bold;
padding: 5px;
}
""")
layout.addWidget(self.code_input)
copy_button = QPushButton("Copy Code")
copy_button.clicked.connect(self.copy_code)
layout.addWidget(copy_button)
open_url_button = QPushButton("Open Authentication Page")
open_url_button.clicked.connect(lambda: self.open_url(url))
layout.addWidget(open_url_button)
continue_button = QPushButton("I've Completed Authentication")
continue_button.clicked.connect(self.accept)
layout.addWidget(continue_button)
def copy_code(self):
clipboard = QApplication.clipboard()
clipboard.setText(self.code_input.text())
def open_url(self, url):
QDesktopServices.openUrl(QUrl(url))
class AuthenticationThread(QThread):
auth_data_received = pyqtSignal(dict)
error_occurred = pyqtSignal(str)
auth_error_detected = pyqtSignal(str)
finished = pyqtSignal()
def __init__(self, account):
super().__init__()
self.account = account
self.process = None
self.is_running = True
self.current_output = ""
self.waiting_for_auth = False
def run(self):
try:
command = f'picomc account authenticate {self.account}'
self.process = subprocess.Popen(
command,
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
stdin=subprocess.PIPE,
text=True,
bufsize=1,
universal_newlines=True
)
self.current_output = ""
while self.is_running and self.process.poll() is None:
line = self.process.stdout.readline()
if line:
self.current_output += line
if not self.waiting_for_auth:
parsed_data = AuthenticationParser.parse_auth_output(self.current_output)
if parsed_data:
self.auth_data_received.emit(parsed_data)
self.waiting_for_auth = True
self.current_output = ""
elif AuthenticationParser.is_auth_error(self.current_output):
self.auth_error_detected.emit(self.current_output)
self.waiting_for_auth = False
self.current_output = ""
self.process.wait()
self.finished.emit()
except Exception as e:
self.error_occurred.emit(str(e))
self.finished.emit()
def send_enter(self):
if self.process and self.process.poll() is None:
self.process.stdin.write("\n")
self.process.stdin.flush()
def stop(self):
self.is_running = False
if self.process:
self.process.terminate()
class MinecraftAuthenticator(QObject): # Changed to inherit from QObject
auth_finished = pyqtSignal(bool) # Add signal for completion
def __init__(self, parent=None):
super().__init__(parent)
self.auth_thread = None
self.current_auth_data = None
self.auth_dialog = None
self.success = False
def authenticate(self, username):
"""
Start the authentication process for the given username
Returns immediately, authentication result will be emitted via auth_finished signal
"""
self.success = False
self.auth_thread = AuthenticationThread(username)
self.auth_thread.auth_data_received.connect(self.show_auth_dialog)
self.auth_thread.auth_error_detected.connect(self.handle_auth_error)
self.auth_thread.error_occurred.connect(self.show_error)
self.auth_thread.finished.connect(self.on_authentication_finished)
self.auth_thread.start()
def show_auth_dialog(self, auth_data):
self.current_auth_data = auth_data
if self.auth_dialog is not None:
self.auth_dialog.close()
self.auth_dialog = None
self.auth_dialog = AuthDialog(auth_data['url'], auth_data['code'])
if self.auth_dialog.exec_() == QDialog.Accepted:
self.auth_thread.send_enter()
def handle_auth_error(self, output):
if self.current_auth_data:
if self.auth_dialog is not None:
self.auth_dialog.close()
self.auth_dialog = None
self.auth_dialog = AuthDialog(
self.current_auth_data['url'],
self.current_auth_data['code'],
error_mode=True
)
if self.auth_dialog.exec_() == QDialog.Accepted:
self.auth_thread.send_enter()
def show_error(self, error_message):
QMessageBox.critical(None, "Error", f"Authentication error: {error_message}")
self.success = False
self.auth_finished.emit(False)
def on_authentication_finished(self):
if self.auth_dialog is not None:
self.auth_dialog.close()
self.auth_dialog = None
if self.auth_thread:
self.auth_thread.stop()
self.auth_thread = None
self.success = True
self.auth_finished.emit(True)
def cleanup(self):
if self.auth_dialog is not None:
self.auth_dialog.close()
self.auth_dialog = None
if self.auth_thread and self.auth_thread.isRunning():
self.auth_thread.stop()
self.auth_thread.wait()
# Example usage
if __name__ == '__main__':
authenticator = MinecraftAuthenticator()
authenticator.authenticate("TestUser")

119
healthcheck.py Normal file
View File

@ -0,0 +1,119 @@
import os
import json
import requests
class HealthCheck:
def __init__(self):
self.config = None
def check_config_file(self):
config_path = "config.json"
default_config = {
"IsRCPenabled": False,
"CheckUpdate": False,
"IsBleeding": False,
"LastPlayed": "",
"IsFirstLaunch": True,
"Instance": "default",
"Theme": "Dark.json",
"ThemeBackground": True,
"ThemeRepository": "https://raw.githubusercontent.com/nixietab/picodulce-themes/main/repo.json",
"Locale": "en"
}
# Step 1: Check if the file exists; if not, create it with default values
if not os.path.exists(config_path):
with open(config_path, "w") as config_file:
json.dump(default_config, config_file, indent=4)
self.config = default_config
return
# Step 2: Try loading the config file, handle invalid JSON
try:
with open(config_path, "r") as config_file:
self.config = json.load(config_file)
except (json.JSONDecodeError, ValueError):
# File is corrupted, overwrite it with default configuration
with open(config_path, "w") as config_file:
json.dump(default_config, config_file, indent=4)
self.config = default_config
return
# Step 3: Check for missing keys and add defaults if necessary
updated = False
for key, value in default_config.items():
if key not in self.config: # Field is missing
self.config[key] = value
updated = True
# Step 4: Save the repaired config back to the file
if updated:
with open(config_path, "w") as config_file:
json.dump(self.config, config_file, indent=4)
def themes_integrity(self):
# Define folder and file paths
themes_folder = "themes"
dark_theme_file = os.path.join(themes_folder, "Dark.json")
native_theme_file = os.path.join(themes_folder, "Native.json")
# Define the default content for Dark.json
dark_theme_content = {
"manifest": {
"name": "Dark",
"description": "The default picodulce launcher theme",
"author": "Nixietab",
"license": "MIT"
},
"palette": {
"Window": "#353535",
"WindowText": "#ffffff",
"Base": "#191919",
"AlternateBase": "#353535",
"ToolTipBase": "#ffffff",
"ToolTipText": "#ffffff",
"Text": "#ffffff",
"Button": "#353535",
"ButtonText": "#ffffff",
"BrightText": "#ff0000",
"Link": "#2a82da",
"Highlight": "#4bb679",
"HighlightedText": "#ffffff"
},
"background_image_base64": ""
}
# Define the default content for Native.json
native_theme_content = {
"manifest": {
"name": "Native",
"description": "The native looks of your OS",
"author": "Your Qt Style",
"license": "Any"
},
"palette": {}
}
# Step 1: Ensure the themes folder exists
if not os.path.exists(themes_folder):
print(f"Creating folder: {themes_folder}")
os.makedirs(themes_folder)
# Step 2: Ensure Dark.json exists
if not os.path.isfile(dark_theme_file):
print(f"Creating file: {dark_theme_file}")
with open(dark_theme_file, "w", encoding="utf-8") as file:
json.dump(dark_theme_content, file, indent=2)
print("Dark.json has been created successfully.")
# Step 3: Ensure Native.json exists
if not os.path.isfile(native_theme_file):
print(f"Creating file: {native_theme_file}")
with open(native_theme_file, "w", encoding="utf-8") as file:
json.dump(native_theme_content, file, indent=2)
print("Native.json has been created successfully.")
# Check if both files exist and print OK message
if os.path.isfile(dark_theme_file) and os.path.isfile(native_theme_file):
print("Theme Integrity OK")

View File

@ -3,12 +3,17 @@ import subprocess
import threading
from threading import Thread
import logging
import re
import shutil
import platform
import requests
import json
import os
import time
from authser import MinecraftAuthenticator
from healthcheck import HealthCheck
from PyQt5.QtWidgets import QApplication, QComboBox, QWidget, QInputDialog, QVBoxLayout, QListWidget, QPushButton, QMessageBox, QDialog, QHBoxLayout, QLabel, QLineEdit, QCheckBox, QTabWidget, QFrame, QSpacerItem, QSizePolicy, QMainWindow, QGridLayout, QTextEdit, QListWidget, QListWidgetItem, QMenu
from PyQt5.QtGui import QFont, QIcon, QColor, QPalette, QMovie, QPixmap, QDesktopServices, QBrush
from PyQt5.QtCore import Qt, QObject, pyqtSignal, QThread, QUrl, QMetaObject, Q_ARG, QByteArray, QSize
@ -20,10 +25,14 @@ class PicomcVersionSelector(QWidget):
def __init__(self):
self.current_state = "menu"
self.open_dialogs = []
self.check_config_file()
self.themes_integrity()
# Set up and use the health_check module
health_checker = HealthCheck()
health_checker.themes_integrity()
health_checker.check_config_file()
self.config = health_checker.config
themes_folder = "themes"
theme_file = self.config.get("Theme", "Dark.json")
# Ensure the theme file exists in the themes directory
@ -47,6 +56,13 @@ class PicomcVersionSelector(QWidget):
discord_rcp_thread.daemon = True # Make the thread a daemon so it terminates when the main program exits
discord_rcp_thread.start()
if self.config.get("IsFirstLaunch", False):
self.FirstLaunch()
self.authenticator = MinecraftAuthenticator(self)
self.authenticator.auth_finished.connect(self._on_auth_finished)
def load_theme_from_file(self, file_path, app):
self.theme = {}
# Check if the file exists, else load 'Dark.json'
@ -104,76 +120,79 @@ class PicomcVersionSelector(QWidget):
stylesheet = self.theme["stylesheet"]
app.setStyleSheet(stylesheet)
else:
print("No 'stylesheet' section found in the theme file.")
print("Theme dosn't seem to have a stylesheet")
def themes_integrity(self):
# Define folder and file paths
themes_folder = "themes"
dark_theme_file = os.path.join(themes_folder, "Dark.json")
native_theme_file = os.path.join(themes_folder, "Native.json")
def FirstLaunch(self):
try:
self.config_path = "config.json"
print("Running picomc instance create default command...")
# Run the command using subprocess
result = subprocess.run(["picomc", "instance", "create", "default"], check=True, capture_output=True, text=True)
# Print the output of the command
print("Command output:", result.stdout)
# Change the value of IsFirstLaunch to False
self.config["IsFirstLaunch"] = False
print("IsFirstLaunch set to False")
# Define the default content for Dark.json
dark_theme_content = {
"manifest": {
"name": "Dark",
"description": "The default picodulce launcher theme",
"author": "Nixietab",
"license": "MIT"
},
"palette": {
"Window": "#353535",
"WindowText": "#ffffff",
"Base": "#191919",
"AlternateBase": "#353535",
"ToolTipBase": "#ffffff",
"ToolTipText": "#ffffff",
"Text": "#ffffff",
"Button": "#353535",
"ButtonText": "#ffffff",
"BrightText": "#ff0000",
"Link": "#2a82da",
"Highlight": "#4bb679",
"HighlightedText": "#ffffff"
},
"background_image_base64": ""
}
# Save the updated config to the config.json file
with open(self.config_path, 'w') as f:
json.dump(self.config, f, indent=4)
print("Configuration saved to", self.config_path)
# Define the default content for Native.json
native_theme_content = {
"manifest": {
"name": "Native",
"description": "The native looks of your OS",
"author": "Your Qt Style",
"license": "Any"
},
"palette": {}
}
except subprocess.CalledProcessError as e:
print("An error occurred while creating the instance.")
print("Error output:", e.stderr)
# Step 1: Ensure the themes folder exists
if not os.path.exists(themes_folder):
print(f"Creating folder: {themes_folder}")
os.makedirs(themes_folder)
else:
print(f"Folder already exists: {themes_folder}")
def resize_event(self, event):
if hasattr(self, 'movie_label'):
self.movie_label.setGeometry(0, 0, 400, 320)
event.accept() # Accept the resize event
# Step 2: Ensure Dark.json exists
if not os.path.isfile(dark_theme_file):
print(f"Creating file: {dark_theme_file}")
with open(dark_theme_file, "w", encoding="utf-8") as file:
json.dump(dark_theme_content, file, indent=2)
print("Dark.json has been created successfully.")
else:
print(f"File already exists: {dark_theme_file}")
def load_theme_background(self):
"""Load and set the theme background image from base64 data in the theme configuration."""
if not self.config.get("ThemeBackground", False): # Default to False if ThemeBackground is missing
return
# Step 3: Ensure Native.json exists
if not os.path.isfile(native_theme_file):
print(f"Creating file: {native_theme_file}")
with open(native_theme_file, "w", encoding="utf-8") as file:
json.dump(native_theme_content, file, indent=2)
print("Native.json has been created successfully.")
else:
print(f"File already exists: {native_theme_file}")
# Get the base64 string for the background image from the theme file
theme_background_base64 = self.theme.get("background_image_base64", "")
if not theme_background_base64:
print("No background GIF base64 string found in the theme file.")
return
try:
# Decode the base64 string to get the binary data
background_image_data = QByteArray.fromBase64(theme_background_base64.encode())
temp_gif_path = "temp.gif" # Write the gif into a temp file because Qt stuff
with open(temp_gif_path, 'wb') as temp_gif_file:
temp_gif_file.write(background_image_data)
# Create a QMovie object from the temporary file
movie = QMovie(temp_gif_path)
if movie.isValid():
self.setAutoFillBackground(True)
palette = self.palette()
# Set the QMovie to a QLabel
self.movie_label = QLabel(self)
self.movie_label.setMovie(movie)
self.movie_label.setGeometry(0, 0, movie.frameRect().width(), movie.frameRect().height())
self.movie_label.setScaledContents(True) # Ensure the QLabel scales its contents
movie.start()
# Use the QLabel pixmap as the brush texture
brush = QBrush(QPixmap(movie.currentPixmap()))
brush.setStyle(Qt.TexturePattern)
palette.setBrush(QPalette.Window, brush)
self.setPalette(palette)
# Adjust the QLabel size when the window is resized
self.movie_label.resizeEvent = self.resize_event
else:
print("Error: Failed to load background GIF from base64 string.")
except Exception as e:
print(f"Error: Failed to decode and set background GIF. {e}")
def init_ui(self):
self.setWindowTitle('PicoDulce Launcher') # Change window title
@ -190,27 +209,8 @@ class PicomcVersionSelector(QWidget):
with open("config.json", "r") as config_file:
config = json.load(config_file)
if config.get("ThemeBackground", False): # Default to False if ThemeBackground is missing
# Get the base64 string for the background image from the theme file
theme_background_base64 = self.theme.get("background_image_base64", "")
if theme_background_base64:
try:
# Decode the base64 string and create a QPixmap
background_image_data = QByteArray.fromBase64(theme_background_base64.encode())
pixmap = QPixmap()
if pixmap.loadFromData(background_image_data):
self.setAutoFillBackground(True)
palette = self.palette()
palette.setBrush(QPalette.Window, QBrush(pixmap.scaled(
self.size(), Qt.KeepAspectRatioByExpanding, Qt.SmoothTransformation
)))
self.setPalette(palette)
else:
print("Error: Failed to load background image from base64 string.")
except Exception as e:
print(f"Error: Failed to decode and set background image. {e}")
else:
print("No background image base64 string found in the theme file.")
# Load theme background
self.load_theme_background()
# Create title label
title_label = QLabel('PicoDulce Launcher') # Change label text
@ -228,7 +228,6 @@ class PicomcVersionSelector(QWidget):
# Create play button for installed versions
self.play_button = QPushButton('Play')
self.play_button.setFocusPolicy(Qt.NoFocus) # Set focus policy to prevent highlighting
self.play_button.clicked.connect(self.play_instance)
highlight_color = self.palette().color(QPalette.Highlight)
self.play_button.setStyleSheet(f"background-color: {highlight_color.name()}; color: white;")
@ -276,7 +275,7 @@ class PicomcVersionSelector(QWidget):
main_layout.setSpacing(20)
self.setLayout(main_layout)
def keyPressEvent(self, event):
focus_widget = self.focusWidget()
if event.key() == Qt.Key_Down:
@ -291,48 +290,6 @@ class PicomcVersionSelector(QWidget):
else:
super().keyPressEvent(event)
def check_config_file(self):
config_path = "config.json"
default_config = {
"IsRCPenabled": False,
"CheckUpdate": False,
"LastPlayed": "",
"Instance": "default",
"Theme": "Dark.json",
"ThemeBackground": True,
"ThemeRepository": "https://raw.githubusercontent.com/nixietab/picodulce-themes/main/repo.json"
}
# Step 1: Check if the file exists; if not, create it with default values
if not os.path.exists(config_path):
with open(config_path, "w") as config_file:
json.dump(default_config, config_file, indent=4)
self.config = default_config
return
# Step 2: Try loading the config file, handle invalid JSON
try:
with open(config_path, "r") as config_file:
self.config = json.load(config_file)
except (json.JSONDecodeError, ValueError):
# File is corrupted, overwrite it with default configuration
with open(config_path, "w") as config_file:
json.dump(default_config, config_file, indent=4)
self.config = default_config
return
# Step 3: Check for missing keys and add defaults if necessary
updated = False
for key, value in default_config.items():
if key not in self.config: # Field is missing
self.config[key] = value
updated = True
# Step 4: Save the repaired config back to the file
if updated:
with open(config_path, "w") as config_file:
json.dump(self.config, config_file, indent=4)
def open_settings_dialog(self):
dialog = QDialog(self)
dialog.setWindowTitle('Settings')
@ -357,9 +314,14 @@ class PicomcVersionSelector(QWidget):
check_updates_checkbox = QCheckBox('Check Updates on Start')
check_updates_checkbox.setChecked(self.config.get("CheckUpdate", False))
bleeding_edge_checkbox = QCheckBox('Bleeding Edge')
bleeding_edge_checkbox.setChecked(self.config.get("IsBleeding", False))
bleeding_edge_checkbox.stateChanged.connect(lambda: self.show_bleeding_edge_popup(bleeding_edge_checkbox))
settings_layout.addWidget(title_label)
settings_layout.addWidget(discord_rcp_checkbox)
settings_layout.addWidget(check_updates_checkbox)
settings_layout.addWidget(bleeding_edge_checkbox)
# Add buttons in the settings tab
update_button = QPushButton('Check for updates')
@ -391,24 +353,27 @@ class PicomcVersionSelector(QWidget):
# QListWidget to display available themes
json_files_label = QLabel('Installed Themes:')
json_files_list_widget = QListWidget()
self.json_files_list_widget = QListWidget()
# Track selected theme
self.selected_theme = theme_filename # Default to current theme
# Build the list of themes
themes_list = self.build_themes_list()
# Populate themes initially
self.populate_themes(json_files_list_widget)
self.populate_themes(self.json_files_list_widget, themes_list)
# Update current theme label when a theme is selected
json_files_list_widget.itemClicked.connect(
lambda: self.on_theme_selected(json_files_list_widget, current_theme_label)
self.json_files_list_widget.itemClicked.connect(
lambda: self.on_theme_selected(self.json_files_list_widget, current_theme_label)
)
# Add widgets to the layout
customization_layout.addWidget(theme_background_checkbox)
customization_layout.addWidget(current_theme_label)
customization_layout.addWidget(json_files_label)
customization_layout.addWidget(json_files_list_widget)
customization_layout.addWidget(self.json_files_list_widget)
# Button to download themes
download_themes_button = QPushButton("Download More Themes")
@ -429,7 +394,8 @@ class PicomcVersionSelector(QWidget):
discord_rcp_checkbox.isChecked(),
check_updates_checkbox.isChecked(),
theme_background_checkbox.isChecked(),
self.selected_theme # Pass the selected theme here
self.selected_theme, # Pass the selected theme here
bleeding_edge_checkbox.isChecked() # Pass the bleeding edge setting here
)
)
@ -441,9 +407,20 @@ class PicomcVersionSelector(QWidget):
dialog.setLayout(main_layout)
dialog.exec_()
def populate_themes(self, json_files_list_widget):
def show_bleeding_edge_popup(self, checkbox):
if checkbox.isChecked():
response = QMessageBox.question(
self,
"Bleeding Edge Feature",
"Enabling 'Bleeding Edge' mode may expose you to unstable and experimental features. Do you want to enable it anyway? In normal mode, updates are only downloaded when a stable release is made.",
QMessageBox.Yes | QMessageBox.No
)
if response == QMessageBox.No:
checkbox.setChecked(False)
def build_themes_list(self):
themes_folder = os.path.join(os.getcwd(), "themes")
json_files_list_widget.clear()
themes_list = []
if os.path.exists(themes_folder):
json_files = [f for f in os.listdir(themes_folder) if f.endswith('.json')]
for json_file in json_files:
@ -459,15 +436,21 @@ class PicomcVersionSelector(QWidget):
# Create display text and list item
display_text = f"{name}\n{description}\nBy: {author}"
list_item = QListWidgetItem(display_text)
list_item.setData(Qt.UserRole, json_file) # Store the JSON filename as metadata
themes_list.append((display_text, json_file))
return themes_list
# Style the name in bold
font = QFont()
font.setBold(False)
list_item.setFont(font)
def populate_themes(self, json_files_list_widget, themes_list):
json_files_list_widget.clear()
for display_text, json_file in themes_list:
list_item = QListWidgetItem(display_text)
list_item.setData(Qt.UserRole, json_file) # Store the JSON filename as metadata
json_files_list_widget.addItem(list_item)
# Style the name in bold
font = QFont()
font.setBold(False)
list_item.setFont(font)
json_files_list_widget.addItem(list_item)
# Apply spacing and styling to the list
json_files_list_widget.setStyleSheet("""
@ -485,53 +468,63 @@ class PicomcVersionSelector(QWidget):
if selected_item:
self.selected_theme = selected_item.data(Qt.UserRole)
current_theme_label.setText(f"Current Theme: {self.selected_theme}")
## REPOSITORY BLOCK BEGGINS
## REPOSITORY BLOCK BEGGINS
def download_themes_window(self):
# Create a QDialog to open the themes window
dialog = QDialog(self)
dialog.setWindowTitle("Themes Repository")
dialog.setGeometry(100, 100, 400, 300)
dialog.setGeometry(100, 100, 800, 600)
# Layout setup for the new window
layout = QVBoxLayout()
main_layout = QHBoxLayout(dialog)
# List widget to display themes
self.theme_list = QListWidget(dialog)
self.theme_list.setSelectionMode(QListWidget.SingleSelection)
self.theme_list.clicked.connect(self.on_theme_click)
layout.addWidget(self.theme_list)
main_layout.addWidget(self.theme_list)
# Label to display the details of the selected theme
self.details_label = QLabel(dialog) # Define the label here
layout.addWidget(self.details_label)
right_layout = QVBoxLayout()
self.details_label = QLabel(dialog)
self.details_label.setWordWrap(True)
self.details_label.setStyleSheet("padding: 10px;")
right_layout.addWidget(self.details_label)
self.image_label = QLabel(dialog)
self.image_label.setAlignment(Qt.AlignCenter)
self.image_label.setStyleSheet("padding: 10px;")
right_layout.addWidget(self.image_label)
# Download button to download the selected theme's JSON
download_button = QPushButton("Download Theme", dialog)
download_button.clicked.connect(self.theme_download)
layout.addWidget(download_button)
right_layout.addWidget(download_button)
# Add a spacer to push the button to the bottom
spacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
right_layout.addItem(spacer)
main_layout.addLayout(right_layout)
dialog.setLayout(main_layout)
dialog.finished.connect(lambda: self.update_themes_list())
dialog.setLayout(layout)
# Initially load themes into the list
self.load_themes()
dialog.exec_() # Open the dialog as a modal window
dialog.exec_()
def update_themes_list(self):
themes_list = self.build_themes_list()
self.populate_themes(self.json_files_list_widget, themes_list)
def fetch_themes(self):
try:
# Read the config.json file
with open("config.json", "r") as config_file:
config = json.load(config_file)
# Get the ThemeRepository value
url = config.get("ThemeRepository")
if not url:
raise ValueError("ThemeRepository is not defined in config.json")
# Fetch themes from the specified URL
response = requests.get(url)
response.raise_for_status() # Raise an exception for HTTP errors
response.raise_for_status()
return response.json()
except (FileNotFoundError, json.JSONDecodeError) as config_error:
self.show_error_popup("Error reading configuration", f"An error occurred while reading config.json: {config_error}")
@ -546,13 +539,9 @@ class PicomcVersionSelector(QWidget):
def download_theme_json(self, theme_url, theme_name):
try:
response = requests.get(theme_url)
response.raise_for_status() # Raise an exception for HTTP errors
# Create the themes directory if it doesn't exist
response.raise_for_status()
if not os.path.exists('themes'):
os.makedirs('themes')
# Save the content of the theme JSON file to the 'themes' folder
theme_filename = os.path.join('themes', f'{theme_name}.json')
with open(theme_filename, 'wb') as f:
f.write(response.content)
@ -571,26 +560,25 @@ class PicomcVersionSelector(QWidget):
return os.path.exists(os.path.join('themes', f'{theme_name}.json'))
def load_themes(self):
theme_list = self.theme_list
themes_data = self.fetch_themes()
themes = themes_data.get("themes", [])
# Separate themes into installed and uninstalled
installed_themes = []
uninstalled_themes = []
for theme in themes:
theme_display_name = f"{theme['name']} by {theme['author']}"
if self.is_theme_installed(theme['name']):
theme_display_name += " [I]" # Mark installed themes
theme_display_name += " [I]"
installed_themes.append(theme_display_name)
else:
uninstalled_themes.append(theme_display_name)
self.theme_list.clear()
self.theme_list.addItems(uninstalled_themes)
self.theme_list.addItems(installed_themes)
# Clear the list and add uninstalled themes first, then installed ones
theme_list.clear()
theme_list.addItems(uninstalled_themes)
theme_list.addItems(installed_themes)
# Autoselect the first item in the list if it exists
if self.theme_list.count() > 0:
self.theme_list.setCurrentRow(0)
self.on_theme_click()
def on_theme_click(self):
selected_item = self.theme_list.currentItem()
@ -598,14 +586,33 @@ class PicomcVersionSelector(QWidget):
theme_name = selected_item.text().split(" by ")[0]
theme = self.find_theme_by_name(theme_name)
if theme:
# Display theme details in the QLabel (details_label)
self.details_label.setText(
f"Name: {theme['name']}\n"
f"Description: {theme['description']}\n"
f"Author: {theme['author']}\n"
f"License: {theme['license']}\n"
f"Link: {theme['link']}"
f"<b>Name:</b> {theme['name']}<br>"
f"<b>Description:</b> {theme['description']}<br>"
f"<b>Author:</b> {theme['author']}<br>"
f"<b>License:</b> {theme['license']}<br>"
f"<b>Link:</b> <a href='{theme['link']}'>{theme['link']}</a><br>"
)
self.details_label.setTextFormat(Qt.RichText)
self.details_label.setOpenExternalLinks(True)
preview = theme.get('preview')
if preview:
image_data = self.fetch_image(preview)
if image_data:
pixmap = QPixmap()
pixmap.loadFromData(image_data)
self.image_label.setPixmap(pixmap)
else:
self.image_label.clear()
def fetch_image(self, url):
try:
response = requests.get(url)
response.raise_for_status()
return response.content
except requests.exceptions.RequestException as e:
self.show_error_popup("Error fetching image", f"An error occurred while fetching the image: {e}")
return None
def find_theme_by_name(self, theme_name):
themes_data = self.fetch_themes()
@ -623,18 +630,19 @@ class PicomcVersionSelector(QWidget):
if theme:
theme_url = theme["link"]
self.download_theme_json(theme_url, theme_name)
self.load_themes() # Reload the list to show the "[I]" marker
self.load_themes()
## REPOSITORY BLOCK ENDS
def save_settings(self, is_rcp_enabled, check_updates_on_start, theme_background, selected_theme):
def save_settings(self, is_rcp_enabled, check_updates_on_start, theme_background, selected_theme, is_bleeding):
config_path = "config.json"
updated_config = {
"IsRCPenabled": is_rcp_enabled,
"CheckUpdate": check_updates_on_start,
"ThemeBackground": theme_background,
"Theme": selected_theme
"Theme": selected_theme,
"IsBleeding": is_bleeding
}
# Update config values
@ -762,7 +770,20 @@ class PicomcVersionSelector(QWidget):
self.installed_version_combo.addItems(versions)
def populate_installed_versions_normal_order(self):
# Run the command and get the output
# Run the 'picomc instance create default' command at the start
try:
process = subprocess.Popen(['picomc', 'instance', 'create', 'default'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
output, error = process.communicate()
if process.returncode != 0:
raise subprocess.CalledProcessError(process.returncode, process.args, error)
except FileNotFoundError:
logging.error("'picomc' command not found. Please make sure it's installed and in your PATH.")
return
except subprocess.CalledProcessError as e:
logging.error("Error creating default instance: %s", e.stderr)
return
# Run the 'picomc version list' command and get the output
try:
process = subprocess.Popen(['picomc', 'version', 'list'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
output, error = process.communicate()
@ -933,20 +954,69 @@ class PicomcVersionSelector(QWidget):
dialog.exec_()
self.open_dialogs.remove(dialog)
def create_account(self, dialog, username, is_microsoft):
# Remove leading and trailing spaces from the username
username = username.strip()
if not username:
QMessageBox.warning(dialog, "Warning", "Username cannot be blank.")
return
if not self.is_valid_username(username):
QMessageBox.warning(dialog, "Warning", "Invalid username. Usernames must be 3-16 characters long and can only contain letters, numbers, and underscores.")
return
try:
command = ['picomc', 'account', 'create', username]
if is_microsoft:
command.append('--ms')
subprocess.run(command, check=True)
QMessageBox.information(dialog, "Success", f"Account '{username}' created successfully!")
self.populate_accounts_for_all_dialogs()
dialog.accept()
except subprocess.CalledProcessError as e:
error_message = f"Error creating account: {e.stderr.decode()}"
logging.error(error_message)
QMessageBox.critical(dialog, "Error", error_message)
def is_valid_username(self, username):
# Validate the username according to Minecraft's rules
if 3 <= len(username) <= 16 and re.match(r'^[a-zA-Z0-9_]+$', username):
return True
return False
def authenticate_account(self, dialog, account_name):
# Authenticate a selected account
# Clean up the account name
account_name = account_name.strip().lstrip(" * ")
if not account_name:
QMessageBox.warning(dialog, "Warning", "Please select an account to authenticate.")
return
try:
subprocess.run(['picomc', 'account', 'authenticate', account_name], check=True)
QMessageBox.information(self, "Success", f"Account '{account_name}' authenticated successfully!")
except subprocess.CalledProcessError as e:
error_message = f"Error authenticating account '{account_name}': {e.stderr.decode()}"
# Create authenticator instance if it doesn't exist
if self.authenticator is None:
self.authenticator = MinecraftAuthenticator(self)
self.authenticator.auth_finished.connect(self._on_auth_finished)
# Start authentication process
self.authenticator.authenticate(account_name)
except Exception as e:
error_message = f"Error authenticating account '{account_name}': {str(e)}"
logging.error(error_message)
QMessageBox.critical(self, "Error", error_message)
QMessageBox.critical(dialog, "Error", error_message)
def _on_auth_finished(self, success):
if success:
QMessageBox.information(self, "Success", "Account authenticated successfully!")
else:
QMessageBox.critical(self, "Error", "Failed to authenticate account")
# Cleanup
if self.authenticator:
self.authenticator.cleanup()
self.authenticator = None
def remove_account(self, dialog, username):
# Remove a selected account
@ -967,24 +1037,6 @@ class PicomcVersionSelector(QWidget):
logging.error(error_message)
QMessageBox.critical(dialog, "Error", error_message)
def create_account(self, dialog, username, is_microsoft):
# Create a new account
if not username.strip():
QMessageBox.warning(dialog, "Warning", "Username cannot be blank.")
return
try:
command = ['picomc', 'account', 'create', username]
if is_microsoft:
command.append('--ms')
subprocess.run(command, check=True)
QMessageBox.information(dialog, "Success", f"Account '{username}' created successfully!")
self.populate_accounts_for_all_dialogs()
except subprocess.CalledProcessError as e:
error_message = f"Error creating account: {e.stderr.decode()}"
logging.error(error_message)
QMessageBox.critical(dialog, "Error", error_message)
def populate_accounts(self, account_combo):
# Populate the account dropdown
@ -1051,8 +1103,22 @@ class PicomcVersionSelector(QWidget):
with open('version.json', 'r') as version_file:
version_data = json.load(version_file)
version_number = version_data.get('version', 'unknown version')
version_bleeding = version_data.get('versionBleeding', None)
except (FileNotFoundError, json.JSONDecodeError):
version_number = 'unknown version'
version_bleeding = None
# Check the configuration for IsBleeding
try:
with open('config.json', 'r') as config_file:
config_data = json.load(config_file)
is_bleeding = config_data.get('IsBleeding', False)
except (FileNotFoundError, json.JSONDecodeError):
is_bleeding = False
# Use versionBleeding if IsBleeding is true
if is_bleeding and version_bleeding:
version_number = version_bleeding
about_message = (
f"PicoDulce Launcher (v{version_number})\n\n"
@ -1069,19 +1135,39 @@ class PicomcVersionSelector(QWidget):
with open("version.json") as f:
local_version_info = json.load(f)
local_version = local_version_info.get("version")
local_version_bleeding = local_version_info.get("versionBleeding")
logging.info(f"Local version: {local_version}")
logging.info(f"Local bleeding version: {local_version_bleeding}")
with open("config.json") as config_file:
config = json.load(config_file)
is_bleeding = config.get("IsBleeding", False)
if local_version:
remote_version_info = self.fetch_remote_version()
remote_version = remote_version_info.get("version")
remote_version_bleeding = remote_version_info.get("versionBleeding")
logging.info(f"Remote version: {remote_version}")
if remote_version and remote_version != local_version:
update_message = f"A new version ({remote_version}) is available!\nDo you want to download it now?"
logging.info(f"Remote bleeding version: {remote_version_bleeding}")
if is_bleeding:
remote_version_to_check = remote_version_bleeding
local_version_to_check = local_version_bleeding
else:
remote_version_to_check = remote_version
local_version_to_check = local_version
if remote_version_to_check and (remote_version_to_check != local_version_to_check):
if is_bleeding:
update_message = f"Do you want to update to the bleeding edge version ({remote_version_bleeding})?"
else:
update_message = f"A new version ({remote_version}) is available!\nDo you want to download it now?"
update_dialog = QMessageBox.question(self, "Update Available", update_message, QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
if update_dialog == QMessageBox.Yes:
# Download and apply the update
self.download_update(remote_version_info)
else:
print("Up to Date", "You already have the latest version!")
print(f"You already have the latest version!")
else:
logging.error("Failed to read local version information.")
QMessageBox.critical(self, "Error", "Failed to check for updates.")
@ -1094,13 +1180,33 @@ class PicomcVersionSelector(QWidget):
with open("version.json") as f:
local_version_info = json.load(f)
local_version = local_version_info.get("version")
local_version_bleeding = local_version_info.get("versionBleeding")
logging.info(f"Local version: {local_version}")
logging.info(f"Local bleeding version: {local_version_bleeding}")
with open("config.json") as config_file:
config = json.load(config_file)
is_bleeding = config.get("IsBleeding", False)
if local_version:
remote_version_info = self.fetch_remote_version()
remote_version = remote_version_info.get("version")
remote_version_bleeding = remote_version_info.get("versionBleeding")
logging.info(f"Remote version: {remote_version}")
if remote_version and remote_version != local_version:
update_message = f"A new version ({remote_version}) is available!\nDo you want to download it now?"
logging.info(f"Remote bleeding version: {remote_version_bleeding}")
if is_bleeding:
remote_version_to_check = remote_version_bleeding
local_version_to_check = local_version_bleeding
else:
remote_version_to_check = remote_version
local_version_to_check = local_version
if remote_version_to_check and (remote_version_to_check != local_version_to_check):
if is_bleeding:
update_message = f"Do you want to update to the bleeding edge version ({remote_version_bleeding})?"
else:
update_message = f"A new version ({remote_version}) is available!\nDo you want to download it now?"
update_dialog = QMessageBox.question(self, "Update Available", update_message, QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
if update_dialog == QMessageBox.Yes:
# Download and apply the update

View File

@ -2,3 +2,4 @@ picomc
PyQt5
requests
pypresence
tqdm

View File

@ -1,11 +1,14 @@
{
"version": "0.11.5",
"version": "0.12.1",
"links": [
"https://raw.githubusercontent.com/nixietab/picodulce/main/version.json",
"https://raw.githubusercontent.com/nixietab/picodulce/main/picodulce.py",
"https://raw.githubusercontent.com/nixietab/picodulce/main/requirements.txt",
"https://raw.githubusercontent.com/nixietab/picodulce/main/drums.gif",
"https://raw.githubusercontent.com/nixietab/picodulce/main/marroc.py",
"https://raw.githubusercontent.com/nixietab/picodulce/main/holiday.ico"
]
"https://raw.githubusercontent.com/nixietab/picodulce/main/holiday.ico",
"https://raw.githubusercontent.com/nixietab/picodulce/main/authser.py",
"https://raw.githubusercontent.com/nixietab/picodulce/main/healthcheck.py"
],
"versionBleeding": "0.12.1-188"
}