diff --git a/addon.xml b/addon.xml
index c8fb8f1..5ace20c 100644
--- a/addon.xml
+++ b/addon.xml
@@ -5,7 +5,7 @@
-
+
video
diff --git a/contributing.md b/contributing.md
index 36b996d..0a65f2e 100644
--- a/contributing.md
+++ b/contributing.md
@@ -21,24 +21,29 @@ The workflow is the following:
1. if the pipeline passed, the merge request may be merged by one of the
maintainers. Note that the preferred option is to squash commits.
-Note: more information about the pipeline is available in the
+More information about the pipeline is available in the
[CI file](.gitlab-ci.yml).
## Design
+Basically the add-on is composed of:
+* a service which will download the torrent videos in the background
+* classes which will retrieved information and play videos from a PeerTube
+ instance
+
The add-on is based on the following python modules:
| Name | Description |
| ------ | ------ |
| main.py | Entry point of the add-on. |
| service.py | Service responsible for downloading torrent files in the background. |
-| resources/lib/addon.py | It handles the routing and the interaction between the other modules. |
+| resources/lib/addon.py | Handles the routing and the interaction between the other modules. |
| resources/lib/peertube.py | Responsible for interacting with PeerTube. |
| resources/lib/kodi_utils.py | Provides utility functions to interact easily with Kodi. |
### main.py
-The file `peertube.py` is currently being redesigned into the `main` module.
+The file `peertube.py` is the entry point of the add-on.
This module must be as short as possible (15 effective lines of code maximum)
to comply with Kodi add-on development best practices (checked by the
@@ -46,7 +51,13 @@ to comply with Kodi add-on development best practices (checked by the
### service.py
-This module is being redesigned currently.
+Note: the design of this module is still based on the alpha version.
+
+It contains 2 classes:
+* PeertubeService: code of the service which is run by Kodi. It will
+ instantiate `PeertubeDownloader` when the signal to start a download is
+ received from `addon.py`
+* PeertubeDownloader: downloads torrent in an independent thread
This module must be as short as possible (15 effective lines of code maximum)
to comply with Kodi add-on development best practices (checked by the
@@ -54,12 +65,14 @@ to comply with Kodi add-on development best practices (checked by the
### addon.py
-This module does not exist yet.
+This module contains the class `PeerTubeAddon` which is the main class of the
+add-on. It is responsible for calling the other modules and classes to provide
+the features of the add-on.
### peertube.py
This file contains:
-* the class PeerTube which provides simple method to send REST APIs to a
+* the class `PeerTube` which provides simple method to send REST APIs to a
PeerTube instance
* the function `list_instances` which lists the PeerTube instances from
joinpeertube.org. The URL of the API used by this function and the structure
@@ -70,10 +83,25 @@ This file contains:
### kodi_utils.py
-This module only contains functions (no classes) as no common data between them
-was identified.
+This module contains the class `KodiUtils` which provides utility methods
+to the other modules so that the Kodi APIs can be called easily. It imports the
+xbmc file and the other modules should not import any xmbc file.
-The functions must be sorted alphabetically to make the maintenance easier.
+A global instance of the class `KodiUtils` which is called `kodi` is defined in
+this file so that it can be reused easily anywhere in the add-on by simply
+importing this module.
+
+Some important features provided by this module:
+* The methods `get_property` and `set_property` allows to manage data which
+ will remain available when the current call of the add-on ends. It can also
+ be used to share information between the service and the rest of the add-on.
+* There are some helper functions which make the creation of items in Kodi UI
+ easier.
+ `generate_item_info` creates a dict with the required information to create
+ an item: it allows to define only the parameters that are useful for a given
+ items and the method will use a correct value for the other parameters.
+ Then `create_items_in_ui` is called with the information generated by
+ `generate_item_info` to actually create the items in the UI.
## Coding style
@@ -106,7 +134,7 @@ These steps should be followed only by maintainers.
the release process like:
- a bump of the add-on version in `addon.xml` (note that the version
numbering must follow the [semantic versioning](https://semver.org/))
- - the update of the changelog in the `news` tag in `addon.xml` (using
+ - the update of the change log in the `news` tag in `addon.xml` (using
Markdown syntax since it will be re-used automatically in the release
notes)
3. Merge the merge request (maintainers only)
diff --git a/main.py b/main.py
new file mode 100644
index 0000000..f9d7c5d
--- /dev/null
+++ b/main.py
@@ -0,0 +1,29 @@
+# -*- coding: utf-8 -*-
+"""
+ Entry point of the add-on
+
+ Copyright (C) 2018 Cyrille Bollu
+ Copyright (C) 2021 Thomas Bétous
+
+ SPDX-License-Identifier: GPL-3.0-only
+ See LICENSE.txt for more information.
+"""
+import sys
+
+from resources.lib.addon import PeerTubeAddon
+from resources.lib.kodi_utils import kodi
+
+def main(argv):
+ """First function called by the add-on
+
+ This function is created to be able to test the code in this module easily.
+ """
+ # Update the kodi object with the system arguments of this call
+ kodi.update_call_info(argv)
+ # Initialize the main class of the add-on
+ addon = PeerTubeAddon()
+ # Call the router function to execute the requested action
+ addon.router(kodi.get_run_parameters())
+
+if __name__ == "__main__":
+ main(sys.argv)
diff --git a/peertube.py b/peertube.py
deleted file mode 100644
index 8c2a148..0000000
--- a/peertube.py
+++ /dev/null
@@ -1,457 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- A Kodi add-on to play video hosted on the PeerTube service
- (http://joinpeertube.org/)
-
- Copyright (C) 2018 Cyrille Bollu
- Copyright (C) 2021 Thomas Bétous
-
- SPDX-License-Identifier: GPL-3.0-only
- See LICENSE.txt for more information.
-"""
-import sys
-
-try:
- # Python 3.x
- from urllib.parse import parse_qsl
-except ImportError:
- # Python 2.x
- from urlparse import parse_qsl
-
-import AddonSignals # Module exists only in Kodi - pylint: disable=import-error
-from requests.compat import urlencode
-import xbmc # Kodistubs for Leia is not compatible with python3 / pylint: disable=syntax-error
-import xbmcgui # Kodistubs for Leia is not compatible with python3 / pylint: disable=syntax-error
-import xbmcplugin
-
-from resources.lib.kodi_utils import (
- debug, get_property, get_setting, notif_error, notif_info, notif_warning,
- open_dialog, set_setting)
-from resources.lib.peertube import PeerTube, list_instances
-
-
-class PeertubeAddon():
- """
- Main class of the addon
- """
-
- # URL of the page which explains how to install libtorrent
- HELP_URL = "https://link.infini.fr/libtorrent-peertube-kodi"
-
- def __init__(self, plugin, plugin_id):
- """
- Initialisation of the PeertubeAddon class
- :param plugin, plugin_id: str, int
- """
-
- # Save addon URL and ID
- self.plugin_url = plugin
- self.plugin_id = plugin_id
-
- # Select preferred instance by default
- self.selected_inst ="https://{}"\
- .format(get_setting("preferred_instance"))
-
- # Get the number of videos to show per page
- self.items_per_page = int(get_setting("items_per_page"))
-
- # Get the preferred resolution for video
- self.preferred_resolution = get_setting("preferred_resolution")
-
- # Nothing to play at initialisation
- self.play = 0
- self.torrent_name = ""
- self.torrent_f = ""
-
- # Check whether libtorrent could be imported by the service. The value
- # of the associated property is retrieved only once and stored in an
- # attribute because libtorrent is imported only once at the beginning
- # of the service (we assume it is not possible to start the add-on
- # before the service)
- self.libtorrent_imported = \
- get_property("libtorrent_imported") == "True"
-
- # Create a PeerTube object to send requests: settings which are used
- # only by this object are directly retrieved from the settings
- self.peertube = PeerTube(instance=self.selected_inst,
- count=self.items_per_page,
- sort=get_setting("video_sort_method"),
- video_filter=get_setting("video_filter"))
-
- def create_list(self, lst, data_type, start):
- """
- Create an array of xmbcgui.ListItem's from the lst parameter
- :param lst, data_type, start: dict, str, str
- :result listing: array
- """
- # Create a list for our items.
- listing = []
- for data in lst["data"]:
-
- # Create a list item with a text label
- list_item = xbmcgui.ListItem(label=data["name"])
-
- if data_type == "videos":
- # Add thumbnail
- list_item.setArt({
- "thumb": "{0}/{1}".format(self.selected_inst,
- data["thumbnailPath"])})
-
- # Set a fanart image for the list item.
- # list_item.setProperty("fanart_image", data["thumb"])
-
- # Compute media info from item's metadata
- info = {"title": data["name"],
- "playcount": data["views"],
- "plotoutline": data["description"],
- "duration": data["duration"]
- }
-
- # For videos, add a rating based on likes and dislikes
- if data["likes"] > 0 or data["dislikes"] > 0:
- info["rating"] = data["likes"] / (
- data["likes"] + data["dislikes"])
-
- # Set additional info for the list item.
- list_item.setInfo("video", info)
-
- # Videos are playable
- list_item.setProperty("IsPlayable", "true")
-
- # Build the Kodi URL to play the associated video only with the
- # id of the video. The instance is omitted because the
- # currently selected instance will be used automatically.
- url = self.build_kodi_url({
- "action": "play_video",
- "id": data["uuid"]
- })
-
- elif data_type == "instances":
- # TODO: Add a context menu to select instance as preferred
- # Instances are not playable
- list_item.setProperty("IsPlayable", "false")
-
- # Set URL to select this instance
- url = self.build_kodi_url({
- "action": "select_instance",
- "url": data["host"]
- })
-
- # Add our item to the listing as a 3-element tuple.
- listing.append((url, list_item, False))
-
- # Add a "Next page" button when there are more data to show
- start = int(start) + self.items_per_page
- if lst["total"] > start:
- list_item = xbmcgui.ListItem(label="Next page ({0})"
- .format(start/self.items_per_page))
- url = self.build_kodi_url({
- "action": "browse_{0}".format(data_type),
- "start": start})
- listing.append((url, list_item, True))
-
- return listing
-
- def get_video_url(self, video_id, instance=None):
- """Find the URL of the video with the best possible quality matching
- user's preferences.
-
- :param video_id: ID of the torrent linked to the video
- :type video_id: str
- :param instance: PeerTube instance hosting the video (optional)
- :type instance: str
- """
-
- # If no instance was provided, use the selected one (internal call)
- if instance is None:
- instance = self.selected_inst
- else:
- # If an instance was provided (external call), ensure the URL is
- # prefixed with HTTPS
- if not instance.startswith("https://"):
- instance = "https://{}".format(instance)
-
- # Retrieve the information about the video
- metadata = self.peertube.get_video(video_id)
-
- # 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"]
-
- debug("Looking for the best resolution matching the user preferences")
-
- current_res = 0
- higher_res = -1
- torrent_url = ""
-
- for f in files:
- # Get the resolution
- res = f["resolution"]["id"]
- if res == self.preferred_resolution:
- # Stop directly when we find the exact same resolution as the
- # user's preferred one
- debug("Found video with preferred resolution")
- torrent_url = f["torrentUrl"]
- break
- elif res < self.preferred_resolution and res > current_res:
- # Otherwise, try to find the best one just below the user's
- # preferred one
- debug("Found video with good lower resolution ({0})"
- .format(f["resolution"]["label"]))
- torrent_url = f["torrentUrl"]
- current_res = res
- elif (res > self.preferred_resolution
- and (res < higher_res or higher_res == -1)):
- # In the worst case, we'll take the one just above the user's
- # preferred one
- debug("Saving video with higher resolution ({0}) as a possible"
- " alternative".format(f["resolution"]["label"]))
- backup_url = f["torrentUrl"]
- higher_res = res
-
- # When we didn't find a resolution equal or lower than the user's
- # preferred one, use the resolution just above the preferred one
- if not torrent_url:
- debug("Using video with higher resolution as alternative")
- torrent_url = backup_url
-
- return torrent_url
-
- def search_videos(self, start):
- """
- Function to search for videos on a PeerTube instance and navigate
- in the results
-
- :param start: string
- """
-
- # Ask the user which keywords must be searched for
- keywords = xbmcgui.Dialog().input(
- heading="Search videos on {}".format(self.selected_inst),
- type=xbmcgui.INPUT_ALPHANUM)
-
- # Go back to main menu when user cancels
- if not keywords:
- return
-
- # Send the query
- results = self.peertube.search_videos(keywords, start)
-
- # Exit directly when no result is found
- if not results:
- notif_warning(title="No videos found",
- message="No videos found matching the query.")
- return
-
- # Create array of xmbcgui.ListItem's
- listing = self.create_list(results, "videos", start)
-
- # Add our listing to Kodi.
- xbmcplugin.addDirectoryItems(self.plugin_id, listing, len(listing))
- xbmcplugin.endOfDirectory(self.plugin_id)
-
- def browse_videos(self, start):
- """
- Function to navigate through all the video published by a PeerTube
- instance
-
- :param start: string
- """
-
- # Send the query
- results = self.peertube.list_videos(start)
-
- # Create array of xmbcgui.ListItem's
- listing = self.create_list(results, "videos", start)
-
- # Add our listing to Kodi.
- xbmcplugin.addDirectoryItems(self.plugin_id, listing, len(listing))
- xbmcplugin.endOfDirectory(self.plugin_id)
-
- def browse_instances(self, start):
- """
- Function to navigate through all PeerTube instances
- :param start: str
- """
-
- # Send the query
- results = list_instances(start)
-
- # Create array of xmbcgui.ListItem's
- listing = self.create_list(results, "instances", start)
-
- # Add our listing to Kodi.
- xbmcplugin.addDirectoryItems(self.plugin_id, listing, len(listing))
- xbmcplugin.endOfDirectory(self.plugin_id)
-
- def play_video_continue(self, data):
- """
- Callback function to let the play_video function resume when the
- PeertubeDownloader has downloaded all the torrent's metadata
-
- :param data: dict
- """
-
- debug("Received metadata_downloaded signal, will start playing media")
- self.play = 1
- self.torrent_f = data["file"]
-
- def play_video(self, torrent_url):
- """
- Start the torrent's download and play it while being downloaded
- :param torrent_url: str
- """
- # If libtorrent could not be imported, display a message and do not try
- # download nor play the video as it will fail.
- if not self.libtorrent_imported:
- open_dialog(title="Error: libtorrent could not be imported",
- message="PeerTube cannot play videos without"
- " libtorrent.\nPlease follow the instructions"
- " at {}".format(self.HELP_URL))
- return
-
- debug("Starting torrent download ({0})".format(torrent_url))
-
- # Start a downloader thread
- AddonSignals.sendSignal("start_download", {"url": torrent_url})
-
- # Wait until the PeerTubeDownloader has downloaded all the torrent's
- # metadata
- AddonSignals.registerSlot("plugin.video.peertube",
- "metadata_downloaded",
- self.play_video_continue)
- timeout = 0
- while self.play == 0 and timeout < 10:
- xbmc.sleep(1000)
- timeout += 1
-
- # Abort in case of timeout
- if timeout == 10:
- notif_error(title="Download timeout",
- message="Timeout fetching {}".format(torrent_url))
- return
- else:
- # Wait a little before starting playing the torrent
- xbmc.sleep(3000)
-
- # Pass the item to the Kodi player for actual playback.
- debug("Starting video playback ({0})".format(torrent_url))
- play_item = xbmcgui.ListItem(path=self.torrent_f)
- xbmcplugin.setResolvedUrl(self.plugin_id, True, listitem=play_item)
-
- def select_instance(self, instance):
- """
- Change currently selected instance to "instance" parameter
- :param instance: str
- """
-
- # Update the object attribute even though it is not used currently but
- # it may be useful in case reuselanguageinvoker is enabled.
- self.selected_inst = "https://{}".format(instance)
-
- # 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
- set_setting("preferred_instance", instance)
-
- # Notify the user and log the event
- message = "{0} is now the selected instance".format(self.selected_inst)
- notif_info(title="Current instance changed",
- message=message)
- debug(message)
-
- def build_kodi_url(self, parameters):
- """Build a Kodi URL based on the parameters.
-
- :param parameters: dict containing all the parameters that will be
- encoded in the URL
- """
-
- return "{0}?{1}".format(self.plugin_url, urlencode(parameters))
-
- def main_menu(self):
- """
- Addon's main menu
- """
-
- # Create a list for our items.
- listing = []
-
- # 1st menu entry
- list_item = xbmcgui.ListItem(label="Browse selected instance")
- url = self.build_kodi_url({"action": "browse_videos", "start": 0})
- listing.append((url, list_item, True))
-
- # 2nd menu entry
- list_item = xbmcgui.ListItem(label="Search on selected instance")
- url = self.build_kodi_url({"action": "search_videos", "start": 0})
- listing.append((url, list_item, True))
-
- # 3rd menu entry
- list_item = xbmcgui.ListItem(label="Select other instance")
- url = self.build_kodi_url({"action": "browse_instances", "start": 0})
- listing.append((url, list_item, True))
-
- # Add our listing to Kodi.
- xbmcplugin.addDirectoryItems(self.plugin_id, listing, len(listing))
-
- # Finish creating a virtual folder.
- xbmcplugin.endOfDirectory(self.plugin_id)
-
- def router(self, paramstring):
- """
- Router function that calls other functions
- depending on the provided paramstring
- :param paramstring: dict
- """
-
- # Parse a URL-encoded paramstring to the dictionary of
- # {: } elements
- params = dict(parse_qsl(paramstring[1:]))
-
- # Check the parameters passed to the plugin
- if params:
- action = params["action"]
- if action == "browse_videos":
- # Browse videos on selected instance
- self.browse_videos(params["start"])
- elif action == "search_videos":
- # Search for videos on selected instance
- self.search_videos(params["start"])
- elif action == "browse_instances":
- # Browse peerTube instances
- self.browse_instances(params["start"])
- 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)
- elif action == "select_instance":
- self.select_instance(params["url"])
- else:
- # Display the addon's main menu when the plugin is called from
- # Kodi UI without any parameters
- self.main_menu()
-
- # Display a warning if libtorrent could not be imported
- if not self.libtorrent_imported:
- 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 {}"
- .format(self.HELP_URL))
-
-if __name__ == "__main__":
-
- # Initialise addon
- addon = PeertubeAddon(sys.argv[0], int(sys.argv[1]))
- # Call the router function and pass the plugin call parameters to it.
- addon.router(sys.argv[2])
diff --git a/resources/lib/addon.py b/resources/lib/addon.py
new file mode 100644
index 0000000..3b23eb5
--- /dev/null
+++ b/resources/lib/addon.py
@@ -0,0 +1,441 @@
+# -*- coding: utf-8 -*-
+"""
+ Main class used by the add-on
+
+ Copyright (C) 2018 Cyrille Bollu
+ Copyright (C) 2021 Thomas Bétous
+
+ SPDX-License-Identifier: GPL-3.0-only
+ See LICENSE.txt for more information.
+"""
+import AddonSignals # Module exists only in Kodi - pylint: disable=import-error
+
+from resources.lib.kodi_utils import kodi
+from resources.lib.peertube import PeerTube, list_instances
+
+
+class PeerTubeAddon():
+ """
+ Main class used by the add-on
+ """
+
+ # URL of the page which explains how to install libtorrent
+ HELP_URL = "https://link.infini.fr/libtorrent-peertube-kodi"
+
+ def __init__(self):
+ """Initialize parameters and create a PeerTube instance"""
+
+ # Get the number of items to show per page
+ self.items_per_page = int(kodi.get_setting("items_per_page"))
+
+ # Get the preferred resolution for video
+ self.preferred_resolution = \
+ int(kodi.get_setting("preferred_resolution"))
+
+ # Nothing to play at initialisation
+ self.play = False
+ self.torrent_name = ""
+ self.torrent_file = ""
+
+ # Check whether libtorrent could be imported by the service. The value
+ # of the associated property is retrieved only once and stored in an
+ # attribute because libtorrent is imported only once at the beginning
+ # of the service (we assume it is not possible to start the add-on
+ # before the service)
+ self.libtorrent_imported = \
+ kodi.get_property("libtorrent_imported") == "True"
+
+ # Create a PeerTube object to send requests: settings which are used
+ # only by this object are directly retrieved from the settings
+ self.peertube = PeerTube(
+ instance=kodi.get_setting("preferred_instance"),
+ count=self.items_per_page,
+ sort=kodi.get_setting("video_sort_method"),
+ video_filter=kodi.get_setting("video_filter"))
+
+ def _browse_videos(self, start):
+ """Display the list of all the videos published on a PeerTube instance
+
+ :param int start: index of the first video to display (pagination)
+ """
+
+ # Use the API to get the list of the videos
+ results = self.peertube.list_videos(start)
+
+ # Extract the information of each video from the API response
+ list_of_videos = self._create_list_of_videos(results, start)
+
+ # Create the associated items in Kodi
+ kodi.create_items_in_ui(list_of_videos)
+
+ def _browse_instances(self, start):
+ """
+ Function to navigate through all the PeerTube instances
+
+ :param int start: index of the first instance to display (pagination)
+ """
+
+ # Use the API to get the list of the instances
+ results = list_instances(start)
+
+ # Extract the information of each instance from the API response
+ list_of_instances = self._create_list_of_instances(results, start)
+
+ # Create the associated items in Kodi
+ kodi.create_items_in_ui(list_of_instances)
+
+ def _create_list_of_instances(self, response, start):
+ """Generator of instance items to be added in Kodi UI
+
+ :param dict response: data returned by joinpeertube
+ :param int start: index of the first item to display (pagination)
+ :return: yield the information of each item
+ :rtype: dict
+ """
+
+ for data in response["data"]:
+
+ # The description of each instance in Kodi will be composed of:
+ # * the description of the instance (from joinpeertube.org)
+ # * the number of local videos hosted on this instance
+ # * the number of users on this instance
+ description = "{}\n\n----------\nNumber of local videos: {}\n"\
+ "Number of users: {}".format(
+ data["shortDescription"].encode("utf-8"),
+ data["totalLocalVideos"],
+ data["totalUsers"])
+ # The value of "totalLocalVideos" and "totalUsers" are int so they
+ # don't need to be encoded.
+
+ instance_info = kodi.generate_item_info(
+ name=data["name"],
+ url=kodi.build_kodi_url({
+ "action": "select_instance",
+ "url": data["host"]
+ }
+ ),
+ is_folder=True,
+ plot=description
+ )
+
+ yield instance_info
+ else:
+ # Add a "Next page" button when there are more items to show
+ next_page_item = self._create_next_page_item(
+ total=int(response["total"]),
+ current_index=start,
+ url=kodi.build_kodi_url(
+ {
+ "action": "browse_instances",
+ "start": start + self.items_per_page
+ }
+ )
+ )
+
+ if next_page_item:
+ yield next_page_item
+
+ def _create_list_of_videos(self, response, start):
+ """Generator of video items to be added in Kodi UI
+
+ :param dict response: data returned by PeerTube
+ :param int start: index of the first item to display (pagination)
+ :return: yield the information of each item
+ :rtype: dict
+ """
+
+ for data in response["data"]:
+
+ video_info = kodi.generate_item_info(
+ name=data["name"],
+ url=kodi.build_kodi_url(
+ {
+ "action": "play_video",
+ "id": data["uuid"]
+ }
+ ),
+ is_folder=False,
+ plot=data["description"],
+ duration=data["duration"],
+ thumbnail="{0}/{1}".format(self.peertube.instance,
+ data["thumbnailPath"]),
+ aired=data["publishedAt"]
+ )
+
+ yield video_info
+ else:
+ # Add a "Next page" button when there are more items to show
+ next_page_item = self._create_next_page_item(
+ total=int(response["total"]),
+ current_index=start,
+ url=kodi.build_kodi_url(
+ {
+ "action": "browse_videos",
+ "start": start + self.items_per_page
+ }
+ )
+ )
+
+ if next_page_item:
+ yield next_page_item
+
+ def _create_next_page_item(self, total, current_index, url):
+ """Return the info required to create an item to go to the next page
+
+ :param int total: total number of elements
+ :param int current_index: index of the first element currently used
+ :param str url: URL to reach when the "Next page" item is run
+ :return: yield the info to create a "Next page" item in Kodi UI if
+ there are more items to show
+ :rtype: dict
+ """
+ next_index = current_index + self.items_per_page
+ if total > next_index:
+ next_page = (next_index / self.items_per_page) + 1
+ total_pages = (total / self.items_per_page) + 1
+
+ next_page_item = kodi.generate_item_info(
+ name="Next page ({}/{})".format(next_page, total_pages),
+ url=url
+ )
+
+ 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
+ user's preferences.
+
+ :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
+ """
+ # Retrieve the information about the video including the different
+ # resolutions available
+ video_files = self.peertube.get_video_urls(video_id, instance=instance)
+
+ # Find the best resolution matching user's preferences
+ current_resolution = 0
+ higher_resolution = -1
+ url = ""
+ for video in video_files:
+ # Get the resolution
+ resolution = video["resolution"]
+ 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
+ elif (resolution < self.preferred_resolution
+ and resolution > current_resolution):
+ # Otherwise, try to find the best one just below the user's
+ # preferred one
+ kodi.debug("Found video with good lower resolution ({})"
+ .format(resolution))
+ url = video["url"]
+ current_resolution = resolution
+ elif (resolution > self.preferred_resolution and
+ (resolution < higher_resolution or
+ higher_resolution == -1)):
+ # In the worst case, we'll take the one just above the user's
+ # preferred one
+ kodi.debug("Saving video with higher resolution ({}) as a"
+ " possible alternative".format(resolution))
+ backup_url = video["url"]
+ higher_resolution = resolution
+ else:
+ kodi.debug("Ignoring the resolution '{}'".format(resolution))
+
+ # When we didn't find a resolution equal or lower than the user's
+ # preferred one, use the resolution just above the preferred one
+ if not url:
+ kodi.debug("Using video with higher resolution as alternative ({})"
+ .format(higher_resolution))
+ url = backup_url
+
+ return url
+
+ def _home_page(self):
+ """Display the items of the home page of the add-on"""
+
+ home_page_items = [
+ kodi.generate_item_info(
+ name="Browse videos on the selected instance",
+ url=kodi.build_kodi_url({"action": "browse_videos","start": 0})
+ ),
+ kodi.generate_item_info(
+ name="Search videos on the selected instance",
+ url=kodi.build_kodi_url({"action": "search_videos","start": 0})
+ ),
+ kodi.generate_item_info(
+ name="Browse videos on the selected instance",
+ url=kodi.build_kodi_url({
+ "action": "browse_instances",
+ "start": 0
+ }
+ )
+ )
+ ]
+
+ kodi.create_items_in_ui(home_page_items)
+
+ def _search_videos(self, start):
+ """
+ Function to search for videos on a PeerTube instance
+
+ :param str start: index of the first video to display (pagination)
+ """
+
+ # Ask the user which keywords must be searched for
+ keywords = kodi.open_input_box(
+ title="Search videos on {}".format(self.peertube.instance))
+
+ # Go back to the home page when the user cancels or didn't enter any
+ # string
+ if not keywords:
+ return
+
+ # Use the API to search for videos
+ results = self.peertube.search_videos(keywords, start)
+
+ # Exit directly when no result is found
+ if not results:
+ kodi.notif_warning(
+ title="No videos found",
+ message="No videos found matching the keywords.")
+ return
+
+ # Extract the information of each video from the API response
+ list_of_videos = self._create_list_of_videos(results, start)
+
+ # Create the associated items in Kodi
+ kodi.create_items_in_ui(list_of_videos)
+
+ def _play_video(self, torrent_url):
+ """
+ Start the torrent's download and play it while being downloaded
+
+ :param str torrent_url: URL of the torrent file to download and play
+ """
+ # If libtorrent could not be imported, display a message and do not try
+ # download nor play the video as it will fail.
+ if not self.libtorrent_imported:
+ kodi.open_dialog(
+ title="Error: libtorrent could not be imported",
+ message="PeerTube cannot play videos without libtorrent\n"
+ "Please follow the instructions at {}"
+ .format(self.HELP_URL))
+ return
+
+ kodi.debug("Starting torrent download ({})".format(torrent_url))
+ kodi.notif_info(title="Download started",
+ message="The video will be played soon.")
+
+ # Start a downloader thread
+ AddonSignals.sendSignal("start_download", {"url": torrent_url})
+
+ # Wait until the PeerTubeDownloader has downloaded all the torrent's
+ # metadata
+ AddonSignals.registerSlot(kodi.addon_id,
+ "metadata_downloaded",
+ self._play_video_continue)
+ timeout = 0
+ while not self.play and timeout < 10:
+ kodi.sleep(1000)
+ timeout += 1
+
+ # Abort in case of timeout
+ if timeout == 10:
+ kodi.notif_error(
+ title="Download timeout",
+ message="Timeout fetching {}".format(torrent_url))
+ return
+ else:
+ # Wait a little before starting playing the torrent
+ kodi.sleep(3000)
+
+ # Pass the item to the Kodi player for actual playback.
+ kodi.debug("Starting video playback ({})".format(self.torrent_file))
+ kodi.play(self.torrent_file)
+
+ def _play_video_continue(self, data):
+ """
+ Callback function to let the _play_video method resume when the
+ PeertubeDownloader has downloaded all the torrent's metadata
+
+ :param data: dict of information sent from PeertubeDownloader
+ """
+
+ kodi.debug(
+ "Received metadata_downloaded signal, will start playing media")
+ self.play = True
+ self.torrent_file = data["file"]
+
+ def _select_instance(self, instance):
+ """
+ Change currently selected instance to "instance" parameter
+
+ :param str instance: URL of the new instance
+ """
+
+ # Update the PeerTube object attribute even though it is not used
+ # currently (because the value will be retrieved from the settings on
+ # the next run of the add-on but it may be useful in case
+ # reuselanguageinvoker is enabled)
+ self.peertube.set_instance(instance)
+
+ # Update the preferred instance in the settings so that this choice is
+ # reused on the next runs and the next calls of the add-on
+ kodi.set_setting("preferred_instance", instance)
+
+ # Notify the user and log the event
+ message = \
+ "{} is now the selected instance".format(self.peertube.instance)
+ kodi.notif_info(title="Current instance changed", message=message)
+ kodi.debug(message)
+
+ def router(self, params):
+ """Route the add-on to the requested actions
+
+ :param dict params: Parameters the add-on was called with
+ """
+
+ # Check the parameters passed to the plugin
+ if params:
+ action = params["action"]
+ if action == "browse_videos":
+ # Browse videos on the selected instance
+ self._browse_videos(int(params["start"]))
+ elif action == "search_videos":
+ # Search for videos on the selected instance
+ self._search_videos(int(params["start"]))
+ elif action == "browse_instances":
+ # Browse PeerTube instances
+ self._browse_instances(int(params["start"]))
+ 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)
+ elif action == "select_instance":
+ # Set the selected instance as the preferred instance
+ self._select_instance(params["url"])
+ else:
+ # Display the addon's main menu when the plugin is called from
+ # Kodi UI without any parameters
+ self._home_page()
+
+ # Display a warning if libtorrent could not be imported
+ if not self.libtorrent_imported:
+ 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 {}"
+ .format(self.HELP_URL))
diff --git a/resources/lib/kodi_utils.py b/resources/lib/kodi_utils.py
index b0b53b0..1b7e602 100644
--- a/resources/lib/kodi_utils.py
+++ b/resources/lib/kodi_utils.py
@@ -7,90 +7,246 @@
SPDX-License-Identifier: GPL-3.0-only
See LICENSE.txt for more information.
"""
+try:
+ # Python 3.x
+ from urllib.parse import parse_qsl
+except ImportError:
+ # Python 2.x
+ from urlparse import parse_qsl
+
+from requests.compat import urlencode
+
import xbmc # Kodistubs for Leia is not compatible with python3 / pylint: disable=syntax-error
import xbmcaddon
import xbmcgui # Kodistubs for Leia is not compatible with python3 / pylint: disable=syntax-error
+import xbmcplugin
-def debug(message):
- """Log a message in Kodi's log with the level xbmc.LOGDEBUG
+class KodiUtils:
+ """Utility class to call Kodi APIs"""
- :param str message: Message to log prefixed with the name of the add-on
- (the name is hard-coded to avoid calling xbmcaddon each time since the name
- should not change)
- """
- xbmc.log("[PeerTube] {}".format(message), xbmc.LOGDEBUG)
+ def __init__(self):
+ """Initialize the object with information about the add-on"""
+ self.addon_name = xbmcaddon.Addon().getAddonInfo("name")
+ self.addon_id = xbmcaddon.Addon().getAddonInfo("id")
-def get_property(name):
- """Retrieve the value of a window property related to the add-on
+ # Prepare other attributes that will be initialized with sys.argv
+ self.addon_url = ""
+ self.addon_handle = 0
+ self.addon_parameters = ""
- :param str name: Name of the property which value will be retrieved (the
- actual name of the property is prefixed with "peertube_")
- :return: Value of the window property
- :rtype: str
- """
- return xbmcgui.Window(10000).getProperty("peertube_{}".format(name))
+ def build_kodi_url(self, parameters):
+ """Build a Kodi URL based on the parameters.
-def get_setting(setting_name):
- """Retrieve the value of a setting
+ This URL will be used to call the add-on with the expected parameters.
- :param str setting_name: Name of the setting
- :return: Value of the setting named setting_name
- :rtype: str
- """
- return xbmcaddon.Addon().getSetting(setting_name)
+ :param dict parameters: The parameters that will be encoded in the URL
+ """
-def notif_error(title, message):
- """Display a notification with the error icon
+ return "{}?{}".format(self.addon_url, urlencode(parameters))
- :param str title: Title of the notification
- :param str message: Message of the notification
- """
- xbmcgui.Dialog().notification(heading=title,
- message=message,
- icon=xbmcgui.NOTIFICATION_ERROR)
+ def create_items_in_ui(self, items_info):
+ """Create items in Kodi UI
-def notif_info(title, message):
- """Display a notification with the info icon
+ :param list items_info: A list of dict containing all the required
+ information to create the items (i.e. the return value of the method
+ generate_item_info)
+ """
+ # Tell Kodi to use the "video" viewtypes
+ xbmcplugin.setContent(handle=self.addon_handle, content="videos")
- :param str title: Title of the notification
- :param str message: Message of the notification
- """
- xbmcgui.Dialog().notification(heading=title,
- message=message,
- icon=xbmcgui.NOTIFICATION_INFO)
+ list_of_items = []
-def notif_warning(title, message):
- """Display a notification with the warning icon
+ for info in items_info:
+ # Create the ListItem object
+ list_item = xbmcgui.ListItem(label=info["name"])
- :param str title: Title of the notification
- :param str message: Message of the notification
- """
- xbmcgui.Dialog().notification(heading=title,
- message=message,
- icon=xbmcgui.NOTIFICATION_WARNING)
+ # Add the general info of the item
+ list_item.setInfo("video", info["info"])
-def open_dialog(title, message):
- """Open a dialog box with an "OK" button
+ # Add the art info of the item
+ list_item.setArt(info["art"])
- :param str title: Title of the box
- :param str message: Message in the box
- """
- xbmcgui.Dialog().ok(heading=title, line1=message)
+ if not info["is_folder"]:
+ list_item.setProperty("IsPlayable", "true")
-def set_property(name, value):
- """Modify the value of a window property related to the add-on
+ # Add to the list the tuple expected by addDirectoryItems
+ list_of_items.append((info["url"], list_item, info["is_folder"]))
- :param str name: Name of the property which value will be modified (the
- actual name of the property is prefixed with "peertube_")
- :param str value: New value of the property
- """
- xbmcgui.Window(10000).setProperty("peertube_{}".format(name), value)
+ # Create the items
+ xbmcplugin.addDirectoryItems(
+ handle=self.addon_handle,
+ items=list_of_items,
+ totalItems=len(list_of_items)
+ )
-def set_setting(setting_name, setting_value):
- """Modify the value of a setting
+ # Terminate the items creation
+ xbmcplugin.endOfDirectory(self.addon_handle)
- :param str setting_name: Name of the setting
- :param str setting_value: New value of the setting
- """
- xbmcaddon.Addon().setSetting(setting_name, setting_value)
+ def debug(self, message, prefix=None):
+ """Log a message in Kodi's log with the level xbmc.LOGDEBUG
+
+ The message will be prefixed with the prefix passed as argument or with
+ the name of the add-on.
+
+ :param str message: Message to log
+ :param str prefix: String to prefix the message with
+ """
+ if not prefix:
+ prefix = self.addon_name
+
+ xbmc.log("[{}] {}".format(prefix, message), xbmc.LOGDEBUG)
+
+ def generate_item_info(self, name, url, is_folder=True, thumbnail="",
+ aired="", duration=0, plot="",):
+ """Return all the information required to create an item in Kodi UI
+
+ This function makes the creation of an item easier: it allows to pass
+ to the function only the known information about an item, and it will
+ return a dict with all the keys expected by create_items_in_ui
+ correctly initialized (including the ones that were not passed).
+
+ :param str name: Name of the item
+ :param str url: URL to reach when the item is used
+ :param bool is_folder: Whether the item is a folder or is playable
+ :param : The other parameters are the ones expected by
+ ListItem.setInfo() and ListItem.setArt()
+ :return: Information required to create the item in Kodi UI
+ :rtype: dict
+ """
+ return {
+ "name": name,
+ "url": url,
+ "is_folder": is_folder,
+ "art": {
+ "thumb": thumbnail,
+ },
+ "info": {
+ "aired": aired,
+ "duration": duration,
+ "plot": plot,
+ "title": name
+ }
+ }
+
+ def get_run_parameters(self):
+ """Return the parameter the add-on was called with
+
+ The parameters are read in the method "update_call_info"
+
+ :return: The extracted parameters
+ :rtype: dict
+ """
+ # The first character ("?") is skipped
+ return dict(parse_qsl(self.addon_parameters[1:]))
+
+ def get_property(self, name):
+ """Retrieve the value of a window property related to the add-on
+
+ :param str name: Name of the property which value will be retrieved (the
+ actual name of the property is prefixed with "peertube_")
+ :return: Value of the window property
+ :rtype: str
+ """
+ return xbmcgui.Window(10000).getProperty("peertube_{}".format(name))
+
+ def get_setting(self, setting_name):
+ """Retrieve the value of a setting
+
+ :param str setting_name: Name of the setting
+ :return: Value of the setting named setting_name
+ :rtype: str
+ """
+ return xbmcaddon.Addon().getSetting(setting_name)
+
+ def notif_error(self, title, message):
+ """Display a notification with the error icon
+
+ :param str title: Title of the notification
+ :param str message: Message of the notification
+ """
+ xbmcgui.Dialog().notification(heading=title,
+ message=message,
+ icon=xbmcgui.NOTIFICATION_ERROR)
+
+ def notif_info(self, title, message):
+ """Display a notification with the info icon
+
+ :param str title: Title of the notification
+ :param str message: Message of the notification
+ """
+ xbmcgui.Dialog().notification(heading=title,
+ message=message,
+ icon=xbmcgui.NOTIFICATION_INFO)
+
+ def notif_warning(self, title, message):
+ """Display a notification with the warning icon
+
+ :param str title: Title of the notification
+ :param str message: Message of the notification
+ """
+ xbmcgui.Dialog().notification(heading=title,
+ message=message,
+ icon=xbmcgui.NOTIFICATION_WARNING)
+
+ def open_dialog(self, title, message):
+ """Open a dialog box with an "OK" button
+
+ :param str title: Title of the box
+ :param str message: Message in the box
+ """
+ xbmcgui.Dialog().ok(heading=title, line1=message)
+
+ def open_input_box(self, title):
+ """Open a box for the user to input alphanumeric data
+
+ :param str title: Title of the box
+ :return: Entered data or an empty string
+ :rtype: str
+ """
+ return xbmcgui.Dialog().input(heading=title,
+ type=xbmcgui.INPUT_ALPHANUM)
+
+ def play(self, url):
+ """Play the media behind the URL
+
+ :param str url: URL of the media to play
+ """
+ xbmcplugin.setResolvedUrl(handle=self.addon_handle,
+ succeeded=True,
+ listitem=xbmcgui.ListItem(path=url))
+
+ def set_property(self, name, value):
+ """Modify the value of a window property related to the add-on
+
+ :param str name: Name of the property which value will be modified (the
+ actual name of the property is prefixed with "peertube_")
+ :param str value: New value of the property
+ """
+ xbmcgui.Window(10000).setProperty("peertube_{}".format(name), value)
+
+ def set_setting(self, setting_name, setting_value):
+ """Modify the value of a setting
+
+ :param str setting_name: Name of the setting
+ :param str setting_value: New value of the setting
+ """
+ xbmcaddon.Addon().setSetting(setting_name, setting_value)
+
+ def sleep(self, time_us):
+ """Sleep for some micro seconds
+
+ :param int time_us: Sleep time in micro seconds
+ """
+ xbmc.sleep(time_us)
+
+ def update_call_info(self, argv):
+ """Update the attributes related to the current call of the add-on
+
+ :param list argv: System arguments
+ """
+ self.addon_url = argv[0]
+ self.addon_handle = int(argv[1])
+ self.addon_parameters = argv[2]
+
+kodi = KodiUtils()
\ No newline at end of file
diff --git a/resources/lib/peertube.py b/resources/lib/peertube.py
index 60e5a44..ebf9413 100644
--- a/resources/lib/peertube.py
+++ b/resources/lib/peertube.py
@@ -11,7 +11,7 @@
import requests
from requests.compat import urljoin
-from resources.lib.kodi_utils import debug, get_setting, notif_error
+from resources.lib.kodi_utils import kodi
class PeerTube:
@@ -23,9 +23,9 @@ class PeerTube:
:param str instance: URL of the PeerTube instance
:param str sort: sort method to use when listing items
:param int count: number of items to display
- :param str sort: filter to apply when listing/searching videos
+ :param str video_filter: filter to apply when listing/searching videos
"""
- self.instance = instance
+ self.set_instance(instance)
self.list_settings = {
"sort": sort,
@@ -39,7 +39,7 @@ class PeerTube:
else:
self.filter = "local"
- def _request(self, method, url, params=None, data=None):
+ def _request(self, method, url, params=None, data=None, instance=None):
"""Call a REST API on the instance
:param str method: REST API method (get, post, put, delete, etc.)
@@ -47,12 +47,22 @@ class PeerTube:
instance
:param dict params: dict of the parameters to send in the request
:param dict data: dict of the data to send with the request
+ :param str instance: URL of the instance hosting the video. The
+ configured instance will be used if empty.
:return: the response as JSON data
:rtype: dict
"""
+ # If no instance was provided, use the one from the settings (which was
+ # used when instantianting this object)
+ if instance is None:
+ instance = self.instance
+ else:
+ # If an instance was provided ensure the URL is prefixed with HTTPS
+ if not instance.startswith("https://"):
+ instance = "https://{}".format(instance)
# Build the URL of the REST API
- api_url = urljoin("{}/api/v1/".format(self.instance), url)
+ api_url = urljoin("{}/api/v1/".format(instance), url)
# Send a request with a time-out of 5 seconds
response = requests.request(method=method,
@@ -69,19 +79,19 @@ class PeerTube:
response.raise_for_status()
except requests.HTTPError as exception:
# Print in Kodi's log some information about the request
- debug("Error when sending a {} request to {} with params={} and"
- " data={}".format(method, url, params, data))
+ kodi.debug("Error when sending a {} request to {} with params={}"
+ " and data={}".format(method, url, params, data))
# Report the error to the user with a notification: if the response
# contains an "error" attribute, use it as error message, otherwise
# use a default message.
if "error" in json:
message = json["error"]
- debug(message)
+ kodi.debug(message)
else:
message = ("No details returned by the server. Check the log"
" for more information.")
- notif_error(title="Request error", message=message)
+ kodi.notif_error(title="Request error", message=message)
raise exception
return json
@@ -107,20 +117,43 @@ class PeerTube:
return params
- def get_video(self, video_id):
- """Get the information of a video
+ def get_video_urls(self, video_id, instance=None):
+ """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.
:param str video_id: ID or UUID of the video
- :return: the information of the video as returned by the REST API
- :rtype: dict
+ :param str instance: URL of the instance hosting the video. The
+ configured instance will be used if empty.
+ :return: pair(s) of URL/resolution
+ :rtype: generator
"""
+ # Get the information about the video
+ metadata = self._request(method="GET",
+ url="videos/{}".format(video_id),
+ instance=instance)
- return self._request(method="GET", url="videos/{}".format(video_id))
+ # 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:
+ yield {
+ "resolution": int(file["resolution"]["id"]),
+ "url": file["torrentUrl"],
+ "is_live": False
+ }
def list_videos(self, start):
"""List the videos in the instance
- :param str start: index of the first video to display
+ :param int start: index of the first video to display
:return: the list of videos as returned by the REST API
:rtype: dict
"""
@@ -133,7 +166,7 @@ class PeerTube:
"""Search for videos on the instance and beyond.
:param str keywords: keywords to seach for
- :param str start: index of the first video to display
+ :param int start: index of the first video to display
:return: the videos matching the keywords as returned by the REST API
:rtype: dict
"""
@@ -144,11 +177,27 @@ class PeerTube:
return self._request(method="GET", url="search/videos", params=params)
+ def set_instance(self, instance):
+ """Set the URL of the current instance with the right format
+
+ The URL of the instance may not be prefixed with HTTPS, for instance:
+ * in the settings the URL does not use this prefix to allow the user to
+ change it easily
+ * the URL from the list of instances is not prefixed
+ This method is used to ensure the URL is correctly prefixed with HTTPS
+
+ :param str instance: URL of the instance
+ """
+ if not instance.startswith("https://"):
+ instance = "https://{}".format(instance)
+
+ self.instance = instance
+
def list_instances(start):
"""List all the peertube instances from joinpeertube.org
- :param str start: index of the first instance to display
+ :param int start: index of the first instance to display
:return: the list of instances as returned by the REST API
:rtype: dict
"""
@@ -156,7 +205,7 @@ def list_instances(start):
api_url = "https://instances.joinpeertube.org/api/v1/instances"
# Build the parameters that will be sent in the request from the settings
params = {
- "count": get_setting("items_per_page"),
+ "count": kodi.get_setting("items_per_page"),
"start": start
}
@@ -171,8 +220,8 @@ def list_instances(start):
response.raise_for_status()
except requests.HTTPError as exception:
# Print in Kodi's log some information about the request
- debug("Error when getting the list of instances with params={}"
- .format(params))
+ kodi.debug("Error when getting the list of instances with params={}"
+ .format(params))
# Report the error to the user with a notification: use the details of
# the error if it exists in the response, otherwise use a default
@@ -182,11 +231,11 @@ def list_instances(start):
# name. Then get the second element in the sublist which contains
# the details of the error.
message = list(json["errors"].items())[0][1]["msg"]
- debug(message)
+ kodi.debug(message)
except KeyError:
message = ("No details returned by the server. Check the log"
" for more information.")
- notif_error(title="Request error", message=message)
+ kodi.notif_error(title="Request error", message=message)
raise exception
return json
diff --git a/service.py b/service.py
index 888d6df..4eb4a1e 100644
--- a/service.py
+++ b/service.py
@@ -14,7 +14,7 @@ from threading import Thread
import xbmc # Kodistubs for Leia is not compatible with python3 / pylint: disable=syntax-error
import xbmcvfs # Kodistubs for Leia is not compatible with python3 / pylint: disable=syntax-error
-from resources.lib.kodi_utils import debug, set_property
+from resources.lib.kodi_utils import kodi
class PeertubeDownloader(Thread):
"""
@@ -39,7 +39,7 @@ class PeertubeDownloader(Thread):
:param str message: Message to log (will be prefixed with the name of
the class)
"""
- debug("PeertubeDownloader: {}".format(message))
+ kodi.debug(message=message, prefix="PeertubeDownloader")
def run(self):
"""
@@ -97,7 +97,7 @@ class PeertubeService():
:param str message: Message to log (will be prefixed with the name of
the class)
"""
- debug("PeertubeService: {}".format(message))
+ kodi.debug(message=message, prefix="PeertubeService")
def download_torrent(self, data):
"""
@@ -122,7 +122,7 @@ class PeertubeService():
# Launch the download_torrent callback function when the
# "start_download" signal is received
- AddonSignals.registerSlot("plugin.video.peertube",
+ AddonSignals.registerSlot(kodi.addon_id,
"start_download",
self.download_torrent)
@@ -151,7 +151,7 @@ if __name__ == "__main__":
# Save whether libtorrent could be imported as a window property so that
# this information can be retrieved by the add-on
- set_property("libtorrent_imported", str(LIBTORRENT_IMPORTED))
+ kodi.set_property("libtorrent_imported", str(LIBTORRENT_IMPORTED))
# Start the service
service.run()