Pause download when playback stops

Use the add-on's service to monitor when the playback is stopped.
When it occurs, the download of the torrent is paused to have a
behavior similar to streaming.

To be able to pause the torrent, the service needs to use the
torrent handle. The torrent handle is recreated on the service-side
using the URL that was sent to vfs.libtorrent. This URL is sent to
the service before the playback starts using the AddonSignals.
Calling `xbmcvfs.File()` on existing torrent is fast enough for this
solution to be used even on slow devices.
This commit is contained in:
Thomas Bétous 2021-10-10 21:51:50 +02:00
parent 5eb7a46f34
commit 1ebb06d271
6 changed files with 48 additions and 122 deletions

View File

@ -9,7 +9,7 @@
<extension point="xbmc.python.pluginsource" library="main.py">
<provides>video</provides>
</extension>
<!--<extension point="xbmc.service" library="service.py"/>-->
<extension point="xbmc.service" library="service.py"/>
<extension point="xbmc.addon.metadata">
<summary lang="de_DE">Add-on für PeerTube</summary>
<description lang="de_DE">PeerTube ist eine kostenlose, dezentralisierte und föderierte Alternative zu anderen Videoplattformen (wie YouTube).</description>

View File

@ -86,8 +86,8 @@ msgid "PeerTube service started"
msgstr "PeerTube-Dienst gestartet"
msgctxt "#30401"
msgid "Torrents can now be downloaded."
msgstr "Torrents können jetzt heruntergeladen werden."
msgid "Torrents can now be controlled."
msgstr ""
msgctxt "#30402"
msgid "Request error"

View File

@ -86,7 +86,7 @@ msgid "PeerTube service started"
msgstr ""
msgctxt "#30401"
msgid "Torrents can now be downloaded."
msgid "Torrents can now be controlled."
msgstr ""
msgctxt "#30402"

View File

@ -86,8 +86,8 @@ msgid "PeerTube service started"
msgstr "Le service PeerTube a démarré"
msgctxt "#30401"
msgid "Torrents can now be downloaded."
msgstr "Les torrents peuvent maintenant être téléchargés."
msgid "Torrents can now be controlled."
msgstr "Les torrents peuvent maintenant être contrôlés."
msgctxt "#30402"
msgid "Request error"

View File

