Merge branch 'master' into matrix

This commit is contained in:
Rob Weber 2019-12-04 11:53:12 -06:00
commit f9f49e3fe6
13 changed files with 64 additions and 53 deletions

View File

@ -10,5 +10,5 @@ before_script:
# command to run our tests
script:
- flake8 ./ --statistics --show-source --ignore=E501,E722 --exclude=croniter.py,relativedelta.py,*/dropbox/* # check python structure against flake8 tests, ignore long lines
- flake8 ./ --statistics --show-source --builtins=sys --ignore=E501,E722 --exclude=croniter.py,relativedelta.py,*/dropbox/* # check python structure against flake8 tests, ignore long lines
- kodi-addon-checker --branch=matrix --allow-folder-id-mismatch

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="script.xbmcbackup"
name="Backup" version="1.6.0~beta1" provider-name="robweber">
name="Backup" version="1.6.1~beta1" provider-name="robweber">
<requires>
<import addon="xbmc.python" version="3.0.0"/>
<import addon="script.module.dateutil" version="2.8.0" />
@ -78,12 +78,10 @@
<description lang="sk_SK">Už ste niekedy poškodili konfiguráciu Kodi a priali si mať zálohu? Teraz môžete - na jeden klik. Môžete exportovať Vašu databázu, playlist, náhľady, doplnky a konfigurácie na ktorýkoľvek zdroj zapisovateľný Kodi. Zálohy môžu byť púšťané na požiadanie alebo plánovačom. </description>
<description lang="sv_SE">Har du någonsin tappat bort din Kodi konfiguration och önskat att du hade en backup? Nu kan du enkelt med ett klick. Du kan exportera din databas, spellista, minityrer, tillägg och andra konfigurationsdetaljer till valfri källa som är skrivbar för Kodi. Backupper kan köras på begäran eller via scheman.</description>
<description lang="zh_CN">你是否经常折腾你的 Kodi因而希望能够有个备份你可以把资料库、播放列表、缩略图、插件和其他配置细节导出到 Kodi 可以写入的任意位置。备份可以按需运行或通过计划任务执行。</description>
<language></language>
<platform>all</platform>
<license>The MIT License</license>
<license>MIT</license>
<forum>https://forum.kodi.tv/showthread.php?tid=129499</forum>
<source>https://github.com/robweber/xbmcbackup</source>
<email></email>
<assets>
<icon>resources/images/icon.png</icon>
<screenshot>resources/images/screenshot1.png</screenshot>
@ -91,10 +89,9 @@
<screenshot>resources/images/screenshot3.png</screenshot>
<screenshot>resources/images/screenshot4.png</screenshot>
</assets>
<news>Version 1.5.2
- Added script.module.dropbox import as a dependency for Dropbox filesystem
- 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
<news>Version 1.6.0
- dependency on script.module.dateutil for relativedelta.py class
- added Python 3 compatibility
</news>
</extension>
</addon>

View File

@ -4,12 +4,13 @@ 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/)
## [Unreleased](https://github.com/robweber/xbmcbackup/compare/krypton-1.5.2...HEAD)
## [Version 1.6.0](https://github.com/robweber/xbmcbackup/compare/krypton-1.5.2...robweber:matrix-1.6.0) - 2019-11-26
### Added
- added new badges for Kodi Version, TravisCI and license information from shields.io
- dependency on script.module.dateutil for relativedelta.py class
- add Dropbox library back in v 9.4.0, for Python 3 compatibility
### Changed

View File

@ -1,4 +1,3 @@
import sys
import xbmc
import xbmcgui
import resources.lib.utils as utils
@ -39,7 +38,7 @@ if(mode == -1):
options = [utils.getString(30016), utils.getString(30017), utils.getString(30099)]
# find out if we're using the advanced editor
if(int(utils.getSetting('backup_selection_type')) == 1):
if(utils.getSettingInt('backup_selection_type') == 1):
options.append(utils.getString(30125))
# figure out if this is a backup or a restore from the user
@ -53,7 +52,7 @@ if(mode != -1):
if(mode == 2):
# open the settings dialog
utils.openSettings()
elif(mode == 3 and int(utils.getSetting('backup_selection_type')) == 1):
elif(mode == 3 and utils.getSettingInt('backup_selection_type') == 1):
# open the advanced editor
xbmc.executebuiltin('RunScript(special://home/addons/script.xbmcbackup/launcher.py, action=advanced_editor)')
elif(backup.remoteConfigured()):

View File

View File

@ -2,6 +2,7 @@ import json
import xbmc
import xbmcgui
import xbmcvfs
import os.path
from . import utils as utils
@ -180,14 +181,14 @@ class AdvancedBackupEditor:
while(exitCondition != -1):
# load the custom paths
listItem = xbmcgui.ListItem(utils.getString(30126), '')
listItem.setArt({'icon': utils.addon_dir() + 'resources/images/plus-icon.png'})
listItem.setArt({'icon': os.path.join(utils.addon_dir(), 'resources', 'images', 'plus-icon.png')})
options = [listItem]
for index in range(0, len(customPaths.getSets())):
aSet = customPaths.getSet(index)
listItem = xbmcgui.ListItem(aSet['name'], utils.getString(30121) + ': ' + aSet['set']['root'])
listItem.setArt({'icon': utils.addon_dir() + 'resources/images/folder-icon.png'})
listItem.setArt({'icon': os.path.join(utils.addon_dir(), 'resources', 'images', 'folder-icon.png')})
options.append(listItem)
# show the gui
@ -226,7 +227,7 @@ class AdvancedBackupEditor:
shouldContinue = self.dialog.yesno(utils.getString(30139), utils.getString(30140), utils.getString(30141))
if(shouldContinue):
source = xbmc.translatePath(utils.addon_dir() + "/resources/data/default_files.json")
dest = xbmc.translatePath(utils.data_dir() + "/custom_paths.json")
source = xbmc.translatePath(os.path.join(utils.addon_dir(), 'resources', 'data', 'default_files.json'))
dest = xbmc.translatePath(os.path.join(utils.data_dir(), 'custom_paths.json'))
xbmcvfs.copy(source, dest)

View File

@ -124,14 +124,14 @@ class XbmcBackup:
utils.log('File Selection Type: ' + str(utils.getSetting('backup_selection_type')))
allFiles = []
if(int(utils.getSetting('backup_selection_type')) == 0):
if(utils.getSettingInt('backup_selection_type') == 0):
# read in a list of the directories to backup
selectedDirs = self._readBackupConfig(utils.addon_dir() + "/resources/data/default_files.json")
# simple mode - get file listings for all enabled directories
for aDir in self.simple_directory_list:
# if this dir enabled
if(utils.getSetting('backup_' + aDir) == 'true'):
if(utils.getSettingBool('backup_' + aDir)):
# get a file listing and append it to the allfiles array
allFiles.append(self._addBackupDir(aDir, selectedDirs[aDir]['root'], selectedDirs[aDir]['dirs']))
else:
@ -176,7 +176,7 @@ class XbmcBackup:
self.xbmc_vfs.set_root("special://home/")
self.remote_vfs.set_root(orig_base_path)
if(utils.getSetting("compress_backups") == 'true'):
if(utils.getSettingBool("compress_backups")):
fileManager = FileManager(self.xbmc_vfs)
# send the zip file to the real remote vfs
@ -333,7 +333,7 @@ class XbmcBackup:
# append backup folder name
progressBarTitle = utils.getString(30010) + " - "
if(mode == self.Backup and self.remote_vfs.root_path != ''):
if(utils.getSetting("compress_backups") == 'true'):
if(utils.getSettingBool("compress_backups")):
# delete old temp file
if(self.xbmc_vfs.exists(xbmc.translatePath('special://temp/xbmc_backup_temp.zip'))):
if(not self.xbmc_vfs.rmfile(xbmc.translatePath('special://temp/xbmc_backup_temp.zip'))):
@ -387,7 +387,9 @@ class XbmcBackup:
for aFile in fileList:
if(not self.progressBar.checkCancel()):
utils.log('Writing file: ' + aFile, xbmc.LOGDEBUG)
if(utils.getSettingBool('verbose_logging')):
utils.log('Writing file: ' + aFile)
if(aFile.startswith("-")):
self._updateProgress(aFile[len(source.root_path) + 1:])
dest.mkdir(dest.root_path + aFile[len(source.root_path) + 1:])
@ -438,7 +440,7 @@ class XbmcBackup:
self.progressBar.updateProgress(int((float(self.filesTotal - self.filesLeft) / float(self.filesTotal)) * 100), message)
def _rotateBackups(self):
total_backups = int(utils.getSetting('backup_rotation'))
total_backups = utils.getSettingInt('backup_rotation')
if(total_backups > 0):
# get a list of valid backup folders
@ -522,14 +524,12 @@ class XbmcBackup:
return result
def _createResumeBackupFile(self):
rFile = xbmcvfs.File(xbmc.translatePath(utils.data_dir() + "resume.txt"), 'w')
rFile.write(self.restore_point)
rFile.close()
with xbmcvfs.File(xbmc.translatePath(utils.data_dir() + "resume.txt"), 'w') as f:
f.write(self.restore_point)
def _readBackupConfig(self, aFile):
jFile = xbmcvfs.File(xbmc.translatePath(aFile), 'r')
jsonString = jFile.read()
jFile.close()
with xbmcvfs.File(xbmc.translatePath(aFile), 'r') as f:
jsonString = f.read()
return json.loads(jsonString)
@ -552,7 +552,9 @@ class FileManager:
self.walkTree(xbmc.translatePath(aDir['path']), aDir['recurse'])
def walkTree(self, directory, recurse=True):
if(utils.getSettingBool('verbose_logging')):
utils.log('walking ' + directory + ', recurse: ' + str(recurse))
if(directory[-1:] == '/' or directory[-1:] == '\\'):
directory = directory[:-1]
@ -593,11 +595,12 @@ class FileManager:
def addFile(self, filename):
# write the full remote path name of this file
if(utils.getSettingBool('verbose_logging')):
utils.log("Add File: " + filename)
self.fileArray.append(filename)
def excludeFile(self, filename):
# remove trailing slash
if(filename[-1] == '/' or filename[-1] == '\\'):
filename = filename[:-1]

View File

@ -15,9 +15,9 @@ class BackupProgressBar:
self.override = progressOverride
# check if we should use the progress bar
if(int(utils.getSetting('progress_mode')) != 2):
if(utils.getSettingInt('progress_mode') != 2):
# check if background or normal
if(int(utils.getSetting('progress_mode')) == 0 and not self.override):
if(utils.getSettingInt('progress_mode') == 0 and not self.override):
self.mode = self.DIALOG
self.progressBar = xbmcgui.DialogProgress()
else:

View File

@ -9,4 +9,4 @@ def shorten(aUrl):
data = req.read()
# should be a tiny url
return str(data)
return data

View File

@ -30,6 +30,14 @@ def getSetting(name):
return __Addon.getSetting(name)
def getSettingBool(name):
return bool(__Addon.getSettingBool(name))
def getSettingInt(name):
return __Addon.getSettingInt(name)
def setSetting(name, value):
__Addon.setSetting(name, value)

View File

@ -1,9 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<settings>
<category id="general" label="30011">
<category id="general" label="30011" level="expert">
<setting id="compress_backups" type="bool" label="30087" default="false" />
<setting id="backup_rotation" type="number" label="30026" default="0" />
<setting id="progress_mode" type="enum" label="30022" lvalues="30082|30083|30084" default="0" />
<setting type="sep" />
<setting id="verbose_logging" type="bool" label="Enable Verbose Logging" default="false" />
<setting id="upgrade_notes" type="number" label="upgrade_notes" visible="false" default="1" />
</category>
<category id="backup_path" label="30048">

View File

@ -19,10 +19,10 @@ class BackupScheduler:
def __init__(self):
self.monitor = UpdateMonitor(update_method=self.settingsChanged)
self.enabled = utils.getSetting("enable_scheduler")
self.enabled = utils.getSettingBool("enable_scheduler")
self.next_run_path = xbmc.translatePath(utils.data_dir()) + 'next_run.txt'
if(self.enabled == "true"):
if(self.enabled):
# sleep for 2 minutes so Kodi can start and time can update correctly
xbmc.Monitor().waitForAbort(120)
@ -40,9 +40,9 @@ class BackupScheduler:
fh.close()
# if we missed and the user wants to play catch-up
if(0 < nr <= time.time() and utils.getSetting('schedule_miss') == 'true'):
if(0 < nr <= time.time() and utils.getSettingBool('schedule_miss')):
utils.log("scheduled backup was missed, doing it now...")
progress_mode = int(utils.getSetting('progress_mode'))
progress_mode = utils.getSettingInt('progress_mode')
if(progress_mode == 0):
progress_mode = 1 # Kodi just started, don't block it with a foreground progress bar
@ -59,7 +59,7 @@ class BackupScheduler:
def start(self):
# display upgrade messages if they exist
if(int(utils.getSetting('upgrade_notes')) < UPGRADE_INT):
if(utils.getSettingInt('upgrade_notes') < UPGRADE_INT):
xbmcgui.Dialog().ok(utils.getString(30010), utils.getString(30132))
utils.setSetting('upgrade_notes', str(UPGRADE_INT))
@ -75,16 +75,16 @@ class BackupScheduler:
while(not self.monitor.abortRequested()):
if(self.enabled == "true"):
if(self.enabled):
# scheduler is still on
now = time.time()
if(self.next_run <= now):
progress_mode = int(utils.getSetting('progress_mode'))
progress_mode = utils.getSettingInt('progress_mode')
self.doScheduledBackup(progress_mode)
# check if we should shut the computer down
if(utils.getSetting("cron_shutdown") == 'true'):
if(utils.getSettingBool("cron_shutdown")):
# wait 10 seconds to make sure all backup processes and files are completed
time.sleep(10)
xbmc.executebuiltin('ShutDown()')
@ -105,21 +105,21 @@ class BackupScheduler:
if(backup.remoteConfigured()):
if(int(utils.getSetting('progress_mode')) in [0, 1]):
if(utils.getSettingInt('progress_mode') in [0, 1]):
backup.backup(True)
else:
backup.backup(False)
# check if this is a "one-off"
if(int(utils.getSetting("schedule_interval")) == 0):
if(utils.getSettingInt("schedule_interval") == 0):
# disable the scheduler after this run
self.enabled = "false"
self.enabled = False
utils.setSetting('enable_scheduler', 'false')
else:
utils.showNotification(utils.getString(30045))
def findNextRun(self, now):
progress_mode = int(utils.getSetting('progress_mode'))
progress_mode = utils.getSettingInt('progress_mode')
# find the cron expression and get the next run time
cron_exp = self.parseSchedule()
@ -141,22 +141,22 @@ class BackupScheduler:
utils.showNotification(utils.getString(30081) + " " + utils.getRegionalTimestamp(datetime.fromtimestamp(self.next_run), ['dateshort', 'time']))
def settingsChanged(self):
current_enabled = utils.getSetting("enable_scheduler")
current_enabled = utils.getSettingBool("enable_scheduler")
if(current_enabled == "true" and self.enabled == "false"):
if(current_enabled and not self.enabled):
# scheduler was just turned on
self.enabled = current_enabled
self.setup()
elif (current_enabled == "false" and self.enabled == "true"):
elif (not current_enabled and self.enabled):
# schedule was turn off
self.enabled = current_enabled
if(self.enabled == "true"):
if(self.enabled):
# always recheck the next run time after an update
self.findNextRun(time.time())
def parseSchedule(self):
schedule_type = int(utils.getSetting("schedule_interval"))
schedule_type = utils.getSettingInt("schedule_interval")
cron_exp = utils.getSetting("cron_schedule")
hour_of_day = utils.getSetting("schedule_time")