Source code for controllers.models.services

# -*- coding: utf-8 -*-

##############################################################################
#                        2011 E2OpenPlugins                                  #
#                                                                            #
#  This file is open source software; you can redistribute it and/or modify  #
#     it under the terms of the GNU General Public License version 2 as      #
#               published by the Free Software Foundation.                   #
#                                                                            #
##############################################################################
import os
import re
import unicodedata
import logging
from time import localtime, strftime
from urllib import quote, unquote

from ..i18n import _, tstrings
from ..defaults import PICON_EXT, PICON_PATH
from Components.Sources.ServiceList import ServiceList
from Components.ParentalControl import parentalControl
from Components.config import config
from Components.NimManager import nimmanager
from ServiceReference import ServiceReference
from Screens.ChannelSelection import service_types_tv, service_types_radio, \
    FLAG_SERVICE_NEW_FOUND
from enigma import eServiceCenter, eServiceReference, \
    iServiceInformation, eEPGCache
from info import GetWithAlternative
from info import FALLBACK_PICON_LOCATION, PICON_ENDPOINT_PATH

from model_utilities import mangle_epg_text
from events import FLAGS_WEB, ServicesEventDict

SLOG = logging.getLogger("services")


def getServiceInfoString(info, what):
    v = info.getInfo(what)
    if v == -1:
        return "N/A"
    if v == -2:
        return info.getInfoString(what)
    return v


def getCurrentService(session):
    try:
        info = session.nav.getCurrentService().info()
        return {
            "result": True,
            "name": mangle_epg_text(info.getName()),
            "namespace": getServiceInfoString(info,
                                              iServiceInformation.sNamespace),
            "aspect": getServiceInfoString(info, iServiceInformation.sAspect),
            "provider": getServiceInfoString(info,
                                             iServiceInformation.sProvider),
            "width": getServiceInfoString(info,
                                          iServiceInformation.sVideoWidth),
            "height": getServiceInfoString(info,
                                           iServiceInformation.sVideoHeight),
            "apid": getServiceInfoString(info, iServiceInformation.sAudioPID),
            "vpid": getServiceInfoString(info, iServiceInformation.sVideoPID),
            "pcrpid": getServiceInfoString(info, iServiceInformation.sPCRPID),
            "pmtpid": getServiceInfoString(info, iServiceInformation.sPMTPID),
            "txtpid": getServiceInfoString(info, iServiceInformation.sTXTPID),
            "tsid": getServiceInfoString(info, iServiceInformation.sTSID),
            "onid": getServiceInfoString(info, iServiceInformation.sONID),
            "sid": getServiceInfoString(info, iServiceInformation.sSID),
            "ref": quote(
                getServiceInfoString(info, iServiceInformation.sServiceref),
                safe=' ~@#$&()*!+=:;,.?/\''),
            "iswidescreen": info.getInfo(iServiceInformation.sAspect) in (
                3, 4, 7, 8, 0xB, 0xC, 0xF, 0x10)
        }
    except Exception:
        return {
            "result": False,
            "name": "",
            "namespace": "",
            "aspect": 0,
            "provider": "",
            "width": 0,
            "height": 0,
            "apid": 0,
            "vpid": 0,
            "pcrpid": 0,
            "pmtpid": 0,
            "txtpid": "N/A",
            "tsid": 0,
            "onid": 0,
            "sid": 0,
            "ref": "",
            "iswidescreen": False
        }


def getBouquets(stype):
    s_type = service_types_tv
    s_type2 = "bouquets.tv"
    if stype == "radio":
        s_type = service_types_radio
        s_type2 = "bouquets.radio"
    serviceHandler = eServiceCenter.getInstance()
    services = serviceHandler.list(
        eServiceReference(
            '%s FROM BOUQUET "%s" ORDER BY bouquet' %
            (s_type, s_type2)))
    bouquets = services and services.getContent("SN", True)
    bouquets = removeHiddenBouquets(bouquets)
    return {"bouquets": bouquets}


def removeHiddenBouquets(bouquetList):
    bouquets = bouquetList
    if hasattr(eServiceReference, 'isInvisible'):
        for bouquet in bouquetList:
            flags = int(bouquet[0].split(':')[1])
            if flags & eServiceReference.isInvisible and bouquet in bouquets:
                bouquets.remove(bouquet)
    return bouquets


