def show_tvshow_menu(self, use_favorites=False): """The VRT NU add-on 'All programs' listing menu""" # My favorites menus may need more up-to-date favorites self._favorites.refresh(ttl=ttl('direct' if use_favorites else 'indirect')) self._resumepoints.refresh(ttl=ttl('direct' if use_favorites else 'indirect')) tvshow_items = self._apihelper.list_tvshows(use_favorites=use_favorites) show_listing(tvshow_items, category=30440, sort='label', content='tvshows') # A-Z
def show_offline_menu(self, page=0, use_favorites=False): """The VRT NU add-on 'Soon offline' and 'My soon offline' listing menu""" # My favorites menus may need more up-to-date favorites self._favorites.refresh( ttl=ttl('direct' if use_favorites else 'indirect')) self._resumepoints.refresh( ttl=ttl('direct' if use_favorites else 'indirect')) page = realpage(page) items_per_page = get_setting_int('itemsperpage', default=50) sort_key = 'assetOffTime' episode_items, sort, ascending, content = self._apihelper.list_episodes( page=page, items_per_page=items_per_page, use_favorites=use_favorites, variety='offline', sort_key=sort_key) # Add 'More...' entry at the end if len(episode_items) == items_per_page: offline = 'favorites_offline' if use_favorites else 'offline' episode_items.append( TitleItem( label=localize(30300), path=url_for(offline, page=page + 1), art_dict=dict(thumb='DefaultYear.png'), info_dict=dict(), )) show_listing(episode_items, category=30022, sort=sort, ascending=ascending, content=content, cache=False)
def show_episodes_menu(self, program, season=None): """The VRT NU add-on episodes listing menu""" self._favorites.refresh(ttl=ttl('indirect')) self._resumepoints.refresh(ttl=ttl('indirect')) episode_items, sort, ascending, content = self._apihelper.list_episodes(program=program, season=season) # FIXME: Translate program in Program Title show_listing(episode_items, category=program.title(), sort=sort, ascending=ascending, content=content, cache=False)
def show_recent_menu(self, page=0, use_favorites=False): """The VRT NU add-on 'Most recent' and 'My most recent' listing menu""" # My favorites menus may need more up-to-date favorites self._favorites.refresh( ttl=ttl('direct' if use_favorites else 'indirect')) self._resumepoints.refresh( ttl=ttl('direct' if use_favorites else 'indirect')) page = realpage(page) episode_items, sort, ascending, content = self._apihelper.list_episodes( page=page, use_favorites=use_favorites, variety='recent') # Add 'More...' entry at the end if len(episode_items) == get_setting_int('itemsperpage', default=50): recent = 'favorites_recent' if use_favorites else 'recent' episode_items.append( TitleItem( label=colour(localize(30300)), path=url_for(recent, page=page + 1), art_dict=dict(thumb='DefaultRecentlyAddedEpisodes.png'), info_dict=dict(), )) show_listing(episode_items, category=30020, sort=sort, ascending=ascending, content=content, cache=False)
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=ttl('indirect')) self._resumepoints.refresh(ttl=ttl('indirect')) cache_file = 'schedule.{date}.json'.format(date=date) if date in ('today', 'yesterday', 'tomorrow'): schedule = get_cached_url_json(url=epg_url, cache=cache_file, ttl=ttl('indirect'), fail={}) else: schedule = get_url_json(url=epg_url, fail={}) entry = find_entry(CHANNELS, 'name', channel) if entry: episodes = schedule.get(entry.get('id'), []) else: episodes = [] episode_items = [] for episode in episodes: program = url_to_program(episode.get('url', '')) if episode.get('url'): video_url = add_https_proto(episode.get('url')) path = url_for('play_url', video_url=video_url) context_menu, favorite_marker, watchlater_marker = self._metadata.get_context_menu( episode, program, cache_file) label = self._metadata.get_label( episode) + favorite_marker + watchlater_marker is_playable = True else: label = '[COLOR={greyedout}]%s[/COLOR]' % self._metadata.get_label( episode) path = url_for('noop') context_menu, _, _ = self._metadata.get_context_menu( episode, program, cache_file) is_playable = False info_labels = self._metadata.get_info_labels(episode, date=date, channel=entry) # FIXME: Due to a bug in Kodi, ListItem.Title is used when Sort methods are used, not ListItem.Label info_labels['title'] = colour(label) episode_items.append( TitleItem( label=colour(label), path=path, art_dict=self._metadata.get_art(episode), info_dict=info_labels, context_menu=context_menu, is_playable=is_playable, )) return episode_items
def show_continue_menu(self, page=0): """The VRT NU add-on 'Continue waching' listing menu""" # Continue watching menu may need more up-to-date favorites self._favorites.refresh(ttl=ttl('direct')) self._resumepoints.refresh(ttl=ttl('direct')) page = realpage(page) episode_items, sort, ascending, content = self._apihelper.list_episodes(page=page, variety='continue') show_listing(episode_items, category=30054, sort=sort, ascending=ascending, content=content, cache=False)
def show_featured_menu(self, feature=None): """The VRT NU add-on 'Featured content' listing menu""" if feature: self._favorites.refresh(ttl=ttl('indirect')) self._resumepoints.refresh(ttl=ttl('indirect')) tvshow_items = self._apihelper.list_tvshows(feature=feature) from data import FEATURED feature_msgctxt = find_entry(FEATURED, 'id', feature).get('msgctxt') show_listing(tvshow_items, category=feature_msgctxt, sort='label', content='tvshows', cache=False) else: featured_items = self._apihelper.list_featured() show_listing(featured_items, category=30024, sort='label', content='files')
def show_category_menu(self, category=None): """The VRT NU add-on 'Categories' listing menu""" if category: self._favorites.refresh(ttl=ttl('indirect')) self._resumepoints.refresh(ttl=ttl('indirect')) tvshow_items = self._apihelper.list_tvshows(category=category) from data import CATEGORIES category_msgctxt = find_entry(CATEGORIES, 'id', category).get('msgctxt') show_listing(tvshow_items, category=category_msgctxt, sort='label', content='tvshows') else: category_items = self._apihelper.list_categories() show_listing(category_items, category=30014, sort='unsorted', content='files') # Categories
def show_favorites_music_menu(self): """The VRT NU add-on 'My music' listing menu""" self._favorites.refresh(ttl=ttl('indirect')) self._resumepoints.refresh(ttl=ttl('indirect')) episode_items, sort, ascending, content = self._apihelper.list_episodes( category='muziek', season='allseasons', programtype='oneoff') show_listing(episode_items, category=30046, sort=sort, ascending=ascending, content=content, cache=False)
def show_channels_menu(self, channel=None): """The VRT NU add-on 'Channels' listing menu""" if channel: from tvguide import TVGuide self._favorites.refresh(ttl=ttl('indirect')) self._resumepoints.refresh(ttl=ttl('indirect')) channel_items = self._apihelper.list_channels(channels=[channel]) # Live TV channel_items.extend(TVGuide().get_channel_items(channel=channel)) # TV guide channel_items.extend(self._apihelper.list_youtube(channels=[channel])) # YouTube channel_items.extend(self._apihelper.list_tvshows(channel=channel)) # TV shows from data import CHANNELS channel_name = find_entry(CHANNELS, 'name', channel).get('label') show_listing(channel_items, category=channel_name, sort='unsorted', content='tvshows', cache=False) # Channel else: channel_items = self._apihelper.list_channels(live=False) show_listing(channel_items, category=30016, cache=False)
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 = {} if category: params['facets[categories]'] = category cache_file = 'category.{category}.json'.format(category=category) if channel: params['facets[programBrands]'] = channel cache_file = 'channel.{channel}.json'.format(channel=channel) if feature: params['facets[programTags.title]'] = feature cache_file = 'featured.{feature}.json'.format(feature=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' querystring = '&'.join('{}={}'.format(key, value) for key, value in list(params.items())) suggest_url = self._VRTNU_SUGGEST_URL + '?' + querystring return get_cached_url_json(url=suggest_url, cache=cache_file, ttl=ttl('indirect'), fail=[])
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) entry = find_entry(CHANNELS, 'name', channel) if not entry: return '' epg_url = epg.strftime(self.VRT_TVGUIDE) schedule = get_cached_url_json(url=epg_url, cache='schedule.today.json', ttl=ttl('indirect'), fail={}) 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 show_featured_menu(self, feature=None): """The VRT NU add-on 'Featured content' listing menu""" if feature: self._favorites.refresh(ttl=ttl('indirect')) self._resumepoints.refresh(ttl=ttl('indirect')) programs = None sort = 'label' content = 'tvshows' ascending = True if feature.startswith('jcr_'): media = self._apihelper.get_featured_media_from_web( feature.split('jcr_')[1]) if media.get('mediatype') == 'episodes': variety = 'featured.{name}'.format(name=media.get( 'name').strip().lower().replace(' ', '_')) media_items, sort, ascending, content = self._apihelper.list_episodes( whatson_id=media.get('medialist'), variety=variety) elif media.get('mediatype') == 'tvshows': feature = None media_items = self._apihelper.list_tvshows( feature=feature, programs=media.get('medialist')) else: media_items = self._apihelper.list_tvshows(feature=feature, programs=programs) from data import FEATURED feature_msgctxt = None feature = find_entry(FEATURED, 'id', feature) if feature: feature_msgctxt = feature.get('msgctxt') show_listing(media_items, category=feature_msgctxt, sort=sort, ascending=ascending, content=content, cache=False) else: featured_items = self._apihelper.list_featured() show_listing(featured_items, category=30024, sort='label', content='files')
def live_description(self, channel): """Return the EPG information for current and next live program""" 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) entry = find_entry(CHANNELS, 'name', channel) if not entry: return '' epg_url = epg.strftime(self.VRT_TVGUIDE) schedule = get_cached_url_json(url=epg_url, cache='schedule.today.json', ttl=ttl('indirect'), fail={}) episodes = iter(schedule.get(entry.get('id'), [])) description = '' episode = None 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 description = '[COLOR={highlighted}][B]%s[/B] %s[/COLOR]\n' % ( localize(30421), self.episode_description(episode)) try: description += '[B]%s[/B] %s' % (localize(30422), self.episode_description( next(episodes))) except StopIteration: break break if now < start_date: # Nothing playing now, but this may be next description = '[B]%s[/B] %s\n' % ( localize(30422), self.episode_description(episode)) try: description += '[B]%s[/B] %s' % (localize(30422), self.episode_description( next(episodes))) except StopIteration: break break if episode and not description: # Add a final 'No transmission' program description = '[COLOR={highlighted}][B]%s[/B] %s - 06:00\n» %s[/COLOR]' % ( localize(30421), episode.get('end'), localize(30423)) return colour(description)
def get_video_attributes(vrtnu_url): """Return a dictionary with video attributes by scraping the VRT NU website""" # Get cache cache_file = 'web_video_attrs_multi.json' video_attrs_multi = get_cache(cache_file, ttl=ttl('indirect')) if not video_attrs_multi: video_attrs_multi = {} if vrtnu_url in video_attrs_multi: return video_attrs_multi[vrtnu_url] # Scrape video attributes from bs4 import BeautifulSoup, SoupStrainer try: response = open_url(vrtnu_url, raise_errors='all') except HTTPError as exc: log_error('Web scraping video attributes failed: {error}', error=exc) return None if response is None: return None html_page = response.read() strainer = SoupStrainer( ['section', 'div'], {'class': ['video-detail__player', 'livestream__inner']}) soup = BeautifulSoup(html_page, 'html.parser', parse_only=strainer) item = None epg_channel = None if '#epgchannel=' in vrtnu_url: epg_channel = vrtnu_url.split('#epgchannel=')[1] for item in soup: if epg_channel and epg_channel == item.get('data-epgchannel'): break if not epg_channel and len(soup) > 1: return None try: video_attrs = item.find(name='nui-media').attrs except AttributeError as exc: log_error('Web scraping video attributes failed: {error}', error=exc) return None # Update cache if vrtnu_url in video_attrs_multi: # Update existing video_attrs_multi[vrtnu_url] = video_attrs else: # Create new video_attrs_multi.update({vrtnu_url: video_attrs}) from json import dumps update_cache(cache_file, dumps(video_attrs_multi)) return video_attrs
def search(self, keywords=None, page=0, edit=False): """The VRT NU add-on Search functionality and results""" if keywords is None or edit is True: keywords = get_search_string(keywords) if not keywords: end_of_directory() return if edit is True: container_update(url_for('search_query', keywords=keywords)) return from apihelper import ApiHelper from utils import realpage page = realpage(page) self.add(keywords) search_items, sort, ascending, content = ApiHelper(self._favorites, self._resumepoints).list_search(keywords, page=page) if not search_items: ok_dialog(heading=localize(30135), message=localize(30136, keywords=keywords)) end_of_directory() return # Add 'More…' entry at the end from helperobjects import TitleItem if len(search_items) == get_setting_int('itemsperpage', default=50): search_items.append(TitleItem( label=colour(localize(30300)), # More… path=url_for('search_query', keywords=keywords, page=page + 1), art_dict=dict(thumb='DefaultAddonSearch.png'), info_dict={}, )) self._favorites.refresh(ttl=ttl('indirect')) show_listing(search_items, category=30032, sort=sort, ascending=ascending, content=content, cache=False)
def get_episodes(self, program=None, season=None, episodes=None, category=None, feature=None, programtype=None, keywords=None, whatson_id=None, video_id=None, video_url=None, page=None, use_favorites=False, variety=None, cache_file=None): """Get episodes or season data from VRT NU Search API""" # Contruct params if page: page = realpage(page) all_items = False items_per_page = get_setting_int('itemsperpage', default=50) params = { 'from': ((page - 1) * items_per_page) + 1, 'i': 'video', 'size': items_per_page, } elif variety == 'single': all_items = False params = { 'i': 'video', 'size': '1', } else: all_items = True params = { 'i': 'video', 'size': '300', } if variety: season = 'allseasons' if variety == 'offline': from datetime import datetime, timedelta import dateutil.tz now = datetime.now(dateutil.tz.gettz('Europe/Brussels')) off_dates = [(now + timedelta(days=day)).strftime('%Y-%m-%d') for day in range(0, 7)] params['facets[assetOffTime]'] = '[%s]' % (','.join(off_dates)) if variety == 'oneoff': params[ 'facets[episodeNumber]'] = '[0,1]' # This to avoid VRT NU metadata errors (see #670) params['facets[programType]'] = 'oneoff' if variety == 'watchlater': self._resumepoints.refresh(ttl=ttl('direct')) episode_urls = self._resumepoints.watchlater_urls() params['facets[url]'] = '[%s]' % (','.join(episode_urls)) if variety == 'continue': self._resumepoints.refresh(ttl=ttl('direct')) episode_urls = self._resumepoints.resumepoints_urls() params['facets[url]'] = '[%s]' % (','.join(episode_urls)) if use_favorites: program_urls = [ program_to_url(p, 'medium') for p in self._favorites.programs() ] params['facets[programUrl]'] = '[%s]' % ( ','.join(program_urls)) elif variety in ('offline', 'recent'): channel_filter = [] for channel in CHANNELS: if channel.get('vod') is True and get_setting_bool( channel.get('name'), default=True): channel_filter.append(channel.get('name')) params['facets[programBrands]'] = '[%s]' % ( ','.join(channel_filter)) if program: params['facets[programUrl]'] = program_to_url(program, 'medium') if season and season != 'allseasons': params['facets[seasonTitle]'] = season if episodes: params['facets[episodeNumber]'] = '[%s]' % (','.join( str(episode) for episode in episodes)) if category: params['facets[categories]'] = category if feature: params['facets[programTags.title]'] = feature if programtype: params['facets[programType]'] = programtype if keywords: if not season: season = 'allseasons' params['q'] = quote_plus(from_unicode(keywords)) params['highlight'] = 'true' if whatson_id: params['facets[whatsonId]'] = whatson_id if video_id: params['facets[videoId]'] = video_id if video_url: params['facets[url]'] = video_url # Construct VRT NU Search API Url and get api data querystring = '&'.join('{}={}'.format(key, value) for key, value in list(params.items())) search_url = self._VRTNU_SEARCH_URL + '?' + querystring.replace( ' ', '%20') # Only encode spaces to minimize url length if cache_file: search_json = get_cached_url_json(url=search_url, cache=cache_file, ttl=ttl('indirect'), fail={}) else: search_json = get_url_json(url=search_url, fail={}) # Check for multiple seasons seasons = [] if 'facets[seasonTitle]' not in unquote(search_url): facets = search_json.get('facets', {}).get('facets') if facets: seasons = next((f.get('buckets', []) for f in facets if f.get('name') == 'seasons' and len(f.get('buckets', [])) > 1), None) # Experimental: VRT Search API only returns a maximum of 10 seasons, to get all seasons we need to use the "model.json" API if seasons and program and len(seasons) == 10: season_json = get_url_json( 'https://www.vrt.be/vrtnu/a-z/%s.model.json' % program) season_items = None try: season_items = season_json.get(':items').get('parsys').get(':items').get('container') \ .get(':items').get('banner').get(':items').get('navigation').get(':items') except AttributeError: pass if season_items: seasons = [] for item in season_items: seasons.append(dict(key=item.lstrip('0'))) episodes = search_json.get('results', [{}]) show_seasons = bool(season != 'allseasons') # Return seasons if show_seasons and seasons: return (seasons, episodes) api_pages = search_json.get('meta').get('pages').get('total') api_page_size = search_json.get('meta').get('pages').get('size') total_results = search_json.get('meta').get('total_results') if all_items and total_results > api_page_size: for api_page in range(1, api_pages): api_page_url = search_url + '&from=' + str(api_page * api_page_size + 1) api_page_json = get_url_json(api_page_url) if api_page_json is not None: episodes += api_page_json.get('results', [{}]) # Return episodes return episodes
def show_favorites_menu(self): """The VRT NU addon 'My programs' menu""" self._favorites.refresh(ttl=ttl('indirect')) favorites_items = [ TitleItem(label=localize(30040), # My programs path=url_for('favorites_programs'), art_dict=dict(thumb='DefaultMovieTitle.png'), info_dict=dict(plot=localize(30041))), TitleItem(label=localize(30048), # My recent items path=url_for('favorites_recent'), art_dict=dict(thumb='DefaultRecentlyAddedEpisodes.png'), info_dict=dict(plot=localize(30049))), TitleItem(label=localize(30050), # My soon offline path=url_for('favorites_offline'), art_dict=dict(thumb='DefaultYear.png'), info_dict=dict(plot=localize(30051))), ] # Only add 'My watch later' and 'Continue watching' when it has been activated if self._resumepoints.is_activated(): favorites_items.append(TitleItem( label=localize(30052), # My watch later path=url_for('resumepoints_watchlater'), art_dict=dict(thumb='DefaultVideoPlaylists.png'), info_dict=dict(plot=localize(30053)), )) favorites_items.append(TitleItem( label=localize(30054), # Continue Watching path=url_for('resumepoints_continue'), art_dict=dict(thumb='DefaultInProgressShows.png'), info_dict=dict(plot=localize(30055)), )) if get_setting_bool('addmymovies', default=True): favorites_items.append( TitleItem(label=localize(30042), # My movies path=url_for('categories', category='films'), art_dict=dict(thumb='DefaultAddonVideo.png'), info_dict=dict(plot=localize(30043))), ) if get_setting_bool('addmydocu', default=True): favorites_items.append( TitleItem(label=localize(30044), # My documentaries path=url_for('favorites_docu'), art_dict=dict(thumb='DefaultMovies.png'), info_dict=dict(plot=localize(30045))), ) if get_setting_bool('addmymusic', default=True): favorites_items.append( TitleItem(label=localize(30046), # My music path=url_for('favorites_music'), art_dict=dict(thumb='DefaultAddonMusic.png'), info_dict=dict(plot=localize(30047))), ) show_listing(favorites_items, category=30010, cache=False) # My favorites # Show dialog when no favorites were found if not self._favorites.titles(): ok_dialog(heading=localize(30415), message=localize(30416))
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=ttl('indirect')) self._resumepoints.refresh(ttl=ttl('indirect')) cache_file = 'schedule.{date}.json'.format(date=date) if date in ('today', 'yesterday', 'tomorrow'): schedule = get_cached_url_json(url=epg_url, cache=cache_file, ttl=ttl('indirect'), fail={}) else: schedule = get_url_json(url=epg_url, fail={}) entry = find_entry(CHANNELS, 'name', channel) if entry: episodes = schedule.get(entry.get('id'), []) else: episodes = [] episode_items = [] for episode in episodes: program = url_to_program(episode.get('url', '')) context_menu, favorite_marker, watchlater_marker = self._metadata.get_context_menu( episode, program, cache_file) label = self._metadata.get_label(episode) path = self.get_episode_path(episode, channel) # Playable item if '/play/' in path: is_playable = True label += favorite_marker + watchlater_marker # Non-actionable item else: is_playable = False label = '[COLOR={greyedout}]%s[/COLOR]' % label # Now playing start_date = dateutil.parser.parse(episode.get('startTime')) end_date = dateutil.parser.parse(episode.get('endTime')) if start_date <= now <= end_date: if is_playable: label = '[COLOR={highlighted}]%s[/COLOR] %s' % ( label, localize(30301)) else: label += localize(30301) info_labels = self._metadata.get_info_labels(episode, date=date, channel=entry) # FIXME: Due to a bug in Kodi, ListItem.Title is used when Sort methods are used, not ListItem.Label info_labels['title'] = colour(label) episode_items.append( TitleItem( label=colour(label), path=path, art_dict=self._metadata.get_art(episode), info_dict=info_labels, context_menu=context_menu, is_playable=is_playable, )) return episode_items