import xbmc import xbmcvfs import xbmcgui from datetime import datetime import time 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.getSetting("enable_scheduler") self.next_run_path = xbmc.translatePath(utils.data_dir()) + 'next_run.txt' if(self.enabled == "true"): #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.getSetting('schedule_miss') == 'true'): utils.log("scheduled backup was missed, doing it now...") progress_mode = int(utils.getSetting('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(int(utils.getSetting('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 == "true"): #scheduler is still on now = time.time() if(self.next_run <= now): progress_mode = int(utils.getSetting('progress_mode')) self.doScheduledBackup(progress_mode) #check if we should shut the computer down if(utils.getSetting("cron_shutdown") == 'true'): #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(int(utils.getSetting('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): #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 = int(utils.getSetting('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.getSetting("enable_scheduler") if(current_enabled == "true" and self.enabled == "false"): #scheduler was just turned on self.enabled = current_enabled self.setup() elif (current_enabled == "false" and self.enabled == "true"): #schedule was turn off self.enabled = current_enabled if(self.enabled == "true"): #always recheck the next run time after an update self.findNextRun(time.time()) def parseSchedule(self): schedule_type = int(utils.getSetting("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()