From 9d271b4e01786f6916011143d78778b2516cb6d6 Mon Sep 17 00:00:00 2001 From: Max Demian Date: Sat, 11 Jul 2015 17:01:00 +0200 Subject: [PATCH] version bump, fixing issues #46 and #47 --- README.md | 1 + blockify/blockify.py | 75 +++++++++++++++++------------- blockify/blockifyui.py | 65 +++++++++++++------------- blockify/data/example_blockify.ini | 9 ++-- blockify/interludeplayer.py | 14 +++++- blockify/util.py | 9 ++-- 6 files changed, 95 insertions(+), 78 deletions(-) diff --git a/README.md b/README.md index 588aec2..15bc9f9 100644 --- a/README.md +++ b/README.md @@ -131,6 +131,7 @@ http://skyserver5.skydisc.net:8000 You can use relative and absolute paths as well as basically any audio source/format, as long as you have the respective gstreamer codec installed. ## Changelog +- v1.8.8 (2015-07-11): Fix [issue #46](https://github.com/mikar/blockify/issues/46) and [issue #47](https://github.com/mikar/blockify/issues/47) - v1.8.7 (2015-06-11): Pressing play will now properly pause interlude music before resuming spotify playback. - v1.8.6 (2015-05-10): Minor refactoring and removed incomplete "fix" for [issue #44](https://github.com/mikar/blockify/issues/44). - v1.8.5 (2015-05-09): Signal cleanups and [issue #44](https://github.com/mikar/blockify/issues/44) again. diff --git a/blockify/blockify.py b/blockify/blockify.py index c213b08..205f36d 100644 --- a/blockify/blockify.py +++ b/blockify/blockify.py @@ -33,6 +33,7 @@ class Blockify(object): + def __init__(self, blocklist): self.blocklist = blocklist self.orglist = blocklist[:] @@ -43,7 +44,7 @@ def __init__(self, blocklist): self._automute = util.CONFIG["general"]["automute"] self.update_interval = util.CONFIG["cli"]["update_interval"] self.unmute_delay = util.CONFIG["cli"]["unmute_delay"] - self.pacmd_muted_value = util.CONFIG["general"]["pacmd_muted_value"] + self.pulse_unmuted_value = "" self.env = os.environ.copy() self.env["LC_ALL"] = "en_US" self.found = False @@ -63,8 +64,12 @@ def __init__(self, blocklist): # Determine if we can use sinks or have to use alsa. try: devnull = open(os.devnull) - subprocess.check_output(["pacmd", "list-sink-inputs"], stderr=devnull) + pacmd_out = subprocess.check_output(["pacmd", "list-sink-inputs"], stderr=devnull) self.mutemethod = self.pulsesink_mute + # Properly initialize unmuted state, i.e. self.pulse_unmuted_value + self.toggle_mute(2) + sink_status = self.extract_pulse_sink_status(pacmd_out) + self.pulse_unmuted_value = sink_status[1] log.debug("Mute method is pulse sink.") except (OSError, subprocess.CalledProcessError): log.info("No pulse sinks found, falling back to system mute via alsa.") @@ -77,6 +82,7 @@ def __init__(self, blocklist): log.info("Blockify initialized.") + def check_for_blockify_process(self): try: pid = subprocess.check_output(["pgrep", "-f", "python.*blockify"]) @@ -115,6 +121,7 @@ def init_dbus(self): def start(self): self.bind_signals() + # Force unmute to properly initialize unmuted state self.toggle_mute() gtk.timeout_add(self.update_interval, self.update) @@ -285,48 +292,52 @@ def pulse_mute(self, mode): for channel in self.channels: subprocess.Popen(["amixer", "-qD", "pulse", "set", channel, state]) - def pulsesink_mute(self, mode): - "Finds spotify's audio sink and toggles its mute state." - try: - pacmd_out = subprocess.check_output(["pacmd", "list-sink-inputs"], env=self.env) - except subprocess.CalledProcessError: - log.error("Spotify sink not found. Is Pulse running? Resorting to pulse amixer as mute method.") - self.mutemethod = self.pulse_mute # Fall back to amixer mute. - self.use_interlude_music = False - return - # Match muted and application.process.id values. + def extract_pulse_sink_status(self, pacmd_out): + sink_status = ("", "") + # Match muted_value and application.process.id values. pattern = re.compile(r"(?: index|muted|application\.process\.id).*?(\w+)") # Put valid spotify PIDs in a list output = pacmd_out.decode("utf-8") spotify_sink_list = [" index: " + i for i in output.split("index: ") if "spotify" in i] - if not len(spotify_sink_list): - return + if len(spotify_sink_list) and self.spotify_pids: + sink_infos = [pattern.findall(sink) for sink in spotify_sink_list] + # Every third element per sublist is a key, the value is the preceding + # two elements in the form of a tuple - {pid : (index, muted_value)} + idxd = {sink_status[2]: (sink_status[0], sink_status[1]) for sink_status in sink_infos if len(sink_status) == 3} + + pid = [k for k in idxd.keys() if k in self.spotify_pids][0] + sink_status = idxd[pid] # tuple of 2 elements: (index, muted_value) - sink_infos = [pattern.findall(sink) for sink in spotify_sink_list] - # Every third element per sublist is a key, the value is the preceding - # two elements in the form of a tuple - {pid : (index, muted)} - idxd = {info[2]: (info[0], info[1]) for info in sink_infos if len(info) == 3} + return sink_status + + def pulsesink_mute(self, mode): + "Finds spotify's audio sink and toggles its mute state." try: - pid = [k for k in idxd.keys() if k in self.spotify_pids][0] - index, muted = idxd[pid] - self.is_sink_muted = True if muted == self.pacmd_muted_value else False - except IndexError as e: - log.debug("Could not match spotify pid to sink pid: {}".format(e)) + pacmd_out = subprocess.check_output(["pacmd", "list-sink-inputs"], env=self.env) + except subprocess.CalledProcessError: + log.error("Spotify sink not found. Is Pulse running? Resorting to pulse amixer as mute method.") + self.mutemethod = self.pulse_mute # Fall back to amixer mute. + self.use_interlude_music = False return - if self.is_sink_muted and (mode == 2 or not self.current_song): - log.info("Forcing unmute.") - subprocess.call(["pacmd", "set-sink-input-mute", index, "0"]) - elif not self.is_sink_muted and mode == 1: - log.info("Muting {}.".format(self.current_song)) - subprocess.call(["pacmd", "set-sink-input-mute", index, "1"]) - elif self.is_sink_muted and not mode: - log.info("Unmuting.") - subprocess.call(["pacmd", "set-sink-input-mute", index, "0"]) + index, muted_value = self.extract_pulse_sink_status(pacmd_out) + + self.is_sink_muted = False if muted_value == self.pulse_unmuted_value else True + + if index: + if self.is_sink_muted and (mode == 2 or not self.current_song): + log.info("Forcing unmute.") + subprocess.call(["pacmd", "set-sink-input-mute", index, "0"]) + elif not self.is_sink_muted and mode == 1: + log.info("Muting {}.".format(self.current_song)) + subprocess.call(["pacmd", "set-sink-input-mute", index, "1"]) + elif self.is_sink_muted and not mode: + log.info("Unmuting.") + subprocess.call(["pacmd", "set-sink-input-mute", index, "0"]) def prev(self): self.dbus.prev() diff --git a/blockify/blockifyui.py b/blockify/blockifyui.py index 3b89a1b..e4807f0 100755 --- a/blockify/blockifyui.py +++ b/blockify/blockifyui.py @@ -236,35 +236,35 @@ def create_tray(self): def create_traymenu(self, event_button, event_time): menu = gtk.Menu() - toggleblock = gtk.MenuItem("Toggle Block") - toggleblock.show() - menu.append(toggleblock) - toggleblock.connect("activate", self.on_toggle_block_btn) - - toggleplay = gtk.MenuItem("Toggle Play") - toggleplay.show() - toggleplay.connect("activate", self.on_toggleplay_btn) - menu.append(toggleplay) - - prevsong = gtk.MenuItem("Previous Song") - prevsong.show() - prevsong.connect("activate", self.on_prev_btn) - menu.append(prevsong) - - nextsong = gtk.MenuItem("Next Song") - nextsong.show() - nextsong.connect("activate", self.on_next_btn) - menu.append(nextsong) - - about = gtk.MenuItem("About") - about.show() - menu.append(about) - about.connect("activate", self.show_about_dialogue) - - exit = gtk.MenuItem("Exit") - exit.show() - menu.append(exit) - exit.connect("activate", self.on_exit_btn) + toggleblock_menuitem = gtk.MenuItem("Toggle Block") + toggleblock_menuitem.show() + menu.append(toggleblock_menuitem) + toggleblock_menuitem.connect("activate", self.on_toggle_block_btn) + + toggleplay_menuitem = gtk.MenuItem("Toggle Play") + toggleplay_menuitem.show() + toggleplay_menuitem.connect("activate", self.on_toggleplay_btn) + menu.append(toggleplay_menuitem) + + prevsong_menuitem = gtk.MenuItem("Previous Song") + prevsong_menuitem.show() + prevsong_menuitem.connect("activate", self.on_prev_btn) + menu.append(prevsong_menuitem) + + nextsong_menuitem = gtk.MenuItem("Next Song") + nextsong_menuitem.show() + nextsong_menuitem.connect("activate", self.on_next_btn) + menu.append(nextsong_menuitem) + + about_menuitem = gtk.MenuItem("About") + about_menuitem.show() + menu.append(about_menuitem) + about_menuitem.connect("activate", self.show_about_dialogue) + + exit_menuitem = gtk.MenuItem("Exit") + exit_menuitem.show() + menu.append(exit_menuitem) + exit_menuitem.connect("activate", self.on_exit_btn) menu.popup(None, None, gtk.status_icon_position_menu, event_button, event_time, self.status_icon) @@ -397,12 +397,11 @@ def create_layout(self): self.add(main) def set_states(self): - - checkboxes = [self.autodetect_chk, self.automute_chk, self.autoresume_chk, self.autohide_cover_chk] + checkboxes = [self.autodetect_chk, self.automute_chk, self.autohide_cover_chk, self.autoresume_chk] values = [util.CONFIG["general"]["autodetect"], util.CONFIG["general"]["automute"], - util.CONFIG["interlude"]["autoresume"], util.CONFIG["gui"]["autohide_cover"]] + util.CONFIG["gui"]["autohide_cover"], util.CONFIG["interlude"]["autoresume"]] - for i in range(len(checkboxes)): + for i in range(len(checkboxes) - 1): checkboxes[i].set_active(values[i]) # Pretend that a song is playing to keep disable_interlude_box() from pausing playback. diff --git a/blockify/data/example_blockify.ini b/blockify/data/example_blockify.ini index c2ee30c..2410b6f 100755 --- a/blockify/data/example_blockify.ini +++ b/blockify/data/example_blockify.ini @@ -6,14 +6,11 @@ automute = True # By default blockify matches blocklist entries by reading the current song from # left to right, meaning to block "Bloodhound Gang - Yvan Eht Nioj", the blocklist # entry needs to (at least) start with B. -# If you want to be able to block that song by adding "Eht" or any other substring +# If you want to be able to block that song by adding "Gang" or any other substring # within that song, set this option to True. Beware of false positives though as -# the string "Eth" could appear in a number of other songs. +# the string "Gang" could appear in a number of other songs, e.g. the track +# "Gangnam Style" would be blocked, too. Which might not be that bad, actually. substring_search = False -# Blockify parses the output of pacmd list-sink-inputs. That output CAN be -# localized in which case you'd need to put your translation of "yes" here. -# For polish it'd be "tak" instead of "yes". -pacmd_muted_value = yes [cli] # Update frequency for the CLI in seconds. Lower means quicker detection diff --git a/blockify/interludeplayer.py b/blockify/interludeplayer.py index 8939768..6f4f421 100755 --- a/blockify/interludeplayer.py +++ b/blockify/interludeplayer.py @@ -20,14 +20,14 @@ class InterludePlayer(object): def __init__(self, blockify): self.gst = gst self.b = blockify - self._index = 0 self.manual_control = False self.temp_autoresume = False self.temp_disable = False + self._index = 0 + self._autoresume = util.CONFIG["interlude"]["autoresume"] self.playback_delay = util.CONFIG["interlude"]["playback_delay"] # Automatically resume spotify playback after n seconds. self.radio_timeout = util.CONFIG["interlude"]["radio_timeout"] - self.autoresume = util.CONFIG["interlude"]["autoresume"] self.uri_rx = re.compile("[A-Za-z]+:\/\/") self.formats = ["mp3", "mp4", "flac", "wav", "wma", "ogg", "avi", "mov", "mpg", "flv", "wmv", \ "spx", "3gp", "b-mtp", "aac", "aiff", "raw", "midi", "ulaw", "alaw", "gsm" ] @@ -260,6 +260,16 @@ def set_uri(self): log.debug("Setting interlude to: {0}".format(uri)) self.player.set_property("uri", uri) + + @property + def autoresume(self): + return self._autoresume + + @autoresume.setter + def autoresume(self, autoresume): + log.debug("Setting autoresume to: {0}".format(autoresume)) + self._autoresume = autoresume + @property def index(self): return self._index diff --git a/blockify/util.py b/blockify/util.py index 3f15ab5..a64cb32 100755 --- a/blockify/util.py +++ b/blockify/util.py @@ -11,7 +11,7 @@ except ImportError: log.error("ImportError: Please install docopt to use the CLI.") -VERSION = "1.8.7" +VERSION = "1.8.8" CONFIG = None CONFIG_DIR = os.path.join(os.path.expanduser("~"), ".config/blockify") CONFIG_FILE = os.path.join(CONFIG_DIR, "blockify.ini") @@ -83,9 +83,8 @@ def get_default_options(): "general": { "autodetect": True, "automute": True, - "substring_search": False, - "pacmd_muted_value":"yes" - }, + "substring_search": False + }, "cli": { "update_interval": 200, "unmute_delay": 700 @@ -118,7 +117,7 @@ def load_options(): except Exception as e: log.error("Could not read config file: {}. Using default options.".format(e)) else: - option_tuples = [("general", "autodetect", "bool"), ("general", "automute", "bool"), ("general", "substring_search", "bool"), ("general", "pacmd_muted_value", "str"), + option_tuples = [("general", "autodetect", "bool"), ("general", "automute", "bool"), ("general", "substring_search", "bool"), ("cli", "update_interval", "int"), ("cli", "unmute_delay", "int"), ("gui", "use_cover_art", "bool"), ("gui", "autohide_cover", "bool"), ("gui", "update_interval", "int"), ("gui", "unmute_delay", "int"), ("interlude", "use_interlude_music", "bool"), ("interlude", "start_shuffled", "bool"), ("interlude", "autoresume", "bool"),