def index(): """ Ask to login, or go to the main menu. """ if not kodiutils.has_credentials(): if not kodiutils.yesno_dialog(message=kodiutils.localize( 30701)): # You need to configure your credentials... # We have no credentials, return to the Home Menu kodiutils.end_of_directory() kodiutils.execute_builtin('ActivateWindow(Home)') return kodiutils.open_settings() try: # Try authentication AuthApi(username=kodiutils.get_setting('username'), password=kodiutils.get_setting('password'), tenant=kodiutils.get_setting('tenant'), token_path=kodiutils.get_tokens_path()) except InvalidLoginException: kodiutils.ok_dialog(message=kodiutils.localize( 30203)) # Your credentials are not valid! kodiutils.open_settings() kodiutils.execute_builtin('ActivateWindow(Home)') kodiutils.end_of_directory() return except HTTPError as exc: kodiutils.ok_dialog(message=kodiutils.localize( 30702, code='HTTP %d' % exc.response.status_code) ) # Unknown error while logging in: {code} kodiutils.end_of_directory() return show_main_menu()
def search(): # type: () -> Union[bool, None] query = get_arg("q") # remove saved search item if bool(get_arg("delete", False)): eyes.searches.remove(query) xbmc.executebuiltin("Container.Refresh()") return True # View saved search menu if bool(get_arg("menu", False)): add_menu_item(search, "[{}]".format(ku.localize(32016)), {"new": True}) # [New Search] for item in eyes.searches.retrieve(): text = item.encode("utf-8") add_menu_item(search, text, {"q": text}) xbmcplugin.setPluginCategory(plugin.handle, ku.localize(32007)) # Search xbmcplugin.endOfDirectory(plugin.handle) return True # look-up if bool(get_arg("new", False)): query = ku.user_input() if not query: return False if eyes.SEARCH_SAVED: eyes.searches.append(query) category = "{} '{}'".format(ku.localize(32007), query) # Search 'query' xbmcplugin.setPluginCategory(plugin.handle, category) parse_results(eyes.get_search_url(query, 0), category) # first page is 0 xbmcplugin.endOfDirectory(plugin.handle)
def _resolve_stream(uuid): """ Resolve the stream for the requested item :type uuid: string """ try: # Check if we have credentials if not kodiutils.get_setting('username') or not kodiutils.get_setting('password'): confirm = kodiutils.yesno_dialog( message=kodiutils.localize(30701)) # To watch a video, you need to enter your credentials. Do you want to enter them now? if confirm: kodiutils.open_settings() kodiutils.end_of_directory() return None # Fetch an auth token now try: auth = AuthApi(kodiutils.get_setting('username'), kodiutils.get_setting('password'), kodiutils.get_tokens_path()) # Get stream information resolved_stream = ContentApi(auth).get_stream_by_uuid(uuid) return resolved_stream except (InvalidLoginException, AuthenticationException) as ex: _LOGGER.exception(ex) kodiutils.ok_dialog(message=kodiutils.localize(30702, error=str(ex))) kodiutils.end_of_directory() return None except GeoblockedException: kodiutils.ok_dialog(message=kodiutils.localize(30710)) # This video is geo-blocked... return None except UnavailableException: kodiutils.ok_dialog(message=kodiutils.localize(30712)) # The video is unavailable... return None
def search(): # type: () -> Optional[bool] """Search the archive""" query = get_arg("q") category = get_arg("category", ku.localize(32007)) # Search total = get_arg("total") # Remove saved search item if bool(get_arg("delete", False)): pas.searches.remove(query) xbmc.executebuiltin("Container.Refresh()") return True # View saved search menu if bool(get_arg("menu", False)): add_menu_item(search, "[{}]".format(ku.localize(32016)), args={"new": True}) # [New Search] for item in pas.searches.retrieve(): text = item.encode("utf-8") add_menu_item(search, text, args={"q": text, "category": "{} '{}'".format(ku.localize(32007), text)}) xbmcplugin.setPluginCategory(plugin.handle, category) xbmcplugin.endOfDirectory(plugin.handle) return True # New look-up if bool(get_arg("new", False)): query = ku.user_input() if not query: return False category = "{} '{}'".format(ku.localize(32007), query) if pas.SEARCH_SAVED: pas.searches.append(query) # Process search url = get_arg("href", pas.get_search_url(query=query)) soup = pas.get_html(url) count = int(total) if total else pas.text_to_int(soup.find("div", "results_count").text) parse_search_results(soup, url, count, category) xbmcplugin.setPluginCategory(plugin.handle, category) xbmcplugin.endOfDirectory(plugin.handle)
def add_menu_item(method, label, args=None, art=None, info=None, directory=True): # type: (Callable, Union[str, int], dict, dict, dict, bool) -> None """wrapper for xbmcplugin.addDirectoryItem""" info = {} if info is None else info art = {} if art is None else art args = {} if args is None else args label = ku.localize(label) if isinstance(label, int) else label list_item = ListItem(label) list_item.setArt(art) if method == search and "q" in args: # saved search menu items can be removed via context menu list_item.addContextMenuItems([(ku.localize(32019), "XBMC.RunPlugin({})".format( plugin.url_for(search, delete=True, q=label)))]) if method in [play_film, programme]: list_item.setInfo("video", info) list_item.setProperty("IsPlayable", "true") xbmcplugin.addDirectoryItem(plugin.handle, plugin.url_for(method, **args), list_item, directory)
def index(): """Main menu""" if ku.get_setting_as_bool("show_genres"): add_menu_item(themes, 32005, args={ "href": "en/categories/stories", "title": ku.localize(32005) }, art=ku.icon("genres.png")) if ku.get_setting_as_bool("show_motions"): add_menu_item(themes, 32002, args={ "href": "en/categories/motions", "title": ku.localize(32002) }, art=ku.icon("techniques.png")) if ku.get_setting_as_bool("show_characters"): add_menu_item(themes, 32003, args={ "href": "en/categories/characters", "title": ku.localize(32003) }, art=ku.icon("characters.png")) if ku.get_setting_as_bool("show_authors"): add_menu_item(authors, 32004, art=ku.icon("authors.png")) if ku.get_setting_as_bool("show_experts"): add_menu_item(experts, 32023, art=ku.icon("experts.png")) if ku.get_setting_as_bool("show_techniques"): add_menu_item(themes, 32006, args={"href": "en/categories/techniques", "title": ku.localize(32006)}, art=ku.icon("techniques.png")) if ku.get_setting_as_bool("show_search"): add_menu_item(search, 32007, args={"menu": True}, art=ku.icon("search.png")) if ku.get_setting_as_bool("show_recent"): add_menu_item(recent, 32026, art=ku.icon("saved.png")) if ku.get_setting_as_bool("show_settings"): add_menu_item(settings, 32010, art=ku.icon("settings.png"), directory=False) xbmcplugin.setPluginCategory(plugin.handle, ADDON_NAME) xbmcplugin.endOfDirectory(plugin.handle)
def films(): """Playable movie items""" url = jafc.get_url(get_arg("href")) data = jafc.get_html(url) paginate(data.find("ul", "pager-menu"), films) container = data.find(True, {"class": ["categories", "characters", "writer-works"]}) if container is None: ku.notification(ku.localize(32008), ku.localize(32009)) # Error - No playable items found return for item in container.find_all("li"): action = item.find("a") if action is None: continue add_menu_item( play_film, item.find(True, {"class": ["category-title", "character-serif", "writer-work-heading"]}).text, args={"href": action["href"]}, info=jafc.get_info(action["href"]), art=ku.art(jafc.get_url(item.find("img", "thumbnail")["src"])), directory=False) xbmcplugin.setContent(plugin.handle, "videos") xbmcplugin.setPluginCategory(plugin.handle, get_arg("title")) xbmcplugin.addSortMethod(plugin.handle, xbmcplugin.SORT_METHOD_LABEL_IGNORE_THE) xbmcplugin.addSortMethod(plugin.handle, xbmcplugin.SORT_METHOD_GENRE) xbmcplugin.addSortMethod(plugin.handle, xbmcplugin.SORT_METHOD_VIDEO_YEAR) xbmcplugin.addSortMethod(plugin.handle, xbmcplugin.SORT_METHOD_DURATION) xbmcplugin.endOfDirectory(plugin.handle)
def shows(): page = get_arg("page", 0) category = get_arg("category", ku.localize(32003)) soup = ngs.get_html(ngs.get_show_url(page)) # Shows paging load_more = soup.find("a", "load-more") if load_more: next_page = ngs.get_gp(load_more.get("href")) add_menu_item(shows, "[{} {}]".format(ku.localize(32011), next_page + 1), args={"page": next_page}) for item in soup.find_all("div", "media-module"): # shows menu action = item.find("a") title = item.find("div", "title").text img = action.find("img") count = ngs.get_playable_item_count(action.get("href")) add_menu_item(section, "{} [{} items]".format(title, count), args={ "href": action.get("href"), "category": title }, art=ku.art(img.get("src"))) xbmcplugin.setPluginCategory(plugin.handle, category) xbmcplugin.endOfDirectory(plugin.handle)
def _format_channel_plot(cls, channel): """ Format a plot for a channel. :param resources.lib.solocoo.util.Channel channel: The channel we want to have a plot for. :returns: A formatted plot for this channel. :rtype: str """ plot = '' if channel.epg_now: plot += kodiutils.localize( 30213, # Now start=channel.epg_now.start.strftime('%H:%M'), end=channel.epg_now.end.strftime('%H:%M'), title=channel.epg_now.title) + "\n" if channel.epg_next: plot += kodiutils.localize( 30214, # Next start=channel.epg_next.start.strftime('%H:%M'), end=channel.epg_next.end.strftime('%H:%M'), title=channel.epg_next.title) + "\n" return plot
def _get_dates(date_format): """ Return a dict of dates. :param str date_format: The date format to use for the labels. :rtype: list[dict] """ dates = [] today = datetime.today() # The API provides content for 8 days in the past and 0 days in the future for i in range(0, -8, -1): day = today + timedelta(days=i) if i == -1: dates.append({ 'title': '%s, %s' % (kodiutils.localize(30301), day.strftime(date_format)), # Yesterday 'key': 'yesterday', 'date': day.strftime('%d.%m.%Y'), 'highlight': False, }) elif i == 0: dates.append({ 'title': '%s, %s' % (kodiutils.localize(30302), day.strftime(date_format)), # Today 'key': 'today', 'date': day.strftime('%d.%m.%Y'), 'highlight': True, }) elif i == 1: dates.append({ 'title': '%s, %s' % (kodiutils.localize(30303), day.strftime(date_format)), # Tomorrow 'key': 'tomorrow', 'date': day.strftime('%d.%m.%Y'), 'highlight': False, }) else: dates.append({ 'title': day.strftime(date_format), 'key': day.strftime('%Y-%m-%d'), 'date': day.strftime('%d.%m.%Y'), 'highlight': False, }) return dates
def refresh(cls, show_progress=False): """ Update channels and EPG data """ channels = [] epg = dict() if show_progress: progress = kodiutils.progress( message=kodiutils.localize(30703)) # Detecting IPTV add-ons... else: progress = None addons = cls.get_iptv_addons() for index, addon in enumerate(addons): _LOGGER.info('Updating IPTV data for %s...', addon.addon_id) if progress: progress.update(int(100 * index / len(addons)), kodiutils.localize(30704).format( addon=kodiutils.addon_name(addon.addon_obj) )) # Fetching channels and guide of {addon}... # Fetch channels channels.extend(addon.get_channels()) if progress and progress.iscanceled(): progress.close() return # Fetch EPG data epg.update(addon.get_epg()) if progress and progress.iscanceled(): progress.close() return # Write files if show_progress: progress.update( 100, kodiutils.localize(30705)) # Updating channels and guide... IptvSimple.write_playlist(channels) IptvSimple.write_epg(epg) if kodiutils.get_setting_bool('iptv_simple_restart'): if show_progress: # Try to restart now. We will schedule it if the user is watching TV. IptvSimple.restart(True) else: # Schedule a restart IptvSimple.restart(False) # Update last_refreshed kodiutils.set_setting_int('last_refreshed', int(time.time())) if show_progress: progress.close() kodiutils.ok_dialog(message=kodiutils.localize( 30706)) # The channels and guide are updated successfully!
def past24h(): """Shows playable items from the last 24 hours""" url = dws.get_search_url() soup = dws.get_html(url) parse_search_results(soup, url, past24h, ku.localize(32004)) xbmcplugin.setContent(plugin.handle, "episodes") xbmcplugin.setPluginCategory(plugin.handle, ku.localize(32004)) # Past 24 hours xbmcplugin.endOfDirectory(plugin.handle)
def paginate(query, count, total, offset): # type: (str, int, int, int) -> None """Adds search partition menu items""" if count < total and count == iwm.SEARCH_MAX_RESULTS: offset += 1 if offset > 1: add_menu_item(search, "[{} 1]".format(ku.localize(32011)), {"q": query, "offset": 1}) add_menu_item(search, "[{} {}]".format(ku.localize(32011), offset), {"q": query, "offset": offset}) add_menu_item(index, "[{}]".format(ku.localize(32012)))
def new(): # type: () -> None """Shows new playable items menu""" href = get_arg("href", ngs.NG_URI) soup = ngs.get_html(ngs.get_url(href)) parse_search_results( soup.find("div", { "id": "grid-frame" }).extract(), ku.localize(32006), new) xbmcplugin.setPluginCategory(plugin.handle, ku.localize(32006)) xbmcplugin.endOfDirectory(plugin.handle)
def setup_iptv_simple(): """ Setup IPTV Simple """ reply = kodiutils.yesno_dialog( message=kodiutils.localize(30700)) # Are you sure... if reply: if IptvSimple.setup(): kodiutils.ok_dialog(message=kodiutils.localize( 30701)) # The configuration of IPTV Simple is completed! else: kodiutils.ok_dialog(message=kodiutils.localize( 30702)) # The configuration of IPTV Simple has failed!
def clean(): """ Clear metadata (called from settings) """ cache_path = kodiutils.get_cache_path() _, files = kodiutils.listdir(cache_path) for filename in files: if not kodiutils.delete(os.path.join(cache_path, filename)): return kodiutils.ok_dialog(message=kodiutils.localize( 30721)) # Clearing local metadata failed kodiutils.set_setting('metadata_last_updated', '0') return kodiutils.ok_dialog( message=kodiutils.localize(30714)) # Local metadata is cleared
def paginate(data, method): """Adds pagination links as required""" if data is None: return items = data.find_all("li") for item in items: if "class" in item.attrs and any(x in item.attrs["class"] for x in ["disabled", "active", "prev", "next"]): continue action = item.find("a") add_menu_item(method, "[{} {}]".format(ku.localize(32011), item.text), { # [Page n] "href": action["href"] }) add_menu_item(index, "[{}]".format(ku.localize(32012))) # [Menu]
def paginate(query, count, total, offset): # type: (str, int, int, int) -> None """Adds search partition menu items""" if count < total and count == bfis.SEARCH_MAX_RESULTS: offset += 1 next_page = "[{} {}]".format(ku.localize(32011), offset + 1) # [Page n+1] first_page = "[{} 1]".format(ku.localize(32011)) # [Page 1] main_menu = "[{}]".format(ku.localize(32012)) # [Menu] if offset > 1: add_menu_item(search, first_page, args={"q": query, "offset": 0}) add_menu_item(search, next_page, args={"q": query, "offset": offset}) add_menu_item(index, main_menu)
def play_epg_datetime(self, channel, timestamp): """ Play a program based on the channel and the timestamp when it was aired :type channel: str :type timestamp: str """ broadcast = self._vtm_go_epg.get_broadcast(channel, timestamp) if not broadcast: kodiutils.ok_dialog(heading=kodiutils.localize(30711), message=kodiutils.localize(30713)) # The requested video was not found in the guide. kodiutils.end_of_directory() return kodiutils.redirect( kodiutils.url_for('play', category=broadcast.playable_type, item=broadcast.playable_uuid))
def search(): query = get_arg("q") href = get_arg("href", False) # remove saved search item if bool(get_arg("delete", False)): jafc.remove(query) xbmc.executebuiltin("Container.Refresh()") return True # View saved search menu if bool(get_arg("menu", False)): add_menu_item(search, "[{}]".format(ku.localize(32016)), {"new": True}) # [New Search] for item in jafc.retrieve(): text = item.encode("utf-8") add_menu_item( search, text, args={"q": text}) xbmcplugin.setPluginCategory(plugin.handle, ku.localize(32007)) # Search xbmcplugin.endOfDirectory(plugin.handle) return True # look-up if bool(get_arg("new", False)): query = ku.user_input() jafc.append(query) if not query: return False url = jafc.get_url(href) if href else jafc.get_search_url(query) data = jafc.get_html(url) results = data.find("ul", "work-results") if not results: return paginate(data.find("ul", "pager-menu"), search) for item in results.find_all("li", "work-result"): action = item.find("a") img = item.find("img", "thumbnail") add_menu_item(play_film, item.find("h2").text, args={"href": action["href"]}, info=jafc.get_info(action["href"]), art=ku.art(jafc.get_url(img["src"])), directory=False) xbmcplugin.setPluginCategory(plugin.handle, "{} '{}'".format(ku.localize(32016), query)) xbmcplugin.endOfDirectory(plugin.handle)
def show_search(self, query=None): """ Shows the search dialog :type query: str """ if not query: # Ask for query query = kodiutils.get_search_string( heading=kodiutils.localize(30009)) # Search if not query: kodiutils.end_of_directory() return # Do search try: items = self._search.search(query) except Exception as ex: # pylint: disable=broad-except kodiutils.notification(message=str(ex)) kodiutils.end_of_directory() return # Display results listing = [self._menu.generate_titleitem(item) for item in items] # Sort like we get our results back. kodiutils.show_listing(listing, 30009, content='tvshows')
def programme(): """Shows the programme menu or a programme's playable items""" href = get_arg("href") category = get_arg("category", ku.localize(32009)) # Programs if not href: # TV Shows menu soup = dws.get_html(dws.DW_PROGRAMME_URI) content = soup.find("div", {"id": "bodyContent"}).extract() items = content.find_all("div", "epg") for item in items: img = item.find("img") title = item.find("h2").text.encode("utf-8") action = item.find("a", string="All videos") pid = dws.get_program_id(action.get("href")) plot = item.find("p").text.strip() add_menu_item(programme, title, { "href": dws.get_search_url(pid=pid), "category": title }, ku.art(dws.get_url(img.get("src"))), {"plot": plot if plot else title}) xbmcplugin.setContent(plugin.handle, "tvshows") else: # TV Show's playable episodes soup = dws.get_html(href) parse_search_results(soup, href, programme, category) xbmcplugin.setContent(plugin.handle, "episodes") xbmcplugin.setPluginCategory(plugin.handle, category) xbmcplugin.endOfDirectory(plugin.handle)
def play_or_live(self, category, item, channel): """ Ask to play the requested item or switch to the live channel :type category: str :type item: str :type channel: str """ res = kodiutils.show_context_menu([kodiutils.localize(30103), kodiutils.localize(30105)]) # Watch Live | Play from Catalog if res == -1: # user has cancelled return if res == 0: # user selected "Watch Live" # Play live self.play('channels', channel) return # Play this program self.play(category, item)
def show_catalog_category(self, category=None): """ Show a category in the catalog :type category: str """ try: items = self._vtm_go.get_items(category) except ApiUpdateRequired: kodiutils.ok_dialog(message=kodiutils.localize( 30705)) # The VTM GO Service has been updated... return except Exception as ex: # pylint: disable=broad-except _LOGGER.error("%s", ex) kodiutils.ok_dialog(message="%s" % ex) return listing = [] for item in items: listing.append(self._menu.generate_titleitem(item)) # Sort items by label, but don't put folders at the top. # Used for A-Z listing or when movies and episodes are mixed. kodiutils.show_listing( listing, 30003, content='movies' if category == 'films' else 'tvshows', sort=['label', 'year', 'duration'])
def show_continuewatching(self): """ Show the items in "Continue Watching" """ try: mylist = self._vtm_go.get_swimlane('continue-watching') except ApiUpdateRequired: kodiutils.ok_dialog(message=kodiutils.localize( 30705)) # The VTM GO Service has been updated... return except Exception as ex: # pylint: disable=broad-except _LOGGER.error("%s", ex) kodiutils.ok_dialog(message="%s" % ex) return listing = [] for item in mylist: titleitem = self._menu.generate_titleitem(item, progress=True) # Add Program Name to title since this list contains episodes from multiple programs title = '%s - %s' % (titleitem.info_dict.get('tvshowtitle'), titleitem.info_dict.get('title')) titleitem.info_dict['title'] = title listing.append(titleitem) # Sort categories by default like in VTM GO. kodiutils.show_listing(listing, 30019, content='episodes', sort='label')
def show_catalog(self): """ Show the catalog """ try: categories = self._vtm_go.get_categories() except ApiUpdateRequired: kodiutils.ok_dialog(message=kodiutils.localize( 30705)) # The VTM GO Service has been updated... return except Exception as ex: # pylint: disable=broad-except _LOGGER.error("%s", ex) kodiutils.ok_dialog(message="%s" % ex) return listing = [] for cat in categories: listing.append( kodiutils.TitleItem( title=cat.title, path=kodiutils.url_for('show_catalog_category', category=cat.category_id), info_dict=dict( plot='[B]{category}[/B]'.format(category=cat.title), ), )) # Sort categories by default like in VTM GO. kodiutils.show_listing(listing, 30003, content='files')
def show_recommendations_category(self, storefront, category): """ Show the items in a recommendations category. :type storefront: str :type category: str """ try: recommendations = self._vtm_go.get_recommendations(storefront) except ApiUpdateRequired: kodiutils.ok_dialog(message=kodiutils.localize( 30705)) # The VTM GO Service has been updated... return except Exception as ex: # pylint: disable=broad-except _LOGGER.error("%s", ex) kodiutils.ok_dialog(message="%s" % ex) return listing = [] for cat in recommendations: # Only show the requested category if cat.category_id != category: continue for item in cat.content: listing.append(self._menu.generate_titleitem(item)) # Sort categories by default like in VTM GO. kodiutils.show_listing(listing, 30015, content='tvshows')
def show_program_season(self, program, season): """ Show the episodes of a program from the catalog :type program: str :type season: int """ try: program_obj = self._vtm_go.get_program( program ) # Use CACHE_AUTO since the data is just refreshed in show_program except UnavailableException: kodiutils.ok_dialog( message=kodiutils.localize(30717) ) # This program is not available in the VTM GO catalogue. kodiutils.end_of_directory() return if season == -1: # Show all seasons seasons = list(program_obj.seasons.values()) else: # Show the season that was selected seasons = [program_obj.seasons[season]] listing = [ self._menu.generate_titleitem(e) for s in seasons for e in list(s.episodes.values()) ] # Sort by episode number by default. Takes seasons into account. kodiutils.show_listing(listing, 30003, content='episodes', sort=['episode', 'duration'])
def show_search(self, query=None): """ Shows the search dialog. :type query: str """ if not query: # Ask for query query = kodiutils.get_search_string( heading=kodiutils.localize(30009)) # Search Streamz if not query: kodiutils.end_of_directory() return # Do search try: items = self._api.do_search(query) except Exception as ex: # pylint: disable=broad-except kodiutils.notification(message=str(ex)) kodiutils.end_of_directory() return # Display results show_unavailable = kodiutils.get_setting_bool( 'interface_show_unavailable') listing = [] for item in items: if show_unavailable or item.available: listing.append(Menu.generate_titleitem(item)) # Sort like we get our results back. kodiutils.show_listing(listing, 30009, content='tvshows')
def show_recommendations_category(self, storefront, category): """ Show the items in a recommendations category. :type storefront: str :type category: str """ try: result = self._vtm_go.get_storefront_category(storefront, category) except ApiUpdateRequired: kodiutils.ok_dialog(message=kodiutils.localize( 30705)) # The VTM GO Service has been updated... return except Exception as ex: # pylint: disable=broad-except _LOGGER.error("%s", ex) kodiutils.ok_dialog(message="%s" % ex) return listing = [] for item in result.content: listing.append(Menu.generate_titleitem(item)) if storefront == STOREFRONT_SERIES: content = 'tvshows' elif storefront == STOREFRONT_MOVIES: content = 'movies' else: content = 'tvshows' # Fallback to a list of tvshows kodiutils.show_listing(listing, result.title, content=content, sort=['unsorted', 'label', 'year', 'duration'])