moved scheduler to resources/lib and created non-complex entry point

This commit is contained in:
Rob Weber 2019-12-17 15:02:07 -06:00
parent b1f6d36d73
commit 04bac77690
4 changed files with 204 additions and 202 deletions

View File

@ -1,6 +1,6 @@
<?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.1~beta2" provider-name="robweber"> name="Backup" version="1.6.1~beta3" 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" />
@ -10,7 +10,7 @@
<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="scheduler.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>
<summary lang="be_BY">Backup and restore your Kodi database and configuration files in the event of a crash or file corruption.</summary> <summary lang="be_BY">Backup and restore your Kodi database and configuration files in the event of a crash or file corruption.</summary>

View File

@ -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 - display every file transfered in progress bar, not just directory
- base progress bar percent on transfer size, not total files - base progress bar percent on transfer size, not total files
- changed getSettings where needed to getSettingBool and getSettingInt - 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 ## [Version 1.6.0](https://github.com/robweber/xbmcbackup/compare/krypton-1.5.2...robweber:matrix-1.6.0) - 2019-11-26

View File

@ -1,200 +1,197 @@
import time import time
from datetime import datetime from datetime import datetime
import xbmc import xbmc
import xbmcvfs import xbmcvfs
import xbmcgui import xbmcgui
import resources.lib.utils as utils from . import utils as utils
from resources.lib.croniter import croniter from resources.lib.croniter import croniter
from resources.lib.backup import XbmcBackup from resources.lib.backup import XbmcBackup
UPGRADE_INT = 2 # to keep track of any upgrade notifications UPGRADE_INT = 2 # to keep track of any upgrade notifications
class BackupScheduler: class BackupScheduler:
monitor = None monitor = None
enabled = "false" enabled = "false"
next_run = 0 next_run = 0
next_run_path = None next_run_path = None
restore_point = None restore_point = None
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 = xbmc.translatePath(utils.data_dir()) + 'next_run.txt'
if(self.enabled): if(self.enabled):
# sleep for 2 minutes so Kodi can start and time can update correctly # sleep for 2 minutes so Kodi can start and time can update correctly
xbmc.Monitor().waitForAbort(120) xbmc.Monitor().waitForAbort(120)
nr = 0 nr = 0
if(xbmcvfs.exists(self.next_run_path)): if(xbmcvfs.exists(self.next_run_path)):
fh = xbmcvfs.File(self.next_run_path) fh = xbmcvfs.File(self.next_run_path)
try: try:
# check if we saved a run time from the last run # check if we saved a run time from the last run
nr = float(fh.read()) nr = float(fh.read())
except ValueError: except ValueError:
nr = 0 nr = 0
fh.close() fh.close()
# if we missed and the user wants to play catch-up # if we missed and the user wants to play catch-up
if(0 < nr <= time.time() and utils.getSettingBool('schedule_miss')): if(0 < nr <= time.time() and utils.getSettingBool('schedule_miss')):
utils.log("scheduled backup was missed, doing it now...") utils.log("scheduled backup was missed, doing it now...")
progress_mode = utils.getSettingInt('progress_mode') progress_mode = utils.getSettingInt('progress_mode')
if(progress_mode == 0): if(progress_mode == 0):
progress_mode = 1 # Kodi just started, don't block it with a foreground progress bar progress_mode = 1 # Kodi just started, don't block it with a foreground progress bar
self.doScheduledBackup(progress_mode) self.doScheduledBackup(progress_mode)
self.setup() self.setup()
def setup(self): def setup(self):
# scheduler was turned on, find next run time # scheduler was turned on, find next run time
utils.log("scheduler enabled, finding next run time") utils.log("scheduler enabled, finding next run time")
self.findNextRun(time.time()) self.findNextRun(time.time())
def start(self): def start(self):
# display upgrade messages if they exist # display upgrade messages if they exist
if(utils.getSettingInt('upgrade_notes') < UPGRADE_INT): if(utils.getSettingInt('upgrade_notes') < UPGRADE_INT):
xbmcgui.Dialog().ok(utils.getString(30010), utils.getString(30132)) xbmcgui.Dialog().ok(utils.getString(30010), utils.getString(30132))
utils.setSetting('upgrade_notes', str(UPGRADE_INT)) utils.setSetting('upgrade_notes', str(UPGRADE_INT))
# check if a backup should be resumed # check if a backup should be resumed
resumeRestore = self._resumeCheck() resumeRestore = self._resumeCheck()
if(resumeRestore): if(resumeRestore):
restore = XbmcBackup() restore = XbmcBackup()
restore.selectRestore(self.restore_point) restore.selectRestore(self.restore_point)
# skip the advanced settings check # skip the advanced settings check
restore.skipAdvanced() restore.skipAdvanced()
restore.restore() restore.restore()
while(not self.monitor.abortRequested()): while(not self.monitor.abortRequested()):
if(self.enabled): if(self.enabled):
# scheduler is still on # scheduler is still on
now = time.time() now = time.time()
if(self.next_run <= now): if(self.next_run <= now):
progress_mode = utils.getSettingInt('progress_mode') progress_mode = utils.getSettingInt('progress_mode')
self.doScheduledBackup(progress_mode) self.doScheduledBackup(progress_mode)
# check if we should shut the computer down # check if we should shut the computer down
if(utils.getSettingBool("cron_shutdown")): if(utils.getSettingBool("cron_shutdown")):
# wait 10 seconds to make sure all backup processes and files are completed # wait 10 seconds to make sure all backup processes and files are completed
time.sleep(10) time.sleep(10)
xbmc.executebuiltin('ShutDown()') xbmc.executebuiltin('ShutDown()')
else: else:
# find the next run time like normal # find the next run time like normal
self.findNextRun(now) self.findNextRun(now)
xbmc.sleep(500) xbmc.sleep(500)
# delete monitor to free up memory # delete monitor to free up memory
del self.monitor del self.monitor
def doScheduledBackup(self, progress_mode): def doScheduledBackup(self, progress_mode):
if(progress_mode != 2): if(progress_mode != 2):
utils.showNotification(utils.getString(30053)) utils.showNotification(utils.getString(30053))
backup = XbmcBackup() backup = XbmcBackup()
if(backup.remoteConfigured()): if(backup.remoteConfigured()):
if(utils.getSettingInt('progress_mode') in [0, 1]): if(utils.getSettingInt('progress_mode') in [0, 1]):
backup.backup(True) backup.backup(True)
else: else:
backup.backup(False) backup.backup(False)
# check if this is a "one-off" # check if this is a "one-off"
if(utils.getSettingInt("schedule_interval") == 0): if(utils.getSettingInt("schedule_interval") == 0):
# disable the scheduler after this run # disable the scheduler after this run
self.enabled = False self.enabled = False
utils.setSetting('enable_scheduler', 'false') utils.setSetting('enable_scheduler', 'false')
else: else:
utils.showNotification(utils.getString(30045)) utils.showNotification(utils.getString(30045))
def findNextRun(self, now): def findNextRun(self, now):
progress_mode = utils.getSettingInt('progress_mode') progress_mode = utils.getSettingInt('progress_mode')
# find the cron expression and get the next run time # find the cron expression and get the next run time
cron_exp = self.parseSchedule() cron_exp = self.parseSchedule()
cron_ob = croniter(cron_exp, datetime.fromtimestamp(now)) cron_ob = croniter(cron_exp, datetime.fromtimestamp(now))
new_run_time = cron_ob.get_next(float) new_run_time = cron_ob.get_next(float)
if(new_run_time != self.next_run): if(new_run_time != self.next_run):
self.next_run = new_run_time self.next_run = new_run_time
utils.log("scheduler will run again on " + utils.getRegionalTimestamp(datetime.fromtimestamp(self.next_run), ['dateshort', 'time'])) utils.log("scheduler will run again on " + utils.getRegionalTimestamp(datetime.fromtimestamp(self.next_run), ['dateshort', 'time']))
# write the next time to a file # write the next time to a file
fh = xbmcvfs.File(self.next_run_path, 'w') fh = xbmcvfs.File(self.next_run_path, 'w')
fh.write(str(self.next_run)) fh.write(str(self.next_run))
fh.close() fh.close()
# only show when not in silent mode # only show when not in silent mode
if(progress_mode != 2): if(progress_mode != 2):
utils.showNotification(utils.getString(30081) + " " + utils.getRegionalTimestamp(datetime.fromtimestamp(self.next_run), ['dateshort', 'time'])) utils.showNotification(utils.getString(30081) + " " + utils.getRegionalTimestamp(datetime.fromtimestamp(self.next_run), ['dateshort', 'time']))
def settingsChanged(self): def settingsChanged(self):
current_enabled = utils.getSettingBool("enable_scheduler") current_enabled = utils.getSettingBool("enable_scheduler")
if(current_enabled and not self.enabled): if(current_enabled and not self.enabled):
# scheduler was just turned on # scheduler was just turned on
self.enabled = current_enabled self.enabled = current_enabled
self.setup() self.setup()
elif (not current_enabled and self.enabled): elif (not current_enabled and self.enabled):
# schedule was turn off # schedule was turn off
self.enabled = current_enabled self.enabled = current_enabled
if(self.enabled): if(self.enabled):
# always recheck the next run time after an update # always recheck the next run time after an update
self.findNextRun(time.time()) self.findNextRun(time.time())
def parseSchedule(self): def parseSchedule(self):
schedule_type = utils.getSettingInt("schedule_interval") schedule_type = utils.getSettingInt("schedule_interval")
cron_exp = utils.getSetting("cron_schedule") cron_exp = utils.getSetting("cron_schedule")
hour_of_day = utils.getSetting("schedule_time") hour_of_day = utils.getSetting("schedule_time")
hour_of_day = int(hour_of_day[0:2]) hour_of_day = int(hour_of_day[0:2])
if(schedule_type == 0 or schedule_type == 1): if(schedule_type == 0 or schedule_type == 1):
# every day # every day
cron_exp = "0 " + str(hour_of_day) + " * * *" cron_exp = "0 " + str(hour_of_day) + " * * *"
elif(schedule_type == 2): elif(schedule_type == 2):
# once a week # once a week
day_of_week = utils.getSetting("day_of_week") day_of_week = utils.getSetting("day_of_week")
cron_exp = "0 " + str(hour_of_day) + " * * " + day_of_week cron_exp = "0 " + str(hour_of_day) + " * * " + day_of_week
elif(schedule_type == 3): elif(schedule_type == 3):
# first day of month # first day of month
cron_exp = "0 " + str(hour_of_day) + " 1 * *" cron_exp = "0 " + str(hour_of_day) + " 1 * *"
return cron_exp return cron_exp
def _resumeCheck(self): def _resumeCheck(self):
shouldContinue = False shouldContinue = False
if(xbmcvfs.exists(xbmc.translatePath(utils.data_dir() + "resume.txt"))): if(xbmcvfs.exists(xbmc.translatePath(utils.data_dir() + "resume.txt"))):
rFile = xbmcvfs.File(xbmc.translatePath(utils.data_dir() + "resume.txt"), 'r') rFile = xbmcvfs.File(xbmc.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(xbmc.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
class UpdateMonitor(xbmc.Monitor): class UpdateMonitor(xbmc.Monitor):
update_method = None update_method = None
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
xbmc.Monitor.__init__(self) xbmc.Monitor.__init__(self)
self.update_method = kwargs['update_method'] self.update_method = kwargs['update_method']
def onSettingsChanged(self): def onSettingsChanged(self):
self.update_method() self.update_method()
BackupScheduler().start()

4
service.py Normal file
View File

@ -0,0 +1,4 @@
from resources.lib.scheduler import BackupScheduler
# start the backup scheduler
BackupScheduler().start()