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:
parent
5eb7a46f34
commit
1ebb06d271
|
@ -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>
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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))
|
||||
|
|
143
service.py
143
service.py
|
@ -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()
|
||||
|
|
Loading…
Reference in New Issue