Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
Size: Mime:
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()