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"> | ||||
|     <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() | ||||
|   | ||||
		Reference in New Issue
	
	Block a user