mirror of
https://framagit.org/StCyr/plugin.video.peertube
synced 2025-06-05 22:09:16 +02:00
Replace single quotes with double quotes
Make this a coding rule so that apostrophes don't need to be escaped in strings (they are common in English and other languages).
This commit is contained in:
@@ -77,13 +77,15 @@ The functions must be sorted alphabetically to make the maintenance easier.
|
|||||||
|
|
||||||
## Coding style
|
## Coding style
|
||||||
|
|
||||||
The code is still based on the design of the alpha version so the coding style
|
Here are the rules to follow when modifying the code:
|
||||||
is not mature yet.
|
|
||||||
A redesign is planned but until then please:
|
|
||||||
* document the usage of functions following [Sphinx
|
* document the usage of functions following [Sphinx
|
||||||
format](https://www.sphinx-doc.org/en/master/usage/restructuredtext/domains.html#python-signatures)
|
format](https://www.sphinx-doc.org/en/master/usage/restructuredtext/domains.html#python-signatures)
|
||||||
|
* use double quotes instead of single quotes when defining strings (to avoid
|
||||||
|
escaping apostrophes which are common characters in English and other
|
||||||
|
languages)
|
||||||
* follow [PEP 8](https://www.python.org/dev/peps/pep-0008/) conventions. The
|
* follow [PEP 8](https://www.python.org/dev/peps/pep-0008/) conventions. The
|
||||||
compliance can be checked with [pylint](https://www.pylint.org/) and the
|
compliance is checked in the `quality` job (more details are available in the
|
||||||
|
[CI file](.gitlab-ci.yml)). Pylint can also be run locally with the
|
||||||
following commands:
|
following commands:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
@@ -91,11 +93,7 @@ python3 -m pip install -r misc/python_requirements.txt
|
|||||||
python3 -m pylint --rcfile=misc/pylint-rcfile.txt
|
python3 -m pylint --rcfile=misc/pylint-rcfile.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
The pylint violations are also checked in the `quality` job.
|
Note: pylint is run with python3 to have latest features even though the add-on
|
||||||
|
|
||||||
More details are available in the [CI file](.gitlab-ci.yml).
|
|
||||||
|
|
||||||
Note: pylint is run with python3 to have latest features whereas the add-on
|
|
||||||
only supports Kodi v18 Leia (which uses python2)
|
only supports Kodi v18 Leia (which uses python2)
|
||||||
|
|
||||||
## How to release a new version of this add-on
|
## How to release a new version of this add-on
|
||||||
|
202
peertube.py
202
peertube.py
@@ -36,7 +36,7 @@ class PeertubeAddon():
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# URL of the page which explains how to install libtorrent
|
# URL of the page which explains how to install libtorrent
|
||||||
HELP_URL = 'https://link.infini.fr/libtorrent-peertube-kodi'
|
HELP_URL = "https://link.infini.fr/libtorrent-peertube-kodi"
|
||||||
|
|
||||||
def __init__(self, plugin, plugin_id):
|
def __init__(self, plugin, plugin_id):
|
||||||
"""
|
"""
|
||||||
@@ -49,19 +49,19 @@ class PeertubeAddon():
|
|||||||
self.plugin_id = plugin_id
|
self.plugin_id = plugin_id
|
||||||
|
|
||||||
# Select preferred instance by default
|
# Select preferred instance by default
|
||||||
self.selected_inst ='https://{}'\
|
self.selected_inst ="https://{}"\
|
||||||
.format(get_setting('preferred_instance'))
|
.format(get_setting("preferred_instance"))
|
||||||
|
|
||||||
# Get the number of videos to show per page
|
# Get the number of videos to show per page
|
||||||
self.items_per_page = int(get_setting('items_per_page'))
|
self.items_per_page = int(get_setting("items_per_page"))
|
||||||
|
|
||||||
# Get the preferred resolution for video
|
# Get the preferred resolution for video
|
||||||
self.preferred_resolution = get_setting('preferred_resolution')
|
self.preferred_resolution = get_setting("preferred_resolution")
|
||||||
|
|
||||||
# Nothing to play at initialisation
|
# Nothing to play at initialisation
|
||||||
self.play = 0
|
self.play = 0
|
||||||
self.torrent_name = ''
|
self.torrent_name = ""
|
||||||
self.torrent_f = ''
|
self.torrent_f = ""
|
||||||
|
|
||||||
# Check whether libtorrent could be imported by the service. The value
|
# Check whether libtorrent could be imported by the service. The value
|
||||||
# of the associated property is retrieved only once and stored in an
|
# of the associated property is retrieved only once and stored in an
|
||||||
@@ -69,14 +69,14 @@ class PeertubeAddon():
|
|||||||
# of the service (we assume it is not possible to start the add-on
|
# of the service (we assume it is not possible to start the add-on
|
||||||
# before the service)
|
# before the service)
|
||||||
self.libtorrent_imported = \
|
self.libtorrent_imported = \
|
||||||
get_property('libtorrent_imported') == 'True'
|
get_property("libtorrent_imported") == "True"
|
||||||
|
|
||||||
# Create a PeerTube object to send requests: settings which are used
|
# Create a PeerTube object to send requests: settings which are used
|
||||||
# only by this object are directly retrieved from the settings
|
# only by this object are directly retrieved from the settings
|
||||||
self.peertube = PeerTube(instance=self.selected_inst,
|
self.peertube = PeerTube(instance=self.selected_inst,
|
||||||
count=self.items_per_page,
|
count=self.items_per_page,
|
||||||
sort=get_setting('video_sort_method'),
|
sort=get_setting("video_sort_method"),
|
||||||
video_filter=get_setting('video_filter'))
|
video_filter=get_setting("video_filter"))
|
||||||
|
|
||||||
def create_list(self, lst, data_type, start):
|
def create_list(self, lst, data_type, start):
|
||||||
"""
|
"""
|
||||||
@@ -86,68 +86,68 @@ class PeertubeAddon():
|
|||||||
"""
|
"""
|
||||||
# Create a list for our items.
|
# Create a list for our items.
|
||||||
listing = []
|
listing = []
|
||||||
for data in lst['data']:
|
for data in lst["data"]:
|
||||||
|
|
||||||
# Create a list item with a text label
|
# Create a list item with a text label
|
||||||
list_item = xbmcgui.ListItem(label=data['name'])
|
list_item = xbmcgui.ListItem(label=data["name"])
|
||||||
|
|
||||||
if data_type == 'videos':
|
if data_type == "videos":
|
||||||
# Add thumbnail
|
# Add thumbnail
|
||||||
list_item.setArt({
|
list_item.setArt({
|
||||||
'thumb': '{0}/{1}'.format(self.selected_inst,
|
"thumb": "{0}/{1}".format(self.selected_inst,
|
||||||
data['thumbnailPath'])})
|
data["thumbnailPath"])})
|
||||||
|
|
||||||
# Set a fanart image for the list item.
|
# Set a fanart image for the list item.
|
||||||
# list_item.setProperty('fanart_image', data['thumb'])
|
# list_item.setProperty("fanart_image", data["thumb"])
|
||||||
|
|
||||||
# Compute media info from item's metadata
|
# Compute media info from item's metadata
|
||||||
info = {'title': data['name'],
|
info = {"title": data["name"],
|
||||||
'playcount': data['views'],
|
"playcount": data["views"],
|
||||||
'plotoutline': data['description'],
|
"plotoutline": data["description"],
|
||||||
'duration': data['duration']
|
"duration": data["duration"]
|
||||||
}
|
}
|
||||||
|
|
||||||
# For videos, add a rating based on likes and dislikes
|
# For videos, add a rating based on likes and dislikes
|
||||||
if data['likes'] > 0 or data['dislikes'] > 0:
|
if data["likes"] > 0 or data["dislikes"] > 0:
|
||||||
info['rating'] = data['likes'] / (
|
info["rating"] = data["likes"] / (
|
||||||
data['likes'] + data['dislikes'])
|
data["likes"] + data["dislikes"])
|
||||||
|
|
||||||
# Set additional info for the list item.
|
# Set additional info for the list item.
|
||||||
list_item.setInfo('video', info)
|
list_item.setInfo("video", info)
|
||||||
|
|
||||||
# Videos are playable
|
# Videos are playable
|
||||||
list_item.setProperty('IsPlayable', 'true')
|
list_item.setProperty("IsPlayable", "true")
|
||||||
|
|
||||||
# Build the Kodi URL to play the associated video only with the
|
# Build the Kodi URL to play the associated video only with the
|
||||||
# id of the video. The instance is omitted because the
|
# id of the video. The instance is omitted because the
|
||||||
# currently selected instance will be used automatically.
|
# currently selected instance will be used automatically.
|
||||||
url = self.build_kodi_url({
|
url = self.build_kodi_url({
|
||||||
'action': 'play_video',
|
"action": "play_video",
|
||||||
'id': data['uuid']
|
"id": data["uuid"]
|
||||||
})
|
})
|
||||||
|
|
||||||
elif data_type == 'instances':
|
elif data_type == "instances":
|
||||||
# TODO: Add a context menu to select instance as preferred
|
# TODO: Add a context menu to select instance as preferred
|
||||||
# Instances are not playable
|
# Instances are not playable
|
||||||
list_item.setProperty('IsPlayable', 'false')
|
list_item.setProperty("IsPlayable", "false")
|
||||||
|
|
||||||
# Set URL to select this instance
|
# Set URL to select this instance
|
||||||
url = self.build_kodi_url({
|
url = self.build_kodi_url({
|
||||||
'action': 'select_instance',
|
"action": "select_instance",
|
||||||
'url': data['host']
|
"url": data["host"]
|
||||||
})
|
})
|
||||||
|
|
||||||
# Add our item to the listing as a 3-element tuple.
|
# Add our item to the listing as a 3-element tuple.
|
||||||
listing.append((url, list_item, False))
|
listing.append((url, list_item, False))
|
||||||
|
|
||||||
# Add a 'Next page' button when there are more data to show
|
# Add a "Next page" button when there are more data to show
|
||||||
start = int(start) + self.items_per_page
|
start = int(start) + self.items_per_page
|
||||||
if lst['total'] > start:
|
if lst["total"] > start:
|
||||||
list_item = xbmcgui.ListItem(label='Next page ({0})'
|
list_item = xbmcgui.ListItem(label="Next page ({0})"
|
||||||
.format(start/self.items_per_page))
|
.format(start/self.items_per_page))
|
||||||
url = self.build_kodi_url({
|
url = self.build_kodi_url({
|
||||||
'action': 'browse_{0}'.format(data_type),
|
"action": "browse_{0}".format(data_type),
|
||||||
'start': start})
|
"start": start})
|
||||||
listing.append((url, list_item, True))
|
listing.append((url, list_item, True))
|
||||||
|
|
||||||
return listing
|
return listing
|
||||||
@@ -168,8 +168,8 @@ class PeertubeAddon():
|
|||||||
else:
|
else:
|
||||||
# If an instance was provided (external call), ensure the URL is
|
# If an instance was provided (external call), ensure the URL is
|
||||||
# prefixed with HTTPS
|
# prefixed with HTTPS
|
||||||
if not instance.startswith('https://'):
|
if not instance.startswith("https://"):
|
||||||
instance = 'https://{}'.format(instance)
|
instance = "https://{}".format(instance)
|
||||||
|
|
||||||
# Retrieve the information about the video
|
# Retrieve the information about the video
|
||||||
metadata = self.peertube.get_video(video_id)
|
metadata = self.peertube.get_video(video_id)
|
||||||
@@ -178,46 +178,46 @@ class PeertubeAddon():
|
|||||||
# different resolutions available for a video may be stored in "files"
|
# different resolutions available for a video may be stored in "files"
|
||||||
# or "streamingPlaylists[].files". Note that "files" will always exist
|
# or "streamingPlaylists[].files". Note that "files" will always exist
|
||||||
# in the response but may be empty.
|
# in the response but may be empty.
|
||||||
if len(metadata['files']) != 0:
|
if len(metadata["files"]) != 0:
|
||||||
files = metadata['files']
|
files = metadata["files"]
|
||||||
else:
|
else:
|
||||||
files = metadata['streamingPlaylists'][0]['files']
|
files = metadata["streamingPlaylists"][0]["files"]
|
||||||
|
|
||||||
debug('Looking for the best resolution matching the user preferences')
|
debug("Looking for the best resolution matching the user preferences")
|
||||||
|
|
||||||
current_res = 0
|
current_res = 0
|
||||||
higher_res = -1
|
higher_res = -1
|
||||||
torrent_url = ''
|
torrent_url = ""
|
||||||
|
|
||||||
for f in files:
|
for f in files:
|
||||||
# Get the resolution
|
# Get the resolution
|
||||||
res = f['resolution']['id']
|
res = f["resolution"]["id"]
|
||||||
if res == self.preferred_resolution:
|
if res == self.preferred_resolution:
|
||||||
# Stop directly when we find the exact same resolution as the
|
# Stop directly when we find the exact same resolution as the
|
||||||
# user's preferred one
|
# user's preferred one
|
||||||
debug('Found video with preferred resolution')
|
debug("Found video with preferred resolution")
|
||||||
torrent_url = f['torrentUrl']
|
torrent_url = f["torrentUrl"]
|
||||||
break
|
break
|
||||||
elif res < self.preferred_resolution and res > current_res:
|
elif res < self.preferred_resolution and res > current_res:
|
||||||
# Otherwise, try to find the best one just below the user's
|
# Otherwise, try to find the best one just below the user's
|
||||||
# preferred one
|
# preferred one
|
||||||
debug('Found video with good lower resolution ({0})'
|
debug("Found video with good lower resolution ({0})"
|
||||||
.format(f['resolution']['label']))
|
.format(f["resolution"]["label"]))
|
||||||
torrent_url = f['torrentUrl']
|
torrent_url = f["torrentUrl"]
|
||||||
current_res = res
|
current_res = res
|
||||||
elif (res > self.preferred_resolution
|
elif (res > self.preferred_resolution
|
||||||
and (res < higher_res or higher_res == -1)):
|
and (res < higher_res or higher_res == -1)):
|
||||||
# In the worst case, we'll take the one just above the user's
|
# In the worst case, we'll take the one just above the user's
|
||||||
# preferred one
|
# preferred one
|
||||||
debug('Saving video with higher resolution ({0}) as a possible'
|
debug("Saving video with higher resolution ({0}) as a possible"
|
||||||
' alternative'.format(f['resolution']['label']))
|
" alternative".format(f["resolution"]["label"]))
|
||||||
backup_url = f['torrentUrl']
|
backup_url = f["torrentUrl"]
|
||||||
higher_res = res
|
higher_res = res
|
||||||
|
|
||||||
# When we didn't find a resolution equal or lower than the user's
|
# When we didn't find a resolution equal or lower than the user's
|
||||||
# preferred one, use the resolution just above the preferred one
|
# preferred one, use the resolution just above the preferred one
|
||||||
if not torrent_url:
|
if not torrent_url:
|
||||||
debug('Using video with higher resolution as alternative')
|
debug("Using video with higher resolution as alternative")
|
||||||
torrent_url = backup_url
|
torrent_url = backup_url
|
||||||
|
|
||||||
return torrent_url
|
return torrent_url
|
||||||
@@ -232,7 +232,7 @@ class PeertubeAddon():
|
|||||||
|
|
||||||
# Ask the user which keywords must be searched for
|
# Ask the user which keywords must be searched for
|
||||||
keywords = xbmcgui.Dialog().input(
|
keywords = xbmcgui.Dialog().input(
|
||||||
heading='Search videos on {}'.format(self.selected_inst),
|
heading="Search videos on {}".format(self.selected_inst),
|
||||||
type=xbmcgui.INPUT_ALPHANUM)
|
type=xbmcgui.INPUT_ALPHANUM)
|
||||||
|
|
||||||
# Go back to main menu when user cancels
|
# Go back to main menu when user cancels
|
||||||
@@ -244,12 +244,12 @@ class PeertubeAddon():
|
|||||||
|
|
||||||
# Exit directly when no result is found
|
# Exit directly when no result is found
|
||||||
if not results:
|
if not results:
|
||||||
notif_warning(title='No videos found',
|
notif_warning(title="No videos found",
|
||||||
message='No videos found matching the query.')
|
message="No videos found matching the query.")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Create array of xmbcgui.ListItem's
|
# Create array of xmbcgui.ListItem's
|
||||||
listing = self.create_list(results, 'videos', start)
|
listing = self.create_list(results, "videos", start)
|
||||||
|
|
||||||
# Add our listing to Kodi.
|
# Add our listing to Kodi.
|
||||||
xbmcplugin.addDirectoryItems(self.plugin_id, listing, len(listing))
|
xbmcplugin.addDirectoryItems(self.plugin_id, listing, len(listing))
|
||||||
@@ -267,7 +267,7 @@ class PeertubeAddon():
|
|||||||
results = self.peertube.list_videos(start)
|
results = self.peertube.list_videos(start)
|
||||||
|
|
||||||
# Create array of xmbcgui.ListItem's
|
# Create array of xmbcgui.ListItem's
|
||||||
listing = self.create_list(results, 'videos', start)
|
listing = self.create_list(results, "videos", start)
|
||||||
|
|
||||||
# Add our listing to Kodi.
|
# Add our listing to Kodi.
|
||||||
xbmcplugin.addDirectoryItems(self.plugin_id, listing, len(listing))
|
xbmcplugin.addDirectoryItems(self.plugin_id, listing, len(listing))
|
||||||
@@ -283,7 +283,7 @@ class PeertubeAddon():
|
|||||||
results = list_instances(start)
|
results = list_instances(start)
|
||||||
|
|
||||||
# Create array of xmbcgui.ListItem's
|
# Create array of xmbcgui.ListItem's
|
||||||
listing = self.create_list(results, 'instances', start)
|
listing = self.create_list(results, "instances", start)
|
||||||
|
|
||||||
# Add our listing to Kodi.
|
# Add our listing to Kodi.
|
||||||
xbmcplugin.addDirectoryItems(self.plugin_id, listing, len(listing))
|
xbmcplugin.addDirectoryItems(self.plugin_id, listing, len(listing))
|
||||||
@@ -297,9 +297,9 @@ class PeertubeAddon():
|
|||||||
:param data: dict
|
:param data: dict
|
||||||
"""
|
"""
|
||||||
|
|
||||||
debug('Received metadata_downloaded signal, will start playing media')
|
debug("Received metadata_downloaded signal, will start playing media")
|
||||||
self.play = 1
|
self.play = 1
|
||||||
self.torrent_f = data['file']
|
self.torrent_f = data["file"]
|
||||||
|
|
||||||
def play_video(self, torrent_url):
|
def play_video(self, torrent_url):
|
||||||
"""
|
"""
|
||||||
@@ -309,21 +309,21 @@ class PeertubeAddon():
|
|||||||
# If libtorrent could not be imported, display a message and do not try
|
# If libtorrent could not be imported, display a message and do not try
|
||||||
# download nor play the video as it will fail.
|
# download nor play the video as it will fail.
|
||||||
if not self.libtorrent_imported:
|
if not self.libtorrent_imported:
|
||||||
open_dialog(title='Error: libtorrent could not be imported',
|
open_dialog(title="Error: libtorrent could not be imported",
|
||||||
message='PeerTube cannot play videos without'
|
message="PeerTube cannot play videos without"
|
||||||
' libtorrent.\nPlease follow the instructions'
|
" libtorrent.\nPlease follow the instructions"
|
||||||
' at {}'.format(self.HELP_URL))
|
" at {}".format(self.HELP_URL))
|
||||||
return
|
return
|
||||||
|
|
||||||
debug('Starting torrent download ({0})'.format(torrent_url))
|
debug("Starting torrent download ({0})".format(torrent_url))
|
||||||
|
|
||||||
# Start a downloader thread
|
# Start a downloader thread
|
||||||
AddonSignals.sendSignal('start_download', {'url': torrent_url})
|
AddonSignals.sendSignal("start_download", {"url": torrent_url})
|
||||||
|
|
||||||
# Wait until the PeerTubeDownloader has downloaded all the torrent's
|
# Wait until the PeerTubeDownloader has downloaded all the torrent's
|
||||||
# metadata
|
# metadata
|
||||||
AddonSignals.registerSlot('plugin.video.peertube',
|
AddonSignals.registerSlot("plugin.video.peertube",
|
||||||
'metadata_downloaded',
|
"metadata_downloaded",
|
||||||
self.play_video_continue)
|
self.play_video_continue)
|
||||||
timeout = 0
|
timeout = 0
|
||||||
while self.play == 0 and timeout < 10:
|
while self.play == 0 and timeout < 10:
|
||||||
@@ -332,35 +332,35 @@ class PeertubeAddon():
|
|||||||
|
|
||||||
# Abort in case of timeout
|
# Abort in case of timeout
|
||||||
if timeout == 10:
|
if timeout == 10:
|
||||||
notif_error(title='Download timeout',
|
notif_error(title="Download timeout",
|
||||||
message='Timeout fetching {}'.format(torrent_url))
|
message="Timeout fetching {}".format(torrent_url))
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
# Wait a little before starting playing the torrent
|
# Wait a little before starting playing the torrent
|
||||||
xbmc.sleep(3000)
|
xbmc.sleep(3000)
|
||||||
|
|
||||||
# Pass the item to the Kodi player for actual playback.
|
# Pass the item to the Kodi player for actual playback.
|
||||||
debug('Starting video playback ({0})'.format(torrent_url))
|
debug("Starting video playback ({0})".format(torrent_url))
|
||||||
play_item = xbmcgui.ListItem(path=self.torrent_f)
|
play_item = xbmcgui.ListItem(path=self.torrent_f)
|
||||||
xbmcplugin.setResolvedUrl(self.plugin_id, True, listitem=play_item)
|
xbmcplugin.setResolvedUrl(self.plugin_id, True, listitem=play_item)
|
||||||
|
|
||||||
def select_instance(self, instance):
|
def select_instance(self, instance):
|
||||||
"""
|
"""
|
||||||
Change currently selected instance to 'instance' parameter
|
Change currently selected instance to "instance" parameter
|
||||||
:param instance: str
|
:param instance: str
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Update the object attribute even though it is not used currently but
|
# Update the object attribute even though it is not used currently but
|
||||||
# it may be useful in case reuselanguageinvoker is enabled.
|
# it may be useful in case reuselanguageinvoker is enabled.
|
||||||
self.selected_inst = 'https://{}'.format(instance)
|
self.selected_inst = "https://{}".format(instance)
|
||||||
|
|
||||||
# Update the preferred instance in the settings so that this choice is
|
# Update the preferred instance in the settings so that this choice is
|
||||||
# reused on the next run and the next call of the add-on
|
# reused on the next run and the next call of the add-on
|
||||||
set_setting('preferred_instance', instance)
|
set_setting("preferred_instance", instance)
|
||||||
|
|
||||||
# Notify the user and log the event
|
# Notify the user and log the event
|
||||||
message = '{0} is now the selected instance'.format(self.selected_inst)
|
message = "{0} is now the selected instance".format(self.selected_inst)
|
||||||
notif_info(title='Current instance changed',
|
notif_info(title="Current instance changed",
|
||||||
message=message)
|
message=message)
|
||||||
debug(message)
|
debug(message)
|
||||||
|
|
||||||
@@ -371,7 +371,7 @@ class PeertubeAddon():
|
|||||||
encoded in the URL
|
encoded in the URL
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return '{0}?{1}'.format(self.plugin_url, urlencode(parameters))
|
return "{0}?{1}".format(self.plugin_url, urlencode(parameters))
|
||||||
|
|
||||||
def main_menu(self):
|
def main_menu(self):
|
||||||
"""
|
"""
|
||||||
@@ -382,18 +382,18 @@ class PeertubeAddon():
|
|||||||
listing = []
|
listing = []
|
||||||
|
|
||||||
# 1st menu entry
|
# 1st menu entry
|
||||||
list_item = xbmcgui.ListItem(label='Browse selected instance')
|
list_item = xbmcgui.ListItem(label="Browse selected instance")
|
||||||
url = self.build_kodi_url({'action': 'browse_videos', 'start': 0})
|
url = self.build_kodi_url({"action": "browse_videos", "start": 0})
|
||||||
listing.append((url, list_item, True))
|
listing.append((url, list_item, True))
|
||||||
|
|
||||||
# 2nd menu entry
|
# 2nd menu entry
|
||||||
list_item = xbmcgui.ListItem(label='Search on selected instance')
|
list_item = xbmcgui.ListItem(label="Search on selected instance")
|
||||||
url = self.build_kodi_url({'action': 'search_videos', 'start': 0})
|
url = self.build_kodi_url({"action": "search_videos", "start": 0})
|
||||||
listing.append((url, list_item, True))
|
listing.append((url, list_item, True))
|
||||||
|
|
||||||
# 3rd menu entry
|
# 3rd menu entry
|
||||||
list_item = xbmcgui.ListItem(label='Select other instance')
|
list_item = xbmcgui.ListItem(label="Select other instance")
|
||||||
url = self.build_kodi_url({'action': 'browse_instances', 'start': 0})
|
url = self.build_kodi_url({"action": "browse_instances", "start": 0})
|
||||||
listing.append((url, list_item, True))
|
listing.append((url, list_item, True))
|
||||||
|
|
||||||
# Add our listing to Kodi.
|
# Add our listing to Kodi.
|
||||||
@@ -415,27 +415,27 @@ class PeertubeAddon():
|
|||||||
|
|
||||||
# Check the parameters passed to the plugin
|
# Check the parameters passed to the plugin
|
||||||
if params:
|
if params:
|
||||||
action = params['action']
|
action = params["action"]
|
||||||
if action == 'browse_videos':
|
if action == "browse_videos":
|
||||||
# Browse videos on selected instance
|
# Browse videos on selected instance
|
||||||
self.browse_videos(params['start'])
|
self.browse_videos(params["start"])
|
||||||
elif action == 'search_videos':
|
elif action == "search_videos":
|
||||||
# Search for videos on selected instance
|
# Search for videos on selected instance
|
||||||
self.search_videos(params['start'])
|
self.search_videos(params["start"])
|
||||||
elif action == 'browse_instances':
|
elif action == "browse_instances":
|
||||||
# Browse peerTube instances
|
# Browse peerTube instances
|
||||||
self.browse_instances(params['start'])
|
self.browse_instances(params["start"])
|
||||||
elif action == 'play_video':
|
elif action == "play_video":
|
||||||
# This action comes with the id of the video to play as
|
# This action comes with the id of the video to play as
|
||||||
# parameter. The instance may also be in the parameters. Use
|
# parameter. The instance may also be in the parameters. Use
|
||||||
# these parameters to retrieve the complete URL (containing the
|
# these parameters to retrieve the complete URL (containing the
|
||||||
# resolution).
|
# resolution).
|
||||||
url = self.get_video_url(instance=params.get('instance'),
|
url = self.get_video_url(instance=params.get("instance"),
|
||||||
video_id=params.get('id'))
|
video_id=params.get("id"))
|
||||||
# Play the video using the URL
|
# Play the video using the URL
|
||||||
self.play_video(url)
|
self.play_video(url)
|
||||||
elif action == 'select_instance':
|
elif action == "select_instance":
|
||||||
self.select_instance(params['url'])
|
self.select_instance(params["url"])
|
||||||
else:
|
else:
|
||||||
# Display the addon's main menu when the plugin is called from
|
# Display the addon's main menu when the plugin is called from
|
||||||
# Kodi UI without any parameters
|
# Kodi UI without any parameters
|
||||||
@@ -443,13 +443,13 @@ class PeertubeAddon():
|
|||||||
|
|
||||||
# Display a warning if libtorrent could not be imported
|
# Display a warning if libtorrent could not be imported
|
||||||
if not self.libtorrent_imported:
|
if not self.libtorrent_imported:
|
||||||
open_dialog(title='Error: libtorrent could not be imported',
|
open_dialog(title="Error: libtorrent could not be imported",
|
||||||
message='You can still browse and search videos'
|
message="You can still browse and search videos"
|
||||||
' but you will not be able to play them.\n'
|
" but you will not be able to play them.\n"
|
||||||
'Please follow the instructions at {}'
|
"Please follow the instructions at {}"
|
||||||
.format(self.HELP_URL))
|
.format(self.HELP_URL))
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
|
|
||||||
# Initialise addon
|
# Initialise addon
|
||||||
addon = PeertubeAddon(sys.argv[0], int(sys.argv[1]))
|
addon = PeertubeAddon(sys.argv[0], int(sys.argv[1]))
|
||||||
|
@@ -19,7 +19,7 @@ def debug(message):
|
|||||||
(the name is hard-coded to avoid calling xbmcaddon each time since the name
|
(the name is hard-coded to avoid calling xbmcaddon each time since the name
|
||||||
should not change)
|
should not change)
|
||||||
"""
|
"""
|
||||||
xbmc.log('[PeerTube] {}'.format(message), xbmc.LOGDEBUG)
|
xbmc.log("[PeerTube] {}".format(message), xbmc.LOGDEBUG)
|
||||||
|
|
||||||
def get_property(name):
|
def get_property(name):
|
||||||
"""Retrieve the value of a window property related to the add-on
|
"""Retrieve the value of a window property related to the add-on
|
||||||
@@ -29,7 +29,7 @@ def get_property(name):
|
|||||||
:return: Value of the window property
|
:return: Value of the window property
|
||||||
:rtype: str
|
:rtype: str
|
||||||
"""
|
"""
|
||||||
return xbmcgui.Window(10000).getProperty('peertube_{}'.format(name))
|
return xbmcgui.Window(10000).getProperty("peertube_{}".format(name))
|
||||||
|
|
||||||
def get_setting(setting_name):
|
def get_setting(setting_name):
|
||||||
"""Retrieve the value of a setting
|
"""Retrieve the value of a setting
|
||||||
@@ -85,7 +85,7 @@ def set_property(name, value):
|
|||||||
actual name of the property is prefixed with "peertube_")
|
actual name of the property is prefixed with "peertube_")
|
||||||
:param str value: New value of the property
|
:param str value: New value of the property
|
||||||
"""
|
"""
|
||||||
xbmcgui.Window(10000).setProperty('peertube_{}'.format(name), value)
|
xbmcgui.Window(10000).setProperty("peertube_{}".format(name), value)
|
||||||
|
|
||||||
def set_setting(setting_name, setting_value):
|
def set_setting(setting_name, setting_value):
|
||||||
"""Modify the value of a setting
|
"""Modify the value of a setting
|
||||||
|
@@ -34,10 +34,10 @@ class PeerTube:
|
|||||||
|
|
||||||
# The value "video_filter" is directly retrieved from the settings so
|
# The value "video_filter" is directly retrieved from the settings so
|
||||||
# it must be converted into one of the expected values by the REST APIs
|
# it must be converted into one of the expected values by the REST APIs
|
||||||
if 'all-local' in video_filter:
|
if "all-local" in video_filter:
|
||||||
self.filter = 'all-local'
|
self.filter = "all-local"
|
||||||
else:
|
else:
|
||||||
self.filter = 'local'
|
self.filter = "local"
|
||||||
|
|
||||||
def _request(self, method, url, params=None, data=None):
|
def _request(self, method, url, params=None, data=None):
|
||||||
"""Call a REST API on the instance
|
"""Call a REST API on the instance
|
||||||
|
48
service.py
48
service.py
@@ -39,7 +39,7 @@ class PeertubeDownloader(Thread):
|
|||||||
:param str message: Message to log (will be prefixed with the name of
|
:param str message: Message to log (will be prefixed with the name of
|
||||||
the class)
|
the class)
|
||||||
"""
|
"""
|
||||||
debug('PeertubeDownloader: {}'.format(message))
|
debug("PeertubeDownloader: {}".format(message))
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
"""
|
"""
|
||||||
@@ -48,20 +48,20 @@ class PeertubeDownloader(Thread):
|
|||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.debug('Opening BitTorent session')
|
self.debug("Opening BitTorent session")
|
||||||
# Open BitTorrent session
|
# Open BitTorrent session
|
||||||
ses = libtorrent.session()
|
ses = libtorrent.session()
|
||||||
ses.listen_on(6881, 6891)
|
ses.listen_on(6881, 6891)
|
||||||
|
|
||||||
# Add torrent
|
# Add torrent
|
||||||
self.debug('Adding torrent {}'.format(self.torrent))
|
self.debug("Adding torrent {}".format(self.torrent))
|
||||||
h = ses.add_torrent({'url': self.torrent, 'save_path': self.temp_dir})
|
h = ses.add_torrent({"url": self.torrent, "save_path": self.temp_dir})
|
||||||
|
|
||||||
# Set sequential mode to allow watching while downloading
|
# Set sequential mode to allow watching while downloading
|
||||||
h.set_sequential_download(True)
|
h.set_sequential_download(True)
|
||||||
|
|
||||||
# Download torrent
|
# Download torrent
|
||||||
self.debug('Downloading torrent {}'.format(self.torrent))
|
self.debug("Downloading torrent {}".format(self.torrent))
|
||||||
signal_sent = 0
|
signal_sent = 0
|
||||||
while not h.is_seed():
|
while not h.is_seed():
|
||||||
xbmc.sleep(1000)
|
xbmc.sleep(1000)
|
||||||
@@ -69,11 +69,11 @@ class PeertubeDownloader(Thread):
|
|||||||
# Inform addon that all the metadata has been downloaded and that
|
# Inform addon that all the metadata has been downloaded and that
|
||||||
# it may start playing the torrent
|
# it may start playing the torrent
|
||||||
if s.state >=3 and signal_sent == 0:
|
if s.state >=3 and signal_sent == 0:
|
||||||
self.debug('Received all torrent metadata, notifying'
|
self.debug("Received all torrent metadata, notifying"
|
||||||
' PeertubeAddon')
|
" PeertubeAddon")
|
||||||
i = h.torrent_file()
|
i = h.torrent_file()
|
||||||
f = self.temp_dir + i.name()
|
f = self.temp_dir + i.name()
|
||||||
AddonSignals.sendSignal('metadata_downloaded', {'file': f})
|
AddonSignals.sendSignal("metadata_downloaded", {"file": f})
|
||||||
signal_sent = 1
|
signal_sent = 1
|
||||||
|
|
||||||
class PeertubeService():
|
class PeertubeService():
|
||||||
@@ -86,8 +86,8 @@ class PeertubeService():
|
|||||||
PeertubeService initialisation function
|
PeertubeService initialisation function
|
||||||
"""
|
"""
|
||||||
# Create our temporary directory
|
# Create our temporary directory
|
||||||
self.temp = '{}{}'.format(xbmc.translatePath('special://temp'),
|
self.temp = "{}{}".format(xbmc.translatePath("special://temp"),
|
||||||
'plugin.video.peertube/')
|
"plugin.video.peertube/")
|
||||||
if not xbmcvfs.exists(self.temp):
|
if not xbmcvfs.exists(self.temp):
|
||||||
xbmcvfs.mkdir(self.temp)
|
xbmcvfs.mkdir(self.temp)
|
||||||
|
|
||||||
@@ -97,17 +97,17 @@ class PeertubeService():
|
|||||||
:param str message: Message to log (will be prefixed with the name of
|
:param str message: Message to log (will be prefixed with the name of
|
||||||
the class)
|
the class)
|
||||||
"""
|
"""
|
||||||
debug('PeertubeService: {}'.format(message))
|
debug("PeertubeService: {}".format(message))
|
||||||
|
|
||||||
def download_torrent(self, data):
|
def download_torrent(self, data):
|
||||||
"""
|
"""
|
||||||
Start a downloader thread to download torrent specified by data['url']
|
Start a downloader thread to download torrent specified by data["url"]
|
||||||
:param data: dict
|
:param data: dict
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.debug('Received a start_download signal')
|
self.debug("Received a start_download signal")
|
||||||
downloader = PeertubeDownloader(data['url'], self.temp)
|
downloader = PeertubeDownloader(data["url"], self.temp)
|
||||||
downloader.start()
|
downloader.start()
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
@@ -118,25 +118,25 @@ class PeertubeService():
|
|||||||
thread when needed, and exit when Kodi is shutting down.
|
thread when needed, and exit when Kodi is shutting down.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.debug('Starting')
|
self.debug("Starting")
|
||||||
|
|
||||||
# Launch the download_torrent callback function when the
|
# Launch the download_torrent callback function when the
|
||||||
# 'start_download' signal is received
|
# "start_download" signal is received
|
||||||
AddonSignals.registerSlot('plugin.video.peertube',
|
AddonSignals.registerSlot("plugin.video.peertube",
|
||||||
'start_download',
|
"start_download",
|
||||||
self.download_torrent)
|
self.download_torrent)
|
||||||
|
|
||||||
# Monitor Kodi's shutdown signal
|
# Monitor Kodi's shutdown signal
|
||||||
self.debug('Service started, waiting for signals')
|
self.debug("Service started, waiting for signals")
|
||||||
monitor = xbmc.Monitor()
|
monitor = xbmc.Monitor()
|
||||||
while not monitor.abortRequested():
|
while not monitor.abortRequested():
|
||||||
if monitor.waitForAbort(1):
|
if monitor.waitForAbort(1):
|
||||||
# Abort was requested while waiting. We must exit
|
# Abort was requested while waiting. We must exit
|
||||||
# TODO: Clean temporary directory
|
# TODO: Clean temporary directory
|
||||||
self.debug('Exiting')
|
self.debug("Exiting")
|
||||||
break
|
break
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
# Create a PeertubeService instance
|
# Create a PeertubeService instance
|
||||||
service = PeertubeService()
|
service = PeertubeService()
|
||||||
|
|
||||||
@@ -146,12 +146,12 @@ if __name__ == '__main__':
|
|||||||
LIBTORRENT_IMPORTED = True
|
LIBTORRENT_IMPORTED = True
|
||||||
except ImportError as exception:
|
except ImportError as exception:
|
||||||
LIBTORRENT_IMPORTED = False
|
LIBTORRENT_IMPORTED = False
|
||||||
service.debug('The libtorrent library could not be imported because of'
|
service.debug("The libtorrent library could not be imported because of"
|
||||||
' the following error:\n{}'.format(exception))
|
" the following error:\n{}".format(exception))
|
||||||
|
|
||||||
# Save whether libtorrent could be imported as a window property so that
|
# Save whether libtorrent could be imported as a window property so that
|
||||||
# this information can be retrieved by the add-on
|
# this information can be retrieved by the add-on
|
||||||
set_property('libtorrent_imported', str(LIBTORRENT_IMPORTED))
|
set_property("libtorrent_imported", str(LIBTORRENT_IMPORTED))
|
||||||
|
|
||||||
# Start the service
|
# Start the service
|
||||||
service.run()
|
service.run()
|
||||||
|
Reference in New Issue
Block a user