From 483d5a03fcf311b60478942db0c7f3b5911ed9b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Mand=C3=A1k?= Date: Sat, 17 Jan 2015 19:07:31 +0100 Subject: [PATCH 1/9] Added 'deadbeef' module. --- i3pystatus/deadbeef.py | 171 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 i3pystatus/deadbeef.py diff --git a/i3pystatus/deadbeef.py b/i3pystatus/deadbeef.py new file mode 100644 index 0000000..f8b16b0 --- /dev/null +++ b/i3pystatus/deadbeef.py @@ -0,0 +1,171 @@ +# -*- coding: utf-8 -*- +import shutil +import subprocess + +from i3pystatus import IntervalModule, formatp + + +class Deadbeef(IntervalModule): + """ + Plugin for DeaDBeeF music player. + + - Requires deadbeef >= 0.6.2 + + The playing/paused detection is a bit wonky since you can't get the + status directly from DB, so please do not use intervals shorter than 1s. + + Available formatters (uses :ref:`formatp`) + + * `{state}` — play, pause, stop mapped through the `status` dictionary + * `{artist}` — artist of current song + * `{title}` — title of current song + * `{album}` — album of current song + * `{band}` — gives "band", or "album artist" or "albumartist" or "artist",\ + whichever exists, in this order + * `{composer}` — composer + * `{track}` — (current song number + * `{numtracks}` — number of tracks in current song's album + * `{year}` — date or year + * `{genre}` — genre + * `{comment}` — comment + * `{copyright}` — copyright + * `{length}` — current song length (duration) + * `{elapsed}` — elaped time of current song + * `{filename}` — filename without path + * `{fullname}` — full pathname/uri + * `{dir}` — directory without path + * `{fulldir}` — directory name with full path + * `{channels}` — channel configuration (mono/stereo/..) + * `{version}` — deadbeef version number + + """ + + interval = 1.1 + + settings = ( + ("format", "formatp string"), + ("format_down", "String to use when DB is not running"), + ("status", "Dictionary mapping pause, play and stop to output"), + ("color", "Text color"), + ("color_down", "Text color when DB is not running"), + ) + + format = "{state} {elapsed} {artist} - {title}" + color = "#00dd00" + + format_down = "DeaDBeeF" + color_down = "#dd0000" + + # format when stopped is allways just "{state}" i.e. status["stop"] + status = { + "pause": u"▷", + "play": u"▶", + "stop": u"◾ DeaDBeeF", + } + + on_rightclick = "db_quit" + on_leftclick = "db_play_pause" + on_upscroll = "db_prev" + on_downscroll = "db_next" + + def init(self): + self.elapsed = "0:00" + db_dict = { + "state": "{state}", + "artist": "%a", + "title": "%t", + "album": "%b", + "band": "%B", + "composer": "%C", + "track": "%n", + "numtracks": "%N", + "year": "%y", + "genre": "%g", + "comment": "%c", + "copyright": "%r", + "length": "%l", + "elapsed": "%e", + "filename": "%f", + "fullname": "%F", + "dir": "%d", + "fulldir": "%D", + "channels": "%Z", + "version": "%V", + } + self.db_format = formatp(self.format, **db_dict).strip() + + def run(self): + if not self.db_running(): + self.output = { + "full_text": self.format_down, + "color": self.color_down, + } + return + + try: + command = ['deadbeef', '--nowplaying', '%e ' + self.db_format] + db = subprocess.Popen(command, shell=False, + stdout=subprocess.PIPE, + stderr=subprocess.DEVNULL) + db_out, db_err = db.communicate() + except OSError as e: + self.output = { + "full_text": "Error: " + e.strerror, + "color": "#ff0000", + } + return + except subprocess.CalledProcessError as e: + self.output = { + "full_text": "Error: " + e.output, + "color": "#ff0000", + } + return + db_out = db_out.decode("UTF-8").replace("\n", " ").strip() + + if db_out == "nothing": + self.output = { + "full_text": self.status["stop"], + "color": self.color, + } + return + + elapsed, db_out = db_out.split(" ", 1) + if elapsed == self.elapsed: + state = "pause" + else: + state = "play" + self.elapsed = elapsed + + self.output = { + "full_text": db_out.format(state=self.status[state]).strip(), + "color": self.color, + } + + def db_running(self): + command = "pidof deadbeef" + pidof = subprocess.Popen(command.split(), shell=False, + stdin=subprocess.DEVNULL, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL) + pidof.communicate() + return False if pidof.returncode != 0 else True + + def db_quit(self): + command = "deadbeef" + if self.db_running(): + command += " --quit" + subprocess.Popen(command.split()) + + def db_play_pause(self): + command = "deadbeef --play-pause" + subprocess.Popen(command.split()) + + def db_prev(self): + if self.db_running(): + command = "deadbeef --prev" + subprocess.Popen(command.split()) + + def db_next(self): + if self.db_running(): + command = "deadbeef --next" + subprocess.Popen(command.split()) From 1bacaa1cf58636d62703d429c73c83d63a51b2e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Mand=C3=A1k?= Date: Sat, 17 Jan 2015 19:09:38 +0100 Subject: [PATCH 2/9] Updated 'pulseaudio' module to use pactl instead of amixer. --- i3pystatus/pulseaudio/__init__.py | 33 ++++++++++++++----------------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/i3pystatus/pulseaudio/__init__.py b/i3pystatus/pulseaudio/__init__.py index 390caa2..6aaef19 100644 --- a/i3pystatus/pulseaudio/__init__.py +++ b/i3pystatus/pulseaudio/__init__.py @@ -12,7 +12,6 @@ class PulseAudio(Module, ColorRangeModule): """ Shows volume of default PulseAudio sink (output). - - Requires amixer for toggling mute and incrementing/decrementing volume on scroll. - Depends on the PyPI colour module - https://pypi.python.org/pypi/colour/0.0.5 .. rubric:: Available formatters: @@ -29,7 +28,8 @@ class PulseAudio(Module, ColorRangeModule): ("format_muted", "optional format string to use when muted"), "muted", "unmuted", "color_muted", "color_unmuted", - ("step", "percentage to increment volume on scroll"), + ("step_in_db", "change step unit from percentage to decibels"), + ("step", "percentage (or decibel value) to increment volume on scroll"), ("bar_type", "type of volume bar. Allowed values are 'vertical' or 'horizontal'"), ("multi_colors", "whether or not to change the color from " "'color_muted' to 'color_unmuted' based on volume percentage"), @@ -46,6 +46,7 @@ class PulseAudio(Module, ColorRangeModule): color_unmuted = "#FFFFFF" step = 5 + step_db = False multi_colors = False bar_type = 'vertical' vertical_bar_width = 2 @@ -78,9 +79,8 @@ class PulseAudio(Module, ColorRangeModule): pa_threaded_mainloop_start(_mainloop) self.colors = self.get_hex_color_range(self.color_muted, self.color_unmuted, 100) - - # Check that we have amixer for toggling mute/unmute and incrementing/decrementing volume - self.has_amixer = shutil.which('alsamixer') is not None + self.sink_utf = None + self.unit = "dB" if self.step_db else "%" def request_update(self, context): """Requests a sink info update (sink_info_cb is called)""" @@ -95,6 +95,8 @@ class PulseAudio(Module, ColorRangeModule): server_info = server_info_p.contents self.sink = server_info.default_sink_name + self.sink_utf = server_info.default_sink_name + self.sink_utf = self.sink_utf.decode("UTF-8").strip() self.request_update(context) @@ -161,20 +163,15 @@ class PulseAudio(Module, ColorRangeModule): } def switch_mute(self): - if self.has_amixer: - command = "amixer -q -D pulse sset Master " - if self.currently_muted: - command += 'unmute' - else: - command += 'mute' - subprocess.Popen(command.split()) + command = "pactl -- set-sink-mute {sink} toggle".format(sink=self.sink_utf) + subprocess.Popen(command.split()) def increase_volume(self): - if self.has_amixer: - command = "amixer -q -D pulse sset Master %s%%+" % self.step - subprocess.Popen(command.split()) + command = "pactl -- set-sink-volume {sink} +{step}{unit}".format( + sink=self.sink_utf, step=self.step, unit=self.unit) + subprocess.Popen(command.split()) def decrease_volume(self): - if self.has_amixer: - command = "amixer -q -D pulse sset Master %s%%-" % self.step - subprocess.Popen(command.split()) + command = "pactl -- set-sink-volume {sink} -{step}{unit}".format( + sink=self.sink_utf, step=self.step, unit=self.unit) + subprocess.Popen(command.split()) From 6e7c22526975c75d7bc893b856fb73a933ff1610 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Mand=C3=A1k?= Date: Sat, 17 Jan 2015 19:30:49 +0100 Subject: [PATCH 3/9] Fixed 'clock' module to properly format locale specific formats. --- i3pystatus/clock.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/i3pystatus/clock.py b/i3pystatus/clock.py index ba95678..d0db703 100644 --- a/i3pystatus/clock.py +++ b/i3pystatus/clock.py @@ -32,10 +32,12 @@ class Clock(IntervalModule): on_downscroll = ["scroll_format", -1] def init(self): + lang = os.environ.get('LANG', None) + if lang: + # affects function time.strftime() in whole program + locale.setlocale(locale.LC_TIME, lang) + if self.format is None: - lang = os.environ.get('LANG', None) - if lang: - locale.setlocale(locale.LC_ALL, lang) lang = locale.getlocale()[0] if lang == 'en_US': # MDY format - United States of America From e2c709481c38730c11267c1aaa4bd21e52d8a446 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Mand=C3=A1k?= Date: Sat, 17 Jan 2015 19:59:20 +0100 Subject: [PATCH 4/9] Fixed variable name. --- i3pystatus/pulseaudio/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/i3pystatus/pulseaudio/__init__.py b/i3pystatus/pulseaudio/__init__.py index 6aaef19..2d293b5 100644 --- a/i3pystatus/pulseaudio/__init__.py +++ b/i3pystatus/pulseaudio/__init__.py @@ -46,7 +46,7 @@ class PulseAudio(Module, ColorRangeModule): color_unmuted = "#FFFFFF" step = 5 - step_db = False + step_in_db = False multi_colors = False bar_type = 'vertical' vertical_bar_width = 2 @@ -80,7 +80,7 @@ class PulseAudio(Module, ColorRangeModule): self.colors = self.get_hex_color_range(self.color_muted, self.color_unmuted, 100) self.sink_utf = None - self.unit = "dB" if self.step_db else "%" + self.unit = "dB" if self.step_in_db else "%" def request_update(self, context): """Requests a sink info update (sink_info_cb is called)""" From f8a7a86fab92d65cbd8af25f9722283359716749 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Mand=C3=A1k?= Date: Sun, 18 Jan 2015 11:36:35 +0100 Subject: [PATCH 5/9] Reverted changes in 'pulseaudio' module. --- i3pystatus/pulseaudio/__init__.py | 33 +++++++++++++++++-------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/i3pystatus/pulseaudio/__init__.py b/i3pystatus/pulseaudio/__init__.py index 2d293b5..390caa2 100644 --- a/i3pystatus/pulseaudio/__init__.py +++ b/i3pystatus/pulseaudio/__init__.py @@ -12,6 +12,7 @@ class PulseAudio(Module, ColorRangeModule): """ Shows volume of default PulseAudio sink (output). + - Requires amixer for toggling mute and incrementing/decrementing volume on scroll. - Depends on the PyPI colour module - https://pypi.python.org/pypi/colour/0.0.5 .. rubric:: Available formatters: @@ -28,8 +29,7 @@ class PulseAudio(Module, ColorRangeModule): ("format_muted", "optional format string to use when muted"), "muted", "unmuted", "color_muted", "color_unmuted", - ("step_in_db", "change step unit from percentage to decibels"), - ("step", "percentage (or decibel value) to increment volume on scroll"), + ("step", "percentage to increment volume on scroll"), ("bar_type", "type of volume bar. Allowed values are 'vertical' or 'horizontal'"), ("multi_colors", "whether or not to change the color from " "'color_muted' to 'color_unmuted' based on volume percentage"), @@ -46,7 +46,6 @@ class PulseAudio(Module, ColorRangeModule): color_unmuted = "#FFFFFF" step = 5 - step_in_db = False multi_colors = False bar_type = 'vertical' vertical_bar_width = 2 @@ -79,8 +78,9 @@ class PulseAudio(Module, ColorRangeModule): pa_threaded_mainloop_start(_mainloop) self.colors = self.get_hex_color_range(self.color_muted, self.color_unmuted, 100) - self.sink_utf = None - self.unit = "dB" if self.step_in_db else "%" + + # Check that we have amixer for toggling mute/unmute and incrementing/decrementing volume + self.has_amixer = shutil.which('alsamixer') is not None def request_update(self, context): """Requests a sink info update (sink_info_cb is called)""" @@ -95,8 +95,6 @@ class PulseAudio(Module, ColorRangeModule): server_info = server_info_p.contents self.sink = server_info.default_sink_name - self.sink_utf = server_info.default_sink_name - self.sink_utf = self.sink_utf.decode("UTF-8").strip() self.request_update(context) @@ -163,15 +161,20 @@ class PulseAudio(Module, ColorRangeModule): } def switch_mute(self): - command = "pactl -- set-sink-mute {sink} toggle".format(sink=self.sink_utf) - subprocess.Popen(command.split()) + if self.has_amixer: + command = "amixer -q -D pulse sset Master " + if self.currently_muted: + command += 'unmute' + else: + command += 'mute' + subprocess.Popen(command.split()) def increase_volume(self): - command = "pactl -- set-sink-volume {sink} +{step}{unit}".format( - sink=self.sink_utf, step=self.step, unit=self.unit) - subprocess.Popen(command.split()) + if self.has_amixer: + command = "amixer -q -D pulse sset Master %s%%+" % self.step + subprocess.Popen(command.split()) def decrease_volume(self): - command = "pactl -- set-sink-volume {sink} -{step}{unit}".format( - sink=self.sink_utf, step=self.step, unit=self.unit) - subprocess.Popen(command.split()) + if self.has_amixer: + command = "amixer -q -D pulse sset Master %s%%-" % self.step + subprocess.Popen(command.split()) From efcf8c8610767ac021e2e493b561201a0d6a21c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Mand=C3=A1k?= Date: Sun, 18 Jan 2015 11:59:05 +0100 Subject: [PATCH 6/9] Updated 'clock' module to change locale only if necessary. --- i3pystatus/clock.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/i3pystatus/clock.py b/i3pystatus/clock.py index d0db703..67a8fa7 100644 --- a/i3pystatus/clock.py +++ b/i3pystatus/clock.py @@ -32,13 +32,12 @@ class Clock(IntervalModule): on_downscroll = ["scroll_format", -1] def init(self): - lang = os.environ.get('LANG', None) - if lang: - # affects function time.strftime() in whole program - locale.setlocale(locale.LC_TIME, lang) - + lang, enc = os.environ.get('LANG', None).split('.', 1) + if lang != locale.getlocale(locale.LC_TIME)[0]: + # affects datetime.time.strftime() in whole program + locale.setlocale(locale.LC_TIME, (lang, enc)) + if self.format is None: - lang = locale.getlocale()[0] if lang == 'en_US': # MDY format - United States of America self.format = ["%a %b %-d %X"] From 9b2591018cf7b72159c1757e8ae1c3ac6c5161c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Mand=C3=A1k?= Date: Sun, 18 Jan 2015 18:24:35 +0100 Subject: [PATCH 7/9] Updated 'now_playing' module: - Added custom format and color, when no player is running. - Make a difference between DBus error and no players found. - Exdended try-cath for DBus errors. - Changed player method calls according to docs [1] since my player did not recognize them. [1] http://dbus.freedesktop.org/doc/dbus-python/doc/tutorial.html#interfaces-and-methods --- i3pystatus/now_playing.py | 109 +++++++++++++++++++++++--------------- 1 file changed, 65 insertions(+), 44 deletions(-) diff --git a/i3pystatus/now_playing.py b/i3pystatus/now_playing.py index fbbccdf..a413130 100644 --- a/i3pystatus/now_playing.py +++ b/i3pystatus/now_playing.py @@ -8,39 +8,49 @@ from i3pystatus import IntervalModule, formatp from i3pystatus.core.util import TimeWrapper +class NoPlayerException(Exception): + pass + + class NowPlaying(IntervalModule): """ Shows currently playing track information, supports most media players + * Requires python-dbus available from every distros' package manager. + + Left click on the module play/pauses, right click goes to the next track. + Available formatters (uses :ref:`formatp`) * `{title}` — (the title of the current song) * `{album}` — (the album of the current song, can be an empty string (e.g. for online streams)) * `{artist}` — (can be empty, too) * `{filename}` — (file name with out extension and path; empty unless title is empty) - * `{song_elapsed}` — (Position in the currently playing song, uses :ref:`TimeWrapper`, default is `%m:%S`) - * `{song_length}` — (Length of the current song, same as song_elapsed) + * `{song_elapsed}` — (position in the currently playing song, uses :ref:`TimeWrapper`, default is `%m:%S`) + * `{song_length}` — (length of the current song, same as song_elapsed) * `{status}` — (play, pause, stop mapped through the `status` dictionary) - * `{volume}` — (Volume) - - Left click on the module play/pauses, right click goes to the next track. - - Requires python-dbus available from every distros' package manager. + * `{volume}` — (volume) """ interval = 1 settings = ( - ("player", "Player name"), + ("player", "Player name. If not set, compatible players will be \ + detected automatically."), ("status", "Dictionary mapping pause, play and stop to output text"), - ("color", "Text color"), ("format", "formatp string"), + ("color", "Text color"), + ("format_no_player", "Text to show if no player is detected"), + ("color_no_player", "Text color when no player is detected"), ("hide_no_player", "Hide output if no player is detected"), ) hide_no_player = True - player = None + format_no_player = "No Player" + color_no_player = "#ffffff" + format = "{title} {status}" + color = "#ffffff" status = { "pause": "▷", "play": "▶", @@ -51,18 +61,19 @@ class NowPlaying(IntervalModule): "Paused": "pause", "Stopped": "stop", } - color = "#FFFFFF" - old_player = None on_leftclick = "playpause" on_rightclick = "next_song" + player = None + old_player = None + def find_player(self): players = [a for a in dbus.SessionBus().get_object("org.freedesktop.DBus", "/org/freedesktop/DBus").ListNames() if a.startswith("org.mpris.MediaPlayer2.")] if self.old_player in players: return self.old_player if not players: - raise dbus.exceptions.DBusException() + raise NoPlayerException() self.old_player = players[0] return players[0] @@ -76,45 +87,55 @@ class NowPlaying(IntervalModule): def run(self): try: player = self.get_player() - except dbus.exceptions.DBusException: + properties = dbus.Interface(player, "org.freedesktop.DBus.Properties") + get_prop = functools.partial(properties.Get, "org.mpris.MediaPlayer2.Player") + currentsong = get_prop("Metadata") + + fdict = { + "status": self.status[self.statusmap[get_prop("PlaybackStatus")]], + "len": 0, # TODO: Use optional(!) TrackList interface for this to gain 100 % mpd<->now_playing compat + "pos": 0, + "volume": int(get_prop("Volume") * 100), + + "title": currentsong.get("xesam:title", ""), + "album": currentsong.get("xesam:album", ""), + "artist": ", ".join(currentsong.get("xesam:artist", "")), + "song_length": TimeWrapper((currentsong.get("mpris:length") or 0) / 1000 ** 2), + "song_elapsed": TimeWrapper((get_prop("Position") or 0) / 1000 ** 2), + "filename": "", + } + + if not fdict["title"]: + fdict["filename"] = '.'.join( + basename((currentsong.get("xesam:url") or "")).split('.')[:-1]) + + self.output = { + "full_text": formatp(self.format, **fdict).strip(), + "color": self.color, + } + + except NoPlayerException: if self.hide_no_player: self.output = None else: self.output = { - "full_text": "now_playing: d-bus error", + "full_text": self.format_no_player, + "color": self.color, + } + return + + except dbus.exceptions.DBusException as e: + if self.hide_no_player: + self.output = None + else: + self.output = { + "full_text": "DBus error: " + e.get_dbus_message(), "color": "#ff0000", } return - properties = dbus.Interface(player, "org.freedesktop.DBus.Properties") - get_prop = functools.partial(properties.Get, "org.mpris.MediaPlayer2.Player") - currentsong = get_prop("Metadata") - - fdict = { - "status": self.status[self.statusmap[get_prop("PlaybackStatus")]], - "len": 0, # TODO: Use optional(!) TrackList interface for this to gain 100 % mpd<->now_playing compat - "pos": 0, - "volume": int(get_prop("Volume") * 100), - - "title": currentsong.get("xesam:title", ""), - "album": currentsong.get("xesam:album", ""), - "artist": ", ".join(currentsong.get("xesam:artist", "")), - "song_length": TimeWrapper((currentsong.get("mpris:length") or 0) / 1000 ** 2), - "song_elapsed": TimeWrapper((get_prop("Position") or 0) / 1000 ** 2), - "filename": "", - } - - if not fdict["title"]: - fdict["filename"] = '.'.join( - basename((currentsong.get("xesam:url") or "")).split('.')[:-1]) - - self.output = { - "full_text": formatp(self.format, **fdict).strip(), - "color": self.color, - } - def playpause(self): - self.get_player().PlayPause() + dbus.Interface(self.get_player(), "org.mpris.MediaPlayer2.Player").PlayPause() def next_song(self): - self.get_player().Next() + dbus.Interface(self.get_player(), "org.mpris.MediaPlayer2.Player").Next() From 877233ffab79e3d76938e64422a45bc8ffce5b77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Mand=C3=A1k?= Date: Sun, 18 Jan 2015 18:37:22 +0100 Subject: [PATCH 8/9] Removed 'deadbeef' module since it had mpris plugin. *facepalm* --- i3pystatus/deadbeef.py | 171 ----------------------------------------- 1 file changed, 171 deletions(-) delete mode 100644 i3pystatus/deadbeef.py diff --git a/i3pystatus/deadbeef.py b/i3pystatus/deadbeef.py deleted file mode 100644 index f8b16b0..0000000 --- a/i3pystatus/deadbeef.py +++ /dev/null @@ -1,171 +0,0 @@ -# -*- coding: utf-8 -*- -import shutil -import subprocess - -from i3pystatus import IntervalModule, formatp - - -class Deadbeef(IntervalModule): - """ - Plugin for DeaDBeeF music player. - - - Requires deadbeef >= 0.6.2 - - The playing/paused detection is a bit wonky since you can't get the - status directly from DB, so please do not use intervals shorter than 1s. - - Available formatters (uses :ref:`formatp`) - - * `{state}` — play, pause, stop mapped through the `status` dictionary - * `{artist}` — artist of current song - * `{title}` — title of current song - * `{album}` — album of current song - * `{band}` — gives "band", or "album artist" or "albumartist" or "artist",\ - whichever exists, in this order - * `{composer}` — composer - * `{track}` — (current song number - * `{numtracks}` — number of tracks in current song's album - * `{year}` — date or year - * `{genre}` — genre - * `{comment}` — comment - * `{copyright}` — copyright - * `{length}` — current song length (duration) - * `{elapsed}` — elaped time of current song - * `{filename}` — filename without path - * `{fullname}` — full pathname/uri - * `{dir}` — directory without path - * `{fulldir}` — directory name with full path - * `{channels}` — channel configuration (mono/stereo/..) - * `{version}` — deadbeef version number - - """ - - interval = 1.1 - - settings = ( - ("format", "formatp string"), - ("format_down", "String to use when DB is not running"), - ("status", "Dictionary mapping pause, play and stop to output"), - ("color", "Text color"), - ("color_down", "Text color when DB is not running"), - ) - - format = "{state} {elapsed} {artist} - {title}" - color = "#00dd00" - - format_down = "DeaDBeeF" - color_down = "#dd0000" - - # format when stopped is allways just "{state}" i.e. status["stop"] - status = { - "pause": u"▷", - "play": u"▶", - "stop": u"◾ DeaDBeeF", - } - - on_rightclick = "db_quit" - on_leftclick = "db_play_pause" - on_upscroll = "db_prev" - on_downscroll = "db_next" - - def init(self): - self.elapsed = "0:00" - db_dict = { - "state": "{state}", - "artist": "%a", - "title": "%t", - "album": "%b", - "band": "%B", - "composer": "%C", - "track": "%n", - "numtracks": "%N", - "year": "%y", - "genre": "%g", - "comment": "%c", - "copyright": "%r", - "length": "%l", - "elapsed": "%e", - "filename": "%f", - "fullname": "%F", - "dir": "%d", - "fulldir": "%D", - "channels": "%Z", - "version": "%V", - } - self.db_format = formatp(self.format, **db_dict).strip() - - def run(self): - if not self.db_running(): - self.output = { - "full_text": self.format_down, - "color": self.color_down, - } - return - - try: - command = ['deadbeef', '--nowplaying', '%e ' + self.db_format] - db = subprocess.Popen(command, shell=False, - stdout=subprocess.PIPE, - stderr=subprocess.DEVNULL) - db_out, db_err = db.communicate() - except OSError as e: - self.output = { - "full_text": "Error: " + e.strerror, - "color": "#ff0000", - } - return - except subprocess.CalledProcessError as e: - self.output = { - "full_text": "Error: " + e.output, - "color": "#ff0000", - } - return - db_out = db_out.decode("UTF-8").replace("\n", " ").strip() - - if db_out == "nothing": - self.output = { - "full_text": self.status["stop"], - "color": self.color, - } - return - - elapsed, db_out = db_out.split(" ", 1) - if elapsed == self.elapsed: - state = "pause" - else: - state = "play" - self.elapsed = elapsed - - self.output = { - "full_text": db_out.format(state=self.status[state]).strip(), - "color": self.color, - } - - def db_running(self): - command = "pidof deadbeef" - pidof = subprocess.Popen(command.split(), shell=False, - stdin=subprocess.DEVNULL, - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL) - pidof.communicate() - return False if pidof.returncode != 0 else True - - def db_quit(self): - command = "deadbeef" - if self.db_running(): - command += " --quit" - subprocess.Popen(command.split()) - - def db_play_pause(self): - command = "deadbeef --play-pause" - subprocess.Popen(command.split()) - - def db_prev(self): - if self.db_running(): - command = "deadbeef --prev" - subprocess.Popen(command.split()) - - def db_next(self): - if self.db_running(): - command = "deadbeef --next" - subprocess.Popen(command.split()) From f726300ca59a4a7d8f697589247cd15a2e7c706d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Mand=C3=A1k?= Date: Sun, 18 Jan 2015 19:51:55 +0100 Subject: [PATCH 9/9] Fixed freezing on mouse events when player is not running. Fixed color when player is not running. --- i3pystatus/now_playing.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/i3pystatus/now_playing.py b/i3pystatus/now_playing.py index a413130..b75104c 100644 --- a/i3pystatus/now_playing.py +++ b/i3pystatus/now_playing.py @@ -120,7 +120,7 @@ class NowPlaying(IntervalModule): else: self.output = { "full_text": self.format_no_player, - "color": self.color, + "color": self.color_no_player, } return @@ -135,7 +135,17 @@ class NowPlaying(IntervalModule): return def playpause(self): - dbus.Interface(self.get_player(), "org.mpris.MediaPlayer2.Player").PlayPause() + try: + dbus.Interface(self.get_player(), "org.mpris.MediaPlayer2.Player").PlayPause() + except NoPlayerException: + return + except dbus.exceptions.DBusException: + return def next_song(self): - dbus.Interface(self.get_player(), "org.mpris.MediaPlayer2.Player").Next() + try: + dbus.Interface(self.get_player(), "org.mpris.MediaPlayer2.Player").Next() + except NoPlayerException: + return + except dbus.exceptions.DBusException: + return