def getSatellites(stype):
    ret = []
    s_type = service_types_tv
    if stype == "radio":
        s_type = service_types_radio
    refstr = '%s FROM SATELLITES ORDER BY satellitePosition' % (s_type)
    ref = eServiceReference(refstr)
    serviceHandler = eServiceCenter.getInstance()
    servicelist = serviceHandler.list(ref)
    if servicelist is not None:
        while True:
            service = servicelist.getNext()
            if not service.valid():
                break
            unsigned_orbpos = service.getUnsignedData(4) >> 16
            orbpos = service.getData(4) >> 16
            if orbpos < 0:
                orbpos += 3600
            if service.getPath().find("FROM PROVIDER") != -1:
                # service_type = _("Providers")
                continue
            elif service.getPath().find(
                            "flags == %d" % (FLAG_SERVICE_NEW_FOUND)) != -1:
                service_type = _("New")
            else:
                service_type = _("Services")
            try:
                service_name = str(nimmanager.getSatDescription(orbpos))
            except BaseException:
                if unsigned_orbpos == 0xFFFF:  # Cable
                    service_name = _("Cable")
                elif unsigned_orbpos == 0xEEEE:  # Terrestrial
                    service_name = _("Terrestrial")
                else:
                    if orbpos > 1800:  # west
                        orbpos = 3600 - orbpos
                        h = _("W")
                    else:
                        h = _("E")
                    service_name = ("%d.%d" + h) % (orbpos / 10, orbpos % 10)
            service.setName("%s - %s" % (service_name, service_type))
            ret.append({
                "service": service.toString(),
                "name": service.getName()
            })
    ret = sortSatellites(ret)
    return {"satellites": ret}


def sortSatellites(satList):
    sortDict = {}
    i = 0
    for k in satList:
        result = re.search(
            "[(]\s*satellitePosition\s*==\s*(\d+)\s*[)]",
            k["service"],
            re.IGNORECASE)
        if result is None:
            return satList
        orb = int(result.group(1))
        if orb > 3600:
            orb *= -1
        elif orb > 1800:
            orb -= 3600
        if orb not in sortDict:
            sortDict[orb] = []
        sortDict[orb].append(i)
        i += 1
    outList = []
    for l in sorted(sortDict.keys()):
        for v in sortDict[l]:
            outList.append(satList[v])
    return outList


def getServices(sRef, showAll=True, showHidden=False, pos=0):
    services = []

    if sRef == "":
        sRef = '%s FROM BOUQUET "bouquets.tv" ORDER BY bouquet' % (
            service_types_tv)

    servicelist = ServiceList(eServiceReference(sRef))
    slist = servicelist.getServicesAsList()

    for sitem in slist:
        st = int(sitem[0].split(":")[1])
        if (sitem[0][:7] == '1:832:D') or (not (st & 512) and not (st & 64)):
            pos = pos + 1
        if not st & 512 or showHidden:
            if showAll or st == 0:
                service = {}
                service['pos'] = 0 if (st & 64) else pos
                service['servicereference'] = unicode(
                    sitem[0], 'utf_8', errors='ignore').encode(
                    'utf_8', 'ignore')
                service['program'] = int(
                    service['servicereference'].split(':')[3], 16)
                service['servicename'] = unicode(
                    sitem[1], 'utf_8', errors='ignore').encode(
                    'utf_8', 'ignore')
                services.append(service)

    return {"services": services, "pos": pos}


def getAllServices(type):
    services = []
    if type is None:
        type = "tv"
    bouquets = getBouquets(type)["bouquets"]
    pos = 0
    for bouquet in bouquets:
        sv = getServices(bouquet[0], True, False, pos)
        services.append({
            "servicereference": bouquet[0],
            "servicename": bouquet[1],
            "subservices": sv["services"]
        })
        pos = sv["pos"]

    return {
        "result": True,
        "services": services
    }


def getPlayableServices(sRef, sRefPlaying):
    if sRef == "":
        sRef = '%s FROM BOUQUET "bouquets.tv" ORDER BY bouquet' % (
            service_types_tv)

    services = []
    servicecenter = eServiceCenter.getInstance()
    servicelist = servicecenter.list(eServiceReference(sRef))
    servicelist2 = servicelist and servicelist.getContent('S') or []

    for service in servicelist2:
        # 512 is hidden service on sifteam image. Doesn't affect other images
        if not int(service.split(":")[1]) & 512:
            service2 = {}
            service2['servicereference'] = service
            info = servicecenter.info(eServiceReference(service))
            service2['isplayable'] = info.isPlayable(
                eServiceReference(service), eServiceReference(sRefPlaying)) > 0
            services.append(service2)

    return {
        "result": True,
        "services": services
    }


