def __init__(self): logging.StreamHandler.__init__(self) formatter = logging.Formatter("[{}] [%(name)s] %(message)s".format(ADDON.getAddonInfo("id"))) self.setFormatter(formatter) # xbmc.LOGNOTICE is deprecated in Kodi 19 Matrix if kodiutils.kodi_version_major() > 18: self.info_level = xbmc.LOGINFO else: self.info_level = xbmc.LOGNOTICE
def clean(path=None): """ Cleanup the library integration. """ _LOGGER.debug('Cleaning %s', path) if path: # We can use this to instantly remove something from the library when we've removed it from 'My List'. # This only works from Kodi 19 however. See https://github.com/xbmc/xbmc/pull/18562 if kodiutils.kodi_version_major() > 18: kodiutils.jsonrpc(method='VideoLibrary.Clean', params=dict( directory=path, showdialogs=False, )) else: kodiutils.jsonrpc(method='VideoLibrary.Clean', params=dict(showdialogs=False, )) else: kodiutils.jsonrpc(method='VideoLibrary.Clean')
def _construct_epg_program_xml(cls, item, channel): """ Generate the XML for the EPG of a program. """ try: start = dateutil.parser.parse(item.get('start')).strftime('%Y%m%d%H%M%S %z').rstrip() stop = dateutil.parser.parse(item.get('stop')).strftime('%Y%m%d%H%M%S %z').rstrip() title = item.get('title', '') # Add an icon ourselves in Kodi 18 if kodiutils.kodi_version_major() < 19 and item.get('stream'): # We use a clever way to hide the direct URI in the label so Kodi 18 can access the it title = '%s [COLOR green]•[/COLOR][COLOR vod="%s"][/COLOR]' % ( title, item.get('stream') ) program = '<programme start="{start}" stop="{stop}" channel="{channel}"{vod}>\n'.format( start=start, stop=stop, channel=cls._xml_encode(channel), vod=' catchup-id="%s"' % cls._xml_encode(item.get('stream')) if item.get('stream') else '') program += ' <title>{title}</title>\n'.format( title=cls._xml_encode(title)) if item.get('subtitle'): program += ' <sub-title>{subtitle}</sub-title>\n'.format( subtitle=cls._xml_encode(item.get('subtitle'))) if item.get('description'): program += ' <desc>{description}</desc>\n'.format( description=cls._xml_encode(item.get('description'))) if item.get('credits'): program += ' <credits>\n' for credit in item.get('credits'): # IPTV Simple only supports `actor`, `director` and `writer`, so we need to narrow the options down. # actor -> actor (with optional role) # director -> director # writer -> writer # adapter -> writer # producer -> director # composer -> writer # editor -> writer # presenter -> actor # commentator -> actor # guest -> actor if credit.get('type') == 'actor': if credit.get('role'): program += ' <actor role="{role}">{name}</actor>\n'.format(role=cls._xml_encode(credit.get('role')), name=cls._xml_encode(credit.get('name'))) else: program += ' <actor>{name}</actor>\n'.format(name=cls._xml_encode(credit.get('name'))) elif credit.get('type') == 'director': program += ' <director>{name}</director>\n'.format(name=cls._xml_encode(credit.get('name'))) elif credit.get('type') == 'writer': program += ' <writer>{name}</writer>\n'.format(name=cls._xml_encode(credit.get('name'))) elif credit.get('type') == 'adapter': program += ' <writer>{name}</writer>\n'.format(name=cls._xml_encode(credit.get('name'))) elif credit.get('type') == 'producer': program += ' <director>{name}</director>\n'.format(name=cls._xml_encode(credit.get('name'))) elif credit.get('type') == 'composer': program += ' <writer>{name}</writer>\n'.format(name=cls._xml_encode(credit.get('name'))) elif credit.get('type') == 'editor': program += ' <writer>{name}</writer>\n'.format(name=cls._xml_encode(credit.get('name'))) elif credit.get('type') == 'presenter': program += ' <actor>{name}</actor>\n'.format(name=cls._xml_encode(credit.get('name'))) elif credit.get('type') == 'commentator': program += ' <actor>{name}</actor>\n'.format(name=cls._xml_encode(credit.get('name'))) elif credit.get('type') == 'guest': program += ' <actor>{name}</actor>\n'.format(name=cls._xml_encode(credit.get('name'))) program += ' </credits>\n' if item.get('date'): program += ' <date>{date}</date>\n'.format( date=cls._xml_encode(item.get('date'))) if item.get('genre'): if isinstance(item.get('genre'), list): for genre in item.get('genre'): program += ' <category>{genre}</category>\n'.format( genre=cls._xml_encode(genre)) else: program += ' <category>{genre}</category>\n'.format( genre=cls._xml_encode(item.get('genre'))) if item.get('image'): program += ' <icon src="{image}"/>\n'.format( image=cls._xml_encode(item.get('image'))) if item.get('episode'): program += ' <episode-num system="onscreen">{episode}</episode-num>\n'.format( episode=cls._xml_encode(item.get('episode'))) program += '</programme>\n' return program except Exception as exc: # pylint: disable=broad-except # When we encounter an error, log an error, but don't error out for the other programs _LOGGER.error('Could not parse item: %s', item) _LOGGER.exception(exc) return ''
def write_epg(cls, epg): """Write EPG data""" output_dir = kodiutils.addon_profile() # Make sure our output dir exists if not os.path.exists(output_dir): os.mkdir(output_dir) epg_path = os.path.join(output_dir, IPTV_SIMPLE_EPG) # Write XML file by hand # The reason for this is that it takes less memory to write the file line by line then to construct an # XML object in memory and writing that in one go. # We can't depend on lxml.etree.xmlfile, since that's not available as a Kodi module with open(epg_path + '.tmp', 'wb') as fdesc: fdesc.write( '<?xml version="1.0" encoding="UTF-8"?>\n'.encode('utf-8')) fdesc.write('<!DOCTYPE tv SYSTEM "xmltv.dtd">\n'.encode('utf-8')) fdesc.write('<tv>\n'.encode('utf-8')) # Write channel info for _, key in enumerate(epg): fdesc.write('<channel id="{key}"></channel>\n'.format( key=cls._xml_encode(key)).encode('utf-8')) # Write program info for _, key in enumerate(epg): for item in epg[key]: start = dateutil.parser.parse( item.get('start')).strftime('%Y%m%d%H%M%S %z') stop = dateutil.parser.parse( item.get('stop')).strftime('%Y%m%d%H%M%S %z') title = item.get('title', '') # Add an icon ourselves in Kodi 18 if kodiutils.kodi_version_major() < 19 and item.get( 'stream'): # We use a clever way to hide the direct URI in the label so Kodi 18 can access the it title = '%s [COLOR green]•[/COLOR][COLOR vod="%s"][/COLOR]' % ( title, item.get('stream')) program = '<programme start="{start}" stop="{stop}" channel="{channel}"{vod}>\n'.format( start=start, stop=stop, channel=cls._xml_encode(key), vod=' catchup-id="%s"' % cls._xml_encode(item.get('stream')) if item.get('stream') else '') program += ' <title>{title}</title>\n'.format( title=cls._xml_encode(title)) if item.get('description'): program += ' <desc>{description}</desc>\n'.format( description=cls._xml_encode(item.get( 'description'))) if item.get('subtitle'): program += ' <sub-title>{subtitle}</sub-title>\n'.format( subtitle=cls._xml_encode(item.get('subtitle'))) if item.get('episode'): program += ' <episode-num system="onscreen">{episode}</episode-num>\n'.format( episode=cls._xml_encode(item.get('episode'))) if item.get('image'): program += ' <icon src="{image}"/>\n'.format( image=cls._xml_encode(item.get('image'))) if item.get('date'): program += ' <date>{date}</date>\n'.format( date=cls._xml_encode(item.get('date'))) if item.get('genre'): if isinstance(item.get('genre'), list): for genre in item.get('genre'): program += ' <category>{genre}</category>\n'.format( genre=cls._xml_encode(genre)) else: program += ' <category>{genre}</category>\n'.format( genre=cls._xml_encode(item.get('genre'))) program += '</programme>\n' fdesc.write(program.encode('utf-8')) fdesc.write('</tv>\n'.encode('utf-8')) # Move new file to the right place if os.path.isfile(epg_path): os.remove(epg_path) os.rename(epg_path + '.tmp', epg_path)