Skip to content

Commit

Permalink
Merge pull request #5 from Advik-B/issue-4-Add_CLI_support
Browse files Browse the repository at this point in the history
Issue 4 add cli support
  • Loading branch information
Advik-B authored Feb 4, 2022
2 parents 6215794 + 3076590 commit 1b0697e
Show file tree
Hide file tree
Showing 11 changed files with 697 additions and 2 deletions.
5 changes: 4 additions & 1 deletion src/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
*.env
*.env
*.jar
*.log
build/
164 changes: 164 additions & 0 deletions src/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
from PyQt5.QtWidgets import QMainWindow, QApplication, QLabel, QLineEdit, QCheckBox, QProgressBar, QListWidget, QPushButton, QFileDialog, QTextEdit
from PyQt5 import uic
from PyQt5.QtCore import QThread
from logger import Logger
from threading import Thread
from backend import ModPack
import sys
import os

class UI(QMainWindow):

def __init__(self):
super(UI, self).__init__()
# Set up the logger
self.logger = logger

# Load the UI
uic.loadUi("design.ui", self)
# Set up the window title and make it non-resizable
self.setWindowTitle("CMPDL by Advik-B")
self.setMaximumSize(self.size())
# Define widgets from ui file
self.title_lbl = self.findChild(QLabel, "title_lbl")
self.modpack_pth = self.findChild(QLineEdit, "modpack_pth")
self.output_dir = self.findChild(QLineEdit, "download_pth")
self.optional_mods = self.findChild(QCheckBox, "optional_mods")
self.keep_config = self.findChild(QCheckBox, "keep_config")
self.overall_progress = self.findChild(QProgressBar, "progress")
self.per_mod_progress = self.findChild(QProgressBar, "per_mod_progress")
self.mod_list = self.findChild(QListWidget, "DownloadList")
self.log_box = self.findChild(QTextEdit, "logbox")
self.start_download = self.findChild(QPushButton, "start_download_btn")
self.copy_logs = self.findChild(QPushButton, "copy_logs_btn")
self.browse_modpack = self.findChild(QPushButton, "browse_modpack")
self.output_dir_btn = self.findChild(QPushButton, "download_pth_browse")
# Create a list of widgets, this is used to check if all widgets are found in the ui file and be deleted later to free up memory
self.widgets = [

self.title_lbl,
self.modpack_pth,
self.output_dir,
self.optional_mods,
self.keep_config,
self.overall_progress,
self.per_mod_progress,
self.mod_list,
self.log_box,
self.start_download,
self.copy_logs,
self.browse_modpack,
self.output_dir_btn,
]
# Check if all widgets are defined properly
# if the code below works then all widgets are defined properly
try:
self.log("Checking widgets", "info")
self.check_widgets()
except AssertionError as e:
self.log("Widget not found: %s" % e, "error")
sys.exit(1)
# set up the log box
self.log_box.setReadOnly(True)

# Connect the buttons to their functions
self.browse_modpack.clicked.connect(self.browse__modpack)
self.output_dir_btn.clicked.connect(self.browse_output_dir)
self.copy_logs.clicked.connect(self.copy_logs_func)
self.start_download.clicked.connect(self.start_payload)
# show the window
self.show()
self.log("Window sucessfully loaded", "info")

def log(self, message: str, type_: str):

msg = self.logger.log(message, type_)
try:

self.log_box.setReadOnly(False)
self.log_box.setText(self.log_box.toPlainText() + str(msg)+ '\n')
self.log_box.setReadOnly(True)
except AttributeError:
self.logger.log("Log box not found", "error")

def check_widgets(self):
for widget in self.widgets:
self.log(widget, "debug")
assert widget is not None, "Widget not found"
# Free up memory by deleting the list
del self.widgets

def browse__modpack(self):
"""Browse for a .json / .zip file"""
file_ = QFileDialog.getOpenFileName(
self,
caption="Select modpack or a manifest file",
filter="Manifest (*.json);;ModPack (*.zip)",
initialFilter="ModPack (*.zip)",
directory=os.path.expanduser("~"),
)