def getPlayableService(sRef, sRefPlaying):
    servicecenter = eServiceCenter.getInstance()
    info = servicecenter.info(eServiceReference(sRef))
    return {
        "result": True,
        "service": {
            "servicereference": sRef,
            "isplayable": info.isPlayable(
                eServiceReference(sRef),
                eServiceReference(sRefPlaying)) > 0}}


def getSubServices(session):
    # TODO: duplicated .. stream/getStreamSubservices
    services = []
    service = session.nav.getCurrentService()
    if service is not None:
        services.append({"servicereference": service.info().getInfoString(
            iServiceInformation.sServiceref),
            "servicename": service.info().getName()})
        subservices = service.subServices()
        if subservices and subservices.getNumberOfSubservices() > 0:
            SLOG.debug(
                "subservices.getNumberOfSubservices() yielded {!r}".format(
                    subservices.getNumberOfSubservices()))
            for i in range(subservices.getNumberOfSubservices()):
                sub = subservices.getSubservice(i)
                services.append({
                    "servicereference": sub.toString(),
                    "servicename": sub.getName()
                })
    else:
        services.append({
            "servicereference": "N/A",
            "servicename": "N/A"
        })

    return {"services": services}


def getEvent(ref, idev):
    epgcache = eEPGCache.getInstance()
    events = epgcache.lookupEvent(['IBDTSENRX', (ref, 2, int(idev))])
    info = {}
    for event in events:
        info['id'] = event[0]
        info['begin_str'] = strftime("%H:%M", (localtime(event[1])))
        info['begin'] = event[1]
        info['end'] = strftime("%H:%M", (localtime(event[1] + event[2])))
        info['duration'] = event[2]
        info['title'] = mangle_epg_text(event[3])
        info['shortdesc'] = event[4]
        info['longdesc'] = event[5]
        info['channel'] = mangle_epg_text(event[6])
        info['sref'] = event[7]
        break
    return {'event': info}


def getChannelEpg(ref, begintime=-1, endtime=-1):
    ret = []
    ev = {}
    use_empty_ev = False
    if ref:
        ref = unquote(ref)

        # When quering EPG we dont need URL, also getPicon doesn't like URL
        if "://" in ref:
            ref = ":".join(ref.split(":")[:10]) + "::" + ref.split(":")[-1]

        picon = getPicon(ref)
        epgcache = eEPGCache.getInstance()
        events = epgcache.lookupEvent(
            ['IBDTSENC', (ref, 0, begintime, endtime)])
        if events is not None:
            for event in events:
                ev = {}
                ev['picon'] = picon
                ev['id'] = event[0]
                if event[1]:
                    ev['date'] = "%s %s" % (tstrings[(
                        "day_" + strftime("%w", (localtime(event[1]))))],
                                            strftime("%d.%m.%Y",
                                                     (localtime(event[1]))))
                    ev['begin'] = strftime("%H:%M", (localtime(event[1])))
                    ev['begin_timestamp'] = event[1]
                    ev['duration'] = int(event[2] / 60)
                    ev['duration_sec'] = event[2]
                    ev['end'] = strftime(
                        "%H:%M", (localtime(event[1] + event[2])))
                    ev['title'] = mangle_epg_text(event[3])
                    ev['shortdesc'] = event[4]
                    ev['longdesc'] = event[5]
                    ev['sref'] = ref
                    ev['sname'] = mangle_epg_text(event[6])
                    ev['tleft'] = int(((event[1] + event[2]) - event[7]) / 60)
                    if ev['duration_sec'] == 0:
                        ev['progress'] = 0
                    else:
                        ev['progress'] = int(
                            ((event[7] - event[1]) * 100 / event[2]) * 4)
                    ev['now_timestamp'] = event[7]
                    ret.append(ev)
                else:
                    use_empty_ev = True
                    ev['sref'] = ref
    else:
        use_empty_ev = True
        ev['sref'] = ""

    if use_empty_ev:
        ev['date'] = 0
        ev['begin'] = 0
        ev['begin_timestamp'] = 0
        ev['duration'] = 0
        ev['duration_sec'] = 0
        ev['end'] = 0
        ev['title'] = "N/A"
        ev['shortdesc'] = ""
        ev['sname'] = ""
        ev['longdesc'] = ""
        ev['tleft'] = 0
        ev['progress'] = 0
        ev['now_timestamp'] = 0
        ret.append(ev)

    return {"events": ret, "result": True}