@ -15,6 +15,7 @@ from urllib import quote_plus
from resources.lib.kodi_utils import kodi
from resources.lib.peertube import PeerTube, list_instances
import AddonSignals
import xbmcvfs
class PeerTubeAddon():
@ -22,9 +23,6 @@ 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"""
@ -40,14 +38,6 @@ class PeerTubeAddon():
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(
@ -349,6 +339,8 @@ class PeerTubeAddon():
vfs_url = "torrent://{}".format(quote_plus(torrent_url))
torrent = xbmcvfs.File(vfs_url)
AddonSignals.sendSignal("get_torrent", {"torrent_url": vfs_url})
# Download the file
if(torrent.write("download")):
@ -366,6 +358,7 @@ class PeerTubeAddon():
# Play the file
kodi.debug("Starting video playback of {}".format(self.torrent_file))
kodi.play(self.torrent_file)
else:
kodi.notif_error(title=kodi.get_string(30421),
message=kodi.get_string(30422))

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""
PeerTube service to download torrents in the background
PeerTube service to perform action on torrents in background
Copyright (C) 2018 Cyrille Bollu
Copyright (C) 2021 Thomas Bétous
@ -8,73 +8,36 @@
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 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
import AddonSignals
import xbmc
import xbmcvfs
from resources.lib.kodi_utils import kodi
class PeertubeDownloader(Thread):
"""
A class to download PeerTube torrents in the background
"""
class PeertubePlayer(xbmc.Player):
# Initialize the attributes and call the parent class constructor
def __init__(self):
self.torrent_url = None
super(xbmc.Player, self).__init__()
# super().__init__()
def __init__(self, url, temp_dir):
"""
Initialise a PeertubeDownloader instance for downloading the torrent
specified by url
# This function will be called through AddonSignals when a video is played.
# It will save the URL expected by vfs.libtorrent to control the torrent.
def receive_torrent(self, data):
self.torrent_url = data["torrent_url"]
kodi.debug(message="Received handle:\n{}".format(self.torrent_url), prefix="PeertubePlayer")
:param url, temp_dir: str
:return: None
"""
Thread.__init__(self)
self.torrent = url
self.temp_dir = temp_dir
def debug(self, message):
"""Log a debug message
:param str message: Message to log (will be prefixed with the name of
the class)
"""
kodi.debug(message=message, prefix="PeertubeDownloader")
def run(self):
"""
Download the torrent specified by self.torrent
:param: None
:return: None
"""
self.debug("Opening BitTorent session")
# Open BitTorrent session
ses = libtorrent.session()
ses.listen_on(6881, 6891)
# Add torrent
self.debug("Adding torrent {}".format(self.torrent))
h = ses.add_torrent({"url": self.torrent, "save_path": self.temp_dir})
# Set sequential mode to allow watching while downloading
h.set_sequential_download(True)
# Download torrent
self.debug("Downloading torrent {}".format(self.torrent))
signal_sent = 0
while not h.is_seed():
xbmc.sleep(1000)
s = h.status()
# Inform addon that all the metadata has been downloaded and that
# it may start playing the torrent
if s.state >=3 and signal_sent == 0:
self.debug("Received all torrent metadata, notifying"
" PeertubeAddon")
i = h.torrent_file()
f = self.temp_dir + i.name()
AddonSignals.sendSignal("metadata_downloaded", {"file": f})
signal_sent = 1
# Callback when the playback is stopped. It is used to pause the torrent to
# avoid downloading in background a video which may never be played.
def onPlayBackStopped(self):
if self.torrent_url is not None:
kodi.debug(message="Playback stopped: pausing torrent...", prefix="PeertubePlayer")
# Get the torrent handle
torrent = xbmcvfs.File(self.torrent_url)
# Call seek() to pause the torrent. 0 is used as second argument so
# that GetLength() is not called.
torrent.seek(0, 0)
self.torrent_url = None
class PeertubeService():
"""
@ -83,13 +46,10 @@ class PeertubeService():
def __init__(self):
"""
PeertubeService initialisation function
Create an instance of PeertubePlayer that will always be active
(required to monitor the events and call the callbacks)
"""
# Create our temporary directory
self.temp = "{}{}".format(xbmc.translatePath("special://temp"),
"plugin.video.peertube/")
if not xbmcvfs.exists(self.temp):
xbmcvfs.mkdir(self.temp)
self.player = PeertubePlayer()
def debug(self, message):
"""Log a debug message
@ -99,43 +59,29 @@ class PeertubeService():
"""
kodi.debug(message=message, prefix="PeertubeService")
def download_torrent(self, data):
"""
Start a downloader thread to download torrent specified by data["url"]
:param data: dict
:return: None
"""
self.debug("Received a start_download signal")
downloader = PeertubeDownloader(data["url"], self.temp)
downloader.start()
def run(self):
"""
Main loop of the PeertubeService class
It registers the start_download signal to start a PeertubeDownloader
thread when needed, and exit when Kodi is shutting down.
"""
self.debug("Starting")
# Launch the download_torrent callback function when the
# "start_download" signal is received
# Signal that is sent by the main script of the add-on
AddonSignals.registerSlot(kodi.addon_id,
"start_download",
self.download_torrent)
"get_torrent",
self.player.receive_torrent)
# Monitor Kodi's shutdown signal
self.debug("Service started, waiting for signals")
# Display a notification now that the service started.
self.debug("Service started")
if kodi.get_setting("service_start_notif") == "true":
kodi.notif_info(title=kodi.get_string(30400),
message=kodi.get_string(30401))
# Run the service until Kodi exits
monitor = xbmc.Monitor()
while not monitor.abortRequested():
if monitor.waitForAbort(1):
# Abort was requested while waiting. We must exit
# TODO: Clean temporary directory
# Abort was requested while waiting. We must exit.
self.debug("Exiting")
break
@ -143,18 +89,5 @@ if __name__ == "__main__":
# Create a PeertubeService instance
service = PeertubeService()
# Import libtorrent here to manage when the library is not installed
try:
from python_libtorrent import libtorrent
LIBTORRENT_IMPORTED = True
except ImportError as exception:
LIBTORRENT_IMPORTED = False
service.debug("The libtorrent library could not be imported because of"
" the following error:\n{}".format(exception))
# Save whether libtorrent could be imported as a window property so that
# this information can be retrieved by the add-on
kodi.set_property("libtorrent_imported", str(LIBTORRENT_IMPORTED))
# Start the service
service.run()