def update_channels(_config, _namespace, _query_data): db = DBChannels(_config) ch_data = db.get_channels(_namespace, None) results = 'Status Results<ul>' for key, values in _query_data.items(): key_pair = key.split('-', 2) uid = key_pair[0] instance = key_pair[1] name = key_pair[2] value = values[0] if name == 'enabled': value = int(value) db_value = None for ch_db in ch_data[uid]: if ch_db['instance'] == instance: db_value = ch_db[name] break if value != db_value: if value is None: lookup_name = translate_main2json(name) if lookup_name is not None: value = ch_db['json'][lookup_name] results += ''.join([ '<li>Updated [', uid, '][', instance, '][', name, '] to ', str(value), '</li>' ]) ch_db[name] = value if name == 'thumbnail': thumbnail_size = get_thumbnail_size(value) ch_db['thumbnail_size'] = thumbnail_size db.update_channel(ch_db) results += '</ul><hr>' return results
def reset_channels(_config, _name): db_channel = DBChannels(_config) db_channel.del_status(_name) if _name is None: return 'Channels updated and will refresh all data on next request' else: return 'Channels for plugin {} updated and will refresh all data on next request' \ .format(_name)
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 __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 __init__(self, _locast_instance): self.locast_instance = _locast_instance self.locast = _locast_instance.locast self.instance = _locast_instance.instance self.db = DBChannels(self.locast_instance.config_obj.data) self.config_section = self.locast_instance.config_section if self.locast_instance.location.has_dma_changed: self.db.del_channels( self.locast.name, self.instance) self.db.del_status( self.locast.name, self.instance)
def get_channels_xml(_config, _base_url, namespace, instance): db = DBChannels(_config) ch_data = db.get_channels(namespace, instance) return_xml = '' for sid, sid_data in ch_data.items(): return_xml = return_xml + \ tvh_templates['xmlLineup'].format( sid_data['number'], escape(sid_data['display_name']), _base_url + '/' + sid_data['namespace'] + '/watch/' + sid, sid_data['json']['HD']) return "<Lineup>" + return_xml + "</Lineup>"
def get_channels_json(_config, _base_url, namespace, instance): db = DBChannels(_config) ch_data = db.get_channels(namespace, instance) return_json = '' for sid, sid_data in ch_data.items(): return_json = return_json + \ tvh_templates['jsonLineup'].format( sid_data['number'], sid_data['display_name'], _base_url + '/' + sid_data['namespace'] + '/watch/' + sid, sid_data['json']['HD']) return_json = return_json + ',' return "[" + return_json[:-1] + "]"
def get_channels_m3u(_config, _base_url, _namespace, _instance): format_descriptor = '#EXTM3U' record_marker = '#EXTINF' db = DBChannels(_config) ch_data = db.get_channels(_namespace, _instance) fakefile = StringIO() fakefile.write('%s\n' % format_descriptor) sids_processed = [] for sid, sid_data_list in ch_data.items(): for sid_data in sid_data_list: if sid in sids_processed: continue sids_processed.append(sid) if not sid_data['enabled']: continue # NOTE tvheadend supports '|' separated names in two attributes # either 'group-title' or 'tvh-tags' # if a ';' is used in group-title, tvheadend will use the # entire string as a tag groups = sid_data['namespace'] if sid_data['json']['groups_other']: groups += '|' + '|'.join(sid_data['json']['groups_other']) if sid_data['json']['HD']: if sid_data['json']['group_hdtv']: groups += '|' + sid_data['json']['group_hdtv'] elif sid_data['json']['group_sdtv']: groups += '|' + sid_data['json']['group_sdtv'] updated_chnum = utils.wrap_chnum(str(sid_data['display_number']), sid_data['namespace'], sid_data['instance'], _config) service_name = set_service_name(_config, sid_data) fakefile.write( '%s\n' % (record_marker + ':-1' + ' ' + 'channelID=\'' + sid + '\' ' + 'tvg-num=\'' + updated_chnum + '\' ' + 'tvg-chno=\'' + updated_chnum + '\' ' + 'tvg-name=\'' + sid_data['display_name'] + '\' ' + 'tvg-id=\'' + sid + '\' ' + (('tvg-logo=\'' + sid_data['thumbnail'] + '\' ') if sid_data['thumbnail'] else '') + 'group-title=\'' + groups + '\',' + service_name)) fakefile.write( '%s\n' % (('%s%s/%s/watch/%s' % ('http://', _base_url, sid_data['namespace'], str(sid))))) return fakefile.getvalue()
def init_class_var(cls, _plugins, _hdhr_queue): WebHTTPHandler.logger = logging.getLogger(__name__) WebHTTPHandler.config = _plugins.config_obj.data if platform.system() in ['Windows']: unpickle_it = Pickling(WebHTTPHandler.config) _plugins = unpickle_it.from_pickle(_plugins.__class__.__name__) PluginHandler.cls_plugins = _plugins.plugins WebHTTPHandler.plugins = _plugins WebHTTPHandler.hdhr_queue = _hdhr_queue if not cls.plugins.config_obj.defn_json: cls.plugins.config_obj.defn_json = ConfigDefn( _config=_plugins.config_obj.data) plugins_db = DBPlugins(_plugins.config_obj.data) WebHTTPHandler.namespace_list = plugins_db.get_instances() WebHTTPHandler.channels_db = DBChannels(_plugins.config_obj.data) tmp_rmg_scans = {} for plugin_name in _plugins.plugins.keys(): if 'player-tuner_count' in _plugins.config_obj.data[ plugin_name.lower()]: tmp_rmg_scans[plugin_name] = [] for x in range( int(_plugins.config_obj.data[plugin_name.lower()] ['player-tuner_count'])): tmp_rmg_scans[plugin_name].append('Idle') WebHTTPHandler.rmg_station_scans = tmp_rmg_scans if WebHTTPHandler.total_instances == 0: WebHTTPHandler.total_instances = _plugins.config_obj.data['web'][ 'concurrent_listeners']
def select_reset_channel(self): db_channel = DBChannels(self.config) plugins_channel = db_channel.get_channel_names() html_option = ''.join([ '<td nowrap>Plugin: <select id="name" name="name"</select>', '<option value="">ALL</option>' ]) for name in plugins_channel: html_option = ''.join([ html_option, '<option value="', name['namespace'], '">', name['namespace'], '</option>', ]) return ''.join([html_option, '</select></td></tr>'])
def get_channels_xml(_config, _base_url, _namespace, _instance): db = DBChannels(_config) ch_data = db.get_channels(_namespace, _instance) return_xml = '' sids_processed = [] for sid, sid_data_list in ch_data.items(): for sid_data in sid_data_list: if sid in sids_processed: continue sids_processed.append(sid) if not sid_data['enabled']: continue updated_chnum = utils.wrap_chnum(str(sid_data['display_number']), sid_data['namespace'], sid_data['instance'], _config) return_xml = return_xml + \ ch_templates['xmlLineup'].format( updated_chnum, escape(sid_data['display_name']), _base_url + '/' + sid_data['namespace'] + '/watch/' + sid, sid_data['json']['HD']) return "<Lineup>" + return_xml + "</Lineup>"
def init_class_var(_plugins, _hdhr_queue): TunerHttpHandler.logger = logging.getLogger(__name__) TunerHttpHandler.plugins = _plugins TunerHttpHandler.config = _plugins.config_obj.data TunerHttpHandler.hdhr_queue = _hdhr_queue if not _plugins.config_obj.defn_json: _plugins.config_obj.defn_json = ConfigDefn( _config=_plugins.config_obj.data) plugins_db = DBPlugins(_plugins.config_obj.data) TunerHttpHandler.namespace_list = plugins_db.get_instances() TunerHttpHandler.channels_db = DBChannels(_plugins.config_obj.data) tmp_rmg_scans = [] for x in range(int(_plugins.config_obj.data['main']['tuner_count'])): tmp_rmg_scans.append('Idle') TunerHttpHandler.rmg_station_scans = tmp_rmg_scans
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
class Channels: logger = None def __init__(self, _locast_instance): self.locast_instance = _locast_instance self.locast = _locast_instance.locast self.instance = _locast_instance.instance self.db = DBChannels(self.locast_instance.config_obj.data) self.config_section = self.locast_instance.config_section if self.locast_instance.location.has_dma_changed: self.db.del_channels( self.locast.name, self.instance) self.db.del_status( self.locast.name, self.instance) def refresh_channels(self, force=False): last_update = self.db.get_status(self.locast.name, self.instance) update_needed = False if not last_update: update_needed = True else: delta = datetime.datetime.now() - last_update if delta.days >= self.locast_instance.config_obj.data[self.locast.name.lower()]['channel_update_timeout']: update_needed = True if update_needed or force: ch_dict = self.get_locast_channels() self.db.save_channel_list(self.locast.name, self.instance, ch_dict) else: self.logger.debug('Channel data still new for {} {}, not refreshing'.format(self.locast.name, self.instance)) @handle_json_except @handle_url_except def get_locast_channels(self): channels_url = 'https://api.locastnet.org/api/watch/epg/{}' \ .format(self.locast_instance.location.dma) url_headers = { 'Content-Type': 'application/json', 'authorization': 'Bearer {}'.format(self.locast_instance.token), 'User-agent': constants.DEFAULT_USER_AGENT} req = urllib.request.Request(channels_url, headers=url_headers) with urllib.request.urlopen(req) as resp: ch_json = json.load(resp) ch_list = [] if len(ch_json) == 0: self.logger.warning('Locast HTTP Channel Request Failed for instance {}'.format(self.instance)) raise exceptions.CabernetException('Locast HTTP Channel Request Failed') self.logger.info("{}: Found {} stations for DMA {} on instance {}" .format(self.locast.name, len(ch_json), str(self.locast_instance.location.dma), self.instance)) for locast_channel in ch_json: hd = 0 ch_id = str(locast_channel['id']) ch_callsign = locast_channel['name'] thumbnail = None thumbnail_size = None if 'logoUrl' in locast_channel.keys(): thumbnail = locast_channel['logoUrl'] elif 'logo226Url' in locast_channel.keys(): thumbnail = locast_channel['logo226Url'] if thumbnail is not None: thumbnail_size = channels.get_thumbnail_size(thumbnail) try: if 'videoProperties' in locast_channel['listings'][0]: if 'HD' in locast_channel['listings'][0]['videoProperties']: hd = 1 else: hd = 0 except IndexError: pass try: assert (float(locast_channel['callSign'].split()[0])) channel = locast_channel['callSign'].split()[0] friendly_name = locast_channel['callSign'].split()[1] channel = { 'id': ch_id, 'enabled': True, 'callsign': ch_callsign, 'number': channel, 'name': friendly_name, 'HD': hd, 'group_hdtv': self.locast_instance.config_obj.data[self.config_section]['m3u-group_hdtv'], 'group_sdtv': self.locast_instance.config_obj.data[self.config_section]['m3u-group_sdtv'], 'groups_other': None, # array list of groups/categories 'thumbnail': thumbnail, 'thumbnail_size': thumbnail_size } ch_list.append(channel) except ValueError: self.logger.warning( '################### CALLSIGN ERROR Channel ignored: {} {}' .format(ch_id, locast_channel['callSign'])) return ch_list @handle_json_except @handle_url_except def get_channel_uri(self, _channel_id): self.logger.info(self.locast.name + ": Getting station info for " + _channel_id) stream_url = ''.join([ 'https://api.locastnet.org/api/watch/station/', str(_channel_id), '/', self.locast_instance.location.latitude, '/', self.locast_instance.location.longitude]) stream_headers = {'Content-Type': 'application/json', 'authorization': 'Bearer ' + self.locast_instance.token, 'User-agent': constants.DEFAULT_USER_AGENT} req = urllib.request.Request(stream_url, headers=stream_headers) with urllib.request.urlopen(req) as resp: stream_result = json.load(resp) self.logger.debug("Determining best video stream for " + _channel_id + "...") bestStream = None # find the heighest stream url resolution and save it to the list videoUrlM3u = m3u8.load(stream_result['streamUrl'], headers={'authorization': 'Bearer ' + self.locast_instance.token, 'User-agent': constants.DEFAULT_USER_AGENT}) self.logger.debug("Found " + str(len(videoUrlM3u.playlists)) + " Playlists") if len(videoUrlM3u.playlists) > 0: for videoStream in videoUrlM3u.playlists: if bestStream is None: bestStream = videoStream elif ((videoStream.stream_info.resolution[0] > bestStream.stream_info.resolution[0]) and (videoStream.stream_info.resolution[1] > bestStream.stream_info.resolution[1])): bestStream = videoStream elif ((videoStream.stream_info.resolution[0] == bestStream.stream_info.resolution[0]) and (videoStream.stream_info.resolution[1] == bestStream.stream_info.resolution[1]) and (videoStream.stream_info.bandwidth > bestStream.stream_info.bandwidth)): bestStream = videoStream if bestStream is not None: self.logger.debug(_channel_id + " will use " + str(bestStream.stream_info.resolution[0]) + "x" + str(bestStream.stream_info.resolution[1]) + " resolution at " + str(bestStream.stream_info.bandwidth) + "bps") return bestStream.absolute_uri else: self.logger.debug("No variant streams found for this station. Assuming single stream only.") return stream_result['streamUrl']
def __init__(self, _locast): self.locast = _locast self.db = DBChannels(self.locast.config)
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