mirror of
https://github.com/powerline/powerline.git
synced 2025-07-26 07:16:31 +02:00
Merge branch 'enhance-now_playing' into develop
This commit is contained in:
commit
5635eea233
@ -29,6 +29,24 @@ object it should receive the following arguments:
|
|||||||
And also any other argument(s) specified by user in :ref:`args key
|
And also any other argument(s) specified by user in :ref:`args key
|
||||||
<config-themes-seg-args>` (no additional arguments by default).
|
<config-themes-seg-args>` (no additional arguments by default).
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
For powerline-lint to work properly the following things may be needed:
|
||||||
|
|
||||||
|
#. If your segment is a :py:class:`powerline.segments.Segment` and used
|
||||||
|
arguments are scattered over multiple methods
|
||||||
|
:py:meth:`powerline.segments.Segment.argspecobjs` should be overridden in
|
||||||
|
subclass to tell powerline-lint which objects should be inspected for
|
||||||
|
arguments.
|
||||||
|
#. If your segment takes some arguments that are never listed, but accessed
|
||||||
|
via ``kwargs.get()`` or you cannot use previous function for whatever
|
||||||
|
reason :py:meth:`powerline.segments.Segment.additional_args` should be
|
||||||
|
overridden in subclass.
|
||||||
|
#. If you are expecting user to use one :ref:`name <config-themes-seg-name>`
|
||||||
|
for multiple segments which cannot be linked to the segment function
|
||||||
|
automatically by powerline-lint (e.g. because there are no instances of
|
||||||
|
the segments in question in the default configuration) you should use
|
||||||
|
:py:func:`powerline.lint.checks.register_common_name`.
|
||||||
|
|
||||||
Object representing segment may have the following attributes used by
|
Object representing segment may have the following attributes used by
|
||||||
powerline:
|
powerline:
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
"battery_gradient": { "fg": "white_red", "bg": "gray0", "attr": [] },
|
"battery_gradient": { "fg": "white_red", "bg": "gray0", "attr": [] },
|
||||||
"battery_full": { "fg": "red", "bg": "gray0", "attr": [] },
|
"battery_full": { "fg": "red", "bg": "gray0", "attr": [] },
|
||||||
"battery_empty": { "fg": "white", "bg": "gray0", "attr": [] },
|
"battery_empty": { "fg": "white", "bg": "gray0", "attr": [] },
|
||||||
"now_playing": { "fg": "gray10", "bg": "black", "attr": [] },
|
"player": { "fg": "gray10", "bg": "black", "attr": [] },
|
||||||
"user": { "fg": "white", "bg": "darkblue", "attr": ["bold"] },
|
"user": { "fg": "white", "bg": "darkblue", "attr": ["bold"] },
|
||||||
"superuser": { "fg": "white", "bg": "brightred", "attr": ["bold"] },
|
"superuser": { "fg": "white", "bg": "brightred", "attr": ["bold"] },
|
||||||
"branch": { "fg": "gray9", "bg": "gray2", "attr": [] },
|
"branch": { "fg": "gray9", "bg": "gray2", "attr": [] },
|
||||||
|
@ -20,6 +20,16 @@
|
|||||||
"ellipsis": "..."
|
"ellipsis": "..."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"player": {
|
||||||
|
"args": {
|
||||||
|
"state_symbols": {
|
||||||
|
"fallback": "",
|
||||||
|
"play": ">",
|
||||||
|
"pause": "~",
|
||||||
|
"stop": "X"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
"line_current_symbol": {
|
"line_current_symbol": {
|
||||||
"contents": "LN "
|
"contents": "LN "
|
||||||
|
@ -23,6 +23,16 @@
|
|||||||
"line_current_symbol": {
|
"line_current_symbol": {
|
||||||
"contents": " "
|
"contents": " "
|
||||||
},
|
},
|
||||||
|
"player": {
|
||||||
|
"args": {
|
||||||
|
"state_symbols": {
|
||||||
|
"fallback": "♫",
|
||||||
|
"play": "▶",
|
||||||
|
"pause": "▮▮",
|
||||||
|
"stop": "■"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
"time": {
|
"time": {
|
||||||
"before": "⌚ "
|
"before": "⌚ "
|
||||||
|
@ -19,6 +19,16 @@
|
|||||||
"ellipsis": "⋯"
|
"ellipsis": "⋯"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"player": {
|
||||||
|
"args": {
|
||||||
|
"state_symbols": {
|
||||||
|
"fallback": "♫",
|
||||||
|
"play": "▶",
|
||||||
|
"pause": "▮▮",
|
||||||
|
"stop": "■"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
"line_current_symbol": {
|
"line_current_symbol": {
|
||||||
"contents": " "
|
"contents": " "
|
||||||
|
@ -23,6 +23,16 @@
|
|||||||
"line_current_symbol": {
|
"line_current_symbol": {
|
||||||
"contents": " "
|
"contents": " "
|
||||||
},
|
},
|
||||||
|
"player": {
|
||||||
|
"args": {
|
||||||
|
"state_symbols": {
|
||||||
|
"fallback": "♫",
|
||||||
|
"play": "▶",
|
||||||
|
"pause": "▮▮",
|
||||||
|
"stop": "■"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
"time": {
|
"time": {
|
||||||
"before": ""
|
"before": ""
|
||||||
|
@ -24,6 +24,16 @@
|
|||||||
"line_current_symbol": {
|
"line_current_symbol": {
|
||||||
"contents": ""
|
"contents": ""
|
||||||
},
|
},
|
||||||
|
"player": {
|
||||||
|
"args": {
|
||||||
|
"state_symbols": {
|
||||||
|
"fallback": "♫",
|
||||||
|
"play": "▶",
|
||||||
|
"pause": "▮▮",
|
||||||
|
"stop": "■"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
"time": {
|
"time": {
|
||||||
"before": ""
|
"before": ""
|
||||||
|
@ -16,7 +16,7 @@ if sys.platform.startswith('win32'):
|
|||||||
Popen = partial(Popen, creationflags=0x08000000)
|
Popen = partial(Popen, creationflags=0x08000000)
|
||||||
|
|
||||||
|
|
||||||
def run_cmd(pl, cmd, stdin=None):
|
def run_cmd(pl, cmd, stdin=None, strip=True):
|
||||||
'''Run command and return its stdout, stripped
|
'''Run command and return its stdout, stripped
|
||||||
|
|
||||||
If running command fails returns None and logs failure to ``pl`` argument.
|
If running command fails returns None and logs failure to ``pl`` argument.
|
||||||
@ -27,6 +27,8 @@ def run_cmd(pl, cmd, stdin=None):
|
|||||||
Command which will be run.
|
Command which will be run.
|
||||||
:param str stdin:
|
:param str stdin:
|
||||||
String passed to command. May be None.
|
String passed to command. May be None.
|
||||||
|
:param bool strip:
|
||||||
|
True if the result should be stripped.
|
||||||
'''
|
'''
|
||||||
try:
|
try:
|
||||||
p = Popen(cmd, shell=False, stdout=PIPE, stdin=PIPE)
|
p = Popen(cmd, shell=False, stdout=PIPE, stdin=PIPE)
|
||||||
@ -36,7 +38,7 @@ def run_cmd(pl, cmd, stdin=None):
|
|||||||
else:
|
else:
|
||||||
stdout, err = p.communicate(stdin)
|
stdout, err = p.communicate(stdin)
|
||||||
stdout = stdout.decode(get_preferred_input_encoding())
|
stdout = stdout.decode(get_preferred_input_encoding())
|
||||||
return stdout.strip()
|
return stdout.strip() if strip else stdout
|
||||||
|
|
||||||
|
|
||||||
def asrun(pl, ascript):
|
def asrun(pl, ascript):
|
||||||
|
@ -19,7 +19,7 @@ from powerline.lint.checks import (check_matcher_func, check_ext, check_config,
|
|||||||
check_segment_module, check_exinclude_function, type_keys,
|
check_segment_module, check_exinclude_function, type_keys,
|
||||||
check_segment_function, check_args, get_one_segment_function,
|
check_segment_function, check_args, get_one_segment_function,
|
||||||
check_highlight_groups, check_highlight_group, check_full_segment_data,
|
check_highlight_groups, check_highlight_group, check_full_segment_data,
|
||||||
get_all_possible_functions, check_segment_data_key)
|
get_all_possible_functions, check_segment_data_key, register_common_name)
|
||||||
from powerline.lint.spec import Spec
|
from powerline.lint.spec import Spec
|
||||||
from powerline.lint.context import Context
|
from powerline.lint.context import Context
|
||||||
|
|
||||||
@ -289,6 +289,10 @@ theme_spec = common_theme_spec().update(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def register_common_names():
|
||||||
|
register_common_name('player', 'powerline.segments.common.players', '_player')
|
||||||
|
|
||||||
|
|
||||||
def check(paths=None, debug=False, echoerr=echoerr, require_ext=None):
|
def check(paths=None, debug=False, echoerr=echoerr, require_ext=None):
|
||||||
'''Check configuration sanity
|
'''Check configuration sanity
|
||||||
|
|
||||||
@ -308,6 +312,7 @@ def check(paths=None, debug=False, echoerr=echoerr, require_ext=None):
|
|||||||
``False`` if user configuration seems to be completely sane and ``True``
|
``False`` if user configuration seems to be completely sane and ``True``
|
||||||
if some problems were found.
|
if some problems were found.
|
||||||
'''
|
'''
|
||||||
|
register_common_names()
|
||||||
search_paths = paths or get_config_paths()
|
search_paths = paths or get_config_paths()
|
||||||
find_config_files = generate_config_finder(lambda: search_paths)
|
find_config_files = generate_config_finder(lambda: search_paths)
|
||||||
|
|
||||||
|
@ -5,6 +5,8 @@ import os
|
|||||||
import re
|
import re
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
from powerline.lib.threaded import ThreadedSegment
|
from powerline.lib.threaded import ThreadedSegment
|
||||||
from powerline.lib.unicode import unicode
|
from powerline.lib.unicode import unicode
|
||||||
from powerline.lint.markedjson.markedvalue import MarkedUnicode
|
from powerline.lint.markedjson.markedvalue import MarkedUnicode
|
||||||
@ -673,6 +675,16 @@ def get_one_segment_function(data, context, echoerr):
|
|||||||
yield func
|
yield func
|
||||||
|
|
||||||
|
|
||||||
|
common_names = defaultdict(set)
|
||||||
|
|
||||||
|
|
||||||
|
def register_common_name(name, cmodule, cname):
|
||||||
|
s = cmodule + '.' + cname
|
||||||
|
cmodule_mark = Mark('<common name definition>', 1, 1, s, 1)
|
||||||
|
cname_mark = Mark('<common name definition>', 1, len(cmodule) + 1, s, len(cmodule) + 1)
|
||||||
|
common_names[name].add((MarkedUnicode(cmodule, cmodule_mark), MarkedUnicode(cname, cname_mark)))
|
||||||
|
|
||||||
|
|
||||||
def get_all_possible_functions(data, context, echoerr):
|
def get_all_possible_functions(data, context, echoerr):
|
||||||
name = context[-2][0]
|
name = context[-2][0]
|
||||||
module, name = name.rpartition('.')[::2]
|
module, name = name.rpartition('.')[::2]
|
||||||
@ -681,6 +693,11 @@ def get_all_possible_functions(data, context, echoerr):
|
|||||||
if func:
|
if func:
|
||||||
yield func
|
yield func
|
||||||
else:
|
else:
|
||||||
|
if name in common_names:
|
||||||
|
for cmodule, cname in common_names[name]:
|
||||||
|
cfunc = import_segment(cname, data, context, echoerr, module=MarkedUnicode(cmodule, None))
|
||||||
|
if cfunc:
|
||||||
|
yield cfunc
|
||||||
for ext, theme_config in list_themes(data, context):
|
for ext, theme_config in list_themes(data, context):
|
||||||
for segments in theme_config.get('segments', {}).values():
|
for segments in theme_config.get('segments', {}).values():
|
||||||
for segment in segments:
|
for segment in segments:
|
||||||
|
@ -27,6 +27,12 @@ def import_function(function_type, name, data, context, echoerr, module):
|
|||||||
problem='module {0} is deprecated'.format(module),
|
problem='module {0} is deprecated'.format(module),
|
||||||
problem_mark=module.mark)
|
problem_mark=module.mark)
|
||||||
|
|
||||||
|
if module == 'powerline.segments.common.players' and name == 'now_playing':
|
||||||
|
echoerr(context='Warning while checking segments (key {key})'.format(key=context.key),
|
||||||
|
context_mark=name.mark,
|
||||||
|
problem='function {0}.{1} is deprecated: use {0}.{{player_name}} instead'.format(module, name),
|
||||||
|
problem_mark=module.mark)
|
||||||
|
|
||||||
with WithPath(data['import_paths']):
|
with WithPath(data['import_paths']):
|
||||||
try:
|
try:
|
||||||
func = getattr(__import__(str(module), fromlist=[str(name)]), str(name))
|
func = getattr(__import__(str(module), fromlist=[str(name)]), str(name))
|
||||||
|
@ -5,7 +5,7 @@ import sys
|
|||||||
|
|
||||||
from powerline.lib.shell import asrun, run_cmd
|
from powerline.lib.shell import asrun, run_cmd
|
||||||
from powerline.lib.unicode import out_u
|
from powerline.lib.unicode import out_u
|
||||||
from powerline.segments import Segment
|
from powerline.segments import Segment, with_docstring
|
||||||
|
|
||||||
|
|
||||||
STATE_SYMBOLS = {
|
STATE_SYMBOLS = {
|
||||||
@ -16,9 +16,25 @@ STATE_SYMBOLS = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class NowPlayingSegment(Segment):
|
def _convert_state(state):
|
||||||
def __call__(self, player='mpd', format='{state_symbol} {artist} - {title} ({total})', state_symbols=STATE_SYMBOLS, **kwargs):
|
'''Guess player state'''
|
||||||
player_func = getattr(self, 'player_{0}'.format(player))
|
state = state.lower()
|
||||||
|
if 'play' in state:
|
||||||
|
return 'play'
|
||||||
|
if 'pause' in state:
|
||||||
|
return 'pause'
|
||||||
|
if 'stop' in state:
|
||||||
|
return 'stop'
|
||||||
|
return 'fallback'
|
||||||
|
|
||||||
|
|
||||||
|
def _convert_seconds(seconds):
|
||||||
|
'''Convert seconds to minutes:seconds format'''
|
||||||
|
return '{0:.0f}:{1:02.0f}'.format(*divmod(float(seconds), 60))
|
||||||
|
|
||||||
|
|
||||||
|
class PlayerSegment(Segment):
|
||||||
|
def __call__(self, format='{state_symbol} {artist} - {title} ({total})', state_symbols=STATE_SYMBOLS, **kwargs):
|
||||||
stats = {
|
stats = {
|
||||||
'state': 'fallback',
|
'state': 'fallback',
|
||||||
'album': None,
|
'album': None,
|
||||||
@ -27,28 +43,83 @@ class NowPlayingSegment(Segment):
|
|||||||
'elapsed': None,
|
'elapsed': None,
|
||||||
'total': None,
|
'total': None,
|
||||||
}
|
}
|
||||||
func_stats = player_func(**kwargs)
|
func_stats = self.get_player_status(**kwargs)
|
||||||
if not func_stats:
|
if not func_stats:
|
||||||
return None
|
return None
|
||||||
stats.update(func_stats)
|
stats.update(func_stats)
|
||||||
stats['state_symbol'] = state_symbols.get(stats['state'])
|
stats['state_symbol'] = state_symbols.get(stats['state'])
|
||||||
return format.format(**stats)
|
return [{
|
||||||
|
'contents': format.format(**stats),
|
||||||
|
'highlight_group': ['now_playing', 'player_' + (stats['state'] or 'fallback'), 'player'],
|
||||||
|
}]
|
||||||
|
|
||||||
@staticmethod
|
def get_player_status(self, pl):
|
||||||
def _convert_state(state):
|
pass
|
||||||
state = state.lower()
|
|
||||||
if 'play' in state:
|
|
||||||
return 'play'
|
|
||||||
if 'pause' in state:
|
|
||||||
return 'pause'
|
|
||||||
if 'stop' in state:
|
|
||||||
return 'stop'
|
|
||||||
|
|
||||||
@staticmethod
|
def argspecobjs(self):
|
||||||
def _convert_seconds(seconds):
|
for ret in super(PlayerSegment, self).argspecobjs():
|
||||||
return '{0:.0f}:{1:02.0f}'.format(*divmod(float(seconds), 60))
|
yield ret
|
||||||
|
yield 'get_player_status', self.get_player_status
|
||||||
|
|
||||||
def player_cmus(self, pl):
|
def omitted_args(self, name, method):
|
||||||
|
return (0,)
|
||||||
|
|
||||||
|
|
||||||
|
_common_args = '''
|
||||||
|
This player segment should be added like this:
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
{{
|
||||||
|
"function": "powerline.segments.common.players.{0}",
|
||||||
|
"name": "player"
|
||||||
|
}}
|
||||||
|
|
||||||
|
(with additional ``"args": {{…}}`` if needed).
|
||||||
|
|
||||||
|
Highlight groups used: ``player_fallback`` or ``player``, ``player_play`` or ``player``, ``player_pause`` or ``player``, ``player_stop`` or ``player``.
|
||||||
|
|
||||||
|
:param str format:
|
||||||
|
Format used for displaying data from player. Should be a str.format-like
|
||||||
|
string with the following keyword parameters:
|
||||||
|
|
||||||
|
+------------+-------------------------------------------------------------+
|
||||||
|
|Parameter |Description |
|
||||||
|
+============+=============================================================+
|
||||||
|
|state_symbol|Symbol displayed for play/pause/stop states. There is also |
|
||||||
|
| |“fallback” state used in case function failed to get player |
|
||||||
|
| |state. For this state symbol is by default empty. All |
|
||||||
|
| |symbols are defined in ``state_symbols`` argument. |
|
||||||
|
+------------+-------------------------------------------------------------+
|
||||||
|
|album |Album that is currently played. |
|
||||||
|
+------------+-------------------------------------------------------------+
|
||||||
|
|artist |Artist whose song is currently played |
|
||||||
|
+------------+-------------------------------------------------------------+
|
||||||
|
|title |Currently played composition. |
|
||||||
|
+------------+-------------------------------------------------------------+
|
||||||
|
|elapsed |Composition duration in format M:SS (minutes:seconds). |
|
||||||
|
+------------+-------------------------------------------------------------+
|
||||||
|
|total |Composition length in format M:SS. |
|
||||||
|
+------------+-------------------------------------------------------------+
|
||||||
|
:param dict state_symbols:
|
||||||
|
Symbols used for displaying state. Must contain all of the following keys:
|
||||||
|
|
||||||
|
======== ========================================================
|
||||||
|
Key Description
|
||||||
|
======== ========================================================
|
||||||
|
play Displayed when player is playing.
|
||||||
|
pause Displayed when player is paused.
|
||||||
|
stop Displayed when player is not playing anything.
|
||||||
|
fallback Displayed if state is not one of the above or not known.
|
||||||
|
======== ========================================================
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
_player = with_docstring(PlayerSegment(), _common_args.format('_player'))
|
||||||
|
|
||||||
|
|
||||||
|
class CmusPlayerSegment(PlayerSegment):
|
||||||
|
def get_player_status(self, pl):
|
||||||
'''Return cmus player information.
|
'''Return cmus player information.
|
||||||
|
|
||||||
cmus-remote -Q returns data with multi-level information i.e.
|
cmus-remote -Q returns data with multi-level information i.e.
|
||||||
@ -75,21 +146,37 @@ class NowPlayingSegment(Segment):
|
|||||||
now_playing = dict(((token[0] if token[0] not in ignore_levels else token[1],
|
now_playing = dict(((token[0] if token[0] not in ignore_levels else token[1],
|
||||||
(' '.join(token[1:]) if token[0] not in ignore_levels else
|
(' '.join(token[1:]) if token[0] not in ignore_levels else
|
||||||
' '.join(token[2:]))) for token in [line.split(' ') for line in now_playing_str.split('\n')[:-1]]))
|
' '.join(token[2:]))) for token in [line.split(' ') for line in now_playing_str.split('\n')[:-1]]))
|
||||||
state = self._convert_state(now_playing.get('status'))
|
state = _convert_state(now_playing.get('status'))
|
||||||
return {
|
return {
|
||||||
'state': state,
|
'state': state,
|
||||||
'album': now_playing.get('album'),
|
'album': now_playing.get('album'),
|
||||||
'artist': now_playing.get('artist'),
|
'artist': now_playing.get('artist'),
|
||||||
'title': now_playing.get('title'),
|
'title': now_playing.get('title'),
|
||||||
'elapsed': self._convert_seconds(now_playing.get('position', 0)),
|
'elapsed': _convert_seconds(now_playing.get('position', 0)),
|
||||||
'total': self._convert_seconds(now_playing.get('duration', 0)),
|
'total': _convert_seconds(now_playing.get('duration', 0)),
|
||||||
}
|
}
|
||||||
|
|
||||||
def player_mpd(self, pl, host='localhost', port=6600):
|
|
||||||
|
cmus = with_docstring(CmusPlayerSegment(),
|
||||||
|
('''Return CMUS player information
|
||||||
|
|
||||||
|
Requires cmus-remote command be acessible from $PATH.
|
||||||
|
|
||||||
|
{0}
|
||||||
|
''').format(_common_args.format('cmus')))
|
||||||
|
|
||||||
|
|
||||||
|
class MpdPlayerSegment(PlayerSegment):
|
||||||
|
def get_player_status(self, pl, host='localhost', port=6600):
|
||||||
try:
|
try:
|
||||||
import mpd
|
import mpd
|
||||||
except ImportError:
|
except ImportError:
|
||||||
now_playing = run_cmd(pl, ['mpc', 'current', '-f', '%album%\n%artist%\n%title%\n%time%', '-h', str(host), '-p', str(port)])
|
now_playing = run_cmd(pl, [
|
||||||
|
'mpc', 'current',
|
||||||
|
'-f', '%album%\n%artist%\n%title%\n%time%',
|
||||||
|
'-h', str(host),
|
||||||
|
'-p', str(port)
|
||||||
|
], strip=False)
|
||||||
if not now_playing:
|
if not now_playing:
|
||||||
return
|
return
|
||||||
now_playing = now_playing.split('\n')
|
now_playing = now_playing.split('\n')
|
||||||
@ -113,16 +200,33 @@ class NowPlayingSegment(Segment):
|
|||||||
'album': now_playing.get('album'),
|
'album': now_playing.get('album'),
|
||||||
'artist': now_playing.get('artist'),
|
'artist': now_playing.get('artist'),
|
||||||
'title': now_playing.get('title'),
|
'title': now_playing.get('title'),
|
||||||
'elapsed': self._convert_seconds(now_playing.get('elapsed', 0)),
|
'elapsed': _convert_seconds(now_playing.get('elapsed', 0)),
|
||||||
'total': self._convert_seconds(now_playing.get('time', 0)),
|
'total': _convert_seconds(now_playing.get('time', 0)),
|
||||||
}
|
}
|
||||||
|
|
||||||
def player_dbus(self, player_name, bus_name, player_path, iface_prop, iface_player):
|
|
||||||
|
mpd = with_docstring(MpdPlayerSegment(),
|
||||||
|
('''Return Music Player Daemon information
|
||||||
|
|
||||||
|
Requires mpc command to be acessible from $PATH or ``mpd`` Python module.
|
||||||
|
|
||||||
|
{0}
|
||||||
|
:param str host:
|
||||||
|
Host on which mpd runs.
|
||||||
|
:param int port:
|
||||||
|
Port which should be connected to.
|
||||||
|
''').format(_common_args.format('mpd')))
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import dbus
|
import dbus
|
||||||
except ImportError:
|
except ImportError:
|
||||||
self.exception('Could not add {0} segment: requires dbus module', player_name)
|
def _get_dbus_player_status(pl, player_name, **kwargs):
|
||||||
|
pl.error('Could not add {0} segment: requires dbus module', player_name)
|
||||||
return
|
return
|
||||||
|
else:
|
||||||
|
def _get_dbus_player_status(pl, bus_name, player_path, iface_prop,
|
||||||
|
iface_player, player_name='player'):
|
||||||
bus = dbus.SessionBus()
|
bus = dbus.SessionBus()
|
||||||
try:
|
try:
|
||||||
player = bus.get_object(bus_name, player_path)
|
player = bus.get_object(bus_name, player_path)
|
||||||
@ -136,7 +240,7 @@ class NowPlayingSegment(Segment):
|
|||||||
album = out_u(info.get('xesam:album'))
|
album = out_u(info.get('xesam:album'))
|
||||||
title = out_u(info.get('xesam:title'))
|
title = out_u(info.get('xesam:title'))
|
||||||
artist = info.get('xesam:artist')
|
artist = info.get('xesam:artist')
|
||||||
state = self._convert_state(status)
|
state = _convert_state(status)
|
||||||
if artist:
|
if artist:
|
||||||
artist = out_u(artist[0])
|
artist = out_u(artist[0])
|
||||||
return {
|
return {
|
||||||
@ -144,11 +248,38 @@ class NowPlayingSegment(Segment):
|
|||||||
'album': album,
|
'album': album,
|
||||||
'artist': artist,
|
'artist': artist,
|
||||||
'title': title,
|
'title': title,
|
||||||
'total': self._convert_seconds(info.get('mpris:length') / 1e6),
|
'total': _convert_seconds(info.get('mpris:length') / 1e6),
|
||||||
}
|
}
|
||||||
|
|
||||||
def player_spotify_dbus(self, pl):
|
|
||||||
return self.player_dbus(
|
class DbusPlayerSegment(PlayerSegment):
|
||||||
|
get_player_status = staticmethod(_get_dbus_player_status)
|
||||||
|
|
||||||
|
|
||||||
|
dbus_player = with_docstring(DbusPlayerSegment(),
|
||||||
|
('''Return generic dbus player state
|
||||||
|
|
||||||
|
Requires ``dbus`` python module. Only for players that support specific protocol
|
||||||
|
(e.g. like :py:func:`spotify` and :py:func:`clementine`).
|
||||||
|
|
||||||
|
{0}
|
||||||
|
:param str player_name:
|
||||||
|
Player name. Used in error messages only.
|
||||||
|
:param str bus_name:
|
||||||
|
Dbus bus name.
|
||||||
|
:param str player_path:
|
||||||
|
Path to the player on the given bus.
|
||||||
|
:param str iface_prop:
|
||||||
|
Interface properties name for use with dbus.Interface.
|
||||||
|
:param str iface_player:
|
||||||
|
Player name.
|
||||||
|
''').format(_common_args.format('dbus_player')))
|
||||||
|
|
||||||
|
|
||||||
|
class SpotifyDbusPlayerSegment(PlayerSegment):
|
||||||
|
def get_player_status(self, pl):
|
||||||
|
return _get_dbus_player_status(
|
||||||
|
pl=pl,
|
||||||
player_name='Spotify',
|
player_name='Spotify',
|
||||||
bus_name='com.spotify.qt',
|
bus_name='com.spotify.qt',
|
||||||
player_path='/',
|
player_path='/',
|
||||||
@ -156,16 +287,18 @@ class NowPlayingSegment(Segment):
|
|||||||
iface_player='org.freedesktop.MediaPlayer2',
|
iface_player='org.freedesktop.MediaPlayer2',
|
||||||
)
|
)
|
||||||
|
|
||||||
def player_clementine(self, pl):
|
|
||||||
return self.player_dbus(
|
|
||||||
player_name='Clementine',
|
|
||||||
bus_name='org.mpris.MediaPlayer2.clementine',
|
|
||||||
player_path='/org/mpris/MediaPlayer2',
|
|
||||||
iface_prop='org.freedesktop.DBus.Properties',
|
|
||||||
iface_player='org.mpris.MediaPlayer2.Player',
|
|
||||||
)
|
|
||||||
|
|
||||||
def player_spotify_apple_script(self, pl):
|
spotify_dbus = with_docstring(SpotifyDbusPlayerSegment(),
|
||||||
|
('''Return spotify player information
|
||||||
|
|
||||||
|
Requires ``dbus`` python module.
|
||||||
|
|
||||||
|
{0}
|
||||||
|
''').format(_common_args.format('spotify_dbus')))
|
||||||
|
|
||||||
|
|
||||||
|
class SpotifyAppleScriptPlayerSegment(PlayerSegment):
|
||||||
|
def get_player_status(self, pl):
|
||||||
status_delimiter = '-~`/='
|
status_delimiter = '-~`/='
|
||||||
ascript = '''
|
ascript = '''
|
||||||
tell application "System Events"
|
tell application "System Events"
|
||||||
@ -196,7 +329,7 @@ class NowPlayingSegment(Segment):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
spotify_status = spotify.split(status_delimiter)
|
spotify_status = spotify.split(status_delimiter)
|
||||||
state = self._convert_state(spotify_status[0])
|
state = _convert_state(spotify_status[0])
|
||||||
if state == 'stop':
|
if state == 'stop':
|
||||||
return None
|
return None
|
||||||
return {
|
return {
|
||||||
@ -204,21 +337,58 @@ class NowPlayingSegment(Segment):
|
|||||||
'album': spotify_status[1],
|
'album': spotify_status[1],
|
||||||
'artist': spotify_status[2],
|
'artist': spotify_status[2],
|
||||||
'title': spotify_status[3],
|
'title': spotify_status[3],
|
||||||
'total': self._convert_seconds(int(spotify_status[4]))
|
'total': _convert_seconds(int(spotify_status[4]))
|
||||||
}
|
}
|
||||||
|
|
||||||
try:
|
|
||||||
__import__('dbus')
|
|
||||||
except ImportError:
|
|
||||||
if sys.platform.startswith('darwin'):
|
|
||||||
player_spotify = player_spotify_apple_script
|
|
||||||
else:
|
|
||||||
player_spotify = player_spotify_dbus
|
|
||||||
else:
|
|
||||||
player_spotify = player_spotify_dbus
|
|
||||||
|
|
||||||
def player_rhythmbox(self, pl):
|
spotify_apple_script = with_docstring(SpotifyAppleScriptPlayerSegment(),
|
||||||
now_playing = run_cmd(pl, ['rhythmbox-client', '--no-start', '--no-present', '--print-playing-format', '%at\n%aa\n%tt\n%te\n%td'])
|
('''Return spotify player information
|
||||||
|
|
||||||
|
Requires ``osascript`` available in $PATH.
|
||||||
|
|
||||||
|
{0}
|
||||||
|
''').format(_common_args.format('spotify_apple_script')))
|
||||||
|
|
||||||
|
|
||||||
|
if 'dbus' in globals() or not sys.platform.startswith('darwin'):
|
||||||
|
spotify = spotify_dbus
|
||||||
|
_old_name = 'spotify_dbus'
|
||||||
|
else:
|
||||||
|
spotify = spotify_apple_script
|
||||||
|
_old_name = 'spotify_apple_script'
|
||||||
|
|
||||||
|
|
||||||
|
spotify = with_docstring(spotify, spotify.__doc__.replace(_old_name, 'spotify'))
|
||||||
|
|
||||||
|
|
||||||
|
class ClementinePlayerSegment(PlayerSegment):
|
||||||
|
def get_player_status(self, pl):
|
||||||
|
return _get_dbus_player_status(
|
||||||
|
pl=pl,
|
||||||
|
player_name='Clementine',
|
||||||
|
bus_name='org.mpris.MediaPlayer2.clementine',
|
||||||
|
player_path='/org/mpris/MediaPlayer2',
|
||||||
|
iface_prop='org.freedesktop.DBus.Properties',
|
||||||
|
iface_player='org.mpris.MediaPlayer2.Player',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
clementine = with_docstring(ClementinePlayerSegment(),
|
||||||
|
('''Return clementine player information
|
||||||
|
|
||||||
|
Requires ``dbus`` python module.
|
||||||
|
|
||||||
|
{0}
|
||||||
|
''').format(_common_args.format('clementine')))
|
||||||
|
|
||||||
|
|
||||||
|
class RhythmboxPlayerSegment(PlayerSegment):
|
||||||
|
def get_player_status(self, pl):
|
||||||
|
now_playing = run_cmd(pl, [
|
||||||
|
'rhythmbox-client',
|
||||||
|
'--no-start', '--no-present',
|
||||||
|
'--print-playing-format', '%at\n%aa\n%tt\n%te\n%td'
|
||||||
|
], strip=False)
|
||||||
if not now_playing:
|
if not now_playing:
|
||||||
return
|
return
|
||||||
now_playing = now_playing.split('\n')
|
now_playing = now_playing.split('\n')
|
||||||
@ -230,7 +400,18 @@ class NowPlayingSegment(Segment):
|
|||||||
'total': now_playing[4],
|
'total': now_playing[4],
|
||||||
}
|
}
|
||||||
|
|
||||||
def player_rdio(self, pl):
|
|
||||||
|
rhythmbox = with_docstring(RhythmboxPlayerSegment(),
|
||||||
|
('''Return rhythmbox player information
|
||||||
|
|
||||||
|
Requires ``rhythmbox-client`` available in $PATH.
|
||||||
|
|
||||||
|
{0}
|
||||||
|
''').format(_common_args.format('rhythmbox')))
|
||||||
|
|
||||||
|
|
||||||
|
class RDIOPlayerSegment(PlayerSegment):
|
||||||
|
def get_player_status(self, pl):
|
||||||
status_delimiter = '-~`/='
|
status_delimiter = '-~`/='
|
||||||
ascript = '''
|
ascript = '''
|
||||||
tell application "System Events"
|
tell application "System Events"
|
||||||
@ -255,9 +436,9 @@ class NowPlayingSegment(Segment):
|
|||||||
now_playing = now_playing.split('\n')
|
now_playing = now_playing.split('\n')
|
||||||
if len(now_playing) != 6:
|
if len(now_playing) != 6:
|
||||||
return
|
return
|
||||||
state = self._convert_state(now_playing[5])
|
state = _convert_state(now_playing[5])
|
||||||
total = self._convert_seconds(now_playing[4])
|
total = _convert_seconds(now_playing[4])
|
||||||
elapsed = self._convert_seconds(float(now_playing[3]) * float(now_playing[4]) / 100)
|
elapsed = _convert_seconds(float(now_playing[3]) * float(now_playing[4]) / 100)
|
||||||
return {
|
return {
|
||||||
'title': now_playing[0],
|
'title': now_playing[0],
|
||||||
'artist': now_playing[1],
|
'artist': now_playing[1],
|
||||||
@ -265,6 +446,34 @@ class NowPlayingSegment(Segment):
|
|||||||
'elapsed': elapsed,
|
'elapsed': elapsed,
|
||||||
'total': total,
|
'total': total,
|
||||||
'state': state,
|
'state': state,
|
||||||
'state_symbol': self.STATE_SYMBOLS.get(state)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
rdio = with_docstring(RDIOPlayerSegment(),
|
||||||
|
('''Return rdio player information
|
||||||
|
|
||||||
|
Requires ``osascript`` available in $PATH.
|
||||||
|
|
||||||
|
{0}
|
||||||
|
''').format(_common_args.format('rdio')))
|
||||||
|
|
||||||
|
|
||||||
|
class NowPlayingSegment(Segment):
|
||||||
|
def __call__(self, player='mpd', **kwargs):
|
||||||
|
player_segment = globals()[player]
|
||||||
|
assert(isinstance(player_segment, PlayerSegment))
|
||||||
|
return player_segment(**kwargs)
|
||||||
|
|
||||||
|
def argspecobjs(self):
|
||||||
|
for ret in super(NowPlayingSegment, self).argspecobjs():
|
||||||
|
yield ret
|
||||||
|
yield '__call__', PlayerSegment.__call__
|
||||||
|
for k, v in globals().items():
|
||||||
|
if isinstance(v, type) and issubclass(v, PlayerSegment) and v is not DbusPlayerSegment:
|
||||||
|
yield 'get_player_status', v.get_player_status
|
||||||
|
|
||||||
|
def omitted_args(self, name, method):
|
||||||
|
return (0,)
|
||||||
|
|
||||||
|
|
||||||
now_playing = NowPlayingSegment()
|
now_playing = NowPlayingSegment()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user