def _process_source(self, source, method_name, file_path): remove_file(file_path) path = source.path.strip() source_type = source.source_type archive_type = source.archive_type if source_type == Source.TYPE_ADDON: addon_id = path addon, data = merge_info(addon_id, self.integrations, merging=True) if method_name not in data: raise Error('{} could not be found for {}'.format( method_name, addon_id)) template_tags = { '$ID': addon_id, '$FILE': file_path, '$IP': xbmc.getIPAddress(), } path = data[method_name] for tag in template_tags: path = path.replace(tag, template_tags[tag]) path = path.strip() if path.lower().startswith('plugin'): self._call_addon_method(path) return if path.lower().startswith('http'): source_type = Source.TYPE_URL else: source_type = Source.TYPE_FILE archive_extensions = { '.gz': Source.ARCHIVE_GZIP, '.xz': Source.ARCHIVE_XZ, } name, ext = os.path.splitext(path.lower()) archive_type = archive_extensions.get(ext, Source.ARCHIVE_NONE) if source_type == Source.TYPE_URL and path.lower().startswith('http'): log.debug('Downloading: {} > {}'.format(path, file_path)) Session().chunked_dl(path, file_path) elif not xbmcvfs.exists(path): raise Error(_(_.LOCAL_PATH_MISSING, path=path)) else: log.debug('Copying local file: {} > {}'.format(path, file_path)) xbmcvfs.copy(path, file_path) if archive_type == Source.ARCHIVE_GZIP: gzip_extract(file_path) elif archive_type == Source.ARCHIVE_XZ: xz_extract(file_path)
def race(slug, **kwargs): races = api.races() if slug not in races: raise Error(_.RACE_NOT_FOUND) race = races[slug] folder = plugin.Folder(race['title'], no_items_label=_.NO_STREAMS) for stream in race['streams']: if not stream['slug']: continue item = plugin.Item( label = stream['label'], path = plugin.url_for(play, slug=stream['slug']), playable = True, ) if stream['live']: item.label = _(_.LIVE_LABEL, title=stream['label']) item.context.append((_.PLAY_FROM_LIVE, "PlayMedia({})".format( plugin.url_for(play, slug=stream['slug'], play_type=PLAY_FROM_LIVE, _is_live=True) ))) item.context.append((_.PLAY_FROM_START, "PlayMedia({})".format( plugin.url_for(play, slug=stream['slug'], play_type=PLAY_FROM_START, _is_live=True) ))) item.path = plugin.url_for(play, slug=stream['slug'], play_type=settings.getEnum('live_play_type', PLAY_FROM_TYPES, PLAY_FROM_ASK), _is_live=True) folder.add_items([item]) return folder
def merge_info(addon_id, merging=False): addon = get_addon(addon_id, required=True, install=False) addon_path = xbmc.translatePath(addon.getAddonInfo('path')) merge_path = os.path.join(addon_path, MERGE_SETTING_FILE) data = {} if os.path.exists(merge_path): try: with codecs.open(merge_path, 'r', encoding='utf8') as f: data = json.load(f) data['type'] = TYPE_IPTV_MERGE except Exception as e: log.exception(e) log.debug('failed to parse merge file: {}'.format(merge_path)) return addon, {} elif addon.getSetting('iptv.enabled'): data = { 'type': TYPE_IPTV_MANAGER, 'playlist': addon.getSetting('iptv.channels_uri'), 'epg': addon.getSetting('iptv.epg_uri'), } elif addon_id.lower() in INTEGRATIONS: data = INTEGRATIONS[addon_id.lower()] data['type'] = TYPE_INTEGRATION elif merging: raise Error('No integration found for this source') min_version = data.get('min_version') max_version = data.get('max_version') current_version = LooseVersion(addon.getAddonInfo('version')) if min_version and current_version < LooseVersion(min_version): if merging: raise Error('Min version {} required'.format(min_version)) else: data = {} if max_version and current_version > LooseVersion(max_version): if merging: raise Error('Max version {} exceeded'.format(max_version)) else: data = {} return addon, data
def install_xbian(): password = gui.input(_.XBIAN_PASSWORD, default='raspberry') sudo_cmd = 'echo "{}" | sudo -S su -c "{{}}"'.format(password) try: install_debian(sudo_cmd, 'xbian') except Exception as e: log.exception(e) raise Error(_.XBIAN_ERROR)
def merge_info(addon_id, integrations=None, merging=False): addon = get_addon(addon_id, required=True, install=False) addon_path = xbmc.translatePath(addon.getAddonInfo('path')) merge_path = os.path.join(addon_path, MERGE_SETTING_FILE) if os.path.exists(merge_path): try: with codecs.open(merge_path, 'r', encoding='utf8') as f: data = json.load(f) except Exception as e: log.exception(e) log.debug('failed to parse merge file: {}'.format(merge_path)) return addon, {} else: if integrations is None: integrations = get_integrations() data = integrations.get(addon_id) or {} if merging: if not integrations: raise Error('Failed to download integrations') elif not data: raise Error('No integration found for this source') min_version = data.get('min_version') max_version = data.get('max_version') current_version = LooseVersion(addon.getAddonInfo('version')) if min_version and current_version < LooseVersion(min_version): if merging: raise Error('Min version {} required'.format(min_version)) else: data = {} if max_version and current_version > LooseVersion(max_version): if merging: raise Error('Max version {} exceeded'.format(max_version)) else: data = {} return addon, data
def select_path(self, creating=False): try: default = self.TYPES.index(self.source_type) except: default = -1 index = gui.select(_.SELECT_SOURCE_TYPE, [self.TYPE_LABELS[x] for x in self.TYPES], preselect=default) if index < 0: return False orig_source_type = self.source_type self.source_type = self.TYPES[index] if self.source_type == self.TYPE_ADDON: addons = self.get_addon_sources() if not addons: raise Error(_.NO_SOURCE_ADDONS) options = [] default = -1 addons.sort(key=lambda x: x[0].getAddonInfo('name').lower()) for idx, row in enumerate(addons): options.append( plugin.Item(label=row[0].getAddonInfo('name'), art={'thumb': row[0].getAddonInfo('icon')})) if orig_source_type == self.TYPE_ADDON and row[0].getAddonInfo( 'id') == self.path: default = idx index = gui.select(_.SELECT_SOURCE_ADDON, options, preselect=default, useDetails=True) if index < 0: return False addon, data = addons[index] self.path = addon.getAddonInfo('id') elif self.source_type == self.TYPE_URL: self.path = gui.input(_.ENTER_SOURCE_URL, default=self.path if orig_source_type == self.TYPE_URL else '').strip() elif self.source_type == self.TYPE_FILE: self.path = xbmcgui.Dialog().browseSingle( 1, _.SELECT_SOURCE_FILE, '', '', defaultt=self.path if orig_source_type == self.TYPE_FILE else '') elif self.source_type == self.TYPE_CUSTOM: self.path = gui.input('Custom Name', default=self.path if orig_source_type == self.TYPE_CUSTOM else '').strip() if not self.path: return False self.save() if self.source_type == self.TYPE_ADDON: if creating: if self.__class__ == Playlist and METHOD_EPG in data: epg = EPG(source_type=EPG.TYPE_ADDON, path=self.path) try: epg.save() except: pass elif self.__class__ == EPG and METHOD_PLAYLIST in data: playlist = Playlist(source_type=Playlist.TYPE_ADDON, path=self.path) try: playlist.save() except: pass for key in data.get('settings', {}): value = data['settings'][key].replace('$ID', self.path) log.debug('Set setting {}={} for addon {}'.format( key, value, self.path)) addon.setSetting(key, value) if 'configure' in data: path = data['configure'].replace('$ID', self.path) run_plugin(path, wait=True) return True
def save(self, *args, **kwargs): try: super(Source, self).save(*args, **kwargs) except peewee.IntegrityError as e: raise Error(_.SOURCE_EXISTS)
def _process_playlist(self, playlist, file_path): channel = None to_create = set() slugs = set() added_count = 0 Channel.delete().where(Channel.playlist == playlist).execute() if playlist.use_start_chno: chnos = {'tv': playlist.start_chno, 'radio': playlist.start_chno} if not self._is_troll: for troll in TROLLS: if troll.lower() in playlist.path.lower(): self._is_troll = True break valid_file = False default_attribs = {} with codecs.open(file_path, 'r', encoding='utf8', errors='replace') as infile: for line in infile: line = line.strip() if not line: continue if not self._is_troll: for troll in TROLLS: if troll.lower() in line.lower(): self._is_troll = True break if not valid_file and '#EXTM3U' not in line: raise Error( 'Invalid playlist - Does not start with #EXTM3U') if '#EXTM3U' in line: valid_file = True #if not playlist.ignore_playlist_epg: attribs = {} for key, value in re.findall('([\w-]+)="([^"]*)"', line): attribs[key] = value.strip() xml_urls = attribs.get('x-tvg-url', '').split(',') xml_urls.extend(attribs.get('url-tvg', '').split(',')) for url in xml_urls: url = url.strip() if url: self._playlist_epgs.append(url) if 'tvg-shift' in attribs: default_attribs['tvg-shift'] = attribs['tvg-shift'] if 'catchup-correction' in attribs: default_attribs['catchup-correction'] = attribs[ 'catchup-correction'] if line.startswith('#EXTINF'): channel = Channel.from_playlist(line) for key in default_attribs: if key not in channel.attribs: channel.attribs[key] = default_attribs[key] elif not channel: continue if line.startswith('#EXTGRP'): value = line.split(':', 1)[1].strip() if value: channel.groups.extend(value.split(';')) elif line.startswith('#KODIPROP') or line.startswith( '#EXTVLCOPT'): value = line.split(':', 1)[1].strip() if value and '=' in value: key, value = value.split('=', 1) channel.properties[key] = value elif line.startswith('#EXT-X-PLAYLIST-TYPE'): value = line.split(':', 1)[1].strip() if value and value.upper() == 'VOD': channel.is_live = False elif not line.startswith('#'): channel.url = line if not channel.url: channel = None continue channel.playlist = playlist if playlist.skip_playlist_groups: channel.groups = [] if playlist.group_name: channel.groups.extend(playlist.group_name.split(';')) if playlist.skip_playlist_chno: channel.chno = None if playlist.use_start_chno: if channel.radio: if channel.chno is None: channel.chno = chnos['radio'] chnos['radio'] = channel.chno + 1 else: if channel.chno is None: channel.chno = chnos['tv'] chnos['tv'] = channel.chno + 1 if self._is_troll: channel.url = TROLL_URL channel.name = TROLL_NAME channel.groups = [x for x in channel.groups if x.strip()] channel.visible = playlist.default_visible channel.slug = slug = '{}.{}'.format( playlist.id, hash_6(channel.epg_id or channel.url.lower().strip())) channel.order = added_count + 1 count = 1 while channel.slug in slugs: channel.slug = '{}.{}'.format(slug, count) count += 1 slugs.add(channel.slug) to_create.add(channel) if Channel.bulk_create_lazy(to_create): to_create.clear() channel = None added_count += 1 if not valid_file: raise Error('Invalid playlist - Does not start with #EXTM3U') Channel.bulk_create_lazy(to_create, force=True) to_create.clear() slugs.clear() return added_count
def _process_source(self, source, method_name, file_path): remove_file(file_path) path = source.path.strip() source_type = source.source_type archive_type = source.archive_type self._is_troll = False if source_type == Source.TYPE_ADDON: addon_id = path addon, data = merge_info(addon_id, merging=True) if method_name not in data: raise Error('{} could not be found for {}'.format( method_name, addon_id)) path = data[method_name] if data['type'] == TYPE_IPTV_MANAGER: iptv_manager.process_path(path, file_path) return template_tags = { '$ID': addon_id, '$FILE': file_path, '$IP': xbmc.getIPAddress(), } for tag in template_tags: path = path.replace(tag, template_tags[tag]) path = path.strip() if path.lower().startswith('plugin://'): self._call_addon_method(path) return if path.lower().startswith('http://') or path.lower().startswith( 'https://'): source_type = Source.TYPE_URL else: source_type = Source.TYPE_FILE if source_type == Source.TYPE_URL and ( path.lower().startswith('http://') or path.lower().startswith('https://')): if 'drive.google.com' in path.lower(): log.debug('Gdrive Downloading: {} > {}'.format( path, file_path)) path = gdrivedl(path, file_path) else: log.debug('Downloading: {} > {}'.format(path, file_path)) resp = Session().chunked_dl(path, file_path) for troll in TROLLS: if troll.lower() in resp.url.lower(): self._is_troll = True break elif not xbmcvfs.exists(path): raise Error(_(_.LOCAL_PATH_MISSING, path=path)) else: _safe_copy(path, file_path) if archive_type == Source.ARCHIVE_AUTO: archive_type = Source.auto_archive_type(path) if archive_type == Source.ARCHIVE_GZIP: gzip_extract(file_path) elif archive_type == Source.ARCHIVE_XZ: xz_extract(file_path)