import os import sys from urllib.parse import urlencode, parse_qsl from lib.piped_api import PipedClient from datetime import datetime import xbmcgui import xbmcplugin from xbmcaddon import Addon from xbmcvfs import translatePath # Get the plugin url in plugin:// notation. URL = sys.argv[0] # Get a plugin handle as an integer number. HANDLE = int(sys.argv[1]) # Get addon base path ADDON_PATH = translatePath(Addon().getAddonInfo('path')) ICONS_DIR = os.path.join(ADDON_PATH, 'resources', 'images', 'icons') FANART_DIR = os.path.join(ADDON_PATH, 'resources', 'images', 'fanart') PIPED = PipedClient(base_api_url=xbmcplugin.getSetting(int(sys.argv[1]), "base_api_url")) def get_url(**kwargs): """ Create a URL for calling the plugin recursively from the given set of keyword arguments. :param kwargs: "argument=value" pairs :return: plugin call URL :rtype: str """ return '{}?{}'.format(URL, urlencode(kwargs)) def get_videos(): """ Get the list of videofiles/streams. Here you can insert some code that retrieves the list of video streams in the given section from some site or API. :return: the list of videos from the search results :rtype: list """ query = xbmcgui.Dialog().input("Search Videos") return PIPED.get_search_results(query, "videos")['items'] def list_videos(): """ Create the list of playable videos in the Kodi interface. """ # Set plugin category. It is displayed in some skins as the name # of the current section. xbmcplugin.setPluginCategory(HANDLE, "Search Results") # Set plugin content. It allows Kodi to select appropriate views # for this type of content. xbmcplugin.setContent(HANDLE, 'movies') # Get the list of videos in the category. videos = get_videos() # Iterate through videos. for video in videos: # Create a list item with a text label list_item = xbmcgui.ListItem(label=video['title']) # Set graphics (thumbnail, fanart, banner, poster, landscape etc.) for the list item. # Here we use only poster for simplicity's sake. # In a real-life plugin you may need to set multiple image types. list_item.setArt({'poster': video['thumbnail']}) # Set additional info for the list item via InfoTag. # 'mediatype' is needed for skin to display info for this ListItem correctly. info_tag = list_item.getVideoInfoTag() info_tag.setMediaType('movie') info_tag.setTitle(video['title']) info_tag.setPlot(video['shortDescription']) # info_tag.setYear(video['uploadedDate']) # Set 'IsPlayable' property to 'true'. # This is mandatory for playable items! list_item.setProperty('IsPlayable', 'true') # Create a URL for a plugin recursive call. # Example: plugin://plugin.video.example/?action=play&video=https%3A%2F%2Fia600702.us.archive.org%2F3%2Fitems%2Firon_mask%2Firon_mask_512kb.mp4 # /watch?v= is 9 characters long, strip it vidid = video['url'][9:] url = get_url(action='play', video=vidid) # Add the list item to a virtual Kodi folder. # is_folder = False means that this item won't open any sub-list. is_folder = False # Add our item to the Kodi virtual folder listing. xbmcplugin.addDirectoryItem(HANDLE, url, list_item, is_folder) # Add sort methods for the virtual folder items xbmcplugin.addSortMethod(HANDLE, xbmcplugin.SORT_METHOD_LABEL_IGNORE_THE) xbmcplugin.addSortMethod(HANDLE, xbmcplugin.SORT_METHOD_VIDEO_YEAR) # Finish creating a virtual folder. xbmcplugin.endOfDirectory(HANDLE) def play_video(path): """ Play a video by the provided path. :param path: Fully-qualified video URL :type path: str """ # Create a playable item with a path to play. # offscreen=True means that the list item is not meant for displaying, # only to pass info to the Kodi player play_item = xbmcgui.ListItem(offscreen=True) play_item.setPath(path) # Pass the item to the Kodi player. xbmcplugin.setResolvedUrl(HANDLE, True, listitem=play_item) def play_yt_video(vidid): """ Play a HLS video stream by the provided video ID. :param path: Fully-qualified video URL :type path: str """ # Create a playable item with a path to play. # offscreen=True means that the list item is not meant for displaying, # only to pass info to the Kodi player play_item = xbmcgui.ListItem(offscreen=True) play_item.setPath(PIPED.get_video(vidid).hls) play_item.setProperty('inputstream', 'inputstream.adaptive') play_item.setProperty('inputstream.adaptive.manifest_type', 'hls') # Pass the item to the Kodi player. xbmcplugin.setResolvedUrl(HANDLE, True, listitem=play_item) def router(paramstring): """ Router function that calls other functions depending on the provided paramstring :param paramstring: URL encoded plugin paramstring :type paramstring: str """ # Parse a URL-encoded paramstring to the dictionary of # {: } elements params = dict(parse_qsl(paramstring)) # Check the parameters passed to the plugin if not params: # If the plugin is called from Kodi UI without any parameters, # display the list of video categories list_videos() elif params['action'] == 'play': # Play a video from a provided URL. play_yt_video(params['video']) else: # If the provided paramstring does not contain a supported action # we raise an exception. This helps to catch coding errors, # e.g. typos in action names. raise ValueError(f'Invalid paramstring: {paramstring}!') if __name__ == '__main__': # Call the router function and pass the plugin call parameters to it. # We use string slicing to trim the leading '?' from the plugin call paramstring router(sys.argv[2][1:])