def move_new_soundpack(self): # Find the soundpack in the self.extract_dir # Move the soundpack from that location into self.soundpacks_dir self.moving_new_soundpack = True main_window = self.get_main_window() status_bar = main_window.statusBar() status_bar.showMessage(_('Finding the soundpack')) next_scans = deque() current_scan = scandir(self.extract_dir) soundpack_dir = None while True: try: entry = next(current_scan) if entry.is_dir(): next_scans.append(entry.path) elif entry.is_file(): dirname, basename = os.path.split(entry.path) if basename == 'soundpack.txt': soundpack_dir = dirname entry = None break except StopIteration: if len(next_scans) > 0: current_scan = scandir(next_scans.popleft()) else: break for item in current_scan: pass if soundpack_dir is None: status_bar.showMessage(_('Soundpack installation cancelled - There ' 'is no soundpack in the downloaded archive')) delete_path(self.extract_dir) self.moving_new_soundpack = False self.finish_install_new_soundpack() else: soundpack_dir_name = os.path.basename(soundpack_dir) target_dir = os.path.join(self.soundpacks_dir, soundpack_dir_name) if os.path.exists(target_dir): status_bar.showMessage(_('Soundpack installation cancelled - ' 'There is already a {basename} directory in ' '{soundpacks_dir}').format(basename=soundpack_dir_name, soundpacks_dir=self.soundpacks_dir)) else: shutil.move(soundpack_dir, self.soundpacks_dir) status_bar.showMessage(_('Soundpack installation completed')) delete_path(self.extract_dir) self.moving_new_soundpack = False self.game_dir_changed(self.game_dir) self.finish_install_new_soundpack()
def prune_auto_backups(self): max_auto_backups = max(int(get_config_value('max_auto_backups', '6')), 1) search_start = (_('auto') + '_').lower() backup_dir = os.path.join(self.game_dir, 'save_backups') if not os.path.isdir(backup_dir): return auto_backups = [] for entry in scandir(backup_dir): filename, ext = os.path.splitext(entry.name) if entry.is_file() and ext.lower() == '.zip': filename_lower = filename.lower() if filename_lower.startswith(search_start): auto_backups.append({ 'path': entry.path, 'modified': datetime.fromtimestamp(entry.stat().st_mtime) }) if len(auto_backups) >= max_auto_backups: # Remove backups to have a total of max_auto_backups - 1 auto_backups.sort(key=lambda x: x['modified']) remove_count = len(auto_backups) - max_auto_backups + 1 to_remove = auto_backups[:remove_count] for backup in to_remove: delete_path(backup['path'])
def finish_restore_backup(self): main_window = self.get_main_window() status_bar = main_window.statusBar() status_bar.removeWidget(self.extracting_label) status_bar.removeWidget(self.extracting_speed_label) status_bar.removeWidget(self.extracting_size_label) status_bar.removeWidget(self.extracting_progress_bar) status_bar.busy -= 1 self.extracting_backup = False if self.extracting_zipfile is not None: self.extracting_zipfile.close() if self.temp_save_dir is not None: delete_path(self.temp_save_dir) self.enable_tab() self.get_main_tab().enable_tab() self.get_soundpacks_tab().enable_tab() self.get_settings_tab().enable_tab() self.get_mods_tab().enable_tab() self.get_backups_tab().enable_tab() self.restore_button.setText(_('Restore backup')) self.get_main_tab().game_dir_group_box.update_saves()
def completed(): save_dir = os.path.join(self.game_dir, 'save') delete_path(save_dir) if self.temp_save_dir is not None: retry_rename(self.temp_save_dir, save_dir) self.temp_save_dir = None self.finish_restore_backup() self.extracting_thread = None
def timeout(): self.extracting_progress_bar.setValue(self.extracting_index) if self.extracting_index == len(self.extracting_infolist): self.extracting_timer.stop() main_window = self.get_main_window() status_bar = main_window.statusBar() status_bar.removeWidget(self.extracting_label) status_bar.removeWidget(self.extracting_progress_bar) status_bar.busy -= 1 self.extracting_new_soundpack = False self.extracting_zipfile.close() self.extracting_zipfile = None if self.downloaded_file.lower().endswith('.7z'): self.extracting_archive = None if self.install_type == 'direct_download': download_dir = os.path.dirname(self.downloaded_file) delete_path(download_dir) self.move_new_soundpack() else: extracting_element = self.extracting_infolist[ self.extracting_index] self.extracting_label.setText( _('Extracting {0}').format(extracting_element.filename)) if self.downloaded_file.lower().endswith('.7z'): destination = os.path.join( self.extract_dir, *extracting_element.filename.split('/')) dest_dir = os.path.dirname(destination) if not os.path.isdir(dest_dir): os.makedirs(dest_dir) with open(destination, 'wb') as f: f.write(extracting_element.read()) else: self.extracting_zipfile.extract(extracting_element, self.extract_dir) self.extracting_index += 1
def delete_existing(self): selection_model = self.installed_lv.selectionModel() if selection_model is None or not selection_model.hasSelection(): return selected = selection_model.currentIndex() selected_info = self.soundpacks[selected.row()] confirm_msgbox = QMessageBox() confirm_msgbox.setWindowTitle(_('Delete soundpack')) confirm_msgbox.setText(_('This will delete the soundpack directory. It ' 'cannot be undone.')) confirm_msgbox.setInformativeText(_('Are you sure you want to ' 'delete the {view} soundpack?').format(view=selected_info['VIEW'])) confirm_msgbox.addButton(_('Delete the soundpack'), QMessageBox.YesRole) confirm_msgbox.addButton(_('I want to keep the soundpack'), QMessageBox.NoRole) confirm_msgbox.setIcon(QMessageBox.Warning) if confirm_msgbox.exec() == 0: main_window = self.get_main_window() status_bar = main_window.statusBar() if not delete_path(selected_info['path']): status_bar.showMessage(_('Soundpack deletion cancelled')) else: self.soundpacks_model.removeRows(selected.row(), 1) self.soundpacks.remove(selected_info) status_bar.showMessage(_('Soundpack deleted'))
def http_finished(self): self.downloading_file.close() if self.download_aborted: download_dir = os.path.dirname(self.downloaded_file) delete_path(download_dir) else: redirect = self.http_reply.attribute( QNetworkRequest.RedirectionTargetAttribute) if redirect is not None: download_dir = os.path.dirname(self.downloaded_file) delete_path(download_dir) os.makedirs(download_dir) redirected_url = urljoin( self.http_reply.request().url().toString(), redirect.toString()) self.downloading_file = open(self.downloaded_file, 'wb') self.download_last_read = datetime.utcnow() self.download_last_bytes_read = 0 self.download_speed_count = 0 self.download_aborted = False self.progress_bar.setValue(0) self.http_reply = self.qnam.get( QNetworkRequest(QUrl(redirected_url))) self.http_reply.finished.connect(self.http_finished) self.http_reply.readyRead.connect(self.http_ready_read) self.http_reply.downloadProgress.connect(self.dl_progress) else: # Download completed if getattr(sys, 'frozen', False): # Launch self.downloaded_file and close subprocess.Popen([self.downloaded_file]) self.updated = True self.done(0)
def delete_button_clicked(self): selection_model = self.backups_table.selectionModel() if selection_model is None or not selection_model.hasSelection(): return selected = selection_model.currentIndex() table_item = self.backups_table.item(selected.row(), 0) selected_info = self.backups[table_item] if not os.path.isfile(selected_info['path']): return confirm_msgbox = QMessageBox() confirm_msgbox.setWindowTitle(_('Delete backup')) confirm_msgbox.setText( _('This will delete the backup file. It ' 'cannot be undone.')) confirm_msgbox.setInformativeText( _('Are you sure you want to ' 'delete the <strong>{filename}</strong> backup?').format( filename=selected_info['path'])) confirm_msgbox.addButton(_('Delete the backup'), QMessageBox.YesRole) confirm_msgbox.addButton(_('I want to keep the backup'), QMessageBox.NoRole) confirm_msgbox.setIcon(QMessageBox.Warning) if confirm_msgbox.exec() == 0: main_window = self.get_main_window() status_bar = main_window.statusBar() if not delete_path(selected_info['path']): status_bar.showMessage(_('Backup deletion cancelled')) else: self.backups_table.removeRow(selected.row()) del self.backups[table_item] status_bar.showMessage(_('Backup deleted'))
def download_http_finished(self): self.downloading_file.close() main_window = self.get_main_window() status_bar = main_window.statusBar() status_bar.removeWidget(self.downloading_label) status_bar.removeWidget(self.dowloading_speed_label) status_bar.removeWidget(self.downloading_size_label) status_bar.removeWidget(self.downloading_progress_bar) status_bar.busy -= 1 if self.download_aborted: download_dir = os.path.dirname(self.downloaded_file) delete_path(download_dir) self.downloading_new_soundpack = False else: redirect = self.download_http_reply.attribute( QNetworkRequest.RedirectionTargetAttribute) if redirect is not None: download_dir = os.path.dirname(self.downloaded_file) delete_path(download_dir) os.makedirs(download_dir) self.downloading_file = open(self.downloaded_file, 'wb') status_bar.busy += 1 redirected_url = urljoin( self.download_http_reply.request().url().toString(), redirect.toString()) downloading_label = QLabel() downloading_label.setText(_('Downloading: {0}').format( redirected_url)) status_bar.addWidget(downloading_label, 100) self.downloading_label = downloading_label dowloading_speed_label = QLabel() status_bar.addWidget(dowloading_speed_label) self.dowloading_speed_label = dowloading_speed_label downloading_size_label = QLabel() status_bar.addWidget(downloading_size_label) self.downloading_size_label = downloading_size_label progress_bar = QProgressBar() status_bar.addWidget(progress_bar) self.downloading_progress_bar = progress_bar progress_bar.setMinimum(0) self.download_last_read = datetime.utcnow() self.download_last_bytes_read = 0 self.download_speed_count = 0 progress_bar.setValue(0) request = QNetworkRequest(QUrl(redirected_url)) request.setRawHeader(b'User-Agent', cons.FAKE_USER_AGENT) self.download_http_reply = self.qnam.get(request) self.download_http_reply.finished.connect( self.download_http_finished) self.download_http_reply.readyRead.connect( self.download_http_ready_read) self.download_http_reply.downloadProgress.connect( self.download_dl_progress) else: # Test downloaded file status_bar.showMessage(_('Testing downloaded file archive')) if self.downloaded_file.lower().endswith('.7z'): try: with open(self.downloaded_file, 'rb') as f: archive = Archive7z(f) except FormatError: status_bar.clearMessage() status_bar.showMessage(_('Selected file is a ' 'bad archive file')) self.finish_install_new_soundpack() return except NoPasswordGivenError: status_bar.clearMessage() status_bar.showMessage(_('Selected file is a ' 'password protected archive file')) self.finish_install_new_soundpack() return else: archive_exception = None if self.downloaded_file.lower().endswith('.zip'): archive_class = zipfile.ZipFile archive_exception = zipfile.BadZipFile test_method = 'testzip' elif self.downloaded_file.lower().endswith('.rar'): archive_class = rarfile.RarFile archive_exception = rarfile.Error test_method = 'testrar' else: extension = os.path.splitext(self.downloaded_file)[1] status_bar.clearMessage() status_bar.showMessage( _('Unknown downloaded archive format ({extension})' ).format(extension=extension)) self.finish_install_new_soundpack() return try: with archive_class(self.downloaded_file) as z: test = getattr(z, test_method) if test() is not None: status_bar.clearMessage() status_bar.showMessage( _('Downloaded archive is invalid')) self.finish_install_new_soundpack() return except archive_exception: status_bar.clearMessage() status_bar.showMessage(_('Selected file is a ' 'bad archive file')) self.finish_install_new_soundpack() return status_bar.clearMessage() self.downloading_new_soundpack = False self.extract_new_soundpack()
def install_new(self): if not self.installing_new_soundpack: selection_model = self.repository_lv.selectionModel() if selection_model is None or not selection_model.hasSelection(): return selected = selection_model.currentIndex() selected_info = self.repo_soundpacks[selected.row()] # Is it already installed? for soundpack in self.soundpacks: if soundpack['NAME'] == selected_info['name']: confirm_msgbox = QMessageBox() confirm_msgbox.setWindowTitle(_('Soundpack already present' )) confirm_msgbox.setText(_('It seems this soundpack is ' 'already installed. The launcher will not overwrite ' 'the soundpack if it has the same directory name. You ' 'might want to delete the soundpack first if you want ' 'to update it. Also, there can only be a single ' 'soundpack with the same name value available in the ' 'game.')) confirm_msgbox.setInformativeText(_('Are you sure you want ' 'to install the {view} soundpack?').format( view=selected_info['viewname'])) confirm_msgbox.addButton(_('Install the soundpack'), QMessageBox.YesRole) confirm_msgbox.addButton(_('Do not install again'), QMessageBox.NoRole) confirm_msgbox.setIcon(QMessageBox.Warning) if confirm_msgbox.exec() == 1: return break self.install_type = selected_info['type'] if selected_info['type'] == 'direct_download': if self.http_reply is not None and self.http_reply.isRunning(): self.http_reply_aborted = True self.http_reply.abort() self.installing_new_soundpack = True self.download_aborted = False download_dir = tempfile.mkdtemp(prefix=cons.TEMP_PREFIX) download_url = selected_info['url'] url = QUrl(download_url) file_info = QFileInfo(url.path()) file_name = file_info.fileName() self.downloaded_file = os.path.join(download_dir, file_name) self.downloading_file = open(self.downloaded_file, 'wb') main_window = self.get_main_window() status_bar = main_window.statusBar() status_bar.clearMessage() status_bar.busy += 1 downloading_label = QLabel() downloading_label.setText(_('Downloading: {0}').format( selected_info['url'])) status_bar.addWidget(downloading_label, 100) self.downloading_label = downloading_label dowloading_speed_label = QLabel() status_bar.addWidget(dowloading_speed_label) self.dowloading_speed_label = dowloading_speed_label downloading_size_label = QLabel() status_bar.addWidget(downloading_size_label) self.downloading_size_label = downloading_size_label progress_bar = QProgressBar() status_bar.addWidget(progress_bar) self.downloading_progress_bar = progress_bar progress_bar.setMinimum(0) self.download_last_read = datetime.utcnow() self.download_last_bytes_read = 0 self.download_speed_count = 0 self.downloading_new_soundpack = True request = QNetworkRequest(QUrl(url)) request.setRawHeader(b'User-Agent', cons.FAKE_USER_AGENT) self.download_http_reply = self.qnam.get(request) self.download_http_reply.finished.connect( self.download_http_finished) self.download_http_reply.readyRead.connect( self.download_http_ready_read) self.download_http_reply.downloadProgress.connect( self.download_dl_progress) self.install_new_button.setText(_('Cancel soundpack ' 'installation')) self.installed_lv.setEnabled(False) self.repository_lv.setEnabled(False) self.get_main_tab().disable_tab() self.get_mods_tab().disable_tab() self.get_settings_tab().disable_tab() self.get_backups_tab().disable_tab() elif selected_info['type'] == 'browser_download': bd_dialog = BrowserDownloadDialog('soundpack', selected_info['url'], selected_info.get('expected_filename', None)) bd_dialog.exec() if bd_dialog.downloaded_path is not None: self.installing_new_soundpack = True self.downloaded_file = bd_dialog.downloaded_path self.install_new_button.setText(_('Cancel soundpack ' 'installation')) self.installed_lv.setEnabled(False) self.repository_lv.setEnabled(False) self.get_main_tab().disable_tab() self.get_mods_tab().disable_tab() self.get_settings_tab().disable_tab() self.get_backups_tab().disable_tab() main_window = self.get_main_window() status_bar = main_window.statusBar() # Test downloaded file status_bar.showMessage(_('Testing downloaded file archive')) if self.downloaded_file.lower().endswith('.7z'): try: with open(self.downloaded_file, 'rb') as f: archive = Archive7z(f) except FormatError: status_bar.clearMessage() status_bar.showMessage(_('Selected file is a ' 'bad archive file')) self.finish_install_new_soundpack() return except NoPasswordGivenError: status_bar.clearMessage() status_bar.showMessage(_('Selected file is a ' 'password protected archive file')) self.finish_install_new_soundpack() return else: archive_exception = None if self.downloaded_file.lower().endswith('.zip'): archive_class = zipfile.ZipFile archive_exception = zipfile.BadZipFile test_method = 'testzip' elif self.downloaded_file.lower().endswith('.rar'): archive_class = rarfile.RarFile archive_exception = rarfile.Error test_method = 'testrar' else: extension = os.path.splitext(self.downloaded_file )[1] status_bar.clearMessage() status_bar.showMessage( _('Unknown downloaded archive format ' '({extension})').format(extension=extension)) self.finish_install_new_soundpack() return try: with archive_class(self.downloaded_file) as z: test = getattr(z, test_method) if test() is not None: status_bar.clearMessage() status_bar.showMessage( _('Downloaded archive is invalid')) self.finish_install_new_soundpack() return except archive_exception: status_bar.clearMessage() status_bar.showMessage(_('Selected file is a ' 'bad archive file')) self.finish_install_new_soundpack() return status_bar.clearMessage() self.extract_new_soundpack() else: main_window = self.get_main_window() status_bar = main_window.statusBar() # Cancel installation if self.downloading_new_soundpack: self.download_aborted = True self.download_http_reply.abort() elif self.extracting_new_soundpack: self.extracting_timer.stop() status_bar.removeWidget(self.extracting_label) status_bar.removeWidget(self.extracting_progress_bar) status_bar.busy -= 1 self.extracting_new_soundpack = False self.extracting_zipfile.close() download_dir = os.path.dirname(self.downloaded_file) delete_path(download_dir) if os.path.isdir(self.extract_dir): delete_path(self.extract_dir) status_bar.showMessage(_('Soundpack installation cancelled')) self.finish_install_new_soundpack()
def backup_saves(self, name, single=False): main_window = self.get_main_window() status_bar = main_window.statusBar() if self.game_dir is None: status_bar.showMessage(_('Game directory not found')) if self.after_backup is not None: self.after_backup() self.after_backup = None return save_dir = os.path.join(self.game_dir, 'save') if not os.path.isdir(save_dir): status_bar.showMessage(_('Save directory not found')) if self.after_backup is not None: self.after_backup() self.after_backup = None return self.save_dir = save_dir backup_dir = os.path.join(self.game_dir, 'save_backups') if not os.path.isdir(backup_dir): if os.path.isfile(backup_dir): os.remove(backup_dir) os.makedirs(backup_dir) if single: backup_filename = name + '.zip' self.backup_path = os.path.join(backup_dir, backup_filename) if os.path.isfile(self.backup_path): if not delete_path(self.backup_path): status_bar.showMessage(_('Could not delete previous ' 'backup archive')) return else: ''' Finding a backup filename which does not already exists or is the next backup name based on an incremental counter placed at the end of the filename without the extension. ''' name_lower = name.lower() name_key = alphanum_key(name_lower) if len(name_key) > 1 and isinstance(name_key[-1:][0], int): name_key = name_key[:-1] duplicate_name = False duplicate_basename = False max_counter = 0 for entry in scandir(backup_dir): filename, ext = os.path.splitext(entry.name) if entry.is_file() and ext.lower() == '.zip': filename_lower = filename.lower() if filename_lower == name_lower: duplicate_name = True else: filename_key = alphanum_key(filename_lower) counter = filename_key[-1:][0] if len(filename_key) > 1 and isinstance(counter, int): filename_key = filename_key[:-1] if name_key == filename_key: duplicate_basename = True max_counter = max(max_counter, counter) if duplicate_basename: name_key = alphanum_key(name) if len(name_key) > 1 and isinstance(name_key[-1:][0], int): name_key = name_key[:-1] name_key.append(max_counter + 1) backup_filename = ''.join(map(lambda x: str(x), name_key)) elif duplicate_name: backup_filename = name + '2' else: backup_filename = name backup_filename = backup_filename + '.zip' self.backup_path = os.path.join(backup_dir, backup_filename) self.backup_file = None status_bar.clearMessage() status_bar.busy += 1 compressing_label = QLabel() status_bar.addWidget(compressing_label, 100) self.compressing_label = compressing_label self.compressing_progress_bar = None self.compressing_speed_label = None self.compressing_size_label = None timer = QTimer(self) self.compressing_timer = timer self.backup_searching = True self.backup_compressing = False self.backup_files = deque() self.backup_file_sizes = {} self.backup_scan = None self.next_backup_scans = deque() self.total_backup_size = 0 self.total_files = 0 self.disable_tab() self.get_main_tab().disable_tab() self.get_soundpacks_tab().disable_tab() self.get_settings_tab().disable_tab() self.get_mods_tab().disable_tab() self.get_backups_tab().disable_tab() if self.manual_backup: self.backup_current_button.setText(_('Cancel backup')) self.backup_current_button.setEnabled(True) compressing_label.setText(_('Searching for save files')) def timeout(): if self.backup_scan is None: self.backup_scan = scandir(self.save_dir) else: try: entry = next(self.backup_scan) if entry.is_file(): self.compressing_label.setText( _('Found {filename} in {path}').format( filename=entry.name, path=os.path.dirname(entry.path))) self.backup_files.append(entry.path) self.total_backup_size += entry.stat().st_size self.backup_file_sizes[entry.path ] = entry.stat().st_size self.total_files += 1 elif entry.is_dir(): self.next_backup_scans.append(entry.path) except StopIteration: try: self.backup_scan = scandir( self.next_backup_scans.popleft()) except IndexError: self.backup_searching = False self.backup_compressing = True self.compressing_label.setText( _('Compressing save files')) compressing_speed_label = QLabel() compressing_speed_label.setText(_('{bytes_sec}/s' ).format(bytes_sec=sizeof_fmt(0))) status_bar.addWidget(compressing_speed_label) self.compressing_speed_label = ( compressing_speed_label) compressing_size_label = QLabel() compressing_size_label.setText( '{bytes_read}/{total_bytes}' .format(bytes_read=sizeof_fmt(0), total_bytes=sizeof_fmt(self.total_backup_size)) ) status_bar.addWidget(compressing_size_label) self.compressing_size_label = ( compressing_size_label) progress_bar = QProgressBar() progress_bar.setRange(0, self.total_backup_size) progress_bar.setValue(0) status_bar.addWidget(progress_bar) self.compressing_progress_bar = progress_bar self.comp_size = 0 self.comp_files = 0 self.last_comp_bytes = 0 self.last_comp = datetime.utcnow() self.next_backup_file = None if self.compressing_timer is not None: self.compressing_timer.stop() self.compressing_timer = None self.backup_saves_step2() timer.timeout.connect(timeout) timer.start(0)
def completed(): self.finish_backup_saves() delete_path(self.backup_path) self.compress_thread = None
def backup_current_clicked(self): if self.manual_backup and self.backup_searching: if (self.compressing_timer is not None and self.compressing_timer.isActive()): self.compressing_timer.stop() self.backup_searching = False self.finish_backup_saves() main_window = self.get_main_window() status_bar = main_window.statusBar() status_bar.showMessage(_('Manual backup cancelled')) elif self.manual_backup and self.backup_compressing: class WaitingThread(QThread): completed = pyqtSignal() def __init__(self, wthread): super(WaitingThread, self).__init__() self.wthread = wthread def __del__(self): self.wait() def run(self): self.wthread.wait() self.completed.emit() if self.compress_thread is not None: self.backup_current_button.setEnabled(False) self.compress_thread.quit() def completed(): self.finish_backup_saves() delete_path(self.backup_path) self.compress_thread = None waiting_thread = WaitingThread(self.compress_thread) waiting_thread.completed.connect(completed) self.waiting_thread = waiting_thread waiting_thread.start() else: self.finish_backup_saves() delete_path(self.backup_path) self.compress_thread = None self.backup_compressing = False main_window = self.get_main_window() status_bar = main_window.statusBar() status_bar.showMessage(_('Manual backup cancelled')) else: self.manual_backup = True name = safe_filename(self.name_le.text()) if name == '': name = _('manual_backup') self.name_le.setText(name) set_config_value('last_manual_backup_name', name) self.backup_saves(name)
def restore_backup(self): selection_model = self.backups_table.selectionModel() if selection_model is None or not selection_model.hasSelection(): return selected = selection_model.currentIndex() table_item = self.backups_table.item(selected.row(), 0) selected_info = self.backups[table_item] model = selection_model.model() backup_name = model.data(model.index(selected.row(), 0)) if not os.path.isfile(selected_info['path']): return main_window = self.get_main_window() status_bar = main_window.statusBar() self.temp_save_dir = None save_dir = os.path.join(self.game_dir, 'save') if os.path.isdir(save_dir): temp_save_dir = os.path.join(self.game_dir, 'save-{0}'.format( '%08x' % random.randrange(16**8))) while os.path.exists(temp_save_dir): temp_save_dir = os.path.join(self.game_dir, 'save-{0}'.format( '%08x' % random.randrange(16**8))) if not retry_rename(save_dir, temp_save_dir): status_bar.showMessage(_('Could not rename the save directory')) return self.temp_save_dir = temp_save_dir elif os.path.isfile(save_dir): if not delete_path(save_dir): status_bar.showMessage(_('Could not remove the save file')) return # Extract the backup archive self.extracting_backup = True self.extract_dir = self.game_dir status_bar.clearMessage() status_bar.busy += 1 self.total_extract_size = selected_info['actual_size'] extracting_label = QLabel() extracting_label.setText(_('Extracting backup')) status_bar.addWidget(extracting_label, 100) self.extracting_label = extracting_label extracting_speed_label = QLabel() extracting_speed_label.setText(_('{bytes_sec}/s' ).format(bytes_sec=sizeof_fmt(0))) status_bar.addWidget(extracting_speed_label) self.extracting_speed_label = extracting_speed_label extracting_size_label = QLabel() extracting_size_label.setText( '{bytes_read}/{total_bytes}' .format(bytes_read=sizeof_fmt(0), total_bytes=sizeof_fmt(self.total_extract_size)) ) status_bar.addWidget(extracting_size_label) self.extracting_size_label = ( extracting_size_label) progress_bar = QProgressBar() progress_bar.setRange(0, self.total_extract_size) progress_bar.setValue(0) status_bar.addWidget(progress_bar) self.extracting_progress_bar = progress_bar self.extract_size = 0 self.extract_files = 0 self.last_extract_bytes = 0 self.last_extract = datetime.utcnow() self.next_backup_file = None self.disable_tab() self.get_main_tab().disable_tab() self.get_soundpacks_tab().disable_tab() self.get_settings_tab().disable_tab() self.get_mods_tab().disable_tab() self.get_backups_tab().disable_tab() self.restore_button.setEnabled(True) self.restore_button.setText(_('Cancel restore backup')) class ExtractingThread(QThread): completed = pyqtSignal() def __init__(self, zfile, element, dir): super(ExtractingThread, self).__init__() self.zfile = zfile self.element = element self.dir = dir def __del__(self): self.wait() def run(self): self.zfile.extract(self.element, self.dir) self.completed.emit() def extract_next_file(): try: if self.extracting_backup: extracting_element = self.extracting_infolist.popleft() self.extracting_label.setText(_('Extracting {filename}' ).format(filename=extracting_element.filename)) self.next_extract_file = extracting_element extracting_thread = ExtractingThread( self.extracting_zipfile, extracting_element, self.extract_dir) extracting_thread.completed.connect(completed_extract) self.extracting_thread = extracting_thread extracting_thread.start() except IndexError: self.extracting_backup = False self.extracting_thread = None self.finish_restore_backup() main_window = self.get_main_window() status_bar = main_window.statusBar() status_bar.showMessage(_('{backup_name} backup restored' ).format(backup_name=backup_name)) def completed_extract(): self.extract_size += self.next_extract_file.file_size self.extracting_progress_bar.setValue(self.extract_size) self.extracting_size_label.setText( '{bytes_read}/{total_bytes}' .format(bytes_read=sizeof_fmt(self.extract_size), total_bytes=sizeof_fmt(self.total_extract_size)) ) delta_bytes = self.extract_size - self.last_extract_bytes delta_time = datetime.utcnow() - self.last_extract if delta_time.total_seconds() == 0: delta_time = timedelta.resolution bytes_secs = delta_bytes / delta_time.total_seconds() self.extracting_speed_label.setText(_('{bytes_sec}/s' ).format(bytes_sec=sizeof_fmt(bytes_secs))) self.last_extract_bytes = self.extract_size self.last_extract = datetime.utcnow() extract_next_file() self.extracting_zipfile = zipfile.ZipFile(selected_info['path']) self.extracting_infolist = deque(self.extracting_zipfile.infolist()) extract_next_file()
def restore_button_clicked(self): class WaitingThread(QThread): completed = pyqtSignal() def __init__(self, wthread): super(WaitingThread, self).__init__() self.wthread = wthread def __del__(self): self.wait() def run(self): self.wthread.wait() self.completed.emit() if self.backup_searching: if (self.compressing_timer is not None and self.compressing_timer.isActive()): self.compressing_timer.stop() self.backup_searching = False self.finish_backup_saves() main_window = self.get_main_window() status_bar = main_window.statusBar() status_bar.showMessage(_('Restore backup cancelled')) self.restore_button.setText(_('Restore backup')) elif self.backup_compressing: if self.compress_thread is not None: self.backup_current_button.setEnabled(False) self.compress_thread.quit() def completed(): self.finish_backup_saves() delete_path(self.backup_path) self.compress_thread = None waiting_thread = WaitingThread(self.compress_thread) waiting_thread.completed.connect(completed) self.waiting_thread = waiting_thread waiting_thread.start() else: self.finish_backup_saves() delete_path(self.backup_path) self.compress_thread = None self.backup_compressing = False main_window = self.get_main_window() status_bar = main_window.statusBar() status_bar.showMessage(_('Restore backup cancelled')) self.restore_button.setText(_('Restore backup')) elif self.extracting_backup: if self.extracting_thread is not None: self.restore_button.setEnabled(False) self.extracting_thread.quit() def completed(): save_dir = os.path.join(self.game_dir, 'save') delete_path(save_dir) if self.temp_save_dir is not None: retry_rename(self.temp_save_dir, save_dir) self.temp_save_dir = None self.finish_restore_backup() self.extracting_thread = None waiting_thread = WaitingThread(self.extracting_thread) waiting_thread.completed.connect(completed) self.waiting_thread = waiting_thread waiting_thread.start() else: self.finish_restore_backup() self.extracting_thread = None self.extracting_backup = False main_window = self.get_main_window() status_bar = main_window.statusBar() status_bar.showMessage(_('Restore backup cancelled')) else: selection_model = self.backups_table.selectionModel() if selection_model is None or not selection_model.hasSelection(): return selected = selection_model.currentIndex() table_item = self.backups_table.item(selected.row(), 0) selected_info = self.backups[table_item] if not os.path.isfile(selected_info['path']): return backup_previous = not config_true(get_config_value( 'do_not_backup_previous', 'False')) if backup_previous: ''' If restoring the before_last_restore, we rename it to make sure we make a proper backup first. ''' model = selection_model.model() backup_name = model.data(model.index(selected.row(), 0)) before_last_restore_name = _('before_last_restore') if backup_name.lower() == before_last_restore_name.lower(): backup_dir = os.path.join(self.game_dir, 'save_backups') name_lower = backup_name.lower() name_key = alphanum_key(name_lower) max_counter = 1 for entry in scandir(backup_dir): filename, ext = os.path.splitext(entry.name) if ext.lower() == '.zip': filename_lower = filename.lower() filename_key = alphanum_key(filename_lower) counter = filename_key[-1:][0] if len(filename_key) > 1 and isinstance(counter, int): filename_key = filename_key[:-1] if name_key == filename_key: max_counter = max(max_counter, counter) new_backup_name = (before_last_restore_name + str(max_counter + 1)) new_backup_path = os.path.join(backup_dir, new_backup_name + '.zip') if not retry_rename(selected_info['path'], new_backup_path): return selected_info['path'] = new_backup_path def next_step(): self.restore_backup() self.after_backup = next_step self.backup_saves(before_last_restore_name, True) self.restore_button.setEnabled(True) self.restore_button.setText(_('Cancel restore backup')) else: self.restore_backup()