mirror of
https://github.com/c9moser/sgbackup.git
synced 2026-01-19 19:40:13 +00:00
fixed backup of files
This commit is contained in:
parent
f2c188a500
commit
4608f7a6af
@ -31,6 +31,8 @@ import time
|
||||
|
||||
from ..game import Game
|
||||
from ..settings import settings
|
||||
from ..utility import sanitize_path,sanitize_windows_path
|
||||
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -91,7 +93,7 @@ class Archiver(GObject):
|
||||
dt.strftime("%Y%m%d-%H%M%S"),
|
||||
"sgbackup",
|
||||
self.extensions[0][1:] if self.extensions[0].startswith('.') else self.extensions[0]))
|
||||
return os.path.join(settings.backup_dir,game.savegame_name,game.subdir,basename)
|
||||
return sanitize_path(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:
|
||||
@ -242,8 +244,8 @@ class ArchiverManager(GObject):
|
||||
game_progress._game_progress_connection = self.connect('backup-game-progress',game_progress)
|
||||
threadpool = {}
|
||||
|
||||
if len(game_list) > 8:
|
||||
n = 8
|
||||
if len(game_list) > settings.backup_threads:
|
||||
n = settings.backup_threads
|
||||
else:
|
||||
n = len(games)
|
||||
|
||||
@ -278,8 +280,8 @@ class ArchiverManager(GObject):
|
||||
return self.emit('backup',archiver,game,filename)
|
||||
|
||||
@Signal(name="backup",return_type=bool,arg_types=(Archiver,Game,str),
|
||||
flags=SignalFlags.RUN_FIRST,accumulator=signal_accumulator_true_handled)
|
||||
def backup(self,archiver,game,filename):
|
||||
flags=SignalFlags.RUN_FIRST)
|
||||
def do_backup(self,archiver,game,filename):
|
||||
return True
|
||||
|
||||
def is_archive(self,filename)->bool:
|
||||
|
||||
@ -20,7 +20,7 @@ from gi.repository.GObject import Property
|
||||
from gi.repository import GLib
|
||||
|
||||
from ._archiver import Archiver
|
||||
from tarfile import TarFile
|
||||
from tarfile import open as tf_open, is_tarfile
|
||||
from tempfile import mkdtemp,NamedTemporaryFile
|
||||
import json
|
||||
import os
|
||||
@ -46,10 +46,11 @@ class TarfileArchiver(Archiver):
|
||||
return self.__compression
|
||||
|
||||
def is_archive(self, filename):
|
||||
if (Archiver.is_archive()):
|
||||
if (Archiver.is_archive(self,filename) and is_tarfile(filename)):
|
||||
try:
|
||||
with TarFile(filename,"r:{}".format(self.compression)) as tf:
|
||||
return ("gameconf.json" in tf.getnames())
|
||||
with tf_open(filename,"r:{}".format(self.compression)) as tf:
|
||||
#return ("gameconf.json" in tf.getnames())
|
||||
return True
|
||||
except:
|
||||
pass
|
||||
return False
|
||||
@ -57,14 +58,14 @@ class TarfileArchiver(Archiver):
|
||||
def do_backup(self, game, filename):
|
||||
_calc_fraction = lambda n,cnt: ((1.0 / n) * cnt)
|
||||
|
||||
self._backup_progress(game,0.0,"Starting {game} ...".format(game.name))
|
||||
self._backup_progress(game,0.0,"Starting {game} ...".format(game=game.name))
|
||||
files = game.get_backup_files()
|
||||
|
||||
n = len(files) + 2
|
||||
cnt=1
|
||||
data=json.dumps(game.serialize(),ensure_ascii=False,indent=4)
|
||||
|
||||
with TarFile(filename,'x:{}'.format(self.compression)) as tf:
|
||||
with tf_open(filename,'x:{}'.format(self.compression)) as tf:
|
||||
self._backup_progress(game,_calc_fraction(n,cnt),"gameconf.json")
|
||||
gcf = os.path.join(GLib.get_tmp_dir(),"sgbackup-" + GLib.get_user_name() + "." + "backup." + game.key + ".gameconf.tmp")
|
||||
with open(gcf,"wt",encoding="utf-8") as gcfile:
|
||||
@ -72,7 +73,7 @@ class TarfileArchiver(Archiver):
|
||||
|
||||
tf.add(gcf,"gameconf.json")
|
||||
|
||||
for path,arcname in files:
|
||||
for path,arcname in files.items():
|
||||
cnt += 1
|
||||
self._backup_progress(game,_calc_fraction(n,cnt),"arcname")
|
||||
tf.add(path,arcname)
|
||||
@ -99,7 +100,7 @@ class TarfileArchiver(Archiver):
|
||||
tempdir = mkdtemp(suffix="-sgbackup")
|
||||
tempfile= os.path.join(tempdir,"gameconf.json")
|
||||
try:
|
||||
with TarFile(filename,'r:{}'.format(self.compression)) as tf:
|
||||
with tf_open(filename,'r:{}'.format(self.compression)) as tf:
|
||||
tf.extract("gameconf.json",tempdir)
|
||||
with open(tempfile,"r",encoding="utf-8") as ifile:
|
||||
game = Game.new_from_json_file(tempfile)
|
||||
|
||||
@ -43,6 +43,7 @@ class ZipfileArchiver(Archiver):
|
||||
for path,arcname in files.items():
|
||||
cnt+=1
|
||||
self._backup_progress(game,_calc_fraction(div,cnt),"{} -> {}".format(game.name,arcname))
|
||||
print("writing file:",path)
|
||||
zf.write(path,arcname)
|
||||
|
||||
self._backup_progress(game,1.0,"{game} ... FINISHED".format(game=game.name))
|
||||
|
||||
@ -1356,7 +1356,6 @@ class Game(GObject):
|
||||
|
||||
def get_backup_files(self)->dict[str:str]|None:
|
||||
def get_backup_files_recursive(sgroot:pathlib.Path,sgdir:str,subdir:str|None=None):
|
||||
ret = {}
|
||||
if subdir:
|
||||
path = sgroot / sgdir / subdir
|
||||
else:
|
||||
@ -1366,13 +1365,14 @@ class Game(GObject):
|
||||
for dirent in os.listdir(path):
|
||||
file_path = path / dirent
|
||||
if file_path.is_file():
|
||||
|
||||
if subdir:
|
||||
fname = (os.path.join(subdir,dirent))
|
||||
fname = os.path.join(subdir,dirent).replace("\\","/")
|
||||
else:
|
||||
fname = dirent
|
||||
|
||||
if self.game_data.match(fname):
|
||||
ret[str(path)] = os.path.join(sgdir,fname)
|
||||
ret[str(str(file_path))] = os.path.join(sgdir,fname)
|
||||
elif file_path.is_dir():
|
||||
if subdir:
|
||||
ret.update(get_backup_files_recursive(sgroot,sgdir,os.path.join(subdir,dirent)))
|
||||
|
||||
@ -125,8 +125,16 @@ class SettingsDialog(Gtk.Dialog):
|
||||
grid.attach(label,0,1,1,1)
|
||||
page.backup_versions_spinbutton = Gtk.SpinButton.new_with_range(0,1000,1)
|
||||
page.backup_versions_spinbutton.set_hexpand(True)
|
||||
page.backup_versions_spinbutton.set_value(settings.backup_versions)
|
||||
grid.attach(page.backup_versions_spinbutton,1,1,2,1)
|
||||
|
||||
label = Gtk.Label.new('Max. Backup Threads:')
|
||||
page.backup_threads_spinbutton = Gtk.SpinButton.new_with_range(1,256,1)
|
||||
page.backup_threads_spinbutton.set_hexpand(True)
|
||||
page.backup_threads_spinbutton.set_value(settings.backup_threads)
|
||||
grid.attach(label,0,2,1,1)
|
||||
grid.attach(page.backup_threads_spinbutton,1,2,2,1)
|
||||
|
||||
label = Gtk.Label.new("Archiver:")
|
||||
archiver_model = Gio.ListStore.new(Archiver)
|
||||
for archiver in ArchiverManager.get_global().archivers.values():
|
||||
@ -145,8 +153,10 @@ class SettingsDialog(Gtk.Dialog):
|
||||
if archiver_key == archiver.key:
|
||||
page.archiver_dropdown.set_selected(i)
|
||||
break
|
||||
grid.attach(label,0,2,1,1)
|
||||
grid.attach(page.archiver_dropdown,1,2,2,1)
|
||||
grid.attach(label,0,3,1,1)
|
||||
grid.attach(page.archiver_dropdown,1,3,2,1)
|
||||
|
||||
|
||||
|
||||
vbox.append(grid)
|
||||
page.set_child(vbox)
|
||||
@ -216,13 +226,14 @@ class SettingsDialog(Gtk.Dialog):
|
||||
try:
|
||||
dir = dialog.select_folder_finish(result)
|
||||
if dir is not None:
|
||||
self.__backupdir_label.set_text(dir.get_path())
|
||||
self.general_page.backupdir_label.set_text(dir.get_path())
|
||||
except:
|
||||
pass
|
||||
|
||||
def _on_backupdir_button_clicked(self,button):
|
||||
dialog = Gtk.FileDialog.new()
|
||||
dialog.set_title("sgbackup: Choose backup folder")
|
||||
dialog.set_initial_folder(Gio.File.new_for_path(self.general_page.backupdir_label.get_text()))
|
||||
dialog.select_folder(self,None,self._on_backupdir_dialog_select_folder)
|
||||
|
||||
|
||||
@ -261,6 +272,7 @@ class SettingsDialog(Gtk.Dialog):
|
||||
def do_save(self):
|
||||
settings.backup_dir = self.general_page.backupdir_label.get_text()
|
||||
settings.backup_versions = self.general_page.backup_versions_spinbutton.get_value_as_int()
|
||||
settings.backup_threads = self.general_page.backup_threads_spinbutton.get_value_as_int()
|
||||
settings.archiver = self.general_page.archiver_dropdown.get_selected_item().key
|
||||
settings.zipfile_compression = self.archiver_page.zf_compressor_dropdown.get_selected_item().compressor
|
||||
settings.zipfile_compresslevel = self.archiver_page.zf_compresslevel_spinbutton.get_value_as_int()
|
||||
|
||||
@ -24,6 +24,8 @@ from gi.repository import GLib,GObject
|
||||
import zipfile
|
||||
from threading import RLock
|
||||
|
||||
from .utility import sanitize_path
|
||||
|
||||
ZIPFILE_COMPRESSION_STR = {
|
||||
zipfile.ZIP_STORED: "stored",
|
||||
zipfile.ZIP_DEFLATED: "deflated",
|
||||
@ -315,19 +317,30 @@ class Settings(GObject.GObject):
|
||||
|
||||
@GObject.Property(type=str,nick="backup-dir")
|
||||
def backup_dir(self)->str:
|
||||
return self.get_string('sgbackup','backupDirectory',
|
||||
os.path.join(GLib.get_home_dir(),'SavagameBackups'))
|
||||
return sanitize_path(self.get_string('sgbackup','backupDirectory',
|
||||
os.path.join(GLib.get_home_dir(),'SavagameBackups')))
|
||||
|
||||
@backup_dir.setter
|
||||
def backup_dir(self,directory:str):
|
||||
if not os.path.isabs(directory):
|
||||
raise ValueError("\"backup_dir\" needs to be an absolute path!")
|
||||
return self.set_string('sgbackup','backupDirectory',directory)
|
||||
return self.set_string('sgbackup','backupDirectory',sanitize_path(directory))
|
||||
|
||||
@GObject.Property(type=str)
|
||||
def loglevel(self)->str:
|
||||
return self.get_string('sgbackup','logLevel',"INFO")
|
||||
|
||||
@GObject.Property(type=int)
|
||||
def backup_threads(self)->int:
|
||||
return self.get_integer('sgbackup','maxBackupThreads',1)
|
||||
|
||||
@backup_threads.setter
|
||||
def backup_threads(self,max_threads:int):
|
||||
if (max_threads < 1):
|
||||
max_threads = 1
|
||||
self.set_integer('sgbackup','maxBackupThreads',max_threads)
|
||||
|
||||
|
||||
@GObject.Property
|
||||
def variables(self)->dict[str:str]:
|
||||
ret = {}
|
||||
@ -415,7 +428,7 @@ class Settings(GObject.GObject):
|
||||
|
||||
@GObject.Property(type=int)
|
||||
def zipfile_compression(self)->int:
|
||||
comp = self.parser.has_option('zipfile','compression','deflated')
|
||||
comp = self.get_string('zipfile','compression','deflated')
|
||||
try:
|
||||
return ZIPFILE_STR_COMPRESSION[comp]
|
||||
except:
|
||||
|
||||
36
sgbackup/utility.py
Normal file
36
sgbackup/utility.py
Normal file
@ -0,0 +1,36 @@
|
||||
###############################################################################
|
||||
# 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 <https://www.gnu.org/licenses/>. #
|
||||
###############################################################################
|
||||
|
||||
import sys
|
||||
if sys.platform.lower() == 'win32':
|
||||
PLATFORM_WINDOWS=True
|
||||
else:
|
||||
PLATFORM_WINDOWS=False
|
||||
|
||||
if sys.platform.lower() in ['linux','freebsd','netbsd','openbsd','dragonfly','macos','cygwin']:
|
||||
PLATFORM_UNIX = True
|
||||
else:
|
||||
PLATFORM_UNIX = False
|
||||
|
||||
def sanitize_windows_path(path:str)->str:
|
||||
return path.replace('/','\\')
|
||||
|
||||
def sanitize_path(path:str)->str:
|
||||
if (PLATFORM_WINDOWS):
|
||||
return sanitize_windows_path(path)
|
||||
return path
|
||||
Loading…
Reference in New Issue
Block a user