16 Commits

Author SHA1 Message Date
Rob Weber
a4bb3f3feb completely changed this class, default no longer matters. Just restore settings that differ from current, ignore default flags closes #154 2019-09-11 10:02:45 -05:00
Rob Weber
1f6324b2d5 use path separators for os 2019-09-11 08:58:21 -05:00
Rob Weber
12b25f7cea version bump 2019-09-10 15:38:34 -05:00
Rob
5d9d8a1820 Guisettings fix (#156)
fix guisettings restore issues - thanks Bluerayx
2019-09-10 15:36:52 -05:00
Rob Weber
b34e538d6b probot not adhering to onlyLabels at the moment 2019-08-27 10:41:19 -05:00
Rob Weber
b5a7aada4c added probot to help with stale issues 2019-08-27 09:53:25 -05:00
Rob Weber
1a9c43b998 Merge branch 'master' of https://github.com/robweber/xbmcbackup 2019-08-27 09:42:23 -05:00
Rob Weber
b7f4b14fe2 delmit with comma, not ampersand (xml formatting) 2019-08-27 09:42:07 -05:00
Rob Weber
787b054bba Merge branch 'master' of https://github.com/robweber/xbmcbackup 2019-08-27 09:08:31 -05:00
Rob Weber
a7be48a341 forgot to add screenshots back in 2019-08-27 09:08:05 -05:00
Rob Weber
2fe76b7b52 Merge branch 'master' of https://github.com/robweber/xbmcbackup 2019-08-27 09:06:02 -05:00
Rob Weber
3aed105fd7 lowercase filename ext 2019-08-27 09:05:23 -05:00
Rob Weber
c9b4554eac allow folder/id mistmatch for this addon 2019-08-27 09:02:21 -05:00
Rob Weber
e736b964a5 added build status badge 2019-08-27 09:01:19 -05:00
Rob Weber
4c5f6774df added travis testing script (kodi addon checker) 2019-08-27 08:45:45 -05:00
Rob Weber
1f2e315208 updated screenshots 2019-08-27 08:30:26 -05:00
15 changed files with 92 additions and 82 deletions

18
.github/stale-dontuse.yml vendored Normal file
View File

@@ -0,0 +1,18 @@
# Configuration for probot-stale - https://github.com/probot/stale
# Number of days of inactivity before an Issue or Pull Request becomes stale
daysUntilStale: 31
# Number of days of inactivity before a stale Issue or Pull Request is closed
daysUntilClose: 14
# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled)
onlyLabels:
- waiting for info
- wontfix
# Label to use when marking as stale
staleLabel: inactive
# Comment to post when marking as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as inactive because it has not had
recent activity. It will be closed if no further activity occurs.

13
.travis.yml Normal file
View File

@@ -0,0 +1,13 @@
dist: xenial
language: python
python: 3.7
install:
- pip install kodi-addon-checker
before_script:
- git config core.quotepath false
# command to run our tests
script:
- kodi-addon-checker --branch=krypton --allow-folder-id-mismatch

View File

@@ -1,4 +1,5 @@
# Backup Addon # Backup Addon
[![Build Status](https://travis-ci.org/robweber/xbmcbackup.svg?branch=master)](https://travis-ci.org/robweber/xbmcbackup)
__Kodi Version Compatibility:__ Kodi 17.x (Krypton) and greater __Kodi Version Compatibility:__ Kodi 17.x (Krypton) and greater

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="script.xbmcbackup" <addon id="script.xbmcbackup"
name="Backup" version="1.5.0" provider-name="robweber"> name="Backup" version="1.5.1" provider-name="robweber">
<requires> <requires>
<!-- jarvis --> <!-- jarvis -->
<import addon="xbmc.python" version="2.25.0"/> <import addon="xbmc.python" version="2.25.0"/>
@@ -95,10 +95,8 @@
<screenshot>resources/images/screenshot3.png</screenshot> <screenshot>resources/images/screenshot3.png</screenshot>
<screenshot>resources/images/screenshot4.png</screenshot> <screenshot>resources/images/screenshot4.png</screenshot>
</assets> </assets>
<news>Version 1.1.4 <news>Version 1.5.1
- added file chunk support for dropbox uploads - fix guisettings restores not working - thanks Bluerayx
- fixed settings duplicate ids, thanks aster-anto
- added scheduler delay to assist with time sync (rpi mostly)
</news> </news>
</extension> </extension>
</addon> </addon>

View File

@@ -1,3 +1,7 @@
Version 1.5.1
fix guisettings restores not working - thanks Bluerayx
Version 1.5.0 Version 1.5.0
Overhaul of file selection and restore procedures. Breaking Change with previous versions PR117 Overhaul of file selection and restore procedures. Breaking Change with previous versions PR117

View File

Before

Width:  |  Height:  |  Size: 125 KiB

After

Width:  |  Height:  |  Size: 125 KiB

View File

Before

Width:  |  Height:  |  Size: 129 KiB

After

Width:  |  Height:  |  Size: 129 KiB

View File

Before

Width:  |  Height:  |  Size: 270 KiB

After

Width:  |  Height:  |  Size: 270 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

View File

@@ -4,12 +4,12 @@ import xbmcvfs
import utils as utils import utils as utils
import time import time
import json import json
import os
from datetime import datetime from datetime import datetime
from vfs import XBMCFileSystem,DropboxFileSystem,ZipFileSystem,GoogleDriveFilesystem from vfs import XBMCFileSystem,DropboxFileSystem,ZipFileSystem,GoogleDriveFilesystem
from progressbar import BackupProgressBar from progressbar import BackupProgressBar
from resources.lib.guisettings import GuiSettingsManager from resources.lib.guisettings import GuiSettingsManager
from resources.lib.extractor import ZipExtractor from resources.lib.extractor import ZipExtractor
from __builtin__ import file
def folderSort(aKey): def folderSort(aKey):
result = aKey[0] result = aKey[0]
@@ -321,10 +321,10 @@ class XbmcBackup:
self.xbmc_vfs.rmfile(xbmc.translatePath("special://temp/" + self.restore_point)) self.xbmc_vfs.rmfile(xbmc.translatePath("special://temp/" + self.restore_point))
self.xbmc_vfs.rmdir(self.remote_vfs.root_path) 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) #update the guisettings information (or what we can from it)
gui_settings = GuiSettingsManager('special://home/userdata/guisettings.xml') gui_settings = GuiSettingsManager()
gui_settings.run() gui_settings.run()
#call update addons to refresh everything #call update addons to refresh everything
xbmc.executebuiltin('UpdateLocalAddons') xbmc.executebuiltin('UpdateLocalAddons')
@@ -398,13 +398,15 @@ class XbmcBackup:
dest.mkdir(dest.root_path + aFile[len(source.root_path) + 1:]) dest.mkdir(dest.root_path + aFile[len(source.root_path) + 1:])
else: else:
self._updateProgress() self._updateProgress()
wroteFile = True wroteFile = True
destFile = dest.root_path + aFile[len(source.root_path):]
if(isinstance(source,DropboxFileSystem) or isinstance(source,GoogleDriveFilesystem)): if(isinstance(source,DropboxFileSystem) or isinstance(source,GoogleDriveFilesystem)):
#if copying from cloud storage we need the file handle, use get_file #if copying from cloud storage we need the file handle, use get_file
wroteFile = source.get_file(aFile,dest.root_path + aFile[len(source.root_path):]) wroteFile = source.get_file(aFile,destFile)
else: else:
#copy using normal method #copy using normal method
wroteFile = dest.put(aFile,dest.root_path + aFile[len(source.root_path):]) wroteFile = dest.put(aFile,destFile)
#if result is still true but this file failed #if result is still true but this file failed
if(not wroteFile and result): if(not wroteFile and result):
@@ -555,13 +557,13 @@ class FileManager:
if(directory[-1:] == '/' or directory[-1:] == '\\'): if(directory[-1:] == '/' or directory[-1:] == '\\'):
directory = directory[:-1] directory = directory[:-1]
if(self.vfs.exists(directory + "/")): if(self.vfs.exists(directory + os.path.sep)):
dirs,files = self.vfs.listdir(directory) dirs,files = self.vfs.listdir(directory)
if(recurse): if(recurse):
#create all the subdirs first #create all the subdirs first
for aDir in dirs: for aDir in dirs:
dirPath = xbmc.validatePath(xbmc.translatePath(directory + "/" + aDir)) dirPath = xbmc.validatePath(xbmc.translatePath(directory + os.path.sep + aDir))
file_ext = aDir.split('.')[-1] file_ext = aDir.split('.')[-1]
#check if directory is excluded #check if directory is excluded
@@ -581,7 +583,7 @@ class FileManager:
#copy all the files #copy all the files
for aFile in files: for aFile in files:
filePath = xbmc.translatePath(directory + "/" + aFile) filePath = xbmc.translatePath(directory + os.path.sep + aFile)
self.addFile(filePath) self.addFile(filePath)
def addDir(self,dirMeta): def addDir(self,dirMeta):

View File

@@ -6,94 +6,68 @@ import xbmc,xbmcvfs
class GuiSettingsManager: class GuiSettingsManager:
settingsFile = None
doc = None doc = None
settings_allowed = list()
found_settings = list()
def __init__(self,settingsFile): def __init__(self):
self._readFile(xbmc.translatePath(settingsFile)) #first make a copy of the file
xbmcvfs.copy(xbmc.translatePath('special://home/userdata/guisettings.xml'), xbmc.translatePath("special://home/userdata/guisettings.xml.restored"))
#read in the copy
self._readFile(xbmc.translatePath('special://home/userdata/guisettings.xml.restored'))
def run(self): def run(self):
#get a list of all the settings we can manipulate via json #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"}}')) json_response = json.loads(xbmc.executeJSONRPC('{"jsonrpc":"2.0", "id":1, "method":"Settings.GetSettings","params":{"level":"advanced"}}'))
settings = json_response['result']['settings'] settings = json_response['result']['settings']
currentSettings = {}
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: for aSetting in settings:
secondary_list = self.__parseNodes(self.doc.getElementsByTagName(aNode.name)[0]) if('value' in aSetting):
currentSettings[aSetting['id']] = aSetting['value']
for secondNode in secondary_list: #parse the existing xml file and get all the settings we need to restore
#if the node does not have children and is not default restoreSettings = self.__parseNodes(self.doc.getElementsByTagName('setting'))
if(not secondNode.hasChildren and not secondNode.isDefault):
#get a list where the restore setting value != the current value
if(secondNode.json_name() in self.settings_allowed): updateSettings = {k: v for k, v in restoreSettings.items() if (k in currentSettings and currentSettings[k] != v)}
self.found_settings.append(secondNode)
#go through all the found settings and update them #go through all the found settings and update them
for aSetting in self.found_settings: jsonObj = {"jsonrpc":"2.0","id":1,"method":"Settings.SetSettingValue","params":{"setting":"","value":""}}
utils.log("updating: " + aSetting.json_name() + ", value: " + aSetting.value) for anId, aValue in updateSettings.items():
utils.log("updating: " + anId + ", value: " + str(aValue))
jsonObj['params']['setting'] = anId
jsonObj['params']['value'] = aValue
#check for boolean and numeric values xbmc.executeJSONRPC(json.dumps(jsonObj))
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":"' + utils.encode(aSetting.value) + '"}}')
#make a copy of the guisettings file to make user based restores easier
xbmcvfs.copy(self.settingsFile, xbmc.translatePath("special://home/userdata/guisettings.xml.restored"))
def __parseNodes(self,nodeList): def __parseNodes(self,nodeList):
result = [] result = {}
for node in nodeList.childNodes: for node in nodeList:
if(node.nodeType == self.doc.ELEMENT_NODE): nodeValue = ''
aSetting = SettingNode(node.nodeName) if(node.firstChild != None):
nodeValue = node.firstChild.nodeValue
#detect if there are any element nodes
if(len(node.childNodes) > 0): #check for numbers and booleans
for child_node in node.childNodes: if(nodeValue.isdigit()):
if(child_node.nodeType == self.doc.ELEMENT_NODE): nodeValue = int(nodeValue)
aSetting.hasChildren = True elif(nodeValue == 'true'):
nodeValue = True
if(not aSetting.hasChildren and len(node.childNodes) > 0): elif(nodeValue == 'false'):
aSetting.value = node.firstChild.nodeValue nodeValue = False
if('default' not in node.attributes.keys()): result[node.getAttribute('id')] = nodeValue
aSetting.isDefault = False
aSetting.parent = node.parentNode.nodeName
result.append(aSetting)
return result return result
def _readFile(self,fileLoc): def _readFile(self,fileLoc):
if(xbmcvfs.exists(fileLoc)): if(xbmcvfs.exists(fileLoc)):
try: try:
self.doc = minidom.parse(fileLoc) self.doc = minidom.parse(fileLoc)
self.settingsFile = fileLoc
except ExpatError: except ExpatError:
utils.log("Can't read " + fileLoc) 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

View File

@@ -14,8 +14,8 @@
<setting id="dropbox_secret" type="text" label="30029" visible="eq(-4,2)" default="" /> <setting id="dropbox_secret" type="text" label="30029" visible="eq(-4,2)" default="" />
<setting id="google_drive_id" type="text" label="Client ID" visible="eq(-5,3)" default="" /> <setting id="google_drive_id" type="text" label="Client ID" visible="eq(-5,3)" default="" />
<setting id="google_drive_secret" type="text" label="Client Secret" visible="eq(-6,3)" default="" /> <setting id="google_drive_secret" type="text" label="Client Secret" visible="eq(-6,3)" default="" />
<setting id="auth_dropbox_button" type="action" label="30104" action="RunScript(special://home/addons/script.xbmcbackup/launcher.py,action=authorize_cloud&provider=dropbox)" visible="eq(-7,2)"/> <setting id="auth_dropbox_button" type="action" label="30104" action="RunScript(special://home/addons/script.xbmcbackup/launcher.py,action=authorize_cloud,provider=dropbox)" visible="eq(-7,2)"/>
<setting id="auth_google_button" type="action" label="30104" action="RunScript(special://home/addons/script.xbmcbackup/launcher.py,action=authorize_cloud&provider=google_drive)" visible="eq(-8,3)"/> <setting id="auth_google_button" type="action" label="30104" action="RunScript(special://home/addons/script.xbmcbackup/launcher.py,action=authorize_cloud,provider=google_drive)" visible="eq(-8,3)"/>
<setting id="remove_auth_button" type="action" label="30093" action="RunScript(special://home/addons/script.xbmcbackup/launcher.py,action=remove_auth)" visible="gt(-9,1)"/> <setting id="remove_auth_button" type="action" label="30093" action="RunScript(special://home/addons/script.xbmcbackup/launcher.py,action=remove_auth)" visible="gt(-9,1)"/>
</category> </category>
<category id="selection" label="30012"> <category id="selection" label="30012">