From 9e5873fcb7bec043dd1dceef23e643a8bb61495b Mon Sep 17 00:00:00 2001 From: Rob Weber Date: Fri, 31 Oct 2014 09:50:37 -0500 Subject: [PATCH 1/5] check if destination is writeable --- resources/language/English/strings.xml | 3 +++ resources/lib/backup.py | 33 ++++++++++++++++++++++---- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/resources/language/English/strings.xml b/resources/language/English/strings.xml index 7ba8a6b..a0d22f8 100644 --- a/resources/language/English/strings.xml +++ b/resources/language/English/strings.xml @@ -80,4 +80,7 @@ This version of XBMC is different than the one used to create the archive Compress Archives Copying Zip Archive + Write Error Detected + The destination may not be writeable + Zip archive could not be copied diff --git a/resources/lib/backup.py b/resources/lib/backup.py index 51f437b..3cba560 100644 --- a/resources/lib/backup.py +++ b/resources/lib/backup.py @@ -153,7 +153,14 @@ class XbmcBackup: self.remote_vfs.mkdir(self.remote_vfs.root_path) #create a validation file for backup rotation - self._createValidationFile() + writeCheck = self._createValidationFile() + + if(not writeCheck): + #we may not be able to write to this destination for some reason + shouldContinue = xbmcgui.Dialog().yesno(utils.getString(30089),utils.getString(30090), utils.getString(30044),autoclose=25000) + + if(not shouldContinue): + return utils.log(utils.getString(30051)) allFiles = [] @@ -255,8 +262,12 @@ class XbmcBackup: self.remote_vfs = self.saved_remote_vfs self.progressBar.updateProgress(98, utils.getString(30088)) - self.backupFiles(fileManager.getFiles(),self.xbmc_vfs, self.remote_vfs) + fileCopied = self.backupFiles(fileManager.getFiles(),self.xbmc_vfs, self.remote_vfs) + if(not fileCopied): + #zip archive copy filed, inform the user + shouldContinue = xbmcgui.Dialog().ok(utils.getString(30089),utils.getString(30090), utils.getString(30091)) + #delete the temp zip file self.xbmc_vfs.rmfile(xbmc.translatePath("special://temp/" + zip_name)) @@ -419,6 +430,8 @@ class XbmcBackup: window.setProperty(utils.__addon_id__ + ".running","") def backupFiles(self,fileList,source,dest): + result = True + utils.log("Writing files to: " + dest.root_path) utils.log("Source: " + source.root_path) for aFile in fileList: @@ -429,12 +442,20 @@ class XbmcBackup: dest.mkdir(dest.root_path + aFile[len(source.root_path) + 1:]) else: self._updateProgress() + wroteFile = True if(isinstance(source,DropboxFileSystem)): #if copying from dropbox we need the file handle, use get_file - source.get_file(aFile,dest.root_path + aFile[len(source.root_path):]) + wroteFile = source.get_file(aFile,dest.root_path + aFile[len(source.root_path):]) else: #copy using normal method - dest.put(aFile,dest.root_path + aFile[len(source.root_path):]) + wroteFile = dest.put(aFile,dest.root_path + aFile[len(source.root_path):]) + + #if result is still true but this file failed + if(not wroteFile and result): + result = False + + + return result def _createCRC(self,string): #create hash from string @@ -486,7 +507,9 @@ class XbmcBackup: vFile.write("") vFile.close() - self.remote_vfs.put(xbmc.translatePath(utils.data_dir() + "xbmcbackup.val"),self.remote_vfs.root_path + "xbmcbackup.val") + success = self.remote_vfs.put(xbmc.translatePath(utils.data_dir() + "xbmcbackup.val"),self.remote_vfs.root_path + "xbmcbackup.val") + + return success def _checkValidationFile(self,path): result = False From f41f37782d4c50c7ccad5076a69a0bab631bdc38 Mon Sep 17 00:00:00 2001 From: Rob Weber Date: Fri, 31 Oct 2014 09:51:44 -0500 Subject: [PATCH 2/5] version bump --- addon.xml | 2 +- changelog.txt | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/addon.xml b/addon.xml index d22fffa..a075078 100644 --- a/addon.xml +++ b/addon.xml @@ -1,6 +1,6 @@  + name="XBMC Backup" version="0.5.8.6" provider-name="robweber"> diff --git a/changelog.txt b/changelog.txt index 3c1992b..f899b7e 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,7 @@ +Version 0.5.8.6 + +check if destination is writeable - thanks war59312 + Version 0.5.8.5 added custom library nodes to config backup options - thanks Ned Scott From 2fc26b6e8c76f863b386a48f600d79350e626548 Mon Sep 17 00:00:00 2001 From: Rob Weber Date: Fri, 31 Oct 2014 09:56:02 -0500 Subject: [PATCH 3/5] show notification if files failed to copy --- changelog.txt | 1 + resources/language/English/strings.xml | 1 + resources/lib/backup.py | 6 +++++- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/changelog.txt b/changelog.txt index f899b7e..1777801 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,6 @@ Version 0.5.8.6 +show notification if some files failed check if destination is writeable - thanks war59312 Version 0.5.8.5 diff --git a/resources/language/English/strings.xml b/resources/language/English/strings.xml index a0d22f8..f0b39ae 100644 --- a/resources/language/English/strings.xml +++ b/resources/language/English/strings.xml @@ -83,4 +83,5 @@ Write Error Detected The destination may not be writeable Zip archive could not be copied + Not all files were copied diff --git a/resources/lib/backup.py b/resources/lib/backup.py index 3cba560..dce1270 100644 --- a/resources/lib/backup.py +++ b/resources/lib/backup.py @@ -244,7 +244,11 @@ class XbmcBackup: for fileGroup in allFiles: self.xbmc_vfs.set_root(fileGroup['source']) self.remote_vfs.set_root(fileGroup['dest']) - self.backupFiles(fileGroup['files'],self.xbmc_vfs,self.remote_vfs) + filesCopied = self.backupFiles(fileGroup['files'],self.xbmc_vfs,self.remote_vfs) + + if(not filesCopied): + utils.showNotification(utils.getString(30092)) + utils.log(utils.getString(30092)) #reset remote and xbmc vfs self.xbmc_vfs.set_root("special://home/") From 0b6a3ae5062993539d28e9d15e0103c5d9a3d672 Mon Sep 17 00:00:00 2001 From: Rob Weber Date: Fri, 31 Oct 2014 11:40:47 -0500 Subject: [PATCH 4/5] restore xbmc allowed settings via json --- resources/lib/backup.py | 6 +++ resources/lib/guisettings.py | 96 ++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 resources/lib/guisettings.py diff --git a/resources/lib/backup.py b/resources/lib/backup.py index dce1270..7f65dc5 100644 --- a/resources/lib/backup.py +++ b/resources/lib/backup.py @@ -5,6 +5,7 @@ import utils as utils import time import json from vfs import XBMCFileSystem,DropboxFileSystem,ZipFileSystem +from resources.lib.guisettings import GuiSettingsManager def folderSort(aKey): result = aKey[0] @@ -423,6 +424,11 @@ class XbmcBackup: self.xbmc_vfs.rmfile(xbmc.translatePath("special://temp/" + self.restore_point)) self.xbmc_vfs.rmdir(self.remote_vfs.root_path) + if(utils.getSetting("backup_config") == "true"): + #update the guisettings information (or what we can from it) + gui_settings = GuiSettingsManager('special://home/userdata/guisettings.xml') + gui_settings.run() + #call update addons to refresh everything xbmc.executebuiltin('UpdateLocalAddons') diff --git a/resources/lib/guisettings.py b/resources/lib/guisettings.py new file mode 100644 index 0000000..de50b0e --- /dev/null +++ b/resources/lib/guisettings.py @@ -0,0 +1,96 @@ +import utils as utils +from xml.dom import minidom +from xml.parsers.expat import ExpatError +import json +import xbmc,xbmcvfs + + +class GuiSettingsManager: + doc = None + settings_allowed = list() + found_settings = list() + + def __init__(self,settingsFile): + self._readFile(xbmc.translatePath(settingsFile)) + + def run(self): + #get a list of all the settings we can manipulate via json + json_response = json.loads(xbmc.executeJSONRPC('{"jsonrpc":"2.0", "id":1, "method":"Settings.GetSettings","params":{"level":"advanced"}}')) + + settings = json_response['result']['settings'] + + for aSetting in settings: + self.settings_allowed.append(aSetting['id']) + + #parse the existing xml file and get all the settings + root_nodes = self.__parseNodes(self.doc.documentElement) + + for aNode in root_nodes: + secondary_list = self.__parseNodes(self.doc.getElementsByTagName(aNode.name)[0]) + + for secondNode in secondary_list: + #if the node does not have children and is not default + if(not secondNode.hasChildren and not secondNode.isDefault): + + if(secondNode.json_name() in self.settings_allowed): + self.found_settings.append(secondNode) + + #go through all the found settings and update them + for aSetting in self.found_settings: + utils.log("updating: " + aSetting.json_name() + ", value: " + aSetting.value) + + #check for boolean and numeric values + if(aSetting.value.isdigit() or (aSetting.value == 'true' or aSetting.value == 'false')): + xbmc.executeJSONRPC('{"jsonrpc":"2.0", "id":1, "method":"Settings.SetSettingValue","params":{"setting":"' + aSetting.json_name() + '","value":' + aSetting.value + '}}') + else: + xbmc.executeJSONRPC('{"jsonrpc":"2.0", "id":1, "method":"Settings.SetSettingValue","params":{"setting":"' + aSetting.json_name() + '","value":"' + aSetting.value + '"}}') + + def __parseNodes(self,nodeList): + result = [] + + for node in nodeList.childNodes: + if(node.nodeType == self.doc.ELEMENT_NODE): + aSetting = SettingNode(node.nodeName) + + #detect if there are any element nodes + if(len(node.childNodes) > 0): + for child_node in node.childNodes: + if(child_node.nodeType == self.doc.ELEMENT_NODE): + aSetting.hasChildren = True + + if(not aSetting.hasChildren and len(node.childNodes) > 0): + aSetting.value = node.firstChild.nodeValue + + if('default' not in node.attributes.keys()): + aSetting.isDefault = False + + aSetting.parent = node.parentNode.nodeName + + result.append(aSetting) + return result + + + def _readFile(self,fileLoc): + + if(xbmcvfs.exists(fileLoc)): + + try: + + self.doc = minidom.parse(fileLoc) + except ExpatError: + utils.log("Can't read " + fileLoc) + +class SettingNode: + name = '' + value = '' + hasChildren = False + isDefault = True + parent = '' + + def __init__(self,name): + self.name = name + + def json_name(self): + return self.parent + "." + self.name + + \ No newline at end of file From d6d7221551033d9e7974ce57e1292f0d332d16d7 Mon Sep 17 00:00:00 2001 From: Rob Weber Date: Fri, 31 Oct 2014 11:47:14 -0500 Subject: [PATCH 5/5] updated readme,changelog and version info --- README.txt | 13 ++++++++++++- addon.xml | 2 +- changelog.txt | 4 ++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/README.txt b/README.txt index ddfbb53..ece8adc 100644 --- a/README.txt +++ b/README.txt @@ -14,6 +14,17 @@ On the Backup Selection page you can select which items from your user profile f You can also define non-XBMC directories on your device. See "Custom Directories" for more information on how these are handled. +Restores: + +During the restore process there are a few checks and post-run procedures to know about. + +The first is a version check. If you are restoring to a different version of XBMC than the one used to create the backup archive you'll get a warning. In most cases it is OK to proceed, just know that some specific items like addons and database files may not work correctly. + +The next check is for an advancedsettings.xml file. If you've created this file and it exists in your restore archive you'll be asked to reboot XBMC. This is so that the file can be loaded and used for any special settings, mainly path substitutions, you may have had that would affect the rest of the restore. XBMC Backup will prompt you to continue the restore process when you reboot the program. + +The last bit of post-processing is done after all the backup files have been restored. If you have restored your configuration files the addon will attempt to restore any system specific settings that it can from the guisettings.xml file. This is done by comparing the restored file with settings via the JSONPRC Settings.SetSettingValue method. Only system specific settings can be restored so you will get any custom views or skin specific settings back. See the FAQ for how to restore these. + + Scheduling: You can schedule backups to be completed on a set interval via the scheduling area. When it is time for the backup to run it will be executed in the background. @@ -79,7 +90,7 @@ If you've created restore points with an older version of the addon (pre 0.3.6) Several settings aren't being restored, this includes views, weather, etc. How do I get these back? -GUISETTINGS.xml is a configuration file used heavily by XBMC for remembering GUI specific settings. Due to the fact that XBMC reads this file on startup, and writes from memory to this file on shutdown; it is not possible to restore this file while XBMC is running. You must manually move this file from your backup archives if you wish to restore it. User SouthMark has posted the following steps for restoring in the OpenELEC system where this is more difficult: +GUISETTINGS.xml is a configuration file used heavily by XBMC for remembering GUI specific settings. Due to the fact that XBMC reads this file on startup, and writes from memory to this file on shutdown; it is not possible to restore this file while XBMC is running. This addon attempts to restore what settings it can via the JSONRPC interface, however you will still most likely be missing your specific skin settings and view settings. To get these back you must manually move this file from your backup archives if you wish to restore it. User SouthMark has posted the following steps for restoring in the OpenELEC system where this is more difficult: 1. Run the restore of your backup 2. SSH using putty to the IP Address of your media centre username: root Password openelec diff --git a/addon.xml b/addon.xml index a075078..8940775 100644 --- a/addon.xml +++ b/addon.xml @@ -1,6 +1,6 @@  + name="XBMC Backup" version="0.5.8.7" provider-name="robweber"> diff --git a/changelog.txt b/changelog.txt index 1777801..2a0a54f 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,7 @@ +Version 0.5.8.7 + +allow limited updating of guisettings file through json + Version 0.5.8.6 show notification if some files failed