Refactored torrent module

This commit is contained in:
Fabio Manganiello 2019-02-04 01:01:39 +01:00
parent a4f80d4622
commit 286eb431f0
3 changed files with 91 additions and 130 deletions

View File

@ -46,15 +46,6 @@ class TorrentStateChangeEvent(TorrentEvent):
super().__init__(*args, **kwargs)
class TorrentStateChangeEvent(TorrentEvent):
"""
Event triggered upon torrent state change
"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
class TorrentDownloadCompletedEvent(TorrentEvent):
"""
Event triggered upon torrent state change
@ -74,4 +65,3 @@ class TorrentDownloadStopEvent(TorrentEvent):
# vim:sw=4:ts=4:et:

View File

@ -155,7 +155,7 @@ class MediaMplayerPlugin(MediaPlugin):
self._init_mplayer(mplayer_args)
else:
if not self._mplayer:
raise RuntimeError('MPlayer is not running')
self.logger.warning('MPlayer is not running')
cmd = '{}{}{}{}\n'.format(
prefix + ' ' if prefix else '',
@ -249,8 +249,7 @@ class MediaMplayerPlugin(MediaPlugin):
if resource.startswith('file://'):
resource = resource[7:]
elif resource.startswith('magnet:?'):
torrent_plugin = get_plugin('media.webtorrent')
return torrent_plugin.play(resource)
return get_plugin('media.webtorrent').play(resource)
return self._exec('loadfile', resource, mplayer_args=mplayer_args)

View File

@ -1,14 +1,17 @@
import datetime
import os
import select
import subprocess
import tempfile
import threading
import time
from platypush.config import Config
from platypush.context import get_bus, get_plugin
from platypush.message.response import Response
from platypush.plugins.media import PlayerState, MediaPlugin
from platypush.message.event.media import MediaPlayEvent, MediaPauseEvent, \
MediaStopEvent, NewPlayingMediaEvent
from platypush.message.event.torrent import TorrentDownloadStartEvent, \
TorrentDownloadCompletedEvent
from platypush.plugins import action
from platypush.utils import find_bins_in_path
@ -26,39 +29,19 @@ class MediaWebtorrentPlugin(MediaPlugin):
``npm install -g BlackLight/webtorrent-cli`` as my fork contains
the ``--[player]-args`` options to pass custom arguments to your
installed player)
* A media plugin configured for streaming (e.g. media.mplayer
or media.omxplayer)
"""
_supported_media_plugins = { 'media.mplayer', 'media.omxplayer' }
_supported_webtorrent_players = {'airplay', 'chromecast', 'mplayer',
'mpv', 'omxplayer', 'vlc', 'xbmc'}
# Download at least 10 MBs before starting streaming
_download_size_before_streaming = 10 * 2**20
def __init__(self, player_type=None, player_args=None, webtorrent_bin=None,
*args, **kwargs):
def __init__(self, webtorrent_bin=None, *args, **kwargs):
"""
Create the webtorrent wrapper
:param player_type: Player type to be used for streaming. Check
https://www.npmjs.com/package/webtorrent for a full list of the
supported players. If no player is specified then it will search in
your Platypush configuration for any enabled and supported media
plugin (either ``media.mplayer`` or ``media.omxplayer``) to
configure the streaming player. Currently supported options:
- airplay
- chromecast
- mplayer
- mpv
- omxlayer
- vlc
- xbmc
:type player_type: str
:param player_args: Extra arguments to pass to the player as a list.
For mplayer, vlc, omxplayer and mpv this will be a list of extra
arguments to be passed to the executable (e.g. fullscreen, volume,
audio sink etc.).
:type player_args: list[str]
media.webtorrent will use the default media player plugin you have
configured (e.g. mplayer, omxplayer) to stream the torrent.
:param webtorrent_bin: Path to your webtorrent executable. If not set,
then Platypush will search for the right executable in your PATH
@ -68,11 +51,12 @@ class MediaWebtorrentPlugin(MediaPlugin):
super().__init__(*args, **kwargs)
self._init_webtorrent_bin(webtorrent_bin=webtorrent_bin)
self._init_media_player(player_type=player_type,
player_args=player_args or [])
self._init_media_player()
def _init_webtorrent_bin(self, webtorrent_bin=None):
self._webtorrent_process = None
if not webtorrent_bin:
bin_name = 'webtorrent.exe' if os.name == 'nt' else 'webtorrent'
bins = find_bins_in_path(bin_name)
@ -94,89 +78,59 @@ class MediaWebtorrentPlugin(MediaPlugin):
self.webtorrent_bin = webtorrent_bin
def _init_media_player(self, player_type, player_args):
if player_type is None:
media_plugin = None
plugin_name = None
def _init_media_player(self):
self._media_plugin = None
plugin_name = None
for plugin_name in self._supported_media_plugins:
try:
media_plugin = get_plugin(plugin_name)
for plugin_name in self._supported_media_plugins:
try:
if Config.get(plugin_name):
self._media_plugin = get_plugin(plugin_name)
break
except:
pass
if not media_plugin:
raise RuntimeError(('No media player specified and no ' +
'compatible media plugin configured - ' +
'supported media plugins: {}').format(
self._supported_media_plugins))
self.player_type = plugin_name[len('media.'):]
self.player_args = media_plugin.args
else:
if player_type not in self._supported_webtorrent_players:
raise RuntimeError(('{} is not a supported player (supported ' +
'players: {})').format(
player_type,
self._supported_webtorrent_players))
self.player_type = player_type
self.player_args = player_args or []
if not self.player_args:
try:
plugin = get_plugin('media.' + player_type)
self.player_args = plugin.args
except:
pass
self._player = None
def _start_webtorrent(self, resource):
if self._player:
try:
self.quit()
except:
self.logger.debug('Failed to quit the previous instance: {}'.
format(str))
webtorrent_args = [self.webtorrent_bin, '--' + self.player_type,
resource]
if self.player_args:
webtorrent_args += ['--' + self.player_type + "-args='" + \
repr(' '.join(self.player_args)) + "'"]
popen_args = {
'stdin': subprocess.PIPE,
'stdout': subprocess.PIPE,
}
if self._env:
popen_args['env'] = self._env
else:
try:
plugin = get_plugin('media.' + player_type)
if plugin._env:
popen_args['env'] = plugin._env
except:
pass
self._player = subprocess.Popen(webtorrent_args, **popen_args)
if not self._media_plugin:
raise RuntimeError(('No media player specified and no ' +
'compatible media plugin configured - ' +
'supported media plugins: {}').format(
self._supported_media_plugins))
def _process_monitor(self):
def _process_monitor(self, resource, output_file):
def _thread():
if not self._player:
if not self._webtorrent_process:
return
self._player.wait()
get_bus().post(MediaStopEvent())
self._player = None
bus = get_bus()
bus.post(TorrentDownloadStartEvent(resource=resource))
while True:
try:
if os.path.getsize(output_file) > \
self._download_size_before_streaming:
break
except FileNotFoundError:
pass
self._media_plugin.play(output_file)
self._webtorrent_process.wait()
bus.post(TorrentDownloadCompletedEvent(resource=resource))
self._webtorrent_process = None
return _thread
def _get_torrent_download_path(self):
if self._media_plugin.download_dir:
# TODO set proper file name based on the torrent metadata
return os.path.join(self._media_plugin.download_dir,
'torrent_media_' + datetime.datetime.
today().strftime('%Y-%m-%d_%H-%M-%S-%f'))
else:
return tempfile.NamedTemporaryFile(delete=False).name
@action
def play(self, resource):
"""
@ -187,10 +141,25 @@ class MediaWebtorrentPlugin(MediaPlugin):
:type resource: str
"""
self._start_webtorrent(resource)
bus = get_bus()
bus.post(NewPlayingMediaEvent(resource=resource))
threading.Thread(target=self._process_monitor()).start()
if self._webtorrent_process:
try:
self.quit()
except:
self.logger.debug('Failed to quit the previous instance: {}'.
format(str))
output_file = self._get_torrent_download_path()
webtorrent_args = [self.webtorrent_bin, '--stdout', resource]
with open(output_file, 'w') as f:
self._webtorrent_process = subprocess.Popen(webtorrent_args,
stdout=f)
threading.Thread(target=self._process_monitor(
resource=resource, output_file=output_file)).start()
return { 'resource': resource }
@action
def stop(self):
@ -200,14 +169,18 @@ class MediaWebtorrentPlugin(MediaPlugin):
@action
def quit(self):
""" Quit the player """
if self._player and self._is_process_alive(self._player.pid):
self._player.terminate()
self._player.wait()
try: self._player.kill()
if self._media_plugin:
self._media_plugin.quit()
if self._webtorrent_process and self._is_process_alive(
self._webtorrent_process.pid):
self._webtorrent_process.terminate()
self._webtorrent_process.wait()
try: self._webtorrent_process.kill()
except: pass
bus.post(MediaStopEvent())
self._player = None
self._webtorrent_process = None
@action
def load(self, resource):
@ -217,14 +190,14 @@ class MediaWebtorrentPlugin(MediaPlugin):
return self.play(resource)
def _is_process_alive(self):
if not self._player:
if not self._webtorrent_process:
return False
try:
os.kill(self._player.pid, 0)
os.kill(self._webtorrent_process.pid, 0)
return True
except OSError:
self._player = None
self._webtorrent_process = None
return False
@action
@ -241,9 +214,8 @@ class MediaWebtorrentPlugin(MediaPlugin):
}
"""
return {'state': PlayerState.PLAY.value
if self._player and self._is_process_alive()
else PlayerState.STOP.value}
return {'state': self._media_plugin.status()
.get('state', PlayerState.STOP.value)}
# vim:sw=4:ts=4:et: