Merge branch 'feature/AddConfigBackup' into dev

This commit is contained in:
JackDandy 2023-07-13 20:24:26 +01:00
commit 71787f010f
3 changed files with 125 additions and 3 deletions

View file

@ -13,7 +13,6 @@
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with SickGear. If not, see <http://www.gnu.org/licenses/>. # along with SickGear. If not, see <http://www.gnu.org/licenses/>.
from collections import OrderedDict from collections import OrderedDict
from threading import Lock from threading import Lock
@ -23,6 +22,7 @@ import os
import re import re
import signal import signal
import socket import socket
import time
import webbrowser import webbrowser
# apparently py2exe won't build these unless they're imported somewhere # apparently py2exe won't build these unless they're imported somewhere
@ -54,6 +54,7 @@ from configobj import ConfigObj
from api_trakt import TraktAPI from api_trakt import TraktAPI
from _23 import b64encodestring, decode_bytes, scandir from _23 import b64encodestring, decode_bytes, scandir
from sg_helpers import remove_file_perm
from six import iteritems, string_types from six import iteritems, string_types
import sg_helpers import sg_helpers
@ -2423,7 +2424,69 @@ def save_config():
new_config['ANIME'] = {} new_config['ANIME'] = {}
new_config['ANIME']['anime_treat_as_hdtv'] = int(ANIME_TREAT_AS_HDTV) new_config['ANIME']['anime_treat_as_hdtv'] = int(ANIME_TREAT_AS_HDTV)
new_config.write() from sg_helpers import copy_file
backup_config = re.sub(r'\.ini$', '.bak', CONFIG_FILE)
from .config import check_valid_config
try:
copy_file(CONFIG_FILE, backup_config)
if not check_valid_config(backup_config):
logger.error('config file seams to be invalid, not backing up.')
remove_file_perm(backup_config)
backup_config = None
except (BaseException, Exception):
backup_config = None
for _ in range(0, 3):
new_config.write()
if check_valid_config(CONFIG_FILE):
return
logger.warning('saving config file failed, retrying...')
remove_file_perm(CONFIG_FILE)
time.sleep(3)
# we only get here if the config saving failed multiple times
if None is not backup_config and os.path.isfile(backup_config):
logger.error('saving config file failed, using backup file')
try:
copy_file(backup_config, CONFIG_FILE)
logger.log('using old backup config file')
return
except (BaseException, Exception):
logger.error('failed to use backup config file')
from sg_helpers import scantree
try:
target_base = os.path.join(BACKUP_DB_PATH or os.path.join(DATA_DIR, 'backup'))
file_list = [f for f in scantree(target_base, include='config', filter_kind=False)]
if file_list:
logger.log('trying to use latest config.ini backup')
# sort newest to oldest backup
file_list.sort(key=lambda _f: _f.stat(follow_symlinks=False).st_mtime)
import zipfile
try:
with zipfile.ZipFile(file_list[0].path, mode='r') as zf:
zf.extractall(target_base)
backup_config_file = os.path.join(target_base, 'config.ini')
if os.path.isfile(backup_config_file):
os.replace(backup_config_file, CONFIG_FILE)
if check_valid_config(CONFIG_FILE):
logger.log(f'used latest config.ini backup file: {file_list[0].name}')
return
else:
logger.error(f'failed to use latest config.ini backup file: {file_list[0].name}')
remove_file_perm(CONFIG_FILE)
except (BaseException, Exception):
pass
finally:
try:
remove_file_perm(backup_config_file)
except (BaseException, Exception):
pass
logger.error('failed to use latest config.ini')
except (BaseException, Exception):
pass
logger.error('saving config file failed and no backup available')
def launch_browser(start_port=None): def launch_browser(start_port=None):

View file

