Support live streams

Now the type of a video is defined when trying to play a video so that
live streams (.m3u8) are directly played by Kodi (no download required).

We are able to know if a video is live from the response of the "list"
REST API but it was decided to ignore this information in order to have
a single action to play video (whether it is live or not).
The goal was to keep the API of the add-on as simple as possible so that
externel users only have to call the add-on's API with the ID of a
video, without having to add the type of the video.
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.
This commit is contained in:
Thomas 2021-04-24 14:55:31 +00:00
parent ed39c453e9
commit 4f8af35035
3 changed files with 79 additions and 40 deletions

View File

@ -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
`<kodi_home>/temp/plugin.video.peertube/` (more information on `<kodi_home>`
[here](https://kodi.wiki/view/Kodi_data_folder#Location)).
# Installation and prerequisites

View File

@ -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))

View File

@ -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