Compare commits
16 Commits
krypton-1.
...
krypton-1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a4bb3f3feb | ||
|
|
1f6324b2d5 | ||
|
|
12b25f7cea | ||
|
|
5d9d8a1820 | ||
|
|
b34e538d6b | ||
|
|
b5a7aada4c | ||
|
|
1a9c43b998 | ||
|
|
b7f4b14fe2 | ||
|
|
787b054bba | ||
|
|
a7be48a341 | ||
|
|
2fe76b7b52 | ||
|
|
3aed105fd7 | ||
|
|
c9b4554eac | ||
|
|
e736b964a5 | ||
|
|
4c5f6774df | ||
|
|
1f2e315208 |
18
.github/stale-dontuse.yml
vendored
Normal 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
@@ -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
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
# Backup Addon
|
# Backup Addon
|
||||||
|
[](https://travis-ci.org/robweber/xbmcbackup)
|
||||||
|
|
||||||
__Kodi Version Compatibility:__ Kodi 17.x (Krypton) and greater
|
__Kodi Version Compatibility:__ Kodi 17.x (Krypton) and greater
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 125 KiB After Width: | Height: | Size: 125 KiB |
|
Before Width: | Height: | Size: 129 KiB After Width: | Height: | Size: 129 KiB |
|
Before Width: | Height: | Size: 270 KiB After Width: | Height: | Size: 270 KiB |
|
Before Width: | Height: | Size: 188 KiB |
BIN
resources/images/screenshot4.png
Normal file
|
After Width: | Height: | Size: 150 KiB |
BIN
resources/images/screenshot5.png
Normal file
|
After Width: | Height: | Size: 102 KiB |
BIN
resources/images/screenshot6.png
Normal file
|
After Width: | Height: | Size: 76 KiB |
@@ -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):
|
||||||
|
|||||||
@@ -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
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||