2019-02-18 01:17:21 +01:00
|
|
|
import os
|
2019-02-19 00:15:03 +01:00
|
|
|
import re
|
2019-02-18 01:17:21 +01:00
|
|
|
import threading
|
|
|
|
|
|
|
|
from platypush.context import get_bus, get_plugin
|
|
|
|
from platypush.plugins.media import PlayerState, MediaPlugin
|
|
|
|
from platypush.message.event.media import MediaPlayEvent, MediaPlayRequestEvent, \
|
2019-06-21 02:13:14 +02:00
|
|
|
MediaPauseEvent, MediaStopEvent, NewPlayingMediaEvent, MediaSeekEvent
|
2019-02-18 01:17:21 +01:00
|
|
|
|
|
|
|
from platypush.plugins import action
|
|
|
|
|
|
|
|
|
|
|
|
class MediaMpvPlugin(MediaPlugin):
|
|
|
|
"""
|
|
|
|
Plugin to control MPV instances
|
|
|
|
|
|
|
|
Requires:
|
|
|
|
|
|
|
|
* **python-mpv** (``pip install python-mpv``)
|
|
|
|
* **mpv** executable on your system
|
|
|
|
"""
|
|
|
|
|
2019-02-19 00:15:03 +01:00
|
|
|
_default_mpv_args = {
|
|
|
|
'ytdl': True,
|
|
|
|
'start_event_thread': True,
|
|
|
|
}
|
|
|
|
|
|
|
|
def __init__(self, args=None, *argv, **kwargs):
|
2019-02-18 01:17:21 +01:00
|
|
|
"""
|
|
|
|
Create the MPV wrapper.
|
|
|
|
|
|
|
|
:param args: Default arguments that will be passed to the mpv executable
|
2019-02-19 00:15:03 +01:00
|
|
|
as a key-value dict (names without the `--` prefix). See `man mpv`
|
|
|
|
for available options.
|
|
|
|
:type args: dict[str, str]
|
2019-02-18 01:17:21 +01:00
|
|
|
"""
|
|
|
|
|
|
|
|
super().__init__(*argv, **kwargs)
|
|
|
|
|
2019-02-19 00:15:03 +01:00
|
|
|
self.args = self._default_mpv_args
|
|
|
|
if args:
|
|
|
|
self.args.update(args)
|
|
|
|
|
2019-02-18 01:17:21 +01:00
|
|
|
self._player = None
|
|
|
|
self._is_playing_torrent = False
|
2019-02-19 13:13:17 +01:00
|
|
|
self._playback_rebounce_event = threading.Event()
|
|
|
|
self._on_stop_callbacks = []
|
2019-02-18 01:17:21 +01:00
|
|
|
|
2019-02-19 00:15:03 +01:00
|
|
|
def _init_mpv(self, args=None):
|
|
|
|
import mpv
|
2019-02-18 01:17:21 +01:00
|
|
|
|
2019-02-19 00:15:03 +01:00
|
|
|
mpv_args = self.args.copy()
|
|
|
|
if args:
|
|
|
|
mpv_args.update(args)
|
2019-02-18 01:17:21 +01:00
|
|
|
|
2019-02-19 01:02:07 +01:00
|
|
|
for k,v in self._env.items():
|
2019-02-19 00:58:26 +01:00
|
|
|
os.environ[k] = v
|
|
|
|
|
2019-02-19 00:15:03 +01:00
|
|
|
self._player = mpv.MPV(**mpv_args)
|
|
|
|
self._player.register_event_callback(self._event_callback())
|
2019-02-18 01:17:21 +01:00
|
|
|
|
2019-02-19 00:15:03 +01:00
|
|
|
def _event_callback(self):
|
|
|
|
def callback(event):
|
|
|
|
from mpv import MpvEventID as Event
|
2019-02-19 13:13:17 +01:00
|
|
|
from mpv import MpvEventEndFile as EndFile
|
|
|
|
|
2019-02-19 10:47:10 +01:00
|
|
|
self.logger.info('Received mpv event: {}'.format(event))
|
2019-02-18 01:17:21 +01:00
|
|
|
|
2019-02-19 00:15:03 +01:00
|
|
|
evt = event.get('event_id')
|
|
|
|
if not evt:
|
|
|
|
return
|
2019-02-18 01:17:21 +01:00
|
|
|
|
2019-02-19 00:15:03 +01:00
|
|
|
bus = get_bus()
|
2019-06-21 02:13:14 +02:00
|
|
|
if (evt == Event.FILE_LOADED or evt == Event.START_FILE) and self._get_current_resource():
|
2019-02-19 13:13:17 +01:00
|
|
|
self._playback_rebounce_event.set()
|
2019-06-21 02:13:14 +02:00
|
|
|
bus.post(NewPlayingMediaEvent(resource=self._get_current_resource(), title=self._player.filename))
|
|
|
|
bus.post(MediaPlayEvent(resource=self._get_current_resource(), title=self._player.filename))
|
2019-02-19 13:13:17 +01:00
|
|
|
elif evt == Event.PLAYBACK_RESTART:
|
|
|
|
self._playback_rebounce_event.set()
|
2019-02-19 00:15:03 +01:00
|
|
|
elif evt == Event.PAUSE:
|
2019-06-21 02:13:14 +02:00
|
|
|
bus.post(MediaPauseEvent(resource=self._get_current_resource(), title=self._player.filename))
|
2019-02-19 00:15:03 +01:00
|
|
|
elif evt == Event.UNPAUSE:
|
2019-06-21 02:13:14 +02:00
|
|
|
bus.post(MediaPlayEvent(resource=self._get_current_resource(), title=self._player.filename))
|
2019-02-19 13:13:17 +01:00
|
|
|
elif evt == Event.SHUTDOWN or (
|
|
|
|
evt == Event.END_FILE and event.get('event', {}).get('reason')
|
|
|
|
in [EndFile.EOF_OR_INIT_FAILURE, EndFile.ABORTED, EndFile.QUIT]):
|
|
|
|
playback_rebounced = self._playback_rebounce_event.wait(timeout=0.5)
|
2019-02-19 10:56:05 +01:00
|
|
|
if playback_rebounced:
|
2019-02-19 13:13:17 +01:00
|
|
|
self._playback_rebounce_event.clear()
|
2019-02-19 10:56:05 +01:00
|
|
|
return
|
|
|
|
|
2019-02-19 11:12:57 +01:00
|
|
|
self._player = None
|
2019-02-19 00:15:03 +01:00
|
|
|
bus.post(MediaStopEvent())
|
2019-02-19 13:13:17 +01:00
|
|
|
|
|
|
|
for callback in self._on_stop_callbacks:
|
|
|
|
callback()
|
2019-06-21 02:13:14 +02:00
|
|
|
elif evt == Event.SEEK:
|
|
|
|
bus.post(MediaSeekEvent(position=self._player.playback_time))
|
|
|
|
|
2019-02-19 00:15:03 +01:00
|
|
|
return callback
|
|
|
|
|
2019-06-21 02:13:14 +02:00
|
|
|
@staticmethod
|
|
|
|
def _get_youtube_link(resource):
|
2019-02-19 00:15:03 +01:00
|
|
|
base_url = 'https://youtu.be/'
|
|
|
|
regexes = ['^https://(www\.)?youtube.com/watch\?v=([^?&#]+)',
|
|
|
|
'^https://(www\.)?youtu.be.com/([^?&#]+)',
|
|
|
|
'^(youtube:video):([^?&#]+)']
|
|
|
|
|
|
|
|
for regex in regexes:
|
|
|
|
m = re.search(regex, resource)
|
|
|
|
if m: return base_url + m.group(2)
|
|
|
|
return None
|
2019-02-18 01:17:21 +01:00
|
|
|
|
|
|
|
@action
|
2019-02-19 00:15:03 +01:00
|
|
|
def execute(self, cmd, **args):
|
2019-02-18 01:17:21 +01:00
|
|
|
"""
|
|
|
|
Execute a raw mpv command.
|
|
|
|
"""
|
2019-02-19 00:15:03 +01:00
|
|
|
if not self._player:
|
2019-06-21 02:13:14 +02:00
|
|
|
return None, 'No mpv instance is running'
|
2019-02-19 00:15:03 +01:00
|
|
|
return self._player.command(cmd, *args)
|
2019-02-18 01:17:21 +01:00
|
|
|
|
|
|
|
@action
|
2019-02-19 00:15:03 +01:00
|
|
|
def play(self, resource, subtitles=None, **args):
|
2019-02-18 01:17:21 +01:00
|
|
|
"""
|
|
|
|
Play a resource.
|
|
|
|
|
|
|
|
:param resource: Resource to play - can be a local file or a remote URL
|
|
|
|
:type resource: str
|
|
|
|
|
|
|
|
:param subtitles: Path to optional subtitle file
|
|
|
|
:type subtitles: str
|
|
|
|
|
2019-02-19 00:15:03 +01:00
|
|
|
:param args: Extra runtime arguments that will be passed to the
|
|
|
|
mpv executable as a key-value dict (keys without `--` prefix)
|
|
|
|
:type args: dict[str,str]
|
2019-02-18 01:17:21 +01:00
|
|
|
"""
|
|
|
|
|
|
|
|
get_bus().post(MediaPlayRequestEvent(resource=resource))
|
2019-02-19 00:15:03 +01:00
|
|
|
self._init_mpv(args)
|
|
|
|
|
2019-02-18 01:17:21 +01:00
|
|
|
if subtitles:
|
2019-02-19 00:15:03 +01:00
|
|
|
args['sub_file'] = self.get_subtitles_file(subtitles)
|
2019-02-18 01:17:21 +01:00
|
|
|
|
|
|
|
resource = self._get_resource(resource)
|
2019-02-19 13:13:17 +01:00
|
|
|
|
2019-02-18 01:17:21 +01:00
|
|
|
if resource.startswith('file://'):
|
|
|
|
resource = resource[7:]
|
|
|
|
elif resource.startswith('magnet:?'):
|
|
|
|
self._is_playing_torrent = True
|
|
|
|
return get_plugin('media.webtorrent').play(resource)
|
2019-02-19 00:15:03 +01:00
|
|
|
else:
|
|
|
|
yt_resource = self._get_youtube_link(resource)
|
|
|
|
if yt_resource: resource = yt_resource
|
2019-02-18 01:17:21 +01:00
|
|
|
|
|
|
|
self._is_playing_torrent = False
|
2019-06-21 02:13:14 +02:00
|
|
|
self._player.play(resource)
|
2019-02-19 00:15:03 +01:00
|
|
|
return self.status()
|
|
|
|
|
2019-02-18 01:17:21 +01:00
|
|
|
@action
|
|
|
|
def pause(self):
|
|
|
|
""" Toggle the paused state """
|
2019-02-19 00:15:03 +01:00
|
|
|
if not self._player:
|
2019-06-21 02:13:14 +02:00
|
|
|
return None, 'No mpv instance is running'
|
2019-02-19 00:15:03 +01:00
|
|
|
|
|
|
|
self._player.pause = not self._player.pause
|
|
|
|
return self.status()
|
|
|
|
|
2019-02-18 01:17:21 +01:00
|
|
|
@action
|
|
|
|
def quit(self):
|
2019-06-21 02:13:14 +02:00
|
|
|
""" Stop and quit the player """
|
2019-02-18 01:17:21 +01:00
|
|
|
self._stop_torrent()
|
2019-02-19 00:15:03 +01:00
|
|
|
if not self._player:
|
2019-06-21 02:13:14 +02:00
|
|
|
return None, 'No mpv instance is running'
|
2019-02-19 00:15:03 +01:00
|
|
|
|
|
|
|
self._player.quit()
|
2019-06-21 02:13:14 +02:00
|
|
|
self._player.terminate()
|
2019-02-19 00:15:03 +01:00
|
|
|
self._player = None
|
2019-06-21 02:13:14 +02:00
|
|
|
return {'state': PlayerState.STOP.value}
|
2019-02-19 00:15:03 +01:00
|
|
|
|
|
|
|
@action
|
|
|
|
def stop(self):
|
2019-06-21 02:13:14 +02:00
|
|
|
""" Stop and quit the player """
|
2019-02-19 00:15:03 +01:00
|
|
|
return self.quit()
|
2019-02-18 01:17:21 +01:00
|
|
|
|
|
|
|
@action
|
|
|
|
def voldown(self, step=10.0):
|
|
|
|
""" Volume down by (default: 10)% """
|
2019-02-19 00:15:03 +01:00
|
|
|
if not self._player:
|
2019-06-21 02:13:14 +02:00
|
|
|
return None, 'No mpv instance is running'
|
2019-02-19 00:15:03 +01:00
|
|
|
return self.set_volume(self._player.volume-step)
|
2019-02-18 01:17:21 +01:00
|
|
|
|
|
|
|
@action
|
|
|
|
def volup(self, step=10.0):
|
|
|
|
""" Volume up by (default: 10)% """
|
2019-02-19 00:15:03 +01:00
|
|
|
if not self._player:
|
2019-06-21 02:13:14 +02:00
|
|
|
return None, 'No mpv instance is running'
|
2019-02-19 00:15:03 +01:00
|
|
|
return self.set_volume(self._player.volume+step)
|
|
|
|
|
|
|
|
@action
|
|
|
|
def set_volume(self, volume):
|
|
|
|
"""
|
|
|
|
Set the volume
|
|
|
|
|
|
|
|
:param volume: Volume value between 0 and 100
|
|
|
|
:type volume: float
|
|
|
|
"""
|
|
|
|
if not self._player:
|
2019-06-21 02:13:14 +02:00
|
|
|
return None, 'No mpv instance is running'
|
2019-02-19 00:15:03 +01:00
|
|
|
|
2019-06-21 02:13:14 +02:00
|
|
|
volume = max(0, min([self._player.volume_max, volume]))
|
2019-02-19 00:15:03 +01:00
|
|
|
self._player.volume = volume
|
2019-06-21 02:13:14 +02:00
|
|
|
return {'volume': volume}
|
2019-02-19 00:15:03 +01:00
|
|
|
|
|
|
|
@action
|
|
|
|
def seek(self, position):
|
|
|
|
"""
|
|
|
|
Seek backward/forward by the specified number of seconds
|
|
|
|
|
2019-06-21 02:13:14 +02:00
|
|
|
:param position: Number of seconds relative to the current cursor
|
|
|
|
:type position: int
|
2019-02-19 00:15:03 +01:00
|
|
|
"""
|
|
|
|
if not self._player:
|
2019-06-21 02:13:14 +02:00
|
|
|
return None, 'No mpv instance is running'
|
2019-02-19 00:15:03 +01:00
|
|
|
if not self._player.seekable:
|
2019-06-21 02:13:14 +02:00
|
|
|
return None, 'The resource is not seekable'
|
2019-02-19 00:15:03 +01:00
|
|
|
pos = min(self._player.time_pos+self._player.time_remaining,
|
|
|
|
max(0, position))
|
|
|
|
self._player.time_pos = pos
|
2019-06-21 02:13:14 +02:00
|
|
|
return {'position': pos}
|
2019-02-18 01:17:21 +01:00
|
|
|
|
|
|
|
@action
|
|
|
|
def back(self, offset=60.0):
|
|
|
|
""" Back by (default: 60) seconds """
|
2019-02-19 00:15:03 +01:00
|
|
|
if not self._player:
|
2019-06-21 02:13:14 +02:00
|
|
|
return None, 'No mpv instance is running'
|
2019-02-19 00:15:03 +01:00
|
|
|
if not self._player.seekable:
|
2019-06-21 02:13:14 +02:00
|
|
|
return None, 'The resource is not seekable'
|
2019-02-19 00:15:03 +01:00
|
|
|
pos = max(0, self._player.time_pos-offset)
|
|
|
|
return self.seek(pos)
|
2019-02-18 01:17:21 +01:00
|
|
|
|
|
|
|
@action
|
|
|
|
def forward(self, offset=60.0):
|
|
|
|
""" Forward by (default: 60) seconds """
|
2019-02-19 00:15:03 +01:00
|
|
|
if not self._player:
|
2019-06-21 02:13:14 +02:00
|
|
|
return None, 'No mpv instance is running'
|
2019-02-19 00:15:03 +01:00
|
|
|
if not self._player.seekable:
|
2019-06-21 02:13:14 +02:00
|
|
|
return None, 'The resource is not seekable'
|
2019-02-19 00:15:03 +01:00
|
|
|
pos = min(self._player.time_pos+self._player.time_remaining,
|
|
|
|
self._player.time_pos+offset)
|
|
|
|
return self.seek(pos)
|
2019-02-18 01:17:21 +01:00
|
|
|
|
|
|
|
@action
|
2019-02-19 00:15:03 +01:00
|
|
|
def next(self):
|
|
|
|
""" Play the next item in the queue """
|
|
|
|
if not self._player:
|
2019-06-21 02:13:14 +02:00
|
|
|
return None, 'No mpv instance is running'
|
2019-02-19 00:15:03 +01:00
|
|
|
self._player.playlist_next()
|
2019-02-18 01:17:21 +01:00
|
|
|
|
|
|
|
@action
|
2019-02-19 00:15:03 +01:00
|
|
|
def prev(self):
|
|
|
|
""" Play the previous item in the queue """
|
|
|
|
if not self._player:
|
2019-06-21 02:13:14 +02:00
|
|
|
return None, 'No mpv instance is running'
|
2019-02-19 00:15:03 +01:00
|
|
|
self._player.playlist_prev()
|
2019-02-18 01:17:21 +01:00
|
|
|
|
|
|
|
@action
|
2019-06-21 02:13:14 +02:00
|
|
|
def toggle_subtitles(self, visible=None):
|
2019-02-19 00:15:03 +01:00
|
|
|
""" Toggle the subtitles visibility """
|
|
|
|
return self.toggle_property('sub_visibility')
|
2019-02-18 01:17:21 +01:00
|
|
|
|
|
|
|
@action
|
2019-02-19 00:15:03 +01:00
|
|
|
def toggle_fullscreen(self, fullscreen=None):
|
|
|
|
""" Toggle the fullscreen mode """
|
|
|
|
return self.toggle_property('fullscreen')
|
2019-02-18 01:17:21 +01:00
|
|
|
|
|
|
|
@action
|
2019-02-19 00:15:03 +01:00
|
|
|
def toggle_property(self, property):
|
2019-02-18 01:17:21 +01:00
|
|
|
"""
|
2019-02-19 00:15:03 +01:00
|
|
|
Toggle or sets the value of an mpv property (e.g. fullscreen,
|
|
|
|
sub_visibility etc.). See ``man mpv`` for a full list of properties
|
|
|
|
|
|
|
|
:param property: Property to toggle
|
2019-02-18 01:17:21 +01:00
|
|
|
"""
|
2019-02-19 00:15:03 +01:00
|
|
|
if not self._player:
|
|
|
|
return (None, 'No mpv instance is running')
|
2019-02-18 01:17:21 +01:00
|
|
|
|
2019-02-19 00:15:03 +01:00
|
|
|
if not hasattr(self._player, property):
|
|
|
|
self.logger.warning('No such mpv property: {}'.format(property))
|
|
|
|
|
|
|
|
value = not getattr(self._player, property)
|
|
|
|
setattr(self._player, property, value)
|
|
|
|
return { property: value }
|
2019-02-18 01:17:21 +01:00
|
|
|
|
|
|
|
@action
|
2019-02-19 00:15:03 +01:00
|
|
|
def get_property(self, property):
|
2019-02-18 01:17:21 +01:00
|
|
|
"""
|
2019-02-19 00:15:03 +01:00
|
|
|
Get a player property (e.g. pause, fullscreen etc.). See
|
|
|
|
``man mpv`` for a full list of the available properties
|
2019-02-18 01:17:21 +01:00
|
|
|
"""
|
2019-02-19 00:15:03 +01:00
|
|
|
if not self._player:
|
|
|
|
return (None, 'No mpv instance is running')
|
|
|
|
return getattr(self._player, property)
|
2019-02-18 01:17:21 +01:00
|
|
|
|
|
|
|
@action
|
2019-02-19 00:15:03 +01:00
|
|
|
def set_property(self, **props):
|
2019-02-18 01:17:21 +01:00
|
|
|
"""
|
2019-02-19 00:15:03 +01:00
|
|
|
Set the value of an mpv property (e.g. fullscreen, sub_visibility
|
|
|
|
etc.). See ``man mpv`` for a full list of properties
|
2019-02-18 01:17:21 +01:00
|
|
|
|
2019-02-19 00:15:03 +01:00
|
|
|
:param props: Key-value args for the properties to set
|
|
|
|
:type props: dict
|
2019-02-18 01:17:21 +01:00
|
|
|
"""
|
2019-02-19 00:15:03 +01:00
|
|
|
if not self._player:
|
|
|
|
return (None, 'No mpv instance is running')
|
|
|
|
|
2019-02-19 01:09:53 +01:00
|
|
|
for k,v in props.items():
|
2019-02-19 00:15:03 +01:00
|
|
|
setattr(self._player, k, v)
|
|
|
|
return props
|
2019-02-18 01:17:21 +01:00
|
|
|
|
|
|
|
@action
|
2019-06-21 02:13:14 +02:00
|
|
|
def set_subtitles(self, filename, *args, **kwargs):
|
2019-02-19 00:15:03 +01:00
|
|
|
""" Sets media subtitles from filename """
|
|
|
|
return self.set_property(subfile=filename, sub_visibility=True)
|
2019-02-18 01:17:21 +01:00
|
|
|
|
2019-02-19 00:15:03 +01:00
|
|
|
@action
|
|
|
|
def remove_subtitles(self):
|
|
|
|
""" Removes (hides) the subtitles """
|
|
|
|
if not self._player:
|
|
|
|
return (None, 'No mpv instance is running')
|
|
|
|
self._player.sub_visibility = False
|
2019-02-18 01:17:21 +01:00
|
|
|
|
|
|
|
@action
|
2019-02-19 00:15:03 +01:00
|
|
|
def is_playing(self):
|
2019-02-18 01:17:21 +01:00
|
|
|
"""
|
2019-02-19 00:15:03 +01:00
|
|
|
:returns: True if it's playing, False otherwise
|
2019-02-18 01:17:21 +01:00
|
|
|
"""
|
2019-02-19 00:15:03 +01:00
|
|
|
if not self._player:
|
|
|
|
return False
|
|
|
|
return not self._player.pause
|
2019-02-18 01:17:21 +01:00
|
|
|
|
|
|
|
@action
|
2019-02-19 00:15:03 +01:00
|
|
|
def load(self, resource, **args):
|
2019-02-18 01:17:21 +01:00
|
|
|
"""
|
2019-02-19 00:15:03 +01:00
|
|
|
Load/queue a resource/video to the player
|
2019-02-18 01:17:21 +01:00
|
|
|
"""
|
2019-02-19 00:15:03 +01:00
|
|
|
if not self._player:
|
|
|
|
return self.play(resource, **args)
|
2019-06-21 02:13:14 +02:00
|
|
|
return self._player.loadfile(resource, mode='append-play', **args)
|
2019-02-18 01:17:21 +01:00
|
|
|
|
2019-02-19 00:15:03 +01:00
|
|
|
@action
|
|
|
|
def mute(self):
|
|
|
|
""" Toggle mute state """
|
|
|
|
if not self._player:
|
|
|
|
return (None, 'No mpv instance is running')
|
|
|
|
mute = not self._player.mute
|
|
|
|
self._player.mute = mute
|
|
|
|
return { 'muted': mute }
|
2019-02-18 01:17:21 +01:00
|
|
|
|
|
|
|
@action
|
2019-02-19 00:15:03 +01:00
|
|
|
def set_position(self, position):
|
2019-02-18 01:17:21 +01:00
|
|
|
"""
|
2019-02-19 00:15:03 +01:00
|
|
|
Seek backward/forward to the specified absolute position (same as ``seek``)
|
2019-02-18 01:17:21 +01:00
|
|
|
"""
|
2019-02-19 00:15:03 +01:00
|
|
|
return self.seek(position)
|
2019-02-18 01:17:21 +01:00
|
|
|
|
2019-02-19 00:15:03 +01:00
|
|
|
@action
|
|
|
|
def status(self):
|
|
|
|
"""
|
|
|
|
Get the current player state.
|
2019-02-18 01:17:21 +01:00
|
|
|
|
2019-02-19 00:15:03 +01:00
|
|
|
:returns: A dictionary containing the current state.
|
2019-02-18 01:17:21 +01:00
|
|
|
|
2019-02-19 00:15:03 +01:00
|
|
|
Example::
|
2019-02-18 01:17:21 +01:00
|
|
|
|
2019-02-19 00:15:03 +01:00
|
|
|
output = {
|
|
|
|
"filename": "filename or stream URL",
|
|
|
|
"state": "play" # or "stop" or "pause"
|
|
|
|
}
|
2019-02-18 01:17:21 +01:00
|
|
|
"""
|
2019-02-19 00:15:03 +01:00
|
|
|
if not self._player or not hasattr(self._player, 'pause'):
|
2019-06-21 02:13:14 +02:00
|
|
|
return {'state': PlayerState.STOP.value}
|
2019-02-18 01:17:21 +01:00
|
|
|
|
2019-02-19 00:15:03 +01:00
|
|
|
return {
|
2019-06-21 02:13:14 +02:00
|
|
|
'alang': getattr(self._player, 'alang'),
|
|
|
|
'aspect': getattr(self._player, 'aspect'),
|
|
|
|
'audio': getattr(self._player, 'audio'),
|
|
|
|
'audio_bitrate': getattr(self._player, 'audio_bitrate'),
|
|
|
|
'audio_buffer': getattr(self._player, 'audio_buffer'),
|
|
|
|
'audio_channels': getattr(self._player, 'audio_channels'),
|
|
|
|
'audio_client_name': getattr(self._player, 'audio_client_name'),
|
|
|
|
'audio_codec': getattr(self._player, 'audio_codec'),
|
|
|
|
'audio_codec_name': getattr(self._player, 'audio_codec_name'),
|
|
|
|
'audio_delay': getattr(self._player, 'audio_delay'),
|
|
|
|
'audio_device': getattr(self._player, 'audio_device'),
|
|
|
|
'audio_device_list': getattr(self._player, 'audio_device_list'),
|
|
|
|
'audio_exclusive': getattr(self._player, 'audio_exclusive'),
|
|
|
|
'audio_file_paths': getattr(self._player, 'audio_file_paths'),
|
|
|
|
'audio_files': getattr(self._player, 'audio_files'),
|
|
|
|
'audio_format': getattr(self._player, 'audio_format'),
|
|
|
|
'audio_out_params': getattr(self._player, 'audio_out_params'),
|
|
|
|
'audio_params': getattr(self._player, 'audio_params'),
|
|
|
|
'audio_mixer_device': getattr(self._player, 'alsa_mixer_device'),
|
|
|
|
'audio_mixer_index': getattr(self._player, 'alsa_mixer_index'),
|
|
|
|
'audio_mixer_name': getattr(self._player, 'alsa_mixer_name'),
|
|
|
|
'autosub': getattr(self._player, 'autosub'),
|
|
|
|
'autosync': getattr(self._player, 'autosync'),
|
|
|
|
'background': getattr(self._player, 'background'),
|
|
|
|
'border': getattr(self._player, 'border'),
|
|
|
|
'brightness': getattr(self._player, 'brightness'),
|
|
|
|
'chapter': getattr(self._player, 'chapter'),
|
|
|
|
'chapter_list': getattr(self._player, 'chapter_list'),
|
|
|
|
'chapter_metadata': getattr(self._player, 'chapter_metadata'),
|
|
|
|
'chapters': getattr(self._player, 'chapters'),
|
|
|
|
'chapters_file': getattr(self._player, 'chapters_file'),
|
|
|
|
'clock': getattr(self._player, 'clock'),
|
|
|
|
'cookies': getattr(self._player, 'cookies'),
|
|
|
|
'cookies_file': getattr(self._player, 'cookies_file'),
|
|
|
|
'current_ao': getattr(self._player, 'current_ao'),
|
|
|
|
'current_vo': getattr(self._player, 'current_vo'),
|
|
|
|
'delay': getattr(self._player, 'delay'),
|
|
|
|
'display_names': getattr(self._player, 'display_names'),
|
|
|
|
'end': getattr(self._player, 'end'),
|
|
|
|
'endpos': getattr(self._player, 'endpos'),
|
|
|
|
'eof_reached': getattr(self._player, 'eof_reached'),
|
|
|
|
'file_format': getattr(self._player, 'file_format'),
|
|
|
|
'filename': getattr(self._player, 'filename'),
|
|
|
|
'file_size': getattr(self._player, 'file_size'),
|
|
|
|
'font': getattr(self._player, 'font'),
|
|
|
|
'fps': getattr(self._player, 'fps'),
|
|
|
|
'fullscreen': getattr(self._player, 'fs'),
|
|
|
|
'height': getattr(self._player, 'height'),
|
|
|
|
'idle': getattr(self._player, 'idle'),
|
|
|
|
'idle_active': getattr(self._player, 'idle_active'),
|
|
|
|
'length': getattr(self._player, 'playback_time', 0) + getattr(self._player, 'playtime_remaining', 0)
|
|
|
|
if getattr(self._player, 'playtime_remaining') else None,
|
|
|
|
'loop': getattr(self._player, 'loop'),
|
|
|
|
'media_title': getattr(self._player, 'loop'),
|
|
|
|
'mpv_configuration': getattr(self._player, 'mpv_configuration'),
|
|
|
|
'mpv_version': getattr(self._player, 'mpv_version'),
|
|
|
|
'mute': getattr(self._player, 'mute'),
|
|
|
|
'name': getattr(self._player, 'name'),
|
|
|
|
'pause': getattr(self._player, 'pause'),
|
|
|
|
'percent_pos': getattr(self._player, 'percent_pos'),
|
|
|
|
'playlist': getattr(self._player, 'playlist'),
|
|
|
|
'playlist_pos': getattr(self._player, 'playlist_pos'),
|
|
|
|
'position': getattr(self._player, 'playback_time'),
|
|
|
|
'quiet': getattr(self._player, 'quiet'),
|
|
|
|
'really_quiet': getattr(self._player, 'really_quiet'),
|
|
|
|
'saturation': getattr(self._player, 'saturation'),
|
|
|
|
'screen': getattr(self._player, 'screen'),
|
|
|
|
'screenshot_directory': getattr(self._player, 'screenshot_directory'),
|
|
|
|
'screenshot_format': getattr(self._player, 'screenshot_format'),
|
|
|
|
'screenshot_template': getattr(self._player, 'screenshot_template'),
|
|
|
|
'seekable': getattr(self._player, 'seekable'),
|
|
|
|
'seeking': getattr(self._player, 'seeking'),
|
|
|
|
'shuffle': getattr(self._player, 'shuffle'),
|
|
|
|
'speed': getattr(self._player, 'speed'),
|
|
|
|
'state': (PlayerState.PAUSE.value if self._player.pause else PlayerState.PLAY.value),
|
|
|
|
'stream_pos': getattr(self._player, 'stream_pos'),
|
|
|
|
'sub': getattr(self._player, 'sub'),
|
|
|
|
'sub_file_paths': getattr(self._player, 'sub_file_paths'),
|
|
|
|
'sub_files': getattr(self._player, 'sub_files'),
|
|
|
|
'sub_paths': getattr(self._player, 'sub_paths'),
|
|
|
|
'sub_text': getattr(self._player, 'sub_text'),
|
|
|
|
'subdelay': getattr(self._player, 'subdelay'),
|
|
|
|
'terminal': getattr(self._player, 'terminal'),
|
|
|
|
'time_start': getattr(self._player, 'time_start'),
|
|
|
|
'title': getattr(self._player, 'filename'),
|
|
|
|
'tv_alsa': getattr(self._player, 'tv_alsa'),
|
|
|
|
'tv_audio': getattr(self._player, 'tv_audio'),
|
|
|
|
'tv_audiorate': getattr(self._player, 'tv_audiorate'),
|
|
|
|
'tv_channels': getattr(self._player, 'tv_channels'),
|
|
|
|
'tv_device': getattr(self._player, 'tv_device'),
|
|
|
|
'tv_height': getattr(self._player, 'tv_height'),
|
|
|
|
'tv_volume': getattr(self._player, 'tv_volume'),
|
|
|
|
'tv_width': getattr(self._player, 'tv_width'),
|
|
|
|
'url': self._get_current_resource(),
|
|
|
|
'user_agent': getattr(self._player, 'user_agent'),
|
|
|
|
'video': getattr(self._player, 'video'),
|
|
|
|
'video_align_x': getattr(self._player, 'video_align_x'),
|
|
|
|
'video_align_y': getattr(self._player, 'video_align_y'),
|
|
|
|
'video_aspect': getattr(self._player, 'video_aspect'),
|
|
|
|
'video_bitrate': getattr(self._player, 'video_bitrate'),
|
|
|
|
'video_codec': getattr(self._player, 'video_codec'),
|
|
|
|
'video_format': getattr(self._player, 'video_format'),
|
|
|
|
'video_params': getattr(self._player, 'video_params'),
|
|
|
|
'video_sync': getattr(self._player, 'video_sync'),
|
|
|
|
'video_zoom': getattr(self._player, 'video_zoom'),
|
|
|
|
'vlang': getattr(self._player, 'vlang'),
|
|
|
|
'volume': getattr(self._player, 'volume'),
|
|
|
|
'volume_max': getattr(self._player, 'volume_max'),
|
|
|
|
'width': getattr(self._player, 'width'),
|
|
|
|
'window_minimized': getattr(self._player, 'window_minimized'),
|
|
|
|
'window_scale': getattr(self._player, 'window_scale'),
|
|
|
|
'working_directory': getattr(self._player, 'working_directory'),
|
|
|
|
'ytdl': getattr(self._player, 'ytdl'),
|
2019-02-19 00:15:03 +01:00
|
|
|
}
|
2019-02-18 01:17:21 +01:00
|
|
|
|
2019-02-19 13:13:17 +01:00
|
|
|
def on_stop(self, callback):
|
|
|
|
self._on_stop_callbacks.append(callback)
|
|
|
|
|
2019-02-19 00:15:03 +01:00
|
|
|
def _get_current_resource(self):
|
|
|
|
if not self._player or not self._player.stream_path:
|
|
|
|
return
|
2019-02-18 01:17:21 +01:00
|
|
|
|
2019-02-19 00:15:03 +01:00
|
|
|
return ('file://' if os.path.isfile(self._player.stream_path)
|
|
|
|
else '') + self._player.stream_path
|
2019-02-18 01:17:21 +01:00
|
|
|
|
|
|
|
|
|
|
|
# vim:sw=4:ts=4:et:
|