diff --git a/README.md b/README.md index 57553c3..4f3fbf8 100644 --- a/README.md +++ b/README.md @@ -2,21 +2,22 @@ A Kodi add-on for watching content hosted on [PeerTube](http://joinpeertube.org/ This add-on is under development so only basic features work, and you're welcome to improve it. -See [contribution guidelines](contributing.md) and -[pending issues](https://framagit.org/StCyr/plugin.video.peertube/-/issues) to -start. +If you want to contribute, please start with the +[contribution guidelines](contributing.md) and the +[pending issues](https://framagit.org/StCyr/plugin.video.peertube/-/issues). [[_TOC_]] # Features -* Browse all videos on a PeerTube instance +* Play videos (including live videos) +* Browse the videos on a PeerTube instance * Search for videos on a PeerTube instance * Select the PeerTube instance to use -* Select the preferred video resolution: the plugin will try to play the select - video resolution. +* Select the preferred video resolution: the plugin will try to play the + preferred video resolution. If it's not available, it will play the lower resolution that is the closest - from your preference. + to your preference. If not available, it will play the higher resolution that is the closest from your preference. @@ -24,9 +25,10 @@ start. * Preferred PeerTube instance * Preferred video resolution -* Number of videos to display per page -* Sort method to be used when listing videos (Currently, only 'views' and - 'likes') +* Number of items to display per page +* Value used to sort items when listing/searching videos: + * `views`: sort by number of views (ascending only) + * `likes`: sort by number of likes (ascending only) * Select the filter to use when browsing the videos on an instance: * local will only display the videos which are local to the selected instance * all-local will only display the videos which are local to the selected @@ -53,8 +55,10 @@ the add-on with: * This add-on doesn't support Webtorrent yet. So, it cannot download/share from/to regular PeerTube clients. -* The add-on doesn't delete the downloaded files at the moment. So, it may fill - up your disk. +* The add-on doesn't delete the downloaded files at the moment so it may fill + up your disk. You may delete manually the downloaded files in the folder + `/temp/plugin.video.peertube/` (more information on `` + [here](https://kodi.wiki/view/Kodi_data_folder#Location)). # Installation and prerequisites diff --git a/resources/lib/addon.py b/resources/lib/addon.py index 3b23eb5..c8a7e46 100644 --- a/resources/lib/addon.py +++ b/resources/lib/addon.py @@ -161,6 +161,14 @@ class PeerTubeAddon(): data["thumbnailPath"]), aired=data["publishedAt"] ) + # Note: the type of video (live or not) is available in "response" + # but this information is ignored here so that the "play_video" + # action is the same whatever the type of the video. The goal is to + # allow external users of the API to play a video only with its ID + # without knowing its type. + # The information about the type of the video will anyway be + # available in the response used to get the URL of a video so this + # solution does not impact the performance. yield video_info else: @@ -202,13 +210,18 @@ class PeerTubeAddon(): return next_page_item def _get_video_url(self, video_id, instance=None): - """Find the URL of the video with the best possible quality matching + """Return the URL of a video and its type (live or not) + + Find the URL of the video with the best possible quality matching user's preferences. + The information whether the video is live or not will also be returned. :param str video_id: ID of the torrent linked with the video :param str instance: PeerTube instance hosting the video (optional) - :return: URL of the video containing the resolution - :rtype: str + :return: a boolean indicating if the video is a live stream and the URL + of the video (containing the resolution for non-live videos) as a + string + :rtype: tuple """ # Retrieve the information about the video including the different # resolutions available @@ -218,16 +231,24 @@ class PeerTubeAddon(): current_resolution = 0 higher_resolution = -1 url = "" + is_live = False for video in video_files: # Get the resolution - resolution = video["resolution"] + resolution = video.get("resolution") + if resolution is None: + # If there is no resolution in the dict, then the video is a + # live stream: no need to find the best resolution as there is + # only 1 URL in this case + url = video["url"] + is_live = True + return (is_live, url) if resolution == self.preferred_resolution: # Stop directly when we find the exact same resolution as the # user's preferred one kodi.debug("Found video with preferred resolution ({})" .format(self.preferred_resolution)) url = video["url"] - break + return (is_live, url) elif (resolution < self.preferred_resolution and resolution > current_resolution): # Otherwise, try to find the best one just below the user's @@ -255,7 +276,7 @@ class PeerTubeAddon(): .format(higher_resolution)) url = backup_url - return url + return (is_live, url) def _home_page(self): """Display the items of the home page of the add-on""" @@ -417,12 +438,18 @@ class PeerTubeAddon(): elif action == "play_video": # This action comes with the id of the video to play as # parameter. The instance may also be in the parameters. Use - # these parameters to retrieve the complete URL (containing the - # resolution). - url = self._get_video_url(instance=params.get("instance"), - video_id=params.get("id")) - # Play the video using the URL - self._play_video(url) + # these parameters to retrieve the complete URL of the video + # (containing the resolution) and the type of the video (live + # or not). + is_live, url = self._get_video_url( + instance=params.get("instance"),video_id=params.get("id")) + + # Play the video (Kodi can play live videos (.m3u8) out of the + # box whereas torrents must first be downloaded) + if is_live: + kodi.play(url) + else: + self._play_video(url) elif action == "select_instance": # Set the selected instance as the preferred instance self._select_instance(params["url"]) @@ -436,6 +463,6 @@ class PeerTubeAddon(): kodi.open_dialog( title="Error: libtorrent could not be imported", message="You can still browse and search videos but you" - " will not be able to play them.\n" - "Please follow the instructions at {}" + " will not be able to play them (except live" + " videos).\nPlease follow the instructions at {}" .format(self.HELP_URL)) diff --git a/resources/lib/peertube.py b/resources/lib/peertube.py index ebf9413..5b4265d 100644 --- a/resources/lib/peertube.py +++ b/resources/lib/peertube.py @@ -121,7 +121,8 @@ class PeerTube: """Return the URLs of a video PeerTube creates 1 URL for each resolution of a video so this method - returns a list of URL/resolution pairs. + returns a list of URL/resolution pairs. In the case of a live video, + only an URL will be returned (no resolution). :param str video_id: ID or UUID of the video :param str instance: URL of the instance hosting the video. The @@ -134,21 +135,28 @@ class PeerTube: url="videos/{}".format(video_id), instance=instance) - # Depending if WebTorrent is enabled or not, the files corresponding to - # different resolutions available for a video may be stored in "files" - # or "streamingPlaylists[].files". Note that "files" will always exist - # in the response but may be empty. - if len(metadata["files"]) != 0: - files = metadata["files"] - else: - files = metadata["streamingPlaylists"][0]["files"] - - for file in files: + if metadata["isLive"]: + # When the video is a live, yield the unique playlist URL (there is + # no resolution in this case) yield { - "resolution": int(file["resolution"]["id"]), - "url": file["torrentUrl"], - "is_live": False + "url": metadata['streamingPlaylists'][0]['playlistUrl'], } + else: + # For non live videos, the files corresponding to different + # resolutions available for a video may be stored in "files" or + # "streamingPlaylists[].files" depending if WebTorrent is enabled + # or not. Note that "files" will always exist in the response but + # may be empty so len() must be used. + if len(metadata["files"]) != 0: + files = metadata["files"] + else: + files = metadata["streamingPlaylists"][0]["files"] + + for file in files: + yield { + "resolution": int(file["resolution"]["id"]), + "url": file["torrentUrl"], + } def list_videos(self, start): """List the videos in the instance