import os import re import shutil import tarfile import tempfile from pathlib import Path import dearpygui.dearpygui as dpg import requests from vrutil.dialogs import Dialogs from vrutil.utils import loaded _GE_PROTON_GITHUB = 'GloriousEggroll/proton-ge-custom' _GE_PROTON_INSTALL_DIR = '{steam_dir}/compatibilitytools.d' class GeProtonSettings: """GE Proton related settings""" def __init__(self, steam_dir: str, dialogs: Dialogs): self._install_dir = _GE_PROTON_INSTALL_DIR.format(steam_dir=steam_dir) self._dialogs = dialogs self._installed_list = None self._available_list = None self._page_indicator_item = None self._cur_page = 0 self._max_page = 0 self._page_links = { 'first': '', 'prev': '', 'next': '', 'last': '' } def create_views(self): with dpg.collapsing_header(label='GE Proton', default_open=True): with dpg.group(horizontal=True): # INSTALLED with dpg.group(horizontal=False): dpg.add_text('Installed versions:') dpg.add_button(label='Delete', callback=self._delete_installed) self._installed_list = dpg.add_listbox( items=self._get_installed(), width=250, num_items=8 ) # AVAILABLE with dpg.group(horizontal=False): dpg.add_text('Available versions:') with dpg.group(horizontal=True): dpg.add_button(label='Install', callback=self._install) dpg.add_button(label='Reload', callback=self.reload_available) dpg.add_button(label='⇇', user_data='first', callback=self._switch_page) dpg.add_button(label='←', user_data='prev', callback=self._switch_page) self._page_indicator_item = dpg.add_text('0/0') dpg.add_button(label='→', user_data='next', callback=self._switch_page) dpg.add_button(label='⇉', user_data='last', callback=self._switch_page) self._available_list = dpg.add_listbox( items=(), width=250, num_items=8 ) dpg.add_spacer(height=10) # region Installed def reload_installed(self): dpg.configure_item(self._installed_list, items=self._get_installed()) def _get_installed(self): installed = os.listdir(self._install_dir) sorted_versions = tuple(sorted(installed, reverse=True)) loaded('GE Proton installed', sorted_versions) return sorted_versions def _delete_installed(self): version = dpg.get_value(self._installed_list) install_path = f'{self._install_dir}/{version}' self._dialogs.show_alert( title=f'Delete {version}?', message=f'Will remove\n{install_path}', confirm='Delete', on_confirm=lambda: self._confirmed_delete_installed(install_path), on_dismiss=lambda: self._dialogs.hide_alert() ) def _confirmed_delete_installed(self, version): self._dialogs.hide_alert() self._dialogs.show_loading(title='Deleting...', message=version) print(f'Deleting {version}') shutil.rmtree(version) self.reload_installed() print('Done deleting') self._dialogs.hide_loading() # endregion # region Available def reload_available(self): self._cur_page = 1 self._max_page = 1 dpg.configure_item(self._available_list, items=self._get_available()) def _get_available(self, link=None): url = link or f'https://api.github.com/repos/{_GE_PROTON_GITHUB}/releases' response = requests.get(url) self._update_page_links(response.headers['Link']) releases = response.json() release_names = list(map(lambda release: release['tag_name'], releases)) sorted_releases = tuple(sorted(release_names, reverse=True)) loaded('GE Proton releases', sorted_releases) return sorted_releases def _update_page_links(self, links): matches = re.findall('<(\\S+)>; rel=\\"(\\w+)\\"', links) for link in self._page_links: self._page_links[link] = '' for match in matches: self._page_links[match[1]] = match[0] if self._page_links['last'] != '': self._max_page = self._page_links['last'].split('=')[1] dpg.set_value(self._page_indicator_item, f'{self._cur_page}/{self._max_page}') def _switch_page(self, sender, app_data, user_data): print(f'Switching page {user_data}') link = self._page_links[user_data] if link != '': self._cur_page = link.split('=')[1] dpg.configure_item(self._available_list, items=self._get_available(link)) def _install(self): version = dpg.get_value(self._available_list) url = f'https://github.com/{_GE_PROTON_GITHUB}/releases/download/{version}/{version}.tar.gz' tar_file_path = f'{tempfile.gettempdir()}/{version}.tar.gz' target_extract_path = self._install_dir if version is None or version == '': self._dialogs.show_alert( title=f'No version selected!', message='Reload the release list and select a version to install.', on_confirm=lambda: self._dialogs.hide_alert() ) return if version in self._get_installed(): self._dialogs.show_alert( title=f'Version {version} already installed!', message='To download again, delete it first', on_confirm=lambda: self._dialogs.hide_alert() ) return print(f'Loading {url} to {tar_file_path}') title = f'Installing {version}' self._dialogs.show_loading(title=title, message='Downloading...') response = requests.get(url, stream=True) if response.status_code == 200: with open(tar_file_path, 'wb') as temp_file: temp_file.write(response.raw.read()) print('Download complete, start extracting...') self._dialogs.show_loading(title=title, message='Extracting...') with tarfile.open(tar_file_path) as tar_file: tar_file.extractall(target_extract_path) print('Done installing') else: print(response.status_code) print(response.text) if Path(tar_file_path).exists(): os.remove(tar_file_path) self._dialogs.hide_loading() self.reload_installed() dpg.set_value(self._installed_list, version) # endregion