mirror of
https://github.com/robweber/xbmcbackup.git
synced 2026-01-07 00:04:44 +01:00
Compare commits
18 Commits
matrix-1.6
...
matrix-1.6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d7e4946d9a | ||
|
|
6fdaa4f253 | ||
|
|
5f1f9fef38 | ||
|
|
4098cd18cb | ||
|
|
8119a09449 | ||
|
|
a0ccd85d9e | ||
|
|
71c8d9ae54 | ||
|
|
b470412b4f | ||
|
|
7a5886cd26 | ||
|
|
f20887b6e7 | ||
|
|
a0eb28a5f6 | ||
|
|
a198c9448a | ||
|
|
fa3a30eb55 | ||
|
|
201d04afeb | ||
|
|
2dabb23c2d | ||
|
|
2f19ec2b75 | ||
|
|
db215873cf | ||
|
|
bd963719d4 |
@@ -3,7 +3,7 @@ language: python
|
|||||||
python: 3.7
|
python: 3.7
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- pip install flake8 kodi-addon-checker git+https://github.com/romanvm/kodi-addon-submitter.git
|
- pip install flake8 flake8_polyfill kodi-addon-checker git+https://github.com/romanvm/kodi-addon-submitter.git
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
- git config core.quotepath false
|
- git config core.quotepath false
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Backup Addon
|
# Backup Addon
|
||||||
 [](https://travis-ci.org/robweber/xbmcbackup) [](https://github.com/robweber/xbmcbackup/blob/master/LICENSE.txt) [](https://www.python.org/dev/peps/pep-0008/)
|
  [](https://travis-ci.com/robweber/xbmcbackup) [](https://github.com/robweber/xbmcbackup/blob/master/LICENSE.txt) [](https://www.python.org/dev/peps/pep-0008/)
|
||||||
|
|
||||||
## About
|
## About
|
||||||
|
|
||||||
|
|||||||
25
addon.xml
25
addon.xml
@@ -1,15 +1,15 @@
|
|||||||
<?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.6.3" provider-name="robweber">
|
name="Backup" version="1.6.4" provider-name="robweber">
|
||||||
<requires>
|
<requires>
|
||||||
<import addon="xbmc.python" version="3.0.0"/>
|
<import addon="xbmc.python" version="3.0.0"/>
|
||||||
<import addon="script.module.dateutil" version="2.8.0" />
|
<import addon="script.module.dateutil" version="2.8.0" />
|
||||||
<import addon="script.module.future" version="0.16.0.4"/>
|
<import addon="script.module.future" version="0.18.2+matrix.1" />
|
||||||
<import addon="script.module.dropbox" version="9.4.0"/>
|
<import addon="script.module.dropbox" version="9.4.0" />
|
||||||
</requires>
|
</requires>
|
||||||
<extension point="xbmc.python.script" library="default.py">
|
<extension point="xbmc.python.script" library="default.py">
|
||||||
<provides>executable</provides>
|
<provides>executable</provides>
|
||||||
</extension>
|
</extension>
|
||||||
<extension point="xbmc.service" library="service.py" />
|
<extension point="xbmc.service" library="service.py" />
|
||||||
<extension point="xbmc.addon.metadata">
|
<extension point="xbmc.addon.metadata">
|
||||||
<summary lang="ar_SA">إنسخ إحتياطياً قاعده بيانات إكس بى إم سى وملفات اﻹعدادات فى حاله وقوع إنهيار مع إمكانيه اﻹسترجاع</summary>
|
<summary lang="ar_SA">إنسخ إحتياطياً قاعده بيانات إكس بى إم سى وملفات اﻹعدادات فى حاله وقوع إنهيار مع إمكانيه اﻹسترجاع</summary>
|
||||||
@@ -89,14 +89,11 @@
|
|||||||
<screenshot>resources/images/screenshot3.jpg</screenshot>
|
<screenshot>resources/images/screenshot3.jpg</screenshot>
|
||||||
<screenshot>resources/images/screenshot4.jpg</screenshot>
|
<screenshot>resources/images/screenshot4.jpg</screenshot>
|
||||||
</assets>
|
</assets>
|
||||||
<news>Version 1.6.3
|
<news>Version 1.6.4
|
||||||
- fixed validatePath error (issue #166)
|
- updated deprecated Kodi python methods
|
||||||
Version 1.6.2
|
- added better system settings/restore functionality (enabled by default)
|
||||||
- replaced PNG screenshots with JPG
|
- fixed Dropbox oauth import
|
||||||
Version 1.6.1
|
- fixed xbmcgui.Dialog().ok() parameter list
|
||||||
- added file transfer size to progress bar
|
|
||||||
- progress bar now based on transfer size, not total file count
|
|
||||||
- fixed rotate backups error - thanks @AnonTester
|
|
||||||
</news>
|
</news>
|
||||||
</extension>
|
</extension>
|
||||||
</addon>
|
</addon>
|
||||||
|
|||||||
48
changelog.md
48
changelog.md
@@ -4,6 +4,28 @@ All notable changes to this project will be documented in this file.
|
|||||||
|
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
||||||
|
|
||||||
|
## [Version 1.6.4](https://github.com/robweber/xbmcbackup/compare/matrix-1.6.3...robweber:matrix-1.6.4) - 2020-12-23
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- merged duplicate copy code into ```_copyFile``` method
|
||||||
|
- added method to backup/restore Kodi settings via the GetSettings/SetSettingValue JSON methods in the validation file
|
||||||
|
- added setting to always restore settings or prompt at the time of backup
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- updated script.module.future version to current
|
||||||
|
- swapped xbmc.translatePath for xbmcvfs.translatePath, deprecated
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- fixed calls to ```xbmcgui.Dialog().ok()```, method definition changed to only allow one message arg with Kodi 19
|
||||||
|
- fixed import of dropbox Oauth package in authorizer flow
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
- removed old xml GuiSettings parsing for settings restore
|
||||||
|
|
||||||
## [Version 1.6.3](https://github.com/robweber/xbmcbackup/compare/matrix-1.6.2...robweber:matrix-1.6.3) - 2020-06-15
|
## [Version 1.6.3](https://github.com/robweber/xbmcbackup/compare/matrix-1.6.2...robweber:matrix-1.6.3) - 2020-06-15
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
@@ -48,7 +70,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
|||||||
- addon.xml updated to use Leia specific syntax and library imports
|
- addon.xml updated to use Leia specific syntax and library imports
|
||||||
- removed specific encode() calls per Python2/3 compatibility
|
- removed specific encode() calls per Python2/3 compatibility
|
||||||
- call isdigit() method on the string directly instead of str.isdigit() (results in unicode error)
|
- call isdigit() method on the string directly instead of str.isdigit() (results in unicode error)
|
||||||
- added flake8 testing to travis-ci
|
- added flake8 testing to travis-ci
|
||||||
- updated code to make python3 compatible
|
- updated code to make python3 compatible
|
||||||
- updated code for pep9 styling
|
- updated code for pep9 styling
|
||||||
- use setArt() to set ListItem icons as the icon= constructor is deprecated
|
- use setArt() to set ListItem icons as the icon= constructor is deprecated
|
||||||
@@ -66,16 +88,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
|||||||
|
|
||||||
- Updated Changelog format to the one suggested by [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
- Updated Changelog format to the one suggested by [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
||||||
- Added script.module.dropbox import as a dependency for Dropbox filesystem
|
- Added script.module.dropbox import as a dependency for Dropbox filesystem
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Fixed issue getting xbmcbackup.val file from non-zipped remote directories. Was being copied as though it was a local file so it was failing.
|
- Fixed issue getting xbmcbackup.val file from non-zipped remote directories. Was being copied as though it was a local file so it was failing.
|
||||||
- Use linux path separator (/) all the time, Kodi will interpret this correctly on windows. Was causing issues with remote file systems since os.path.sep
|
- Use linux path separator (/) all the time, Kodi will interpret this correctly on windows. Was causing issues with remote file systems since os.path.sep
|
||||||
- Fixed minor python code style changes based on kodi-addon-checker output
|
- Fixed minor python code style changes based on kodi-addon-checker output
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
- files releated to dropbox library, using script.module.dropbox import now
|
- files releated to dropbox library, using script.module.dropbox import now
|
||||||
|
|
||||||
## Version 1.5.1 - 2019-09-10
|
## Version 1.5.1 - 2019-09-10
|
||||||
|
|
||||||
@@ -85,7 +107,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
|||||||
## Version 1.5.0 - 2019-08-26
|
## Version 1.5.0 - 2019-08-26
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
- Added new Advanced file editor and file selection based on a .json
|
- Added new Advanced file editor and file selection based on a .json
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
- File backups and restores will not work with old version - breaking change with previous versions PR117
|
- File backups and restores will not work with old version - breaking change with previous versions PR117
|
||||||
@@ -95,7 +117,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
|||||||
### Added
|
### Added
|
||||||
- added file chunk support for Dropbox uploads
|
- added file chunk support for Dropbox uploads
|
||||||
- added scheduler delay to assist with time sync (rpi mostly), will delay startup by 2 min
|
- added scheduler delay to assist with time sync (rpi mostly), will delay startup by 2 min
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- fixed settings duplicate ids, thanks aster-anto
|
- fixed settings duplicate ids, thanks aster-anto
|
||||||
|
|
||||||
@@ -107,7 +129,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
|||||||
## Version 1.1.1
|
## Version 1.1.1
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
- added ability to "catchup" on missed scheduled backup
|
- added ability to "catchup" on missed scheduled backup
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- fixed error on authorizers (missing secret/key)
|
- fixed error on authorizers (missing secret/key)
|
||||||
@@ -118,10 +140,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
- added tinyurl generation for oauth urls
|
- added tinyurl generation for oauth urls
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- moved authorize to settings area for cloud storage
|
- moved authorize to settings area for cloud storage
|
||||||
|
|
||||||
## Version 1.0.9
|
## Version 1.0.9
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
@@ -142,7 +164,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
|||||||
### Added
|
### Added
|
||||||
|
|
||||||
- added progress for zip extraction - hopefully helps with extract errors
|
- added progress for zip extraction - hopefully helps with extract errors
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- fix for custom directories not working recursively
|
- fix for custom directories not working recursively
|
||||||
|
|
||||||
@@ -282,7 +304,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
|||||||
|
|
||||||
## Version 0.3.9
|
## Version 0.3.9
|
||||||
|
|
||||||
- added "just once" scheduler for one-off type backups
|
- added "just once" scheduler for one-off type backups
|
||||||
- show notification on scheduler
|
- show notification on scheduler
|
||||||
- update updated language files from Transifex
|
- update updated language files from Transifex
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
# launcher for various helpful functions found in the settings.xml area
|
# launcher for various helpful functions found in the settings.xml area
|
||||||
import sys
|
import sys
|
||||||
import xbmc
|
|
||||||
import xbmcgui
|
import xbmcgui
|
||||||
import xbmcvfs
|
import xbmcvfs
|
||||||
import resources.lib.utils as utils
|
import resources.lib.utils as utils
|
||||||
@@ -14,9 +13,9 @@ def authorize_cloud(cloudProvider):
|
|||||||
authorizer = DropboxAuthorizer()
|
authorizer = DropboxAuthorizer()
|
||||||
|
|
||||||
if(authorizer.authorize()):
|
if(authorizer.authorize()):
|
||||||
xbmcgui.Dialog().ok(utils.getString(30010), utils.getString(30027) + ' ' + utils.getString(30106))
|
xbmcgui.Dialog().ok(utils.getString(30010), '%s %s' % (utils.getString(30027), utils.getString(30106)))
|
||||||
else:
|
else:
|
||||||
xbmcgui.Dialog().ok(utils.getString(30010), utils.getString(30107) + ' ' + utils.getString(30027))
|
xbmcgui.Dialog().ok(utils.getString(30010), '%s %s' % (utils.getString(30107), utils.getString(30027)))
|
||||||
|
|
||||||
|
|
||||||
def remove_auth():
|
def remove_auth():
|
||||||
@@ -25,8 +24,8 @@ def remove_auth():
|
|||||||
|
|
||||||
if(shouldDelete):
|
if(shouldDelete):
|
||||||
# delete any of the known token file types
|
# delete any of the known token file types
|
||||||
xbmcvfs.delete(xbmc.translatePath(utils.data_dir() + "tokens.txt")) # dropbox
|
xbmcvfs.delete(xbmcvfs.translatePath(utils.data_dir() + "tokens.txt")) # dropbox
|
||||||
xbmcvfs.delete(xbmc.translatePath(utils.data_dir() + "google_drive.dat")) # google drive
|
xbmcvfs.delete(xbmcvfs.translatePath(utils.data_dir() + "google_drive.dat")) # google drive
|
||||||
|
|
||||||
|
|
||||||
def get_params():
|
def get_params():
|
||||||
|
|||||||
@@ -581,3 +581,14 @@ msgctxt "#30147"
|
|||||||
msgid "Toggle Sub Folders"
|
msgid "Toggle Sub Folders"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30148"
|
||||||
|
msgid "Ask before restoring Kodi UI settings"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30149"
|
||||||
|
msgid "Restore Kodi UI Settings"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30150"
|
||||||
|
msgid "Restore saved Kodi system settings from backup?"
|
||||||
|
msgstr ""
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import json
|
import json
|
||||||
import xbmc
|
|
||||||
import xbmcgui
|
import xbmcgui
|
||||||
import xbmcvfs
|
import xbmcvfs
|
||||||
import os.path
|
import os.path
|
||||||
@@ -7,7 +6,7 @@ from . import utils as utils
|
|||||||
|
|
||||||
|
|
||||||
class BackupSetManager:
|
class BackupSetManager:
|
||||||
jsonFile = xbmc.translatePath(utils.data_dir() + "custom_paths.json")
|
jsonFile = xbmcvfs.translatePath(utils.data_dir() + "custom_paths.json")
|
||||||
paths = None
|
paths = None
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@@ -106,7 +105,7 @@ class AdvancedBackupEditor:
|
|||||||
rootFolder = rootFolder + '/'
|
rootFolder = rootFolder + '/'
|
||||||
|
|
||||||
# check that this path even exists
|
# check that this path even exists
|
||||||
if(not xbmcvfs.exists(xbmc.translatePath(rootFolder))):
|
if(not xbmcvfs.exists(xbmcvfs.translatePath(rootFolder))):
|
||||||
self.dialog.ok(utils.getString(30117), utils.getString(30118), rootFolder)
|
self.dialog.ok(utils.getString(30117), utils.getString(30118), rootFolder)
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
@@ -227,7 +226,7 @@ class AdvancedBackupEditor:
|
|||||||
shouldContinue = self.dialog.yesno(utils.getString(30139), utils.getString(30140), utils.getString(30141))
|
shouldContinue = self.dialog.yesno(utils.getString(30139), utils.getString(30140), utils.getString(30141))
|
||||||
|
|
||||||
if(shouldContinue):
|
if(shouldContinue):
|
||||||
source = xbmc.translatePath(os.path.join(utils.addon_dir(), 'resources', 'data', 'default_files.json'))
|
source = xbmcvfs.translatePath(os.path.join(utils.addon_dir(), 'resources', 'data', 'default_files.json'))
|
||||||
dest = xbmc.translatePath(os.path.join(utils.data_dir(), 'custom_paths.json'))
|
dest = xbmcvfs.translatePath(os.path.join(utils.data_dir(), 'custom_paths.json'))
|
||||||
|
|
||||||
xbmcvfs.copy(source, dest)
|
xbmcvfs.copy(source, dest)
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import xbmc
|
|
||||||
import xbmcgui
|
import xbmcgui
|
||||||
import xbmcvfs
|
import xbmcvfs
|
||||||
import resources.lib.tinyurl as tinyurl
|
import resources.lib.tinyurl as tinyurl
|
||||||
@@ -7,6 +6,7 @@ import resources.lib.utils as utils
|
|||||||
# don't die on import error yet, these might not even get used
|
# don't die on import error yet, these might not even get used
|
||||||
try:
|
try:
|
||||||
from dropbox import dropbox
|
from dropbox import dropbox
|
||||||
|
from dropbox import oauth
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@ class DropboxAuthorizer:
|
|||||||
|
|
||||||
if(self.APP_KEY == '' and self.APP_SECRET == ''):
|
if(self.APP_KEY == '' and self.APP_SECRET == ''):
|
||||||
# we can't go any farther, need these for sure
|
# we can't go any farther, need these for sure
|
||||||
xbmcgui.Dialog().ok(utils.getString(30010), utils.getString(30027) + ' ' + utils.getString(30058), utils.getString(30059))
|
xbmcgui.Dialog().ok(utils.getString(30010), '%s %s\n%s' % (utils.getString(30027), utils.getString(30058), utils.getString(30059)))
|
||||||
|
|
||||||
result = False
|
result = False
|
||||||
|
|
||||||
@@ -46,13 +46,13 @@ class DropboxAuthorizer:
|
|||||||
self._deleteToken()
|
self._deleteToken()
|
||||||
|
|
||||||
# copied flow from http://dropbox-sdk-python.readthedocs.io/en/latest/moduledoc.html#dropbox.oauth.DropboxOAuth2FlowNoRedirect
|
# copied flow from http://dropbox-sdk-python.readthedocs.io/en/latest/moduledoc.html#dropbox.oauth.DropboxOAuth2FlowNoRedirect
|
||||||
flow = dropbox.oauth.DropboxOAuth2FlowNoRedirect(self.APP_KEY, self.APP_SECRET)
|
flow = oauth.DropboxOAuth2FlowNoRedirect(self.APP_KEY, self.APP_SECRET)
|
||||||
|
|
||||||
url = flow.start()
|
url = flow.start()
|
||||||
|
|
||||||
# print url in log
|
# print url in log
|
||||||
utils.log("Authorize URL: " + url)
|
utils.log("Authorize URL: " + url)
|
||||||
xbmcgui.Dialog().ok(utils.getString(30010), utils.getString(30056), utils.getString(30057), tinyurl.shorten(url))
|
xbmcgui.Dialog().ok(utils.getString(30010), '%s\n%s\n%s' % (utils.getString(30056), utils.getString(30057), str(tinyurl.shorten(url), 'utf-8')))
|
||||||
|
|
||||||
# get the auth code
|
# get the auth code
|
||||||
code = xbmcgui.Dialog().input(utils.getString(30027) + ' ' + utils.getString(30103))
|
code = xbmcgui.Dialog().input(utils.getString(30027) + ' ' + utils.getString(30103))
|
||||||
@@ -89,14 +89,14 @@ class DropboxAuthorizer:
|
|||||||
|
|
||||||
def _setToken(self, token):
|
def _setToken(self, token):
|
||||||
# write the token files
|
# write the token files
|
||||||
token_file = open(xbmc.translatePath(utils.data_dir() + "tokens.txt"), 'w')
|
token_file = open(xbmcvfs.translatePath(utils.data_dir() + "tokens.txt"), 'w')
|
||||||
token_file.write(token)
|
token_file.write(token)
|
||||||
token_file.close()
|
token_file.close()
|
||||||
|
|
||||||
def _getToken(self):
|
def _getToken(self):
|
||||||
# get token, if it exists
|
# get token, if it exists
|
||||||
if(xbmcvfs.exists(xbmc.translatePath(utils.data_dir() + "tokens.txt"))):
|
if(xbmcvfs.exists(xbmcvfs.translatePath(utils.data_dir() + "tokens.txt"))):
|
||||||
token_file = open(xbmc.translatePath(utils.data_dir() + "tokens.txt"))
|
token_file = open(xbmcvfs.translatePath(utils.data_dir() + "tokens.txt"))
|
||||||
token = token_file.read()
|
token = token_file.read()
|
||||||
token_file.close()
|
token_file.close()
|
||||||
|
|
||||||
@@ -105,5 +105,5 @@ class DropboxAuthorizer:
|
|||||||
return ""
|
return ""
|
||||||
|
|
||||||
def _deleteToken(self):
|
def _deleteToken(self):
|
||||||
if(xbmcvfs.exists(xbmc.translatePath(utils.data_dir() + "tokens.txt"))):
|
if(xbmcvfs.exists(xbmcvfs.translatePath(utils.data_dir() + "tokens.txt"))):
|
||||||
xbmcvfs.delete(xbmc.translatePath(utils.data_dir() + "tokens.txt"))
|
xbmcvfs.delete(xbmcvfs.translatePath(utils.data_dir() + "tokens.txt"))
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ class XbmcBackup:
|
|||||||
skip_advanced = False # if we should check for the existance of advancedsettings in the restore
|
skip_advanced = False # if we should check for the existance of advancedsettings in the restore
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.xbmc_vfs = XBMCFileSystem(xbmc.translatePath('special://home'))
|
self.xbmc_vfs = XBMCFileSystem(xbmcvfs.translatePath('special://home'))
|
||||||
|
|
||||||
self.configureRemote()
|
self.configureRemote()
|
||||||
utils.log(utils.getString(30046))
|
utils.log(utils.getString(30046))
|
||||||
@@ -165,7 +165,7 @@ class XbmcBackup:
|
|||||||
# backup all the files
|
# backup all the files
|
||||||
self.transferLeft = self.transferSize
|
self.transferLeft = self.transferSize
|
||||||
for fileGroup in allFiles:
|
for fileGroup in allFiles:
|
||||||
self.xbmc_vfs.set_root(xbmc.translatePath(fileGroup['source']))
|
self.xbmc_vfs.set_root(xbmcvfs.translatePath(fileGroup['source']))
|
||||||
self.remote_vfs.set_root(fileGroup['dest'] + fileGroup['name'])
|
self.remote_vfs.set_root(fileGroup['dest'] + fileGroup['name'])
|
||||||
filesCopied = self._copyFiles(fileGroup['files'], self.xbmc_vfs, self.remote_vfs)
|
filesCopied = self._copyFiles(fileGroup['files'], self.xbmc_vfs, self.remote_vfs)
|
||||||
|
|
||||||
@@ -183,11 +183,11 @@ class XbmcBackup:
|
|||||||
# send the zip file to the real remote vfs
|
# send the zip file to the real remote vfs
|
||||||
zip_name = self.remote_vfs.root_path[:-1] + ".zip"
|
zip_name = self.remote_vfs.root_path[:-1] + ".zip"
|
||||||
self.remote_vfs.cleanup()
|
self.remote_vfs.cleanup()
|
||||||
self.xbmc_vfs.rename(xbmc.translatePath("special://temp/xbmc_backup_temp.zip"), xbmc.translatePath("special://temp/" + zip_name))
|
self.xbmc_vfs.rename(xbmcvfs.translatePath("special://temp/xbmc_backup_temp.zip"), xbmcvfs.translatePath("special://temp/" + zip_name))
|
||||||
fileManager.addFile(xbmc.translatePath("special://temp/" + zip_name))
|
fileManager.addFile(xbmcvfs.translatePath("special://temp/" + zip_name))
|
||||||
|
|
||||||
# set root to data dir home and reset remote
|
# set root to data dir home and reset remote
|
||||||
self.xbmc_vfs.set_root(xbmc.translatePath("special://temp/"))
|
self.xbmc_vfs.set_root(xbmcvfs.translatePath("special://temp/"))
|
||||||
self.remote_vfs = self.saved_remote_vfs
|
self.remote_vfs = self.saved_remote_vfs
|
||||||
|
|
||||||
# update the amount to transfer
|
# update the amount to transfer
|
||||||
@@ -197,10 +197,10 @@ class XbmcBackup:
|
|||||||
|
|
||||||
if(not fileCopied):
|
if(not fileCopied):
|
||||||
# zip archive copy filed, inform the user
|
# zip archive copy filed, inform the user
|
||||||
shouldContinue = xbmcgui.Dialog().ok(utils.getString(30089), utils.getString(30090), utils.getString(30091))
|
shouldContinue = xbmcgui.Dialog().ok(utils.getString(30089), '%s\n%s' % (utils.getString(30090), utils.getString(30091)))
|
||||||
|
|
||||||
# delete the temp zip file
|
# delete the temp zip file
|
||||||
self.xbmc_vfs.rmfile(xbmc.translatePath("special://temp/" + zip_name))
|
self.xbmc_vfs.rmfile(xbmcvfs.translatePath("special://temp/" + zip_name))
|
||||||
|
|
||||||
# remove old backups
|
# remove old backups
|
||||||
self._rotateBackups()
|
self._rotateBackups()
|
||||||
@@ -220,9 +220,9 @@ class XbmcBackup:
|
|||||||
utils.log("copying zip file: " + self.restore_point)
|
utils.log("copying zip file: " + self.restore_point)
|
||||||
|
|
||||||
# set root to data dir home
|
# set root to data dir home
|
||||||
self.xbmc_vfs.set_root(xbmc.translatePath("special://temp/"))
|
self.xbmc_vfs.set_root(xbmcvfs.translatePath("special://temp/"))
|
||||||
|
|
||||||
if(not self.xbmc_vfs.exists(xbmc.translatePath("special://temp/" + self.restore_point))):
|
if(not self.xbmc_vfs.exists(xbmcvfs.translatePath("special://temp/" + self.restore_point))):
|
||||||
# copy just this file from the remote vfs
|
# copy just this file from the remote vfs
|
||||||
self.transferSize = self.remote_vfs.fileSize(self.remote_base_path + self.restore_point)
|
self.transferSize = self.remote_vfs.fileSize(self.remote_base_path + self.restore_point)
|
||||||
zipFile = []
|
zipFile = []
|
||||||
@@ -235,13 +235,13 @@ class XbmcBackup:
|
|||||||
utils.log("zip file exists already")
|
utils.log("zip file exists already")
|
||||||
|
|
||||||
# extract the zip file
|
# extract the zip file
|
||||||
zip_vfs = ZipFileSystem(xbmc.translatePath("special://temp/" + self.restore_point), 'r')
|
zip_vfs = ZipFileSystem(xbmcvfs.translatePath("special://temp/" + self.restore_point), 'r')
|
||||||
extractor = ZipExtractor()
|
extractor = ZipExtractor()
|
||||||
|
|
||||||
if(not extractor.extract(zip_vfs, xbmc.translatePath("special://temp/"), self.progressBar)):
|
if(not extractor.extract(zip_vfs, xbmcvfs.translatePath("special://temp/"), self.progressBar)):
|
||||||
# we had a problem extracting the archive, delete everything
|
# we had a problem extracting the archive, delete everything
|
||||||
zip_vfs.cleanup()
|
zip_vfs.cleanup()
|
||||||
self.xbmc_vfs.rmfile(xbmc.translatePath("special://temp/" + self.restore_point))
|
self.xbmc_vfs.rmfile(xbmcvfs.translatePath("special://temp/" + self.restore_point))
|
||||||
|
|
||||||
xbmcgui.Dialog().ok(utils.getString(30010), utils.getString(30101))
|
xbmcgui.Dialog().ok(utils.getString(30010), utils.getString(30101))
|
||||||
return
|
return
|
||||||
@@ -250,12 +250,12 @@ class XbmcBackup:
|
|||||||
|
|
||||||
self.progressBar.updateProgress(0, utils.getString(30049) + "......")
|
self.progressBar.updateProgress(0, utils.getString(30049) + "......")
|
||||||
# set the new remote vfs and fix xbmc path
|
# set the new remote vfs and fix xbmc path
|
||||||
self.remote_vfs = XBMCFileSystem(xbmc.translatePath("special://temp/" + self.restore_point.split(".")[0] + "/"))
|
self.remote_vfs = XBMCFileSystem(xbmcvfs.translatePath("special://temp/" + self.restore_point.split(".")[0] + "/"))
|
||||||
self.xbmc_vfs.set_root(xbmc.translatePath("special://home/"))
|
self.xbmc_vfs.set_root(xbmcvfs.translatePath("special://home/"))
|
||||||
|
|
||||||
# for restores remote path must exist
|
# for restores remote path must exist
|
||||||
if(not self.remote_vfs.exists(self.remote_vfs.root_path)):
|
if(not self.remote_vfs.exists(self.remote_vfs.root_path)):
|
||||||
xbmcgui.Dialog().ok(utils.getString(30010), utils.getString(30045), self.remote_vfs.root_path)
|
xbmcgui.Dialog().ok(utils.getString(30010), '%s\n%s' % (utils.getString(30045), self.remote_vfs.root_path))
|
||||||
return
|
return
|
||||||
|
|
||||||
valFile = self._checkValidationFile(self.remote_vfs.root_path)
|
valFile = self._checkValidationFile(self.remote_vfs.root_path)
|
||||||
@@ -284,6 +284,12 @@ class XbmcBackup:
|
|||||||
xbmcgui.Dialog().ok(utils.getString(30077), utils.getString(30078))
|
xbmcgui.Dialog().ok(utils.getString(30077), utils.getString(30078))
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# check if settings should be restored from this backup
|
||||||
|
restoreSettings = not utils.getSettingBool('always_prompt_restore_settings')
|
||||||
|
if(not restoreSettings and 'system_settings' in valFile):
|
||||||
|
# prompt the user to restore settings yes/no
|
||||||
|
restoreSettings = xbmcgui.Dialog().yesno(utils.getString(30149), utils.getString(30150))
|
||||||
|
|
||||||
# use a multiselect dialog to select sets to restore
|
# use a multiselect dialog to select sets to restore
|
||||||
restoreSets = [n['name'] for n in valFile['directories']]
|
restoreSets = [n['name'] for n in valFile['directories']]
|
||||||
|
|
||||||
@@ -294,13 +300,14 @@ class XbmcBackup:
|
|||||||
selectedSets = [restoreSets.index(n) for n in selectedSets if n in restoreSets] # if set name not found just skip it
|
selectedSets = [restoreSets.index(n) for n in selectedSets if n in restoreSets] # if set name not found just skip it
|
||||||
|
|
||||||
if(selectedSets is not None):
|
if(selectedSets is not None):
|
||||||
|
|
||||||
# go through each of the directories in the backup and write them to the correct location
|
# go through each of the directories in the backup and write them to the correct location
|
||||||
for index in selectedSets:
|
for index in selectedSets:
|
||||||
|
|
||||||
# add this directory
|
# add this directory
|
||||||
aDir = valFile['directories'][index]
|
aDir = valFile['directories'][index]
|
||||||
|
|
||||||
self.xbmc_vfs.set_root(xbmc.translatePath(aDir['path']))
|
self.xbmc_vfs.set_root(xbmcvfs.translatePath(aDir['path']))
|
||||||
if(self.remote_vfs.exists(self.remote_vfs.root_path + aDir['name'] + '/')):
|
if(self.remote_vfs.exists(self.remote_vfs.root_path + aDir['name'] + '/')):
|
||||||
# walk the directory
|
# walk the directory
|
||||||
fileManager.walkTree(self.remote_vfs.root_path + aDir['name'] + '/')
|
fileManager.walkTree(self.remote_vfs.root_path + aDir['name'] + '/')
|
||||||
@@ -309,7 +316,7 @@ class XbmcBackup:
|
|||||||
allFiles.append({"source": self.remote_vfs.root_path + aDir['name'], "dest": self.xbmc_vfs.root_path, "files": fileManager.getFiles()})
|
allFiles.append({"source": self.remote_vfs.root_path + aDir['name'], "dest": self.xbmc_vfs.root_path, "files": fileManager.getFiles()})
|
||||||
else:
|
else:
|
||||||
utils.log("error path not found: " + self.remote_vfs.root_path + aDir['name'])
|
utils.log("error path not found: " + self.remote_vfs.root_path + aDir['name'])
|
||||||
xbmcgui.Dialog().ok(utils.getString(30010), utils.getString(30045), self.remote_vfs.root_path + aDir['name'])
|
xbmcgui.Dialog().ok(utils.getString(30010), '%s\n%s' % (utils.getString(30045), self.remote_vfs.root_path + aDir['name']))
|
||||||
|
|
||||||
# restore all the files
|
# restore all the files
|
||||||
self.transferLeft = self.transferSize
|
self.transferLeft = self.transferSize
|
||||||
@@ -318,17 +325,19 @@ class XbmcBackup:
|
|||||||
self.xbmc_vfs.set_root(fileGroup['dest'])
|
self.xbmc_vfs.set_root(fileGroup['dest'])
|
||||||
self._copyFiles(fileGroup['files'], self.remote_vfs, self.xbmc_vfs)
|
self._copyFiles(fileGroup['files'], self.remote_vfs, self.xbmc_vfs)
|
||||||
|
|
||||||
|
# update the Kodi settings - if we can
|
||||||
|
if('system_settings' in valFile and restoreSettings):
|
||||||
|
self.progressBar.updateProgress(98, "Restoring Kodi settings")
|
||||||
|
gui_settings = GuiSettingsManager()
|
||||||
|
gui_settings.restore(valFile['system_settings'])
|
||||||
|
|
||||||
self.progressBar.updateProgress(99, "Clean up operations .....")
|
self.progressBar.updateProgress(99, "Clean up operations .....")
|
||||||
|
|
||||||
if(self.restore_point.split('.')[-1] == 'zip'):
|
if(self.restore_point.split('.')[-1] == 'zip'):
|
||||||
# delete the zip file and the extracted directory
|
# delete the zip file and the extracted directory
|
||||||
self.xbmc_vfs.rmfile(xbmc.translatePath("special://temp/" + self.restore_point))
|
self.xbmc_vfs.rmfile(xbmcvfs.translatePath("special://temp/" + self.restore_point))
|
||||||
self.xbmc_vfs.rmdir(self.remote_vfs.root_path)
|
self.xbmc_vfs.rmdir(self.remote_vfs.root_path)
|
||||||
|
|
||||||
# update the guisettings information (or what we can from it)
|
|
||||||
gui_settings = GuiSettingsManager()
|
|
||||||
gui_settings.run()
|
|
||||||
|
|
||||||
# call update addons to refresh everything
|
# call update addons to refresh everything
|
||||||
xbmc.executebuiltin('UpdateLocalAddons')
|
xbmc.executebuiltin('UpdateLocalAddons')
|
||||||
|
|
||||||
@@ -342,15 +351,15 @@ class XbmcBackup:
|
|||||||
if(mode == self.Backup and self.remote_vfs.root_path != ''):
|
if(mode == self.Backup and self.remote_vfs.root_path != ''):
|
||||||
if(utils.getSettingBool("compress_backups")):
|
if(utils.getSettingBool("compress_backups")):
|
||||||
# delete old temp file
|
# delete old temp file
|
||||||
if(self.xbmc_vfs.exists(xbmc.translatePath('special://temp/xbmc_backup_temp.zip'))):
|
if(self.xbmc_vfs.exists(xbmcvfs.translatePath('special://temp/xbmc_backup_temp.zip'))):
|
||||||
if(not self.xbmc_vfs.rmfile(xbmc.translatePath('special://temp/xbmc_backup_temp.zip'))):
|
if(not self.xbmc_vfs.rmfile(xbmcvfs.translatePath('special://temp/xbmc_backup_temp.zip'))):
|
||||||
# we had some kind of error deleting the old file
|
# we had some kind of error deleting the old file
|
||||||
xbmcgui.Dialog().ok(utils.getString(30010), utils.getString(30096), utils.getString(30097))
|
xbmcgui.Dialog().ok(utils.getString(30010), '%s\n%s' % (utils.getString(30096), utils.getString(30097)))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# save the remote file system and use the zip vfs
|
# save the remote file system and use the zip vfs
|
||||||
self.saved_remote_vfs = self.remote_vfs
|
self.saved_remote_vfs = self.remote_vfs
|
||||||
self.remote_vfs = ZipFileSystem(xbmc.translatePath("special://temp/xbmc_backup_temp.zip"), "w")
|
self.remote_vfs = ZipFileSystem(xbmcvfs.translatePath("special://temp/xbmc_backup_temp.zip"), "w")
|
||||||
|
|
||||||
self.remote_vfs.set_root(self.remote_vfs.root_path + time.strftime("%Y%m%d%H%M") + "/")
|
self.remote_vfs.set_root(self.remote_vfs.root_path + time.strftime("%Y%m%d%H%M") + "/")
|
||||||
progressBarTitle = progressBarTitle + utils.getString(30023) + ": " + utils.getString(30016)
|
progressBarTitle = progressBarTitle + utils.getString(30023) + ": " + utils.getString(30016)
|
||||||
@@ -404,14 +413,8 @@ class XbmcBackup:
|
|||||||
self._updateProgress('%s remaining, writing %s' % (utils.diskString(self.transferLeft), os.path.basename(aFile['file'][len(source.root_path):])))
|
self._updateProgress('%s remaining, writing %s' % (utils.diskString(self.transferLeft), os.path.basename(aFile['file'][len(source.root_path):])))
|
||||||
self.transferLeft = self.transferLeft - aFile['size']
|
self.transferLeft = self.transferLeft - aFile['size']
|
||||||
|
|
||||||
wroteFile = True
|
# copy the file
|
||||||
destFile = dest.root_path + aFile['file'][len(source.root_path):]
|
wroteFile = self._copyFile(source, dest, aFile['file'], dest.root_path + aFile['file'][len(source.root_path):])
|
||||||
if(isinstance(source, DropboxFileSystem)):
|
|
||||||
# if copying from cloud storage we need the file handle, use get_file
|
|
||||||
wroteFile = source.get_file(aFile['file'], destFile)
|
|
||||||
else:
|
|
||||||
# copy using normal method
|
|
||||||
wroteFile = dest.put(aFile['file'], 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):
|
||||||
@@ -419,11 +422,23 @@ class XbmcBackup:
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def _copyFile(self, source, dest, sourceFile, destFile):
|
||||||
|
result = True
|
||||||
|
|
||||||
|
if(isinstance(source, DropboxFileSystem)):
|
||||||
|
# if copying from cloud storage we need the file handle, use get_file
|
||||||
|
result = source.get_file(sourceFile, destFile)
|
||||||
|
else:
|
||||||
|
# copy using normal method
|
||||||
|
result = dest.put(sourceFile, destFile)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
def _addBackupDir(self, folder_name, root_path, dirList):
|
def _addBackupDir(self, folder_name, root_path, dirList):
|
||||||
utils.log('Backup set: ' + folder_name)
|
utils.log('Backup set: ' + folder_name)
|
||||||
fileManager = FileManager(self.xbmc_vfs)
|
fileManager = FileManager(self.xbmc_vfs)
|
||||||
|
|
||||||
self.xbmc_vfs.set_root(xbmc.translatePath(root_path))
|
self.xbmc_vfs.set_root(xbmcvfs.translatePath(root_path))
|
||||||
for aDir in dirList:
|
for aDir in dirList:
|
||||||
fileManager.addDir(aDir)
|
fileManager.addDir(aDir)
|
||||||
|
|
||||||
@@ -472,30 +487,35 @@ class XbmcBackup:
|
|||||||
remove_num = remove_num + 1
|
remove_num = remove_num + 1
|
||||||
|
|
||||||
def _createValidationFile(self, dirList):
|
def _createValidationFile(self, dirList):
|
||||||
valInfo = {"name": "XBMC Backup Validation File", "xbmc_version": xbmc.getInfoLabel('System.BuildVersion'), "type": 0}
|
valInfo = {"name": "XBMC Backup Validation File", "xbmc_version": xbmc.getInfoLabel('System.BuildVersion'), "type": 0, "system_settings": []}
|
||||||
valDirs = []
|
valDirs = []
|
||||||
|
|
||||||
|
# save list of file sets
|
||||||
for aDir in dirList:
|
for aDir in dirList:
|
||||||
valDirs.append({"name": aDir['name'], "path": aDir['source']})
|
valDirs.append({"name": aDir['name'], "path": aDir['source']})
|
||||||
valInfo['directories'] = valDirs
|
valInfo['directories'] = valDirs
|
||||||
|
|
||||||
vFile = xbmcvfs.File(xbmc.translatePath(utils.data_dir() + "xbmcbackup.val"), 'w')
|
# dump all current Kodi settings
|
||||||
|
gui_settings = GuiSettingsManager()
|
||||||
|
valInfo['system_settings'] = gui_settings.backup()
|
||||||
|
|
||||||
|
vFile = xbmcvfs.File(xbmcvfs.translatePath(utils.data_dir() + "xbmcbackup.val"), 'w')
|
||||||
vFile.write(json.dumps(valInfo))
|
vFile.write(json.dumps(valInfo))
|
||||||
vFile.write("")
|
vFile.write("")
|
||||||
vFile.close()
|
vFile.close()
|
||||||
|
|
||||||
success = self.remote_vfs.put(xbmc.translatePath(utils.data_dir() + "xbmcbackup.val"), self.remote_vfs.root_path + "xbmcbackup.val")
|
success = self._copyFile(self.xbmc_vfs, self.remote_vfs, xbmcvfs.translatePath(utils.data_dir() + "xbmcbackup.val"), self.remote_vfs.root_path + "xbmcbackup.val")
|
||||||
|
|
||||||
# remove the validation file
|
# remove the validation file
|
||||||
xbmcvfs.delete(xbmc.translatePath(utils.data_dir() + "xbmcbackup.val"))
|
xbmcvfs.delete(xbmcvfs.translatePath(utils.data_dir() + "xbmcbackup.val"))
|
||||||
|
|
||||||
if(success):
|
if(success):
|
||||||
# android requires a .nomedia file to not index the directory as media
|
# android requires a .nomedia file to not index the directory as media
|
||||||
if(not xbmcvfs.exists(xbmc.translatePath(utils.data_dir() + ".nomedia"))):
|
if(not xbmcvfs.exists(xbmcvfs.translatePath(utils.data_dir() + ".nomedia"))):
|
||||||
nmFile = xbmcvfs.File(xbmc.translatePath(utils.data_dir() + ".nomedia"), 'w')
|
nmFile = xbmcvfs.File(xbmcvfs.translatePath(utils.data_dir() + ".nomedia"), 'w')
|
||||||
nmFile.close()
|
nmFile.close()
|
||||||
|
|
||||||
success = self.remote_vfs.put(xbmc.translatePath(utils.data_dir() + ".nomedia"), self.remote_vfs.root_path + ".nomedia")
|
success = self._copyFile(self.xbmc_vfs, self.remote_vfs, xbmcvfs.translatePath(utils.data_dir() + ".nomedia"), self.remote_vfs.root_path + ".nomedia")
|
||||||
|
|
||||||
return success
|
return success
|
||||||
|
|
||||||
@@ -503,16 +523,13 @@ class XbmcBackup:
|
|||||||
result = None
|
result = None
|
||||||
|
|
||||||
# copy the file and open it
|
# copy the file and open it
|
||||||
if(isinstance(self.remote_vfs, DropboxFileSystem)):
|
self._copyFile(self.remote_vfs, self.xbmc_vfs, path + "xbmcbackup.val", xbmcvfs.translatePath(utils.data_dir() + "xbmcbackup_restore.val"))
|
||||||
self.remote_vfs.get_file(path + "xbmcbackup.val", xbmc.translatePath(utils.data_dir() + "xbmcbackup_restore.val"))
|
|
||||||
else:
|
|
||||||
self.xbmc_vfs.put(path + "xbmcbackup.val", xbmc.translatePath(utils.data_dir() + "xbmcbackup_restore.val"))
|
|
||||||
|
|
||||||
with xbmcvfs.File(xbmc.translatePath(utils.data_dir() + "xbmcbackup_restore.val"), 'r') as vFile:
|
with xbmcvfs.File(xbmcvfs.translatePath(utils.data_dir() + "xbmcbackup_restore.val"), 'r') as vFile:
|
||||||
jsonString = vFile.read()
|
jsonString = vFile.read()
|
||||||
|
|
||||||
# delete after checking
|
# delete after checking
|
||||||
xbmcvfs.delete(xbmc.translatePath(utils.data_dir() + "xbmcbackup_restore.val"))
|
xbmcvfs.delete(xbmcvfs.translatePath(utils.data_dir() + "xbmcbackup_restore.val"))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = json.loads(jsonString)
|
result = json.loads(jsonString)
|
||||||
@@ -530,11 +547,11 @@ class XbmcBackup:
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
def _createResumeBackupFile(self):
|
def _createResumeBackupFile(self):
|
||||||
with xbmcvfs.File(xbmc.translatePath(utils.data_dir() + "resume.txt"), 'w') as f:
|
with xbmcvfs.File(xbmcvfs.translatePath(utils.data_dir() + "resume.txt"), 'w') as f:
|
||||||
f.write(self.restore_point)
|
f.write(self.restore_point)
|
||||||
|
|
||||||
def _readBackupConfig(self, aFile):
|
def _readBackupConfig(self, aFile):
|
||||||
with xbmcvfs.File(xbmc.translatePath(aFile), 'r') as f:
|
with xbmcvfs.File(xbmcvfs.translatePath(aFile), 'r') as f:
|
||||||
jsonString = f.read()
|
jsonString = f.read()
|
||||||
return json.loads(jsonString)
|
return json.loads(jsonString)
|
||||||
|
|
||||||
@@ -555,8 +572,8 @@ class FileManager:
|
|||||||
def walk(self):
|
def walk(self):
|
||||||
|
|
||||||
for aDir in self.root_dirs:
|
for aDir in self.root_dirs:
|
||||||
self.addFile('-' + xbmc.translatePath(aDir['path']))
|
self.addFile('-' + xbmcvfs.translatePath(aDir['path']))
|
||||||
self.walkTree(xbmc.translatePath(aDir['path']), aDir['recurse'])
|
self.walkTree(xbmcvfs.translatePath(aDir['path']), aDir['recurse'])
|
||||||
|
|
||||||
def walkTree(self, directory, recurse=True):
|
def walkTree(self, directory, recurse=True):
|
||||||
if(utils.getSettingBool('verbose_logging')):
|
if(utils.getSettingBool('verbose_logging')):
|
||||||
@@ -571,7 +588,7 @@ class FileManager:
|
|||||||
if(recurse):
|
if(recurse):
|
||||||
# create all the subdirs first
|
# create all the subdirs first
|
||||||
for aDir in dirs:
|
for aDir in dirs:
|
||||||
dirPath = xbmcvfs.validatePath(xbmc.translatePath(directory + self.pathSep + aDir))
|
dirPath = xbmcvfs.validatePath(xbmcvfs.translatePath(directory + self.pathSep + aDir))
|
||||||
file_ext = aDir.split('.')[-1]
|
file_ext = aDir.split('.')[-1]
|
||||||
|
|
||||||
# check if directory is excluded
|
# check if directory is excluded
|
||||||
@@ -591,14 +608,14 @@ class FileManager:
|
|||||||
|
|
||||||
# copy all the files
|
# copy all the files
|
||||||
for aFile in files:
|
for aFile in files:
|
||||||
filePath = xbmc.translatePath(directory + self.pathSep + aFile)
|
filePath = xbmcvfs.translatePath(directory + self.pathSep + aFile)
|
||||||
self.addFile(filePath)
|
self.addFile(filePath)
|
||||||
|
|
||||||
def addDir(self, dirMeta):
|
def addDir(self, dirMeta):
|
||||||
if(dirMeta['type'] == 'include'):
|
if(dirMeta['type'] == 'include'):
|
||||||
self.root_dirs.append({'path': dirMeta['path'], 'recurse': dirMeta['recurse']})
|
self.root_dirs.append({'path': dirMeta['path'], 'recurse': dirMeta['recurse']})
|
||||||
else:
|
else:
|
||||||
self.excludeFile(xbmc.translatePath(dirMeta['path']))
|
self.excludeFile(xbmcvfs.translatePath(dirMeta['path']))
|
||||||
|
|
||||||
def addFile(self, filename):
|
def addFile(self, filename):
|
||||||
# write the full remote path name of this file
|
# write the full remote path name of this file
|
||||||
|
|||||||
@@ -1,72 +1,47 @@
|
|||||||
import json
|
import json
|
||||||
import xbmc
|
import xbmc
|
||||||
import xbmcvfs
|
|
||||||
from . import utils as utils
|
from . import utils as utils
|
||||||
from xml.dom import minidom
|
|
||||||
from xml.parsers.expat import ExpatError
|
|
||||||
|
|
||||||
|
|
||||||
class GuiSettingsManager:
|
class GuiSettingsManager:
|
||||||
doc = None
|
filename = 'kodi_settings.json'
|
||||||
|
systemSettings = None
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
# first make a copy of the file
|
# get all of the current Kodi settings
|
||||||
xbmcvfs.copy(xbmc.translatePath('special://home/userdata/guisettings.xml'), xbmc.translatePath("special://home/userdata/guisettings.xml.restored"))
|
json_response = json.loads(xbmc.executeJSONRPC('{"jsonrpc":"2.0", "id":1, "method":"Settings.GetSettings","params":{"level":"expert"}}'))
|
||||||
|
|
||||||
# read in the copy
|
self.systemSettings = json_response['result']['settings']
|
||||||
self._readFile(xbmc.translatePath('special://home/userdata/guisettings.xml.restored'))
|
|
||||||
|
|
||||||
def run(self):
|
def backup(self):
|
||||||
# get a list of all the settings we can manipulate via json
|
utils.log('Backing up Kodi settings')
|
||||||
json_response = json.loads(xbmc.executeJSONRPC('{"jsonrpc":"2.0", "id":1, "method":"Settings.GetSettings","params":{"level":"advanced"}}'))
|
|
||||||
|
|
||||||
settings = json_response['result']['settings']
|
# return all current settings
|
||||||
currentSettings = {}
|
return self.systemSettings
|
||||||
|
|
||||||
for aSetting in settings:
|
def restore(self, restoreSettings):
|
||||||
if('value' in aSetting):
|
utils.log('Restoring Kodi settings')
|
||||||
currentSettings[aSetting['id']] = aSetting['value']
|
|
||||||
|
|
||||||
# parse the existing xml file and get all the settings we need to restore
|
updateJson = {"jsonrpc": "2.0", "id": 1, "method": "Settings.SetSettingValue", "params": {"setting": "", "value": ""}}
|
||||||
restoreSettings = self.__parseNodes(self.doc.getElementsByTagName('setting'))
|
|
||||||
|
|
||||||
# get a list where the restore setting value != the current value
|
# create a setting=value dict of the current settings
|
||||||
updateSettings = {k: v for k, v in list(restoreSettings.items()) if (k in currentSettings and currentSettings[k] != v)}
|
settingsDict = {}
|
||||||
|
for aSetting in self.systemSettings:
|
||||||
|
# ignore action types, no value
|
||||||
|
if(aSetting['type'] != 'action'):
|
||||||
|
settingsDict[aSetting['id']] = aSetting['value']
|
||||||
|
|
||||||
# go through all the found settings and update them
|
restoreCount = 0
|
||||||
jsonObj = {"jsonrpc": "2.0", "id": 1, "method": "Settings.SetSettingValue", "params": {"setting": "", "value": ""}}
|
for aSetting in restoreSettings:
|
||||||
for anId, aValue in list(updateSettings.items()):
|
# only update a setting if its different than the current (action types have no value)
|
||||||
utils.log("updating: " + anId + ", value: " + str(aValue))
|
if(aSetting['type'] != 'action' and settingsDict[aSetting['id']] != aSetting['value']):
|
||||||
|
if(utils.getSettingBool('verbose_logging')):
|
||||||
|
utils.log('%s different than current: %s' % (aSetting['id'], str(aSetting['value'])))
|
||||||
|
|
||||||
jsonObj['params']['setting'] = anId
|
updateJson['params']['setting'] = aSetting['id']
|
||||||
jsonObj['params']['value'] = aValue
|
updateJson['params']['value'] = aSetting['value']
|
||||||
|
|
||||||
xbmc.executeJSONRPC(json.dumps(jsonObj))
|
xbmc.executeJSONRPC(json.dumps(updateJson))
|
||||||
|
restoreCount = restoreCount + 1
|
||||||
|
|
||||||
def __parseNodes(self, nodeList):
|
utils.log('Update %d settings' % restoreCount)
|
||||||
result = {}
|
|
||||||
|
|
||||||
for node in nodeList:
|
|
||||||
nodeValue = ''
|
|
||||||
if(node.firstChild is not None):
|
|
||||||
nodeValue = node.firstChild.nodeValue
|
|
||||||
|
|
||||||
# check for numbers and booleans
|
|
||||||
if(nodeValue.isdigit()):
|
|
||||||
nodeValue = int(nodeValue)
|
|
||||||
elif(nodeValue == 'true'):
|
|
||||||
nodeValue = True
|
|
||||||
elif(nodeValue == 'false'):
|
|
||||||
nodeValue = False
|
|
||||||
|
|
||||||
result[node.getAttribute('id')] = nodeValue
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
def _readFile(self, fileLoc):
|
|
||||||
|
|
||||||
if(xbmcvfs.exists(fileLoc)):
|
|
||||||
try:
|
|
||||||
self.doc = minidom.parse(fileLoc)
|
|
||||||
except ExpatError:
|
|
||||||
utils.log("Can't read " + fileLoc)
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ class BackupScheduler:
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.monitor = UpdateMonitor(update_method=self.settingsChanged)
|
self.monitor = UpdateMonitor(update_method=self.settingsChanged)
|
||||||
self.enabled = utils.getSettingBool("enable_scheduler")
|
self.enabled = utils.getSettingBool("enable_scheduler")
|
||||||
self.next_run_path = xbmc.translatePath(utils.data_dir()) + 'next_run.txt'
|
self.next_run_path = xbmcvfs.translatePath(utils.data_dir()) + 'next_run.txt'
|
||||||
|
|
||||||
if(self.enabled):
|
if(self.enabled):
|
||||||
|
|
||||||
@@ -173,11 +173,11 @@ class BackupScheduler:
|
|||||||
|
|
||||||
def _resumeCheck(self):
|
def _resumeCheck(self):
|
||||||
shouldContinue = False
|
shouldContinue = False
|
||||||
if(xbmcvfs.exists(xbmc.translatePath(utils.data_dir() + "resume.txt"))):
|
if(xbmcvfs.exists(xbmcvfs.translatePath(utils.data_dir() + "resume.txt"))):
|
||||||
rFile = xbmcvfs.File(xbmc.translatePath(utils.data_dir() + "resume.txt"), 'r')
|
rFile = xbmcvfs.File(xbmcvfs.translatePath(utils.data_dir() + "resume.txt"), 'r')
|
||||||
self.restore_point = rFile.read()
|
self.restore_point = rFile.read()
|
||||||
rFile.close()
|
rFile.close()
|
||||||
xbmcvfs.delete(xbmc.translatePath(utils.data_dir() + "resume.txt"))
|
xbmcvfs.delete(xbmcvfs.translatePath(utils.data_dir() + "resume.txt"))
|
||||||
shouldContinue = xbmcgui.Dialog().yesno(utils.getString(30042), utils.getString(30043), utils.getString(30044))
|
shouldContinue = xbmcgui.Dialog().yesno(utils.getString(30042), utils.getString(30043), utils.getString(30044))
|
||||||
|
|
||||||
return shouldContinue
|
return shouldContinue
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import xbmc
|
import xbmc
|
||||||
import xbmcgui
|
import xbmcgui
|
||||||
import xbmcaddon
|
import xbmcaddon
|
||||||
|
import xbmcvfs
|
||||||
|
|
||||||
__addon_id__ = 'script.xbmcbackup'
|
__addon_id__ = 'script.xbmcbackup'
|
||||||
__Addon = xbmcaddon.Addon(__addon_id__)
|
__Addon = xbmcaddon.Addon(__addon_id__)
|
||||||
@@ -23,7 +24,7 @@ def log(message, loglevel=xbmc.LOGDEBUG):
|
|||||||
|
|
||||||
|
|
||||||
def showNotification(message):
|
def showNotification(message):
|
||||||
xbmcgui.Dialog().notification(getString(30010), message, time=4000, icon=xbmc.translatePath(__Addon.getAddonInfo('path') + "/resources/images/icon.png"))
|
xbmcgui.Dialog().notification(getString(30010), message, time=4000, icon=xbmcvfs.translatePath(__Addon.getAddonInfo('path') + "/resources/images/icon.png"))
|
||||||
|
|
||||||
|
|
||||||
def getSetting(name):
|
def getSetting(name):
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ from __future__ import unicode_literals
|
|||||||
import zipfile
|
import zipfile
|
||||||
import os.path
|
import os.path
|
||||||
import sys
|
import sys
|
||||||
import xbmc
|
|
||||||
import xbmcvfs
|
import xbmcvfs
|
||||||
import xbmcgui
|
import xbmcgui
|
||||||
from dropbox import dropbox
|
from dropbox import dropbox
|
||||||
@@ -68,10 +67,10 @@ class XBMCFileSystem(Vfs):
|
|||||||
return xbmcvfs.listdir(directory)
|
return xbmcvfs.listdir(directory)
|
||||||
|
|
||||||
def mkdir(self, directory):
|
def mkdir(self, directory):
|
||||||
return xbmcvfs.mkdir(xbmc.translatePath(directory))
|
return xbmcvfs.mkdir(xbmcvfs.translatePath(directory))
|
||||||
|
|
||||||
def put(self, source, dest):
|
def put(self, source, dest):
|
||||||
return xbmcvfs.copy(xbmc.translatePath(source), xbmc.translatePath(dest))
|
return xbmcvfs.copy(xbmcvfs.translatePath(source), xbmcvfs.translatePath(dest))
|
||||||
|
|
||||||
def rmdir(self, directory):
|
def rmdir(self, directory):
|
||||||
return xbmcvfs.rmdir(directory, True)
|
return xbmcvfs.rmdir(directory, True)
|
||||||
@@ -108,7 +107,7 @@ class ZipFileSystem(Vfs):
|
|||||||
|
|
||||||
def put(self, source, dest):
|
def put(self, source, dest):
|
||||||
|
|
||||||
aFile = xbmcvfs.File(xbmc.translatePath(source), 'r')
|
aFile = xbmcvfs.File(xbmcvfs.translatePath(source), 'r')
|
||||||
|
|
||||||
self.zip.writestr(dest, aFile.readBytes())
|
self.zip.writestr(dest, aFile.readBytes())
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
<category id="general" label="30011" level="expert">
|
<category id="general" label="30011" level="expert">
|
||||||
<setting id="compress_backups" type="bool" label="30087" default="false" />
|
<setting id="compress_backups" type="bool" label="30087" default="false" />
|
||||||
<setting id="backup_rotation" type="number" label="30026" default="0" />
|
<setting id="backup_rotation" type="number" label="30026" default="0" />
|
||||||
|
<setting id="always_prompt_restore_settings" type="bool" label="30148" default="false" />
|
||||||
<setting id="progress_mode" type="enum" label="30022" lvalues="30082|30083|30084" default="0" />
|
<setting id="progress_mode" type="enum" label="30022" lvalues="30082|30083|30084" default="0" />
|
||||||
<setting type="sep" />
|
<setting type="sep" />
|
||||||
<setting id="verbose_logging" type="bool" label="Enable Verbose Logging" default="false" />
|
<setting id="verbose_logging" type="bool" label="Enable Verbose Logging" default="false" />
|
||||||
|
|||||||
Reference in New Issue
Block a user