def getBouquetEpg(ref, begintime=-1, endtime=None, mangle_html=True):
    ret = []
    services = eServiceCenter.getInstance().list(eServiceReference(ref))
    if not services:
        return {"events": ret, "result": False}

    search = [FLAGS_WEB]
    for service in services.getContent('S'):
        if endtime:
            search.append((service, 0, begintime, endtime))
        else:
            search.append((service, 0, begintime))

    epgcache = eEPGCache.getInstance()
    events = epgcache.lookupEvent(search)
    if not events:
        return {"events": [], "result": True}

    for raw_data in events:
        ret.append(ServicesEventDict(
            raw_data, now_next_mode=False, mangle_html=mangle_html))

    return {"events": ret, "result": True}


def getServicesNowNextEpg(sList, mangle_html=True):
    ret = []
    if not sList:
        return {"events": ret, "result": False}

    sRefList = sList.split(",")
    search = [FLAGS_WEB]
    for service in sRefList:
        search.append((service, 0, -1))
        search.append((service, 1, -1))

    epgcache = eEPGCache.getInstance()
    events = epgcache.lookupEvent(search)
    if not events:
        return {"events": [], "result": True}

    for raw_data in events:
        ret.append(ServicesEventDict(
            raw_data, now_next_mode=True, mangle_html=mangle_html))

    return {"events": ret, "result": True}


def getBouquetNowNextEpg(ref, servicetype, mangle_html=True):
    ret = []
    services = eServiceCenter.getInstance().list(eServiceReference(ref))
    if not services:
        return {"events": ret, "result": False}

    search = [FLAGS_WEB]
    if servicetype == -1:
        for service in services.getContent('S'):
            search.append((service, 0, -1))
            search.append((service, 1, -1))
    else:
        for service in services.getContent('S'):
            search.append((service, servicetype, -1))

    epgcache = eEPGCache.getInstance()
    events = epgcache.lookupEvent(search)

    if not events:
        return {"events": [], "result": True}

    for raw_data in events:
        e_data = ServicesEventDict(
            raw_data, now_next_mode=False, mangle_html=mangle_html)
        if e_data['sref'] is not None:
            achannels = GetWithAlternative(e_data['sref'], False)
            if achannels:
                e_data['asrefs'] = achannels
        ret.append(e_data)

    return {"events": ret, "result": True}


def getNowNextEpg(ref, servicetype, mangle_html=True):
    ret = []
    epgcache = eEPGCache.getInstance()
    events = epgcache.lookupEvent([FLAGS_WEB, (ref, servicetype, -1)])

    if not events:
        return {"events": [], "result": True}

    for raw_data in events:
        ret.append(ServicesEventDict(
            raw_data, now_next_mode=True, mangle_html=mangle_html))

    return {"events": ret, "result": True}


