# Copyright (C) 2024 Lunatixz # # # This file is part of PseudoTV Live. # # PseudoTV Live is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # PseudoTV Live is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with PseudoTV Live. If not, see . # -*- coding: utf-8 -*- from globals import * from seasonal import Seasonal #todo pinlock # Enable_Fillers # Enable_Preroll # Random_Pre_Chance # Enable_Postroll # Random_Post_Chance # Build_Post_Folders # Resource_Trailers # Include_Trailers_KODI # Resource_Overlay # Resource_Ratings # Resource_Bumpers # Resource_Adverts class RulesList: def __init__(self, channels=None): self.log('__init__') self.ruleList = [BaseRule(), ShowChannelBug(), ShowOnNext(), SetScreenVingette(), MST3k(), DisableOverlay(), ForceSubtitles(), DisableTrakt(), RollbackPlaycount(), DisableRestart(), DisableOnChange(), # QuotaOptions(), PageLimit(), DurationOptions(), IncludeOptions(), PreRoll(), PostRoll(), InterleaveValue(), ProvisionalRule(), HandleMethodOrder(), ForceEpisode(), ForceRandom(), EvenShowsRule(), PauseRule()] if channels: self.ruleItems = self.loadRules(channels) else: self.ruleItems = [] def log(self, msg, level=xbmc.LOGDEBUG): log('%s: %s'%(self.__class__.__name__,msg),level) def getTemplate(self) -> dict: return getJSON(RULEFLE_ITEM).copy() def dumpRules(self, rules={}): #convert rule instances to channel format nrules = dict() if not list(rules.items()): return None for key, rule in list(rules.items()): ritem = dict() ritem[key] = {"values":dict()} for idx, value in enumerate(rule.optionValues): ritem[key]["values"][str(idx)] = value nrules.update(ritem) return nrules def loadRules(self, channels=None, append=False, incRez=True): #load channel rules and their instances. if channels is None: from channels import Channels channel = Channels() channels = channel.getChannels() del channel def _load(tmpruleList, citem={}): ruleList = {} chrules = citem.get('rules',{}) if not append and len(chrules) == 0: return None for rule in tmpruleList: try: if not incRez and rule.ignore: continue try: chrule = chrules.get(str(rule.myId)) #temp fix.issue after converting list to dict in channels.json except: chrule = {} if chrule: ruleInstance = rule.copy() for key, value in list(chrule.get('values',{}).items()): ruleInstance.optionValues[int(key)] = value ruleList.update({str(rule.myId):ruleInstance}) elif append: ruleList.update({str(rule.myId):rule.copy()}) except Exception as e: log('[%s] loadRules: _load failed! %s\nchrule = %s'%(citem.get('id'), e, chrule), xbmc.LOGERROR) self.log('[%s] loadRules: append = %s, incRez = %s, rule myIds = %s'%(citem.get('id'), append, incRez,list(ruleList.keys()))) rules.update({citem.get('id'):ruleList}) rules = dict() tmpruleList = self.ruleList.copy() tmpruleList.pop(0) #remove boilerplate baseRule() [_load(tmpruleList,channel) for channel in channels] return rules def allRules(self): #load all rules. self.log('allRules') tmpruleList = self.ruleList.copy() tmpruleList.pop(0) #remove boilerplate baseRule() return [rule.copy() for rule in tmpruleList] def runActions(self, action, citem={}, parameter=None, inherited=None): if inherited is None: inherited = self self.log("[%s] runActions, %s action = %s"%(citem.get('id'),inherited.__class__.__name__,action)) rules = self.ruleItems.get(citem.get('id','')) if not rules: rules = (self.loadRules([citem]).get(citem.get('id','')) or {}) for myId, rule in list(sorted(rules.items())): if action in rule.actions: self.log("[%s] runActions, %s performing channel rule: %s"%(citem.get('id'),inherited.__class__.__name__,rule.name)) try: parameter = rule.runAction(action, citem, parameter, inherited) except Exception as e: log('[%s] runActions, failed! %s\nrule = %s'%(citem.get('id'),e,rule), xbmc.LOGERROR) return parameter class BaseRule: dialog = Dialog() def __init__(self): self.myId = 0 self.ignore = False #ignore from manager options, reserved for auto-tuning self.exclude = False #applies only to db queries not smart-playlists self.name = "" self.description = "" self.optionLabels = [] self.optionValues = [] self.optionDescriptions = [] self.actions = [] self.selectBoxOptions = [] self.storedValues = [] def getTitle(self): return self.name def onAction(self, optionindex): return '' def getId(self): return self.myId def runAction(self, actionid, citem, parameter, inherited): return parameter def copy(self): return BaseRule() def log(self, msg, level=xbmc.LOGDEBUG): log('%s: %s'%(self.__class__.__name__,msg),level) def validate(self): pass def reset(self): self.__init__() def validateTextBox(self, optionindex, length): if len(self.optionValues[optionindex]) > length: self.optionValues[optionindex] = self.optionValues[optionindex][:length] def validateTimeBox(self, optionindex): values = [] broken = False try: values.append(int(self.optionValues[optionindex][0])) values.append(int(self.optionValues[optionindex][1])) values.append(int(self.optionValues[optionindex][3])) values.append(int(self.optionValues[optionindex][4])) except: self.optionValues[optionindex] = "00:00" return if values[0] > 2: broken = True if values[0] == 2: if values[1] > 3: broken = True if values[2] > 5: broken = True if broken: self.optionValues[optionindex] = "00:00" return def validateDaysofWeekBox(self, optionindex): self.log("validateDaysofWeekBox") daysofweek = "UMTWHFS" newstr = '' for day in daysofweek: loc = self.optionValues[optionindex].find(day) if loc != -1: newstr += day self.optionValues[optionindex] = newstr def validateRange(self, optionindex, minimum, maximum, default): if int(self.optionValues[optionindex]) < minimum: self.log("Invalid minimum range") self.dialog.notificationDialog(LANGUAGE(32077)%(self.optionLabels[optionindex])) self.optionValues[optionindex] = default return elif int(self.optionValues[optionindex]) > maximum: self.log("Invalid maximum range") self.dialog.notificationDialog(LANGUAGE(32077)%(self.optionLabels[optionindex])) self.optionValues[optionindex] = default return def validateDigitBox(self, optionindex, minimum, maximum, default): if int(self.optionValues[optionindex]) == 0: return try: val = int(self.optionValues[optionindex]) if val >= minimum and val <= maximum: self.optionValues[optionindex] = val return except: pass self.dialog.notificationDialog(LANGUAGE(32077)%(self.optionLabels[optionindex])) self.optionValues[optionindex] = default def onActionToggleBool(self, optionindex): self.log("onActionToggleBool") self.optionValues[optionindex] = not self.optionValues[optionindex] def onActionFunction(self, optionindex): self.log("onActionFunction") value = self.selectBoxOption[optionindex]() if value: self.optionValues[optionindex] = value def onActionPickColor(self, optionindex, colorlist=[], colorfile=""): self.log("onActionPickColor") value = self.dialog.colorDialog(colorlist, self.optionValues[optionindex], colorfile, self.name) if value: self.optionValues[optionindex] = value def onActionTextBox(self, optionindex): self.log("onActionTextBox") value = self.dialog.inputDialog(self.name, default=self.optionValues[optionindex], key=xbmcgui.INPUT_ALPHANUM) if value: self.optionValues[optionindex] = value def onActionDigitBox(self, optionindex): self.log("onActionDigitBox") info = self.dialog.inputDialog(self.optionLabels[optionindex], default=self.optionValues[optionindex], key=xbmcgui.INPUT_NUMERIC) if info != None: self.optionValues[optionindex] = info def onActionTimeBox(self, optionindex): self.log("onActionTimeBox") info = self.dialog.inputDialog(self.optionLabels[optionindex], default=self.optionValues[optionindex], key=xbmcgui.INPUT_NUMERIC) if info != None: if info[0] == ' ': info = info[1:] if len(info) == 4: info = "0" + info self.optionValues[optionindex] = info def onActionSelect(self, optionindex, header=None, preselect=None, useDetails=False, autoclose=SELECT_DELAY, multi=False): self.log("onActionSelect") if header is None: if multi: header = '%s - %s'%(ADDON_NAME,LANGUAGE(32017)%('')) else: header = '%s - %s'%(ADDON_NAME,LANGUAGE(32223)%('')) if isinstance(self.selectBoxOptions[optionindex],dict): selectBoxOptions = list(self.selectBoxOptions[optionindex].keys()) preselect = findItemsInLST(list(self.selectBoxOptions[optionindex].values()), self.optionValues[optionindex]) else: selectBoxOptions = self.selectBoxOptions[optionindex] preselect = findItemsInLST(self.selectBoxOptions[optionindex], self.optionValues[optionindex]) select = self.dialog.selectDialog([str(v).title() for v in selectBoxOptions], header, preselect, useDetails, autoclose, multi) if not select is None: if isinstance(self.selectBoxOptions[optionindex],dict): self.optionValues[optionindex] = self.selectBoxOptions[optionindex].get(selectBoxOptions[select]) else: self.optionValues[optionindex] = selectBoxOptions[select] def onActionBrowse(self, optionindex, type=0, heading=ADDON_NAME, shares='', mask='', useThumbs=True, treatAsFolder=False, multi=False, monitor=False, options=[], exclude=[]): self.log("onActionBrowse") info = self.dialog.browseSources(type, heading, self.optionValues[optionindex], shares, mask, useThumbs, treatAsFolder, multi, monitor, options, exclude) if info is not None: self.optionValues[optionindex] = info def onActionMultiBrowse(self, optionindex, header=ADDON_NAME, exclude=[], monitor=True): self.log("onActionMultiBrowse") info = self.dialog.multiBrowse(self.optionValues[optionindex], header, exclude, monitor) if info is not None: self.optionValues[optionindex] = info def onActionResources(self, optionindex, ftype=''): log("onActionResources") info = self.dialog.browseResources(self.optionValues[optionindex].split('|'), ftype=ftype) if not info is None: self.optionValues[optionindex] = '|'.join(info) #Rules apply sequentially by myId class ShowChannelBug(BaseRule): #OVERLAY RULES [1-49] def __init__(self): self.myId = 1 self.ignore = False self.exclude = False self.name = LANGUAGE(30143) self.description = LANGUAGE(30144) self.optionLabels = [LANGUAGE(30043),LANGUAGE(30112),LANGUAGE(30044),LANGUAGE(30208)] self.optionValues = [SETTINGS.getSettingBool('Enable_ChannelBug'),SETTINGS.getSetting("Channel_Bug_Position_XY"),SETTINGS.getSetting('ChannelBug_Color'),SETTINGS.getSettingBool('Force_Diffuse'),SETTINGS.getSettingInt('ChannelBug_Transparency')] self.optionDescriptions = [LANGUAGE(33043),LANGUAGE(33112),LANGUAGE(33044),LANGUAGE(33209)] self.actions = [RULES_ACTION_OVERLAY_OPEN,RULES_ACTION_OVERLAY_CLOSE] self.selectBoxOptions = [[True,False],[LANGUAGE(30022),LANGUAGE(32136)],"","",list(range(15,51,5))] self.storedValues = [[],[],[],[],[],[]] def log(self, msg, level=xbmc.LOGDEBUG): log('%s: %s'%(self.__class__.__name__,msg),level) def copy(self): return ShowChannelBug() def getTitle(self): return '%s (%s)'%(self.name,{True:LANGUAGE(30184),False:LANGUAGE(30021)}[self.optionValues[0]]) def getPosition(self, optionindex): orgvalue = self.optionValues[optionindex] self.onActionSelect(optionindex, LANGUAGE(32223)%('')) if self.optionValues[optionindex] == self.selectBoxOptions[optionindex][1]: from overlaytool import OverlayTool overlaytool = OverlayTool(OVERLAYTOOL_XML, ADDON_PATH, "default", ADV_RULES=True, Focus_IDX=1, Channel_Bug_Position_XY=self.optionValues[optionindex], ChannelBug_Color=self.optionValues[3]) del overlaytool value = PROPERTIES.getProperty("Channel_Bug_Position_XY") PROPERTIES.clrProperty("Channel_Bug_Position_XY") if value: self.optionValues[optionindex] = value else: self.optionValues[optionindex] = orgvalue elif self.optionValues[optionindex] != self.selectBoxOptions[optionindex][0]: self.optionValues[optionindex] = orgvalue def onAction(self, optionindex): if optionindex == 0: self.onActionToggleBool(optionindex) elif optionindex == 1: self.getPosition(optionindex) elif optionindex == 2: self.onActionPickColor(optionindex) elif optionindex == 3: self.onActionToggleBool(optionindex) elif optionindex == 4: self.onActionSelect(optionindex, LANGUAGE(33209)) return self.optionValues[optionindex] def runAction(self, actionid, citem, parameter, overlay): if actionid == RULES_ACTION_OVERLAY_OPEN: self.storedValues[0] = overlay.enableChannelBug self.storedValues[1] = (overlay.channelBugX, overlay.channelBugY) self.storedValues[2] = overlay.channelBugColor self.storedValues[3] = overlay.channelBugDiffuse self.storedValues[4] = overlay.channelBugFade overlay.enableChannelBug = self.optionValues[0] overlay.channelBugX, overlay.channelBugY = eval(self.optionValues[1]) overlay.channelBugColor = '0x%s'%(self.optionValues[2]) overlay.channelBugDiffuse = self.optionValues[3] overlay.channelBugFade = self.optionValues[4] self.log("runAction, setting enableChannelBug = %s, channelBugColor = %s, channelBugDiffuse = %s"%(overlay.enableChannelBug,(overlay.channelBugX, overlay.channelBugY),overlay.channelBugColor,overlay.channelBugDiffuse,overlay.channelBugFade)) elif actionid == RULES_ACTION_OVERLAY_CLOSE: overlay.enableChannelBug = self.storedValues[0] overlay.channelBugX, overlay.channelBugY = self.storedValues[1] overlay.channelBugColor = self.storedValues[2] overlay.channelBugDiffuse = self.storedValues[3] overlay.channelBugFade = self.storedValues[4] self.log("runAction, restoring enableChannelBug = %s, channelBugColor = %s, channelBugDiffuse = %s"%(overlay.enableChannelBug,(overlay.channelBugX, overlay.channelBugY),overlay.channelBugColor,overlay.channelBugDiffuse,overlay.channelBugFade)) return parameter class ShowOnNext(BaseRule): def __init__(self): self.myId = 2 self.ignore = False self.exclude = False self.name = LANGUAGE(30045) self.description = LANGUAGE(33045) self.optionLabels = [LANGUAGE(30045),LANGUAGE(32229),LANGUAGE(30044),LANGUAGE(30196)] self.optionValues = [bool(SETTINGS.getSettingInt('OnNext_Mode')),SETTINGS.getSetting("OnNext_Position_XY"),SETTINGS.getSettingInt('OnNext_Mode')] self.optionDescriptions = [LANGUAGE(30045),LANGUAGE(33229),LANGUAGE(30196)] self.actions = [RULES_ACTION_PLAYER_START,RULES_ACTION_PLAYER_STOP] self.selectBoxOptions = ["",[LANGUAGE(30022),LANGUAGE(32136)],"",{LANGUAGE(30021):0,LANGUAGE(30193):1,LANGUAGE(30194):2,LANGUAGE(30197):3,LANGUAGE(30195):4}] self.storedValues = [[],[],[],[]] def log(self, msg, level=xbmc.LOGDEBUG): log('%s: %s'%(self.__class__.__name__,msg),level) def copy(self): return ShowOnNext() def getTitle(self): return '%s (%s)'%(self.name,{True:LANGUAGE(30184),False:LANGUAGE(30021)}[self.optionValues[0]]) def getPosition(self, optionindex): orgvalue = self.optionValues[optionindex] self.onActionSelect(optionindex, LANGUAGE(32223)%('')) if self.optionValues[optionindex] == self.selectBoxOptions[optionindex][1]: from overlaytool import OverlayTool overlaytool = OverlayTool(OVERLAYTOOL_XML, ADDON_PATH, "default", ADV_RULES=True, Focus_IDX=0, OnNext_Position_XY=self.optionValues[optionindex], OnNext_Color=self.optionValues[2]) del overlaytool value = PROPERTIES.getProperty("OnNext_Position_XY") PROPERTIES.clrProperty("OnNext_Position_XY") if value: self.optionValues[optionindex] = value else: self.optionValues[optionindex] = orgvalue elif self.optionValues[optionindex] != self.selectBoxOptions[optionindex][0]: self.optionValues[optionindex] = orgvalue def onAction(self, optionindex): if optionindex == 0: self.onActionToggleBool(optionindex) elif optionindex == 1: self.getPosition(optionindex) elif optionindex == 2: self.onActionSelect(optionindex, LANGUAGE(30196)) return self.optionValues[optionindex] def runAction(self, actionid, citem, parameter, player): if actionid == RULES_ACTION_PLAYER_START: self.storedValues[0] = bool(player.OnNextMode) self.storedValues[1] = player.onNextPosition self.storedValues[2] = player.onNextMode player.onNextPosition = self.optionValues[1] player.OnNextMode = self.optionValues[2] self.log("runAction, restoring onNextPosition = %s, onNextMode = %s"%(player.onNextPosition, player.onNextMode)) elif actionid == RULES_ACTION_PLAYER_STOP: player.onNextPosition = self.storedValues[1] player.onNextMode = self.storedValues[2] self.log("runAction, restoring onNextPosition = %s, onNextMode = %s"%(player.onNextPosition, player.onNextMode)) return parameter class SetScreenVingette(BaseRule): def __init__(self): self.myId = 3 self.ignore = False self.exclude = False self.name = LANGUAGE(30177) self.description = LANGUAGE(33177) self.optionLabels = [LANGUAGE(30174),LANGUAGE(30175),LANGUAGE(30176),LANGUAGE(30178),LANGUAGE(30185),LANGUAGE(30186)] self.optionValues = [False,' ',1.00,0.00,1.00,False] self.optionDescriptions = [LANGUAGE(33174),LANGUAGE(33175),LANGUAGE(33176),LANGUAGE(33179),LANGUAGE(33185),LANGUAGE(34186)] self.actions = [RULES_ACTION_OVERLAY_OPEN,RULES_ACTION_OVERLAY_CLOSE] self.selectBoxOptions = ['','',list(frange(5,21,1)),list(range(-2,3,1)),list(frange(5,21,1)),'']#[LANGUAGE(30022),LANGUAGE(32136)]] self.storedValues = [[],[],[]] def log(self, msg, level=xbmc.LOGDEBUG): log('%s: %s'%(self.__class__.__name__,msg),level) def copy(self): return SetScreenVingette() def getTitle(self): return '%s (%s)'%(self.name,self.optionValues) # todo set viewmode as dict() response from json. def getPosition(self, optionindex):#todo vin utility to adjust zoom,vshift,pratio and nls self.dialog.notificationDialog(LANGUAGE(32020)) def getImage(self, image=''): self.log('getImage, In image = %s'%(image)) day = re.compile(r'\_Day(.*?)', re.IGNORECASE).search(image) night = re.compile(r'\_Night(.*?)', re.IGNORECASE).search(image) mytime = time.localtime() if mytime.tm_hour < 6 or mytime.tm_hour > 18: if day: nImage = image.replace(day.group(),'_Night') if FileAccess.exists(nImage): image = nImage else: if night: nImage = image.replace(night.group(),'_Day') if FileAccess.exists(nImage): image = nImage self.log('getImage, Out image = %s'%(image)) return image def onAction(self, optionindex): if optionindex == 1: self.onActionBrowse(optionindex, type=1, heading=self.optionLabels[1], mask=xbmc.getSupportedMedia('picture'), exclude=[12,13,14,15,16,17]) elif optionindex in [0,5]: self.onActionToggleBool(optionindex) elif optionindex in [2,3,4]: self.onActionSelect(optionindex, self.optionLabels[optionindex]) return self.optionValues[optionindex] def runAction(self, actionid, citem, parameter, overlay): if actionid == RULES_ACTION_OVERLAY_OPEN: self.storedValues[0] = overlay.enableVignette self.storedValues[1] = overlay.vinImage self.storedValues[2] = overlay.vinView overlay.enableVignette = self.optionValues[0] overlay.vinImage = self.getImage(self.optionValues[1]) overlay.vinView = {"nonlinearstretch":self.optionValues[5] ,"pixelratio":self.optionValues[4],"verticalshift":self.optionValues[3],"viewmode":"custom","zoom": self.optionValues[2]} self.log('runAction, setting vignette image = %s\nmode = %s'%(overlay.vinImage,overlay.vinView)) elif actionid == RULES_ACTION_OVERLAY_CLOSE: overlay.enableVignette = self.storedValues[0] overlay.vinImage = self.storedValues[1] overlay.vinView = self.storedValues[2] self.log('runAction, restoring vignette image = %s\nmode = %s'%(overlay.vinImage,overlay.vinView)) return parameter class MST3k(BaseRule): def __init__(self): self.myId = 4 self.ignore = False self.exclude = False self.name = "Mystery Science Theater 3K Silhouette" self.description = "Animated Silhouette of MST3K" self.optionLabels = ['Enable MST3K Silhouette'] self.optionValues = [False] self.optionDescriptions = ["Enable Silhouette"] self.actions = [RULES_ACTION_OVERLAY_OPEN,RULES_ACTION_OVERLAY_OPEN+.1,RULES_ACTION_OVERLAY_CLOSE] self.storedValues = [[],[],[]] self.threadTimer = Timer(5.0, self.runAction) self.optionImages = [os.path.join(MEDIA_LOC,'overlays','MST3K_1.gif'), os.path.join(MEDIA_LOC,'overlays','MST3K_2.gif')] def log(self, msg, level=xbmc.LOGDEBUG): log('%s: %s'%(self.__class__.__name__,msg),level) def copy(self): return MST3k() def getTitle(self): return '%s (%s)'%(self.name,{True:LANGUAGE(30184),False:LANGUAGE(30021)}[self.optionValues[0]]) def setImage(self, actionid, citem, overlay, image): if not self.threadTimer.is_alive(): self.threadTimer = Timer(5.0, overlay.runActions,[actionid, citem, None, overlay]) self.threadTimer.name = 'MST3k.setImage' self.threadTimer.start() self.log('setImage, image = %s'%(image)) return image def onAction(self, optionindex): if optionindex == 0: self.onActionToggleBool(optionindex) return self.optionValues[optionindex] def runAction(self, actionid, citem, parameter, overlay): if actionid == RULES_ACTION_OVERLAY_OPEN: self.storedValues[0] = overlay.vinImage self.storedValues[1] = overlay._vinOffsetXY self.storedValues[2] = overlay._vinZoom overlay.vinImage = self.setImage(actionid+.1, citem, overlay, self.optionImages[0]) overlay._vinOffsetXY = (0,0) overlay._vinZoom = 1.0 self.log("runAction, setting overlay enabled = %s, image %s @ (%s) X %s"%(overlay.enableVignette, overlay.vinImage, overlay._vinOffsetXY, overlay._vinZoom)) elif actionid == RULES_ACTION_OVERLAY_OPEN+.1: overlay.vinImage = self.setImage(actionid, citem, overlay, self.optionImages[1]) overlay._setImage(overlay.vignette,overlay.vinImage) self.log("runAction, setting overlay enabled = %s, image %s @ (%s) X %s"%(overlay.enableVignette, overlay.vinImage, overlay._vinOffsetXY, overlay._vinZoom)) elif actionid == RULES_ACTION_OVERLAY_CLOSE: overlay.vinImage = self.storedValues[0] overlay._vinOffsetXY = self.storedValues[1] overlay._vinZoom = self.storedValues[2] self.log("runAction, restoring overlay enabled = %s, image %s @ (%s) X %s"%(overlay.enableVignette, overlay.vinImage, overlay._vinOffsetXY, overlay._vinZoom)) if self.threadTimer.is_alive(): if hasattr(self.threadTimer, 'cancel'): self.threadTimer.cancel() try: self.threadTimer.join() except: pass return parameter class DisableOverlay(BaseRule): #PLAYER RULES [50-99] def __init__(self): self.myId = 50 self.ignore = False self.exclude = False self.name = LANGUAGE(30042) self.description = LANGUAGE(33042) self.optionLabels = [LANGUAGE(30042)] self.optionValues = [SETTINGS.getSettingBool('Overlay_Enable')] self.optionDescriptions = [LANGUAGE(33042)] self.actions = [RULES_ACTION_PLAYER_START,RULES_ACTION_PLAYER_STOP] self.storedValues = [[]] def log(self, msg, level=xbmc.LOGDEBUG): log('%s: %s'%(self.__class__.__name__,msg),level) def copy(self): return DisableOverlay() def getTitle(self): return '%s (%s)'%(self.name,{True:LANGUAGE(30184),False:LANGUAGE(30021)}[self.optionValues[0]]) def onAction(self, optionindex): self.onActionToggleBool(optionindex) return self.optionValues[optionindex] def runAction(self, actionid, citem, parameter, player): if actionid == RULES_ACTION_PLAYER_START: self.storedValues[0] = player.enableOverlay player.enableOverlay = self.optionValues[0] self.log("runAction, setting enableOverlay = %s"%(player.enableOverlay)) elif actionid == RULES_ACTION_PLAYER_STOP: player.enableOverlay = self.storedValues[0] self.log("runAction, restore enableOverlay = %s"%(player.enableOverlay)) return parameter class ForceSubtitles(BaseRule): def __init__(self): self.myId = 51 self.ignore = False self.exclude = False self.name = "Force Subtitles" self.description = "Show Subtitles" self.optionLabels = ['Force Subtitles?'] self.optionValues = [BUILTIN.isSubtitle()] self.optionDescriptions = [""] self.actions = [RULES_ACTION_PLAYER_START,RULES_ACTION_PLAYER_STOP] self.storedValues = [[]] def log(self, msg, level=xbmc.LOGDEBUG): log('%s: %s'%(self.__class__.__name__,msg),level) def copy(self): return ForceSubtitles() def getTitle(self): return '%s (%s)'%(self.name,{True:LANGUAGE(30184),False:LANGUAGE(30021)}[self.optionValues[0]]) def onAction(self, optionindex): self.onActionToggleBool(optionindex) return self.optionValues[optionindex] def runAction(self, actionid, citem, pvritem, player): if actionid == RULES_ACTION_PLAYER_START: self.storedValues[0] = player.lastSubState player.lastSubState = self.optionValues[0] self.log("runAction, setting lastSubState = %s"%(player.lastSubState)) elif actionid == RULES_ACTION_PLAYER_STOP: player.lastSubState = self.storedValues[0] self.log("runAction, restoring lastSubState = %s"%(player.lastSubState)) return pvritem class DisableTrakt(BaseRule): def __init__(self): self.myId = 52 self.ignore = False self.exclude = False self.name = "Trakt scrobbling" self.description = "Disable Trakt scrobbling." self.optionLabels = [LANGUAGE(30131)] self.optionValues = [SETTINGS.getSettingBool('Disable_Trakt')] self.optionDescriptions = [LANGUAGE(33131)] self.actions = [RULES_ACTION_PLAYER_START,RULES_ACTION_PLAYER_STOP] self.storedValues = [[]] def log(self, msg, level=xbmc.LOGDEBUG): log('%s: %s'%(self.__class__.__name__,msg),level) def copy(self): return DisableTrakt() def getTitle(self): return '%s (%s)'%(self.name,{True:LANGUAGE(30184),False:LANGUAGE(30021)}[self.optionValues[0]]) def onAction(self, optionindex): self.onActionToggleBool(optionindex) return self.optionValues[optionindex] def runAction(self, actionid, citem, parameter, player): if actionid == RULES_ACTION_PLAYER_START: self.storedValues[0] = player.disableTrakt player.disableTrakt = self.optionValues[0] self.log("runAction, setting disableTrakt = %s"%(player.disableTrakt)) elif actionid == RULES_ACTION_PLAYER_STOP: player.disableTrakt = self.storedValues[0] self.log("runAction, restoring disableTrakt = %s"%(player.disableTrakt)) return parameter class RollbackPlaycount(BaseRule): """ RollbackPlaycount """ def __init__(self): self.myId = 53 self.ignore = False self.exclude = False self.name = "Rollback Playcount" self.description = "Passive Playback w/o playcount & progress tracking." self.optionLabels = [LANGUAGE(30132)] self.optionValues = [SETTINGS.getSettingBool('Rollback_Watched')] self.optionDescriptions = [LANGUAGE(33132)] self.actions = [RULES_ACTION_PLAYER_START,RULES_ACTION_PLAYER_STOP] self.storedValues = [[]] def log(self, msg, level=xbmc.LOGDEBUG): log('%s: %s'%(self.__class__.__name__,msg),level) def copy(self): return RollbackPlaycount() def getTitle(self): return '%s (%s)'%(self.name,{True:LANGUAGE(30184),False:LANGUAGE(30021)}[self.optionValues[0]]) def onAction(self, optionindex): self.onActionToggleBool(optionindex) return self.optionValues[optionindex] def runAction(self, actionid, citem, parameter, player): if actionid == RULES_ACTION_PLAYER_START: self.storedValues[0] = player.rollbackPlaycount player.rollbackPlaycount = self.optionValues[0] self.log("runAction, setting rollbackPlaycount = %s"%(player.rollbackPlaycount)) elif actionid == RULES_ACTION_PLAYER_STOP: player.rollbackPlaycount = self.storedValues[0] self.log("runAction, restoring rollbackPlaycount = %s"%(player.rollbackPlaycount)) return parameter class DisableRestart(BaseRule): """ DisableRestart """ def __init__(self): self.myId = 54 self.ignore = False self.exclude = False self.name = "Restart Button" self.description = LANGUAGE(33153) self.optionLabels = [LANGUAGE(30153)] self.optionValues = [SETTINGS.getSettingInt('Restart_Percentage')] self.optionDescriptions = [LANGUAGE(33153)] self.actions = [RULES_ACTION_PLAYER_START,RULES_ACTION_PLAYER_STOP] self.selectBoxOptions = [list(range(25,100,5))] self.storedValues = [[]] def log(self, msg, level=xbmc.LOGDEBUG): log('%s: %s'%(self.__class__.__name__,msg),level) def copy(self): return DisableRestart() def getTitle(self): return '%s (%s)'%(self.name,self.optionValues[0]) def onAction(self, optionindex): self.onActionSelect(optionindex, self.optionLabels[optionindex]) return self.optionValues[optionindex] def runAction(self, actionid, citem, parameter, player): if actionid == RULES_ACTION_PLAYER_START: self.storedValues[0] = player.restartPercentage player.restartPercentage = self.optionValues[0] self.log("runAction, setting restartPercentage = %s"%(player.restartPercentage)) elif actionid == RULES_ACTION_PLAYER_STOP: player.restartPercentage = self.storedValues[0] self.log("runAction, restoring restartPercentage = %s"%(player.restartPercentage)) return parameter class DisableOnChange(BaseRule): """ DisableOnChange """ def __init__(self): self.myId = 55 self.ignore = False self.exclude = False self.name = LANGUAGE(30170) self.description = LANGUAGE(33171) self.optionLabels = [LANGUAGE(30170)] self.optionValues = [SETTINGS.getSettingBool('Enable_OnInfo')] self.optionDescriptions = [LANGUAGE(33171)] self.actions = [RULES_ACTION_PLAYER_START,RULES_ACTION_PLAYER_STOP] self.storedValues = [[]] def log(self, msg, level=xbmc.LOGDEBUG): log('%s: %s'%(self.__class__.__name__,msg),level) def copy(self): return DisableOverlay() def getTitle(self): return '%s (%s)'%(self.name,{True:LANGUAGE(30184),False:LANGUAGE(30021)}[self.optionValues[0]]) def onAction(self, optionindex): self.onActionToggleBool(optionindex) return self.optionValues[optionindex] def runAction(self, actionid, citem, parameter, player): if actionid == RULES_ACTION_PLAYER_START: self.storedValues[0] = player.infoOnChange player.infoOnChange = self.optionValues[0] self.log("runAction, setting infoOnChange = %s"%(player.infoOnChange)) elif actionid == RULES_ACTION_PLAYER_STOP: player.infoOnChange = self.storedValues[0] self.log("runAction, restoring infoOnChange = %s"%(player.infoOnChange)) return parameter class QuotaOptions(BaseRule): """ QuotaOptions """ def __init__(self): self.myId = 498 self.ignore = False self.exclude = False self.name = "Quota Options" self.description = "Quota Options" self.optionLabels = ["Pad FileList","Pad Programmes"] self.optionValues = [False,True] self.optionDescriptions = ["Pad FileList with duplicates to meet page limit","Pad Programmes to meet Minimum duration"] self.actions = [RULES_ACTION_CHANNEL_START,RULES_ACTION_CHANNEL_STOP] self.storedValues = [[],[]] def log(self, msg, level=xbmc.LOGDEBUG): log('%s: %s'%(self.__class__.__name__,msg),level) def copy(self): return QuotaOptions() def getTitle(self): return '%s (%s)'%(self.name,self.optionValues) def onAction(self, optionindex): self.onActionToggleBool(optionindex) return self.optionValues[optionindex] def runAction(self, actionid, citem, parameter, builder): if actionid == RULES_ACTION_CHANNEL_START: self.storedValues[0] = builder.filelistQuota self.storedValues[1] = builder.schedulingQuota builder.filelistQuota = self.optionValues[0] builder.schedulingQuota = self.optionValues[1] self.log("runAction, setting filelistQuota = %s, schedulingQuota = %s"%(builder.filelistQuota,builder.schedulingQuota)) elif actionid == RULES_ACTION_CHANNEL_STOP: builder.filelistQuota = self.storedValues[0] builder.schedulingQuota = self.storedValues[1] self.log("runAction, setting filelistQuota = %s, schedulingQuota = %s"%(builder.filelistQuota,builder.schedulingQuota)) return parameter class PageLimit(BaseRule): """ PageLimit """ def __init__(self): self.myId = 499 self.ignore = False self.exclude = False self.name = LANGUAGE(30015) self.description = LANGUAGE(33015) self.optionLabels = [LANGUAGE(30015)] self.optionValues = [SETTINGS.getSettingInt('Page_Limit')] self.optionDescriptions = [LANGUAGE(33015)] self.actions = [RULES_ACTION_CHANNEL_START,RULES_ACTION_CHANNEL_STOP] self.selectBoxOptions = [list(range(25,501,25))] self.storedValues = [[]] def log(self, msg, level=xbmc.LOGDEBUG): log('%s: %s'%(self.__class__.__name__,msg),level) def copy(self): return PageLimit() def getTitle(self): return '%s (%s)'%(LANGUAGE(30015),self.optionValues[0]) def onAction(self, optionindex): self.onActionSelect(optionindex) return self.optionValues[optionindex] def runAction(self, actionid, citem, parameter, builder): if actionid == RULES_ACTION_CHANNEL_START: self.storedValues[0] = builder.limit builder.limit = self.optionValues[0] self.log("runAction, setting limit = %s"%(builder.limit)) elif actionid == RULES_ACTION_CHANNEL_STOP: builder.limit = self.storedValues[0] self.log("runAction, restoring limit = %s"%(builder.limit)) return parameter class DurationOptions(BaseRule): #CHANNEL RULES [500-599] """ DurationOptions """ def __init__(self): self.myId = 500 self.ignore = False self.exclude = False self.name = "Duration Options" self.description = "Duration Options" self.optionLabels = [LANGUAGE(30049),LANGUAGE(30052),LANGUAGE(32233)] self.optionValues = [SETTINGS.getSettingInt('Duration_Type'),SETTINGS.getSettingBool('Store_Duration'),SETTINGS.getSettingInt('Seek_Tolerance')] self.optionDescriptions = [LANGUAGE(33015),LANGUAGE(33049),LANGUAGE(33052),LANGUAGE(32233)] self.actions = [RULES_ACTION_CHANNEL_START,RULES_ACTION_CHANNEL_STOP] self.selectBoxOptions = [{LANGUAGE(30050):0,LANGUAGE(30051):1},[],list(range(0,605,5))] self.storedValues = [[],[],[]] def log(self, msg, level=xbmc.LOGDEBUG): log('%s: %s'%(self.__class__.__name__,msg),level) def copy(self): return DurationOptions() def getTitle(self): return '%s (%s)'%(self.name,self.optionValues) def onAction(self, optionindex): if optionindex == 0: self.onActionSelect(optionindex, LANGUAGE(30049)) self.validateRange(optionindex, 0, 1, 0) else: self.onActionToggleBool(optionindex) return self.optionValues[optionindex] def runAction(self, actionid, citem, parameter, inherited): if actionid == RULES_ACTION_CHANNEL_START: self.storedValues[0] = inherited.accurateDuration self.storedValues[1] = inherited.saveDuration self.storedValues[2] = inherited.minDuration inherited.accurateDuration = self.optionValues[0] inherited.saveDuration = self.optionValues[1] inherited.minDuration = self.optionValues[2] self.log("runAction, setting accurateDuration = %s, saveDuration = %s, minDuration = %s"%(inherited.accurateDuration,inherited.saveDuration,inherited.minDuration)) elif actionid == RULES_ACTION_CHANNEL_STOP: inherited.accurateDuration = self.storedValues[0] inherited.saveDuration = self.storedValues[1] inherited.minDuration = self.storedValues[2] self.log("runAction, restoring accurateDuration = %s, saveDuration = %s, minDuration = %s"%(inherited.accurateDuration,inherited.saveDuration,inherited.minDuration)) return parameter class IncludeOptions(BaseRule): """ IncludeOptions """ def __init__(self): self.myId = 501 self.ignore = False self.exclude = False self.name = "Include Options" self.description = "Include Options" self.optionLabels = [LANGUAGE(30053),LANGUAGE(30054),LANGUAGE(30055)] self.optionValues = [SETTINGS.getSettingBool('Enable_Extras'),SETTINGS.getSettingBool('Enable_Strms'),SETTINGS.getSettingBool('Enable_3D')] self.optionDescriptions = [LANGUAGE(33053),LANGUAGE(33054),LANGUAGE(33055)] self.actions = [RULES_ACTION_CHANNEL_START,RULES_ACTION_CHANNEL_STOP] self.storedValues = [[],[],[]] def log(self, msg, level=xbmc.LOGDEBUG): log('%s: %s'%(self.__class__.__name__,msg),level) def copy(self): return IncludeOptions() def getTitle(self): return '%s (%s)'%(self.name,self.optionValues) def onAction(self, optionindex): self.onActionToggleBool(optionindex) return self.optionValues[optionindex] def runAction(self, actionid, citem, parameter, builder): if actionid == RULES_ACTION_CHANNEL_START: self.storedValues[0] = builder.incExtras self.storedValues[1] = builder.incStrms self.storedValues[2] = builder.inc3D builder.incExtras = self.optionValues[0] builder.incStrms = self.optionValues[1] builder.inc3D = self.optionValues[2] self.log("runAction, setting incExtras = %s, incStrms = %s, inc3D = %s"%(builder.incExtras,builder.incStrms,builder.inc3D)) elif actionid == RULES_ACTION_CHANNEL_STOP: builder.incExtras = self.storedValues[0] builder.incStrms = self.storedValues[1] builder.inc3D = self.storedValues[2] self.log("runAction, restoring incExtras = %s, incStrms = %s, inc3D = %s"%(builder.incExtras,builder.incStrms,builder.inc3D)) return parameter class PreRoll(BaseRule): """ PreRoll """ def __init__(self): self.myId = 502 self.ignore = False self.exclude = False self.name = "Pre-Roll" self.description = "Pre-Roll Options" self.optionLabels = [LANGUAGE(30017),LANGUAGE(30139),LANGUAGE(30028),LANGUAGE(30029),"Ratings Folder","Bumpers Folder"] self.optionValues = [SETTINGS.getSettingInt('Enable_Preroll'),SETTINGS.getSettingInt('Random_Pre_Chance'),SETTINGS.getSetting('Resource_Ratings'),SETTINGS.getSetting('Resource_Bumpers'),[os.path.join(FILLER_LOC,'Ratings','')],[os.path.join(FILLER_LOC,'Bumpers','')]] self.optionDescriptions = [LANGUAGE(30018),LANGUAGE(33134),LANGUAGE(33028),LANGUAGE(33029),"",""] self.actions = [RULES_ACTION_CHANNEL_START,RULES_ACTION_CHANNEL_STOP] self.selectBoxOptions = [{LANGUAGE(30022):-1,LANGUAGE(30021):0},list(range(0,101,1)),"","","",""] self.storedValues = [{},{}] def log(self, msg, level=xbmc.LOGDEBUG): log('%s: %s'%(self.__class__.__name__,msg),level) def copy(self): return PreRoll() def getTitle(self): return self.name def onAction(self, optionindex): if optionindex in [0,1]: self.onActionSelect(optionindex) elif optionindex in [2,3]: self.onActionResources(optionindex, ftype={"2":"ratings","3":"bumpers"}[str(optionindex)]) elif optionindex in [4,5]: self.onActionMultiBrowse(optionindex, header="%s for %s"%(LANGUAGE(32080), {"4":"Ratings","5":"Bumpers"}[str(optionindex)]), exclude=[12,13,14,15,16,21,22]) return self.optionValues[optionindex] def runAction(self, actionid, citem, parameter, builder): if actionid == RULES_ACTION_CHANNEL_START: self.storedValues[0] = builder.bctTypes.get('ratings',{}) self.storedValues[1] = builder.bctTypes.get('bumpers',{}) builder.bctTypes['ratings'].update({"max":self.optionValues[0], "auto":self.optionValues[0] == -1, "enabled":bool(self.optionValues[0]), "chance":self.optionValues[1],"sources":{"ids":self.optionValues[2].split('|'),"paths":self.optionValues[4]}}) builder.bctTypes['bumpers'].update({"max":self.optionValues[0], "auto":self.optionValues[0] == -1, "enabled":bool(self.optionValues[0]), "chance":self.optionValues[1],"sources":{"ids":self.optionValues[3].split('|'),"paths":self.optionValues[5]}}) self.log("runAction, setting bctTypes = %s"%(builder.bctTypes)) elif actionid == RULES_ACTION_CHANNEL_STOP: builder.bctTypes['ratings'] = self.storedValues[0] builder.bctTypes['bumpers'] = self.storedValues[1] self.log("runAction, restoring bctTypes = %s"%(builder.bctTypes)) return parameter class PostRoll(BaseRule): """ PostRoll """ def __init__(self): self.myId = 503 self.ignore = False self.exclude = False self.name = "Post-Roll" self.description = "Post-Roll Options" self.optionLabels = [LANGUAGE(30019),LANGUAGE(30134),LANGUAGE(30030),"Adverts Folder",LANGUAGE(30031),"Trailers Folder",LANGUAGE(30126)] self.optionValues = [SETTINGS.getSettingInt('Enable_Postroll'),SETTINGS.getSettingInt('Random_Post_Chance'),SETTINGS.getSetting('Resource_Adverts'),[os.path.join(FILLER_LOC,'Adverts','')],SETTINGS.getSetting('Resource_Trailers'),[os.path.join(FILLER_LOC,'Trailers','')],SETTINGS.getSettingBool('Include_Trailers_KODI')] self.optionDescriptions = [LANGUAGE(30020),LANGUAGE(33134),LANGUAGE(33030),"",LANGUAGE(33031),"",LANGUAGE(33126)] self.actions = [RULES_ACTION_CHANNEL_START,RULES_ACTION_CHANNEL_STOP] self.selectBoxOptions = [{LANGUAGE(30022):-1,LANGUAGE(30021):0,LANGUAGE(30026):1,LANGUAGE(30024):2,LANGUAGE(30025):3},list(range(0,101,1)),[]] self.storedValues = [{},{}] def log(self, msg, level=xbmc.LOGDEBUG): log('%s: %s'%(self.__class__.__name__,msg),level) def copy(self): return PostRoll() def getTitle(self): return self.name def onAction(self, optionindex): if optionindex in [0,1]: self.onActionSelect(optionindex) elif optionindex in [2,4]: self.onActionResources(optionindex, ftype={"2":"adverts","4":"trailers"}[str(optionindex)]) elif optionindex in [3,5]: self.onActionMultiBrowse(optionindex, header="%s for %s"%(LANGUAGE(32080),{"3":"Adverts","5":"Trailers"}[str(optionindex)]), exclude=[12,13,14,15,16,21,22]) elif optionindex in [6]: self.onActionToggleBool(optionindex) return self.optionValues[optionindex] def runAction(self, actionid, citem, parameter, builder): if actionid == RULES_ACTION_CHANNEL_START: self.storedValues[0] = builder.bctTypes.get('adverts',{}) self.storedValues[1] = builder.bctTypes.get('trailers',{}) builder.bctTypes['adverts'].update({"min":self.optionValues[0] , "max":builder.limit, "auto":self.optionValues[0] == -1, "enabled":bool(self.optionValues[0]), "chance":self.optionValues[1],"sources":{"ids":self.optionValues[2].split('|'),"paths":self.optionValues[3]}}) builder.bctTypes['trailers'].update({"min":self.optionValues[0], "max":builder.limit, "auto":self.optionValues[0] == -1, "enabled":bool(self.optionValues[0]), "chance":self.optionValues[1],"sources":{"ids":self.optionValues[4].split('|'),"paths":self.optionValues[5]},"incKODI":self.optionValues[6]}) self.log("runAction, setting bctTypes = %s"%(builder.bctTypes)) elif actionid == RULES_ACTION_CHANNEL_STOP: builder.bctTypes['adverts'].update(self.storedValues[0]) builder.bctTypes['trailers'].update(self.storedValues[1]) self.log("runAction, restoring bctTypes = %s"%(builder.bctTypes)) return parameter class InterleaveValue(BaseRule): """ InterleaveValue """ def __init__(self): self.myId = 504 self.ignore = False self.exclude = False self.name = LANGUAGE(30192) self.description = LANGUAGE(33215) self.optionLabels = [LANGUAGE(30179)] self.optionValues = [SETTINGS.getSettingInt('Interleave_Value')] self.optionDescriptions = [LANGUAGE(33215)] self.actions = [RULES_ACTION_CHANNEL_START,RULES_ACTION_CHANNEL_STOP] self.selectBoxOptions = [list(range(0,26,1))] self.storedValues = [[]] def log(self, msg, level=xbmc.LOGDEBUG): log('%s: %s'%(self.__class__.__name__,msg),level) def copy(self): return InterleaveValue() def getTitle(self): return '%s (%s)'%(self.name,self.optionValues[0]) return '%s (%s)'%(LANGUAGE(30192),self.optionValues[0]) if bool(self.optionValues[0]): return '%s %s'%(LANGUAGE(30192),LANGUAGE(30184)) else: return '%s %s'%(LANGUAGE(30192),LANGUAGE(30021)) def onAction(self, optionindex): self.onActionSelect(optionindex) return self.optionValues[optionindex] def runAction(self, actionid, citem, parameter, builder): if actionid == RULES_ACTION_CHANNEL_START: self.storedValues[0] = builder.interleaveValue builder.interleaveValue = self.optionValues[0] self.log("runAction, setting interleaveValue = %s"%(builder.interleaveValue)) elif actionid == RULES_ACTION_CHANNEL_STOP: builder.interleaveValue = self.storedValues[0] self.log("runAction, restoring interleaveValue = %s"%(builder.interleaveValue)) return parameter class ProvisionalRule(BaseRule): #PARSING RULES [800-999] """ ProvisionalRule """ def __init__(self): self.myId = 800 self.ignore = True self.exclude = True self.name = "Provisional Path" self.description = "Fill Provisional Path" self.optionLabels = ["Provisional Value"] self.optionValues = [""] self.optionDescriptions = [""] self.actions = [RULES_ACTION_CHANNEL_BUILD_FILEARRAY_PRE] self.storedValues = [[]] def log(self, msg, level=xbmc.LOGDEBUG): log('%s: %s'%(self.__class__.__name__,msg),level) def copy(self): return ProvisionalRule() def getTitle(self): return '%s (%s)'%(self.name,self.optionValues[0]) def _getProvisional(self, citem): PROVISIONAL_TYPES = {"TV Shows" : [{"path":"videodb://tvshows/titles/" ,"limit":"","sort":{"method":"episode","order":"ascending"},"filter":{"and":[{"field":"tvshow","operator":"is","value":""}]}, "method":"VideoLibrary.GetEpisodes","enum":"Video.Fields.Episode","key":"episodes"}], "TV Networks" : [{"path":"videodb://tvshows/titles/","limit":"","sort":{"method":"episode","order":"ascending"} ,"filter":{"and":[{"field":"studio","operator":"contains","value":""}]}, "method":"VideoLibrary.GetEpisodes","enum":"Video.Fields.Episode","key":"episodes"}], "Movie Studios": [{"path":"videodb://movies/titles/" ,"limit":"","sort":{"method":"random" ,"order":"ascending"} ,"filter":{"and":[{"field":"studio","operator":"contains","value":""}]}, "method":"VideoLibrary.GetMovies" ,"enum":"Video.Fields.Movie","key":"movies"}], "TV Genres" : [{"path":"videodb://tvshows/titles/" ,"limit":"","sort":{"method":"random","order":"ascending"} ,"filter":{"and":[{"field":"genre" ,"operator":"contains","value":""}]}, "method":"VideoLibrary.GetEpisodes","enum":"Video.Fields.Episode","key":"episodes"}], "Movie Genres" : [{"path":"videodb://movies/titles/" ,"limit":"","sort":{"method":"random" ,"order":"ascending"},"filter":{"and":[{"field":"genre" ,"operator":"contains","value":""}]}, "method":"VideoLibrary.GetMovies" ,"enum":"Video.Fields.Movie","key":"movies"}], "Mixed Genres" : [{"path":"videodb://tvshows/titles/" ,"limit":"","sort":{"method":"random","order":"ascending"} ,"filter":{"and":[{"field":"genre" ,"operator":"contains","value":""}]}, "method":"VideoLibrary.GetEpisodes","enum":"Video.Fields.Episode","key":"episodes"}, {"path":"videodb://movies/titles/" ,"limit":"","sort":{"method":"random" ,"order":"ascending"},"filter":{"and":[{"field":"genre" ,"operator":"contains","value":""}]}, "method":"VideoLibrary.GetMovies" ,"enum":"Video.Fields.Movie","key":"movies"}]} return PROVISIONAL_TYPES.get(citem.get('type',''),[]) def runAction(self, actionid, citem, parameter, builder): if actionid == RULES_ACTION_CHANNEL_BUILD_FILEARRAY_PRE: if self.optionValues[0]: try: if builder.pDialog: builder.pDialog = self.dialog.updateProgress(builder.pCount, builder.pDialog, message='%s: %s'%(LANGUAGE(32209),self.name),header='%s, %s'%(ADDON_NAME,builder.pMSG)) if self.optionValues[0] == "Seasonal": queries = list(Seasonal().buildSeasonal()) else: queries = self._getProvisional(citem) self.log("%s: runAction, id: %s, provisional value = %s\nqueries = %s"%(self.__class__.__name__,citem.get('id'),self.optionValues[0],queries)) for provisional in queries: if not provisional: continue else: if self.optionValues[0] == "Seasonal": citem['logo'] = provisional.get('holiday',{}).get('logo',citem['logo']) else: provisional["filter"]["and"][0]['value'] = self.optionValues[0] if not builder.incExtras and provisional["key"].startswith(tuple(TV_TYPES)): #filter out extras/specials provisional["filter"].setdefault("and",[]).extend([{"field":"season" ,"operator":"greaterthan","value":"0"}, {"field":"episode","operator":"greaterthan","value":"0"}]) fileList, dirList, nlimits, errors = builder.buildList(citem, provisional.get('path'), media='video', page=(provisional.get('limit') or builder.limit), sort=provisional.get('sort'), limits=builder.limits, dirItem={}, query=provisional) if len(fileList) > 0: self.storedValues[0].append(fileList) return [fileList for fileList in self.storedValues[0] if fileList] except Exception as e: self.log("runAction, failed! %s"%(e), xbmc.LOGERROR) return [] return parameter class HandleMethodOrder(BaseRule): """ HandleMethodOrder """ def __init__(self): self.myId = 950 self.ignore = False self.exclude = True self.name = LANGUAGE(32232) self.description = LANGUAGE(33232) self.optionLabels = ['Method','Order','Ignore Articles','Ignore Artist Sort Name'] self.optionValues = ['random','ascending',True,True] self.optionDescriptions = ["","","",""] self.actions = [RULES_ACTION_CHANNEL_START,RULES_ACTION_CHANNEL_STOP] self.selectBoxOptions = [self.getSort(), self.getOrder()] self.storedValues = [[],[],[],[],[]] def log(self, msg, level=xbmc.LOGDEBUG): log('%s: %s'%(self.__class__.__name__,msg),level) def copy(self): return HandleMethodOrder() def getTitle(self): return '%s (%s)'%(self.name,self.optionValues) def getSort(self): from jsonrpc import JSONRPC return JSONRPC().getEnums("List.Sort",type="method") def getOrder(self): from jsonrpc import JSONRPC return JSONRPC().getEnums("List.Sort",type="order") def onAction(self, optionindex): if optionindex in [2,3]: self.onActionToggleBool(optionindex) else: self.onActionSelect(optionindex, self.optionLabels[optionindex]) return self.optionValues[optionindex] def runAction(self, actionid, citem, parameter, builder): if actionid == RULES_ACTION_CHANNEL_START: self.storedValues[0] = builder.sort builder.sort.update({"method":self.optionValues[0],"order":self.optionValues[1],"ignorearticle":self.optionValues[2],"useartistsortname":self.optionValues[3]}) self.log("runAction, setting sort to %s"%(builder.sort)) elif actionid == RULES_ACTION_CHANNEL_STOP: builder.sort = self.storedValues[0] self.log("runAction, setting sort to %s"%(builder.sort)) return citem class ForceEpisode(BaseRule): """ ForceEpisode """ def __init__(self): self.myId = 998 self.ignore = False self.exclude = False self.name = LANGUAGE(30181) self.description = LANGUAGE(33230) self.optionLabels = [LANGUAGE(30181)] self.optionValues = [SETTINGS.getSettingBool('Enable_Force_Episode')] self.optionDescriptions = [LANGUAGE(33230)] self.actions = [RULES_ACTION_CHANNEL_BUILD_FILEARRAY_PRE,RULES_ACTION_CHANNEL_BUILD_PATH,RULES_ACTION_CHANNEL_BUILD_FILELIST_PRE] self.storedValues = [{},{},{},[],[],[]] self.selectBoxOptions = ["",list(range(0,26,1))] def log(self, msg, level=xbmc.LOGDEBUG): log('%s: %s'%(self.__class__.__name__,msg),level) def copy(self): return ForceEpisode() def getTitle(self): return '%s (%s)'%(self.name,{True:LANGUAGE(30184),False:LANGUAGE(30021)}[self.optionValues[0]]) def onAction(self, optionindex): self.onActionToggleBool(optionindex) return self.optionValues[optionindex] def _episodeSort(self, showArray: dict={}): try: for show, fileItems in list(showArray.items()): self.storedValues[4] = [] for item in fileItems: if (int(item.get("season","0")) + int(item.get("episode","0"))) > 0: #episode self.storedValues[4].append([int(item.get("season","0")), int(item.get("episode","0")), item]) else: self.storedValues[3].append(item) #movie self.storedValues[4].sort(key=lambda seep: seep[1]) self.storedValues[4].sort(key=lambda seep: seep[0]) for seepitem in self.storedValues[4]: self.storedValues[5].append(seepitem[2]) return self.storedValues[5] except Exception as e: self.log("runAction, _episodeSort failed! %s"%(e), xbmc.LOGERROR) return [] def _sortShows(self, fileList: list=[]): #group by type & show; no duplicates. try: for fileItem in fileList: if fileItem.get('type').startswith(tuple(TV_TYPES)) and fileItem.get('showtitle'): if fileItem not in self.storedValues[2].setdefault(fileItem['showtitle'],[]): self.storedValues[2].setdefault(fileItem['showtitle'],[]).append(fileItem) elif fileItem not in self.storedValues[3]: self.storedValues[3].append(fileItem) #Movies/Other no duplicates allowed return self._episodeSort(self.storedValues[2]), sorted(self.storedValues[3], key=lambda k: k.get('year',0)) except Exception as e: self.log("runAction, _sortShows failed! %s"%(e), xbmc.LOGERROR) return [] def runAction(self, actionid, citem, parameter, builder): if actionid == RULES_ACTION_CHANNEL_BUILD_FILEARRAY_PRE: self.storedValues[0] = builder.sort elif actionid == RULES_ACTION_CHANNEL_BUILD_PATH: if self.optionValues[0]: if parameter.startswith(tuple(['videodb://%s'%tv for tv in TV_TYPES])): builder.sort.update({"method":"episode"}) elif parameter: builder.sort.update({"method":"year"}) self.log("runAction, setting sort to %s"%(builder.sort)) elif actionid == RULES_ACTION_CHANNEL_BUILD_FILELIST_PRE: builder.sort = self.storedValues[0] self.log("runAction, restoring sort and forcing episode/year ordering (%s)"%(len(parameter))) fileList = list(sorted(parameter, key=lambda k: k.get('year',0))) return interleave(list(self._sortShows(fileList)), builder.interleaveValue) return parameter class ForceRandom(BaseRule): """ ForceRandom """ def __init__(self): self.myId = 999 self.ignore = False self.exclude = False self.name = LANGUAGE(30182) self.description = LANGUAGE(33231) self.optionLabels = [LANGUAGE(30182)] self.optionValues = [False] self.optionDescriptions = [LANGUAGE(33231)] self.actions = [RULES_ACTION_CHANNEL_BUILD_FILEARRAY_PRE,RULES_ACTION_CHANNEL_BUILD_FILELIST_PRE] self.storedValues = [{}] def log(self, msg, level=xbmc.LOGDEBUG): log('%s: %s'%(self.__class__.__name__,msg),level) def copy(self): return ForceRandom() def getTitle(self): return '%s (%s)'%(self.name,{True:LANGUAGE(30184),False:LANGUAGE(30021)}[self.optionValues[0]]) def onAction(self, optionindex): self.onActionToggleBool(optionindex) return self.optionValues[optionindex] def runAction(self, actionid, citem, fileList, builder): if self.optionValues[0]: if actionid == RULES_ACTION_CHANNEL_BUILD_FILEARRAY_PRE: self.storedValues[0] = builder.sort builder.sort.update({"method":"random"}) self.log("runAction, setting sort to %s"%(builder.sort)) elif actionid == RULES_ACTION_CHANNEL_BUILD_FILELIST_PRE: builder.sort = self.storedValues[0] self.log("runAction, restoring sort and forcing random shuffle of %s items"%(len(fileList))) return randomShuffle(fileList) return fileList class EvenShowsRule(BaseRule): #BUILDING RULES [1000-2999] """ EvenShowsRule """ def __init__(self): self.myId = 1000 self.ignore = False self.exclude = False self.name = LANGUAGE(30121) self.description = LANGUAGE(33121) self.optionLabels = [LANGUAGE(30180)] self.optionValues = [SETTINGS.getSettingInt('Enable_Even')] self.optionDescriptions = [LANGUAGE(33121)] self.actions = [RULES_ACTION_CHANNEL_BUILD_FILEARRAY_PRE,RULES_ACTION_CHANNEL_BUILD_PATH,RULES_ACTION_CHANNEL_BUILD_FILELIST_PRE,RULES_ACTION_CHANNEL_BUILD_FILELIST_POST] self.selectBoxOptions = [list(range(0,6))] self.storedValues = [[],[],[],{},[],[],[]] def log(self, msg, level=xbmc.LOGDEBUG): log('%s: %s'%(self.__class__.__name__,msg),level) def copy(self): return EvenShowsRule() def getTitle(self): return '%s (%s)'%(self.name,self.optionValues[0]) def onAction(self, optionindex): self.onActionSelect(optionindex,self.optionLabels[optionindex]) return self.optionValues[optionindex] def _chunkEpisodes(self, showArray: dict={}): for show, episodes in list(showArray.items()): yield show,[episodes[i:i+self.optionValues[0]] for i in range(0,len(episodes),self.optionValues[0])] def _isForceOrder(self, builder): return builder.sort.get("method","") in ["episode","year"] def _sortShows(self, fileItems, forceEpisode): #group by type & show; no duplicates. try: for fileItem in fileItems: if fileItem.get('type').startswith(tuple(TV_TYPES)) and fileItem.get('showtitle'): #TV Shows if fileItem not in self.storedValues[3].get(fileItem['showtitle'],[]): self.storedValues[3].setdefault(fileItem['showtitle'],[]).append(fileItem) elif fileItem not in self.storedValues[4]: self.storedValues[4].append(fileItem) #Movies/Other no duplicates allowed if forceEpisode: self.storedValues[4] = list(sorted(self.storedValues[4], key=lambda k: k.get('year',0))) #force year ordering return dict(self._chunkEpisodes(self.storedValues[3])), self.storedValues[4] except Exception as e: self.log("runAction, _sortShows failed! %s"%(e), xbmc.LOGERROR) return {} def _mergeShows(self, shows, movies): nfileList = [] try: while not MONITOR().abortRequested() and shows: for show, chunks in list(shows.items()): if len(movies) > 0: nfileList.append(movies.pop(0)) if len(chunks) == 0: del shows[show] elif len(chunks) > 0: nfileList.extend(shows[show].pop(0)) if len(movies) > 0: self.log('runAction, _mergeShows appending remaining movies, movie count = %s'%(len(movies))) nfileList.extend(movies) #add any remaining movies to the end of sets. self.log('runAction, _mergeShows returning items = %s'%(len(nfileList))) return [_f for _f in nfileList if _f] except Exception as e: self.log("runAction, _mergeShows failed! %s"%(e), xbmc.LOGERROR) return [] def runAction(self, actionid, citem, parameter, builder): if bool(self.optionValues[0]): if actionid == RULES_ACTION_CHANNEL_BUILD_FILEARRAY_PRE: self.storedValues[1] = builder.limit elif actionid == RULES_ACTION_CHANNEL_BUILD_PATH: if parameter.startswith(tuple(['videodb://%s'%tv for tv in TV_TYPES])): builder.limit = builder.limit * self.optionValues[0] #Multiply parser limit for tv content in-order to aid even distribution. self.log('runAction, setting limit %s'%(builder.limit)) elif actionid == RULES_ACTION_CHANNEL_BUILD_FILELIST_PRE: if len(parameter) > 0: forceEpisode = self._isForceOrder(builder) if builder.pDialog: builder.pDialog = self.dialog.updateProgress(builder.pCount, builder.pDialog, message='%s: %s'%(LANGUAGE(32209),self.name),header='%s, %s'%(ADDON_NAME,builder.pMSG)) if forceEpisode: fileItems = list(sorted(parameter, key=lambda k: k.get('episode',0))) #force episode ordering fileItems = list(sorted(fileItems, key=lambda k: k.get('season',0))) #force season ordering else: fileItems = parameter self.log('runAction, group by episode %s'%(forceEpisode)) return self._mergeShows(*(self._sortShows(fileItems, forceEpisode))) elif actionid == RULES_ACTION_CHANNEL_BUILD_FILELIST_POST: builder.limit = (self.storedValues[1] or SETTINGS.getSettingInt('Page_Limit')) self.log('runAction, restoring limit = %s'%(builder.limit)) return parameter class PauseRule(BaseRule): #Finial RULES [3000-~] def __init__(self): self.myId = 3000 self.ignore = False self.exclude = False self.name = LANGUAGE(32230) self.description = LANGUAGE(33228) self.optionLabels = [LANGUAGE(32231),"URL"] self.optionValues = [True,""] self.optionDescriptions = [LANGUAGE(32231),"Self Generated URL"] self.actions = [RULES_ACTION_PLAYBACK_RESUME, RULES_ACTION_PLAYER_START, RULES_ACTION_PLAYER_CHANGE, RULES_ACTION_PLAYER_STOP, RULES_ACTION_CHANNEL_START, RULES_ACTION_CHANNEL_STOP, RULES_ACTION_CHANNEL_BUILD_FILEARRAY_PRE, RULES_ACTION_CHANNEL_BUILD_FILEARRAY_POST, RULES_ACTION_CHANNEL_BUILD_FILELIST_POST, RULES_ACTION_CHANNEL_BUILD_FILELIST_RETURN, RULES_ACTION_CHANNEL_BUILD_TIME_PRE, RULES_ACTION_CHANNEL_CITEM, RULES_ACTION_CHANNEL_TEMP_CITEM] self.storedValues = [[],[],False] def log(self, msg, level=xbmc.LOGDEBUG): log('%s: %s'%(self.__class__.__name__,msg),level) def copy(self): return PauseRule() def getTitle(self): return '%s (%s)'%(self.name,{True:LANGUAGE(30184),False:LANGUAGE(30021)}[self.optionValues[0]]) def onAction(self, optionindex): if optionindex == 0: self.onActionToggleBool(optionindex) return self.optionValues[optionindex] def _getURL(self, id): return 'http://%s/filelist/%s.json'%(PROPERTIES.getRemoteHost(),getMD5('%s.%s'%(SETTINGS.getFriendlyName(),id))) def _getPath(self, id): return os.path.join(TEMP_LOC,'%s.json'%(getMD5('%s.%s'%(SETTINGS.getFriendlyName(),id)))) def _getTotRuntime(self, id, filelist=[]): from jsonrpc import JSONRPC return JSONRPC().getTotRuntime(filelist) def _buildSchedule(self, citem, filelist, builder): self.log('[%s] _buildSchedule, filelist = %s'%(citem.get('id'),len(filelist))) updated = self._getResume(citem.get('id')).get('updated',{}) try: viewed = '%s: %s (%s)'%(LANGUAGE(32250),datetime.datetime.fromtimestamp(updated.get('time')).strftime(BACKUP_TIME_FORMAT),updated.get('instance')) except: viewed = LANGUAGE(32251) return builder.buildCells(citem, duration=self._getTotRuntime(citem.get('id'), filelist), entries=1, info={"title":'%s (%s)'%(citem.get('name'),LANGUAGE(32145)), "episodetitle":viewed, "plot":'%s: %s\nSize: %s\nRuntime: ~%s hrs.'%(LANGUAGE(32249),datetime.datetime.fromtimestamp(time.time()).strftime(BACKUP_TIME_FORMAT),len(filelist),round(self._getTotRuntime(citem.get('id'), filelist)//60//60)), "art":{"thumb":citem.get('logo',COLOR_LOGO),"fanart":FANART,"logo":citem.get('logo',LOGO),"icon":citem.get('logo',LOGO)}}) def _set(self, id, filelist=[], resume={"idx":0,"position":0.0,"total":0.0,"file":"","update":{"instance":"","time":-1}}): self.log("[%s] runAction, _set: filelist = %s, resume = %s, url = %s"%(id,len(filelist),resume,self.optionValues[1])) if self.optionValues[1]: return requestURL(self.optionValues[1],payload={'uuid':SETTINGS.getMYUUID(),'name':SETTINGS.getFriendlyName(),'payload':{'resume':resume,'filelist':filelist}},json_data=True) else: return setJSON(self._getPath(id),{'resume':resume,'filelist':filelist}) def _get(self, id): self.log("[%s] runAction, _get: url = %s"%(id,self.optionValues[1])) if self.optionValues[1]: return requestURL(self.optionValues[1],json_data=True) else: return getJSON(self._getPath(id)) def _getResume(self, id): return (self._get(id).get('resume') or {"idx":0,"position":0.0,"total":0.0,"file":"","update":{"instance":"","time":-1}}) def _getFilelist(self, id): return (self._get(id).get('filelist') or []) def _getPlaylist(self, id): resume = self._getResume(id) filelist = self._getFilelist(id) if len(filelist) > 0: for idx, item in enumerate(filelist): if item.get('file') == resume.get('file',-1): resume.update({'idx':0}) item['resume'] = resume filelist = filelist[idx:] if self._set(id, filelist, resume): break self.log('[%s] runAction, _getPlaylist: filelist = %s, resume = %s'%(id,len(filelist),resume)) return filelist def runAction(self, actionid, citem, parameter, inherited): self.log('[%s] runAction, actionid = %s,'%(citem.get('id'),actionid)) if actionid == RULES_ACTION_CHANNEL_START: self.storedValues[0] = inherited.padScheduling self.storedValues[1] = self._getFilelist(citem.get('id')) inherited.padScheduling = False #disable guide padding with duplicates to fill quota. self.log("[%s] runAction, setting padScheduling = %s"%(citem.get('id'),inherited.padScheduling)) elif actionid == RULES_ACTION_CHANNEL_CITEM: try: parameter["rules"]["3000"]["values"].update({"1":self._getURL(parameter.get('id'))}) self.log("runAction, updated rule values = %s"%( parameter["rules"]["3000"]["values"]), xbmc.LOGERROR) except Exception as e: self.log("runAction, updated rule values failed! %s"%(e), xbmc.LOGERROR) elif actionid == RULES_ACTION_CHANNEL_TEMP_CITEM: parameter['resume'] = True elif actionid == RULES_ACTION_CHANNEL_BUILD_FILEARRAY_PRE: #load cached filelist if not outdated, else new buildFileList if self._getTotRuntime(citem.get('id'), self.storedValues[1]) >= (MIN_GUIDEDAYS * 86400): self.log("[%s] runAction, returning valid cached filelist = %s"%(citem.get('id'),len(self.storedValues[1]))) return [self.storedValues[1]] elif actionid == RULES_ACTION_CHANNEL_BUILD_FILEARRAY_POST: #check if cached filelist is the same as existing filelist. if [self.storedValues[1]] != parameter: self.storedValues[2] = True #finish building new filelist ie. injection rules. elif len(self.storedValues[1]) > 0: return True #use cached filelist elif actionid == RULES_ACTION_CHANNEL_BUILD_FILELIST_POST:#update cached filelist if self.storedValues[2] and len(parameter) > 0: self.log("[%s] runAction, updating fileList (%s) extending by (%s)"%(citem.get('id'),len(self.storedValues[1]),len(parameter))) self.storedValues[1].extend(parameter) self._set(citem.get('id'), self.storedValues[1], self._getResume(citem.get('id'))) elif actionid == RULES_ACTION_CHANNEL_BUILD_FILELIST_RETURN: if parameter: self.log("[%s] runAction, returning fileList (%s)"%(citem.get('id'),len(self.storedValues[1]))) return self.storedValues[1] elif actionid == RULES_ACTION_CHANNEL_BUILD_TIME_PRE: if len(parameter) > 0: if inherited.xmltv.clrProgrammes(citem): return self._buildSchedule(citem, parameter, inherited) elif actionid == RULES_ACTION_CHANNEL_STOP: inherited.padScheduling = self.storedValues[0] self.storedValues[1] = [] self.log("[%s] runAction, restoring padScheduling = %s"%(citem.get('id'),inherited.padScheduling)) elif actionid == RULES_ACTION_PLAYBACK_RESUME: return self._getPlaylist(citem.get('id')) elif actionid == RULES_ACTION_PLAYER_START: self.storedValues[1] = self._getFilelist(citem.get('id')) elif actionid in [RULES_ACTION_PLAYER_CHANGE, RULES_ACTION_PLAYER_STOP]: now = getUTCstamp() if parameter.get('resume',{}).get('file') and (parameter.get('resume',{}).get('updated',{}).get('time') or -1) < now: parameter.get('resume').update({'updated':{'instance':SETTINGS.getFriendlyName(),'time':now}}) self.log("[%s] runAction, updating resume = %s"%(citem.get('id'),parameter.get('resume'))) self._set(citem.get('id'),self.storedValues[1],parameter.get('resume')) return parameter