from __future__ import absolute_import, print_function
import os
import time
import Components.PluginComponent
import Screens.Standby
import NavigationInstance
from enigma import eServiceCenter, eServiceReference, eEPGCache, getDesktop, eTimer, eConsoleAppContainer
from Components.ActionMap import ActionMap
from Components.Button import Button
from Components.config import config, getConfigListEntry, ConfigClock, ConfigEnableDisable, ConfigNumber, ConfigInteger, ConfigSelection, ConfigSubDict, ConfigSubsection, ConfigText, ConfigYesNo, NoSave
from Components.ConfigList import ConfigListScreen
from Components.Console import Console
from Components.Label import Label
from Components.ScrollLabel import ScrollLabel
from Plugins.Plugin import PluginDescriptor
from Screens.ChoiceBox import ChoiceBox
from Screens.MessageBox import MessageBox
from Screens.VirtualKeyBoard import VirtualKeyBoard
from Screens.Screen import Screen
from Tools import Notifications
from Tools.Directories import SCOPE_PLUGINS, fileExists, resolveFilename
from Tools.FuzzyDate import FuzzyTime
from Tools.StbHardware import getFPWasTimerWakeup
from . import EPGConfig, EPGImport, ExpandableSelectionList, _, filtersServices, log
def lastMACbyte():
try:
return int(open('/sys/class/net/eth0/address').readline().strip()[-2:], 16)
except:
return 256
def calcDefaultStarttime():
try:
# Use the last MAC byte as time offset (half-minute intervals)
offset = lastMACbyte() * 30
except:
offset = 7680
return (5 * 60 * 60) + offset
#Set default configuration
config.plugins.epgimport = ConfigSubsection()
config.plugins.epgimport.enabled = ConfigEnableDisable(default=True)
config.plugins.epgimport.repeat_import = ConfigInteger(default=0, limits=(0, 23))
config.plugins.epgimport.runboot = ConfigSelection(default="4", choices=[
("1", _("always")),
("2", _("only manual boot")),
("3", _("only automatic boot")),
("4", _("never"))
])
config.plugins.epgimport.runboot_restart = ConfigYesNo(default=False)
config.plugins.epgimport.runboot_day = ConfigYesNo(default=False)
config.plugins.epgimport.wakeup = ConfigClock(default=calcDefaultStarttime())
config.plugins.epgimport.showinextensions = ConfigYesNo(default=True)
config.plugins.epgimport.deepstandby = ConfigSelection(default="skip", choices=[
("wakeup", _("wake up and import")),
("skip", _("skip the import"))
])
config.plugins.epgimport.loadepg_only = ConfigSelection(default="default", choices=[
("default", _("checking service reference(default)")),
("iptv", _("only IPTV channels")),
("all", _("all channels"))
])
config.plugins.epgimport.execute_shell = ConfigYesNo(default=False)
config.plugins.epgimport.shell_name = ConfigText(default="")
config.plugins.epgimport.standby_afterwakeup = ConfigYesNo(default=False)
config.plugins.epgimport.run_after_standby = ConfigYesNo(default=False)
config.plugins.epgimport.shutdown = ConfigYesNo(default=False)
config.plugins.epgimport.longDescDays = ConfigNumber(default=5)
config.plugins.epgimport.showinmainmenu = ConfigYesNo(default=False)
config.plugins.epgimport.deepstandby_afterimport = NoSave(ConfigYesNo(default=False))
config.plugins.epgimport.parse_autotimer = ConfigYesNo(default=False)
config.plugins.epgimport.import_onlybouquet = ConfigYesNo(default=False)
config.plugins.epgimport.clear_oldepg = ConfigYesNo(default=False)
config.plugins.epgimport.filter_custom_channel = ConfigYesNo(default=True)
config.plugins.epgimport.day_profile = ConfigSelection(choices=[("1", _("Press OK"))], default="1")
config.plugins.extra_epgimport = ConfigSubsection()
config.plugins.extra_epgimport.last_import = ConfigText(default="0")
config.plugins.extra_epgimport.day_import = ConfigSubDict()
for i in range(7):
config.plugins.extra_epgimport.day_import[i] = ConfigEnableDisable(default=True)
weekdays = [
_("Monday"),
_("Tuesday"),
_("Wednesday"),
_("Thursday"),
_("Friday"),
_("Saturday"),
_("Sunday"),
]
# historically located (not a problem, we want to update it)
CONFIG_PATH = '/etc/epgimport'
# Global variable
autoStartTimer = None
_session = None
BouquetChannelListList = None
serviceIgnoreList = None
filterCounter = 0
isFilterRunning = 0
def getAlternatives(service):
if not service:
return None
alternativeServices = eServiceCenter.getInstance().list(service)
return alternativeServices and alternativeServices.getContent("S", True)
def getRefNum(ref):
ref = ref.split(':')[3:7]
try:
return int(ref[0], 16) << 48 | int(ref[1], 16) << 32 | int(ref[2], 16) << 16 | int(ref[3], 16) >> 16
except:
return
def getBouquetChannelList():
channels = []
global isFilterRunning, filterCounter
isFilterRunning = 1
serviceHandler = eServiceCenter.getInstance()
mask = (eServiceReference.isMarker | eServiceReference.isDirectory)
altrernative = eServiceReference.isGroup
if config.usage.multibouquet.value:
bouquet_rootstr = '1:7:1:0:0:0:0:0:0:0:FROM BOUQUET "bouquets.tv" ORDER BY bouquet'
bouquet_root = eServiceReference(bouquet_rootstr)
list = serviceHandler.list(bouquet_root)
if list:
while True:
s = list.getNext()
if not s.valid():
break
if s.flags & eServiceReference.isDirectory:
info = serviceHandler.info(s)
if info:
clist = serviceHandler.list(s)
if clist:
while True:
service = clist.getNext()
filterCounter += 1
if not service.valid():
break
if not (service.flags & mask):
if service.flags & altrernative:
altrernative_list = getAlternatives(service)
if altrernative_list:
for channel in altrernative_list:
refnum = getRefNum(channel)
if refnum and refnum not in channels:
channels.append(refnum)
else:
refnum = getRefNum(service.toString())
if refnum and refnum not in channels:
channels.append(refnum)
else:
bouquet_rootstr = '1:7:1:0:0:0:0:0:0:0:FROM BOUQUET "userbouquet.favourites.tv" ORDER BY bouquet'
bouquet_root = eServiceReference(bouquet_rootstr)
services = serviceHandler.list(bouquet_root)
if not services is None:
while True:
service = services.getNext()
filterCounter += 1
if not service.valid():
break
if not (service.flags & mask):
if service.flags & altrernative:
altrernative_list = getAlternatives(service)
if altrernative_list:
for channel in altrernative_list:
refnum = getRefNum(channel)
if refnum and refnum not in channels:
channels.append(refnum)
else:
refnum = getRefNum(service.toString())
if refnum and refnum not in channels:
channels.append(refnum)
isFilterRunning = 0
return channels
# Filter servicerefs that this box can display by starting a fake recording.
def channelFilter(ref):
if not ref:
return False
loadepg_only = config.plugins.epgimport.loadepg_only.value
if loadepg_only != "default":
if loadepg_only == "all":
return True
elif loadepg_only == "iptv":
return ("%3a//" not in ref.lower() or ref.startswith("1")) and False or True
sref = eServiceReference(ref)
refnum = getRefNum(sref.toString())
if config.plugins.epgimport.import_onlybouquet.value:
global BouquetChannelListList
if BouquetChannelListList is None:
BouquetChannelListList = getBouquetChannelList()
if refnum not in BouquetChannelListList:
print("Serviceref not in bouquets:", sref.toString(), file=log)
return False
global serviceIgnoreList
if serviceIgnoreList is None:
serviceIgnoreList = [getRefNum(x) for x in filtersServices.filtersServicesList.servicesList()]
if refnum in serviceIgnoreList:
print("Serviceref is in ignore list:", sref.toString(), file=log)
return False
if "%3a//" in ref.lower():
# print("URL detected in serviceref, not checking fake recording on serviceref:", ref, file=log)
return True
fakeRecService = NavigationInstance.instance.recordService(sref, True)
if fakeRecService:
fakeRecResult = fakeRecService.start(True)
NavigationInstance.instance.stopRecordService(fakeRecService)
# -7 (errNoSourceFound) occurs when tuner is disconnected.
r = fakeRecResult in (0, -7)
return r
print("Invalid serviceref string:", ref, file=log)
return False
epgimport = EPGImport.EPGImport(eEPGCache.getInstance(), channelFilter)
lastImportResult = None
def startImport():
if not epgimport.isImportRunning():
EPGImport.HDD_EPG_DAT = config.misc.epgcache_filename.value
if config.plugins.epgimport.filter_custom_channel.value:
EPGConfig.filterCustomChannel = True
else:
EPGConfig.filterCustomChannel = False
if config.plugins.epgimport.clear_oldepg.value and hasattr(epgimport.epgcache, 'flushEPG'):
EPGImport.unlink_if_exists(EPGImport.HDD_EPG_DAT)
EPGImport.unlink_if_exists(EPGImport.HDD_EPG_DAT + '.backup')
epgimport.epgcache.flushEPG()
epgimport.onDone = doneImport
epgimport.beginImport(longDescUntil=config.plugins.epgimport.longDescDays.value * 24 * 3600 + time.time())
else:
print("[startImport] Already running, won't start again", file=log)
##################################
# Configuration GUI
HD = False
try:
if getDesktop(0).size().width() >= 1280:
HD = True
except:
pass
class EPGImportConfig(ConfigListScreen, Screen):
if HD:
skin = """
Default
"""
else:
skin = """
Default
"""
def __init__(self, session):
Screen.__init__(self, session)
self.setTitle(_("EPG Import Configuration"))
self["status"] = Label()
self["statusbar"] = Label()
self["key_red"] = Button(_("Cancel"))
self["key_green"] = Button(_("Save"))
self["key_yellow"] = Button(_("Manual"))
self["key_blue"] = Button(_("Sources"))
self["description"] = Label("")
self["setupActions"] = ActionMap(["SetupActions", "ColorActions", "TimerEditActions", "MovieSelectionActions"],
{
"red": self.keyCancel,
"green": self.keyGreen,
"yellow": self.doimport,
"blue": self.dosources,
"cancel": self.keyCancel,
"ok": self.keyOk,
"log": self.keyInfo,
"contextMenu": self.openMenu,
}, -1)
ConfigListScreen.__init__(self, [], session)
self.lastImportResult = None
self.prev_onlybouquet = config.plugins.epgimport.import_onlybouquet.value
self.initConfig()
self.createSetup()
self.filterStatusTemplate = _("Filtering: %s\nPlease wait!")
self.importStatusTemplate = _("Importing: %s\n%s events")
self.updateTimer = eTimer()
self.updateTimer.callback.append(self.updateStatus)
self.updateTimer.start(2000)
self.updateStatus()
self.onLayoutFinish.append(self.createSummary)
def initConfig(self):
dx = 4 * " "
self.EPG = config.plugins.epgimport
self.cfg_enabled = getConfigListEntry(_("Automatic import EPG"), self.EPG.enabled, _("When enabled, it allows you to schedule an automatic EPG update at the given days and time."))
self.cfg_wakeup = getConfigListEntry(dx + _("Automatic start time"), self.EPG.wakeup, _("Specify the time for the automatic EPG update."))
self.cfg_deepstandby = getConfigListEntry(dx + _("When in deep standby"), self.EPG.deepstandby, _("Choose the action to perform when the box is in deep standby and the automatic EPG update should normally start."))
self.cfg_shutdown = getConfigListEntry(dx + _("Return to deep standby after import"), self.EPG.shutdown, _("This will turn back waked up box into deep-standby after automatic EPG import."))
self.cfg_standby_afterwakeup = getConfigListEntry(dx + _("Standby at startup"), self.EPG.standby_afterwakeup, _("The waked up box will be turned into standby after automatic EPG import wake up."))
self.cfg_day_profile = getConfigListEntry(_("Choice days for start import"), self.EPG.day_profile, _("You can select the day(s) when the EPG update must be performed."))
self.cfg_runboot = getConfigListEntry(_("Start import after booting up"), self.EPG.runboot, _("Specify in which case the EPG must be automatically updated after the box has booted."))
self.cfg_import_onlybouquet = getConfigListEntry(dx + _("Load EPG only services in bouquets"), self.EPG.import_onlybouquet, _("To save memory you can decide to only load EPG data for the services that you have in your bouquet files."))
self.cfg_loadepg_only = getConfigListEntry(_("Load EPG"), self.EPG.loadepg_only, _("Select load EPG mode for services."))
self.cfg_runboot_day = getConfigListEntry(dx + _("Consider setting \"Days Profile\""), self.EPG.runboot_day, _("When you decide to import the EPG after the box booted mention if the \"days profile\" must be take into consideration or not."))
self.cfg_runboot_restart = getConfigListEntry(dx + _("Skip import on restart GUI"), self.EPG.runboot_restart, _("When you restart the GUI you can decide to skip or not the EPG data import."))
self.cfg_showinextensions = getConfigListEntry(_("Show \"EPG import now\" in extensions"), self.EPG.showinextensions, _("Display a shortcut \"EPG import now\" in the extension menu. This menu entry will immediately start the EPG update process when selected."))
self.cfg_showinmainmenu = getConfigListEntry(_("Show \"EPG import\" in epg menu"), self.EPG.showinmainmenu, _("Display a shortcut \"EPG import\" in your STB epg menu screen. This allows you to access the configuration."))
self.cfg_longDescDays = getConfigListEntry(_("Load long descriptions up to X days"), self.EPG.longDescDays, _("Define the number of days that you want to get the full EPG data, reducing this number can help you to save memory usage on your box. But you are also limited with the EPG provider available data. You will not have 15 days EPG if it only provide 7 days data."))
self.cfg_parse_autotimer = getConfigListEntry(_("Run AutoTimer after import"), self.EPG.parse_autotimer, _("You can start automatically the plugin AutoTimer after the EPG data update to have it refreshing its scheduling after EPG data refresh."))
self.cfg_clear_oldepg = getConfigListEntry(_("Clearing current EPG before import"), config.plugins.epgimport.clear_oldepg, _("This will clear the current EPG data in memory before updating the EPG data. This allows you to always have a clean new EPG with the latest EPG data, for example in case of program changes between refresh, otherwise EPG data are cumulative."))
self.cfg_filter_custom_channel = getConfigListEntry(_("Also apply \"channel id\" filtering on custom.channels.xml"), self.EPG.filter_custom_channel, _("This is for advanced users that are using the channel id filtering feature. If enabled, the filter rules defined into /etc/epgimport/channel_id_filter.conf will also be applied on your /etc/epgimport/custom.channels.xml file."))
self.cfg_execute_shell = getConfigListEntry(_("Execute shell command before import EPG"), self.EPG.execute_shell, _("When enabled, then you can run the desired script before starting the import, after which the import of the EPG will begin."))
self.cfg_shell_name = getConfigListEntry(dx + _("Shell command name"), self.EPG.shell_name, _("Enter shell command name."))
self.cfg_run_after_standby = getConfigListEntry(_("Start import after standby"), self.EPG.run_after_standby, _("Start import after resuming from standby mode."))
self.cfg_repeat_import = getConfigListEntry(dx + _("Hours after which the import is repeated"), self.EPG.repeat_import, _("Number of hours (1-23, or 0 for no repeat) after which the import is repeated. This value is not saved and will be reset when the GUI restarts."))
def createSetup(self):
self.list = [self.cfg_enabled]
if self.EPG.enabled.value:
self.list.append(self.cfg_wakeup)
self.list.append(self.cfg_deepstandby)
if self.EPG.deepstandby.value == "wakeup":
self.list.append(self.cfg_shutdown)
if not self.EPG.shutdown.value:
self.list.append(self.cfg_standby_afterwakeup)
self.list.append(self.cfg_repeat_import)
else:
self.list.append(self.cfg_repeat_import)
self.list.append(self.cfg_day_profile)
self.list.append(self.cfg_runboot)
if self.EPG.runboot.value != "4":
self.list.append(self.cfg_runboot_day)
self.list.append(self.cfg_runboot_restart)
self.list.append(self.cfg_run_after_standby)
self.list.append(self.cfg_loadepg_only)
if self.EPG.loadepg_only.value == "default":
self.list.append(self.cfg_import_onlybouquet)
if hasattr(eEPGCache, 'flushEPG'):
self.list.append(self.cfg_clear_oldepg)
self.list.append(self.cfg_filter_custom_channel)
self.list.append(self.cfg_longDescDays)
self.list.append(self.cfg_execute_shell)
if self.EPG.execute_shell.value:
self.list.append(self.cfg_shell_name)
if fileExists(resolveFilename(SCOPE_PLUGINS, "Extensions/AutoTimer/plugin.pyo")) or fileExists(resolveFilename(SCOPE_PLUGINS, "Extensions/AutoTimer/plugin.pyc")):
try:
from Plugins.Extensions.AutoTimer.AutoTimer import AutoTimer
self.list.append(self.cfg_parse_autotimer)
except:
print("[EPGImport] AutoTimer plugin not installed correctly", file=log)
self.list.append(self.cfg_showinmainmenu)
self.list.append(self.cfg_showinextensions)
self["config"].list = self.list
self["config"].setList(self.list)
def newConfig(self):
cur = self["config"].getCurrent()
if cur in (self.cfg_enabled, self.cfg_shutdown, self.cfg_deepstandby, self.cfg_runboot, self.cfg_loadepg_only, self.cfg_execute_shell):
self.createSetup()
def keyGreen(self):
self.updateTimer.stop()
if self.EPG.parse_autotimer.value and (not fileExists(resolveFilename(SCOPE_PLUGINS, "Extensions/AutoTimer/plugin.pyo")) or not fileExists(resolveFilename(SCOPE_PLUGINS, "Extensions/AutoTimer/plugin.pyc"))):
self.EPG.parse_autotimer.value = False
if self.EPG.deepstandby.value == "skip" and self.EPG.shutdown.value:
self.EPG.shutdown.value = False
if self.EPG.shutdown.value:
self.EPG.standby_afterwakeup.value = False
self.EPG.repeat_import.value = 0
self.EPG.save()
if self.prev_onlybouquet != config.plugins.epgimport.import_onlybouquet.value or (autoStartTimer is not None and autoStartTimer.prev_multibouquet != config.usage.multibouquet.value):
EPGConfig.channelCache = {}
self.close(True)
def keyLeft(self):
ConfigListScreen.keyLeft(self)
self.newConfig()
def keyRight(self):
ConfigListScreen.keyRight(self)
self.newConfig()
def keyOk(self):
ConfigListScreen.keyOK(self)
sel = self["config"].getCurrent() and self["config"].getCurrent()[1]
if sel:
if sel == self.EPG.day_profile:
self.session.open(EPGImportProfile)
elif sel == self.EPG.shell_name:
self.session.openWithCallback(self.textEditCallback, VirtualKeyBoard, text=self.EPG.shell_name.value)
def textEditCallback(self, callback=None):
if callback != None:
self.EPG.shell_name.value = callback
self.EPG.shell_name.save()
self.createSetup()
def updateStatus(self):
text = ""
global isFilterRunning, filterCounter
if isFilterRunning == 1:
text = self.filterStatusTemplate % (str(filterCounter))
elif epgimport.isImportRunning():
src = epgimport.source
text = self.importStatusTemplate % (src.description, epgimport.eventCount)
self["status"].setText(text)
if lastImportResult and (lastImportResult != self.lastImportResult):
start, count = lastImportResult
try:
d, t = FuzzyTime(start, inPast=True)
except:
# Not all images have inPast
d, t = FuzzyTime(start)
self["statusbar"].setText(_("Last: %s %s, %d events") % (d, t, count))
self.lastImportResult = lastImportResult
def keyInfo(self):
self.session.open(MessageBox, _("Last import: %s events") % config.plugins.extra_epgimport.last_import.value, type=MessageBox.TYPE_INFO)
def doimport(self, one_source=None):
if epgimport.isImportRunning():
print("[EPGImport] Already running, won't start again", file=log)
self.session.open(MessageBox, _("EPGImport\nImport of epg data is still in progress. Please wait."), MessageBox.TYPE_ERROR, timeout=10, close_on_any_key=True)
return
if self.prev_onlybouquet != config.plugins.epgimport.import_onlybouquet.value or (autoStartTimer is not None and autoStartTimer.prev_multibouquet != config.usage.multibouquet.value):
EPGConfig.channelCache = {}
if one_source is None:
cfg = EPGConfig.loadUserSettings()
else:
cfg = one_source
sources = [s for s in EPGConfig.enumSources(CONFIG_PATH, filter=cfg["sources"])]
EPGImport.ServerStatusList = {}
if not sources:
self.session.open(MessageBox, _("No active EPG sources found, nothing to do"), MessageBox.TYPE_INFO, timeout=10, close_on_any_key=True)
return
# make it a stack, first on top.
sources.reverse()
epgimport.sources = sources
self.session.openWithCallback(self.do_import_callback, MessageBox, _("EPGImport\nImport of epg data will start.\nThis may take a few minutes.\nIs this ok?"), MessageBox.TYPE_YESNO, timeout=15, default=True)
def do_import_callback(self, confirmed):
if not confirmed:
return
try:
if config.plugins.epgimport.execute_shell.value and config.plugins.epgimport.shell_name.value:
Console().eBatch([config.plugins.epgimport.shell_name.value], self.executeShellEnd, debug=True)
else:
startImport()
except Exception as e:
print("[EPGImport] Error at start:", e, file=log)
self.session.open(MessageBox, _("EPGImport Plugin\nFailed to start:\n") + str(e), MessageBox.TYPE_ERROR, timeout=15, close_on_any_key=True)
self.updateStatus()
def executeShellEnd(self, *args, **kwargs):
startImport()
def dosources(self):
self.session.openWithCallback(self.sourcesDone, EPGImportSources)
def sourcesDone(self, confirmed, sources, cfg):
# Called with True and list of config items on Okay.
print("sourcesDone(): ", confirmed, sources, file=log)
if cfg is not None:
self.doimport(one_source=cfg)
def openMenu(self):
menu = [(_("Show log"), self.showLog)]
if config.plugins.epgimport.loadepg_only.value == "default":
menu.append((_("Ignore services list"), self.openIgnoreList))
text = _("Select action")
def setAction(choice):
if choice:
choice[1]()
self.session.openWithCallback(setAction, ChoiceBox, title=text, list=menu)
def openIgnoreList(self):
self.session.open(filtersServices.filtersServicesSetup)
def showLog(self):
self.session.open(EPGImportLog)
class EPGImportSources(Screen):
"Pick sources from config"
skin = """
Default
"""
def __init__(self, session):
Screen.__init__(self, session)
self.setTitle(_("EPG Import Sources"))
self["key_red"] = Button(_("Cancel"))
self["key_green"] = Button(_("Save"))
self["key_blue"] = Button()
cfg = EPGConfig.loadUserSettings()
filter = cfg["sources"]
tree = []
cat = None
for x in EPGConfig.enumSources(CONFIG_PATH, filter=None, categories=True):
if hasattr(x, 'description'):
sel = (filter is None) or (x.description in filter)
entry = (x.description, x.description, sel)
if cat is None:
# If no category defined, use a default one.
cat = ExpandableSelectionList.category("[.]")
tree.append(cat)
cat[0][2].append(entry)
if sel:
ExpandableSelectionList.expand(cat, True)
else:
cat = ExpandableSelectionList.category(x)
tree.append(cat)
self["list"] = ExpandableSelectionList.ExpandableSelectionList(tree, enableWrapAround=True)
if tree:
self["key_yellow"] = Button(_("Import current source"))
else:
self["key_yellow"] = Button()
self["setupActions"] = ActionMap(["SetupActions", "ColorActions"],
{
"red": self.cancel,
"green": self.save,
"yellow": self.do_import,
"save": self.save,
"cancel": self.cancel,
"ok": self["list"].toggleSelection,
}, -2)
def save(self):
# Make the entries unique through a set
sources = list(set([item[1] for item in self["list"].enumSelected()]))
print("[EPGImport] Selected sources:", sources, file=log)
EPGConfig.storeUserSettings(sources=sources)
self.close(True, sources, None)
def cancel(self):
self.close(False, None, None)
def do_import(self):
list = self["list"].list
if list and len(list) > 0:
try:
idx = self["list"].getSelectedIndex()
item = self["list"].list[idx][0]
source = [item[1] or ""]
cfg = {"sources": source}
print("[EPGImport] Selected source: ", source, file=log)
except Exception as e:
print("[EPGImport] Error at selected source:", e, file=log)
else:
if cfg["sources"] != "":
self.close(False, None, cfg)
class EPGImportProfile(ConfigListScreen, Screen):
skin = """
"""
def __init__(self, session):
Screen.__init__(self, session)
self.setTitle(_("Days Profile"))
self.list = []
for i in range(7):
self.list.append(getConfigListEntry(weekdays[i], config.plugins.extra_epgimport.day_import[i]))
ConfigListScreen.__init__(self, self.list)
self["key_red"] = Button(_("Cancel"))
self["key_green"] = Button(_("Save"))
self["setupActions"] = ActionMap(["SetupActions", "ColorActions"],
{
"red": self.keyCancel,
"green": self.save,
"save": self.save,
"cancel": self.keyCancel,
"ok": self.save,
}, -2)
def save(self):
if not config.plugins.extra_epgimport.day_import[0].value:
if not config.plugins.extra_epgimport.day_import[1].value:
if not config.plugins.extra_epgimport.day_import[2].value:
if not config.plugins.extra_epgimport.day_import[3].value:
if not config.plugins.extra_epgimport.day_import[4].value:
if not config.plugins.extra_epgimport.day_import[5].value:
if not config.plugins.extra_epgimport.day_import[6].value:
self.session.open(MessageBox, _("You may not use this settings!\nAt least one day a week should be included!"), MessageBox.TYPE_INFO, timeout=6)
return
ConfigListScreen.keySave(self)
class EPGImportLog(Screen):
skin = """
Default
"""
def __init__(self, session):
self.session = session
Screen.__init__(self, session)
self.setTitle(_("EPG Import Log"))
self["key_red"] = Button(_("Clear"))
self["key_green"] = Button()
self["key_yellow"] = Button()
self["key_blue"] = Button(_("Save"))
self["list"] = ScrollLabel(log.getvalue())
self["actions"] = ActionMap(["DirectionActions", "OkCancelActions", "ColorActions"],
{
"red": self.clear,
"green": self.cancel,
"yellow": self.cancel,
"save": self.save,
"blue": self.save,
"cancel": self.cancel,
"ok": self.cancel,
"left": self["list"].pageUp,
"right": self["list"].pageDown,
"up": self["list"].pageUp,
"down": self["list"].pageDown,
"pageUp": self["list"].pageUp,
"pageDown": self["list"].pageDown
}, -2)
def save(self):
try:
f = open('/tmp/epgimport.log', 'w')
f.write(log.getvalue())
self.session.open(MessageBox, _("Write to /tmp/epgimport.log"), MessageBox.TYPE_INFO, timeout=5, close_on_any_key=True)
f.close()
except Exception as e:
self["list"].setText("Failed to write /tmp/epgimport.log:str" + str(e))
self.close(True)
def cancel(self):
self.close(False)
def clear(self):
log.logfile.seek(0, 0)
log.logfile.truncate()
self.close(False)
class EPGImportDownloader(MessageBox):
def __init__(self, session):
MessageBox.__init__(self, session, _("Last import: ") + config.plugins.extra_epgimport.last_import.value + _(" events\n") + _("\nImport of epg data will start.\nThis may take a few minutes.\nIs this ok?"), MessageBox.TYPE_YESNO)
self.skinName = "MessageBox"
def msgClosed(ret):
global autoStartTimer
if ret:
if autoStartTimer is not None and not epgimport.isImportRunning():
print("[EPGImport] Run manual starting import", file=log)
autoStartTimer.runImport()
def start_import(session, **kwargs):
session.openWithCallback(msgClosed, EPGImportDownloader)
def main(session, **kwargs):
session.openWithCallback(doneConfiguring, EPGImportConfig)
def doneConfiguring(retval=False):
if retval is True:
if autoStartTimer is not None:
autoStartTimer.update(clock=True)
def doneImport(reboot=False, epgfile=None):
global _session, lastImportResult, BouquetChannelListList, serviceIgnoreList
BouquetChannelListList = None
serviceIgnoreList = None
lastImportResult = (time.time(), epgimport.eventCount)
try:
start, count = lastImportResult
#localtime = time.asctime(time.localtime(time.time()))
localtime = time.strftime('%d.%m.%Y - %H:%M:%S', (time.localtime(time.time())))
lastimport = "%s, %d" % (localtime, count)
config.plugins.extra_epgimport.last_import.value = lastimport
config.plugins.extra_epgimport.last_import.save()
print("[EPGImport] Save last import date and count event", file=log)
except:
print("[EPGImport] Error to save last import date and count event", file=log)
if reboot:
if Screens.Standby.inStandby:
print("[EPGImport] Restart enigma2", file=log)
restartEnigma(True)
else:
msg = _("EPG Import finished, %d events") % epgimport.eventCount + "\n" + _("You must restart Enigma2 to load the EPG data,\nis this OK?")
_session.openWithCallback(restartEnigma, MessageBox, msg, MessageBox.TYPE_YESNO, timeout=15, default=True)
print("[EPGImport] Need restart enigma2", file=log)
else:
if config.plugins.epgimport.parse_autotimer.value and (fileExists(resolveFilename(SCOPE_PLUGINS, "Extensions/AutoTimer/plugin.pyo")) or fileExists(resolveFilename(SCOPE_PLUGINS, "Extensions/AutoTimer/plugin.pyc"))):
try:
from Plugins.Extensions.AutoTimer.plugin import autotimer
if autotimer is None:
from Plugins.Extensions.AutoTimer.AutoTimer import AutoTimer
autotimer = AutoTimer()
autotimer.readXml()
checkDeepstandby(_session, parse=True)
autotimer.parseEPGAsync(simulateOnly=False)
print("[EPGImport] Run start parse autotimers", file=log)
except:
print("[EPGImport] Could not start autotimers", file=log)
checkDeepstandby(_session, parse=False)
else:
checkDeepstandby(_session, parse=False)
class checkDeepstandby:
def __init__(self, session, parse=False):
self.session = session
if config.plugins.epgimport.enabled.value:
if parse:
self.FirstwaitCheck = eTimer()
self.FirstwaitCheck.callback.append(self.runCheckDeepstandby)
self.FirstwaitCheck.startLongTimer(600)
print("[EPGImport] Wait for parse autotimers 600 sec.", file=log)
else:
self.runCheckDeepstandby()
def runCheckDeepstandby(self):
print("[EPGImport] Run check deep standby after import")
if config.plugins.epgimport.shutdown.value and config.plugins.epgimport.deepstandby.value == 'wakeup':
if config.plugins.epgimport.deepstandby_afterimport.value and getFPWasTimerWakeup():
config.plugins.epgimport.deepstandby_afterimport.value = False
if Screens.Standby.inStandby and not self.session.nav.getRecordings() and not Screens.Standby.inTryQuitMainloop:
print("[EPGImport] Returning to deep standby after wake up for import", file=log)
self.session.open(Screens.Standby.TryQuitMainloop, 1)
else:
print("[EPGImport] No return to deep standby, not standby or running recording", file=log)
def restartEnigma(confirmed):
if not confirmed:
return
# save state of enigma, so we can return to previeus state
if Screens.Standby.inStandby:
try:
open('/tmp/enigmastandby', 'wb').close()
except:
print("Failed to create /tmp/enigmastandby", file=log)
else:
try:
os.remove("/tmp/enigmastandby")
except:
pass
# now reboot
_session.open(Screens.Standby.TryQuitMainloop, 3)
##################################
# Autostart section
class AutoStartTimer:
def __init__(self, session):
self.session = session
self.prev_onlybouquet = config.plugins.epgimport.import_onlybouquet.value
self.prev_multibouquet = config.usage.multibouquet.value
self.clock = config.plugins.epgimport.wakeup.value
self.autoStartImport = eTimer()
self.autoStartImport.callback.append(self.onTimer)
self.onceRepeatImport = eTimer()
self.onceRepeatImport.callback.append(self.runImport)
self.pauseAfterFinishImportCheck = eTimer()
self.pauseAfterFinishImportCheck.callback.append(self.afterFinishImportCheck)
self.pauseAfterFinishImportCheck.startLongTimer(30)
self.container = None
config.misc.standbyCounter.addNotifier(self.standbyCounterChangedRunImport)
self.update()
def getWakeTime(self):
if config.plugins.epgimport.enabled.value:
nowt = time.time()
now = time.localtime(nowt)
return int(time.mktime((now.tm_year, now.tm_mon, now.tm_mday, self.clock[0], self.clock[1], lastMACbyte() // 5, 0, now.tm_yday, now.tm_isdst)))
else:
return -1
def update(self, atLeast=0, clock=False):
self.autoStartImport.stop()
if clock and self.clock != config.plugins.epgimport.wakeup.value:
self.clock = config.plugins.epgimport.wakeup.value
self.onceRepeatImport.stop()
wake = self.getWakeTime()
now_t = time.time()
now = int(now_t)
now_day = time.localtime(now_t)
if wake > 0:
cur_day = int(now_day.tm_wday)
wakeup_day = WakeupDayOfWeek()
if wakeup_day == -1:
print("[EPGImport] wakeup day of week disabled", file=log)
return -1
if wake < now + atLeast:
wake += 86400 * wakeup_day
else:
if not config.plugins.extra_epgimport.day_import[cur_day].value:
wake += 86400 * wakeup_day
next = wake - now
self.autoStartImport.startLongTimer(next)
else:
self.onceRepeatImport.stop()
wake = -1
print("[EPGImport] WakeUpTime now set to", wake, "(now=%s)" % now, file=log)
return wake
def runImport(self):
if self.prev_onlybouquet != config.plugins.epgimport.import_onlybouquet.value or self.prev_multibouquet != config.usage.multibouquet.value:
self.prev_onlybouquet = config.plugins.epgimport.import_onlybouquet.value
self.prev_multibouquet = config.usage.multibouquet.value
EPGConfig.channelCache = {}
cfg = EPGConfig.loadUserSettings()
sources = [s for s in EPGConfig.enumSources(CONFIG_PATH, filter=cfg["sources"])]
if sources:
sources.reverse()
epgimport.sources = sources
if config.plugins.epgimport.execute_shell.value and config.plugins.epgimport.shell_name.value:
if self.container:
del self.container
self.container = eConsoleAppContainer()
self.container.appClosed.append(self.executeShellEnd)
if self.container.execute(config.plugins.epgimport.shell_name.value):
self.executeShellEnd(-1)
else:
startImport()
def executeShellEnd(self, retval):
if self.container:
self.container.appClosed.remove(self.executeShellEnd)
self.container = None
startImport()
def onTimer(self):
self.autoStartImport.stop()
now = int(time.time())
print("[EPGImport] onTimer occured at", now, file=log)
wake = self.getWakeTime()
# If we're close enough, we're okay...
atLeast = 0
if wake - now < 60:
self.runImport()
repeat_time = config.plugins.epgimport.repeat_import.value
if repeat_time:
self.onceRepeatImport.startLongTimer(repeat_time * 3600)
print("[EPGImport] start once repeat timer, wait in nours -", repeat_time, file=log)
atLeast = 60
self.update(atLeast)
def getSources(self):
cfg = EPGConfig.loadUserSettings()
sources = [s for s in EPGConfig.enumSources(CONFIG_PATH, filter=cfg["sources"])]
if sources:
return True
return False
def getStatus(self):
wake_up = self.getWakeTime()
now_t = time.time()
now = int(now_t)
now_day = time.localtime(now_t)
if wake_up > 0:
cur_day = int(now_day.tm_wday)
wakeup_day = WakeupDayOfWeek()
if wakeup_day == -1:
print("[EPGImport] wakeup day of week disabled", file=log)
return -1
if wake_up < now:
wake_up += 86400 * wakeup_day
else:
if not config.plugins.extra_epgimport.day_import[cur_day].value:
wake_up += 86400 * wakeup_day
else:
wake_up = -1
return wake_up
def afterFinishImportCheck(self):
if config.plugins.epgimport.deepstandby.value == 'wakeup' and getFPWasTimerWakeup():
if os.path.exists("/tmp/enigmastandby") or os.path.exists("/tmp/.EPGImportAnswerBoot"):
print("[EPGImport] is restart enigma2", file=log)
else:
wake = self.getStatus()
now_t = time.time()
now = int(now_t)
if 0 < wake - now <= 60 * 5:
if config.plugins.epgimport.standby_afterwakeup.value:
if not Screens.Standby.inStandby:
Notifications.AddNotification(Screens.Standby.Standby)
print("[EPGImport] Run to standby after wake up", file=log)
if config.plugins.epgimport.shutdown.value:
if not config.plugins.epgimport.standby_afterwakeup.value:
if not Screens.Standby.inStandby:
Notifications.AddNotification(Screens.Standby.Standby)
print("[EPGImport] Run to standby after wake up for checking", file=log)
if not config.plugins.epgimport.deepstandby_afterimport.value:
config.plugins.epgimport.deepstandby_afterimport.value = True
self.wait_timer = eTimer()
self.wait_timer.timeout.get().append(self.startStandby)
print("[EPGImport] start wait_timer (10sec) for goto standby", file=log)
self.wait_timer.start(10000, True)
def afterStandbyRunImport(self):
if config.plugins.epgimport.run_after_standby.value:
print("[EPGImport] start import after standby", file=log)
self.runImport()
def standbyCounterChangedRunImport(self, configElement):
if Screens.Standby.inStandby:
try:
if self.afterStandbyRunImport not in Screens.Standby.inStandby.onClose:
Screens.Standby.inStandby.onClose.append(self.afterStandbyRunImport)
except:
print("[EPGImport] error inStandby.onClose append afterStandbyRunImport", file=log)
def startStandby(self):
if Screens.Standby.inStandby:
print("[EPGImport] add checking standby", file=log)
try:
if self.onLeaveStandbyFinishImportCheck not in Screens.Standby.inStandby.onClose:
Screens.Standby.inStandby.onClose.append(self.onLeaveStandbyFinishImportCheck)
except:
print("[EPGImport] error inStandby.onClose append .onLeaveStandbyFinishImportCheck", file=log)
def onLeaveStandbyFinishImportCheck(self):
if config.plugins.epgimport.deepstandby_afterimport.value:
config.plugins.epgimport.deepstandby_afterimport.value = False
print("[EPGImport] checking standby remove, not deep standby after import", file=log)
def WakeupDayOfWeek():
start_day = -1
try:
now = time.time()
now_day = time.localtime(now)
cur_day = int(now_day.tm_wday)
except:
cur_day = -1
if cur_day >= 0:
for i in (1, 2, 3, 4, 5, 6, 7):
if config.plugins.extra_epgimport.day_import[(cur_day + i) % 7].value:
return i
return start_day
def onBootStartCheck():
global autoStartTimer
print("[EPGImport] onBootStartCheck", file=log)
now = int(time.time())
wake = autoStartTimer.getStatus()
print("[EPGImport] now=%d wake=%d wake-now=%d" % (now, wake, wake - now), file=log)
if (wake < 0) or (wake - now > 600):
runboot = config.plugins.epgimport.runboot.value
on_start = False
if runboot == "1":
on_start = True
print("[EPGImport] is always boot", file=log)
elif runboot == "2" and not getFPWasTimerWakeup():
on_start = True
print("[EPGImport] is manual boot", file=log)
elif runboot == "3" and getFPWasTimerWakeup():
on_start = True
print("[EPGImport] is automatic boot", file=log)
flag = '/tmp/.EPGImportAnswerBoot'
if config.plugins.epgimport.runboot_restart.value:
if os.path.exists(flag):
on_start = False
print("[EPGImport] not starting import - is restart enigma2", file=log)
else:
try:
open(flag, 'wb').close()
except:
print("Failed to create /tmp/.EPGImportAnswerBoot", file=log)
if config.plugins.epgimport.runboot_day.value:
now = time.localtime()
cur_day = int(now.tm_wday)
if not config.plugins.extra_epgimport.day_import[cur_day].value:
on_start = False
print("[EPGImport] wakeup day of week does not match", file=log)
if on_start:
print("[EPGImport] starting import because auto-run on boot is enabled", file=log)
autoStartTimer.runImport()
else:
print("[EPGImport] import to start in less than 10 minutes anyway, skipping...", file=log)
def autostart(reason, session=None, **kwargs):
"called with reason=1 to during shutdown, with reason=0 at startup?"
global autoStartTimer
global _session
if reason == 0 and _session is None:
print("[EPGImport] autostart (%s) occured at" % reason, time.time(), file=log)
if session is not None:
_session = session
if autoStartTimer is None:
autoStartTimer = AutoStartTimer(session)
if config.plugins.epgimport.runboot.value != "4":
onBootStartCheck()
# If WE caused the reboot, put the box back in standby.
if os.path.exists("/tmp/enigmastandby"):
print("[EPGImport] Returning to standby", file=log)
if not Screens.Standby.inStandby:
Notifications.AddNotification(Screens.Standby.Standby)
try:
os.remove("/tmp/enigmastandby")
except:
pass
def getNextWakeup():
"returns timestamp of next time when autostart should be called"
if autoStartTimer:
if config.plugins.epgimport.enabled.value and config.plugins.epgimport.deepstandby.value == 'wakeup' and autoStartTimer.getSources():
print("[EPGImport] Will wake up from deep sleep", file=log)
return autoStartTimer.getStatus()
return -1
# we need this helper function to identify the descriptor
def run_from_epg_menu(menuid, **kwargs):
if menuid == "epg" and config.plugins.epgimport.showinmainmenu.getValue():
return [(_("EPG Import"), main, "epgimporter", 90)]
else:
return []
def setExtensionsmenu(el):
try:
if el.value:
Components.PluginComponent.plugins.addPlugin(extDescriptor)
else:
Components.PluginComponent.plugins.removePlugin(extDescriptor)
except Exception as e:
print("[EPGImport] Failed to update extensions menu:", e)
description = _("Automated EPG Importer")
config.plugins.epgimport.showinextensions.addNotifier(setExtensionsmenu, initial_call=False, immediate_feedback=False)
extDescriptor = PluginDescriptor(name=_("EPG import now"), description=description, where=PluginDescriptor.WHERE_EXTENSIONSMENU, fnc=start_import)
def Plugins(**kwargs):
result = [
PluginDescriptor(name="EPGImport", description=description, where=[PluginDescriptor.WHERE_AUTOSTART, PluginDescriptor.WHERE_SESSIONSTART], fnc=autostart, wakeupfnc=getNextWakeup),
PluginDescriptor(name=_("EPG Import"), description=description, where=[PluginDescriptor.WHERE_PLUGINMENU], icon="plugin.png", fnc=main),
PluginDescriptor(name="EPG importer", description=description, where=[PluginDescriptor.WHERE_MENU], fnc=run_from_epg_menu)
]
if config.plugins.epgimport.showinextensions.value:
result.append(extDescriptor)
return result