def setup(cls): """ Setup IPTV Simple """ try: # Install IPTV Simple kodiutils.execute_builtin('InstallAddon', IPTV_SIMPLE_ID) addon = kodiutils.get_addon(IPTV_SIMPLE_ID) except Exception as exc: # pylint: disable=broad-except _LOGGER.warning('Could not setup IPTV Simple: %s', str(exc)) return False # Deactivate IPTV Simple to hide the "Needs to be restarted" messages cls._deactivate() # Configure IPTV Simple output_dir = kodiutils.addon_profile() playlist_path = os.path.join(output_dir, IPTV_SIMPLE_PLAYLIST) epg_path = os.path.join(output_dir, IPTV_SIMPLE_EPG) logo_path = '/' addon.setSetting('m3uPathType', '0') # Local path addon.setSetting('m3uPath', playlist_path) addon.setSetting('epgPathType', '0') # Local path addon.setSetting('epgPath', epg_path) addon.setSetting('logoPathType', '0') # Local path addon.setSetting('logoPath', logo_path) # Activate IPTV Simple cls._activate() return True
def write_playlist(channels): """ Write playlist data """ output_dir = kodiutils.addon_profile() # Make sure our output dir exists if not os.path.exists(output_dir): os.mkdir(output_dir) playlist_path = os.path.join(output_dir, IPTV_SIMPLE_PLAYLIST) with open(playlist_path + '.tmp', 'wb') as fdesc: fdesc.write('#EXTM3U\n'.encode('utf-8')) for channel in channels: if channel.get('radio'): header = '#EXTINF:0 tvg-id="{id}" tvg-logo="{logo}" tvg-name="{name}" radio="true",{name}\n' else: header = '#EXTINF:0 tvg-id="{id}" tvg-logo="{logo}" tvg-name="{name}",{name}\n' fdesc.write(header.format(id=channel.get('id'), logo=channel.get('logo'), name=channel.get('name')).encode('utf-8')) fdesc.write("{url}\n".format(url=channel.get('stream')).encode('utf-8')) fdesc.write("\n".encode('utf-8')) # Move new file to the right place os.rename(playlist_path + '.tmp', playlist_path)
def write_playlist(channels): """ Write playlist data """ output_dir = kodiutils.addon_profile() # Make sure our output dir exists if not os.path.exists(output_dir): os.mkdir(output_dir) playlist_path = os.path.join(output_dir, IPTV_SIMPLE_PLAYLIST) with open(playlist_path + '.tmp', 'wb') as fdesc: m3u8_data = '#EXTM3U\n' for channel in channels: m3u8_data += '#EXTINF:-1 tvg-name="{name}"'.format(**channel) if channel.get('id'): m3u8_data += ' tvg-id="{id}"'.format(**channel) if channel.get('logo'): m3u8_data += ' tvg-logo="{logo}"'.format(**channel) if channel.get('preset'): m3u8_data += ' tvg-chno="{preset}"'.format(**channel) if channel.get('group'): m3u8_data += ' group-title="{group}"'.format(**channel) if channel.get('radio'): m3u8_data += ' radio="true"' m3u8_data += ',{name}\n{stream}\n\n'.format(**channel) fdesc.write(m3u8_data.encode('utf-8')) # Move new file to the right place if os.path.isfile(playlist_path): os.remove(playlist_path) os.rename(playlist_path + '.tmp', playlist_path)
def setup(cls): """ Setup IPTV Simple """ try: # Install IPTV Simple kodiutils.execute_builtin('InstallAddon', IPTV_SIMPLE_ID) addon = kodiutils.get_addon(IPTV_SIMPLE_ID) except: raise Exception('Could not enable IPTV Simple.') # Deactivate IPTV Simple to hide the "Needs to be restarted" messages cls._deactivate() # Configure IPTV Simple output_dir = kodiutils.addon_profile() playlist_path = os.path.join(output_dir, IPTV_SIMPLE_PLAYLIST) epg_path = os.path.join(output_dir, IPTV_SIMPLE_EPG) addon.setSetting('m3uPathType', '0') # Local path addon.setSetting('m3uPath', playlist_path) addon.setSetting('epgPathType', '0') # Local path addon.setSetting('epgPath', epg_path) addon.setSetting('logoPathType', '0') # Local path addon.setSetting('logoPath', '/') # Activate IPTV Simple cls._activate()
def write_epg(cls, epg_list, channels): """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 addon in channels: for channel in addon.get('channels'): if isinstance(channel, dict) and channel.get('id'): fdesc.write( '<channel id="{id}">\n'.format(id=cls._xml_encode( channel.get('id'))).encode('utf-8')) fdesc.write( ' <display-name>{name}</display-name>\n'.format( name=cls._xml_encode(channel.get( 'name'))).encode('utf-8')) if channel.get('logo'): fdesc.write(' <icon src="{logo}"/>\n'.format( logo=cls._xml_encode(channel.get( 'logo'))).encode('utf-8')) fdesc.write('</channel>\n'.encode('utf-8')) for epg in epg_list: # RAW XMLTV data if not isinstance(epg, dict): fdesc.write(epg.encode('utf-8')) fdesc.write('\n'.encode('utf-8')) continue # Write program info for _, key in enumerate(epg): for item in epg[key]: program = cls._construct_epg_program_xml(item, key) 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)
def __get_subtitle_paths(): """ Get the external subtitles path """ temp_path = kodiutils.addon_profile() + 'temp/' files = None if kodiutils.exists(temp_path): _, files = kodiutils.listdir(temp_path) if files and len(files) >= 1: return [temp_path + kodiutils.to_unicode(filename) for filename in files] _LOGGER.debug('Player: No subtitle path') return None
def write_channels(channels): """Write the channel data to a file.""" output_dir = kodiutils.addon_profile() # Make sure our output dir exists if not os.path.exists(output_dir): os.mkdir(output_dir) channels_path = os.path.join(output_dir, CHANNELS_CACHE) with open(channels_path, 'w') as fdesc: json.dump(channels, fdesc)
def _get_addons_for_channel(channel): """Returns a list of Add-ons that can play this channel.""" channels_path = os.path.join(kodiutils.addon_profile(), CHANNELS_CACHE) with open(channels_path, 'r') as fdesc: data = json.load(fdesc) matches = {} for _addon in data: for _channel in _addon.get('channels', []): if _channel.get('name').lower() == channel.lower() and _channel.get('vod') is not None: matches.update({_addon.get('addon_name'): _channel.get('vod')}) return matches
def write_playlist(channels): """Write playlist data""" output_dir = kodiutils.addon_profile() # Make sure our output dir exists if not os.path.exists(output_dir): os.mkdir(output_dir) # Write playlist for IPTV Simple playlist_path = os.path.join(output_dir, IPTV_SIMPLE_PLAYLIST) with open(playlist_path + '.tmp', 'wb') as fdesc: m3u8_data = '#EXTM3U\n' for addon in channels: m3u8_data += '## {addon_name}\n'.format(**addon) # RAW M3U8 data if not isinstance(addon['channels'], list): m3u8_data += addon['channels'] continue # JSON-STREAMS format for channel in addon['channels']: m3u8_data += '#EXTINF:-1 tvg-name="{name}"'.format( **channel) if channel.get('id'): m3u8_data += ' tvg-id="{id}"'.format(**channel) if channel.get('logo'): m3u8_data += ' tvg-logo="{logo}"'.format(**channel) if channel.get('preset'): m3u8_data += ' tvg-chno="{preset}"'.format(**channel) if channel.get('group'): m3u8_data += ' group-title="{groups}"'.format( groups=';'.join(channel.get('group'))) if channel.get('radio'): m3u8_data += ' radio="true"' m3u8_data += ' catchup="vod",{name}\n'.format(**channel) if channel.get('kodiprops'): for key, value in channel.get('kodiprops').items(): m3u8_data += '#KODIPROP:{key}={value}\n'.format( key=key, value=value) m3u8_data += '{stream}\n\n'.format(**channel) fdesc.write(m3u8_data.encode('utf-8')) # Move new file to the right place if os.path.isfile(playlist_path): os.remove(playlist_path) os.rename(playlist_path + '.tmp', playlist_path)
def write_epg(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. # TODO: proper escaping of XML with open(epg_path + '.tmp', 'wb') as fdesc: fdesc.write('<?xml version="1.0" encoding="ISO-8859-1"?>\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=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') fdesc.write('<programme start="{start}" stop="{stop}" channel="{channel}">\n'.format(start=start, stop=stop, channel=key).encode('utf8')) fdesc.write(' <title>{title}</title>'.format(title=item.get('title')).encode('utf8')) if item.get('description'): fdesc.write(' <desc>{description}</desc>\n'.format(description=item.get('description')).encode('utf8')) if item.get('subtitle'): fdesc.write(' <sub-title>{subtitle}</sub-title>\n'.format(subtitle=item.get('subtitle')).encode('utf-8')) if item.get('episode'): fdesc.write(' <episode-num system="onscreen">{episode}</episode-num>\n'.format(episode=item.get('episode')).encode('utf-8')) if item.get('image'): fdesc.write(' <icon src="{image}"/>\n'.format(image=item.get('image')).encode('utf-8')) if item.get('date'): fdesc.write(' <date>{date}</date>\n'.format(date=item.get('date')).encode('utf-8')) fdesc.write('</programme>\n'.encode('utf-8')) fdesc.write('</tv>\n'.encode('utf-8')) # Move new file to the right place os.rename(epg_path + '.tmp', epg_path)
def _delay_subtitles(self, subtitles, json_manifest): """ Modify the subtitles timings to account for ad breaks. :type subtitles: list[string] :type json_manifest: dict :rtype list[str] """ # Clean up old subtitles temp_dir = os.path.join(kodiutils.addon_profile(), 'temp', '') _, files = kodiutils.listdir(temp_dir) if files: for item in files: if kodiutils.to_unicode(item).endswith('.vtt'): kodiutils.delete(temp_dir + kodiutils.to_unicode(item)) # Return if there are no subtitles available if not subtitles: return None import re if not kodiutils.exists(temp_dir): kodiutils.mkdirs(temp_dir) ad_breaks = list() delayed_subtitles = list() webvtt_timing_regex = re.compile( r'\n(\d{2}:\d{2}:\d{2}\.\d{3}) --> (\d{2}:\d{2}:\d{2}\.\d{3})\s') # Get advertising breaks info from json manifest cues = json_manifest.get('interstitials').get('cues') for cue in cues: ad_breaks.append( dict(start=cue.get('start'), duration=cue.get('break_duration'))) for subtitle in subtitles: output_file = temp_dir + subtitle.get('name') + '.' + subtitle.get( 'url').split('.')[-1] webvtt_content = util.http_get(subtitle.get('url')).text webvtt_content = webvtt_timing_regex.sub( lambda match: self._delay_webvtt_timing(match, ad_breaks), webvtt_content) with kodiutils.open_file(output_file, 'w') as webvtt_output: webvtt_output.write(kodiutils.from_unicode(webvtt_content)) delayed_subtitles.append(output_file) return delayed_subtitles
def setup(cls): """Setup IPTV Simple""" try: # Install IPTV Simple kodiutils.execute_builtin('InstallAddon', IPTV_SIMPLE_ID) # Activate IPTV Simple so we can get the addon to be able to configure it cls._activate() addon = kodiutils.get_addon(IPTV_SIMPLE_ID) except Exception as exc: # pylint: disable=broad-except _LOGGER.warning('Could not setup IPTV Simple: %s', str(exc)) return False # Deactivate IPTV Simple to hide the "Needs to be restarted" messages cls._deactivate() # Configure IPTV Simple output_dir = kodiutils.addon_profile() addon.setSetting('m3uPathType', '0') # Local path addon.setSetting('m3uPath', os.path.join(output_dir, IPTV_SIMPLE_PLAYLIST)) addon.setSetting('epgPathType', '0') # Local path addon.setSetting('epgPath', os.path.join(output_dir, IPTV_SIMPLE_EPG)) addon.setSetting('epgCache', 'true') addon.setSetting('epgTimeShift', '0') addon.setSetting('logoPathType', '0') # Local path addon.setSetting('logoPath', '/') addon.setSetting('catchupEnabled', 'true') addon.setSetting('allChannelsCatchupMode', '1') addon.setSetting('catchupOnlyOnFinishedProgrammes', 'false') # Activate IPTV Simple cls._activate() return True
def _download_subtitles(subtitles): # Clean up old subtitles temp_dir = os.path.join(kodiutils.addon_profile(), 'temp', '') _, files = kodiutils.listdir(temp_dir) if files: for item in files: kodiutils.delete(temp_dir + kodiutils.to_unicode(item)) # Return if there are no subtitles available if not subtitles: return None if not kodiutils.exists(temp_dir): kodiutils.mkdirs(temp_dir) downloaded_subtitles = list() for subtitle in subtitles: output_file = temp_dir + subtitle.get('name') webvtt_content = util.http_get(subtitle.get('url')).text with kodiutils.open_file(output_file, 'w') as webvtt_output: webvtt_output.write(kodiutils.from_unicode(webvtt_content)) downloaded_subtitles.append(output_file) return downloaded_subtitles
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]: program = cls._construct_epg_program_xml(item, key) 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)
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)