if file_[0]:
self.modpack_pth.setText(file_[0])
self.log("Modpack path set to: %s" % file_[0], "info")

def browse_output_dir(self):
"""Browse for a directory"""
if directory := QFileDialog.getExistingDirectory(
self,
caption="Select output directory",
):
self.output_dir.setText(directory)
self.log("Output directory set to: %s" % directory, "info")

def copy_logs_func(self):
"""Copy the logs to the clipboard"""
self.log_box.selectAll()
self.log_box.copy()
self.log_box.undo()
self.log("Logs copied to clipboard", "info")

def secondry_log(self, message: str):
"""Secondry log function"""
self.log(message, "info")
self.mod_list.addItem(message)

def start_payload(self):
try:
kwargs = {

'log_func': self.log,
'secondry_log': self.secondry_log,
'pbar': self.overall_progress,
'pbar2': self.per_mod_progress,
'output_dir': self.output_dir.text(),
'download_optional_mods': self.optional_mods.isChecked(),
'keep_config': self.keep_config.isChecked(),

}

self.log("Starting payload", "info")
modpack = ModPack(self.modpack_pth.text(), **kwargs)
modpack.init()
t = Thread(target=modpack.install)
t.start()

except Exception as e:
self.log('Error: %s' % e, 'error')
self.log("Payload failed", "error")
raise e
return

def main():
global logger
logger = Logger()
app = QApplication(sys.argv)
UIWindow = UI()
app.setActiveWindow(UIWindow)
app.exec_()
logger.quit()
sys.exit(0)


if __name__ == "__main__":
main()
Binary file added src/__pycache__/backend.cpython-39.pyc
Binary file not shown.
Binary file added src/__pycache__/logger.cpython-39.pyc
Binary file not shown.
164 changes: 164 additions & 0 deletions src/backend.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
from cursepy import CurseClient
from tree_generator import gentree
from urllib.parse import unquote
from clint.textui import progress
from PyQt5.QtWidgets import QProgressBar
from time import perf_counter as now
import zipfile
import tempfile
import os
import json
import shutil
import requests


class ModPack:
def __init__(self, path: str, **kwargs) -> None:
self.log = kwargs.get("log_func")
self.secondry_log = kwargs.get("secondry_log")
# Set up the progress bar
self.progressbar = kwargs.get("pbar")
self.progressbar.setValue(0)

self.sec_progressbar = QProgressBar(kwargs.get("pbar2"))
self.output_dir = kwargs.get("output_dir")
self.download_optional = kwargs.get("download_optional_mods")
self.keep_config = kwargs.get("keep_config")

self.ini = False
self.path = path

def step(self, pbar: QProgressBar, value: int):
pbar.setValue(pbar.value() + value)

def init(self):
self.log("Initializing Curse client...", 'info')
start = now()
self.c = CurseClient()
stop = now()
self.log("Successfully initialized Curse client in %s seconds" % str(stop - start),
'info')
del stop, start
self.log("Initializing ModPack...", 'info')
start = now()
if self.path.endswith('.zip'):
self.tempdir = tempfile.mkdtemp(prefix='CMPDL')
self.log("Extracting modpack to %s" % self.tempdir, 'info')
with zipfile.ZipFile(self.path, 'r') as z:
z.extractall(self.tempdir)
self.log("Successfully extracted modpack", 'info')
self.log("ModPack file structure:\n%s" % gentree(self.tempdir), 'info')
self.manifest_path = os.path.join(self.tempdir, 'manifest.json')
self.meth = 'ZIP'
elif self.path.endswith('.json'):
self.manifest_path = self.path
self.meth = 'JSON'
try:
self.log("Manifest path set to %s" % self.manifest_path, 'info')
except AttributeError:
self.log("Manifest path not set", 'info')

