From 04bac77690d750f26bfd956d983e275591fa82b0 Mon Sep 17 00:00:00 2001 From: Rob Weber Date: Tue, 17 Dec 2019 15:02:07 -0600 Subject: [PATCH] moved scheduler to resources/lib and created non-complex entry point --- addon.xml | 4 +- changelog.md | 1 + scheduler.py => resources/lib/scheduler.py | 397 ++++++++++----------- service.py | 4 + 4 files changed, 204 insertions(+), 202 deletions(-) rename scheduler.py => resources/lib/scheduler.py (96%) create mode 100644 service.py diff --git a/addon.xml b/addon.xml index e39a1ed..d9be9a6 100644 --- a/addon.xml +++ b/addon.xml @@ -1,6 +1,6 @@  + name="Backup" version="1.6.1~beta3" provider-name="robweber"> @@ -10,7 +10,7 @@ executable - + إنسخ إحتياطياً قاعده بيانات إكس بى إم سى وملفات اﻹعدادات فى حاله وقوع إنهيار مع إمكانيه اﻹسترجاع Backup and restore your Kodi database and configuration files in the event of a crash or file corruption. diff --git a/changelog.md b/changelog.md index 54215f7..fd6d6e5 100644 --- a/changelog.md +++ b/changelog.md @@ -19,6 +19,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - display every file transfered in progress bar, not just directory - base progress bar percent on transfer size, not total files - changed getSettings where needed to getSettingBool and getSettingInt + - use service.py to start scheduler, moving scheduler to resources/lib/scheduler.py Kodi doesn't cache files in the root directory ## [Version 1.6.0](https://github.com/robweber/xbmcbackup/compare/krypton-1.5.2...robweber:matrix-1.6.0) - 2019-11-26 diff --git a/scheduler.py b/resources/lib/scheduler.py similarity index 96% rename from scheduler.py rename to resources/lib/scheduler.py index b6cb429..8147309 100644 --- a/scheduler.py +++ b/resources/lib/scheduler.py @@ -1,200 +1,197 @@ -import time -from datetime import datetime -import xbmc -import xbmcvfs -import xbmcgui -import resources.lib.utils as utils -from resources.lib.croniter import croniter -from resources.lib.backup import XbmcBackup - -UPGRADE_INT = 2 # to keep track of any upgrade notifications - - -class BackupScheduler: - monitor = None - enabled = "false" - next_run = 0 - next_run_path = None - restore_point = None - - def __init__(self): - self.monitor = UpdateMonitor(update_method=self.settingsChanged) - self.enabled = utils.getSettingBool("enable_scheduler") - self.next_run_path = xbmc.translatePath(utils.data_dir()) + 'next_run.txt' - - if(self.enabled): - - # sleep for 2 minutes so Kodi can start and time can update correctly - xbmc.Monitor().waitForAbort(120) - - nr = 0 - if(xbmcvfs.exists(self.next_run_path)): - - fh = xbmcvfs.File(self.next_run_path) - try: - # check if we saved a run time from the last run - nr = float(fh.read()) - except ValueError: - nr = 0 - - fh.close() - - # if we missed and the user wants to play catch-up - if(0 < nr <= time.time() and utils.getSettingBool('schedule_miss')): - utils.log("scheduled backup was missed, doing it now...") - 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 - - self.doScheduledBackup(progress_mode) - - self.setup() - - def setup(self): - # scheduler was turned on, find next run time - utils.log("scheduler enabled, finding next run time") - self.findNextRun(time.time()) - - def start(self): - - # display upgrade messages if they exist - if(utils.getSettingInt('upgrade_notes') < UPGRADE_INT): - xbmcgui.Dialog().ok(utils.getString(30010), utils.getString(30132)) - utils.setSetting('upgrade_notes', str(UPGRADE_INT)) - - # check if a backup should be resumed - resumeRestore = self._resumeCheck() - - if(resumeRestore): - restore = XbmcBackup() - restore.selectRestore(self.restore_point) - # skip the advanced settings check - restore.skipAdvanced() - restore.restore() - - while(not self.monitor.abortRequested()): - - if(self.enabled): - # scheduler is still on - now = time.time() - - if(self.next_run <= now): - progress_mode = utils.getSettingInt('progress_mode') - self.doScheduledBackup(progress_mode) - - # check if we should shut the computer down - if(utils.getSettingBool("cron_shutdown")): - # wait 10 seconds to make sure all backup processes and files are completed - time.sleep(10) - xbmc.executebuiltin('ShutDown()') - else: - # find the next run time like normal - self.findNextRun(now) - - xbmc.sleep(500) - - # delete monitor to free up memory - del self.monitor - - def doScheduledBackup(self, progress_mode): - if(progress_mode != 2): - utils.showNotification(utils.getString(30053)) - - backup = XbmcBackup() - - if(backup.remoteConfigured()): - - if(utils.getSettingInt('progress_mode') in [0, 1]): - backup.backup(True) - else: - backup.backup(False) - - # check if this is a "one-off" - if(utils.getSettingInt("schedule_interval") == 0): - # disable the scheduler after this run - self.enabled = False - utils.setSetting('enable_scheduler', 'false') - else: - utils.showNotification(utils.getString(30045)) - - def findNextRun(self, now): - progress_mode = utils.getSettingInt('progress_mode') - - # find the cron expression and get the next run time - cron_exp = self.parseSchedule() - - cron_ob = croniter(cron_exp, datetime.fromtimestamp(now)) - new_run_time = cron_ob.get_next(float) - - if(new_run_time != self.next_run): - self.next_run = new_run_time - utils.log("scheduler will run again on " + utils.getRegionalTimestamp(datetime.fromtimestamp(self.next_run), ['dateshort', 'time'])) - - # write the next time to a file - fh = xbmcvfs.File(self.next_run_path, 'w') - fh.write(str(self.next_run)) - fh.close() - - # only show when not in silent mode - if(progress_mode != 2): - utils.showNotification(utils.getString(30081) + " " + utils.getRegionalTimestamp(datetime.fromtimestamp(self.next_run), ['dateshort', 'time'])) - - def settingsChanged(self): - current_enabled = utils.getSettingBool("enable_scheduler") - - if(current_enabled and not self.enabled): - # scheduler was just turned on - self.enabled = current_enabled - self.setup() - elif (not current_enabled and self.enabled): - # schedule was turn off - self.enabled = current_enabled - - if(self.enabled): - # always recheck the next run time after an update - self.findNextRun(time.time()) - - def parseSchedule(self): - schedule_type = utils.getSettingInt("schedule_interval") - cron_exp = utils.getSetting("cron_schedule") - - hour_of_day = utils.getSetting("schedule_time") - hour_of_day = int(hour_of_day[0:2]) - if(schedule_type == 0 or schedule_type == 1): - # every day - cron_exp = "0 " + str(hour_of_day) + " * * *" - elif(schedule_type == 2): - # once a week - day_of_week = utils.getSetting("day_of_week") - cron_exp = "0 " + str(hour_of_day) + " * * " + day_of_week - elif(schedule_type == 3): - # first day of month - cron_exp = "0 " + str(hour_of_day) + " 1 * *" - - return cron_exp - - def _resumeCheck(self): - shouldContinue = False - if(xbmcvfs.exists(xbmc.translatePath(utils.data_dir() + "resume.txt"))): - rFile = xbmcvfs.File(xbmc.translatePath(utils.data_dir() + "resume.txt"), 'r') - self.restore_point = rFile.read() - rFile.close() - xbmcvfs.delete(xbmc.translatePath(utils.data_dir() + "resume.txt")) - shouldContinue = xbmcgui.Dialog().yesno(utils.getString(30042), utils.getString(30043), utils.getString(30044)) - - return shouldContinue - - -class UpdateMonitor(xbmc.Monitor): - update_method = None - - def __init__(self, *args, **kwargs): - xbmc.Monitor.__init__(self) - self.update_method = kwargs['update_method'] - - def onSettingsChanged(self): - self.update_method() - - -BackupScheduler().start() +import time +from datetime import datetime +import xbmc +import xbmcvfs +import xbmcgui +from . import utils as utils +from resources.lib.croniter import croniter +from resources.lib.backup import XbmcBackup + +UPGRADE_INT = 2 # to keep track of any upgrade notifications + + +class BackupScheduler: + monitor = None + enabled = "false" + next_run = 0 + next_run_path = None + restore_point = None + + def __init__(self): + self.monitor = UpdateMonitor(update_method=self.settingsChanged) + self.enabled = utils.getSettingBool("enable_scheduler") + self.next_run_path = xbmc.translatePath(utils.data_dir()) + 'next_run.txt' + + if(self.enabled): + + # sleep for 2 minutes so Kodi can start and time can update correctly + xbmc.Monitor().waitForAbort(120) + + nr = 0 + if(xbmcvfs.exists(self.next_run_path)): + + fh = xbmcvfs.File(self.next_run_path) + try: + # check if we saved a run time from the last run + nr = float(fh.read()) + except ValueError: + nr = 0 + + fh.close() + + # if we missed and the user wants to play catch-up + if(0 < nr <= time.time() and utils.getSettingBool('schedule_miss')): + utils.log("scheduled backup was missed, doing it now...") + 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 + + self.doScheduledBackup(progress_mode) + + self.setup() + + def setup(self): + # scheduler was turned on, find next run time + utils.log("scheduler enabled, finding next run time") + self.findNextRun(time.time()) + + def start(self): + + # display upgrade messages if they exist + if(utils.getSettingInt('upgrade_notes') < UPGRADE_INT): + xbmcgui.Dialog().ok(utils.getString(30010), utils.getString(30132)) + utils.setSetting('upgrade_notes', str(UPGRADE_INT)) + + # check if a backup should be resumed + resumeRestore = self._resumeCheck() + + if(resumeRestore): + restore = XbmcBackup() + restore.selectRestore(self.restore_point) + # skip the advanced settings check + restore.skipAdvanced() + restore.restore() + + while(not self.monitor.abortRequested()): + + if(self.enabled): + # scheduler is still on + now = time.time() + + if(self.next_run <= now): + progress_mode = utils.getSettingInt('progress_mode') + self.doScheduledBackup(progress_mode) + + # check if we should shut the computer down + if(utils.getSettingBool("cron_shutdown")): + # wait 10 seconds to make sure all backup processes and files are completed + time.sleep(10) + xbmc.executebuiltin('ShutDown()') + else: + # find the next run time like normal + self.findNextRun(now) + + xbmc.sleep(500) + + # delete monitor to free up memory + del self.monitor + + def doScheduledBackup(self, progress_mode): + if(progress_mode != 2): + utils.showNotification(utils.getString(30053)) + + backup = XbmcBackup() + + if(backup.remoteConfigured()): + + if(utils.getSettingInt('progress_mode') in [0, 1]): + backup.backup(True) + else: + backup.backup(False) + + # check if this is a "one-off" + if(utils.getSettingInt("schedule_interval") == 0): + # disable the scheduler after this run + self.enabled = False + utils.setSetting('enable_scheduler', 'false') + else: + utils.showNotification(utils.getString(30045)) + + def findNextRun(self, now): + progress_mode = utils.getSettingInt('progress_mode') + + # find the cron expression and get the next run time + cron_exp = self.parseSchedule() + + cron_ob = croniter(cron_exp, datetime.fromtimestamp(now)) + new_run_time = cron_ob.get_next(float) + + if(new_run_time != self.next_run): + self.next_run = new_run_time + utils.log("scheduler will run again on " + utils.getRegionalTimestamp(datetime.fromtimestamp(self.next_run), ['dateshort', 'time'])) + + # write the next time to a file + fh = xbmcvfs.File(self.next_run_path, 'w') + fh.write(str(self.next_run)) + fh.close() + + # only show when not in silent mode + if(progress_mode != 2): + utils.showNotification(utils.getString(30081) + " " + utils.getRegionalTimestamp(datetime.fromtimestamp(self.next_run), ['dateshort', 'time'])) + + def settingsChanged(self): + current_enabled = utils.getSettingBool("enable_scheduler") + + if(current_enabled and not self.enabled): + # scheduler was just turned on + self.enabled = current_enabled + self.setup() + elif (not current_enabled and self.enabled): + # schedule was turn off + self.enabled = current_enabled + + if(self.enabled): + # always recheck the next run time after an update + self.findNextRun(time.time()) + + def parseSchedule(self): + schedule_type = utils.getSettingInt("schedule_interval") + cron_exp = utils.getSetting("cron_schedule") + + hour_of_day = utils.getSetting("schedule_time") + hour_of_day = int(hour_of_day[0:2]) + if(schedule_type == 0 or schedule_type == 1): + # every day + cron_exp = "0 " + str(hour_of_day) + " * * *" + elif(schedule_type == 2): + # once a week + day_of_week = utils.getSetting("day_of_week") + cron_exp = "0 " + str(hour_of_day) + " * * " + day_of_week + elif(schedule_type == 3): + # first day of month + cron_exp = "0 " + str(hour_of_day) + " 1 * *" + + return cron_exp + + def _resumeCheck(self): + shouldContinue = False + if(xbmcvfs.exists(xbmc.translatePath(utils.data_dir() + "resume.txt"))): + rFile = xbmcvfs.File(xbmc.translatePath(utils.data_dir() + "resume.txt"), 'r') + self.restore_point = rFile.read() + rFile.close() + xbmcvfs.delete(xbmc.translatePath(utils.data_dir() + "resume.txt")) + shouldContinue = xbmcgui.Dialog().yesno(utils.getString(30042), utils.getString(30043), utils.getString(30044)) + + return shouldContinue + + +class UpdateMonitor(xbmc.Monitor): + update_method = None + + def __init__(self, *args, **kwargs): + xbmc.Monitor.__init__(self) + self.update_method = kwargs['update_method'] + + def onSettingsChanged(self): + self.update_method() diff --git a/service.py b/service.py new file mode 100644 index 0000000..50614a3 --- /dev/null +++ b/service.py @@ -0,0 +1,4 @@ +from resources.lib.scheduler import BackupScheduler + +# start the backup scheduler +BackupScheduler().start() \ No newline at end of file