1
0
mirror of https://framagit.org/StCyr/plugin.video.peertube synced 2025-06-05 22:09:16 +02:00

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"> <extension point="xbmc.python.pluginsource" library="main.py">
<provides>video</provides> <provides>video</provides>
</extension> </extension>
<!--<extension point="xbmc.service" library="service.py"/>--> <extension point="xbmc.service" library="service.py"/>
<extension point="xbmc.addon.metadata"> <extension point="xbmc.addon.metadata">
<summary lang="de_DE">Add-on für PeerTube</summary> <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> <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" msgstr "PeerTube-Dienst gestartet"
msgctxt "#30401" msgctxt "#30401"
msgid "Torrents can now be downloaded." msgid "Torrents can now be controlled."
msgstr "Torrents können jetzt heruntergeladen werden." msgstr ""
msgctxt "#30402" msgctxt "#30402"
msgid "Request error" msgid "Request error"

View File

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

View File

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

View File

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

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- 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) 2018 Cyrille Bollu
Copyright (C) 2021 Thomas Bétous Copyright (C) 2021 Thomas Bétous
@ -8,73 +8,36 @@
SPDX-License-Identifier: GPL-3.0-only SPDX-License-Identifier: GPL-3.0-only
See LICENSE.txt for more information. See LICENSE.txt for more information.
""" """
import AddonSignals
import AddonSignals # Module exists only in Kodi - pylint: disable=import-error import xbmc
from threading import Thread import xbmcvfs
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 kodi from resources.lib.kodi_utils import kodi
class PeertubeDownloader(Thread): class PeertubePlayer(xbmc.Player):
""" # Initialize the attributes and call the parent class constructor
A class to download PeerTube torrents in the background def __init__(self):
""" self.torrent_url = None
super(xbmc.Player, self).__init__()
# super().__init__()
def __init__(self, url, temp_dir): # 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.
Initialise a PeertubeDownloader instance for downloading the torrent def receive_torrent(self, data):
specified by url self.torrent_url = data["torrent_url"]
kodi.debug(message="Received handle:\n{}".format(self.torrent_url), prefix="PeertubePlayer")
:param url, temp_dir: str # Callback when the playback is stopped. It is used to pause the torrent to
:return: None # avoid downloading in background a video which may never be played.
""" def onPlayBackStopped(self):
Thread.__init__(self) if self.torrent_url is not None:
self.torrent = url kodi.debug(message="Playback stopped: pausing torrent...", prefix="PeertubePlayer")
self.temp_dir = temp_dir # Get the torrent handle
torrent = xbmcvfs.File(self.torrent_url)
def debug(self, message): # Call seek() to pause the torrent. 0 is used as second argument so
"""Log a debug message # that GetLength() is not called.
torrent.seek(0, 0)
:param str message: Message to log (will be prefixed with the name of self.torrent_url = None
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
class PeertubeService(): class PeertubeService():
""" """
@ -83,13 +46,10 @@ class PeertubeService():
def __init__(self): 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.player = PeertubePlayer()
self.temp = "{}{}".format(xbmc.translatePath("special://temp"),
"plugin.video.peertube/")
if not xbmcvfs.exists(self.temp):
xbmcvfs.mkdir(self.temp)
def debug(self, message): def debug(self, message):
"""Log a debug message """Log a debug message
@ -99,43 +59,29 @@ class PeertubeService():
""" """
kodi.debug(message=message, prefix="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): def run(self):
""" """
Main loop of the PeertubeService class 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") # Signal that is sent by the main script of the add-on
# Launch the download_torrent callback function when the
# "start_download" signal is received
AddonSignals.registerSlot(kodi.addon_id, AddonSignals.registerSlot(kodi.addon_id,
"start_download", "get_torrent",
self.download_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": if kodi.get_setting("service_start_notif") == "true":
kodi.notif_info(title=kodi.get_string(30400), kodi.notif_info(title=kodi.get_string(30400),
message=kodi.get_string(30401)) message=kodi.get_string(30401))
# Run the service until Kodi exits
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
self.debug("Exiting") self.debug("Exiting")
break break
@ -143,18 +89,5 @@ if __name__ == "__main__":
# Create a PeertubeService instance # Create a PeertubeService instance
service = PeertubeService() 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 # Start the service
service.run() service.run()