From 52c986846e9483e6cb7f02b9358d93c2c6804f9d Mon Sep 17 00:00:00 2001 From: Christian Moser Date: Tue, 21 Jan 2025 10:02:46 +0100 Subject: [PATCH] 2025.01.21 10:02:45 --- sgbackup/archiver/_archiver.py | 71 +++++++++++++++++++++++++--- sgbackup/archiver/zipfilearchiver.py | 16 ++++++- sgbackup/gui/_backupdialog.py | 22 +++++++++ 3 files changed, 101 insertions(+), 8 deletions(-) create mode 100644 sgbackup/gui/_backupdialog.py diff --git a/sgbackup/archiver/_archiver.py b/sgbackup/archiver/_archiver.py index ebcd853..6e43476 100644 --- a/sgbackup/archiver/_archiver.py +++ b/sgbackup/archiver/_archiver.py @@ -26,6 +26,8 @@ from gi.repository.GObject import ( import datetime import os +import threading +import time from ..game import Game from ..settings import settings @@ -67,9 +69,6 @@ class Archiver: self.emit('backup',game,filename) - def restore(self,game,file)->bool: - pass - def generate_new_backup_filename(self,game:Game)->str: dt = datetime.datetime.now() basename = '.'.join(game.savegame_name, @@ -79,7 +78,13 @@ class Archiver: self.extensions[0]) return os.path.join(settings.backup_dir,game.savegame_name,game.subdir,basename) - + def _backup_progress(self,game:Game,fraction:float,message:str|None): + if fraction > 1.0: + fraction = 1.0 + elif fraction < 0.0: + fraction = 0.0 + + self.emit("progress",game,fraction,message) @Signal(name="backup",flags=SignalFlags.RUN_FIRST, @@ -94,6 +99,13 @@ class Archiver: def do_restore(self,filanme:str): raise NotImplementedError("{_class}.{function}() is not implemented!",_class=__class__,function="do_restore") + @Signal(name="backup_progress",flags=SignalFlags.RUN_FIRST, + return_type=bool,arg_types=(Game,float,str)) + def do_backup_progress(self,game:Game,fraction:float,message:str): + pass + + + class ArchiverManager(GObject): __global_archiver_manager = None @@ -114,7 +126,54 @@ class ArchiverManager(GObject): try: return self.__archivers[settings.archiver] except: - return self.__archivers["zipfile"] + return self.__archivers["zipfile"] + + def _on_archiver_backup_progress_single(self,archiver,game,fraction,message): + pass + + def _on_archiver_backup_progress_multi(self,archiver,game,fraction,message): + pass + + def backup(self,game:Game): + archiver = self.standard_archiver + archiver.backup(game) - \ No newline at end of file + def backup_many(self,games:list[Game]): + def thread_function(game): + archiver = self.standard_archiver + self._on_archiver_backup_progress_multi(archiver,game,1.0,"Finished ...") + + game_list = list(games) + threadpool = {} + + if len(game_list) > 8: + n = 8 + else: + n = len(games) + + for i in range(n): + game=game_list[0] + del game_list[0] + + thread = threading.Thread(thread_function,args=game,daemon=True) + threadpool.append(thread) + thread.start() + + while threadpool: + time.sleep(0.25) + for i in range(len(threadpool)): + thread = threadpool[i] + if thread.is_alive(): + continue + del threadpool[i] + + if game_list: + game = game_list[0] + del game_list[0] + thread = threading.Thread(thread_function,args=game,daemon=True) + threadpool.append(thread) + thread.start() + + + \ No newline at end of file diff --git a/sgbackup/archiver/zipfilearchiver.py b/sgbackup/archiver/zipfilearchiver.py index 37a1e47..48f84d4 100644 --- a/sgbackup/archiver/zipfilearchiver.py +++ b/sgbackup/archiver/zipfilearchiver.py @@ -28,15 +28,26 @@ class ZipfileArchiver(Archiver): Archiver.__init__(self,"zipfile","ZipFile",[".zip"],"Archiver for .zip files.") def do_backup(self, game:Game, filename:str): + _calc_fraction = lambda n,cnt: ((1.0 / n) * cnt) + + self._backup_progress(game,0.0,"Starting {game} ...".format(game=game.name)) files = game.get_backup_files() + div = len(files + 2) + cnt=1 game_data = json.dumps(game.serialize(),ensure_ascii=False,indent=4) with zipfile.ZipFile(filename,mode="w", compression=settings.zipfile_compression, compresslevel=settings.zipfile_compresslevel) as zf: - zf.writestr("game.conf",game_data) + self._backup_progress(game,_calc_fraction(div,cnt),"{} -> {}".format(game.name,"gameconf.json")) + zf.writestr("gameconf.json",game_data) for path,arcname in files.items(): + cnt+=1 + self._backup_progress(game,_calc_fraction(div,cnt),"{} -> {}".format(game.name,arcname)) zf.write(path,arcname) - + + self._backup_progress("{game} ... FINISHED".format(game=game.name)) + + def is_archive(self,filename:str)->bool: if zipfile.is_zipfile(filename): with zipfile.ZipFile(filename,"r") as zf: @@ -47,6 +58,7 @@ class ZipfileArchiver(Archiver): def do_restore(self,filename:str): # TODO: convert savegame dir if not the same SvaegameType!!! + if not zipfile.is_zipfile(filename): raise RuntimeError("\"{filename}\" is not a valid sgbackup zipfile archive!") diff --git a/sgbackup/gui/_backupdialog.py b/sgbackup/gui/_backupdialog.py new file mode 100644 index 0000000..12c66a2 --- /dev/null +++ b/sgbackup/gui/_backupdialog.py @@ -0,0 +1,22 @@ +############################################################################### +# sgbackup - The SaveGame Backup tool # +# Copyright (C) 2024 Christian Moser # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +############################################################################### + +from ..game import GameManager,Game +from ..archiver import ArchiverManager + +