This commit is contained in:
faroukbmiled 2023-07-31 07:02:53 +01:00
parent 202cdfffae
commit 975e5013ba
2 changed files with 287 additions and 0 deletions

282
boiiiwd.py Normal file
View File

@ -0,0 +1,282 @@
import os
import sys
import subprocess
import configparser
import filecmp
import json
import requests
import time
import threading
from bs4 import BeautifulSoup
from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QLineEdit, QPushButton, QVBoxLayout, QMessageBox, QHBoxLayout, QProgressBar, QSizePolicy
from PyQt5.QtCore import Qt, QThread, pyqtSignal
from PyQt5.QtCore import QCoreApplication
import qdarktheme
CONFIG_FILE_PATH = "config.ini"
global stopped
stopped = False
def create_default_config():
config = configparser.ConfigParser()
config["Settings"] = {
"SteamCMDPath": "steamcmd",
"DestinationFolder": ""
}
with open(CONFIG_FILE_PATH, "w") as config_file:
config.write(config_file)
def run_steamcmd_command(command):
steamcmd_path = get_steamcmd_path()
process = subprocess.Popen([steamcmd_path] + command.split(), stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1, universal_newlines=True)
while True:
output = process.stdout.readline().rstrip()
if process.poll() is not None:
break
def get_steamcmd_path():
config = configparser.ConfigParser()
config.read(CONFIG_FILE_PATH)
return config.get("Settings", "SteamCMDPath", fallback="steamcmd")
def set_steamcmd_path(steamcmd_path):
config = configparser.ConfigParser()
config.read(CONFIG_FILE_PATH)
config.set("Settings", "SteamCMDPath", steamcmd_path)
with open(CONFIG_FILE_PATH, "w") as config_file:
config.write(config_file)
def extract_json_data(json_path):
with open(json_path, "r") as json_file:
data = json.load(json_file)
return data["Type"], data["FolderName"]
def get_file_size(url):
response = requests.head(url)
if "Content-Length" in response.headers:
file_size = int(response.headers["Content-Length"])
return file_size
return None
def get_workshop_file_size(workshop_id):
url = f"https://steamcommunity.com/sharedfiles/filedetails/?id={workshop_id}&searchtext="
response = requests.get(url)
soup = BeautifulSoup(response.text, "html.parser")
file_size_element = soup.find("div", class_="detailsStatRight")
if file_size_element:
file_size_text = file_size_element.get_text(strip=True)
file_size_text = file_size_text.replace(",", "")
file_size_in_mb = float(file_size_text.replace(" MB", ""))
file_size_in_bytes = int(file_size_in_mb * 1024 * 1024)
return file_size_in_bytes
return None
def update_progress_bar(current_size, file_size, progress_bar):
if file_size is not None:
progress = int(current_size / file_size * 100)
progress_bar.setValue(progress)
def check_and_update_progress(file_size, folder_name_path, progress_bar):
while not stopped:
current_size = sum(os.path.getsize(os.path.join(folder_name_path, f)) for f in os.listdir(folder_name_path))
update_progress_bar(current_size, file_size, progress_bar)
QCoreApplication.processEvents()
time.sleep(1)
def download_workshop_map(workshop_id, destination_folder, progress_bar):
file_size = get_workshop_file_size(workshop_id)
if file_size is None:
show_message("Error", "Failed to retrieve file size.")
return
download_folder = os.path.join("steamapps", "workshop", "downloads", "311210", workshop_id)
if not os.path.exists(download_folder):
os.makedirs(download_folder)
command = f"+login anonymous +workshop_download_item 311210 {workshop_id} +quit"
progress_thread = threading.Thread(target=check_and_update_progress, args=(file_size, download_folder, progress_bar))
progress_thread.daemon = True
progress_thread.start()
run_steamcmd_command(command)
global stopped
stopped = True
progress_bar.setValue(100)
map_folder = os.path.join("steamapps", "workshop", "content", "311210", workshop_id)
json_file_path = os.path.join(map_folder, "workshop.json")
if os.path.exists(json_file_path):
global mod_type
mod_type, folder_name = extract_json_data(json_file_path)
if mod_type == "mod":
mods_folder = os.path.join(destination_folder, "mods")
folder_name_path = os.path.join(mods_folder, folder_name, "zone")
elif mod_type == "map":
usermaps_folder = os.path.join(destination_folder, "usermaps")
folder_name_path = os.path.join(usermaps_folder, folder_name, "zone")
else:
show_message("Error", "Invalid map type in workshop.json.")
return
os.makedirs(folder_name_path, exist_ok=True)
map_files = os.listdir(map_folder)
for map_file in map_files:
source_path = os.path.join(map_folder, map_file)
destination_path = os.path.join(folder_name_path, map_file)
if os.path.exists(destination_path) and filecmp.cmp(source_path, destination_path):
pass
else:
os.replace(source_path, destination_path)
QCoreApplication.processEvents()
show_message("Download Complete", f"{mod_type} files are downloaded at {folder_name_path}")
def show_message(title, message):
msg = QMessageBox()
msg.setWindowTitle(title)
msg.setText(message)
msg.exec_()
class DownloadThread(QThread):
finished = pyqtSignal()
def __init__(self, workshop_id, destination_folder, progress_bar):
super().__init__()
self.workshop_id = workshop_id
self.destination_folder = destination_folder
self.progress_bar = progress_bar
def run(self):
download_workshop_map(self.workshop_id, self.destination_folder, self.progress_bar)
self.finished.emit()
class WorkshopDownloaderApp(QWidget):
def __init__(self):
super().__init__()
self.initUI()
self.download_thread = None
self.button_download.setEnabled(True)
self.button_stop.setEnabled(False)
def initUI(self):
self.setWindowTitle('BOIII Workshop Downloader')
self.setGeometry(100, 100, 400, 200)
layout = QVBoxLayout()
self.label_workshop_id = QLabel("Enter the Workshop ID of the map/mod you want to download:")
layout.addWidget(self.label_workshop_id)
self.edit_workshop_id = QLineEdit()
layout.addWidget(self.edit_workshop_id)
self.label_destination_folder = QLabel("Enter Your BOIII folder:")
layout.addWidget(self.label_destination_folder)
self.edit_destination_folder = QLineEdit()
layout.addWidget(self.edit_destination_folder)
self.label_steamcmd_path = QLabel("Enter SteamCMD path (default):")
layout.addWidget(self.label_steamcmd_path)
self.edit_steamcmd_path = QLineEdit()
layout.addWidget(self.edit_steamcmd_path)
buttons_layout = QHBoxLayout()
self.button_download = QPushButton("Download")
self.button_download.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
self.button_download.clicked.connect(self.download_map)
buttons_layout.addWidget(self.button_download, 75)
self.button_stop = QPushButton("Stop")
self.button_stop.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
self.button_stop.clicked.connect(self.stop_download)
buttons_layout.addWidget(self.button_stop, 25)
layout.addLayout(buttons_layout)
self.progress_bar = QProgressBar()
layout.addWidget(self.progress_bar)
self.setLayout(layout)
self.load_config()
def download_map(self):
global stopped
stopped = False
workshop_id = self.edit_workshop_id.text()
destination_folder = self.edit_destination_folder.text()
steamcmd_path = self.edit_steamcmd_path.text()
if not destination_folder:
show_message("Error", "Please select a destination folder.")
return
if not steamcmd_path:
show_message("Error", "Please enter the SteamCMD path.")
return
self.button_stop.setEnabled(True)
self.progress_bar.setValue(0)
self.button_download.setEnabled(False)
self.download_thread = DownloadThread(workshop_id, destination_folder, self.progress_bar)
self.download_thread.finished.connect(self.on_download_finished)
self.download_thread.start()
def stop_download(self):
global stopped
stopped = True
subprocess.run(['taskkill', '/F', '/IM', 'steamcmd.exe'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if self.download_thread and self.download_thread.isRunning():
self.download_thread.terminate()
self.button_download.setEnabled(True)
self.button_stop.setEnabled(False)
def on_download_finished(self):
self.button_download.setEnabled(True)
self.save_config(self.edit_destination_folder.text(), self.edit_steamcmd_path.text())
def load_config(self):
config = configparser.ConfigParser()
if os.path.exists(CONFIG_FILE_PATH):
config.read(CONFIG_FILE_PATH)
destination_folder = config.get("Settings", "DestinationFolder", fallback="")
steamcmd_path = config.get("Settings", "SteamCMDPath", fallback="")
self.edit_destination_folder.setText(destination_folder)
self.edit_steamcmd_path.setText(steamcmd_path)
else:
create_default_config()
def save_config(self, destination_folder, steamcmd_path):
config = configparser.ConfigParser()
config.read(CONFIG_FILE_PATH)
config.set("Settings", "DestinationFolder", destination_folder)
config.set("Settings", "SteamCMDPath", steamcmd_path)
with open(CONFIG_FILE_PATH, "w") as config_file:
config.write(config_file)
if __name__ == "__main__":
app = QApplication(sys.argv)
qdarktheme.setup_theme()
if not os.path.exists(CONFIG_FILE_PATH):
create_default_config()
window = WorkshopDownloaderApp()
window.show()
sys.exit(app.exec_())

5
requirements.txt Normal file
View File

@ -0,0 +1,5 @@
beautifulsoup4==4.11.1
PyQt5==5.15.9
PyQt5_sip==12.11.0
Requests==2.31.0
pyqtdarktheme==2.1.0