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:
@ -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>
|
||||||
|
@ -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"
|
||||||
|
@ -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"
|
||||||
|
@ -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"
|
||||||
|
@ -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))
|
||||||
|
143
service.py
143
service.py
@ -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()
|
||||||
|
Reference in New Issue
Block a user