From 16e13c7d8034e8920cca7c3008189d060121133f Mon Sep 17 00:00:00 2001 From: Rob Weber Date: Tue, 26 Nov 2019 13:14:57 -0600 Subject: [PATCH 1/8] version bump - 1.6.0 --- addon.xml | 2 +- changelog.md | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/addon.xml b/addon.xml index 8482ea6..e879414 100644 --- a/addon.xml +++ b/addon.xml @@ -1,6 +1,6 @@  + name="Backup" version="1.6.0" provider-name="robweber"> diff --git a/changelog.md b/changelog.md index 92cec7d..9d438de 100644 --- a/changelog.md +++ b/changelog.md @@ -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 From 4492ab593e3416a0f72b89fd64ca8a4ca7544220 Mon Sep 17 00:00:00 2001 From: Rob Weber Date: Wed, 27 Nov 2019 13:14:42 -0600 Subject: [PATCH 2/8] update news --- addon.xml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/addon.xml b/addon.xml index e879414..c2dd334 100644 --- a/addon.xml +++ b/addon.xml @@ -93,10 +93,9 @@ resources/images/screenshot3.png resources/images/screenshot4.png - 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 + Version 1.6.0 + - dependency on script.module.dateutil for relativedelta.py class + - added Python 3 compatibility From 3849a902ea56dac200891b3627940d99b767bd28 Mon Sep 17 00:00:00 2001 From: Rob Weber Date: Wed, 27 Nov 2019 14:05:08 -0600 Subject: [PATCH 3/8] working 16.1 beta --- addon.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon.xml b/addon.xml index c2dd334..74d199d 100644 --- a/addon.xml +++ b/addon.xml @@ -1,6 +1,6 @@  + name="Backup" version="1.6.1~beta1" provider-name="robweber"> From 8c4465f552531757c13147798a99dc1bb1ad70a2 Mon Sep 17 00:00:00 2001 From: Rob Weber Date: Wed, 27 Nov 2019 14:19:25 -0600 Subject: [PATCH 4/8] add methods for getting bools and int values directly --- resources/lib/utils.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/resources/lib/utils.py b/resources/lib/utils.py index f6f4df5..2be1ef2 100644 --- a/resources/lib/utils.py +++ b/resources/lib/utils.py @@ -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) From 5ee610a586b63e667e979cf44aef70485916d946 Mon Sep 17 00:00:00 2001 From: Rob Weber Date: Wed, 27 Nov 2019 14:19:41 -0600 Subject: [PATCH 5/8] update getSetting calls to get ints and bools where needed --- default.py | 4 ++-- resources/lib/backup.py | 10 +++++----- resources/lib/progressbar.py | 4 ++-- scheduler.py | 34 +++++++++++++++++----------------- 4 files changed, 26 insertions(+), 26 deletions(-) diff --git a/default.py b/default.py index 7c6c190..a79c28f 100644 --- a/default.py +++ b/default.py @@ -39,7 +39,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 +53,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()): diff --git a/resources/lib/backup.py b/resources/lib/backup.py index a040885..77fd8de 100644 --- a/resources/lib/backup.py +++ b/resources/lib/backup.py @@ -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'))): @@ -438,7 +438,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 diff --git a/resources/lib/progressbar.py b/resources/lib/progressbar.py index baafb81..8320bcd 100644 --- a/resources/lib/progressbar.py +++ b/resources/lib/progressbar.py @@ -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: diff --git a/scheduler.py b/scheduler.py index 204aeab..b6cb429 100644 --- a/scheduler.py +++ b/scheduler.py @@ -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") From 8d66fa6a9f1a41ffbddd746d14f6ff5bb161a896 Mon Sep 17 00:00:00 2001 From: Rob Weber Date: Wed, 27 Nov 2019 14:24:35 -0600 Subject: [PATCH 6/8] part of #159 - this will get rid of the most significant logging and keep the essentials --- resources/lib/backup.py | 13 +++++++++---- resources/settings.xml | 4 +++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/resources/lib/backup.py b/resources/lib/backup.py index 77fd8de..e2d5869 100644 --- a/resources/lib/backup.py +++ b/resources/lib/backup.py @@ -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:]) @@ -552,7 +554,9 @@ class FileManager: self.walkTree(xbmc.translatePath(aDir['path']), aDir['recurse']) def walkTree(self, directory, recurse=True): - utils.log('walking ' + directory + ', recurse: ' + str(recurse)) + if(utils.getSettingBool('verbose_logging')): + utils.log('walking ' + directory + ', recurse: ' + str(recurse)) + if(directory[-1:] == '/' or directory[-1:] == '\\'): directory = directory[:-1] @@ -593,11 +597,12 @@ class FileManager: def addFile(self, filename): # write the full remote path name of this file - utils.log("Add File: " + filename) + 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] diff --git a/resources/settings.xml b/resources/settings.xml index 12189a7..7e19f0c 100644 --- a/resources/settings.xml +++ b/resources/settings.xml @@ -1,9 +1,11 @@ - + + + From 5e2d0994480a52700855ab4da6e3e90378f5a7ee Mon Sep 17 00:00:00 2001 From: Rob Weber Date: Wed, 4 Dec 2019 11:31:00 -0600 Subject: [PATCH 7/8] fixes per enen92 --- addon.xml | 13 ++++++------- default.py | 1 - resources/__init__.py | 0 resources/lib/__init__.py | 0 resources/lib/advanced_editor.py | 9 +++++---- resources/lib/backup.py | 10 ++++------ resources/lib/tinyurl.py | 2 +- 7 files changed, 16 insertions(+), 19 deletions(-) delete mode 100644 resources/__init__.py delete mode 100644 resources/lib/__init__.py diff --git a/addon.xml b/addon.xml index e879414..10093e4 100644 --- a/addon.xml +++ b/addon.xml @@ -80,12 +80,10 @@ 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. 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. 你是否经常折腾你的 Kodi,因而希望能够有个备份?你可以把资料库、播放列表、缩略图、插件和其他配置细节导出到 Kodi 可以写入的任意位置。备份可以按需运行或通过计划任务执行。 - all - The MIT License + MIT https://forum.kodi.tv/showthread.php?tid=129499 https://github.com/robweber/xbmcbackup - resources/images/icon.png resources/images/screenshot1.png @@ -93,10 +91,11 @@ resources/images/screenshot3.png resources/images/screenshot4.png - 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 + Version 1.6.0 +- Backups/Restores now use the concept of a Set to define groups of related files. Restores can restore one set or all sets within a backup archive (no more all or nothing restores) +- Added a new Advanced Editor script for more dynamic included/excluded directories based on a JSON formatted file +- Fixed guisettings restores +- Removed GoogleDrive support - Python 3 compatibility was an issue diff --git a/default.py b/default.py index 7c6c190..40b4422 100644 --- a/default.py +++ b/default.py @@ -1,4 +1,3 @@ -import sys import xbmc import xbmcgui import resources.lib.utils as utils diff --git a/resources/__init__.py b/resources/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/resources/lib/__init__.py b/resources/lib/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/resources/lib/advanced_editor.py b/resources/lib/advanced_editor.py index 6332278..23a0885 100644 --- a/resources/lib/advanced_editor.py +++ b/resources/lib/advanced_editor.py @@ -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) diff --git a/resources/lib/backup.py b/resources/lib/backup.py index a040885..a8b9f46 100644 --- a/resources/lib/backup.py +++ b/resources/lib/backup.py @@ -522,14 +522,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) diff --git a/resources/lib/tinyurl.py b/resources/lib/tinyurl.py index 7f82053..efb769d 100644 --- a/resources/lib/tinyurl.py +++ b/resources/lib/tinyurl.py @@ -9,4 +9,4 @@ def shorten(aUrl): data = req.read() # should be a tiny url - return str(data) + return data From 710bcd08f874037aae89351a158136e15e1b0c40 Mon Sep 17 00:00:00 2001 From: Rob Weber Date: Wed, 4 Dec 2019 11:38:25 -0600 Subject: [PATCH 8/8] pep8 fixes --- .travis.yml | 2 +- resources/lib/advanced_editor.py | 8 ++++---- resources/lib/backup.py | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7651c98..7484677 100644 --- a/.travis.yml +++ b/.travis.yml @@ -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 \ No newline at end of file diff --git a/resources/lib/advanced_editor.py b/resources/lib/advanced_editor.py index 23a0885..31acc7a 100644 --- a/resources/lib/advanced_editor.py +++ b/resources/lib/advanced_editor.py @@ -181,14 +181,14 @@ class AdvancedBackupEditor: while(exitCondition != -1): # load the custom paths listItem = xbmcgui.ListItem(utils.getString(30126), '') - listItem.setArt({'icon': os.path.join(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': os.path.join(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 @@ -227,7 +227,7 @@ class AdvancedBackupEditor: shouldContinue = self.dialog.yesno(utils.getString(30139), utils.getString(30140), utils.getString(30141)) if(shouldContinue): - 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')) + 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) diff --git a/resources/lib/backup.py b/resources/lib/backup.py index a8b9f46..58b5761 100644 --- a/resources/lib/backup.py +++ b/resources/lib/backup.py @@ -522,7 +522,7 @@ class XbmcBackup: return result def _createResumeBackupFile(self): - with xbmcvfs.File(xbmc.translatePath(utils.data_dir() + "resume.txt"), 'w') as f: + with xbmcvfs.File(xbmc.translatePath(utils.data_dir() + "resume.txt"), 'w') as f: f.write(self.restore_point) def _readBackupConfig(self, aFile):