Skip to content

Commit

Permalink
Update to 1.8.0
Browse files Browse the repository at this point in the history
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)
  • Loading branch information
Max Demian committed Mar 15, 2015
1 parent ddb6fd6 commit 2d2a3fe
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 61 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```
Expand Down Expand Up @@ -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.
Expand Down
53 changes: 20 additions & 33 deletions blockify/blockify.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):

Expand All @@ -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 = ""
Expand All @@ -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

Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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()
Expand All @@ -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

Expand Down Expand Up @@ -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"])

Expand Down Expand Up @@ -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())

Expand Down
21 changes: 10 additions & 11 deletions blockify/blockifyui.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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__))
Expand All @@ -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):
Expand Down Expand Up @@ -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])
Expand All @@ -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)
Expand All @@ -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)")
Expand Down Expand Up @@ -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__":
Expand Down
16 changes: 11 additions & 5 deletions blockify/blocklist.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down
9 changes: 5 additions & 4 deletions blockify/interludeplayer.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import gst
import gtk
import random
from blockify import util


log = logging.getLogger("player")
Expand All @@ -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" ]
Expand Down Expand Up @@ -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:
Expand Down
31 changes: 27 additions & 4 deletions blockify/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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"),
Expand Down Expand Up @@ -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()
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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",
Expand Down

0 comments on commit 2d2a3fe

Please sign in to comment.