self.log("Loading manifest...", 'info')
with open(self.manifest_path, 'r') as f:
mani = json.load(f)
self.manifest = mani['files']
self.log("Minecraft version: %s" % mani['minecraft']['version'], 'info')
self.log("Modpack version: %s" % mani['version'], 'info')
self.log("Modpack name: %s" % mani['name'], 'info')
self.log("Modpack author(s): %s" % mani['author'], 'info')
self.log("Modpack description: %s" % mani.get('description'), 'info')
self.log("Modpack website: %s" % mani.get('website'), 'info')
self.log("Modpack modloader: %s" % mani['minecraft']["modLoaders"][0]['id'], 'info')
self.log("Modpack config/overrides: %s" % mani['overrides'], 'info')
self.log("Successfully loaded manifest", 'info')
if self.meth == 'ZIP':
self.override_folder = os.path.join(self.tempdir, mani['overrides'])
del mani
stop = now()
self.log("Successfully initialized ModPack in %s seconds" % str(stop - start), 'info')
del stop, start
self.ini = True

def install(self):
global mods_folder
self.log("Installing ModPack...", 'info')
start = now()
if self.meth == 'ZIP' and os.path.isdir(self.override_folder):

self.log("Extracting overrides to %s" % self.output_dir, 'info')
for file in os.listdir(self.override_folder):
if os.path.isfile(os.path.join(self.override_folder, file)):
shutil.copyfile(os.path.join(self.override_folder, file), self.output_dir)
else:
try:
shutil.copytree(os.path.join(self.override_folder, file), os.path.join(self.output_dir, file))
except FileExistsError:
shutil.rmtree(os.path.join(self.output_dir, file))
shutil.copytree(os.path.join(self.override_folder, file), os.path.join(self.output_dir, file))

self.log("Successfully extracted overrides", 'info')
mods_folder = os.path.join(self.output_dir, 'mods')
try:
os.makedirs(mods_folder)
except FileExistsError:
shutil.rmtree(mods_folder)
os.makedirs(mods_folder)

mods_folder = self.output_dir
self.log("Downloading mods to %s" % mods_folder, 'info')
# Adjust the progress bar(s)
self.progressbar.setValue(0)
self.progressbar.setMaximum(len(self.manifest))
self.sec_progressbar.setValue(0)
for i, mod_ in enumerate(self.manifest):
mod = self.c.addon(mod_['projectID'])
self.log("Downloading mod %s out of %s" % (i, len(self.manifest)), 'info')
self.log("Mod name: %s" % mod.name, 'info')
self.log("Mod url: %s" % mod.url, 'info')
self.log("Mod author(s): %s" % str(mod.authors), 'info')
if mod_['required']:
self.log("Mod is required", 'info')
file = mod.file(mod_['fileID'])
save_path = os.path.join(
mods_folder, unquote(file.download_url.split('/')[-1]
))
self.download_raw(file.download_url, save_path, self.sec_progressbar)
else:
self.log("Mod is optional", 'info')
if self.download_optional:
file = mod.file(mod_['fileID'])
save_path = os.path.join(
mods_folder, unquote(file.download_url.split['/'][-1])
)
self.download_raw(file.download_url, save_path, self.sec_progressbar)
self.sec_progressbar.setValue(0)
self.step(self.progressbar, 1)
self.secondry_log('%s' % mod.name)
stop = now()
self.log("Successfully installed ModPack in %s seconds" % str(stop - start), 'info')
self.clean()

def download_raw(self, link: str, path: str, pbar: QProgressBar):
if not self.ini:
raise Exception("ModPack not initialized")

r = requests.get(link, stream=True)
with open(path, 'wb') as f:
self.log('LINK: %s' % link, 'debug')
self.log('PATH: %s' % path, 'debug')
self.log('HEADERS: %s' % r.headers, 'debug')
total_length = int(r.headers.get('content-length'))
self.log('TOTAL LENGTH: %s' % total_length, 'debug')
for chunk in progress.bar(r.iter_content(chunk_size=1024), expected_size=(total_length / 1024) + 1):
if chunk:
f.write(chunk)
f.flush()
self.step(pbar, 1)
self.log('Downloaded %s to %s' % (link, path), 'debug')

def clean(self):
self.log("Cleaning up...", 'info')
if self.meth == 'ZIP':
shutil.rmtree(self.tempdir, ignore_errors=True)
self.log("Successfully cleaned up", 'info')
self.ini = False
Loading

0 comments on commit 1b0697e

Please sign in to comment.