diff --git a/resources/language/resource.language.en_gb/strings.po b/resources/language/resource.language.en_gb/strings.po index 3d9de8cd..88c4dbdb 100644 --- a/resources/language/resource.language.en_gb/strings.po +++ b/resources/language/resource.language.en_gb/strings.po @@ -518,6 +518,18 @@ msgctxt "#33088" msgid "Database reset has completed, Kodi will now restart to apply the changes." msgstr "" +msgctxt "#33089" +msgid "Enter folder name for backup" +msgstr "" + +msgctxt "#33090" +msgid "Replace existing backup?" +msgstr "" + +msgctxt "#33091" +msgid "Created backup at:" +msgstr "" + msgctxt "#33092" msgid "Create a backup" msgstr "" @@ -797,3 +809,7 @@ msgstr "" msgctxt "#33164" msgid "Mask sensitive information in log (does not apply to kodi logging)" msgstr "" + +msgctxt "#33165" +msgid "Failed to create backup" +msgstr "" diff --git a/resources/lib/database/__init__.py b/resources/lib/database/__init__.py index 4c0f6698..ab8ad547 100644 --- a/resources/lib/database/__init__.py +++ b/resources/lib/database/__init__.py @@ -159,6 +159,7 @@ def reset(): if xbmcvfs.exists(os.path.join(addon_data, "sync.json")): xbmcvfs.delete(os.path.join(addon_data, "sync.json")) + settings('enableMusic.bool', False) settings('MinimumSetup.bool', False) settings('MusicRescan.bool', False) settings('SyncInstallRunDone.bool', False) diff --git a/resources/lib/entrypoint/default.py b/resources/lib/entrypoint/default.py index 2dfed4e8..4e5ed16b 100644 --- a/resources/lib/entrypoint/default.py +++ b/resources/lib/entrypoint/default.py @@ -115,6 +115,8 @@ class Events(object): event('UpdateServer') elif mode == 'thememedia': get_themes() + elif mode == 'backup': + backup() else: listing() @@ -143,13 +145,13 @@ def listing(): context = [] if view_id and node in ('movies', 'tvshows', 'musicvideos', 'music') and view_id not in whitelist: - context.append((_(33123), "RunPlugin(plugin://plugin.video.emby?mode=synclib&id=%s)" % view_id)) + context.append((_(33123), "RunPlugin(plugin://plugin.video.emby/?mode=synclib&id=%s)" % view_id)) if view_id and node in ('movies', 'tvshows', 'musicvideos', 'music') and view_id in whitelist: - context.append((_(33136), "RunPlugin(plugin://plugin.video.emby?mode=synclib&id=%s)" % view_id)) - context.append((_(33132), "RunPlugin(plugin://plugin.video.emby?mode=repairlib&id=%s)" % view_id)) - context.append((_(33133), "RunPlugin(plugin://plugin.video.emby?mode=removelib&id=%s)" % view_id)) + context.append((_(33136), "RunPlugin(plugin://plugin.video.emby/?mode=synclib&id=%s)" % view_id)) + context.append((_(33132), "RunPlugin(plugin://plugin.video.emby/?mode=repairlib&id=%s)" % view_id)) + context.append((_(33133), "RunPlugin(plugin://plugin.video.emby/?mode=removelib&id=%s)" % view_id)) LOG.debug("--[ listing/%s/%s ] %s", node, label, path) @@ -184,6 +186,10 @@ def listing(): directory(_(33140), "plugin://plugin.video.emby/?mode=repairlibs", False) directory(_(33060), "plugin://plugin.video.emby/?mode=thememedia", False) directory(_(33058), "plugin://plugin.video.emby/?mode=reset", False) + + if settings('backupPath'): + directory(_(33092), "plugin://plugin.video.emby/?mode=backup", False) + directory(_(33163), None, False, artwork="special://home/addons/plugin.video.emby/donations.png") xbmcplugin.setContent(int(sys.argv[1]), 'files') @@ -275,12 +281,12 @@ def browse(media, view_id=None, folder=None, server_id=None): context = [] if item['Type'] in ('Series', 'Season', 'Playlist'): - context.append(("Play", "RunPlugin(plugin://plugin.video.emby?mode=playlist&id=%s&server=%s)" % (item['Id'], server_id))) + context.append(("Play", "RunPlugin(plugin://plugin.video.emby/?mode=playlist&id=%s&server=%s)" % (item['Id'], server_id))) if item['UserData']['Played']: - context.append((_(16104), "RunPlugin(plugin://plugin.video.emby?mode=unwatched&id=%s&server=%s)" % (item['Id'], server_id))) + context.append((_(16104), "RunPlugin(plugin://plugin.video.emby/?mode=unwatched&id=%s&server=%s)" % (item['Id'], server_id))) else: - context.append((_(16103), "RunPlugin(plugin://plugin.video.emby?mode=watched&id=%s&server=%s)" % (item['Id'], server_id))) + context.append((_(16103), "RunPlugin(plugin://plugin.video.emby/?mode=watched&id=%s&server=%s)" % (item['Id'], server_id))) li.addContextMenuItems(context) list_li.append((path, li, True)) @@ -293,12 +299,12 @@ def browse(media, view_id=None, folder=None, server_id=None): } path = "%s?%s" % ("plugin://plugin.video.emby", urllib.urlencode(params)) li.setProperty('path', path) - context = [(_(13412), "RunPlugin(plugin://plugin.video.emby?mode=playlist&id=%s&server=%s)" % (item['Id'], server_id))] + context = [(_(13412), "RunPlugin(plugin://plugin.video.emby/?mode=playlist&id=%s&server=%s)" % (item['Id'], server_id))] if item['UserData']['Played']: - context.append((_(16104), "RunPlugin(plugin://plugin.video.emby?mode=unwatched&id=%s&server=%s)" % (item['Id'], server_id))) + context.append((_(16104), "RunPlugin(plugin://plugin.video.emby/?mode=unwatched&id=%s&server=%s)" % (item['Id'], server_id))) else: - context.append((_(16103), "RunPlugin(plugin://plugin.video.emby?mode=watched&id=%s&server=%s)" % (item['Id'], server_id))) + context.append((_(16103), "RunPlugin(plugin://plugin.video.emby/?mode=watched&id=%s&server=%s)" % (item['Id'], server_id))) li.addContextMenuItems(context) @@ -688,3 +694,59 @@ def delete_item(): import context context.Context(delete=True) + +def backup(): + + ''' Emby backup. + ''' + from helper.utils import delete_folder, copytree + + path = settings('backupPath') + folder_name = "Kodi%s.%s" % (xbmc.getInfoLabel('System.BuildVersion')[:2], xbmc.getInfoLabel('System.Date(dd-mm-yy)')) + folder_name = dialog("input", heading=_(33089), defaultt=folder_name) + + if not folder_name: + return + + backup = os.path.join(path, folder_name) + + if xbmcvfs.exists(backup + '/'): + if not dialog("yesno", heading="{emby}", line1=_(33090)): + + return backup() + + delete_folder(backup) + + addon_data = xbmc.translatePath("special://profile/addon_data/plugin.video.emby").decode('utf-8') + destination_data = os.path.join(backup, "addon_data", "plugin.video.emby") + destination_databases = os.path.join(backup, "Database") + + if not xbmcvfs.mkdirs(path) or not xbmcvfs.mkdirs(destination_databases): + + LOG.info("Unable to create all directories") + dialog("notification", heading="{emby}", icon="{emby}", message=_(33165), sound=False) + + return + + copytree(addon_data, destination_data) + + databases = Objects().objects + + db = xbmc.translatePath(databases['emby']).decode('utf-8') + xbmcvfs.copy(db, os.path.join(destination_databases, db.rsplit('\\', 1)[1])) + LOG.info("copied emby.db") + + db = xbmc.translatePath(databases['video']).decode('utf-8') + filename = db.rsplit('\\', 1)[1] + xbmcvfs.copy(db, os.path.join(destination_databases, filename)) + LOG.info("copied %s", filename) + + if settings('enableMusic.bool'): + + db = xbmc.translatePath(databases['music']).decode('utf-8') + filename = db.rsplit('\\', 1)[1] + xbmcvfs.copy(db, os.path.join(destination_databases, filename)) + LOG.info("copied %s", filename) + + LOG.info("backup completed") + dialog("ok", heading="{emby}", line1="%s %s" % (_(33091), backup)) diff --git a/resources/lib/helper/utils.py b/resources/lib/helper/utils.py index 775cf19e..c1e44914 100644 --- a/resources/lib/helper/utils.py +++ b/resources/lib/helper/utils.py @@ -289,7 +289,7 @@ def delete_recursive(path, dirs): def unzip(path, dest, folder=None): - ''' Unzip file. zipfile module seems to fail on android. + ''' Unzip file. zipfile module seems to fail on android with badziperror. ''' path = urllib.quote_plus(path) root = "zip://" + path + '/' @@ -345,6 +345,43 @@ def get_zip_directory(path, folder): if result: return result +def copytree(path, dest): + + ''' Copy folder content from one to another. + ''' + dirs, files = xbmcvfs.listdir(path) + + if dirs: + copy_recursive(path, dirs, dest) + + for file in files: + copy_file(os.path.join(path, file.decode('utf-8')), os.path.join(dest, file.decode('utf-8'))) + + LOG.info("Copied %s", path) + +def copy_recursive(path, dirs, dest): + + for directory in dirs: + + dirs_dir = os.path.join(path, directory.decode('utf-8')) + dest_dir = os.path.join(dest, directory.decode('utf-8')) + xbmcvfs.mkdir(dest_dir) + + dirs2, files = xbmcvfs.listdir(dirs_dir) + + if dirs2: + copy_recursive(dirs_dir, dirs2, dest_dir) + + for file in files: + copy_file(os.path.join(dirs_dir, file.decode('utf-8')), os.path.join(dest_dir, file.decode('utf-8'))) + +def copy_file(path, dest): + + ''' Copy specific file. + ''' + xbmcvfs.copy(path, dest) + LOG.debug("copy: %s to %s", path, dest) + def normalize_string(text): ''' For theme media, do not modify unless