Commit 97c25bac authored by echel0n's avatar echel0n
Browse files

fixed issue with sabnzbd priority checkbox in ui

fixed issue with nzbget "connect using https" checkbox in ui
fixed issue with formatting check for nzbget host:post textbox
fixed issue with m4v files being ignored
fixed issue with getting local IP address
refined backup and restore options
added automatic backup feature for app
added option to enable/disable updating of video file metadata
parent c9ab287b
......@@ -160,7 +160,7 @@ test_py310:
build_master:
stage: build
image:
name: nikolaik/python-nodejs:python3.9-nodejs10-alpine
name: nikolaik/python-nodejs:python3.10-nodejs14-alpine
variables:
NODE_ENV: "development"
CARGO_HOME: "$CI_PROJECT_DIR/cargo"
......@@ -210,7 +210,7 @@ build_develop:
stage: build
retry: 2
image:
name: nikolaik/python-nodejs:python3.9-nodejs10-alpine
name: nikolaik/python-nodejs:python3.10-nodejs14-alpine
variables:
NODE_ENV: "development"
CARGO_HOME: "$CI_PROJECT_DIR/cargo"
......
......@@ -19,7 +19,7 @@ def upgrade():
'announcements',
sa.Column('id', sa.Integer, primary_key=True),
sa.Column('hash', sa.String(255), unique=True, nullable=False),
sa.Column('seen', sa.Boolean, default=False)
sa.Column('seen', sa.Boolean, server_default='false')
)
......
......@@ -263,6 +263,7 @@ class ConfigDB(SRDatabase):
view_changelog = Column(Boolean, default=False)
strip_special_file_bits = Column(Boolean, default=True)
max_queue_workers = Column(Integer, default=5)
update_video_metadata = Column(Boolean, default=True)
class GUI(base):
__tablename__ = 'gui'
......
"""Initial migration
Revision ID: 8
Revises:
Create Date: 2017-12-29 14:39:27.854291
"""
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision = '8'
down_revision = '7'
def upgrade():
op.add_column('general', sa.Column('update_video_metadata', sa.Boolean, server_default='true'))
def downgrade():
pass
......@@ -35,7 +35,7 @@ def upgrade():
tv_shows = sa.Table('tv_shows', meta, autoload=True)
if not hasattr(tv_shows.c, 'scene'):
op.add_column('tv_shows', sa.Column('scene', sa.Boolean, default=0))
op.add_column('tv_shows', sa.Column('scene', sa.Boolean, server_default='false'))
with op.get_context().begin_transaction():
for row in conn.execute(tv_shows.select()):
......
......@@ -63,6 +63,7 @@ from sickrage.core.enums import TorrentMethod
from sickrage.core.helpers import encryption
from sickrage.core.websession import WebSession
mimetypes.add_type('video/x-m4v', '.m4v')
mimetypes.add_type('video/x-matroska', '.mkv')
mimetypes.add_type('video/divx', '.divx')
mimetypes.add_type("video/x-flv", ".flv")
......@@ -873,58 +874,48 @@ def create_zipfile(fileList, archive, arcname=None):
return False
def restore_config_zip(archive, targetDir, restore_database=True, restore_config=True, restore_cache=True):
def restore_config_zip(archive, target_dir, restore_main_database=True, restore_config_database=True, restore_cache_database=True, restore_image_cache=True):
"""
Restores a backup ZIP file back in place
:param archive: ZIP filename
:param targetDir: Directory to restore to
:return: True on success, False on failure
"""
if not os.path.isfile(archive):
return
try:
if not os.path.exists(targetDir):
os.mkdir(targetDir)
if not os.path.exists(target_dir):
os.mkdir(target_dir)
else:
def path_leaf(path):
head, tail = os.path.split(path)
return tail or os.path.basename(head)
bakFilename = '{0}-{1}'.format(path_leaf(targetDir), datetime.datetime.now().strftime('%Y%m%d_%H%M%S'))
move_file(targetDir, os.path.join(os.path.dirname(targetDir), bakFilename))
bak_filename = f'{path_leaf(target_dir)}-{datetime.datetime.now().strftime("%Y%m%d_%H%M%S")}'
move_file(target_dir, os.path.join(os.path.dirname(target_dir), bak_filename))
with zipfile.ZipFile(archive, 'r', allowZip64=True) as zip_file:
for member in zip_file.namelist():
if not restore_database and member.split('/')[0] in ['main_db_backup.json',
'main.db',
'main.db-shm',
'main.db-wal',
'cache_db_backup.json',
'cache.db',
'cache.db-shm',
'cache.db-wal']:
if not restore_main_database and member.split('/')[0] == 'main_db_backup.json':
continue
if not restore_config_database and member.split('/')[0] == 'config_db_backup.json':
continue
if not restore_config and member.split('/')[0] in ['config.ini',
'privatekey.pem'
'config.db']:
if not restore_cache_database and member.split('/')[0] == 'cache_db_backup.json':
continue
if not restore_cache and member.split('/')[0] == 'cache':
if not restore_image_cache and member.split('/')[0] == 'cache':
continue
zip_file.extract(member, targetDir)
zip_file.extract(member, target_dir)
return True
except Exception as e:
sickrage.app.log.warning("Zip extraction error: {}".format(e))
shutil.rmtree(targetDir)
shutil.rmtree(target_dir)
def backup_app_data(backupDir, keep_latest=False):
def backup_app_data(backup_dir, backup_main_db=True, backup_config_db=True, backup_cache_db=True, backup_image_cache=True, keep_latest=False):
source = []
# files_list = [
......@@ -933,11 +924,11 @@ def backup_app_data(backupDir, keep_latest=False):
# ]
def _keep_latest_backup():
for x in sorted(glob.glob(os.path.join(backupDir, '*.zip')), key=os.path.getctime, reverse=True)[1:]:
for x in sorted(glob.glob(os.path.join(backup_dir, '*.zip')), key=os.path.getctime, reverse=True)[1:]:
os.remove(x)
if not os.path.exists(backupDir):
os.mkdir(backupDir)
if not os.path.exists(backup_dir):
os.mkdir(backup_dir)
if keep_latest:
_keep_latest_backup()
......@@ -949,13 +940,23 @@ def backup_app_data(backupDir, keep_latest=False):
# source += [fp]
# databases
for db in [sickrage.app.main_db, sickrage.app.config.db, sickrage.app.cache_db]:
backup_file = os.path.join(*[sickrage.app.data_dir, '{}_db_backup.json'.format(db.name)])
db.backup(backup_file)
if backup_main_db:
backup_file = os.path.join(*[sickrage.app.data_dir, f'{sickrage.app.main_db.name}_db_backup.json'])
sickrage.app.main_db.backup(backup_file)
source += [backup_file]
if backup_config_db:
backup_file = os.path.join(*[sickrage.app.data_dir, f'{sickrage.app.config.db.name}_db_backup.json'])
sickrage.app.config.db.backup(backup_file)
source += [backup_file]
if backup_cache_db:
backup_file = os.path.join(*[sickrage.app.data_dir, f'{sickrage.app.cache_db.name}_db_backup.json'])
sickrage.app.cache_db.backup(backup_file)
source += [backup_file]
# cache folder
if sickrage.app.cache_dir:
if backup_image_cache and sickrage.app.cache_dir:
for (path, dirs, files) in os.walk(sickrage.app.cache_dir, topdown=True):
for dirname in dirs:
if path == sickrage.app.cache_dir and dirname not in ['images']:
......@@ -965,12 +966,12 @@ def backup_app_data(backupDir, keep_latest=False):
source += [os.path.join(path, filename)]
# ZIP filename
target = os.path.join(backupDir, 'sickrage-{}.zip'.format(datetime.datetime.now().strftime('%Y%m%d%H%M%S')))
target = os.path.join(backup_dir, f'sickrage-{datetime.datetime.now().strftime("%Y%m%d%H%M%S")}.zip')
return create_zipfile(source, target, sickrage.app.data_dir)
def restore_app_data(srcDir, dstDir):
def restore_app_data(src_dir, dst_dir):
try:
files_list = [
'main.db',
......@@ -980,33 +981,31 @@ def restore_app_data(srcDir, dstDir):
'cache.db',
'cache.db-shm',
'cache.db-wal',
'main.codernitydb',
'cache.codernitydb',
'privatekey.pem',
os.path.basename(sickrage.app.config_file)
]
for filename in files_list:
srcFile = os.path.join(srcDir, filename)
dstFile = os.path.join(dstDir, filename)
bakFile = os.path.join(dstDir, '{}_{}.bak'.format(filename, datetime.datetime.now().strftime('%Y%m%d_%H%M%S')))
src_file = os.path.join(src_dir, filename)
dst_file = os.path.join(dst_dir, filename)
bak_file = os.path.join(dst_dir, '{}_{}.bak'.format(filename, datetime.datetime.now().strftime('%Y%m%d_%H%M%S')))
if os.path.exists(srcFile):
if os.path.isfile(dstFile):
move_file(dstFile, bakFile)
move_file(srcFile, dstFile)
if os.path.exists(src_file):
if os.path.isfile(dst_file):
move_file(dst_file, bak_file)
move_file(src_file, dst_file)
# database
for db in [sickrage.app.main_db, sickrage.app.config.db, sickrage.app.cache_db]:
backup_file = os.path.join(*[srcDir, '{}_db_backup.json'.format(db.name)])
backup_file = os.path.join(*[src_dir, '{}_db_backup.json'.format(db.name)])
if os.path.exists(backup_file):
db.restore(backup_file)
# cache
if os.path.exists(os.path.join(srcDir, 'cache')):
if os.path.exists(os.path.join(dstDir, 'cache')):
move_file(os.path.join(dstDir, 'cache'), os.path.join(dstDir, '{}_{}.bak'.format('cache', datetime.datetime.now().strftime('%Y%m%d_%H%M%S'))))
move_file(os.path.join(srcDir, 'cache'), dstDir)
if os.path.exists(os.path.join(src_dir, 'cache')):
if os.path.exists(os.path.join(dst_dir, 'cache')):
move_file(os.path.join(dst_dir, 'cache'), os.path.join(dst_dir, '{}_{}.bak'.format('cache', datetime.datetime.now().strftime('%Y%m%d_%H%M%S'))))
move_file(os.path.join(src_dir, 'cache'), dst_dir)
return True
except Exception as e:
......@@ -1746,9 +1745,12 @@ def get_external_ip():
def get_internal_ip():
"""Return internal IP of system."""
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(('8.8.8.8', 1))
return s.getsockname()[0]
try:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(('8.8.8.8', 1))
return s.getsockname()[0]
except Exception:
return socket.gethostbyname(socket.gethostname())
def get_ip_address(hostname):
......
......@@ -1094,7 +1094,8 @@ class PostProcessor(object):
root_episode_object.create_meta_files()
# update video file metadata
root_episode_object.update_video_metadata()
if sickrage.app.config.general.update_video_metadata:
root_episode_object.update_video_metadata()
# save changes to database
[cur_ep.save() for cur_ep in episode_objects]
......
......@@ -838,7 +838,9 @@ class TVShow(object):
if not show_only:
self.write_episode_nfos(force)
self.update_episode_video_metadata()
if sickrage.app.config.general.update_video_metadata:
self.update_episode_video_metadata()
def write_episode_nfos(self, force=False):
if not os.path.isdir(self.location):
......@@ -867,7 +869,6 @@ class TVShow(object):
continue
sickrage.app.log.debug(str(self.series_id) + ": Updating video metadata for episode S%02dE%02d" % (episode_obj.season, episode_obj.episode))
episode_obj.update_video_metadata()
# find all media files in the show folder and create episodes for as many as possible
......
......@@ -65,9 +65,10 @@ class ConfigRestoreHandler(BaseHandler):
@authenticated
def get(self, *args, **kwargs):
backup_file = self.get_argument('backupFile')
restore_database = self.get_argument('restore_database')
restore_config = self.get_argument('restore_config')
restore_cache = self.get_argument('restore_cache')
restore_main_database = self.get_argument('restore_main_database')
restore_config_database = self.get_argument('restore_config_database')
restore_cache_database = self.get_argument('restore_cache_database')
restore_image_cache = self.get_argument('restore_image_cache')
final_result = ''
......@@ -75,11 +76,12 @@ class ConfigRestoreHandler(BaseHandler):
source = backup_file
target_dir = os.path.join(sickrage.app.data_dir, 'restore')
restore_database = checkbox_to_value(restore_database)
restore_config = checkbox_to_value(restore_config)
restore_cache = checkbox_to_value(restore_cache)
restore_main_database = checkbox_to_value(restore_main_database)
restore_config_database = checkbox_to_value(restore_config_database)
restore_cache_database = checkbox_to_value(restore_cache_database)
restore_image_cache = checkbox_to_value(restore_image_cache)
if restore_config_zip(source, target_dir, restore_database, restore_config, restore_cache):
if restore_config_zip(source, target_dir, restore_main_database, restore_config_database, restore_cache_database, restore_image_cache):
final_result += _("Successfully extracted restore files to " + target_dir)
final_result += _("<br>Restart sickrage to complete the restore.")
else:
......
......@@ -161,6 +161,7 @@ class SaveGeneralHandler(BaseHandler):
web_username = self.get_argument('web_username', '')
web_password = self.get_argument('web_password', '')
enable_sickrage_api = self.get_argument('enable_sickrage_api', None)
update_video_metadata = self.get_argument('update_video_metadata', None)
results = []
......@@ -271,6 +272,8 @@ class SaveGeneralHandler(BaseHandler):
sickrage.app.config.general.max_queue_workers = try_int(max_queue_workers)
sickrage.app.config.general.update_video_metadata = checkbox_to_value(update_video_metadata)
sickrage.app.config.save()
if auth_method_changed:
......
......@@ -10,21 +10,36 @@
<div class="col-lg-3 col-md-4 col-sm-4 card-title">
<h3>${_('Backup')}</h3>
<small class="form-text text-muted">
<b>${_('Backup your main database file and config')}</b>
<b>${_('Backup SiCKRAGE')}</b>
</small>
</div>
<fieldset class="col-lg-9 col-md-8 col-sm-8 card-text">
<div class="form-row form-group">
<div class="col-md-12 component-desc">
<div class="input-group">
<input name="backupDir" id="backupDir" class="form-control"
placeholder="${_('Select the folder you wish to save your backup file to')}"
autocapitalize="off"/>
<div class="input-group-append">
<span class="input-group-text">
<a href="#" class="fas fa-download" title="${_('Backup')}" id="Backup"></a>
</span>
<div class="form-row">
<div class="col-md-12">
<div class="input-group">
<input name="backupDir" id="backupDir" class="form-control"
placeholder="${_('Select the folder you wish to save your backup file to')}"
autocapitalize="off"/>
<div class="input-group-append">
<span class="input-group-text">
<a href="#" class="fas fa-download" title="${_('Backup')}" id="Backup"></a>
</span>
</div>
</div>
</div>
</div>
<br/>
<div class="form-row">
<div class="col-md-12">
<label for="auto_backup">
<input type="checkbox" class="toggle color-primary is-material" name="auto_backup" id="auto_backup" checked/>
${_('Automatic Backups')}
</label>
</div>
</div>
</div>
......@@ -43,7 +58,7 @@
<div class="col-lg-3 col-md-4 col-sm-4 card-title">
<h3>${_('Restore')}</h3>
<small class="form-text text-muted">
<b>${_('Restore your main database file and config')}</b>
<b>${_('Restore SiCKRAGE')}</b>
</small>
</div>
<fieldset class="col-lg-9 col-md-8 col-sm-8 card-text">
......@@ -68,27 +83,36 @@
<div class="form-row">
<div class="col-md-12">
<label for="restore_database">
<input type="checkbox" class="toggle color-primary is-material" name="restore_database" id="restore_database" checked/>
${_('Restore database files')}
<label for="restore_main_database">
<input type="checkbox" class="toggle color-primary is-material" name="restore_main_database" id="restore_main_database" checked/>
${_('Restore main database file')}
</label>
</div>
</div>
<div class="form-row">
<div class="col-md-12">
<label for="restore_config_database">
<input type="checkbox" class="toggle color-primary is-material" name="restore_config_database" id="restore_config_database" checked/>
${_('Restore config database file')}
</label>
</div>
</div>
<div class="form-row">
<div class="col-md-12">
<label for="restore_config">
<input type="checkbox" class="toggle color-primary is-material" name="restore_config" id="restore_config" checked/>
${_('Restore configuration file')}
<label for="restore_cache_database">
<input type="checkbox" class="toggle color-primary is-material" name="restore_cache_database" id="restore_cache_database" checked/>
${_('Restore cache database file')}
</label>
</div>
</div>
<div class="form-row">
<div class="col-md-12">
<label for="restore_cache">
<input type="checkbox" class="toggle color-primary is-material" name="restore_cache" id="restore_cache" checked/>
${_('Restore cache files')}
<label for="restore_image_cache">
<input type="checkbox" class="toggle color-primary is-material" name="restore_image_cache" id="restore_image_cache" checked/>
${_('Restore image cache files')}
</label>
<div class="checkbox"></div>
</div>
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment