コード例 #1
0
ファイル: epg2xml.py プロジェクト: arantius/tvheadend-locast
class EPG:
    def __init__(self, _plugins, _namespace, _instance):
        self.logger = logging.getLogger(__name__)
        self.config = _plugins.config_obj.data
        self.epg_db = DBepg(self.config)
        self.channels_db = DBChannels(self.config)
        self.plugins = _plugins
        self.namespace = _namespace
        self.instance = _instance

    def get_epg_xml(self):
        self.plugins.refresh_epg(self.namespace, self.instance)
        xml_out = self.gen_header_xml()
        channel_list = self.channels_db.get_channels(self.namespace,
                                                     self.instance)
        self.gen_channel_xml(xml_out, channel_list)
        self.epg_db.init_get_query(self.namespace, self.instance)
        day_data = self.epg_db.get_next_row()
        while day_data:
            self.gen_program_xml(xml_out, day_data)
            day_data = self.epg_db.get_next_row()
        epg_dom = minidom.parseString(
            ElementTree.tostring(xml_out, encoding='UTF-8', method='xml'))
        return epg_dom.toprettyxml()

    def gen_channel_xml(self, _et_root, _channel_list):
        for sid, ch_data in _channel_list.items():
            c_out = self.sub_el(_et_root, 'channel', id=sid)
            self.sub_el(
                c_out,
                'display-name',
                text='%s%s%s %s' %
                (self.config['epg']['epg_prefix'], ch_data['display_number'],
                 self.config['epg']['epg_suffix'], ch_data['display_name']))
            self.sub_el(
                c_out,
                'display-name',
                text='%s%s%s %s' %
                (self.config['epg']['epg_prefix'], ch_data['display_number'],
                 self.config['epg']['epg_suffix'],
                 ch_data['json']['callsign']))
            self.sub_el(c_out, 'display-name', text=ch_data['display_number'])
            self.sub_el(c_out,
                        'display-name',
                        text=ch_data['json']['callsign'])
            self.sub_el(c_out, 'display-name', text=ch_data['display_name'])

            if self.config['locast']['epg_channel_icon']:
                self.sub_el(c_out, 'icon', src=ch_data['thumbnail'])
        return _et_root

    def gen_program_xml(self, _et_root, _prog_list):
        for prog_data in _prog_list:
            prog_out = self.sub_el(_et_root,
                                   'programme',
                                   start=prog_data['start'],
                                   stop=prog_data['stop'],
                                   channel=prog_data['channel'])
            if prog_data['title']:
                self.sub_el(prog_out, 'title', text=prog_data['title'])
            if prog_data['subtitle']:
                self.sub_el(prog_out, 'sub-title', text=prog_data['subtitle'])
            descr_add = ''
            if self.config['epg']['description'] == 'extend':

                if prog_data['formatted_date']:
                    descr_add += '(' + prog_data['formatted_date'] + ') '
                if prog_data['genres']:
                    descr_add += ' / '.join(prog_data['genres']) + ' / '
                if prog_data['se_common']:
                    descr_add += prog_data['se_common']
                descr_add += '\n' + prog_data['desc']
            elif self.config['epg']['description'] == 'brief':
                descr_add = prog_data['short_desc']
            elif self.config['epg']['description'] == 'normal':
                descr_add = prog_data['desc']
            else:
                self.logger.warning(
                    'Config value [epg][description] is invalid: ' +
                    self.config['epg']['description'])
            self.sub_el(prog_out, 'desc', text=descr_add)

            if prog_data['video_quality']:
                video_out = self.sub_el(prog_out, 'video')
                self.sub_el(video_out, 'quality', prog_data['video_quality'])

            if prog_data['air_date']:
                self.sub_el(prog_out,
                            'date',
                            lang='en',
                            text=prog_data['air_date'])

            self.sub_el(prog_out,
                        'length',
                        units='minutes',
                        text=str(prog_data['length']))

            if prog_data['genres']:
                for f in prog_data['genres']:
                    if self.config['epg']['genre'] == 'normal':
                        pass
                    elif self.config['epg']['genre'] == 'tvheadend':
                        if f in epg_category.TVHEADEND.keys():
                            f = epg_category.TVHEADEND[f]
                    else:
                        self.logger.warning(
                            'Config value [epg][genre] is invalid: ' +
                            self.config['epg']['genre'])
                    self.sub_el(prog_out,
                                'category',
                                lang='en',
                                text=f.strip())

            if prog_data['icon'] and self.config['locast']['epg_program_icon']:
                self.sub_el(prog_out, 'icon', src=prog_data['icon'])

            if prog_data['rating']:
                r = ElementTree.SubElement(prog_out, 'rating')
                self.sub_el(r, 'value', text=prog_data['rating'])

            if prog_data['se_common']:
                self.sub_el(prog_out,
                            'episode-num',
                            system='common',
                            text=prog_data['se_common'])
                self.sub_el(prog_out,
                            'episode-num',
                            system='xmltv_ns',
                            text=prog_data['se_xmltv_ns'])
                self.sub_el(prog_out,
                            'episode-num',
                            system='SxxExx',
                            text=prog_data['se_common'])

            if prog_data['is_new']:
                self.sub_el(prog_out, 'new')

    def gen_header_xml(self):
        xml_out = ElementTree.Element('tv')
        xml_out.set('source-info-url', 'https://www.locast.org')
        xml_out.set('source-info-name', 'locast.org')
        xml_out.set('generator-info-name', 'locastepg')
        xml_out.set('generator-info-url',
                    'github.com/rocky4546/tvheadend-locast')
        xml_out.set('generator-special-thanks', 'locast2plex')
        return xml_out

    def sub_el(self, parent, name, text=None, **kwargs):
        el = ElementTree.SubElement(parent, name, **kwargs)
        if text:
            el.text = text
        return el
コード例 #2
0
class EPG:
    # https://github.com/XMLTV/xmltv/blob/master/xmltv.dtd
    def __init__(self, _webserver):
        self.logger = logging.getLogger(__name__)
        self.webserver = _webserver
        self.config = _webserver.plugins.config_obj.data
        self.epg_db = DBepg(self.config)
        self.channels_db = DBChannels(self.config)
        self.plugins = _webserver.plugins
        self.namespace = _webserver.query_data['name']
        self.instance = _webserver.query_data['instance']

    def get_epg_xml(self, _webserver):
        try:
            _webserver.do_dict_response({
                'code': 200,
                'headers': {
                    'Content-type':
                    'application/xml; Transfer-Encoding: chunked'
                },
                'text': None
            })

            self.plugins.refresh_epg(self.namespace, self.instance)
            xml_out = self.gen_header_xml()
            channel_list = self.channels_db.get_channels(
                self.namespace, self.instance)
            self.gen_channel_xml(xml_out, channel_list)
            self.write_xml(xml_out, keep_xml_prolog=True)
            xml_out = None

            self.epg_db.init_get_query(self.namespace, self.instance)
            day_data, ns, inst = self.epg_db.get_next_row()
            self.prog_processed = []
            data_written = False
            while day_data:
                xml_out = self.gen_minimal_header_xml()
                self.gen_program_xml(xml_out, day_data, channel_list, ns, inst)
                did_write_data = self.write_xml(xml_out)
                data_written = did_write_data or data_written
                day_data, ns, inst = self.epg_db.get_next_row()
            self.epg_db.close_query()
            if data_written:
                self.webserver.wfile.write(b'</tv>\r\n')
            else:
                self.webserver.wfile.write(b'<tv/>\r\n')
            self.webserver.wfile.flush()
        except MemoryError as e:
            self.logger.error('MemoryError parsing large xml')
            raise e
        xml_out = None

    def write_xml(self, _xml, keep_xml_prolog=False):
        if self.config['epg']['epg_prettyprint']:
            if not keep_xml_prolog:
                epg_dom = ElementTree.tostring(_xml)
                if len(epg_dom) < 20:
                    return False
                epg_dom = minidom.parseString(epg_dom).toprettyxml()[27:-6]
            else:
                epg_dom = minidom.parseString(
                    ElementTree.tostring(_xml, encoding='UTF-8',
                                         method='xml')).toprettyxml()[:-6]
                if len(epg_dom) < 250:
                    return False
            self.webserver.wfile.write(epg_dom.encode())
        else:
            if not keep_xml_prolog:
                epg_dom = ElementTree.tostring(_xml)[4:-5]
                if len(epg_dom) < 10:
                    return False
            else:
                epg_dom = ElementTree.tostring(_xml)[:-5]
                if len(epg_dom) < 250:
                    return False
            self.webserver.wfile.write(epg_dom + b'\r\n')
        epg_dom = None
        return True

    def gen_channel_xml(self, _et_root, _channel_list):
        sids_processed = []
        for sid, sid_data_list in _channel_list.items():
            if sid in sids_processed:
                continue
            sids_processed.append(sid)
            for ch_data in sid_data_list:
                if not ch_data['enabled']:
                    break
                updated_chnum = utils.wrap_chnum(ch_data['display_number'],
                                                 ch_data['namespace'],
                                                 ch_data['instance'],
                                                 self.config)
                c_out = EPG.sub_el(_et_root, 'channel', id=sid)
                EPG.sub_el(c_out,
                           'display-name',
                           _text='%s %s' %
                           (updated_chnum, ch_data['display_name']))
                EPG.sub_el(c_out,
                           'display-name',
                           _text='%s %s' %
                           (updated_chnum, ch_data['json']['callsign']))
                EPG.sub_el(c_out, 'display-name', _text=updated_chnum)
                EPG.sub_el(c_out,
                           'display-name',
                           _text=ch_data['json']['callsign'])
                EPG.sub_el(c_out,
                           'display-name',
                           _text=ch_data['display_name'])

                if self.config['epg']['epg_channel_icon']:
                    EPG.sub_el(c_out, 'icon', src=ch_data['thumbnail'])
                break
        return _et_root

    def gen_program_xml(self, _et_root, _prog_list, _channel_list, _ns, _inst):
        for prog_data in _prog_list:
            proginfo = prog_data['start'] + prog_data['channel']
            if proginfo in self.prog_processed:
                continue
            skip = False
            try:
                for ch_data in _channel_list[prog_data['channel']]:
                    if ch_data['namespace'] == _ns \
                            and ch_data['instance'] == _inst \
                            and not ch_data['enabled']:
                        skip = True
                        break
            except KeyError:
                skip = True
            if skip:
                continue
            self.prog_processed.append(proginfo)

            prog_out = EPG.sub_el(_et_root,
                                  'programme',
                                  start=prog_data['start'],
                                  stop=prog_data['stop'],
                                  channel=prog_data['channel'])
            if prog_data['title']:
                EPG.sub_el(prog_out,
                           'title',
                           lang='en',
                           _text=prog_data['title'])
            if prog_data['subtitle']:
                EPG.sub_el(prog_out,
                           'sub-title',
                           lang='en',
                           _text=prog_data['subtitle'])
            descr_add = ''
            if self.config['epg']['description'] == 'extend':
                if prog_data['formatted_date']:
                    descr_add += '(' + prog_data['formatted_date'] + ') '
                if prog_data['genres']:
                    descr_add += ' / '.join(prog_data['genres']) + ' / '
                if prog_data['se_common']:
                    descr_add += prog_data['se_common']
                descr_add += '\n' + prog_data['desc']
            elif self.config['epg']['description'] == 'brief':
                descr_add = prog_data['short_desc']
            elif self.config['epg']['description'] == 'normal':
                descr_add = prog_data['desc']
            else:
                self.logger.warning(
                    'Config value [epg][description] is invalid: ' +
                    self.config['epg']['description'])
            EPG.sub_el(prog_out, 'desc', lang='en', _text=descr_add)

            if prog_data['video_quality']:
                video_out = EPG.sub_el(prog_out, 'video')
                EPG.sub_el(video_out, 'quality', prog_data['video_quality'])

            if prog_data['air_date']:
                EPG.sub_el(prog_out, 'date', _text=prog_data['air_date'])

            EPG.sub_el(prog_out,
                       'length',
                       units='minutes',
                       _text=str(prog_data['length']))

            if prog_data['genres']:
                for f in prog_data['genres']:
                    if self.config['epg']['genre'] == 'normal':
                        pass
                    elif self.config['epg']['genre'] == 'tvheadend':
                        if f in epg_category.TVHEADEND.keys():
                            f = epg_category.TVHEADEND[f]
                    else:
                        self.logger.warning(
                            'Config value [epg][genre] is invalid: ' +
                            self.config['epg']['genre'])
                    EPG.sub_el(prog_out,
                               'category',
                               lang='en',
                               _text=f.strip())

            if prog_data['icon'] and self.config['epg']['epg_program_icon']:
                EPG.sub_el(prog_out, 'icon', src=prog_data['icon'])

            if prog_data['actors'] or prog_data['directors']:
                r = ElementTree.SubElement(prog_out, 'credits')
                if prog_data['directors']:
                    for actor in prog_data['directors']:
                        EPG.sub_el(r, 'producer', _text=actor)
                if prog_data['actors']:
                    for actor in prog_data['actors']:
                        EPG.sub_el(r, 'presenter', _text=actor)

            if prog_data['rating']:
                r = ElementTree.SubElement(prog_out, 'rating')
                EPG.sub_el(r, 'value', _text=prog_data['rating'])

            if prog_data['se_common']:
                EPG.sub_el(prog_out,
                           'episode-num',
                           system='common',
                           _text=prog_data['se_common'])
                EPG.sub_el(prog_out,
                           'episode-num',
                           system='dd_progid',
                           _text=prog_data['se_progid'])
                EPG.sub_el(prog_out,
                           'episode-num',
                           system='xmltv_ns',
                           _text=prog_data['se_xmltv_ns'])
                EPG.sub_el(prog_out,
                           'episode-num',
                           system='SxxExx',
                           _text=prog_data['se_common'])

            if prog_data['is_new']:
                EPG.sub_el(prog_out, 'new')
            else:
                EPG.sub_el(prog_out, 'previously-shown')
            if prog_data['cc']:
                EPG.sub_el(prog_out, 'subtitles', type='teletext')
            if prog_data['premiere']:
                EPG.sub_el(prog_out, 'premiere')

    def gen_header_xml(self):
        if self.namespace is None:
            website = utils.CABERNET_URL
            name = utils.CABERNET_NAME
        else:
            website = self.plugins.plugins[
                self.namespace].plugin_settings['website']
            name = self.plugins.plugins[self.namespace].plugin_settings['name']

        xml_out = ElementTree.Element('tv')
        xml_out.set('source-info-url', website)
        xml_out.set('source-info-name', name)
        xml_out.set('generator-info-name', utils.CABERNET_NAME)
        xml_out.set('generator-info-url', utils.CABERNET_URL)
        xml_out.set('generator-special-thanks', 'locast2plex')
        return xml_out

    def gen_minimal_header_xml(self):
        return ElementTree.Element('tv')

    @staticmethod
    def sub_el(_parent, _name, _text=None, **kwargs):
        el = ElementTree.SubElement(_parent, _name, **kwargs)
        if _text:
            el.text = _text
        return el