[docs]def getSearchEpg(sstr, endtime=None, fulldesc=False, bouquetsonly=False): """ Search for EPG events. Args: sstr (basestring): search term endtime (int): timetamp fulldesc (bool): search in event's description field too bouquetsonly (bool): limit results to services in known bouquets Returns: list: event datasets """ bsref = {} events = [] epgcache = eEPGCache.getInstance() search_type = eEPGCache.PARTIAL_TITLE_SEARCH if config.OpenWebif.epg_encoding.value != 'utf-8': try: sstr = sstr.encode(config.OpenWebif.epg_encoding.value) except UnicodeEncodeError: pass if fulldesc: if hasattr(eEPGCache, 'FULL_DESCRIPTION_SEARCH'): search_type = eEPGCache.FULL_DESCRIPTION_SEARCH results = epgcache.search(('IBDTSENR', 128, search_type, sstr, 1)) if results: if bouquetsonly: # collect service references from TV bouquets for service in getAllServices('tv')['services']: for service2 in service['subservices']: bsref[service2['servicereference']] = True else: bsref[service['servicereference']] = True for event in results: if bouquetsonly and not event[7] in bsref: continue day_val = strftime("%w", (localtime(event[1]))) date_val = strftime("%d.%m.%Y", (localtime(event[1]))) ev = dict() ev['id'] = event[0] ev['date'] = "%s %s" % (tstrings[("day_" + day_val)], date_val) ev['begin_timestamp'] = event[1] ev['begin'] = strftime("%H:%M", (localtime(event[1]))) ev['duration_sec'] = event[2] ev['duration'] = int(event[2] / 60) ev['end'] = strftime("%H:%M", (localtime(event[1] + event[2]))) ev['title'] = mangle_epg_text(event[3]) ev['shortdesc'] = event[4] ev['longdesc'] = event[5] ev['sref'] = event[7] ev['sname'] = mangle_epg_text(event[6]) ev['picon'] = getPicon(event[7]) ev['now_timestamp'] = None if endtime: # don't show events if begin is after endtime if event[1] <= endtime: events.append(ev) else: events.append(ev) return {"events": events, "result": True}
def getSearchSimilarEpg(ref, eventid): ref = unquote(ref) ret = [] epgcache = eEPGCache.getInstance() events = epgcache.search( ('IBDTSENR', 128, eEPGCache.SIMILAR_BROADCASTINGS_SEARCH, ref, eventid)) if events is not None: for event in events: day_val = strftime("%w", (localtime(event[1]))) date_val = strftime("%d.%m.%Y", (localtime(event[1]))) ev = {} ev['id'] = event[0] ev['date'] = "%s %s" % (tstrings[("day_" + day_val)], date_val) ev['begin_timestamp'] = event[1] ev['begin'] = strftime("%H:%M", (localtime(event[1]))) ev['duration_sec'] = event[2] ev['duration'] = int(event[2] / 60) ev['end'] = strftime("%H:%M", (localtime(event[1] + event[2]))) ev['title'] = event[3] ev['shortdesc'] = event[4] ev['longdesc'] = event[5] ev['sref'] = event[7] ev['sname'] = mangle_epg_text(event[6]) ev['picon'] = getPicon(event[7]) ev['now_timestamp'] = None ret.append(ev) return {"events": ret, "result": True} def getPicon(sname): if not PICON_PATH: return FALLBACK_PICON_LOCATION # remove URL part if ("://" in sname) or ("%3a//" in sname) or ("%3A//" in sname): sname = unquote(sname) sname = ":".join(sname.split(":")[:10]) + "::" + sname.split(":")[-1] sname = GetWithAlternative(sname) if sname is not None: pos = sname.rfind(':') else: return FALLBACK_PICON_LOCATION cname = None if pos != -1: cname = ServiceReference(sname[:pos].rstrip(':')).getServiceName() sname = sname[:pos].rstrip(':').replace(':', '_') + PICON_EXT filename = PICON_PATH + sname if os.path.isfile(filename): return PICON_ENDPOINT_PATH + sname fields = sname.split('_', 8) if len(fields) > 7 and not fields[6].endswith("0000"): # remove "sub-network" from namespace fields[6] = fields[6][:-4] + "0000" sname = '_'.join(fields) filename = PICON_PATH + sname if os.path.isfile(filename): return PICON_ENDPOINT_PATH + sname if len(fields) > 1 and fields[0] != '1': # fallback to 1 for other reftypes fields[0] = '1' sname = '_'.join(fields) filename = PICON_PATH + sname if os.path.isfile(filename): return PICON_ENDPOINT_PATH + sname if len(fields) > 3 and fields[2] != '1': # fallback to 1 for tv services with nonstandard servicetypes fields[2] = '1' sname = '_'.join(fields) filename = PICON_PATH + sname if os.path.isfile(filename): return PICON_ENDPOINT_PATH + sname if cname is not None: # picon by channel name cname1 = mangle_epg_text(cname).replace( '/', '_').encode('utf-8', 'ignore') if os.path.isfile(PICON_PATH + cname1 + PICON_EXT): return PICON_ENDPOINT_PATH + cname1 + PICON_EXT cname = unicodedata.normalize( 'NFKD', unicode(cname, 'utf_8', errors='ignore')).encode( 'ASCII', 'ignore') cname = re.sub( '[^a-z0-9]', '', cname.replace('&', 'and').replace( '+', 'plus').replace( '*', 'star').lower()) if len(cname) > 0: filename = PICON_PATH + cname + PICON_EXT if os.path.isfile(filename): return PICON_ENDPOINT_PATH + cname + PICON_EXT if len(cname) > 2 and cname.endswith( 'hd') and os.path.isfile(PICON_PATH + cname[:-2] + PICON_EXT): return PICON_ENDPOINT_PATH + cname[:-2] + PICON_EXT def getParentalControlList(): if config.ParentalControl.configured.value: return { "result": True, "services": [] } parentalControl.open() if config.ParentalControl.type.value == "whitelist": tservices = parentalControl.whitelist else: tservices = parentalControl.blacklist services = [] if tservices is not None: for service in tservices: tservice = ServiceReference(service) services.append({ "servicereference": service, "servicename": tservice.getServiceName() }) return { "result": True, "type": config.ParentalControl.type.value, "services": services }