def onNotification(self, sender, method, data): # pylint: disable=invalid-name ''' Handler for notifications ''' log(2, '[Notification] sender={sender}, method={method}, data={data}', sender=sender, method=method, data=to_unicode(data)) if method.endswith('source_container'): from json import loads self._container = loads(data).get('container') return if not sender.startswith('upnextprovider'): return if not method.endswith('plugin.video.vrt.nu_play_action'): return from json import loads hexdata = loads(data) if not hexdata: return from binascii import unhexlify data = loads(unhexlify(hexdata[0])) log(2, '[Up Next notification] sender={sender}, method={method}, data={data}', sender=sender, method=method, data=to_unicode(data)) jsonrpc(method='Player.Open', params=dict(item=dict( file='plugin://plugin.video.vrt.nu/play/whatson/%s' % data.get('whatson_id'))))
def onPlayBackEnded(self): # pylint: disable=invalid-name """Called when Kodi has ended playing a file""" if not self.listen: return self.last_pos = self.total self.quit.set() log(3, '[PlayerInfo {id}] Event onPlayBackEnded', id=self.thread_id)
def refresh(self, ttl=None): ''' Get a cached copy or a newer resumepoints from VRT, or fall back to a cached file ''' if not self.is_activated(): return resumepoints_json = get_cache('resume_points.json', ttl) if not resumepoints_json: from tokenresolver import TokenResolver xvrttoken = TokenResolver().get_xvrttoken(token_variant='user') if xvrttoken: headers = { 'authorization': 'Bearer ' + xvrttoken, 'content-type': 'application/json', 'Referer': 'https://www.vrt.be/vrtnu', } req = Request('https://video-user-data.vrt.be/resume_points', headers=headers) log(2, 'URL post: https://video-user-data.vrt.be/resume_points') from json import load try: resumepoints_json = load(urlopen(req)) except (TypeError, ValueError): # No JSON object could be decoded # Force resumepoints from cache resumepoints_json = get_cache('resume_points.json', ttl=None) else: update_cache('resume_points.json', resumepoints_json) if resumepoints_json: self._resumepoints = resumepoints_json
def get_own_pictures(path): _, files = xbmcvfs.listdir(xbmc.translatePath(path)) images_dict = {} image_file = os.path.join(xbmc.translatePath(path), "images.json") if xbmcvfs.exists(image_file): f = xbmcvfs.File(image_file) try: images_dict = json.loads(f.read()) except ValueError: kodiutils.log(kodiutils.get_string(32010), xbmc.LOGERROR) f.close() for _file in files: if _file.endswith(('.png', '.jpg', '.jpeg')): returned_dict = { "url": os.path.join(xbmc.translatePath(path), _file), "private": True } if images_dict: for image in images_dict: if "image" in image.keys() and image["image"] == _file: if "line1" in image.keys(): returned_dict["line1"] = image["line1"] if "line2" in image.keys(): returned_dict["line2"] = image["line2"] yield returned_dict
def _get_new_xvrttoken(self, login_json, token_variant=None): """Get new X-VRT-Token from VRT NU website""" if token_variant == 'roaming': xvrttoken = self._get_roaming_xvrttoken() else: login_token = login_json.get('sessionInfo', {}).get('login_token') if not login_token: return None from json import dumps login_cookie = 'glt_{api_key}={token}'.format(api_key=self._API_KEY, token=login_token) payload = dict( uid=login_json.get('UID'), uidsig=login_json.get('UIDSignature'), ts=login_json.get('signatureTimestamp'), email=from_unicode(get_setting('username')), ) data = dumps(payload).encode() headers = {'Content-Type': 'application/json', 'Cookie': login_cookie} log(2, 'URL post: {url}', url=unquote(self._TOKEN_GATEWAY_URL)) req = Request(self._TOKEN_GATEWAY_URL, data=data, headers=headers) try: # Python 3 setcookie_header = urlopen(req).info().get('Set-Cookie') except AttributeError: # Python 2 setcookie_header = urlopen(req).info().getheader('Set-Cookie') xvrttoken = TokenResolver._create_token_dictionary(setcookie_header) if xvrttoken is None: return None self._set_cached_token(xvrttoken, token_variant) notification(message=localize(30952)) # Login succeeded. return xvrttoken.get('X-VRT-Token')
def update_watchlater(self, episode_id, title, watch_later=None): """Set program watchLater status and update local copy""" self.refresh_watchlater(ttl=5) # Update log(3, "[watchLater] Update {episode_id} watchLater status", episode_id=episode_id) # watchLater status is not changed, nothing to do if watch_later is not None and watch_later is self.is_watchlater( episode_id): return True # Update local watch_later cache if watch_later is True: self._watchlater[episode_id] = dict(title=title) else: del self._watchlater[episode_id] # Update cache from json import dumps update_cache(self.WATCHLATER_CACHE_FILE, dumps(self._watchlater)) invalidate_caches('watchlater-*.json') # Update online self.set_watchlater_graphql(episode_id, title, watch_later) return True
def onPlayBackResumed(self): # pylint: disable=invalid-name '''called when user resumes a paused file or a next playlist item is started ''' suffix = 'after pausing' if self._paused else 'after playlist change' log(2, '[PlayerInfo] %d onPlayBackResumed %s' % (self._id, suffix)) if not self._paused: self._info(dict(path=self._path, position=self._last_pos, total=self._total, event='playbackresumed')) self._paused = False
def playing_now(self, channel): ''' Return the EPG information for what is playing now ''' now = datetime.now(dateutil.tz.tzlocal()) epg = now # Daily EPG information shows information from 6AM until 6AM if epg.hour < 6: epg += timedelta(days=-1) # Try the cache if it is fresh schedule = get_cache('schedule.today.json', ttl=60 * 60) if not schedule: from json import load epg_url = epg.strftime(self.VRT_TVGUIDE) log(2, 'URL get: {url}', url=epg_url) schedule = load(urlopen(epg_url)) update_cache('schedule.today.json', schedule) entry = find_entry(CHANNELS, 'name', channel) if not entry: return '' episodes = iter(schedule.get(entry.get('id'), [])) while True: try: episode = next(episodes) except StopIteration: break start_date = dateutil.parser.parse(episode.get('startTime')) end_date = dateutil.parser.parse(episode.get('endTime')) if start_date <= now <= end_date: # Now playing return episode.get('title') return ''
def _get_cached_token(self, token_name, token_variant=None): """Return a cached token""" path = self._get_token_path(token_name, token_variant) if not exists(path): return None with open_file(path) as fdesc: token = get_json_data(fdesc) if token is None: return None from datetime import datetime import dateutil.parser import dateutil.tz now = datetime.now(dateutil.tz.tzlocal()) exp = dateutil.parser.parse(token.get('expirationDate')) if exp <= now: log(2, "Token expired, cached token '{path}' deleted", path=path) delete(path) return None log(3, "Got cached token '{path}'", path=path) return token.get(token_name)
def stream_position(self): ''' get latest stream position while playing ''' while self.isPlaying() and not self._stop.is_set(): self._last_pos = self.getTime() if self._stop.wait(timeout=0.5): break log(2, '[PlayerInfo] %d stream position loop exited' % self._id)
def get_tvshows(self, category=None, channel=None, feature=None): ''' Get all TV shows for a given category, channel or feature, optionally filtered by favorites ''' params = dict() if category: params['facets[categories]'] = category cache_file = 'category.%s.json' % category if channel: params['facets[programBrands]'] = channel cache_file = 'channel.%s.json' % channel if feature: params['facets[programTags.title]'] = feature cache_file = 'featured.%s.json' % feature # If no facet-selection is done, we return the 'All programs' listing if not category and not channel and not feature: params['facets[transcodingStatus]'] = 'AVAILABLE' # Required for getting results in Suggests API cache_file = 'programs.json' tvshows = get_cache(cache_file, ttl=60 * 60) # Try the cache if it is fresh if not tvshows: from json import load querystring = '&'.join('{}={}'.format(key, value) for key, value in list(params.items())) suggest_url = self._VRTNU_SUGGEST_URL + '?' + querystring log(2, 'URL get: {url}', url=unquote(suggest_url)) tvshows = load(urlopen(suggest_url)) update_cache(cache_file, tvshows) return tvshows
def _get_new_user_xvrttoken(self): """Get new 'user' X-VRT-Token from VRT NU website""" # Get login json login_json = self._get_login_json() if login_json.get('errorCode') != 0: return None payload = dict( UID=login_json.get('UID'), UIDSignature=login_json.get('UIDSignature'), signatureTimestamp=login_json.get('signatureTimestamp'), client_id='vrtnu-site', submit='submit', ) data = urlencode(payload).encode() cookiejar = cookielib.CookieJar() opener = build_opener(HTTPCookieProcessor(cookiejar), ProxyHandler(self._proxies)) log(2, 'URL get: {url}', url=unquote(self._USER_TOKEN_GATEWAY_URL)) opener.open(self._USER_TOKEN_GATEWAY_URL) log(2, 'URL post: {url}', url=unquote(self._VRT_LOGIN_URL)) opener.open(self._VRT_LOGIN_URL, data=data) xvrttoken = TokenResolver._create_token_dictionary(cookiejar) refreshtoken = TokenResolver._create_token_dictionary(cookiejar, cookie_name='vrtlogin-rt') if xvrttoken is None: return None self._set_cached_token(xvrttoken, token_variant='user') if refreshtoken is not None: self._set_cached_token(refreshtoken) return xvrttoken.get('X-VRT-Token')
def __init__(self): """PlayerInfo initialisation""" self.resumepoints = ResumePoints() self.apihelper = ApiHelper(Favorites(), self.resumepoints) self.last_pos = None self.listen = False self.paused = False self.total = 100 self.positionthread = None self.quit = Event() self.asset_str = None # FIXME On Kodi 17, use ListItem.Filenameandpath because Player.FilenameAndPath returns the stream manifest url and # this definitely breaks "Up Next" on Kodi 17, but this is not supported or available through the Kodi add-on repo anyway self.path_infolabel = 'ListItem.Filenameandpath' if kodi_version_major( ) < 18 else 'Player.FilenameAndPath' self.path = None self.title = None self.ep_id = None self.episode_id = None self.episode_title = None self.video_id = None from random import randint self.thread_id = randint(1, 10001) log(3, '[PlayerInfo {id}] Initialized', id=self.thread_id) super(PlayerInfo, self).__init__()
def onPlayBackPaused(self): # pylint: disable=invalid-name """Called when user pauses a playing file""" if not self.listen: return log(3, '[PlayerInfo {id}] Event onPlayBackPaused', id=self.thread_id) self.update_position() self.push_position(position=self.last_pos, total=self.total) self.paused = True
def onPlayerExit(self): # pylint: disable=invalid-name """Called when player exits""" log(3, '[PlayerInfo {id}] Event onPlayerExit', id=self.thread_id) self.positionthread = None self.push_position(position=self.last_pos, total=self.total) # Set property to let wait_for_resumepoints function know that update resume is done set_property('vrtnu_resumepoints', 'ready')
def _select_hls_substreams(self, master_hls_url, protocol): """Select HLS substreams to speed up Kodi player start, workaround for slower kodi selection""" hls_variant_url = None subtitle_url = None hls_audio_id = None hls_subtitle_id = None hls_base_url = master_hls_url.split('.m3u8')[0] log(2, 'URL get: {url}', url=unquote(master_hls_url)) try: hls_playlist = to_unicode(urlopen(master_hls_url).read()) except HTTPError as exc: if exc.code == 415: self._handle_bad_stream_error(protocol, exc.code, exc.reason) return None raise max_bandwidth = get_max_bandwidth() stream_bandwidth = None # Get hls variant url based on max_bandwidth setting import re hls_variant_regex = re.compile(r'#EXT-X-STREAM-INF:[\w\-.,=\"]*?BANDWIDTH=(?P<BANDWIDTH>\d+),' r'[\w\-.,=\"]+\d,(?:AUDIO=\"(?P<AUDIO>[\w\-]+)\",)?(?:SUBTITLES=\"' r'(?P<SUBTITLES>\w+)\",)?[\w\-.,=\"]+?[\r\n](?P<URI>[\w:\/\-.=?&]+)') # reverse sort by bandwidth for match in sorted(re.finditer(hls_variant_regex, hls_playlist), key=lambda m: int(m.group('BANDWIDTH')), reverse=True): stream_bandwidth = int(match.group('BANDWIDTH')) // 1000 if max_bandwidth == 0 or stream_bandwidth < max_bandwidth: if match.group('URI').startswith('http'): hls_variant_url = match.group('URI') else: hls_variant_url = hls_base_url + match.group('URI') hls_audio_id = match.group('AUDIO') hls_subtitle_id = match.group('SUBTITLES') break if stream_bandwidth > max_bandwidth and not hls_variant_url: message = localize(30057, max=max_bandwidth, min=stream_bandwidth) ok_dialog(message=message) open_settings() # Get audio url if hls_audio_id: audio_regex = re.compile(r'#EXT-X-MEDIA:TYPE=AUDIO[\w\-=,\.\"\/]+?GROUP-ID=\"' + hls_audio_id + '' r'\"[\w\-=,\.\"\/]+?URI=\"(?P<AUDIO_URI>[\w\-=]+)\.m3u8\"') match_audio = re.search(audio_regex, hls_playlist) if match_audio: hls_variant_url = hls_base_url + match_audio.group('AUDIO_URI') + '-' + hls_variant_url.split('-')[-1] # Get subtitle url, works only for on demand streams if get_setting_bool('showsubtitles', default=True) and '/live/' not in master_hls_url and hls_subtitle_id: subtitle_regex = re.compile(r'#EXT-X-MEDIA:TYPE=SUBTITLES[\w\-=,\.\"\/]+?GROUP-ID=\"' + hls_subtitle_id + '' r'\"[\w\-=,\.\"\/]+URI=\"(?P<SUBTITLE_URI>[\w\-=]+)\.m3u8\"') match_subtitle = re.search(subtitle_regex, hls_playlist) if match_subtitle: subtitle_url = hls_base_url + match_subtitle.group('SUBTITLE_URI') + '.webvtt' return StreamURLS(hls_variant_url, subtitle_url)
def _get_new_playertoken(self, token_url, headers, token_variant=None): ''' Get new playertoken from VRT Token API ''' from json import load log(2, 'URL post: {url}', url=unquote(token_url)) req = Request(token_url, data=b'', headers=headers) playertoken = load(urlopen(req)) if playertoken is not None: self._set_cached_token(playertoken, token_variant) return playertoken.get('vrtPlayerToken')
def delete_online(self, asset_id): """Delete resumepoint online""" try: result = open_url('https://video-user-data.vrt.be/resume_points/{asset_id}'.format(asset_id=asset_id), headers=self.resumepoint_headers(), method='DELETE', raise_errors='all') log(3, "[Resumepoints] '{asset_id}' online deleted: {code}", asset_id=asset_id, code=result.getcode()) except HTTPError as exc: log_error("Failed to remove '{asset_id}' from resumepoints: {error}", asset_id=asset_id, error=exc) return False return True
def refresh_login(self): """Refresh login if necessary""" if self._credentials_changed() and has_credentials(): log(2, 'Credentials have changed, cleaning up userdata') self.cleanup_userdata() # Refresh login log(2, 'Refresh login') self.login(refresh=True)
def get_upload_playlist(): api_endpoint = 'https://www.googleapis.com/youtube/v3/channels?part=contentDetails&id=%s&key=%s' % (CHANNEL_ID,YOUTUBE_API_KEY) try: resp = requests.get(api_endpoint).json() except ValueError: kodiutils.log(kodiutils.get_string(32004), xbmc.LOGERROR) return None if "items" in resp.keys(): uploads_playlist = resp["items"][0]["contentDetails"]["relatedPlaylists"]["uploads"] return uploads_playlist
def onPlayBackResumed(self): # pylint: disable=invalid-name """Called when user resumes a paused file or a next playlist item is started""" if not self.listen: return suffix = 'after pausing' if self.paused else 'after playlist change' log(3, '[PlayerInfo {id}] Event onPlayBackResumed {suffix}', id=self.thread_id, suffix=suffix) self.paused = False
def send(self): """Decorator to send over a socket""" import json import socket log(2, "Sending data output to IPTV Manager using port {port}", port=self.port) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(('127.0.0.1', self.port)) try: sock.sendall(json.dumps(func()).encode()) # pylint: disable=not-callable finally: sock.close()
def get_episode_items(self, date, channel): ''' Show episodes for a given date and channel ''' now = datetime.now(dateutil.tz.tzlocal()) epg = self.parse(date, now) epg_url = epg.strftime(self.VRT_TVGUIDE) self._favorites.refresh(ttl=60 * 60) cache_file = 'schedule.%s.json' % date if date in ('today', 'yesterday', 'tomorrow'): # Try the cache if it is fresh schedule = get_cache(cache_file, ttl=60 * 60) if not schedule: from json import load log(2, 'URL get: {url}', url=epg_url) schedule = load(urlopen(epg_url)) update_cache(cache_file, schedule) else: from json import load log(2, 'URL get: {url}', url=epg_url) schedule = load(urlopen(epg_url)) entry = find_entry(CHANNELS, 'name', channel) if entry: episodes = schedule.get(entry.get('id'), []) else: episodes = [] episode_items = [] for episode in episodes: label = self._metadata.get_label(episode) context_menu = [] path = None if episode.get('url'): from statichelper import add_https_method, url_to_program video_url = add_https_method(episode.get('url')) path = url_for('play_url', video_url=video_url) program = url_to_program(episode.get('url')) context_menu, favorite_marker, watchlater_marker = self._metadata.get_context_menu(episode, program, cache_file) label += favorite_marker + watchlater_marker info_labels = self._metadata.get_info_labels(episode, date=date, channel=entry) info_labels['title'] = label episode_items.append(TitleItem( title=label, path=path, art_dict=self._metadata.get_art(episode), info_dict=info_labels, is_playable=True, context_menu=context_menu, )) return episode_items
def onSettingsChanged(self): # pylint: disable=invalid-name """Handler for changes to settings""" log(1, 'Settings changed') TokenResolver().refresh_login() # Init watching activity again when settings change self.init_watching_activity() # Refresh container when settings change container_refresh()
def handle_info(self, info): ''' Handle information from PlayerInfo class ''' log(2, 'Got VRT NU Player info: {info}', info=str(info)) # Push resume position if info.get('position'): self.push_position(info) # Push up next episode info if info.get('program'): self.push_upnext(info)
def get_upload_playlist(): api_endpoint = 'https://www.googleapis.com/youtube/v3/channels?part=contentDetails&id=%s&key=%s' % ( CHANNEL_ID, YOUTUBE_API_KEY) try: resp = requests.get(api_endpoint).json() except ValueError: kodiutils.log(kodiutils.get_string(32004), xbmc.LOGERROR) return None if "items" in resp.keys(): uploads_playlist = resp["items"][0]["contentDetails"][ "relatedPlaylists"]["uploads"] return uploads_playlist
def __init__(self, **kwargs): ''' PlayerInfo initialisation ''' self._info = kwargs['info'] self._path = None self._paused = False self._last_pos = None self._total = None self._stop = Event() from random import randint self._id = randint(1, 10001) log(2, '[PlayerInfo] %d initialized' % self._id) Player.__init__(self)
def get_live_videos(): api_endpoint = 'https://www.googleapis.com/youtube/v3/search?eventType=live&part=snippet&channelId=%s&type=video&maxResults=50&key=%s' % ( CHANNEL_ID, YOUTUBE_API_KEY) try: resp = requests.get(api_endpoint).json() except ValueError: kodiutils.log(kodiutils.get_string(32009), xbmc.LOGERROR) sys.exit(0) if "items" in resp.keys(): for item in resp["items"]: yield { "label": item["snippet"]["title"], "video_id": item["id"]["videoId"] }
def get_latest_episode(self, program): ''' Get the latest episode of a program ''' api_data = self.get_episodes(program=program, variety='single') if len(api_data) != 1: return None episode = api_data[0] log(2, str(episode)) video_item = TitleItem( title=self._metadata.get_label(episode), art_dict=self._metadata.get_art(episode), info_dict=self._metadata.get_info_labels(episode), prop_dict=self._metadata.get_properties(episode), ) video = dict(listitem=video_item, video_id=episode.get('videoId'), publication_id=episode.get('publicationId')) return video
def _get_fresh_token(self, refresh_token, token_name, token_variant=None): """Refresh an expired X-VRT-Token, vrtlogin-at or vrtlogin-rt token""" refresh_url = self._TOKEN_GATEWAY_URL + '/refreshtoken' cookie_value = 'vrtlogin-rt=' + refresh_token headers = {'Cookie': cookie_value} cookiejar = cookielib.CookieJar() opener = build_opener(HTTPCookieProcessor(cookiejar), ProxyHandler(self._proxies)) log(2, 'URL get: {url}', url=refresh_url) req = Request(refresh_url, headers=headers) opener.open(req) token = TokenResolver._create_token_dictionary(cookiejar, token_name) if token is None: return None self._set_cached_token(token, token_variant) return list(token.values())[0]
def get_playlists(): api_endpoint = 'https://www.googleapis.com/youtube/v3/playlists?part=snippet,contentDetails&channelId=%s&maxResults=50&key=%s' % (CHANNEL_ID,YOUTUBE_API_KEY) try: resp = requests.get(api_endpoint).json() except ValueError: kodiutils.log(kodiutils.get_string(32003), xbmc.LOGERROR) if "items" in resp.keys(): for playlist in resp["items"]: liz = ListItem(playlist["snippet"]["title"]) infolabels = {"plot": playlist["snippet"]["localized"]["description"]} liz.setInfo(type="video", infoLabels=infolabels) liz.setArt({"thumb": playlist["snippet"]["thumbnails"]["high"]["url"], "fanart": xbmcaddon.Addon().getAddonInfo("fanart")}) liz.setProperty("type","playlist") liz.setProperty("playlist_id", playlist["id"]) yield liz
def onPlayBackSeek(self, time, seekOffset): # pylint: disable=invalid-name """Called when user seeks to a time""" if not self.listen: return log(3, '[PlayerInfo {id}] Event onPlayBackSeek time={time} offset={offset}', id=self.thread_id, time=time, offset=seekOffset) self.last_pos = time // 1000 # If we seek beyond the end, exit Player if self.last_pos >= self.total: self.quit.set() self.stop()
def get_videos(name,playlist_id,token="",page_num=1): items_per_page = kodiutils.get_setting_as_int("items_per_page") url_api = 'https://www.googleapis.com/youtube/v3/playlistItems?part=id,snippet,contentDetails&maxResults=%s&playlistId=%s&key=%s' \ % (str(items_per_page), playlist_id, YOUTUBE_API_KEY) if page_num != 1: url_api += "&pageToken=%s" % (token) try: resp = requests.get(url_api).json() except ValueError: kodiutils.log(kodiutils.get_string(32004), xbmc.LOGERROR) resp = None if resp: nextpagetoken = resp["nextPageToken"] if "nextPageToken" in resp.keys() else "" availablevideos = resp["pageInfo"]["totalResults"] if "pageInfo" in resp.keys() and "totalResults" in resp["pageInfo"].keys() else 1 returnedVideos = resp["items"] totalpages = int(math.ceil((float(availablevideos) / items_per_page))) video_ids = [] if returnedVideos: for video in returnedVideos: videoid = video["contentDetails"]["videoId"] video_ids.append(videoid) video_ids = ','.join(video_ids) url_api = 'https://www.googleapis.com/youtube/v3/videos?part=snippet,contentDetails&id=%s&key=%s' % (video_ids,YOUTUBE_API_KEY) try: resp = requests.get(url_api).json() except ValueError: kodiutils.log(kodiutils.get_string(32005), xbmc.LOGERROR) resp = None if resp: returnedVideos = resp["items"] for video in returnedVideos: title = video["snippet"]["title"] plot = video["snippet"]["description"] aired = video["snippet"]["publishedAt"] thumb = video["snippet"]["thumbnails"]["high"]["url"] videoid = video["id"] # process duration duration_string = video["contentDetails"]["duration"] duration = addonutils.return_duration_as_seconds(duration_string) try: aired = re.compile('(.+?)-(.+?)-(.+?)T').findall(aired)[0] date = aired[2] + '.' + aired[1] + '.' + aired[0] aired = aired[0] + '-' + aired[1] + '-' + aired[2] except IndexError: aired = '' date = '' infolabels = {'plot': plot.encode('utf-8'), 'aired': aired, 'date': date, 'tvshowtitle': TVSHOWTITLE, 'title': title.encode('utf-8'), 'originaltitle': title.encode('utf-8'), 'status': STATUS, 'cast': CAST, 'duration': duration} # Video and audio info video_info = {'codec': 'avc1', 'aspect': 1.78} audio_info = {'codec': 'aac', 'language': 'en'} if video["contentDetails"]["definition"].lower() == 'hd': video_info['width'] = 1280 video_info['height'] = 720 audio_info['channels'] = 2 else: video_info['width'] = 854 video_info['height'] = 480 audio_info['channels'] = 1 if xbmcaddon.Addon(id='plugin.video.youtube').getSetting('kodion.video.quality.ask') == 'false' and xbmcaddon.Addon( id='plugin.video.youtube').getSetting('kodion.video.quality') != '3' and xbmcaddon.Addon( id='plugin.video.youtube').getSetting('kodion.video.quality') != '4': video_info['width'] = 854 video_info['height'] = 480 audio_info['channels'] = 1 yield build_video_item(title.encode('utf-8'), thumb, videoid, infolabels, video_info, audio_info) if totalpages > 1 and (page_num + 1) <= totalpages: nextpage = ListItem("[B]%s[/B] (%s/%s)" % (kodiutils.get_string(32008), str(page_num), str(totalpages))) nextpage.setProperty("type", "next") nextpage.setProperty("page", str(page_num+1)) nextpage.setProperty("token", str(nextpagetoken)) nextpage.setInfo(type="video",infoLabels={"plot": kodiutils.get_string(32002)}) yield nextpage