From 2d2a3fe0d695889c00d7525265c18392a3c552c8 Mon Sep 17 00:00:00 2001 From: Max Demian Date: Sun, 15 Mar 2015 19:58:18 +0100 Subject: [PATCH] Update to 1.8.0 Fixed 3 issues: #36,#38, #39 New options: substring_search (boolean, set to True if you want to be able to block "Bloodhound Gang - Ivan Eth Nioj" with "Ivan". Risk for fals e positives) pacmd_muted_value (string, the localized value for "yes", needed to parse the pacmd list-sink-inputs output correctly) --- README.md | 5 ++-- blockify/blockify.py | 53 ++++++++++++++----------------------- blockify/blockifyui.py | 21 +++++++-------- blockify/blocklist.py | 16 +++++++---- blockify/interludeplayer.py | 9 ++++--- blockify/util.py | 31 +++++++++++++++++++--- setup.py | 4 +-- 7 files changed, 78 insertions(+), 61 deletions(-) diff --git a/README.md b/README.md index ba9ba70..15eb7d0 100644 --- a/README.md +++ b/README.md @@ -25,10 +25,10 @@ sudo pacman -U blockify-X.Y-Z-any.pkg.tar.xz ##### Direct (pip/setup.py) If there is no blockify package available on your distribution, you'll have to install it directly via one of pythons many installation tools. -Example Ubuntu installation routine for fixed version (e.g. v1.7.2): +Example Ubuntu installation routine for fixed version (e.g. v1.8.0): ``` bash sudo apt-get install python-pip python-wnck python-gst0.10 -sudo pip install http://github.com/mikar/blockify/archive/v1.7.2.zip +sudo pip install http://github.com/mikar/blockify/archive/v1.8.0.zip # Create optional desktop icon echo -e '[Desktop Entry]\nName=Blockify\nComment=Blocks Spotify commercials\nExec=blockify-ui\nIcon='$(python -c 'import pkg_resources; print pkg_resources.resource_filename("blockify", "data/icon-red-512.png")')'\nType=Application\nCategories=AudioVideo' | sudo tee /usr/share/applications/blockify.desktop ``` @@ -111,6 +111,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.0 (2015-03-15): Added substring_search option (#36). Added pacmd_muted_value option (#38). Removed gtk.threads_init() (#39). - v1.7.2 (2015-01-10): Added unmute_delay option for the GUI, too. Removed forced unmute when Spotify is not playing a song or blockify can't find an ad. - v1.7.1 (2014-12-26): Fix for [issue #32](https://github.com/mikar/blockify/issues/32) (introduced playback_delay option), better load_config and update_slider error catching - v1.7 (2014-12-24): Unmute delay (avoid last second of commercial), segfault bug fix, Timeout for radio stations, logging improvements, threading improvements (complete switch to gtk), refactorings. diff --git a/blockify/blockify.py b/blockify/blockify.py index be0de6a..312bfea 100644 --- a/blockify/blockify.py +++ b/blockify/blockify.py @@ -30,11 +30,6 @@ log = logging.getLogger("main") pygtk.require("2.0") -try: - from docopt import docopt -except ImportError: - log.error("ImportError: Please install docopt to use the CLI.") - class Blockify(object): @@ -44,12 +39,11 @@ def __init__(self, blocklist): self.check_for_blockify_process() self.check_for_spotify_process() - self.options = util.load_options() - - self._autodetect = self.options["general"]["autodetect"] - self._automute = self.options["general"]["automute"] - self.update_interval = self.options["cli"]["update_interval"] - self.unmute_delay = self.options["cli"]["unmute_delay"] + self._autodetect = util.CONFIG["general"]["autodetect"] + 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.found = False self.current_song = "" self.song_status = "" @@ -74,7 +68,7 @@ def __init__(self, blocklist): self.mutemethod = self.alsa_mute # Only use interlude music if we use pulse sinks and the interlude playlist is non-empty. - self.use_interlude_music = self.options["interlude"]["use_interlude_music"] and \ + self.use_interlude_music = util.CONFIG["interlude"]["use_interlude_music"] and \ self.mutemethod == self.pulsesink_mute and \ self.player.max_index >= 0 @@ -120,7 +114,6 @@ def start(self): self.bind_signals() self.toggle_mute() - gtk.threads_init() gtk.timeout_add(self.update_interval, self.update) log.info("Blockify started.") gtk.main() @@ -165,10 +158,9 @@ def find_ad(self): log.info("Blockfile changed. Reloading.") self.blocklist.__init__() - for i in self.blocklist: - if self.current_song.startswith(i): - self.ad_found() - return True + if self.blocklist.find(self.current_song): + self.ad_found() + return True # Unmute with a certain delay to avoid the last second # of commercial you sometimes hear because it's unmuted too early. @@ -198,11 +190,10 @@ def unmute_with_delay(self): self.toggle_mute() return False - def get_windows(self): + def find_spotify_window(self): "Libwnck list of currently open windows." # Get the current screen. screen = wnck.screen_get_default() - screen.force_update() # Object list of windows in screen. windows = screen.get_windows() @@ -214,10 +205,13 @@ def get_windows(self): def get_current_song(self): "Checks if a Spotify window exists and returns the current songname." song = "" - spotify_window = self.get_windows() + spotify_window = self.find_spotify_window() if spotify_window: - song = " ".join(spotify_window[0].split()[2:]).decode("utf-8") + try: + song = " ".join(spotify_window[0].split()[2:]).decode("utf-8") + except Exception as e: + log.debug("Could not match spotify pid to sink pid: %s", e, exc_info=1) return song @@ -308,18 +302,18 @@ def pulsesink_mute(self, mode): 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 == "yes" else False + 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)) return - if muted == "yes" and (mode == 2 or not self.current_song): + 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 muted == "no" and mode == 1: + 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 muted == "yes" and not mode: + elif self.is_sink_muted and not mode: log.info("Unmuting.") subprocess.call(["pacmd", "set-sink-input-mute", index, "0"]) @@ -363,14 +357,7 @@ def autodetect(self, boolean): def initialize(doc=__doc__): - # Set up the configuration directory & files, if necessary. - util.init_config_dir() - - try: - args = docopt(doc, version=util.BLOCKIFY_VERSION) - util.init_logger(args["--log"], args["-v"], args["--quiet"]) - except NameError: - util.init_logger() + util.initialize(doc) blockify = Blockify(blocklist.Blocklist()) diff --git a/blockify/blockifyui.py b/blockify/blockifyui.py index 4597eb7..f8834e6 100755 --- a/blockify/blockifyui.py +++ b/blockify/blockifyui.py @@ -175,9 +175,9 @@ def __init__(self, blockify): self.thumbnail_dir = util.THUMBNAIL_DIR self.cover_server = "https://i.scdn.co/image/" - self.use_cover_art = self.b.options["gui"]["use_cover_art"] - self.autohide_cover = self.b.options["gui"]["autohide_cover"] - self.b.unmute_delay = self.b.options["gui"]["unmute_delay"] + self.use_cover_art = util.CONFIG["gui"]["use_cover_art"] + self.autohide_cover = util.CONFIG["gui"]["autohide_cover"] + self.b.unmute_delay = util.CONFIG["gui"]["unmute_delay"] self.previous_cover_file = "" self.editor = None @@ -187,7 +187,7 @@ def __init__(self, blockify): # reduce CPU usage and decrease it to improve responsiveness. # If you need absolutely minimal CPU usage you could, in self.start(), # change the line to glib.timeout_add_seconds(2, self.update) or more. - self.update_interval = self.b.options["gui"]["update_interval"] + self.update_interval = util.CONFIG["gui"]["update_interval"] # Update interval for interlude music slider. self.slider_update_interval = 100 @@ -209,7 +209,6 @@ def __init__(self, blockify): self.show_all() self.set_states() log.info("Blockify-UI initialized.") - self.start() def create_tray(self): basedir = os.path.dirname(os.path.realpath(__file__)) @@ -227,7 +226,7 @@ def create_tray(self): self.status_icon.connect("popup-menu", self.on_tray_right_click) self.status_icon.connect("activate", self.on_tray_left_click) - self.status_icon.set_tooltip("blockify v{0}".format(util.BLOCKIFY_VERSION)) + self.status_icon.set_tooltip("blockify v{0}".format(util.VERSION)) self.connect("delete-event", self.on_delete_event) def create_traymenu(self, event_button, event_time): @@ -396,8 +395,8 @@ def create_layout(self): def set_states(self): checkboxes = [self.autodetect_chk, self.automute_chk, self.autoresume_chk, self.autohidecover_chk] - values = [self.b.options["general"]["autodetect"], self.b.options["general"]["automute"], - self.b.options["interlude"]["autoresume"], self.b.options["gui"]["autohide_cover"]] + values = [util.CONFIG["general"]["autodetect"], util.CONFIG["general"]["automute"], + util.CONFIG["interlude"]["autoresume"], util.CONFIG["gui"]["autohide_cover"]] for i in range(len(checkboxes)): checkboxes[i].set_active(values[i]) @@ -412,7 +411,6 @@ def start(self): self.b.toggle_mute() self.bind_signals() - gtk.threads_init() # Start and loop the main update routine once every X ms. # To influence responsiveness or CPU usage, decrease/increase self.update_interval. gtk.timeout_add(self.update_interval, self.update) @@ -437,7 +435,7 @@ def show_about_dialogue(self, widget): about.set_destroy_with_parent (True) about.set_icon_name ("blockify") about.set_name("blockify") - about.set_version(util.BLOCKIFY_VERSION) + about.set_version(util.VERSION) about.set_website("http://github.com/mikar/blockify") about.set_copyright("(c) 2014 Max Demian") about.set_license("The MIT License (MIT)") @@ -877,7 +875,8 @@ def on_exit_btn(self, widget): def main(): "Entry point for the GUI-version of Blockify." - BlockifyUI(blockify.initialize(__doc__)) + blockifyUI = BlockifyUI(blockify.initialize(__doc__)) + blockifyUI.start() if __name__ == "__main__": diff --git a/blockify/blocklist.py b/blockify/blocklist.py index 48e0dad..6b38015 100755 --- a/blockify/blocklist.py +++ b/blockify/blocklist.py @@ -9,9 +9,10 @@ class Blocklist(list): - "A python list extended to store (manually) blocked songs/ads persisently." + "List extended to store (manually) blocked songs/ads persisently." # Could subclass UserList.UserList here instead which inherits from # collections.MutableSequence. In Python3 it's collections.UserList. + def __init__(self): super(Blocklist, self).__init__() self.location = util.BLOCKLIST_FILE @@ -38,12 +39,17 @@ def remove(self, item): log.warn("Could not remove {} from blocklist: {}".format(item, e)) def find(self, song): - # Arbitrary minimum length of 4 to avoid ambiguous song names. - while len(song) > 4: + if util.CONFIG["general"]["substring_search"]: for item in self: - if item.startswith(song): + if item in song: return item - song = song[:len(song) / 2] + else: + # Arbitrary minimum length of 4 to avoid ambiguous song names. + while len(song) > 4: + for item in self: + if item.startswith(song): + return item + song = song[:len(song) / 2] def get_timestamp(self): return os.path.getmtime(self.location) diff --git a/blockify/interludeplayer.py b/blockify/interludeplayer.py index 0ed6015..092b636 100755 --- a/blockify/interludeplayer.py +++ b/blockify/interludeplayer.py @@ -7,6 +7,7 @@ import gst import gtk import random +from blockify import util log = logging.getLogger("player") @@ -21,10 +22,10 @@ def __init__(self, blockify): self.manual_control = False self.temp_autoresume = False self.temp_disable = False - self.playback_delay = self.b.options["interlude"]["playback_delay"] + self.playback_delay = util.CONFIG["interlude"]["playback_delay"] # Automatically resume spotify playback after n seconds. - self.radio_timeout = self.b.options["interlude"]["radio_timeout"] - self.autoresume = self.b.options["interlude"]["autoresume"] + 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" ] @@ -56,7 +57,7 @@ def parse_playlist(self, sourcelist=None, source=None): playlist = [] if sourcelist is None: - sourcelist = [self.b.options["interlude"]["playlist"]] + sourcelist = [util.CONFIG["interlude"]["playlist"]] try: for item in sourcelist: diff --git a/blockify/util.py b/blockify/util.py index 95b212b..dcaf3fe 100755 --- a/blockify/util.py +++ b/blockify/util.py @@ -4,9 +4,14 @@ import os import sys - log = logging.getLogger("util") -BLOCKIFY_VERSION = "1.7.2" + +try: + from docopt import docopt +except ImportError: + log.error("ImportError: Please install docopt to use the CLI.") + +VERSION = "1.8.0" CONFIG_DIR = os.path.join(os.path.expanduser("~"), ".config/blockify") CONFIG_FILE = os.path.join(CONFIG_DIR, "blockify.ini") BLOCKLIST_FILE = os.path.join(CONFIG_DIR, "blocklist.txt") @@ -45,6 +50,7 @@ def init_logger(logpath=None, loglevel=0, quiet=False): except IOError: log.error("Could not attach file handler.") + def rename_file(fname): if not os.path.isfile(fname): try: @@ -75,7 +81,9 @@ def get_default_options(): options = { "general": { "autodetect": True, - "automute": True + "automute": True, + "substring_search": False, + "pacmd_muted_value":"yes" }, "cli": { "update_interval": 200, @@ -107,7 +115,9 @@ def load_options(): options["general"] = { "autodetect":config.getboolean("general", "autodetect"), - "automute":config.getboolean("general", "automute") + "automute":config.getboolean("general", "automute"), + "substring_search":config.getboolean("general", "substring_search"), + "pacmd_muted_value":config.get("general", "pacmd_muted_value") } options["cli"] = { "update_interval":config.getint("cli", "update_interval"), @@ -153,3 +163,16 @@ def save_options(CONFIG_DIR, options): config.write(f) log.info("Configuration file written to {}.".format(configfile)) + + +def initialize(doc): + # Set up the configuration directory & files, if necessary. + init_config_dir() + + try: + args = docopt(doc, version=VERSION) + init_logger(args["--log"], args["-v"], args["--quiet"]) + except NameError: + init_logger() + +CONFIG = load_options() \ No newline at end of file diff --git a/setup.py b/setup.py index 11aa57b..380517c 100755 --- a/setup.py +++ b/setup.py @@ -1,5 +1,5 @@ from os.path import dirname, join -from blockify.util import BLOCKIFY_VERSION +from blockify.util import VERSION try: from setuptools import setup @@ -20,7 +20,7 @@ def read(filename): name=_name, description="Mute spotify advertisements.", long_description=read("README.md"), - version=BLOCKIFY_VERSION, + version=VERSION, license=_license, url="https://github.com/mikar/{}".format(_name), author="Max Demian",