@ -24,6 +24,7 @@ from . import db, helpers, logger, naming
from lib.api_trakt import TraktAPI from lib.api_trakt import TraktAPI
from _23 import urlsplit, urlunsplit from _23 import urlsplit, urlunsplit
from sg_helpers import compress_file, copy_file, remove_file_perm, scantree, try_int
from six import string_types from six import string_types
@ -455,6 +456,58 @@ def check_setting_str(config, cfg_name, item_name, def_val, log=True):
return (my_val, def_val)['None' == my_val] return (my_val, def_val)['None' == my_val]
def check_valid_config(filename):
# type: (str) -> bool
"""
check if file appears to be a vaild config file
:param filename: full path config file name
"""
from configobj import ConfigObj
try:
conf_obj = ConfigObj(filename)
if not (all(section in conf_obj for section in ('General', 'GUI')) and 'config_version' in conf_obj['General']
and isinstance(try_int(conf_obj['General']['config_version'], None), int)):
return False
return True
except (BaseException, Exception):
return False
finally:
try:
del conf_obj
except (BaseException, Exception):
pass
def backup_config():
"""
backup config.ini
"""
logger.log('backing up config.ini')
try:
if not check_valid_config(sickgear.CONFIG_FILE):
logger.error('config file seams to be invalid, not backing up.')
return
now = datetime.datetime.now()
d = datetime.datetime.strftime(now, '%Y-%m-%d')
t = datetime.datetime.strftime(now, '%H-%M')
target_base = os.path.join(sickgear.BACKUP_DB_PATH or os.path.join(sickgear.DATA_DIR, 'backup'))
target = os.path.join(target_base, 'config.ini')
copy_file(sickgear.CONFIG_FILE, target)
if not check_valid_config(target):
logger.error('config file seams to be invalid, not backing up.')
remove_file_perm(target)
return
compress_file(target, 'config.ini')
os.rename(re.sub(r'\.ini$', '.zip', target), os.path.join(target_base, f'config_{d}_{t}.zip'))
# remove old files
use_count = (1, sickgear.BACKUP_DB_MAX_COUNT)[not sickgear.BACKUP_DB_ONEDAY]
file_list = [f for f in scantree(target_base, include='config', filter_kind=False)]
if use_count < len(file_list):
file_list.sort(key=lambda _f: _f.stat(follow_symlinks=False).st_mtime, reverse=True)
for direntry in file_list[use_count:]:
remove_file_perm(direntry.path)
except (BaseException, Exception):
logger.error('backup config.ini error')
class ConfigMigrator(object): class ConfigMigrator(object):
def __init__(self, config_obj): def __init__(self, config_obj):
@ -926,3 +979,4 @@ class ConfigMigrator(object):
def _migrate_v22(self): def _migrate_v22(self):
self.deprecate_anon_service() self.deprecate_anon_service()

View file

@ -24,6 +24,7 @@ from exceptions_helper import ex
import sickgear import sickgear
from . import db, logger, network_timezones, properFinder, ui from . import db, logger, network_timezones, properFinder, ui
from .scheduler import Job from .scheduler import Job
from .config import backup_config
# noinspection PyUnreachableCode # noinspection PyUnreachableCode
if False: if False:
@ -70,7 +71,11 @@ class ShowUpdater(Job):
if sickgear.db.db_supports_backup and 0 < sickgear.BACKUP_DB_MAX_COUNT: if sickgear.db.db_supports_backup and 0 < sickgear.BACKUP_DB_MAX_COUNT:
logger.log('backing up all db\'s') logger.log('backing up all db\'s')
try: try:
sickgear.db.backup_all_dbs(sickgear.BACKUP_DB_PATH or os.path.join(sickgear.DATA_DIR, 'backup')) backup_success = sickgear.db.backup_all_dbs(
sickgear.BACKUP_DB_PATH or os.path.join(sickgear.DATA_DIR, 'backup'))
if isinstance(backup_success, tuple) and backup_success[0]:
# backup config.ini
backup_config()
except (BaseException, Exception): except (BaseException, Exception):
logger.error('backup db error') logger.error('backup db error')