Skip to content

Commit

Permalink
update to 3.5.0 with more robust ad detection, closes #89 #95
Browse files Browse the repository at this point in the history
  • Loading branch information
gmdfalk committed Apr 7, 2016
1 parent 11cc3f9 commit 536360f
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 41 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Changelog

- v3.5.0 (2016-04-08): Reintroduce wmctrl to catch video ads ([issue #89](https://github.com/mikar/blockify/issues/89)) and block some audio ads more reliably. Fix encoding issues ([issue #95](https://github.com/mikar/blockify/issues/95)).
- v3.4.0 (2016-03-25): Fix play/pause toggle button, right click on tray [issue #83](https://github.com/mikar/blockify/issues/83) and add start_minimized option [issue #93](https://github.com/mikar/blockify/issues/93).
- v3.3.1 (2016-01-03): Fix interlude player crashes ([issue #84](https://github.com/mikar/blockify/issues/84)).
- v3.3.0 (2016-01-03): Enable profiling, improve GUI performance, fix playback button & title status functionality and add tray icon toolip.
Expand Down
61 changes: 40 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,18 @@ Blockify is a linux only application that allows you to automatically mute songs

## Installation
### Basic Requirements
- Python3
- Pulseaudio
- Gstreamer1.0 (including the plugins you need for the audio formats you want to be able to play as interlude music)
- Spotify > 1.0.12 (to get the latest version follow the instructions given here [Spotify-Client-1-x-beta-] (https://community.spotify.com/t5/Spotify-Community-Blog/Spotify-Client-1-x-beta-for-Linux-has-been-released/ba-p/1147084))

Mandatory:
- Python3
- Spotify > 1.0.12 (to get the latest version follow the instructions given here [Spotify-Client-1-x-beta-] (https://community.spotify.com/t5/Spotify-Community-Blog/Spotify-Client-1-x-beta-for-Linux-has-been-released/ba-p/1147084))
- wmtrcl (provides information about the Spotify window)

Optional but highly recommended:
- Pulseaudio (allows muting Spotify instead of all system sound)
- Gstreamer1.0 (used to play music of your choice during muted ads. Requires pulseaudio.)

Optional:
- docopt (provides a command-line interface for blockify and blockify-ui)

### Prepackaged

Expand All @@ -30,7 +38,7 @@ Available in the [openSUSE build service](https://build.opensuse.org/package/sho
If there is no blockify package available on your distribution, you'll have to install it directly via one of pythons many installation tools.

Before installing blockify, please make sure you have the appropriate dependencies installed:
`pacman -S python pulseaudio gst-python alsa-utils pygtk python-dbus python-setuptools python-gobject python-docopt`
`pacman -S git python-pip gst-python pulseaudio alsa-utils pygtk python-dbus python-gobject python-docopt wmctrl`

Package names are for ArchLinux and will probably differ slightly between distributions.

Expand All @@ -41,7 +49,7 @@ echo deb http://repository.spotify.com testing non-free | sudo tee /etc/apt/sour
sudo apt-get update
sudo apt-get install spotify-client
# Install blockify dependencies
sudo apt-get install git python3-requests python3-docopt python3-pip python3-gst-1.0 python-configparser
sudo apt-get install git python3-pip python3-gst-1.0 python3-requests python3-docopt wmctrl
```

Install routine:
Expand Down Expand Up @@ -85,29 +93,40 @@ Blockify accepts several signals:

To easily use these signals add the following function to your .bashrc:
```bash
bl() {
bb() {
local signal
local cmd
[[ "$#" -lt 1 ]] && echo "Usage: bb ( b[lock] | u[nblock] | p[revious] | n[ext] | t[oggle] | t[oggle]b[lock] |...)" && return 0
case "$1" in
"") blockify-dbus get && return 0;;
ex) signal='TERM';; # Exit
b) signal='USR1';; # Block
u) signal='USR2';; # Unblock
p) signal='RTMIN';; # Previous song
n) signal='RTMIN+1';; # Next song
t) signal='RTMIN+2';; # Toggle play song
tb) signal='RTMIN+3';; # Toggle block song
pi) signal='RTMIN+10';; # Previous interlude song
ni) signal='RTMIN+11';; # Next interlude song
ti) signal='RTMIN+12';; # Toggle play interlude song
tir) signal='RTMIN+13';; # Toggle interlude resume
'') blockify-dbus get 2>/dev/null && return 0;;
ex|exit)
signal='TERM';; # Exit
b|block)
signal='USR1';; # Block
u|unblock)
signal='USR2';; # Unblock
p|previous)
signal='RTMIN';; # Previous song
n|next)
signal='RTMIN+1';; # Next song
t|toggle)
signal='RTMIN+2';; # Toggle play song
tb|toggleblock)
signal='RTMIN+3';; # Toggle block song
ip|iprevious)
signal='RTMIN+10';; # Previous interlude song
in|inext)
signal='RTMIN+11';; # Next interlude song
it|itoggle)
signal='RTMIN+12';; # Toggle play interlude song
itr|itoggleresume)
signal='RTMIN+13';; # Toggle interlude resume
*) echo "Bad option" && return 0;;
esac
pkill --signal "$signal" -f 'python.*blockify'
}
```

Then use it via e.g. `bl` to get current song info or `bl t` to toggle playback.
Then use it via e.g. `bb` to get current song info or `bb t` to toggle playback.

#### CLI
Blockify has a CLI/daemon that you can start with `blockify`.
Expand Down
40 changes: 39 additions & 1 deletion blockify/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import re
import signal
import subprocess
import sys
import time

from gi import require_version
Expand Down Expand Up @@ -50,6 +51,7 @@ def __init__(self, blocklist):
self.pulse_unmuted_value = ""
self.song_delimiter = " - " # u" \u2013 "
self.found = False
self.current_song_from_window_title = ""
self.current_song = ""
self.current_song_artist = ""
self.current_song_title = ""
Expand Down Expand Up @@ -256,6 +258,22 @@ def update(self):
# Always return True to keep looping this method.
return True

def find_spotify_window(self):
spotify_window = []
try:
pipe = subprocess.Popen(['wmctrl', '-lx'], stdout=subprocess.PIPE).stdout
window_list = pipe.read().decode("utf-8").split("\n")
for window in window_list:
if window.find("spotify.Spotify") >= 0:
# current_song = " ".join(window.split()[5:])
spotify_window.append(window)
break
except OSError:
log.error("Please install wmctrl first! Exiting.")
self.stop()

return spotify_window

def find_ad(self):
"""Main loop. Checks for ads and mutes accordingly."""
self.previous_song = self.current_song
Expand Down Expand Up @@ -302,13 +320,33 @@ def unmute_with_delay(self):
self.toggle_mute()
return False

# Audio ads typically have no artist information (via DBus) and/or "/ad/" in their spotify url.
# Video ads have no DBus information whatsoever so they are determined via window title (wmctrl).
def current_song_is_ad(self):
return self.current_song_title and not self.current_song_artist

no_artist = self.current_song_title and not self.current_song_artist
ad_url = "/ad/" in self.dbus.get_spotify_url()
title_mismatch = self.spotify_is_playing() and self.current_song != self.current_song_from_window_title

return no_artist or ad_url or title_mismatch

def update_current_song_info(self):
self.current_song_artist = self.dbus.get_song_artist()
self.current_song_title = self.dbus.get_song_title()
self.current_song = self.current_song_artist + self.song_delimiter + self.current_song_title
self.current_song_from_window_title = self.get_current_song_from_window_title()

def get_current_song_from_window_title(self):
"""Checks if a Spotify window exists and returns the current songname."""
song = ""
spotify_window = self.find_spotify_window()
if spotify_window:
try:
song = " ".join(map(str, spotify_window[0].split()[4:]))
except Exception as e:
log.debug("Could not match spotify pid to sink pid: %s".format(e), exc_info=1)

return song

def block_current(self):
if self.current_song:
Expand Down
7 changes: 7 additions & 0 deletions blockify/data/blockify.desktop
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[Desktop Entry]
Name=Blockify
Comment=Blocks Spotify commercials
Exec=blockify-ui
Icon=data/icon-red-512.png
Type=Application
Categories=AudioVideo
47 changes: 29 additions & 18 deletions blockify/dbusclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,38 +138,49 @@ def seek(self, seconds):
except Exception as e:
log.warn("Cannot Seek: {}".format(e))

def get_song_length(self):
"""Gets the length of current song from metadata (in seconds)."""
length = 0
try:
metadata = self.get_property("Metadata")
length = int(metadata["mpris:length"] / 1000000)
except Exception as e:
log.warn("Cannot get song length: {}".format(e))

return length

def get_art_url(self):
"""Get album cover"""
url = ""
art_url = ""
try:
metadata = self.get_property("Metadata")
url = str(metadata["mpris:artUrl"], "utf-8")
art_url = str(metadata["mpris:artUrl"])
except Exception as e:
log.error("Cannot fetch album cover url: {}".format(e))

return url
return art_url

def get_spotify_url(self):
"""Get spotify url for the track."""
spotify_url = ""
try:
metadata = self.get_property("Metadata")
spotify_url = str(metadata["xesam:url"])
except Exception as e:
log.error("Cannot fetch spotify url: {}".format(e))

return spotify_url

def get_song_status(self):
"""Get current PlaybackStatus (Paused/Playing...)."""
status = ""
try:
status = str(self.get_property("PlaybackStatus"), "utf-8")
status = str(self.get_property("PlaybackStatus"))
except Exception as e:
log.warn("Cannot get PlaybackStatus: {}".format(e))

return status

def get_song_length(self):
"""Gets the length of current song from metadata (in seconds)."""
length = 0
try:
metadata = self.get_property("Metadata")
length = int(metadata["mpris:length"] / 1000000)
except Exception as e:
log.warn("Cannot get song length: {}".format(e))

return length

def get_song(self):
artist = self.get_song_artist()
title = self.get_song_title()
Expand All @@ -182,7 +193,7 @@ def get_song_title(self):
title = ""
try:
metadata = self.get_property("Metadata")
title = str(metadata["xesam:title"], "utf-8")
title = str(metadata["xesam:title"])
except Exception as e:
log.warn("Cannot get song title: {}".format(e))

Expand All @@ -193,7 +204,7 @@ def get_song_album(self):
album = ""
try:
metadata = self.get_property("Metadata")
album = str(metadata["xesam:album"], "utf-8")
album = str(metadata["xesam:album"])
except Exception as e:
log.warn("Cannot get song album: {}".format(e))

Expand All @@ -204,7 +215,7 @@ def get_song_artist(self):
artist = ""
try:
metadata = self.get_property("Metadata")
artist = str(metadata["xesam:artist"][0], "utf-8")
artist = str(metadata["xesam:artist"][0])
except Exception as e:
log.warn("Cannot get song artist: {}".format(e))

Expand Down
2 changes: 1 addition & 1 deletion blockify/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
except ImportError:
log.error("ImportError: Please install docopt to use the CLI.")

VERSION = "3.4.0"
VERSION = "3.5.0"
CONFIG = None
CONFIG_DIR = os.path.expanduser("~/.config/blockify")
CONFIG_FILE = os.path.join(CONFIG_DIR, "blockify.ini")
Expand Down

0 comments on commit 536360f

Please sign in to comment.