def add_favorite(user_id, username): """Add a user to favorites""" # Add favorite to dictionary favs = Favorites(wf()) favs.add(user_id, username) # Reload user load_user(user_id, username)
def make_add_or_remove_favorite_item(user_id, username): """Create an alfred item to add or remove a user from favorites""" # Add item depending on if this user is a favorite favs = Favorites(wf()) favUsers = favs.get_favorites() special_unicode_value = wf().alfred_items.special_unicode_value.next() command = None if favUsers is not None: if user_id in favUsers: wf().add_item( title=u'Remove from favorites', autocomplete=unichr(special_unicode_value), icon=ICON_TRASH) command = REMOVE_FAV else: wf().add_item(title=u'Add to favorites', autocomplete=unichr(special_unicode_value)) command = ADD_FAV # Else user is not a favorite because the 'favorites' file doesnt exist else: wf().add_item(title=u'Create a favorite', autocomplete=unichr(special_unicode_value)) command = ADD_FAV # Update special info data = {special_unicode_value: (command, {'user_id': user_id, 'username': username})} wf().alfred_items.special_info.send(data)
def main_page(self): favorites = Favorites() theme_manager = ThemeManager() return template( "project", CSS = theme_manager.apply(self.dao.project["theme"]), favorites_options = favorites.make_options(), theme_options = theme_manager.make_options(), is_favorite = favorites.is_favorite(), project_name = self.dao.project["name"], project = json.dumps(self.dao.project), card_background = theme_manager.theme["card_background"] )
def browse(self, path): favorites = Favorites() theme_manager = ThemeManager() for root, folders, files in os.walk(path): folders.sort() return template("browse", CSS=theme_manager.apply("default"), favorites_options=favorites.make_options(), theme_options=theme_manager.make_options(), drives=self.get_drives(), parent=os.path.abspath( os.path.join(path, os.pardir)), folders=folders, path=root, isAProject=("LUCID.json" in files))
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 unfollow(program, title): ''' The API interface to unfollow a program used by the context menu ''' move_down = bool(plugin.args.get('move_down')) from favorites import Favorites Favorites().unfollow(program=program, title=to_unicode(unquote_plus(from_unicode(title))), move_down=move_down)
def __init__(self, **kwargs): super(JournalInterfaceManager, self).__init__(**kwargs) self.windows = {} # initially load the journal window as main window journal_menu = Journal() self.add_window("journal_menu", journal_menu) self.load_window("journal_menu") # add remaining windows to tracked windows enter_tasks = AddEntry() self.add_window("enter_tasks", enter_tasks) update_entry = UpdateEntry() self.add_window("update_entry", update_entry) review_mistakes = ReviewMistakes() self.add_window("review_mistakes", review_mistakes) settings = Settings() self.add_window("settings", settings) collections = Collection() self.add_window("collections", collections) favourites = Favorites() self.add_window("favorites",favourites)
def unfollow(program_name, title, program_id=None): """The API interface to unfollow a program used by the context menu""" move_down = bool(plugin.args.get('move_down')) from favorites import Favorites Favorites().unfollow(program_name=program_name, title=to_unicode(unquote_plus(from_unicode(title))), program_id=program_id, move_down=move_down)
class TestResumePoints(unittest.TestCase): _favorites = Favorites() _resumepoints = ResumePoints() _apihelper = ApiHelper(_favorites, _resumepoints) @unittest.skipUnless(addon.settings.get('username'), 'Skipping as VRT username is missing.') @unittest.skipUnless(addon.settings.get('password'), 'Skipping as VRT password is missing.') def test_get_watchlater_episodes(self): ''' Test items, sort and order ''' episode_items, sort, ascending, content = self._apihelper.list_episodes( page=1, variety='watchlater') self.assertTrue(episode_items) self.assertEqual(sort, 'dateadded') self.assertFalse(ascending) self.assertEqual(content, 'episodes') @unittest.skipUnless(addon.settings.get('username'), 'Skipping as VRT username is missing.') @unittest.skipUnless(addon.settings.get('password'), 'Skipping as VRT password is missing.') def test_get_continue_episodes(self): ''' Test items, sort and order ''' episode_items, sort, ascending, content = self._apihelper.list_episodes( page=1, variety='continue') self.assertTrue(episode_items) self.assertEqual(sort, 'dateadded') self.assertFalse(ascending) self.assertEqual(content, 'episodes') @unittest.skipUnless(addon.settings.get('username'), 'Skipping as VRT username is missing.') @unittest.skipUnless(addon.settings.get('password'), 'Skipping as VRT password is missing.') def test_update_watchlist(self): self._resumepoints.refresh(ttl=0) assetuuid, first_entry = next( iter(self._resumepoints._resumepoints.items())) # pylint: disable=protected-access print('%s = %s' % (assetuuid, first_entry)) url = first_entry.get('value').get('url') self._resumepoints.watchlater(uuid=assetuuid, title='Foo bar', url=url) self._resumepoints.unwatchlater(uuid=assetuuid, title='Foo bar', url=url) self._resumepoints.refresh(ttl=0) assetuuid, first_entry = next( iter(self._resumepoints._resumepoints.items())) # pylint: disable=protected-access print('%s = %s' % (assetuuid, first_entry)) def test_assetpath_to_uuid(self): self.assertEqual(None, self._resumepoints.assetpath_to_uuid(None)) assetpath = '/content/dam/vrt/2019/08/14/woodstock-depot_WP00157456' uuid = 'contentdamvrt20190814woodstockdepotwp00157456' self.assertEqual(uuid, self._resumepoints.assetpath_to_uuid(assetpath))
def __init__(self, view_manager_class, model_class): self.fav = Favorites(open(Setting.fav_filename)) self.playlist = Playlist(open(Setting.playlist_filename)) self.view = view_manager_class(controller=self) self.thumb_view = self.view.get_thumb_view() self.picture_view = self.view.get_picture_view() self.video_view = self.view.get_video_view() self.model = model_class(self) self.history = History() self.loader = Loader() self.thumb_loader = self.loader.get_new_thread( self.on_thumb_load, self.thumb_view.progress_stop) self.picture_loader = self.loader.get_new_thread( lambda x: self.picture_view.refresh()) self.thumb_view.set_cycle_handler(self.cycle_handler)
def init_watching_activity(self): ''' Only load components for watching activity when needed ''' if self._resumepoints.is_activated(): if not self._playerinfo: self._playerinfo = PlayerInfo(info=self.handle_info) if not self._favorites: self._favorites = Favorites() if not self._apihelper: self._apihelper = ApiHelper(self._favorites, self._resumepoints)
class TestSearch(unittest.TestCase): _favorites = Favorites() _resumepoints = ResumePoints() _apihelper = ApiHelper(_favorites, _resumepoints) def test_search_journaal(self): ''' Test for journaal ''' search_items, sort, ascending, content = self._apihelper.list_search( 'journaal', page=1) # Test we get a non-empty search result self.assertEqual(len(search_items), 50) self.assertEqual(sort, 'dateadded') self.assertFalse(ascending) self.assertEqual(content, 'episodes') def test_search_journaal_page2(self): ''' Test for journaal ''' search_items, sort, ascending, content = self._apihelper.list_search( 'journaal', page=2) # Test we get a non-empty search result self.assertEqual(len(search_items), 50) self.assertEqual(sort, 'dateadded') self.assertFalse(ascending) self.assertEqual(content, 'episodes') def test_search_weer(self): ''' Test for journaal ''' search_items, sort, ascending, content = self._apihelper.list_search( 'weer', page=1) # Test we get a non-empty search result self.assertEqual(len(search_items), 50) self.assertEqual(sort, 'dateadded') self.assertFalse(ascending) self.assertEqual(content, 'episodes') def test_search_unicode(self): ''' Test for unicode ''' search_items, sort, ascending, content = self._apihelper.list_search( 'René', page=1) # Test we get a non-empty search result self.assertGreater(len(search_items), 0) self.assertEqual(sort, 'dateadded') self.assertFalse(ascending) self.assertEqual(content, 'episodes') @staticmethod def test_search_empty(): ''' Test for empty search ''' Search().search(keywords='')
def init_watching_activity(self): """Only load components for watching activity when needed""" if self._resumepoints.is_activated(): if not self._playerinfo: self._playerinfo = PlayerInfo() if not self._favorites: self._favorites = Favorites() if not self._apihelper: self._apihelper = ApiHelper(self._favorites, self._resumepoints) else: self._playerinfo = None
def load_favorites(): """Display favorite users""" max_age_fav = HOUR favs = Favorites(wf()) fav_users = favs.get_favorites() if fav_users is not None: fav_ids = sorted(fav_users, key=fav_users.get) for fav_id in fav_ids: fav_username = fav_users[fav_id] fav = Grammie(wf(), fav_id, fav_username, command=LOAD_USER, age_info=max_age_fav) # Update the cached value of a favorite in case the username changes favs.update_info(fav_id, fav.username) fav.display_info() # Save favorites to cache favs.cache_favs() else: # No stored favorites, prompt user to add some wf().add_item('Add users as favorites to see them here')
def __init__(self): """Initialise object""" self._favorites = Favorites() self._resumepoints = ResumePoints() self._apihelper = ApiHelper(self._favorites, self._resumepoints) wait_for_resumepoints()
class TVGuide: ''' This implements a VRT TV-guide that offers Kodi menus and TV guide info ''' VRT_TVGUIDE = 'https://www.vrt.be/bin/epg/schedule.%Y-%m-%d.json' def __init__(self): ''' Initializes TV-guide object ''' self._favorites = Favorites() self._resumepoints = ResumePoints() self._metadata = Metadata(self._favorites, self._resumepoints) install_opener(build_opener(ProxyHandler(get_proxies()))) def show_tvguide(self, date=None, channel=None): ''' Offer a menu depending on the information provided ''' if not date and not channel: date_items = self.get_date_items() show_listing(date_items, category=30026, content='files') # TV guide elif not channel: channel_items = self.get_channel_items(date=date) entry = find_entry(RELATIVE_DATES, 'id', date) date_name = localize(entry.get('msgctxt')) if entry else date show_listing(channel_items, category=date_name) elif not date: date_items = self.get_date_items(channel=channel) channel_name = find_entry(CHANNELS, 'name', channel).get('label') show_listing(date_items, category=channel_name, content='files', selected=7) else: episode_items = self.get_episode_items(date, channel) channel_name = find_entry(CHANNELS, 'name', channel).get('label') entry = find_entry(RELATIVE_DATES, 'id', date) date_name = localize(entry.get('msgctxt')) if entry else date show_listing(episode_items, category='%s / %s' % (channel_name, date_name), content='episodes', cache=False) @staticmethod def get_date_items(channel=None): ''' Offer a menu to select the TV-guide date ''' epg = datetime.now(dateutil.tz.tzlocal()) # Daily EPG information shows information from 6AM until 6AM if epg.hour < 6: epg += timedelta(days=-1) date_items = [] for offset in range(7, -30, -1): day = epg + timedelta(days=offset) title = localize_datelong(day) date = day.strftime('%Y-%m-%d') # Highlight today with context of 2 days entry = find_entry(RELATIVE_DATES, 'offset', offset) if entry: date_name = localize(entry.get('msgctxt')) if entry.get('permalink'): date = entry.get('id') if offset == 0: title = '[COLOR yellow][B]{name}[/B], {date}[/COLOR]'.format(name=date_name, date=title) else: title = '[B]{name}[/B], {date}'.format(name=date_name, date=title) # Show channel list or channel episodes if channel: path = url_for('tvguide', date=date, channel=channel) else: path = url_for('tvguide', date=date) cache_file = 'schedule.%s.json' % date date_items.append(TitleItem( title=title, path=path, art_dict=dict(thumb='DefaultYear.png'), info_dict=dict(plot=localize_datelong(day)), context_menu=[(localize(30413), 'RunPlugin(%s)' % url_for('delete_cache', cache_file=cache_file))], )) return date_items def get_channel_items(self, date=None, channel=None): ''' Offer a menu to select the channel ''' if date: now = datetime.now(dateutil.tz.tzlocal()) epg = self.parse(date, now) datelong = localize_datelong(epg) channel_items = [] for chan in CHANNELS: # Only some channels are supported if not chan.get('has_tvguide'): continue # If a channel is requested, stop processing if it is no match if channel and channel != chan.get('name'): continue art_dict = {} # Try to use the white icons for thumbnails (used for icons as well) if has_addon('resource.images.studios.white'): art_dict['thumb'] = 'resource://resource.images.studios.white/{studio}.png'.format(**chan) else: art_dict['thumb'] = 'DefaultTags.png' if date: title = chan.get('label') path = url_for('tvguide', date=date, channel=chan.get('name')) plot = '%s\n%s' % (localize(30302, **chan), datelong) else: title = '[B]%s[/B]' % localize(30303, **chan) path = url_for('tvguide_channel', channel=chan.get('name')) plot = '%s\n\n%s' % (localize(30302, **chan), self.live_description(chan.get('name'))) channel_items.append(TitleItem( title=title, path=path, art_dict=art_dict, info_dict=dict(plot=plot, studio=chan.get('studio')), )) return channel_items 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 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 '' @staticmethod def episode_description(episode): ''' Return a formatted description for an episode ''' return '{start} - {end}\n» {title}'.format(**episode) 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) # 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'), [])) description = '' 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 yellow][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 not description: # Add a final 'No transmission' program description = '[COLOR yellow][B]%s[/B] %s - 06:00\n» %s[/COLOR]' % (localize(30421), episode.get('end'), localize(30423)) return description @staticmethod def parse(date, now): ''' Parse a given string and return a datetime object This supports 'today', 'yesterday' and 'tomorrow' It also compensates for TV-guides covering from 6AM to 6AM ''' entry = find_entry(RELATIVE_DATES, 'id', date) if not entry: return dateutil.parser.parse(date) offset = entry.get('offset') if now.hour < 6: return now + timedelta(days=offset - 1) return now + timedelta(days=offset)
class VRTPlayer: """An object providing all methods for Kodi menu generation""" def __init__(self): """Initialise object""" self._favorites = Favorites() self._resumepoints = ResumePoints() self._apihelper = ApiHelper(self._favorites, self._resumepoints) wait_for_resumepoints() def show_main_menu(self): """The VRT NU add-on main menu""" # self._favorites.refresh(ttl=ttl('indirect')) main_items = [] # Only add 'My favorites' when it has been activated if self._favorites.is_activated(): main_items.append(TitleItem( label=localize(30010), # My favorites path=url_for('favorites_menu'), art_dict=dict(thumb='DefaultFavourites.png'), info_dict=dict(plot=localize(30011)), )) main_items.extend([ TitleItem(label=localize(30012), # All programs path=url_for('programs'), art_dict=dict(thumb='DefaultMovieTitle.png'), info_dict=dict(plot=localize(30013))), TitleItem(label=localize(30014), # Categories path=url_for('categories'), art_dict=dict(thumb='DefaultGenre.png'), info_dict=dict(plot=localize(30015))), TitleItem(label=localize(30016), # Channels path=url_for('channels'), art_dict=dict(thumb='DefaultTags.png'), info_dict=dict(plot=localize(30017))), TitleItem(label=localize(30018), # Live TV path=url_for('livetv'), art_dict=dict(thumb='DefaultTVShows.png'), info_dict=dict(plot=localize(30019))), TitleItem(label=localize(30020), # Recent items path=url_for('recent'), art_dict=dict(thumb='DefaultRecentlyAddedEpisodes.png'), info_dict=dict(plot=localize(30021))), TitleItem(label=localize(30022), # Soon offline path=url_for('offline'), art_dict=dict(thumb='DefaultYear.png'), info_dict=dict(plot=localize(30023))), TitleItem(label=localize(30024), # Featured content path=url_for('featured'), art_dict=dict(thumb='DefaultCountry.png'), info_dict=dict(plot=localize(30025))), TitleItem(label=localize(30026), # TV guide path=url_for('tvguide'), art_dict=dict(thumb='DefaultAddonTvInfo.png'), info_dict=dict(plot=localize(30027))), TitleItem(label=localize(30028), # Search path=url_for('search'), art_dict=dict(thumb='DefaultAddonsSearch.png'), info_dict=dict(plot=localize(30029))), ]) show_listing(main_items, cache=False) # No category self._version_check() def _version_check(self): first_run, settings_version, addon_version = self._first_run() if first_run: # 2.0.0 version: changed plugin:// url interface: show warning that Kodi favourites and what-was-watched will break if settings_version == '' and has_credentials(): ok_dialog(localize(30978), localize(30979)) if addon_version == '2.2.1': # 2.2.1 version: changed artwork: delete old cached artwork delete_cached_thumbnail(get_addon_info('fanart').replace('.png', '.jpg')) delete_cached_thumbnail(get_addon_info('icon')) # 2.2.1 version: moved tokens: delete old tokens from tokenresolver import TokenResolver TokenResolver().delete_tokens() # Make user aware that timeshift functionality will not work without ISA when user starts up the first time if settings_version == '' and kodi_version_major() > 17 and not has_inputstream_adaptive(): ok_dialog(message=localize(30988)) @staticmethod def _first_run(): '''Check if this add-on version is run for the first time''' # Get version from settings.xml settings_version = get_setting('version', default='') # Get version from addon.xml addon_version = get_addon_info('version') # Compare versions (settings_version was not present in version 1.10.0 and older) settings_comp = tuple(map(int, settings_version.split('+')[0].split('.'))) if settings_version != '' else (1, 10, 0) addon_comp = tuple(map(int, addon_version.split('+')[0].split('.'))) if addon_comp > settings_comp: # New version found, save addon version to settings set_setting('version', addon_version) return True, settings_version, addon_version return False, settings_version, addon_version 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 show_favorites_docu_menu(self): """The VRT NU add-on 'My documentaries' listing menu""" self._favorites.refresh(ttl=ttl('indirect')) self._resumepoints.refresh(ttl=ttl('indirect')) episode_items, sort, ascending, content = self._apihelper.list_episodes(category='docu', season='allseasons', programtype='oneoff') show_listing(episode_items, category=30044, sort=sort, ascending=ascending, content=content, cache=False) 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_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_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_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 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_livetv_menu(self): """The VRT NU add-on 'Live TV' listing menu""" channel_items = self._apihelper.list_channels() show_listing(channel_items, category=30018, 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 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_watchlater_menu(self, page=0): """The VRT NU add-on 'My watch later' listing menu""" # My watch later 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='watchlater') show_listing(episode_items, category=30052, sort=sort, ascending=ascending, content=content, cache=False) 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 play_latest_episode(self, program): """A hidden feature in the VRT NU add-on to play the latest episode of a program""" video = self._apihelper.get_latest_episode(program) if not video: log_error('Play latest episode failed, program {program}', program=program) ok_dialog(message=localize(30954)) end_of_directory() return self.play(video) def play_episode_by_air_date(self, channel, start_date, end_date): """Play an episode of a program given the channel and the air date in iso format (2019-07-06T19:35:00)""" video = self._apihelper.get_episode_by_air_date(channel, start_date, end_date) if video and video.get('errorlabel'): ok_dialog(message=localize(30986, title=video.get('errorlabel'))) end_of_directory() return if not video: log_error('Play episode by air date failed, channel {channel}, start_date {start}', channel=channel, start=start_date) ok_dialog(message=localize(30954)) end_of_directory() return self.play(video) def play_episode_by_whatson_id(self, whatson_id): """Play an episode of a program given the whatson_id""" video = self._apihelper.get_single_episode(whatson_id=whatson_id) if not video: log_error('Play episode by whatson_id failed, whatson_id {whatson_id}', whatson_id=whatson_id) ok_dialog(message=localize(30954)) end_of_directory() return self.play(video) def play_upnext(self, video_id): """Play the next episode of a program by video_id""" video = self._apihelper.get_single_episode(video_id=video_id) if not video: log_error('Play Up Next with video_id {video_id} failed', video_id=video_id) ok_dialog(message=localize(30954)) end_of_directory() return self.play(video) @staticmethod def play(video): """A wrapper for playing video items""" from tokenresolver import TokenResolver from streamservice import StreamService _tokenresolver = TokenResolver() _streamservice = StreamService(_tokenresolver) stream = _streamservice.get_stream(video) if stream is None: end_of_directory() return play(stream, video.get('listitem'))
def favorites_manage(): ''' The API interface to manage your favorites ''' from favorites import Favorites Favorites().manage()
def follow(program, title): ''' The API interface to follow a program used by the context menu ''' from favorites import Favorites Favorites().follow(program=program, title=to_unicode(unquote_plus(from_unicode(title))))
''' Favorite Files Licensed under MIT Copyright (c) 2012 Isaac Muse <*****@*****.**> ''' import sublime import sublime_plugin from os.path import join, exists, normpath from favorites import Favorites Favs = Favorites( join(sublime.packages_path(), 'User', 'favorite_files_list.json')) class Refresh: dummy_file = normpath( join(sublime.packages_path(), 'FavoriteFiles', 'refresh.txt')) on = False class CleanOrphanedFavoritesCommand(sublime_plugin.WindowCommand): def run(self): # Clean out all dead links if not Favs.load(clean=True, win_id=self.window.id()): Favs.load(force=True, clean=True, win_id=self.window.id()) class SelectFavoriteFileCommand(sublime_plugin.WindowCommand): def open_file(self, value, group=False): if value >= 0:
def __init__(self): ''' Initialize searchtes, relies on XBMC vfs ''' self._favorites = Favorites() self._resumepoints = ResumePoints() self._search_history = get_userdata_path() + 'search_history.json'
def __init__(self): ''' Initializes TV-guide object ''' self._favorites = Favorites() self._resumepoints = ResumePoints() self._metadata = Metadata(self._favorites, self._resumepoints) install_opener(build_opener(ProxyHandler(get_proxies())))
class Search: """Search and cache search queries""" def __init__(self): """Initialize searchtes, relies on XBMC vfs""" self._favorites = Favorites() self._resumepoints = ResumePoints() self._search_history = addon_profile() + 'search_history.json' def read_history(self): """Read search history from disk""" with open_file(self._search_history, 'r') as fdesc: return get_json_data(fdesc, fail=[]) def write_history(self, history): """Write search history to disk""" from json import dump with open_file(self._search_history, 'w') as fdesc: dump(history, fdesc) def search_menu(self): """Main search menu""" from helperobjects import TitleItem menu_items = [ TitleItem( label=localize(30424), # New search... path=url_for('search_query'), art_dict=dict(thumb='DefaultAddonsSearch.png'), info_dict=dict(plot=localize(30425)), is_playable=False, ) ] history = self.read_history() for keywords in history: menu_items.append(TitleItem( label=keywords, path=url_for('search_query', keywords=keywords), is_playable=False, context_menu=[( localize(30033), # Edit 'RunPlugin(%s)' % url_for('edit_search', keywords=keywords), ), ( localize(30030), # Remove 'RunPlugin(%s)' % url_for('remove_search', keywords=keywords), )], )) if history: menu_items.append(TitleItem( label=localize(30426), # Clear search history path=url_for('clear_search'), info_dict=dict(plot=localize(30427)), art_dict=dict(thumb='icons/infodialogs/uninstall.png'), is_playable=False, )) show_listing(menu_items, category=30031, cache=False) 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 clear(self): """Clear the search history""" self.write_history([]) end_of_directory() def add(self, keywords): """Add new keywords to search history""" history = self.read_history() # Remove if keywords already was listed try: history.remove(keywords) except ValueError: pass history.insert(0, keywords) self.write_history(history) def remove(self, keywords): """Remove existing keywords from search history""" history = self.read_history() try: history.remove(keywords) except ValueError: return # If keywords was successfully removed, write to disk self.write_history(history) input_down() container_refresh()
class TestFavorites(unittest.TestCase): _favorites = Favorites() _resumepoints = ResumePoints() _apihelper = ApiHelper(_favorites, _resumepoints) def test_get_recent_episodes(self): ''' Test items, sort and order ''' episode_items, sort, ascending, content = self._apihelper.list_episodes(page=1, variety='recent') self.assertEqual(len(episode_items), 50) self.assertEqual(sort, 'dateadded') self.assertFalse(ascending) self.assertEqual(content, 'episodes') def test_get_offline_episodes(self): ''' Test items, sort and order ''' episode_items, sort, ascending, content = self._apihelper.list_episodes(page=1, variety='offline') self.assertTrue(episode_items) self.assertEqual(sort, 'dateadded') self.assertFalse(ascending) self.assertEqual(content, 'episodes') @unittest.SkipTest def test_unfollow_all(self): programs = self._apihelper.get_tvshows() for program_item in programs: program_title = program_item.get('title') program = program_item.get('programName') if self._favorites.is_favorite(program): # Unfollow self._favorites.unfollow(program=program, title=program_title) self.assertFalse(self._favorites.is_favorite(program)) @unittest.SkipTest def test_follow_number(self): number = 118 programs = self._apihelper.get_tvshows() shuffle(programs) print('VRT NU has %d programs available' % len(programs)) for program_item in programs[:number]: program_title = program_item.get('title') program = program_item.get('programName') # Follow self._favorites.follow(program=program, title=program_title) self.assertTrue(self._favorites.is_favorite(program)) # Unfollow # self._favorites.unfollow(program=program, title=program_title) # self.assertFalse(self._favorites.is_favorite(program)) @unittest.skipUnless(addon.settings.get('username'), 'Skipping as VRT username is missing.') @unittest.skipUnless(addon.settings.get('password'), 'Skipping as VRT password is missing.') def test_follow_unfollow(self): programs = [ {'program_title': 'Winteruur', 'program': 'winteruur'}, {'program_title': 'De Campus Cup', 'program': 'de-campus-cup'}, {'program_title': 'Terug naar Siberië', 'program': 'terug-naar-siberie'}, {'program_title': 'Belle & Sebastian', 'program': 'belle---sebastian'}, ] for program_item in programs: program_title = program_item.get('program_title') program = program_item.get('program') self._favorites.follow(program=program, title=program_title) self.assertTrue(self._favorites.is_favorite(program)) self._favorites.unfollow(program=program, title=program_title) self.assertFalse(self._favorites.is_favorite(program)) self._favorites.follow(program=program, title=program_title) self.assertTrue(self._favorites.is_favorite(program)) def test_programs(self): programs = self._favorites.programs() # NOTE: Getting favorites requires credentials if addon.settings.get('username') and addon.settings.get('password'): self.assertTrue(programs) print(programs) def test_titles(self): titles = self._favorites.titles() # NOTE: Getting favorites requires credentials if addon.settings.get('username') and addon.settings.get('password'): self.assertTrue(titles) print(sorted(titles))
def __init__(self): ''' Initialise object ''' self._favorites = Favorites() self._resumepoints = ResumePoints() self._apihelper = ApiHelper(self._favorites, self._resumepoints)
def follow(program_name, title, program_id=None): """The API interface to follow a program used by the context menu""" from favorites import Favorites Favorites().follow(program_name=program_name, title=to_unicode(unquote_plus(from_unicode(title))), program_id=program_id)
class TVGuide: """This implements a VRT TV-guide that offers Kodi menus and TV guide info""" VRT_TVGUIDE = 'https://www.vrt.be/bin/epg/schedule.%Y-%m-%d.json' def __init__(self): """Initializes TV-guide object""" self._favorites = Favorites() self._resumepoints = ResumePoints() self._metadata = Metadata(self._favorites, self._resumepoints) def show_tvguide(self, date=None, channel=None): """Offer a menu depending on the information provided""" if not date and not channel: date_items = self.get_date_items() show_listing(date_items, category=30026, content='files') # TV guide elif not channel: channel_items = self.get_channel_items(date=date) entry = find_entry(RELATIVE_DATES, 'id', date) date_name = localize(entry.get('msgctxt')) if entry else date show_listing(channel_items, category=date_name) elif not date: date_items = self.get_date_items(channel=channel) channel_name = find_entry(CHANNELS, 'name', channel).get('label') show_listing(date_items, category=channel_name, content='files', selected=7) else: episode_items = self.get_episode_items(date, channel) channel_name = find_entry(CHANNELS, 'name', channel).get('label') entry = find_entry(RELATIVE_DATES, 'id', date) date_name = localize(entry.get('msgctxt')) if entry else date show_listing(episode_items, category='%s / %s' % (channel_name, date_name), content='episodes', cache=False) @staticmethod def get_date_items(channel=None): """Offer a menu to select the TV-guide date""" epg = datetime.now(dateutil.tz.tzlocal()) # Daily EPG information shows information from 6AM until 6AM if epg.hour < 6: epg += timedelta(days=-1) date_items = [] for offset in range(14, -19, -1): day = epg + timedelta(days=offset) label = localize_datelong(day) date = day.strftime('%Y-%m-%d') # Highlight today with context of 2 days entry = find_entry(RELATIVE_DATES, 'offset', offset) if entry: date_name = localize(entry.get('msgctxt')) if entry.get('permalink'): date = entry.get('id') if offset == 0: label = '[COLOR={highlighted}][B]{name}[/B], {date}[/COLOR]'.format( highlighted=themecolour('highlighted'), name=date_name, date=label) else: label = '[B]{name}[/B], {date}'.format(name=date_name, date=label) plot = '[B]{datelong}[/B]'.format(datelong=localize_datelong(day)) # Show channel list or channel episodes if channel: path = url_for('tvguide', date=date, channel=channel) else: path = url_for('tvguide', date=date) cache_file = 'schedule.{date}.json'.format(date=date) date_items.append( TitleItem( label=label, path=path, art_dict=dict(thumb='DefaultYear.png'), info_dict=dict(plot=plot), context_menu=[( localize(30413), # Refresh menu 'RunPlugin(%s)' % url_for('delete_cache', cache_file=cache_file))], )) return date_items def get_channel_items(self, date=None, channel=None): """Offer a menu to select the channel""" if date: now = datetime.now(dateutil.tz.tzlocal()) epg = self.parse(date, now) datelong = localize_datelong(epg) channel_items = [] for chan in CHANNELS: # Only some channels are supported if not chan.get('has_tvguide'): continue # If a channel is requested, stop processing if it is no match if channel and channel != chan.get('name'): continue art_dict = {} # Try to use the white icons for thumbnails (used for icons as well) if has_addon('resource.images.studios.white'): art_dict[ 'thumb'] = 'resource://resource.images.studios.white/{studio}.png'.format( **chan) else: art_dict['thumb'] = 'DefaultTags.png' if date: label = chan.get('label') path = url_for('tvguide', date=date, channel=chan.get('name')) plot = '[B]%s[/B]\n%s' % (datelong, localize(30302, **chan)) else: label = '[B]%s[/B]' % localize(30303, **chan) path = url_for('tvguide_channel', channel=chan.get('name')) plot = '%s\n\n%s' % (localize( 30302, **chan), self.live_description(chan.get('name'))) context_menu = [( localize(30413), # Refresh menu 'RunPlugin(%s)' % url_for('delete_cache', cache_file='channel.{channel}.json'.format( channel=chan.get('name'))), )] channel_items.append( TitleItem( label=label, path=path, art_dict=art_dict, context_menu=context_menu, info_dict=dict(plot=plot, studio=chan.get('studio')), )) return channel_items 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 @staticmethod def get_episode_path(episode, channel): """Return a playable plugin:// path for an episode""" now = datetime.now(dateutil.tz.tzlocal()) end_date = dateutil.parser.parse(episode.get('endTime')) if episode.get('url') and episode.get('vrt.whatson-id'): return url_for('play_whatson_id', whatson_id=episode.get('vrt.whatson-id')) if now - timedelta(hours=24) <= end_date <= now: return url_for('play_air_date', channel, episode.get('startTime')[:19], episode.get('endTime')[:19]) return url_for('noop', whatsonid=episode.get('vrt.whatson-id', '')) def get_epg_data(self): """Return EPG data""" now = datetime.now(dateutil.tz.tzlocal()) epg_data = {} for date in ['yesterday', 'today', 'tomorrow']: epg = self.parse(date, now) epg_url = epg.strftime(self.VRT_TVGUIDE) schedule = get_url_json(url=epg_url, fail={}) for channel_id, episodes in list(schedule.items()): channel = find_entry(CHANNELS, 'id', channel_id) epg_id = channel.get('epg_id') if epg_id not in epg_data: epg_data[epg_id] = [] for episode in episodes: if episode.get('url') and episode.get('vrt.whatson-id'): path = url_for( 'play_whatson_id', whatson_id=episode.get('vrt.whatson-id')) else: path = None epg_data[epg_id].append( dict( start=episode.get('startTime'), stop=episode.get('endTime'), image=add_https_proto(episode.get('image', '')), title=episode.get('title'), subtitle=html_to_kodi(episode.get('subtitle', '')), description=html_to_kodi( episode.get('description', '')), stream=path, )) return epg_data 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 '' @staticmethod def episode_description(episode): """Return a formatted description for an episode""" return '{start} - {end}\n» {title}'.format(**episode) 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) @staticmethod def parse(date, now): """Parse a given string and return a datetime object This supports 'today', 'yesterday' and 'tomorrow' It also compensates for TV-guides covering from 6AM to 6AM """ entry = find_entry(RELATIVE_DATES, 'id', date) if not entry: return dateutil.parser.parse(date) offset = entry.get('offset') if now.hour < 6: return now + timedelta(days=offset - 1) return now + timedelta(days=offset)
def __init__(self): """Initializes TV-guide object""" self._favorites = Favorites() self._resumepoints = ResumePoints() self._metadata = Metadata(self._favorites, self._resumepoints)
class TestVRTPlayer(unittest.TestCase): _favorites = Favorites() _resumepoints = ResumePoints() _apihelper = ApiHelper(_favorites, _resumepoints) _vrtplayer = VRTPlayer() def test_show_videos_single_episode_shows_videos(self): program = 'marathonradio' episode_items, sort, ascending, content = self._apihelper.list_episodes( program=program) self.assertTrue(episode_items, msg=program) self.assertEqual(sort, 'dateadded') self.assertFalse(ascending) self.assertEqual(content, 'episodes') self._vrtplayer.show_episodes_menu(program) def test_show_videos_single_season_shows_videos(self): program = 'het-weer' episode_items, sort, ascending, content = self._apihelper.list_episodes( program=program) self.assertTrue(episode_items, msg=program) self.assertEqual(sort, 'dateadded') self.assertFalse(ascending) self.assertEqual(content, 'episodes') self._vrtplayer.show_episodes_menu(program) def test_show_videos_multiple_seasons_shows_videos(self): program = 'pano' episode_items, sort, ascending, content = self._apihelper.list_episodes( program=program) self.assertTrue(episode_items) self.assertEqual(sort, 'label') self.assertFalse(ascending) self.assertEqual(content, 'seasons') self._vrtplayer.show_episodes_menu(program) def test_show_videos_specific_seasons_shows_videos(self): program = 'thuis' episode_items, sort, ascending, content = self._apihelper.list_episodes( program=program) self.assertTrue(episode_items, msg=program) self.assertEqual(sort, 'label') self.assertFalse(ascending) self.assertEqual(content, 'seasons') self._vrtplayer.show_episodes_menu(program) def test_categories_scraping(self): ''' Test to ensure our hardcoded categories conforms to scraped categories ''' # Remove thumbnails from scraped categories first categories_scraped = [ dict(id=c['id'], name=c['name']) for c in self._apihelper.get_categories() ] categories_stored = [ dict(id=c['id'], name=c['name']) for c in CATEGORIES ] self.assertEqual(categories_scraped, categories_stored) def test_random_tvshow_episodes(self): ''' Rest episode from a random tvshow in a random category ''' categories = self._apihelper.get_categories() self.assertTrue(categories) category = random.choice(categories) tvshow_items = self._apihelper.list_tvshows(category['id']) self.assertTrue(tvshow_items, msg=category['id']) tvshow = random.choice(tvshow_items) if tvshow.path.startswith('plugin://plugin.video.vrt.nu/programs/'): # When random program has episodes episode_items, sort, ascending, content = self._apihelper.list_episodes( tvshow.path.split('/')[4].replace('.relevant', '')) self.assertTrue(episode_items, msg=tvshow.path.split('/')[4]) self.assertTrue( sort in ['dateadded', 'episode', 'label', 'unsorted']) self.assertTrue(ascending is True or ascending is False) self.assertTrue( content in ['episodes', 'seasons'], "Content for '%s' is '%s'" % (tvshow.title, content)) elif tvshow.path.startswith('plugin://plugin.video.vrt.nu/play/id/'): # When random program is playable item pass else: self.fail( 'We did not expect this, either we find episodes or it is a playable item' ) def test_categories(self): ''' Test to ensure our hardcoded categories conforms to scraped categories ''' category_items = self._apihelper.list_categories() self.assertEqual(len(category_items), 17) def test_featured(self): ''' Test to ensure our hardcoded categories conforms to scraped categories ''' featured_items = self._apihelper.list_featured() self.assertEqual(len(featured_items), 9) def test_play_unknown_program(self): ''' Test playing latest episode of an unknown program ''' self._vrtplayer.play_latest_episode(program='foobar') def test_play_unknown_airdate(self): ''' Test playing unknown airdate ''' self._vrtplayer.play_episode_by_air_date( channel='een', start_date='2100-01-01T23:59:58', end_date='2100-01-01T23:59:59') self._vrtplayer.play_episode_by_air_date( channel='foo', start_date='2100-01-01T23:59:58', end_date='2100-01-01T23:59:59') def test_play_unknown_whatson_id(self): ''' Test playing unknown whatson id ''' self._vrtplayer.play_whatson(whatson_id='1234567890')
def __init__(self): """Initialize searchtes, relies on XBMC vfs""" self._favorites = Favorites() self._resumepoints = ResumePoints() self._search_history = addon_profile() + 'search_history.json'
def remove_favorite(user_id, username): """Remove a user from favorites""" # Remove favorite from dictionary favs = Favorites(wf()) favs.remove(user_id)
def favorites_refresh(): ''' The API interface to refresh the favorites cache ''' from favorites import Favorites Favorites().refresh(ttl=0) notification(message=localize(30982))
def favorites_manage(): """The API interface to manage your favorites""" from favorites import Favorites Favorites().manage()
class Search: ''' Search and cache search queries ''' def __init__(self): ''' Initialize searchtes, relies on XBMC vfs ''' self._favorites = Favorites() self._resumepoints = ResumePoints() self._search_history = get_userdata_path() + 'search_history.json' def read_history(self): ''' Read search history from disk ''' from json import load try: with open_file(self._search_history, 'r') as fdesc: history = load(fdesc) except (TypeError, ValueError): # No JSON object could be decoded history = [] return history def write_history(self, history): ''' Write search history to disk ''' from json import dump with open_file(self._search_history, 'w') as fdesc: dump(history, fdesc) def search_menu(self): ''' Main search menu ''' from helperobjects import TitleItem menu_items = [ TitleItem( title=localize(30424), # New search... path=url_for('search_query'), art_dict=dict(thumb='DefaultAddonsSearch.png'), info_dict=dict(plot=localize(30425)), is_playable=False, ) ] history = self.read_history() for keywords in history: menu_items.append( TitleItem(title=keywords, path=url_for('search_query', keywords=keywords), art_dict=dict(thumb='DefaultAddonsSearch.png'), is_playable=False, context_menu=[ (localize(30030), 'RunPlugin(%s)' % url_for('remove_search', keywords=keywords)) ])) if history: menu_items.append( TitleItem( title=localize(30426), # Clear search history path=url_for('clear_search'), info_dict=dict(plot=localize(30427)), art_dict=dict(thumb='icons/infodialogs/uninstall.png'), is_playable=False, )) show_listing(menu_items, category=30031, cache=False) def search(self, keywords=None, page=None): ''' The VRT NU add-on Search functionality and results ''' if keywords is None: keywords = get_search_string() if not keywords: end_of_directory() return from statichelper import realpage page = realpage(page) self.add(keywords) from apihelper import ApiHelper 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) == 50: search_items.append( TitleItem( title=localize(30300), path=url_for('search_query', keywords=keywords, page=page + 1), art_dict=dict(thumb='DefaultAddonSearch.png'), info_dict=dict(), )) self._favorites.refresh(ttl=60 * 60) show_listing(search_items, category=30032, sort=sort, ascending=ascending, content=content, cache=False) def clear(self): ''' Clear the search history ''' self.write_history([]) end_of_directory() def add(self, keywords): ''' Add new keywords to search history ''' history = self.read_history() # Remove if keywords already was listed try: history.remove(keywords) except ValueError: pass history.insert(0, keywords) self.write_history(history) def remove(self, keywords): ''' Remove existing keywords from search history ''' history = self.read_history() try: history.remove(keywords) except ValueError: return # If keywords was successfully removed, write to disk self.write_history(history) container_refresh()