Repository URL to install this package:
|
Version:
1.4-r0 ▾
|
enigma2-plugin-extensions-epgload
/
usr
/
lib
/
enigma2
/
python
/
Plugins
/
Extensions
/
EPGLoad
/
plugin.py
|
|---|
from __future__ import print_function
from __future__ import division
#
# EPGLoad Plugin
# Plugin for loading XMLTV EPG data
# (c) by gutemine
#
epgload_version = "1.4-r0"
#
from Screens.Screen import Screen
from Screens.MessageBox import MessageBox
from Screens.HelpMenu import HelpableScreen
from Components.ActionMap import ActionMap, NumberActionMap, HelpableActionMap
from Components.MenuList import MenuList
from Components.Pixmap import Pixmap
from Components.ConfigList import ConfigListScreen, ConfigList
from Plugins.Plugin import PluginDescriptor
from Components.Label import Label
from Components.config import config, configfile, ConfigText, ConfigYesNo, ConfigInteger, ConfigSelection, ConfigEnableDisable, ConfigSlider, NoSave, ConfigSubsection, getConfigListEntry, ConfigIP, ConfigSubList
from Components.ConfigList import ConfigListScreen
from Components.Sources.StaticText import StaticText
from Components.Sources.ServiceList import ServiceList
from enigma import getDesktop, eTimer, eActionMap, ePoint, eEPGCache, eServiceCenter, eServiceReference, eTimer, getDesktop, iPlayableService, eSize, eConsoleAppContainer, eDVBDB
from Screens.InfoBar import InfoBar
from ServiceReference import ServiceReference
from twisted.web.client import downloadPage
from twisted.internet import threads
from twisted.web import resource, http
from backports import lzma
import gzip
from os import path as os_path, remove as os_remove, listdir as os_listdir, mkdir as os_mkdir, rename as os_rename, stat as os_stat
import random
import requests
import time
from xml.sax.handler import ContentHandler
from xml.sax import make_parser
from calendar import timegm
import datetime
date_format = '%Y%m%d%H%M%S'
MAIN=0
SOURCES=1
import xml.etree.ElementTree as etree
bouquet_line="#SERVICE 1:7:1:0:0:0:0:0:0:0:FROM BOUQUET \"%s\" ORDER BY bouquet"
service_types_tv = '1:7:1:0:0:0:0:0:0:0:(type == 1) || (type == 17) || (type == 22) || (type == 25) || (type == 31) || (type == 134) || (type == 195) || (type == 1F)'
from socket import getaddrinfo, gaierror
epgload_languages={"en":"eng","de":"deu","da":"den","dk":"den","fr":"fra","es":"spa","it":"ita","nl":"dut","ro":"rum","sr":"srp","hr":"hrv","pl":"pol","cs":"cze","he":"heb","pt":"por","sk":"slo","ar":"ara","hu":"hun","ja":"jpn","da":"dan","et":"est","fi":"fin","el":"gre","is":"ice","lb":"ltz","lt":"lit","lv":"lav","no":"nor","nb":"nor","pt":"por","ru":"rus","sv":"swe","se":"swe","tr":"tur","uk":"ukr","ro":"rom"}
compressed_magic_dict = { "\x1f\x8b\x08": "gz", "\xfd\x37\x7a": "xz" }
if os_path.exists("/var/lib/dpkg/status"):
from sqlite3 import dbapi2 as sqlite
from epgdb import epgdb_class
else:
import struct
from epgdat import epgdat_class
try:
cprint("EPG max days %s" % config.epg.maxdays.value)
except:
config.epg = ConfigSubsection()
days_options=[]
for days in range(1,366):
days_options.append(( str(days),str(days) ))
config.epg.maxdays= ConfigSelection(default = "28", choices=days_options)
try:
cprint("EPG history %s" % config.epg.histminutes.value)
except:
config.epg.histminutes= ConfigInteger(default = 0, limits=(0,120))
BLUEC = '\033[34m'
ENDC = '\033[m'
def cprint(text):
print(BLUEC+"[EPGLOAD] "+text+ENDC)
def parseXMLFile(file):
parser = make_parser( )
parser.setContentHandler(ContentHandler( ))
parser.parse(file)
def checkXMLFile(filename):
try:
parseXMLFile(filename)
cprint("XML File %s is well-formed" % filename)
return True
except Exception, e:
cprint("XML File %s is NOT well-formed %s" % (filename, e))
return False
def getAllServices():
allservices = []
s_type = service_types_tv
idbouquet = '%s ORDER BY name' % (s_type)
epgcache = eEPGCache.getInstance()
serviceHandler = eServiceCenter.getInstance()
services = serviceHandler.list(eServiceReference(idbouquet))
channels = services and services.getContent("SN", True)
for channel in channels:
mref=miniServiceReference(channel[0])
# cprint("SERVICE %s" % mref)
allservices.append(mref)
cprint("TOTAL TV SERVICES %d" % len(allservices))
return allservices
def getAllNamesServices():
all={}
s_type = service_types_tv
idbouquet = '%s ORDER BY name' % (s_type)
epgcache = eEPGCache.getInstance()
serviceHandler = eServiceCenter.getInstance()
services = serviceHandler.list(eServiceReference(idbouquet))
channels = services and services.getContent("SN", True)
for channel in channels:
name = channel[1].replace('\xc2\x86', '').replace('\xc2\x87', '').replace(" ","").replace("(","").replace(")","").replace("/","").replace("_","").replace("+","").replace("-","").replace(".","").upper()
sref=channel[0]
# cprint(">>>>>>>>>>>>>> %s %s" % (name,sref))
found=all.get(name,None)
if found is None:
all[name]=sref
else: # same channel name for multiple serviceref
if found.find(sref) is -1:
news="%s %s" % (found, sref)
all[name]=news
cprint("NAMED TV SERVICES %d" % len(all))
return all
def miniServiceReference(sref):
if sref is None:
return sref
sp=[]
sp=sref.split(":")
if len(sp) > 9:
if sp[6].upper().startswith("EEEE"): # DVB-T fix
sp[6]="EEEE0000"
if sp[6].upper().startswith("FFFF"): # DVB-T fix
sp[6]="FFFF0000"
sref=":%s:%s:%s:%s:" % (sp[3].upper(),sp[4].upper(),sp[5].upper(),sp[6].upper())
# cprint("MINI service reference %s" % sref)
return sref
def checkCompression(filename):
file_start=""
if os_path.exists(filename):
f=open(filename)
file_start = f.read(3)
f.close()
hex_start=":".join("{:02x}".format(ord(c)) for c in file_start)
for magic, filetype in compressed_magic_dict.items():
if file_start.startswith(str(magic)):
cprint("COMPRESSION %s %s" % (filetype, hex_start))
return filetype
return ""
epgload_name=_("EPGLoad")
epgload_description=_("Import from EPG").replace("EPG","XML")
epgload_title=epgload_name+" - "+_("Setup")
epgload_selection=_("EPGLoad")+" - "+_("Choose upgrade source")
epgload_failed=_("EPGLoad")+" - "+_("Edit upgrade source url.").replace(".","")
epgload_thanks=_("%s Plugin Version %s\n\n(c) gutemine 2019\n\nSpecial Thanks to Rytec for the XMLTV Format !") % (epgload_name, epgload_version)
epgload_plugindir="/usr/lib/enigma2/python/Plugins/Extensions/EPGLoad"
epgload_loading=False
yes_no_descriptions = {False: _("no"), True: _("yes")}
config.plugins.epgload = ConfigSubsection()
m=open("/proc/mounts","r")
mounts=m.read()
m.close()
cache_opt =[]
#cache_opt.append(("etc",_("/etc/epgload") ))
cache_opt.append(("tmp",_("/tmp/epgload") ))
if mounts.find("/data") is not -1:
cache_opt.append(("data",_("/data/epgload") ))
if mounts.find("/media/hdd") is not -1:
cache_opt.append(("hdd",_("/media/hdd/epgload") ))
if mounts.find("/media/usb") is not -1:
cache_opt.append(("usb",_("/media/usb/epgload") ))
if mounts.find("/media/sdcard") is not -1:
cache_opt.append(("sdcard",_("/media/sdcard/epgload") ))
config.plugins.epgload.cache = ConfigSelection(default = "tmp", choices=cache_opt)
latest_options=[]
latest_options.append(( "0",_("never") ))
latest_options.append(( "8","8 "+_("hours") ))
latest_options.append(( "12","12 "+_("hours") ))
latest_options.append(( "24",_("1 day") ))
for hours in range(48,192,24):
days=hours/24
latest_options.append(( str(hours),_("7 days").replace("7",str(days)) ))
config.plugins.epgload.latest = ConfigSelection(default = "0", choices=latest_options)
now = int(time.time())
config.plugins.epgload.last = ConfigInteger(default = now, limits=(0,2000000000)) # epoch time of last epg load
config.plugins.epgload.loaded = ConfigText(default = "")
prio_options=[]
for prio in range(90,111):
prio_options.append(( str(prio),str(prio) ))
config.plugins.epgload.priority = ConfigSelection(default = "100", choices=prio_options)
timeout_options=[]
timeout_options.append(( "0",_("no") ))
for timeout in range(10,130,10):
timeout_options.append(( str(timeout),str(timeout) ))
config.plugins.epgload.timeout = ConfigSelection(default = "0", choices=timeout_options)
empty_options=[]
empty_options.append(( "none",_("no") ))
empty_options.append(( "cache",_("Cache") ))
if os_path.exists("/var/lib/dpkg/status"):
empty_options.append(( "db",_("Database") ))
empty_options.append(( "cachedb",_("Cache")+" & "+_("Database") ))
config.plugins.epgload.empty = ConfigSelection(default="cache", choices=empty_options)
lang_options=[]
lang_options.append(( "default"," "+_("Default") ))
for lang2, lang3 in epgload_languages.iteritems():
lang_options.append(( lang3,lang3.upper() ))
config.plugins.epgload.language = ConfigSelection(default="default", choices=lang_options)
bracket_options=[]
bracket_options.append(( "none",_("no") ))
bracket_options.append(( "brackets","[ ]" ))
bracket_options.append(( "full","["+_("Text")+"]" ))
config.plugins.epgload.brackets = ConfigSelection(default="none", choices=bracket_options)
similar_options=[]
similar_options.append(( "none",_("no") ))
similar_options.append(( "load",_("Load").lower() ))
similar_options.append(( "save",_("Save").lower() ))
config.plugins.epgload.similar = ConfigSelection(default="none", choices=similar_options)
config.plugins.epgload.episode = ConfigYesNo(default=False)
config.plugins.epgload.settings = ConfigYesNo(default=True)
config.plugins.epgload.hiding = ConfigYesNo(default=False)
outdated_options=[]
if os_path.exists("/var/lib/dpkg/status"):
for out in range(0,97):
outdated_options.append(( str(out),str(out) ))
else:
for out in range(0,135,15):
outdated_options.append(( str(out),str(out) ))
config.plugins.epgload.outdated = ConfigSelection(default = "0", choices=outdated_options)
sz_w = getDesktop(0).size().width()
sz_h = getDesktop(0).size().height()
if sz_w == 1920:
defhide=-865
else:
defhide=-565
config.plugins.epgload.hide = ConfigInteger(default = defhide, limits=(0,-sz_h))
epgload_1MB=1048576
# Global variable
EPGLoadStartTimer = None
global num_of_sources
num_of_sources=0
def createSourceConfig():
global num_of_sources
x=0
y=0
z=0
type = "gen_xmltv"
config.plugins.epgload.sources = ConfigSubList()
# first we parse source files ...
for source in os_listdir("/etc/epgload"):
checkfile="/etc/epgload/%s" % source
checkedOK=checkXMLFile(checkfile)
if source.endswith(".sources.xml") and checkedOK:
cprint("SOURCE %s" % source)
num_of_sources+=1
stree=etree.parse("/etc/epgload/%s" % source)
root = stree.getroot()
source=source.replace(".sources.xml","")
# cprint(">>> %d %d %d" % (x,y,z))
config.plugins.epgload.sources.append(ConfigSubsection())
config.plugins.epgload.sources[x].name = ConfigText(default = "")
config.plugins.epgload.sources[x].name.value=source
config.plugins.epgload.sources[x].name.save()
config.plugins.epgload.sources[x].categories = ConfigSubList()
for child in root.iter():
# cprint(">>> %s %s" % (child.tag, child.attrib))
if child.tag == "sourcecat":
category=child.get('sourcecatname')
# cprint("CATEGORY %s" % category)
if category is not None:
# cprint(">>>>>> %d %d %d" % (x,y,z))
config.plugins.epgload.sources[x].categories.append(ConfigSubsection())
config.plugins.epgload.sources[x].categories[y].name = ConfigText(default = "")
config.plugins.epgload.sources[x].categories[y].name.value=category
config.plugins.epgload.sources[x].categories[y].name.save()
config.plugins.epgload.sources[x].categories[y].channels = ConfigSubList()
description=None
y+=1
z=0
elif child.tag == "description":
description=child.text
# cprint("DESCRIPTION %s" % description)
if description is not None and description.find("***") is -1:
# print ">>>>>>>>>", x,y,z
config.plugins.epgload.sources[x].categories[y-1].channels.append(ConfigSubsection())
config.plugins.epgload.sources[x].categories[y-1].channels[z].name = ConfigText(default = "")
config.plugins.epgload.sources[x].categories[y-1].channels[z].name.value=description
config.plugins.epgload.sources[x].categories[y-1].channels[z].name.save()
config.plugins.epgload.sources[x].categories[y-1].channels[z].download = ConfigYesNo(default = False)
z+=1
elif child.tag == "url":
url=child.text
# cprint("URL %s" % url)
elif child.tag == "source":
type=child.get('type')
if type is not None:
pass
# cprint("TYPE %s" % type)
else:
pass
x+=1
y=0
z=0
cprint("NUMBER OF SOURCES %d" % num_of_sources)
def checkEPGChannels():
selected=0
x=0
y=0
z=0
type = "gen_xmltv"
for source in os_listdir("/etc/epgload"):
checkfile="/etc/epgload/%s" % source
checkedOK=checkXMLFile(checkfile)
if source.endswith(".sources.xml") and checkedOK:
cprint("sources.xml: %s" % source)
stree=etree.parse("/etc/epgload/%s" % source)
root = stree.getroot()
for child in root.iter():
if child.tag == "sourcecat":
category=child.get('sourcecatname')
if category is not None:
# cprint("CATEGORY %s" % category)
y+=1
z=0
elif child.tag == "description":
description=child.text
if description is not None and description.find("***") is -1:
# cprint("DESCRIPTION %s" % description)
if config.plugins.epgload.sources[x].categories[y-1].channels[z].download.value:
selected+=1
z+=1
else:
pass
x+=1
y=0
z=0
cprint("SELECTED %d" % selected)
return selected
def checkHidden(filename):
bouquets="/etc/enigma2/bouquets.tv"
all=""
if os_path.exists(bouquets):
f=open(bouquets,"r")
all=f.read().lower()
f.close()
if all.find(filename) is -1 and filename != "userbouquet.favourites.tv" and filename != "userbouquet.favorites.tv":
return True
return False
# second we parse bouquets ...
# Bouquet ...
bouquet_options=[]
config.plugins.epgload.all = ConfigYesNo(default = True)
for bouquet in os_listdir("/etc/enigma2"):
if bouquet.startswith("userbouquet.") and (bouquet.endswith(".tv") or bouquet.endswith(".tv.del")):
f=open("/etc/enigma2/%s" % bouquet,"r")
name=f.readline()
f.close()
name = name.replace("#NAME ","").rstrip().lstrip()
bouquet_options.append(( bouquet,name))
bouquet_options.sort()
config.plugins.epgload.bouquets = ConfigSubList()
bouquet_length=len(bouquet_options)
for x in range(bouquet_length):
config.plugins.epgload.bouquets.append(ConfigSubsection())
config.plugins.epgload.bouquets[x].importing = ConfigYesNo(default = False)
config.plugins.epgload.bouquets[x].hidden = ConfigYesNo(default = False)
if checkHidden(bouquet_options[x][0]):
config.plugins.epgload.bouquets[x].hidden.value = True
else:
config.plugins.epgload.bouquets[x].hidden.value = False
config.plugins.epgload.bouquets[x].file = ConfigText(default = "")
config.plugins.epgload.bouquets[x].file.value = bouquet_options[x][0]
config.plugins.epgload.bouquets[x].file.save()
config.plugins.epgload.bouquets[x].name = ConfigText(default = "")
config.plugins.epgload.bouquets[x].name.value = bouquet_options[x][1]
config.plugins.epgload.bouquets[x].name.save()
def checkEPGLoadBouquet():
selected=0
for x in range(bouquet_length):
if config.plugins.epgload.bouquets[x].importing.value:
selected+=1
return selected
def changedBouquets():
sp=[]
if os_path.exists("/etc/enigma2/bouquets.tv"):
b=open("/etc/enigma2/bouquets.tv","r")
bouquets=b.readline() #NAME
while bouquets:
if bouquets.startswith("#SERVICE"):
sp.append(bouquets.rstrip("\r").rstrip("\n"))
bouquets=b.readline()
b.close()
changed=False
for bouquet in os_listdir("/etc/enigma2"):
if bouquet.startswith("userbouquet.") and (bouquet.endswith(".tv") or bouquet.endswith(".tv.del")):
for x in range(bouquet_length):
if bouquet==config.plugins.epgload.bouquets[x].file.value:
name=config.plugins.epgload.bouquets[x].name.value
file=config.plugins.epgload.bouquets[x].file.value
new=bouquet_line % bouquet
if config.plugins.epgload.bouquets[x].hidden.value:
for b in sp:
if b.find(file) is not -1:
cprint("HIDING BOUQUET %s" % name)
sp.remove(b)
changed=True
else:
missing=True
for b in sp:
if b.find(file) is not -1:
cprint("UNHIDING BOUQUET %s" % name)
missing=False
if missing:
sp.append(new)
changed=True
if changed:
bouquets="\n".join(sp)
cprint("BOUQUETS CHANGED")
b=open("/etc/enigma2/bouquets.tv","w")
b.write(bouquets)
b.close()
else:
cprint("BOUQUETS NOTHING CHANGED")
return changed
def getEPGLoadCache():
cache_path=config.plugins.epgload.cache.value
if cache_path=="etc":
path="/etc/epgload"
elif cache_path=="data":
path="/data/epgload"
elif cache_path=="hdd":
path="/media/hdd/epgload"
elif cache_path=="usb":
path="/media/usb/epgload"
elif cache_path=="sdcard":
path="/media/sdcard/epgload"
else: # tmp
path="/tmp/epgload"
if not os_path.exists(path):
os_mkdir(path,0777)
# cprint("CACHE %s" % path)
return path
def getChannels(channel, nocheck=False):
cprint("CHANNEL SEARCH: %s %s" % (channel, nocheck))
channels=[]
if channel.find("//") is not -1 or os_path.exists(channel):
if nocheck:
channels.append(channel+"#nocheck")
else:
channels.append(channel)
for source in os_listdir("/etc/epgload"):
checkfile="/etc/epgload/%s" % source
checkedOK=checkXMLFile(checkfile)
if source.endswith(".sources.xml") and checkedOK:
nocheck=False # urls could be different ...
cprint("SOURCE %s" % source)
stree=etree.parse("/etc/epgload/%s" % source)
root = stree.getroot()
for child in root.iter():
# cprint(">>> %s %s" % (child.tag, child.attrib))
if child.tag == "url":
url=child.text.rstrip().lstrip()
# cprint(">>>>>>>>> url %s %s" % (url, channel))
if url is not None and url.find(channel) is not -1 and not url in channels:
cprint("CHANNEL url %s %s" % (url, nocheck))
if nocheck:
channels.append(url+"#nocheck")
else:
channels.append(url)
elif child.tag == "channel":
nochecking=child.get("nocheck")
if nochecking is not None:
# cprint("NOCHECK %s" % nochecking)
if nochecking=="1":
nocheck=True
else:
nocheck=False
else:
nocheck=False
elif child.tag == "source":
nochecking=child.get("nocheck")
if nochecking is not None:
# cprint("NOCHECK %s" % nochecking)
if nochecking=="1":
nocheck=True
else:
nocheck=False
else:
nocheck=False
else:
pass
cprint("CHANNELS FOUND %d" % len(channels))
return channels
def getURLs():
global num_of_sources
x=0
y=0
z=0
type = "gen_xmltv"
found=False
chans=[]
urls=[]
downloads=[]
for source in os_listdir("/etc/epgload"):
checkfile="/etc/epgload/%s" % source
checkedOK=checkXMLFile(checkfile)
if source.endswith(".sources.xml") and x < num_of_sources and checkedOK:
found=False
cprint("%d FROM %d sources.xml: %s" % (x,num_of_sources,source))
stree=etree.parse("/etc/epgload/%s" % source)
root = stree.getroot()
# print ">>>", x,y,z
nocheck=False
for child in root.iter():
# cprint(">>> %s %s" %(child.tag, child.attrib))
if child.tag == "sourcecat":
category=child.get('sourcecatname')
# cprint("CATEGORY %s" % category)
if category is not None:
y+=1
z=0
if child.tag == "source":
type=child.get('type')
if type is not None:
# cprint("TYPE %s" % type)
pass
if len(chans) > 0 and len(urls) > 0:
if not chans in downloads: # prevent multiple loads of same channels
downloads.append(chans)
if not urls in downloads: # prevent multiple loads of epg
downloads.append(urls)
del chans
chans=[]
del urls
urls=[]
nocheck=False
channels=child.get('channels')
# cprint("CHANNELS %s" % channels)
nochecking=child.get("nocheck")
if nochecking is not None:
if nochecking=="1":
nocheck=True
else:
nocheck=False
if child.tag == "description":
description=child.text
cprint("DESCR %s" % description)
if description is not None and description.find("***") is -1:
if config.plugins.epgload.sources[x].categories[y-1].channels[z].download.value and config.plugins.epgload.sources[x].categories[y-1].channels[z].name.value==description:
cprint("DOWNLOADING %s FILE %s %s" % (description, channels, nocheck))
chans=getChannels(channels, nocheck)
del urls
urls=[]
found=True
else:
found=False
z+=1
elif child.tag == "url":
url=child.text
if url is not None and type == "gen_xmltv" and found:
# cprint("URL %s %s" % (url, nocheck))
if nocheck:
urls.append(url+"#nocheck")
else:
urls.append(url)
else:
pass
x+=1
y=0
z=0
if len(chans) > 0 and len(urls) > 0:
if not chans in downloads: # prevent multiple loads of same channels
downloads.append(chans)
downloads.append(urls)
cprint("DOWNLOADS %d" % len(downloads))
return downloads
def startEPGLoad(session,**kwargs):
session.open(EPGLoad)
def sessionstart(reason, **kwargs):
if reason == 0 and "session" in kwargs:
session = kwargs["session"]
getEPGLoadCache()
cprint("START CHECK")
global EPGLoadStartTimer
EPGLoadStartTimer = EPGLoadAutoStartTimer(session)
if os_path.exists("/usr/lib/enigma2/python/Plugins/Extensions//WebInterface"):
cprint("START WEBIF")
from Plugins.Extensions.WebInterface.WebChilds.Toplevel import addExternalChild, File
addExternalChild( ("epgload", EPGLoadWeb(), "EPGLoad", "1", True) )
else:
cprint("NO WEBIF")
class EPGLoadWeb(resource.Resource):
# always return current date for a web request
def render_GET(self, req):
cprint("epgload web request ...")
# cprint(req)
now=datetime.datetime.now()
date=now.strftime('%Y-%m-%d %H:%M')
req.setResponseCode(http.OK)
req.setHeader('Content-type', 'text/html')
req.setHeader('charset', 'UTF-8')
global epgload_loading
if epgload_loading:
cprint("ALREADY LOADING ...")
answer=_("Please wait... Loading list...").replace("...",":")+" "+_("In Progress")
else:
cprint("START LOADING ...")
answer=_("Please wait... Loading list...").replace("...",":")+" "+_("EPG")+" @ "+date
# check Sources ...
num_sources=0
for source in os_listdir("/etc/epgload"):
checkfile="/etc/epgload/%s" % source
checkedOK=checkXMLFile(checkfile)
if source.endswith(".sources.xml") and checkedOK:
num_sources+=1
global num_of_sources
if num_sources != num_of_sources:
cprint("NUMBER OF SOURCES CHANGED TO %d" % num_sources)
createSourceConfig()
num_of_sources=num_sources
EPGLoading()
return answer
class EPGLoadAutoStartTimer:
def __init__(self, session):
self.session = session
# check for epg load ...
self.CheckTimer = eTimer()
if os_path.exists("/var/lib/dpkg/status"):
self.CheckTimer_conn = self.CheckTimer.timeout.connect(self.autocheck)
else:
self.CheckTimer.callback.append(self.autocheck)
self.CheckTimer.stop()
# first epg check 30 sec after boot
wake=30*1000
self.CheckTimer.start(wake,True)
def autocheck(self):
self.CheckTimer.stop()
now = int(time.time())
last=int(config.plugins.epgload.last.value)
since= now-last
latest=int(config.plugins.epgload.latest.value)*3600
if latest == 0:
cprint("DISABLED")
else:
if last > 0 and since > latest:
cprint("EPG TOO OLD (%d > %d seconds), loading NOW ..." % (since, latest))
self.checkSources()
EPGLoading()
else:
cprint("EPG STILL VALID (%d < %d seconds)" % (since, latest))
# epg next check every hour
wake=3600*1000
self.CheckTimer.start(wake,True)
def checkSources(self):
num_sources=0
for source in os_listdir("/etc/epgload"):
checkfile="/etc/epgload/%s" % source
checkedOK=checkXMLFile(checkfile)
if source.endswith(".sources.xml") and checkedOK:
num_sources+=1
global num_of_sources
if num_sources != num_of_sources:
cprint("NUMBER OF SOURCES CHANGED TO %d" % num_sources)
createSourceConfig()
num_of_sources=num_sources
def Plugins(**kwargs):
return [PluginDescriptor(name=epgload_name, description=epgload_description, where = PluginDescriptor.WHERE_PLUGINMENU, icon="epgload.png", fnc=startEPGLoad),
PluginDescriptor(name=epgload_name, description=epgload_description, where = PluginDescriptor.WHERE_EXTENSIONSMENU, icon="epgload.png", fnc=startEPGLoad),
PluginDescriptor(where=PluginDescriptor.WHERE_SESSIONSTART, fnc=sessionstart, needsRestart=False)]
class EPGLoad(Screen, HelpableScreen, ConfigListScreen):
if sz_w == 2560:
skin = """
<screen position="center,140" size="1640,1240" title="EPGLoad" >
<widget backgroundColor="#818181" font="Regular;40" halign="center" name="buttonhelp" position="30,20" foregroundColor="black" shadowColor="grey" shadowOffset="-4,-4" size="200,80" valign="center" />
<widget backgroundColor="#9f1313" font="Regular;32" halign="center" name="buttonred" position="260,20" foregroundColor="white" shadowColor="black" shadowOffset="-4,-4" size="280,80" valign="center" />
<widget backgroundColor="#1f771f" font="Regular;32" halign="center" name="buttongreen" position="570,20" foregroundColor="white" shadowColor="black" shadowOffset="-4,-4" size="250,80" valign="center" />
<widget backgroundColor="#a08500" font="Regular;32" halign="center" name="buttonyellow" position="850,20" foregroundColor="white" shadowColor="black" shadowOffset="-4,-4" size="250,80" valign="center" />
<widget backgroundColor="#18188b" font="Regular;32" halign="center" name="buttonblue" position="1130,20" foregroundColor="white" shadowColor="black" shadowOffset="-4,-4" size="250,80" valign="center" />
<widget backgroundColor="#818181" font="Regular;40" halign="center" name="buttonmenu" position="1470,140" zPosition="1" foregroundColor="black" shadowColor="grey" shadowOffset="-4,-4" size="120,44" valign="center" />
<widget name="logo" position="1410,20" size="200,80" transparent="1" alphatest="on" />
<eLabel backgroundColor="grey" position="20,120" size="1600,2" />
<widget name="config" enableWrapAround="1" position="20,140" size="1600,960" scrollbarMode="showOnDemand" />
<eLabel backgroundColor="grey" position="20,1120" size="1600,2" />
<widget name="statustext" position="160,1140" size="1340,80" font="Regular;40" halign="center" valign="center" foregroundColor="white"/>
<widget backgroundColor="#818181" font="Regular;40" halign="center" name="buttoninfo" position="20,1136" foregroundColor="black" shadowColor="grey" shadowOffset="-4,-4" size="120,44" valign="center" />
<widget backgroundColor="#818181" font="Regular;40" halign="center" name="buttonaudio" position="20,1190" foregroundColor="black" shadowColor="grey" shadowOffset="-4,-4" size="120,44" valign="center" />
<widget backgroundColor="#818181" font="Regular;40" halign="center" name="buttonrec" position="1500,1136" foregroundColor="#880000" shadowColor="grey" shadowOffset="-4,-4" size="120,44" valign="center" />
<widget backgroundColor="#818181" font="Regular;40" halign="center" name="buttontext" position="1500,1190" foregroundColor="black" shadowColor="grey" shadowOffset="-4,-4" size="120,44" valign="center" />
</screen>"""
elif sz_w == 1920:
skin = """
<screen position="center,100" size="1200,950" title="EPGLoad" >
<widget backgroundColor="#818181" font="Regular;30" halign="center" name="buttonhelp" position="20,10" foregroundColor="black" shadowColor="grey" shadowOffset="-2,-2" size="150,60" valign="center" />
<widget backgroundColor="#9f1313" font="Regular;26" halign="center" name="buttonred" position="190,10" foregroundColor="white" shadowColor="black" shadowOffset="-2,-2" size="190,60" valign="center" />
<widget backgroundColor="#1f771f" font="Regular;26" halign="center" name="buttongreen" position="400,10" foregroundColor="white" shadowColor="black" shadowOffset="-2,-2" size="190,60" valign="center" />
<widget backgroundColor="#a08500" font="Regular;26" halign="center" name="buttonyellow" position="610,10" foregroundColor="white" shadowColor="black" shadowOffset="-2,-2" size="190,60" valign="center" />
<widget backgroundColor="#18188b" font="Regular;26" halign="center" name="buttonblue" position="820,10" foregroundColor="white" shadowColor="black" shadowOffset="-2,-2" size="190,60" valign="center" />
<widget name="logo" position="1030,10" size="150,60" transparent="1" alphatest="on" />
<eLabel backgroundColor="grey" position="20,80" size="1160,1" />
<widget name="config" enableWrapAround="1" position="20,90" scrollbarMode="showOnDemand" size="1160,760" />
<eLabel backgroundColor="grey" position="20,860" size="1160,1" />
<widget name="statustext" position="120,870" size="960,70" font="Regular;32" halign="center" valign="center" foregroundColor="white"/>
<widget backgroundColor="#818181" font="Regular;28" halign="center" name="buttoninfo" position="20,873" foregroundColor="black" shadowColor="grey" shadowOffset="-2,-2" size="100,30" valign="center" />
<widget backgroundColor="#818181" font="Regular;28" halign="center" name="buttonaudio" position="20,915" foregroundColor="black" shadowColor="grey" shadowOffset="-2,-2" size="100,30" valign="center" />
<widget backgroundColor="#818181" font="Regular;28" halign="center" name="buttonrec" position="1080,873" foregroundColor="#880000" shadowColor="grey" shadowOffset="-2,-2" size="100,30" valign="center" />
<widget backgroundColor="#818181" font="Regular;28" halign="center" name="buttontext" position="1080,915" foregroundColor="black" shadowColor="grey" shadowOffset="-2,-2" size="100,30" valign="center" />
</screen>"""
else:
skin = """
<screen position="center,70" size="820,620" title="EPGLoad" >
<widget backgroundColor="#818181" font="Regular;20" halign="center" name="buttonhelp" position="15,10" foregroundColor="black" shadowColor="grey" shadowOffset="-2,-2" size="100,40" valign="center" />
<widget backgroundColor="#9f1313" font="Regular;16" halign="center" name="buttonred" position="130,10" foregroundColor="white" shadowColor="black" shadowOffset="-2,-2" size="140,40" valign="center" />
<widget backgroundColor="#1f771f" font="Regular;16" halign="center" name="buttongreen" position="285,10" foregroundColor="white" shadowColor="black" shadowOffset="-2,-2" size="125,40" valign="center" />
<widget backgroundColor="#a08500" font="Regular;16" halign="center" name="buttonyellow" position="425,10" foregroundColor="white" shadowColor="black" shadowOffset="-2,-2" size="125,40" valign="center" />
<widget backgroundColor="#18188b" font="Regular;16" halign="center" name="buttonblue" position="565,10" foregroundColor="white" shadowColor="black" shadowOffset="-2,-2" size="125,40" valign="center" />
<widget backgroundColor="#818181" font="Regular;20" halign="center" name="buttonmenu" position="735,70" zPosition="1" foregroundColor="black" shadowColor="grey" shadowOffset="-2,-2" size="60,22" valign="center" />
<widget name="logo" position="705,10" size="100,40" transparent="1" alphatest="on" />
<eLabel backgroundColor="grey" position="10,60" size="800,1" />
<widget name="config" enableWrapAround="1" position="10,70" size="800,480" scrollbarMode="showOnDemand" />
<eLabel backgroundColor="grey" position="10,560" size="800,1" />
<widget name="statustext" position="80,570" size="670,40" font="Regular;20" halign="center" valign="center" foregroundColor="white"/>
<widget backgroundColor="#818181" font="Regular;20" halign="center" name="buttoninfo" position="10,568" foregroundColor="black" shadowColor="grey" shadowOffset="-2,-2" size="60,22" valign="center" />
<widget backgroundColor="#818181" font="Regular;20" halign="center" name="buttonaudio" position="10,595" foregroundColor="black" shadowColor="grey" shadowOffset="-2,-2" size="60,22" valign="center" />
<widget backgroundColor="#818181" font="Regular;20" halign="center" name="buttonrec" position="750,568" foregroundColor="#880000" shadowColor="grey" shadowOffset="-2,-2" size="60,22" valign="center" />
<widget backgroundColor="#818181" font="Regular;20" halign="center" name="buttontext" position="750,595" foregroundColor="black" shadowColor="grey" shadowOffset="-2,-2" size="60,22" valign="center" />
</screen>"""
def __init__(self, session, args = 0):
Screen.__init__(self, session)
HelpableScreen.__init__(self)
self.skin = EPGLoad.skin
self.q=0
self.session = session
self.screen=MAIN
self.reloadSource=False
self.moved=None
self.onShown.append(self.setWindowTitle)
# explicit check on every entry
self.onChangedEntry = []
self.list = []
ConfigListScreen.__init__(self, self.list, session = self.session, on_change = self.changedEntry)
for x in range(bouquet_length):
if checkHidden(bouquet_options[x][0]):
config.plugins.epgload.bouquets[x].hidden.value = True
else:
config.plugins.epgload.bouquets[x].hidden.value = False
self.createSetup(True)
self.onLayoutFinish.append(self.createSetup)
self["statustext"] = Label("")
self["logo"] = Pixmap()
self["buttoninfo"] = Label(_("Info").upper())
self["buttonhelp"] = Label(_("Help").upper())
self["buttonred"] = Label(_("Back"))
self["buttongreen"] = Label(_("Save"))
self["buttonyellow"] = Label(_("Download"))
self["buttonblue"] = Label(_("Select"))
self["buttonaudio"] = Label(_("Audio").upper())
self["buttontext"] = Label(_("Text").upper())
self["buttonrec"] = Label(_("Rec").upper())
self["buttonmenu"] = Label(_("Menu").upper())
self["ColorActions"] = HelpableActionMap(self,"ColorActions",
{
"green": (self.save, _("Save")+" "+_("Settings")),
"red": (self.cancel,_("Exit")),
"yellow": (self.yellow_key,_("Download")+" / "+_("Reset")),
"blue": (self.blue_key,_("Select")+" "+_("Setup")+" / "+_("Choose upgrade source")),
}, -5)
self["ChannelSelectEPGActions"] = HelpableActionMap(self,"ChannelSelectEPGActions",
{
"showEPGList": (self.about,_("Show info screen")),
}, -4)
self["InfobarTeletextActions"] = HelpableActionMap(self,"InfobarTeletextActions",
{
"startTeletext": (self.doClean,_("EPG")+" "+_("Download")+" "+_("Cache")+" "+_("Reset")),
}, -4)
self["InfobarInstantRecord"] = HelpableActionMap(self,"InfobarInstantRecord",
{
"instantRecord": (self.doReset,_("EPG")+" "+_("Database")+" "+_("Reset")),
}, -4)
self["InfobarMenuActions"] = HelpableActionMap(self,"InfobarMenuActions",
{
"mainMenu": (self.toggleSettings,_("show EPG...").replace("EPG...",_("Settings"))),
}, -4)
self["TvRadioActions"] = HelpableActionMap(self,"TvRadioActions",
{
"keyTV": (self.TV_key,_("enable move mode")),
# "keyRadio": (self.about,_("Show")+" "+_("About")),
}, -4)
self["SetupActions"] = HelpableActionMap(self,"SetupActions",
{
"nextSection": (self.down,_("Move screen down")+" / "+_("Bouquet")+" "+_("enable")),
"previousSection": (self.up,_("Move screen up")+" / "+_("Bouquet")+" "+_("disable")),
# "save": (self.save,_("Save")+" "+_("Settings")),
"cancel": (self.cancel,_("Exit")),
"ok": (self.save, _("Save")+" "+_("Settings")),
}, -5)
#
# for new RC use Audio key as Help key alternative
#
self["InfobarAudioSelectionActions"] = HelpableActionMap(self,"InfobarAudioSelectionActions",
{
"audioSelection": (self.audioPressed,_("Help")),
}, -3)
#
# for new RC use Stop key as TV key alternative
#
self["MoviePlayerActions"] = HelpableActionMap(self,"MoviePlayerActions",
{
"leavePlayer": (self.TV_key,_("enable move mode")),
}, -3)
if not os_path.exists("/var/lib/dpkg/status"):
self["KeyboardInputActions"] = HelpableActionMap(self,"KeyboardInputActions",
{
"pageDown": (self.down,_("Move screen down")+" / "+_("Active")+" "+_("Inactive")+" "+_("Bouquet")),
"pageUp": (self.up,_("Move screen up")+" / "+_("Active")+" "+_("Inactive")+" "+_("Bouquet")),
}, -4)
self.top=11
else:
self.top=12
self["HiddenActions"] = NumberActionMap(["SetupActions"],{"0": self.key_number, "1": self.key_number, "2": self.key_number, "3": self.key_number, "4": self.key_number, "5": self.key_number, "6": self.key_number, "7": self.key_number, "8": self.key_number, "9": self.key_number }, -6)
if not checkEPGLoadBouquet():
self.resetting()
self.statusCheckTimer = eTimer()
if os_path.exists("/var/lib/dpkg/status"):
self.statusCheckTimer_conn = self.statusCheckTimer.timeout.connect(self.statusUpdate)
else:
self.statusCheckTimer.callback.append(self.statusUpdate)
self.statusCheckTimer.start(500,True)
def statusUpdate(self):
self.statusCheckTimer.stop()
cur_idx = self["config"].getCurrentIndex()
# cprint("STATUS %s" % config.plugins.epgload.loaded.value)
if epgload_loading:
self["statustext"].setText(config.plugins.epgload.loaded.value)
else:
if self.screen == SOURCES:
if num_of_sources > 0:
self["statustext"].setText(_("Choose upgrade source")+" "+_("..."))
else:
self["statustext"].setText(_("A required tool (%s) was not found.") % "*sources.xml")
else:
if cur_idx > self.top:
self["statustext"].setText(_("Select")+" "+_("Bouquets")+_("..."))
else:
self["statustext"].setText(_("Select")+" "+_("Setup")+_("..."))
self.statusCheckTimer.start(2000,True)
def setWindowTitle(self):
self["logo"].instance.setPixmapFromFile("%s/epgload.png" % epgload_plugindir)
if self.screen == SOURCES:
if num_of_sources > 0:
self.setTitle(epgload_selection)
if not epgload_loading:
self["statustext"].setText(_("Choose upgrade source")+" "+_("..."))
else:
self.setTitle(epgload_failed)
self["statustext"].setText(_("A required tool (%s) was not found.") % "*sources.xml")
else:
self.setTitle(epgload_title)
if not epgload_loading:
self["statustext"].setText(_("Select")+" "+_("Setup")+_("..."))
def save(self):
self["statustext"].setText(_("Save")+" "+_("Setup")+" "+_("..."))
getEPGLoadCache()
for x in self["config"].list:
if len(x) > 1:
x[1].save()
selected=0
for x in range(bouquet_length):
if config.plugins.epgload.bouquets[x].importing.value:
selected+=1
if not config.plugins.epgload.all.value and not checkEPGLoadBouquet():
self.resetting()
for x in range(bouquet_length):
config.plugins.epgload.bouquets[x].importing.save()
config.plugins.epgload.bouquets[x].file.save()
config.plugins.epgload.bouquets[x].name.save()
config.plugins.epgload.bouquets[x].hidden.save()
if self.screen!=MAIN:
self.screen=MAIN
self.createSetup()
self.setWindowTitle()
if os_path.exists("/var/lib/dpkg/status"):
config.misc.epgcache_outdated_timespan.value=int(config.plugins.epgload.outdated.value)
config.misc.epgcache_outdated_timespan.save()
else:
config.epg.histminutes.value=str(config.plugins.epgload.outdated.value)
config.epg.histminutes.save()
config.plugins.epgload.outdated.save()
config.plugins.epgload.hide.save()
config.plugins.epgload.settings.save()
self["config"].setCurrentIndex(0)
if config.plugins.epgload.hiding.value:
if changedBouquets(): # only when changed reload
eDVBDB.getInstance().reloadBouquets()
def cancel(self):
if self.screen==MAIN:
for x in self["config"].list:
if len(x) > 1:
x[1].cancel()
for x in range(bouquet_length):
config.plugins.epgload.bouquets[x].importing.cancel()
config.plugins.epgload.bouquets[x].name.cancel()
config.plugins.epgload.bouquets[x].file.cancel()
config.plugins.epgload.bouquets[x].hidden.cancel()
config.plugins.epgload.hide.cancel()
config.plugins.epgload.outdated.cancel()
config.plugins.epgload.settings.cancel()
self.statusCheckTimer.stop()
self.close(False)
else:
self.screen=MAIN
self.createSetup()
self.setWindowTitle()
self["config"].setCurrentIndex(0)
def createSetup(self, first=False):
num_sources=0
for source in os_listdir("/etc/epgload"):
checkfile="/etc/epgload/%s" % source
checkedOK=checkXMLFile(checkfile)
if source.endswith(".sources.xml") and checkedOK:
num_sources+=1
global num_of_sources
if self.reloadSource or num_sources != num_of_sources:
cprint("NUMBER OF SOURCES CHANGED TO %d" % num_sources)
createSourceConfig()
self.reloadSource=False
num_of_sources=num_sources
self.list = []
timeout_string=_("Waiting")+" "+_("Download")+" ("+_("seconds")+")"
if self.screen==MAIN:
if not first:
self["buttonyellow"].setText(_("Download"))
self["buttonblue"].setText(_("Select"))
self["buttonmenu"].show()
if config.plugins.epgload.settings.value:
self.list.append((("* * * ")+_("Electronic Program Guide")+" "+_("Download")+" "+_("Settings")+" * * *",))
self.list.append(getConfigListEntry(("EPG")+" "+_("Update"), config.plugins.epgload.latest))
self.list.append(getConfigListEntry(_("Location"), config.plugins.epgload.cache))
self.list.append(getConfigListEntry(_("Clear before scan"), config.plugins.epgload.empty))
if os_path.exists("/var/lib/dpkg/status"):
self.list.append(getConfigListEntry(_("Priority"), config.plugins.epgload.priority))
self.list.append(getConfigListEntry(timeout_string, config.plugins.epgload.timeout))
self.list.append(getConfigListEntry(_("Similar").lower()+" "+_("Channels"), config.plugins.epgload.similar))
self.list.append(getConfigListEntry(_("Title")+" - ["+_("Season")+"] "+_("Episode"), config.plugins.epgload.episode))
self.list.append(getConfigListEntry(_("Remove"), config.plugins.epgload.brackets))
self.list.append(getConfigListEntry(_("Bouquets")+" "+_("disable"), config.plugins.epgload.hiding))
self.list.append(getConfigListEntry(_("Import")+" "+_("Language")+" < >", config.plugins.epgload.language))
self.list.append((("* * * ")+_("Electronic Program Guide")+" "+_("System")+" "+_("Settings")+" * * *",))
# timespan time from system settings defined in days
if os_path.exists("/var/lib/dpkg/status"):
self.list.append(getConfigListEntry(_("EPG cache time span"), config.misc.epgcache_timespan))
else:
self.list.append(getConfigListEntry(_("Maximum number of days in EPG"), config.epg.maxdays))
# outdated time for system settings defined in hours or minutes
self.list.append(getConfigListEntry(_("Keep outdated EPG (in hours)"), config.plugins.epgload.outdated))
self.list.append(("* * * %s * * *" % (_("Bouquets")+" "+_("Select")),))
self.list.append(getConfigListEntry(_("All")+" "+_("Channels"), config.plugins.epgload.all))
if not config.plugins.epgload.all.value:
for x in range(bouquet_length):
if config.plugins.epgload.bouquets[x].hidden.value:
self.list.append(getConfigListEntry(_("*")+" "+bouquet_options[x][1], config.plugins.epgload.bouquets[x].importing))
else:
self.list.append(getConfigListEntry(bouquet_options[x][1], config.plugins.epgload.bouquets[x].importing))
elif self.screen==SOURCES:
if not first:
self["buttonyellow"].setText(_("Reset"))
self["buttonblue"].setText(_("Setup"))
self["buttonmenu"].hide()
x=0
y=0
z=0
type = "gen_xmltv"
for source in os_listdir("/etc/epgload"):
checkfile="/etc/epgload/%s" % source
checkedOK=checkXMLFile(checkfile)
if source.endswith(".sources.xml") and checkedOK:
cprint("sources.xml: %s" % source)
stree=etree.parse("/etc/epgload/%s" % source)
root = stree.getroot()
for child in root.iter():
# cprint(">>> %s %s" % (child.tag, child.attrib))
if child.tag == "sourcecat":
category=child.get('sourcecatname')
if category is not None:
# cprint("CATEGORY %s" % category)
self.list.append(("* * * %s * * *" % category,))
y+=1
z=0
elif child.tag == "description":
description=child.text
if description is not None and description.find("***") is -1:
# cprint("DESCRIPTION %s" % description)
# cprint(">>>>>> %d %d %d" % (x,y-1,z))
self.list.append(getConfigListEntry(str(config.plugins.epgload.sources[x].categories[y-1].channels[z].name.value), config.plugins.epgload.sources[x].categories[y-1].channels[z].download))
z+=1
elif child.tag == "url":
url=child.text
if url is not None and type == "gen_xmltv":
# cprint("URL %s" % url)
pass
elif child.tag == "source":
type=child.get('type')
if type is not None:
# cprint("TYPE %s" % type)
pass
else:
pass
x+=1
y=0
z=0
else:
if not first:
self["buttonyellow"].setText(_("Download"))
self["buttonblue"].setText(_("Setup"))
if first:
self.menuList = ConfigList(self.list)
self.menuList.list = self.list
self.menuList.l.setList(self.list)
self["config"] = self.menuList
self["config"].onSelectionChanged.append(self.selectionChanged)
else:
self.menuList.list = self.list
self.menuList.l.setList(self.list)
def selectionChanged(self):
choice = self["config"].getCurrent()
cur_idx = self["config"].getCurrentIndex()
current=choice[1]
if self.screen==MAIN:
if cur_idx > self.top:
self["buttonyellow"].setText(_("Reset"))
if not epgload_loading:
self["statustext"].setText(_("Select")+" "+_("Bouquets")+_("..."))
else:
self["buttonyellow"].setText(_("Download"))
if not epgload_loading:
self["statustext"].setText(_("Select")+" "+_("Setup")+_("..."))
def changedEntry(self):
choice = self["config"].getCurrent()
if len(choice) > 1:
current=choice[1]
if choice != None:
self.createSetup()
def key_number(self,number):
self.numberCheckTimer.stop()
self.q=self.q*10+number
if self.q > 255:
self.q=0
self.numberCheckTimer.start(1000)
def yellow_key(self):
if self.screen==MAIN:
choice = self["config"].getCurrent()
cur_idx = self["config"].getCurrentIndex()
current=choice[1]
if cur_idx > self.top:
self.resetting()
else:
if not config.plugins.epgload.all.value and not checkEPGLoadBouquet():
self.resetting()
if epgload_loading:
self["statustext"].setText(_("EPG")+" "+_("Download")+" "+_("active"))
else:
if not checkEPGChannels():
self.statusUpdate()
self["statustext"].setText(_("EPG")+" "+_("no Services/Providers selected"))
else:
message=_("EPG")+" "+_("Download")+" ..."
self.statusUpdate()
self["statustext"].setText(message)
config.plugins.epgload.loaded.value=message
if os_path.exists("/var/lib/dpkg/status"):
threads.deferToThread(self.startEPGLoad).addCallback(lambda ignore: self.finishedEPGLoading())
else:
self.startEPGLoad()
else:
self.resetting()
self["config"].setCurrentIndex(0)
def startEPGLoad(self):
EPGLoading()
def finishedEPGLoading(self):
pass
def up(self):
cprint("UP")
if self.moved:
nx=self.x
ny=int(config.plugins.epgload.hide.value)-5
if ny < -sz_h:
ny=-sz_h
config.plugins.epgload.hide.value=ny
cprint("moving to %i,%i" % (nx,ny))
self.instance.move(ePoint(nx,ny))
else:
self.toggleActive()
def down(self):
cprint("DOWN")
if self.moved:
nx=self.x
ny=int(config.plugins.epgload.hide.value)+5
if ny > 0:
ny=0
config.plugins.epgload.hide.value=ny
cprint("MOVING TO %i,%i" % (nx,ny))
self.instance.move(ePoint(nx,ny))
else:
self.toggleActive()
def toggleSettings(self):
cprint("TOGGLE SETTINGS")
if self.screen == SOURCES:
return
if config.plugins.epgload.settings.value:
config.plugins.epgload.settings.value=False
else:
config.plugins.epgload.settings.value=True
self.createSetup()
def toggleActive(self):
if not config.plugins.epgload.hiding.value:
return
cur = self["config"].getCurrentIndex()
if config.plugins.epgload.settings.value:
cur=cur-self.top-3
else:
cur=cur-2
if cur >= 0 and cur < bouquet_length:
# cprint(">>>>>> %s %s" % (cur, config.plugins.epgload.bouquets[cur].name.value))
if config.plugins.epgload.bouquets[cur].hidden.value:
config.plugins.epgload.bouquets[cur].hidden.value=False
else:
if config.plugins.epgload.bouquets[cur].file.value != "userbouquet.favourites.tv" and config.plugins.epgload.bouquets[cur].file.value != "userbouquet.favorites.tv":
config.plugins.epgload.bouquets[cur].hidden.value=True
else:
config.plugins.epgload.bouquets[cur].hidden.value=False
self.createSetup()
def TV_key(self):
cprint("TV ...")
ch = self.instance.csize().height()
cw = self.instance.csize().width()
if self.moved is None:
# get current skin position, width and height ...
x = self.instance.position().x()
y = self.instance.position().y()
pw = self.instance.size().width()
ph = self.instance.size().height()
decw=pw-cw
dech=ph-ch
self.x=x+decw//2
if sz_w == 2560:
self.y=140
elif sz_w == 1920:
self.y=100
else:
self.y=70
if ch==ph:
self.y=y+dech//2
self.moved=False
if self.moved:
nx=self.x
ny=self.y
self.moved=False
else:
nx=self.x
ny=int(config.plugins.epgload.hide.value)
self.moved=True
cprint("MOVING TO %i,%i" % (nx,ny))
self.instance.move(ePoint(nx,ny))
def audioPressed(self):
cprint("AUDIO pressed")
#Device Types
TYPE_STANDARD = "dreambox remote control (native)"
TYPE_ADVANCED = "dreambox advanced remote control (native)"
TYPE_KEYBOARD = "dreambox ir keyboard"
#Advanced remote or standard?
if config.misc.rcused.value == 0:
self.remotetype = TYPE_ADVANCED
else:
self.remotetype = TYPE_STANDARD
self.eam = eActionMap.getInstance()
# press the key with the desired flag
keycode=138 # HELP key
cprint("NOW WRITES OUT: %i = HELP" % (keycode))
self.eam.keyPressed(self.remotetype, keycode, 0)
# release the key
self.eam.keyPressed(self.remotetype, keycode, 1)
def about(self):
last=int(config.plugins.epgload.last.value)
last_date_time=datetime.datetime.fromtimestamp(last).strftime('%Y-%m-%d %H:%M')
message=epgload_thanks+"\n\n"+config.plugins.epgload.loaded.value+"\n\n"+epgload_description+": "+last_date_time
self.session.open(MessageBox, message, MessageBox.TYPE_INFO)
def doClean(self):
cache=getEPGLoadCache()
global epgload_loading
epgload_loading=False
for file in os_listdir(cache):
os_remove("%s/%s" % (cache,file))
message=_("EPG")+" "+_("Download")+" "+_("Cache")+" "+_("Reset")+" "+_("Finished")
self["statustext"].setText(message)
def doReset(self, reload=True):
global epgload_loading
epgload_loading=False # should stop extraction ...
self.epginstance = eEPGCache.getInstance()
if os_path.exists(config.misc.epgcache_filename.value+".del"):
os_remove(config.misc.epgcache_filename.value+".del")
if os_path.exists(config.misc.epgcache_filename.value):
os_rename(config.misc.epgcache_filename.value,config.misc.epgcache_filename.value+".del")
if not os_path.exists("/var/lib/dpkg/status"):
# some magic for empty epg.dat file ...
e=open(config.misc.epgcache_filename.value,"wb")
LB_ENDIAN='<'
EPG_HEADER1_channel_count=0
header=struct.pack(LB_ENDIAN+"I13sI",0x98765432,'ENIGMA_EPG_V7',EPG_HEADER1_channel_count)
e.write(header)
e.close()
cprint("EMPTIED epg.dat")
else:
connection = sqlite.connect(config.misc.epgcache_filename.value, timeout=10)
connection.text_factory = str
cursor = connection.cursor()
cursor.execute("CREATE TABLE T_Service (id INTEGER PRIMARY KEY, sid INTEGER NOT NULL, tsid INTEGER, onid INTEGER, dvbnamespace INTEGER, changed DATETIME NOT NULL DEFAULT current_timestamp)")
cursor.execute("CREATE TABLE T_Source (id INTEGER PRIMARY KEY, source_name TEXT NOT NULL, priority INTEGER NOT NULL, changed DATETIME NOT NULL DEFAULT current_timestamp)")
cursor.execute("CREATE TABLE T_Title (id INTEGER PRIMARY KEY, hash INTEGER NOT NULL UNIQUE, title TEXT NOT NULL, changed DATETIME NOT NULL DEFAULT current_timestamp)")
cursor.execute("CREATE TABLE T_Short_Description (id INTEGER PRIMARY KEY, hash INTEGER NOT NULL UNIQUE, short_description TEXT NOT NULL, changed DATETIME NOT NULL DEFAULT current_timestamp)")
cursor.execute("CREATE TABLE T_Extended_Description (id INTEGER PRIMARY KEY, hash INTEGER NOT NULL UNIQUE, extended_description TEXT NOT NULL, changed DATETIME NOT NULL DEFAULT current_timestamp)")
cursor.execute("CREATE TABLE T_Event (id INTEGER PRIMARY KEY, service_id INTEGER NOT NULL, begin_time INTEGER NOT NULL, duration INTEGER NOT NULL, source_id INTEGER NOT NULL, dvb_event_id INTEGER, changed DATETIME NOT NULL DEFAULT current_timestamp)")
cursor.execute("CREATE TABLE T_Data (event_id INTEGER NOT NULL, title_id INTEGER, short_description_id INTEGER, extended_description_id INTEGER, iso_639_language_code TEXT NOT NULL, changed DATETIME NOT NULL DEFAULT current_timestamp)")
cursor.execute("CREATE INDEX data_title ON T_Data (title_id)")
cursor.execute("CREATE INDEX data_shortdescr ON T_Data (short_description_id)")
cursor.execute("CREATE INDEX data_extdescr ON T_Data (extended_description_id)")
cursor.execute("CREATE INDEX service_sid ON T_Service (sid)")
cursor.execute("CREATE INDEX event_service_id_begin_time ON T_Event (service_id, begin_time)")
cursor.execute("CREATE INDEX event_dvb_id ON T_Event (dvb_event_id)")
cursor.execute("CREATE INDEX data_event_id ON T_Data (event_id)")
cursor.execute("CREATE TRIGGER tr_on_delete_cascade_t_event AFTER DELETE ON T_Event FOR EACH ROW BEGIN DELETE FROM T_Data WHERE event_id = OLD.id; END")
cursor.execute("CREATE TRIGGER tr_on_delete_cascade_t_service_t_event AFTER DELETE ON T_Service FOR EACH ROW BEGIN DELETE FROM T_Event WHERE service_id = OLD.id; END")
cursor.execute("CREATE TRIGGER tr_on_delete_cascade_t_data_t_title AFTER DELETE ON T_Data FOR EACH ROW WHEN ((SELECT event_id FROM T_Data WHERE title_id = OLD.title_id LIMIT 1) ISNULL) BEGIN DELETE FROM T_Title WHERE id = OLD.title_id; END")
cursor.execute("CREATE TRIGGER tr_on_delete_cascade_t_data_t_short_description AFTER DELETE ON T_Data FOR EACH ROW WHEN ((SELECT event_id FROM T_Data WHERE short_description_id = OLD.short_description_id LIMIT 1) ISNULL) BEGIN DELETE FROM T_Short_Description WHERE id = OLD.short_description_id; END")
cursor.execute("CREATE TRIGGER tr_on_delete_cascade_t_data_t_extended_description AFTER DELETE ON T_Data FOR EACH ROW WHEN ((SELECT event_id FROM T_Data WHERE extended_description_id = OLD.extended_description_id LIMIT 1) ISNULL) BEGIN DELETE FROM T_Extended_Description WHERE id = OLD.extended_description_id; END")
cursor.execute("CREATE TRIGGER tr_on_update_cascade_t_data AFTER UPDATE ON T_Data FOR EACH ROW WHEN (OLD.title_id <> NEW.title_id AND ((SELECT event_id FROM T_Data WHERE title_id = OLD.title_id LIMIT 1) ISNULL)) BEGIN DELETE FROM T_Title WHERE id = OLD.title_id; END")
cursor.execute("INSERT INTO T_Source (id,source_name,priority) VALUES('0','Sky Private EPG','0')")
cursor.execute("INSERT INTO T_Source (id,source_name,priority) VALUES('1','DVB Now/Next Table','0')")
cursor.execute("INSERT INTO T_Source (id,source_name,priority) VALUES('2','DVB Schedule (same Transponder)','0')")
cursor.execute("INSERT INTO T_Source (id,source_name,priority) VALUES('3','DVB Schedule Other (other Transponder)','0')")
cursor.execute("INSERT INTO T_Source (id,source_name,priority) VALUES('4','Viasat','0')")
connection.commit()
cursor.close()
connection.close()
cprint("EMPTIED epg.db")
if reload:
print("EPG LOADING ...")
eEPGCache.load(self.epginstance)
if os_path.exists(config.misc.epgcache_filename.value+".del"):
os_remove(config.misc.epgcache_filename.value+".del")
message=_("EPG")+" "+_("Database")+" "+_("Reset")+" "+_("Finished")
self["statustext"].setText(message)
def blue_key(self):
if epgload_loading:
self["statustext"].setText(_("EPG")+" "+_("Download")+" "+_("active"))
else:
if self.screen==MAIN:
self.screen=SOURCES
else:
self.screen=MAIN
self.createSetup()
self.setWindowTitle()
self["config"].setCurrentIndex(0)
def resetting(self):
cprint("RESETTING ...")
if self.screen==MAIN:
config.plugins.epgload.all.value=True
config.plugins.epgload.all.save()
for x in range(bouquet_length):
config.plugins.epgload.bouquets[x].importing.value=False
config.plugins.epgload.bouquets[x].importing.save()
config.plugins.epgload.bouquets[x].name.save()
config.plugins.epgload.bouquets[x].file.save()
else:
for x in self["config"].list:
if len(x) > 1:
x[1].value=x[1].default
self.createSetup()
class EPGLoading(Screen):
def __init__(self):
global epgload_loading
if epgload_loading:
cprint("ALREADY LOADING ...")
return
epgload_loading=True
self.cache=getEPGLoadCache()
self.similar="%s/similar_channels.xml" % self.cache
self.downloads = []
self.channelids={}
self.downloads = getURLs()
self.downloads_length=len(self.downloads)
if self.downloads_length < 1:
cprint("NO SOURCES SELECTED")
self.finishedEPGLoad(False)
else:
self.download=0
self.extracted_sources=0
self.extracted_kB=0
self.extracted_channels=0
self.parsed_channels=0
self.loaded_channels=0
self.extracted_events=0
self.allservices={}
if config.plugins.epgload.all.value:
self.services=getAllServices()
else:
self.services=self.getAllBouquetServices()
if config.plugins.epgload.similar.value != "none":
self.allservices=getAllNamesServices()
self.all_channels=[]
if config.plugins.epgload.similar.value == "save":
if os_path.exists(self.similar):
os_remove(self.similar)
# similar channels xml started
s=open(self.similar,"a")
s.write("<channels>\n<!-- EPGLOAD SIMILAR CHANNELS -->\n")
s.close()
# load LOCAL channels files first
for channels in os_listdir("/etc/epgload"):
checkfile="/etc/epgload/%s" % channels
checkedOK=checkXMLFile(checkfile)
if channels.find("channels") is not -1 and channels.find(".xml") is not -1 and channels.find("rytec") is -1 and checkedOK:
target="/etc/epgload/%s" % channels
cprint("LOCAL CHANNELS %s found" % channels)
self.extractXML(target)
self.priority=int(config.plugins.epgload.priority.value)
self.source_name=_("EPGLoad")
do_reset=False
if config.plugins.epgload.empty.value.endswith("db"):
do_reset=True
# save epg.db to be ready to load ....
if os_path.exists("/var/lib/dpkg/status"):
self.epg = epgdb_class(self.source_name, self.priority, config.misc.epgcache_filename.value, clear_oldepg=do_reset)
else:
self.epg = epgdat_class(self.source_name, self.priority, config.misc.epgcache_filename.value)
# config.plugins.epgload.loaded.value=("EPG")+" "+_("Database")+" "+_("checking")+" ..."
self.startEPGDownload()
def startEPGDownload(self):
# status=self.epg.check_epgdb()
# if status == "ok":
if True:
threads.deferToThread(self.doEPGDownload).addCallback(lambda ignore: self.finishEPGDownload())
else:
cprint("STATUS %s" % status)
config.plugins.epgload.loaded.value=("EPG")+" "+_("Database")+" - "+_("Filesystem contains uncorrectable errors")
def finishEPGDownload(self):
pass
def splitURL(self, url):
sp=[]
dirname, filename = os_path.split(url)
sp=filename.split("?")
filename=sp[0]
return dirname, filename
def doEPGDownload(self):
cprint("CURRENT %d %d" % (self.download, self.downloads_length))
self.trials=0
if self.download < self.downloads_length:
self.urls=self.downloads[self.download]
self.url=random.choice(self.urls)
cprint("LOADING %s" % self.url)
dirname, filename = self.splitURL(self.url)
check=self.url.replace("#nocheck","")
if os_path.exists(check):
cprint("LOCAL %s" % (check))
self.nextEPGDownload()
return
host,resolv=self.checkHost(self.url)
if resolv is None: # don't even try if name is not resolvable
self.failedEPGDownload(True)
else:
# if host resolved, then start with LastUpdate.txt check
# unless url contains # nocheck
if not self.url.endswith("#nocheck"):
checkurl = "%s/LastUpdate.txt" % dirname
cprint("CHECK last update %s" % checkurl)
target = "%s/LastUpdate.txt" % self.cache
if os_path.exists(target):
os_remove(target)
downloadPage(checkurl, target,timeout=int(config.plugins.epgload.timeout.value)).addCallbacks(self.afterEPGDownload,self.failedEPGDownload, callbackArgs=(target,False))
else:
sp=filename.split("?")
filename=sp[0]
target="%s/%s" % (self.cache,filename)
if os_path.exists(target):
os_remove(target)
self.afterEPGDownload(True,target)
else: # finished with downloads
self.epg.final_process()
self.exportLastUpdate()
config.plugins.epgload.loaded.value=("EPG")+" #%d " % (self.extracted_sources)+" "+_("Channels")+": %d " % (self.extracted_channels)+_("Info")+" "+_("Details")+": "+'{:,}'.format(self.extracted_events).replace(",",".")+" "+_("Size")+": "+'{:,}'.format(self.extracted_kB).replace(",",".")+" kB"
self.finishedEPGLoad(True)
def finishedEPGLoad(self, success=True):
if config.plugins.epgload.similar.value != "none":
self.all_channels=[]
if config.plugins.epgload.similar.value == "save":
# similar channels xml finished
s=open(self.similar,"a")
s.write("</channels>\n")
s.close()
if success:
now = int(time.time())
config.plugins.epgload.last.value=now
config.plugins.epgload.last.save()
config.plugins.epgload.loaded.save()
cprint("FINISHED SUCCESSFULL")
else:
cprint("FINISHED FAILED")
global epgload_loading
epgload_loading=False
def checkSimilarChannels(self,id):
needle="<channel id=\"%s\">" % id
sp=[]
asp=[]
csp=[]
found_similar=0
if len(self.all_channels) == 0:
for file in os_listdir(self.cache):
if file.find("channels") is not -1 and file.find(".xml") is not -1 and not file.startswith("similar_"):
filename="%s/%s" % (self.cache,file)
type=checkCompression(filename)
if filename.endswith(".xz") or type=="xz":
c=lzma.open(filename,"rb")
elif filename.endswith(".gz") or type=="gz":
c=gzip.open(filename,"rb")
else:
c=open(filename,"rb")
line=c.readline()
while line:
self.all_channels.append(line)
line=c.readline()
c.close()
# cprint(">>>>>>>>> %d" % len(self.all_channels))
for line in self.all_channels:
if line.find(needle) is not -1:
sp=line.split("<!-- ")
l=len(sp)
oriname=sp[l-1].rstrip(" -->\r\n")
name = oriname.replace('\xc2\x86', '').replace('\xc2\x87', '').replace(" ","").replace("(","").replace(")","").replace("/","").replace("_","").replace("+","").replace("-","").replace(".","").upper()
if len(name) > 1:
afound=self.allservices.get(name,None)
cfound=self.channelids.get(id,None)
# cprint("SIMILAR CHANNELID %s NAME %s ALL %s FOUND %s" % (id,name,afound,cfound))
if afound is not None:
if cfound is None:
# cprint("SIMILAR FOUND %s %s" %(id,afound))
self.channelids[id]=afound
found_similar+=1
if config.plugins.epgload.similar.value == "save":
s=open(self.similar,"a")
s.write("<channel id=\"%s\">%s</channel><!-- %s -->\n" % (id,afound,oriname))
s.close()
else: # one channelid for multiple serviceref ?
asp=afound.split(" ")
for a in asp:
if cfound.find(a) is -1:
cfound="%s %s" % (cfound, a)
found_similar+=1
if config.plugins.epgload.similar.value == "save":
s=open(self.similar,"a")
s.write("<channel id=\"%s\">%s</channel><!-- %s -->\n" % (id,a,oriname))
s.close()
self.channelids[id]=cfound
# cprint("SIMILAR REFOUND %s %s" %(id,cfound))
if found_similar > 0:
cprint("SIMILAR CHANNELS %s %d %s" % (id, found_similar, self.channelids[id]))
def checkFile(self, filename):
allowed_delta=3600*24*int(config.plugins.epgload.latest.value)
if allowed_delta==0: # reload only after 5 min
allowed_delta=300
if os_path.exists(filename):
st=os_stat(filename)
age=int((time.time()-st.st_mtime))
size=os_path.getsize(filename)
cprint("file %s age %d (seconds) size %d (kB)" % (filename, age, size//1024))
if age > allowed_delta or size < 1024:
# if too old or too small reload
os_remove(filename)
return True
return False
else:
return True
def checkHost(self,url):
host=url[url.find("//")+2:]
sp=[]
sp=host.split(":")
host=sp[0]
sp=host.split("/")
host=sp[0]
resolv=None
try:
resolv=getaddrinfo(host,None)
except gaierror:
cprint("HOST ERROR %s" % host)
return host, resolv
def removeBrackets(self, description):
if config.plugins.epgload.brackets.value == "brackets":
description=description.replace("]\n","")
description=description.replace("[","").replace("]"," ")
description=description.replace("(","( ").replace(")"," ) ")
elif config.plugins.epgload.brackets.value == "full":
begin=description.find("[")
end=description.find("]")
if begin is not -1 and end is not -1:
end+=2
genre=description[begin:end]
# cprint("REMOVES GENRE %s" % (genre))
description=description.replace(genre,"").replace("\n\n","")
description=description.replace("[","[ ").replace("]"," ] ")
description=description.replace("(","( ").replace(")"," ) ")
else:
pass
return description
def extractXML(self, filename):
if not os_path.exists(filename):
cprint("MISSING %s" % filename)
return
size=os_path.getsize(filename)
if size==0:
cprint("EMPTY %s" % filename)
return
elif size//1024==0:
cprint("TOO SMALL %s" % filename)
return
else:
global epgload_loading
cprint("PARSING %s size %d (kB) ..." % (filename, size//1024))
services=[]
self.extracted_kB+=int(size//1024)
type=checkCompression(filename)
# check for html error pages
x=open(filename,'rb')
xml=x.readline()
x.close()
# cprint(">>>>>>>>> %s" % xml[:1024])
if xml.startswith("<!DOCTYPE html>"):
cprint("FAILED %s, has non-xml header" % (filename))
os_remove(filename)
return
if filename.endswith(".xz") or type=="xz":
x = lzma.open(filename,'rb')
elif filename.endswith(".gz") or type=="gz":
x = gzip.open(filename, 'rb')
else:
x = open(filename, 'rb')
try:
xml=x.read()
x.close()
except:
cprint("TRUNCATED %s" % filename)
return
# cprint ">>>>>>>>> %s" % xml[:1024])
root=etree.fromstring(xml)
found_channels=0
if filename.find("channels") is not -1 and filename.find(".xml") is not -1:
cprint("PROCESSING CHANNELS ...")
for child in root.iter():
if not epgload_loading:
cprint("WAS INTERRUPTED")
del root
del xml
return
if child.tag == "channel":
id=child.get('id')
sref=child.text
mref=miniServiceReference(sref)
# cprint(">>>>>>> %s %s %s" % (id ,sref, mref))
if sref is not None and len(id) > 0:
self.loaded_channels+=1
if self.loadService(mref,sref):
found_channels+=1
config.plugins.epgload.loaded.value=("EPG")+" #%d " % (self.extracted_sources)+" "+_("Channel Selection")+": %d " % (found_channels)+" "+_("Size")+": "+'{:,}'.format(self.extracted_kB).replace(",",".")+" kB"
found=self.channelids.get(id,None)
if found is None:
# cprint("FOUND %s %s" %(id, sref))
self.channelids[id]=sref
else: # one channelid for multiple serviceref ?
if found.find(mref) is -1:
self.channelids[id]="%s %s" % (found, sref)
# cprint("REFOUND %s %s" %(id, sref))
if config.plugins.epgload.similar.value != "none":
self.checkSimilarChannels(id)
else:
pass
cprint("CHANNELS FOUND %d FROM TOTAL %d" %(len(self.channelids),self.loaded_channels))
else: # programme events ...
cprint("PROCESSING PROGRAMS ...")
self.extracted_sources+=1
begin=0
duration=0
description=""
title=""
subtitle=""
channelid_previous=""
language="eng"
self.epg.preprocess_events_channel() # reset events
for child in root.iter():
if not epgload_loading:
cprint("WAS INTERRUPTED")
del root
del xml
return
if child.tag == "programme":
# cprint(">>> %s %s %s" % (child.tag, child.attrib, child.text))
#
# process previous event
#
if config.plugins.epgload.episode.value:
title=self.getSeasonEpisode(title, subtitle, description)
# NOW add event ...
if begin > 0:
if len(subtitle) > 0: # add subtitle at begin of description
description=subtitle+"\n\n"+description
# begin_time = datetime.datetime.fromtimestamp(begin)
# cprint(">>>> %s %d %d %s" % (channelid, begin_time, duration//60, title)
description=self.removeBrackets(description)
if config.plugins.epgload.language.value != "default":
language=str(config.plugins.epgload.language.value)
self.epg.add_event(begin, duration, title, description, language)
self.extracted_events+=1
config.plugins.epgload.loaded.value=("EPG")+" #%d " % (self.extracted_sources)+" "+_("Channels")+": %d " % (self.extracted_channels)+_("Info")+" "+_("Details")+": "+'{:,}'.format(self.extracted_events).replace(",",".")+" "+_("Size")+": "+'{:,}'.format(self.extracted_kB).replace(",",".")+" kB"
description=""
title=""
subtitle=""
#
# now process new programm event
#
start=child.get('start')
begin=self.getUTCTime(start)
stop=child.get('stop')
end=self.getUTCTime(stop)
duration=end-begin
if duration < 1:
duration=1
channelid=child.get('channel')
# cprint(">>> %s" % (channelid))
if len(channelid_previous)==0: # ONLY first time
channelid_previous=channelid
# store previous channel events before parsing new ones
if channelid != channelid_previous:
self.parsed_channels+=1
srefs=self.channelids.get(channelid_previous,None)
if srefs is not None:
services=srefs.split(" ")
self.extracted_channels+=len(services)
# cprint("LOADS CHANNEL(S) %s %s" % (channelid_previous, services))
self.epg.preprocess_events_channel(services)
config.plugins.epgload.loaded.value=("EPG")+" #%d " % (self.extracted_sources)+" "+_("Channels")+": %d " % (self.extracted_channels)+_("Info")+" "+_("Details")+": "+'{:,}'.format(self.extracted_events).replace(",",".")+" "+_("Size")+": "+'{:,}'.format(self.extracted_kB).replace(",",".")+" kB"
else: # reset events for NOT wanted channels
cprint("IGNORES CHANNEL %s" % channelid_previous)
self.epg.preprocess_events_channel()
channelid_previous=channelid
elif child.tag == "title":
lang=child.get('lang')
language=self.getLanguageCode(lang)
utitle=child.text
if utitle is not None:
title=utitle.encode('utf-8')
else:
title=""
subtitle=""
elif child.tag == "sub-title":
lang=child.get('lang')
usubtitle=child.text
if usubtitle is not None:
subtitle=usubtitle.encode('utf-8')
else:
subtitle=""
elif child.tag == "desc":
udescription=child.text
if udescription is not None:
description=udescription.encode('utf-8')
else:
description=""
lang=child.get('lang')
else:
pass
# housekeeping ...
srefs=self.channelids.get(channelid,None)
if srefs is not None:
services=srefs.split(" ")
self.extracted_channels+=1
self.epg.preprocess_events_channel(services)
else: # reset events for NOT wanted channels
cprint("IGNORES CHANNEL %s" % channelid)
self.epg.preprocess_events_channel()
cprint("FOUND %d CHANNELS from %d with %d EVENTS" % (self.extracted_channels, self.parsed_channels, self.extracted_events))
del root
del xml
config.plugins.epgload.loaded.value=("EPG")+" #%d " % (self.extracted_sources)+" "+_("Channels")+": %d " % (self.extracted_channels)+_("Info")+" "+_("Details")+": "+'{:,}'.format(self.extracted_events).replace(",",".")+" "+_("Size")+": "+'{:,}'.format(self.extracted_kB).replace(",",".")+" kB"
# empty cache if asked for it ...
if config.plugins.epgload.empty.value.startswith("cache") and not filename.startswith("/etc/epgload"):
os_remove(filename)
def loadService(self, mref, sref):
if config.plugins.epgload.all.value and sref.find("//") is not -1 :
return True
if mref in self.services:
return True
else:
return False
def nextEPGDownload(self):
if not epgload_loading:
cprint("WAS INTERRUPTED")
return
cprint("NEXT DOWNLOAD !!!!!")
self.download+=1
threads.deferToThread(self.doEPGDownload).addCallback(lambda ignore: self.finishEPGDownload())
def afterEPGDownload(self, result, filename, deleteFile=False):
if not epgload_loading:
cprint("WAS INTERRUPTED")
return
if filename=="/etc/epgload/LastUpdate.txt":
if not self.checkLastUpdate():
self.failedEPGDownload(True)
return
cprint("CHOICE %d %s" % (len(self.urls), self.url))
filename=os_path.basename(self.url)
sp=filename.split("?")
filename=sp[0]
target=self.cache+"/"+filename.replace("#nocheck","")
sourceurl=self.url.replace("#nocheck","")
if self.checkFile(target):
# prevent endless downloads of same file if too small ...
self.trials+=1
if self.trials > 5:
self.trials=0
self.failedEPGDownload(True)
return
else:
cprint("DOWNLOAD %s >>> %s #%d" % (sourceurl, target, self.trials))
downloadPage(sourceurl, target, timeout=int(config.plugins.epgload.timeout.value)).addCallbacks(self.afterEPGDownload,self.failedEPGDownload, callbackArgs=(target,False))
else:
self.trials=0
# found file is OK, extracting data, then download next
cprint("PARSE %s" % target)
threads.deferToThread(self.extractXML, target).addCallback(lambda ignore: self.nextEPGDownload())
def failedEPGDownload(self, failure):
self.trials=0
if not epgload_loading:
cprint("WAS INTERRUPTED")
return
cprint("FAILED %s next try ..." % (self.url))
for x in self.urls:
if x==self.url:
self.urls.remove(x)
if len(self.urls) > 0:
cprint("SAME !!!")
else:
cprint("FAILED %s GIVING UP !!!" % (self.url))
cprint("NEXT !!!")
self.download+=1
threads.deferToThread(self.doEPGDownload).addCallback(lambda ignore: self.finishEPGDownload())
def getSeasonEpisode(self, title, subtitle, description):
episode=[]
episode=description.split()
length=len(episode)
if length > 2:
if episode[length-1].startswith("Ep"):
if episode[length-2].startswith("S") or episode[length-2].startswith("(S"):
# Season and Episode
cprint("*** EPISODE *** %s %s " % (episode[length-2],episode[length-1]))
title="%s - %s - %s" % (title,episode[length-2],episode[length-1])
else:
# only Episode
cprint("*** EPISODE *** %s" % episode[length-1])
title="%s - %s" % (title,episode[length-1])
if episode[1].startswith("Staffel"):
cprint("*** EPISODE *** %s %s %s %s" % (episode[0],episode[1],episode[2],episode[3]))
title="%s - %s %s - %s %s" % (title, episode[0],episode[1],episode[2],episode[3])
if episode[1].startswith("Folge"):
cprint("*** EPISODE *** %s %s" % (episode[0],episode[1]))
title="%s - %s %s" % (title,episode[0],episode[1])
if len(subtitle) > 0:
epi=subtitle.find("(E")
if epi is not -1:
epi+=2
episode=subtitle[epi:]
epi=episode.find(")")
if epi is not -1 and epi < 5:
episode=episode[:epi]
if episode.isdigit():
cprint("*** EPISODE *** %s" % episode)
title="%s - %s %s" % (title,_("Episode"),episode)
sea=subtitle.find("(S")
if sea is not -1:
sea+=2
season=subtitle[sea:]
sea=season.find(")")
if sea is not -1 and sea < 10:
season=season[:sea]
sp=season.split("E")
if sp[0].isdigit():
season=sp[0]
if len(sp) > 1 and sp[1].isdigit():
episode=sp[1]
cprint("*** EPISODE *** %s %s" % (season,episode))
title="%s - %s %s %s %s" % (title,_("Season"),season,_("Episode"),episode)
return title
def getAllBouquetServices(self):
services=[]
sp=[]
for bouquet in os_listdir("/etc/enigma2"):
if bouquet.startswith("userbouquet.") and bouquet.endswith(".tv"):
f=open("/etc/enigma2/%s" % bouquet,"r")
line=f.readline()
bouquet_name = line.replace("#NAME ","").rstrip().lstrip()
# cprint("CHECKS bouquet %s" % bouquet_name)
found=False
for x in range(bouquet_length):
# cprint(">>>>>>>" %s %s %s %s" % (bouquet_name, bouquet, config.plugins.epgload.bouquets[x].file.value, config.plugins.epgload.bouquets[x].importing.value))
if bouquet==config.plugins.epgload.bouquets[x].file.value and config.plugins.epgload.bouquets[x].importing.value:
cprint("FOUND bouquet %s" % bouquet_name)
found=True
if found:
while line:
line=f.readline()
if line.startswith("#SERVICE"):
service=line.replace("#SERVICE ","").rstrip('\r\n')
if service.find(":0:0:0:0:0:0:0:") is -1:
mref=miniServiceReference(service)
services.append(mref)
else:
cprint("IGNORES bouquet %s" % bouquet_name)
f.close()
return services
def getLanguageCode(self, lang='en'):
l=epgload_languages.get(lang,"eng")
# cprint("LANGUAGE %s %s" % (lang,l))
return l.encode('utf-8')
def getUTCTime(self, timestring):
values = timestring.split(' ')
tm = time.strptime(values[0], date_format)
epoch_time = int(timegm(tm))
if len(values) > 1:
# when timestring says +0300 => that means we have
# to substract 3 hours from localtime to get gmt
epoch_time -= int(3600*int(values[1])//100)
# cprint("UTC %s %d" % (timestring, epoch_time)
return epoch_time
def checkLastUpdate(self):
update_file_name = '/etc/epgload/LastUpdate.txt'
if os_path.exists(update_file_name):
# Used to check server validity
date_format = "%Y-%m-%d"
allowed_delta=3600*int(config.plugins.epgload.latest.value)
now=int(time.time())
# cprint("NOW %d" % now)
x = open(update_file_name,'r')
Last=x.readline()
x.close()
LastTime = Last.strip('\n')
file_date = 0
FileDate = datetime.datetime.strptime(LastTime, date_format)
file_date=int(FileDate.strftime('%s'))
delta = (now - file_date)
cprint("DELTA %d ALLOWED %d" % (delta, allowed_delta))
if delta <= allowed_delta:
cprint("DELTA %d OK" % delta)
return True
return False
def exportLastUpdate(self, create=True):
now = int(time.time())
config.plugins.epgload.last.value=now
config.plugins.epgload.last.save()
now=datetime.datetime.now()
date=now.strftime('%Y-%m-%d %H:%M:%S')
cprint("EXPORT DATE %s" % date)
cache=getEPGLoadCache()
update_file_name = "%s/LastUpdate.txt" % cache
if os_path.exists(update_file_name):
os_remove(update_file_name)
if create:
x = open(update_file_name,'w')
x.write(date)
x.write("\n")
x.close()