diff --git a/.gitmodules b/.gitmodules
index ef5b754..b2e3d3b 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,10 +1,10 @@
[submodule "vim/bundle/vundle"]
path = vim/bundle/vundle
- url = git@github.com:gmarik/vundle.git
+ url = https://github.com/gmarik/vundle.git
ignore = dirty
[submodule "oh-my-zsh"]
path = oh-my-zsh
- url = git@github.com:balkian/oh-my-zsh.git
-[submodule "bin"]
- path = bin
- url = https://github.com/balkian/personal-scripts.git
+ url = https://github.com/balkian/oh-my-zsh.git
+[submodule "i3-wm-scripts"]
+ path = bin/i3-wm-scripts
+ url = https://github.com/yiuin/i3-wm-scripts.git
diff --git a/bin b/bin
deleted file mode 160000
index 405a3e1..0000000
--- a/bin
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 405a3e196bcabeb9a49abddea0ffbea7c44f850a
diff --git a/bin/README b/bin/README
new file mode 100644
index 0000000..ea19512
--- /dev/null
+++ b/bin/README
@@ -0,0 +1,2 @@
+Here I will be posting my personal scripts (either written by me or found on the internet).
+Feel free to use them and email me if you have any comment or question.
diff --git a/bin/bashrc b/bin/bashrc
new file mode 100644
index 0000000..ed83f3d
--- /dev/null
+++ b/bin/bashrc
@@ -0,0 +1,125 @@
+# ~/.bashrc: executed by bash(1) for non-login shells.
+# see /usr/share/doc/bash/examples/startup-files (in the package bash-doc)
+# for examples
+
+# If not running interactively, don't do anything
+[ -z "$PS1" ] && return
+
+# don't put duplicate lines in the history. See bash(1) for more options
+# ... or force ignoredups and ignorespace
+HISTCONTROL=ignoredups:ignorespace
+
+# append to the history file, don't overwrite it
+shopt -s histappend
+
+# for setting history length see HISTSIZE and HISTFILESIZE in bash(1)
+HISTSIZE=1000
+HISTFILESIZE=2000
+
+# check the window size after each command and, if necessary,
+# update the values of LINES and COLUMNS.
+shopt -s checkwinsize
+
+# make less more friendly for non-text input files, see lesspipe(1)
+[ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)"
+
+# set variable identifying the chroot you work in (used in the prompt below)
+if [ -z "$debian_chroot" ] && [ -r /etc/debian_chroot ]; then
+ debian_chroot=$(cat /etc/debian_chroot)
+fi
+
+# set a fancy prompt (non-color, unless we know we "want" color)
+case "$TERM" in
+ xterm-color) color_prompt=yes;;
+ xterm) color_prompt=yes;;
+esac
+
+# uncomment for a colored prompt, if the terminal has the capability; turned
+# off by default to not distract the user: the focus in a terminal window
+# should be on the output of commands, not on the prompt
+force_color_prompt=yes
+
+if [ -n "$force_color_prompt" ]; then
+ if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then
+ # We have color support; assume it's compliant with Ecma-48
+ # (ISO/IEC-6429). (Lack of such support is extremely rare, and such
+ # a case would tend to support setf rather than setaf.)
+ color_prompt=yes
+ else
+ color_prompt=
+ fi
+fi
+
+if [ "$color_prompt" = yes ]; then
+ export PROMPT_COMMAND='PS1="\\[\033[1;\`if [[ \$? = "0" ]]; then echo "32m\\]"; else echo "31m\\]"; fi\`[\!]\\[\033[0m\]\u@\h \W: "'
+else
+ PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '
+fi
+
+unset color_prompt force_color_prompt
+
+# If this is an xterm set the title to user@host:dir
+case "$TERM" in
+xterm*|rxvt*)
+ PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@\h: \w\a\]$PS1"
+ ;;
+*)
+ ;;
+esac
+
+# enable color support of ls and also add handy aliases
+if [ -x /usr/bin/dircolors ]; then
+ test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)"
+ alias ls='ls --color=auto'
+ #alias dir='dir --color=auto'
+ #alias vdir='vdir --color=auto'
+
+ alias grep='grep --color=auto'
+ alias fgrep='fgrep --color=auto'
+ alias egrep='egrep --color=auto'
+fi
+
+# some more ls aliases
+alias ll='ls -alhF'
+alias la='ls -A'
+alias l='ls -CF'
+
+# Add an "alert" alias for long running commands. Use like so:
+# sleep 10; alert
+alias alert='notify-send --urgency=low -i "$([ $? = 0 ] && echo terminal || echo error)" "$(history|tail -n1|sed -e '\''s/^\s*[0-9]\+\s*//;s/[;&|]\s*alert$//'\'')"'
+
+# Alias definitions.
+# You may want to put all your additions into a separate file like
+# ~/.bash_aliases, instead of adding them here directly.
+# See /usr/share/doc/bash-doc/examples in the bash-doc package.
+
+if [ -f ~/.bash_aliases ]; then
+ . ~/.bash_aliases
+fi
+
+# enable programmable completion features (you don't need to enable
+# this, if it's already enabled in /etc/bash.bashrc and /etc/profile
+# sources /etc/bash.bashrc).
+if [ -f /etc/bash_completion ] && ! shopt -oq posix; then
+ . /etc/bash_completion
+fi
+export CDPATH=~:/media/Datos:/home/balkian
+
+if [ "$TERM" = "linux" ]; then
+ alias logout="clear && logout && exit"
+else
+ alias logout="exit 0"
+fi
+export PYTHONSTARTUP=/home/$USER/.pythonrc
+export PATH=$PATH:/var/lib/gems/1.8/bin
+
+setWindowTitle() {
+ echo -ne "\e]2;$*\a"
+}
+updateWindowTitle() {
+ setWindowTitle "${HOSTNAME%%.*}:${PWD/$HOME/~}"
+}
+PROMPT_COMMAND=$PROMPT_COMMAND updateWindowTitle
+
+function findHere() { ex=$1; shift; grep -R $* $ex .;}
+
diff --git a/bin/change-xfce4-alt-tab.sh b/bin/change-xfce4-alt-tab.sh
new file mode 100755
index 0000000..baa54e6
--- /dev/null
+++ b/bin/change-xfce4-alt-tab.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+# @balkian
+# Changes the behaviour of the window cycling in xfce4
+#
+# Extracted from: http://askubuntu.com/questions/136052/cycle-windows-over-all-workspaces-in-xfce-while-being-able-to-cycle-over-one-wo
+#
+
+OLD_VALUE=$(xfconf-query -c xfwm4 -p /general/cycle_workspaces)
+
+if [ $OLD_VALUE == "true" ]; then
+ echo 'will now disable workspace cycling'
+ NEW_VALUE="false"
+fi
+
+if [ $OLD_VALUE == "false" ]; then
+ echo 'will now turn on workspace cycling'
+ NEW_VALUE="true"
+fi
+
+xfconf-query -c xfwm4 -p /general/cycle_workspaces -s $NEW_VALUE
diff --git a/bin/changedisplay.sh b/bin/changedisplay.sh
new file mode 100755
index 0000000..26ed7dd
--- /dev/null
+++ b/bin/changedisplay.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+# Author: Andrew Martin
+# Credit: http://ubuntuforums.org/showthread.php?t=1309247
+echo "Enter the primary display from the following:" # prompt for the display
+xrandr --prop | grep "[^dis]connected" | cut --delimiter=" " -f1 # query connected monitors
+
+read choice # read the users's choice of monitor
+
+xrandr --output $choice --primary # set the primary monitor
+
+
+
diff --git a/bin/cpcuevana.sh b/bin/cpcuevana.sh
new file mode 100755
index 0000000..83b509c
--- /dev/null
+++ b/bin/cpcuevana.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+# Script to copy Cuevana files to your home directory.
+# Tested only with one cuevana instance at a time.
+# @author: balkian
+
+destfile="$HOME/VideoCuevana$$"
+info=($(lsof -c /npview/i | grep -i /tmp/flash | awk '{print $2; print $9}'))
+dir="/proc/${info[0]}/fd"
+file=${info[1]}
+#echo "pid" $pid
+#echo "file" $file
+fdn=$(ls -l $dir | grep $file | awk '{print $8}')
+cp $dir/$fdn $destfile && echo "Video copied successfully to $destfile." && echo "Enjoy"
diff --git a/bin/dmenu.sh b/bin/dmenu.sh
new file mode 100755
index 0000000..eb61099
--- /dev/null
+++ b/bin/dmenu.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+#http://unix.stackexchange.com/questions/50539/inconsistent-behaviour-of-wmctrl-i-a-win
+
+# source dmenu config file if it exists
+if [ -f $HOME/.dmenurc ]; then
+ . $HOME/.dmenurc
+else
+ DMENU='dmenu -i'
+fi
+
+# get list of all windows, and their count
+wmctrl_output=$(wmctrl -lx)
+win_count=$(echo "$wmctrl_output" | wc -l)
+# get rid of the hostname and the number in the 2nd column
+hostname=$(uname -n)
+win_list=$(echo "$wmctrl_output" | \
+ sed -r -e 's/[^@]'$hostname'//' | \
+ sed -r -e 's/ [0-9][0-9]? / /')
+
+# select a window ($target) and extract its id ($target_id)
+target=$(echo "$win_list" | $DMENU -l $win_count -p "Switch to: ")
+target_id=$(echo "$target" | cut -d' ' -f1)
+
+# switch to target window
+cmd="wmctrl -i -a \"$target_id\""
+eval "$cmd"
+
diff --git a/bin/dual-monitor b/bin/dual-monitor
new file mode 120000
index 0000000..0029e18
--- /dev/null
+++ b/bin/dual-monitor
@@ -0,0 +1 @@
+dual-monitor.sputnik
\ No newline at end of file
diff --git a/bin/dual-monitor.sputnik b/bin/dual-monitor.sputnik
new file mode 100755
index 0000000..9c22065
--- /dev/null
+++ b/bin/dual-monitor.sputnik
@@ -0,0 +1 @@
+ xrandr --auto --output eDP1 --primary --right-of DP1
diff --git a/bin/elecciones.sh b/bin/elecciones.sh
new file mode 100755
index 0000000..f5f1501
--- /dev/null
+++ b/bin/elecciones.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+cd /tmp
+wget http://www3.upm.es/elecciones/resultados/#1 -O resultados
+
+
diff --git a/bin/flasher.sh b/bin/flasher.sh
new file mode 100755
index 0000000..17ae561
--- /dev/null
+++ b/bin/flasher.sh
@@ -0,0 +1,41 @@
+#!/bin/bash
+# Script to copy/link Cuevana files to your home directory or play them with your default video player.
+# Tested only with one cuevana instance at a time.
+# The video can be played, but it's deleted when the flash player is closed (unless you copied it).
+# @author: balkian
+
+if [ -z "$1" ]; then
+ PLAYER=parole;
+else
+ PLAYER=$1;
+fi
+
+destfile="$HOME/VideoCuevana$$"
+# PID1=$(pgrep -f flash)
+# info=($(lsof -p $PID1 | grep -i /tmp/flash | awk '{print $2; print $9}'))
+# dir="/proc/${info[0]}/fd"
+# file=${info[1]}
+# #echo "pid" $pid
+# #echo "file" $file
+# fdn=$(ls -l $dir | grep $file | awk '{print $9}')
+files=$(sudo lsof 2>/dev/null | grep "Pepper Data" | awk '{gsub(/[a-z]/,"",$4);print "/proc/"$2"/fd/"$4}' | uniq -u)
+
+echo "Files:" $files
+for file in $files; do
+
+ sel=$(zenity --list --radiolist --text "Select action for: $file" --column "pick" --column "Option" TRUE "play" FALSE "copy" FALSE "link")
+ case $sel in
+ play)
+ sudo $PLAYER $file
+ ;;
+ link)
+ sudo ln -s $file $destfile && echo "Video linked successfully to $destfile." && echo "Enjoy"
+ sudo chown $USER:$USER $destfile
+ ;;
+ copy)
+ sudo cp $file $destfile && echo "Video copied successfully to $destfile." && echo "Enjoy"
+ sudo chown $USER:$USER $destfile
+ ;;
+ esac
+
+done;
diff --git a/bin/gnome-terminal b/bin/gnome-terminal
new file mode 100755
index 0000000..7bd0cde
--- /dev/null
+++ b/bin/gnome-terminal
@@ -0,0 +1,2 @@
+#!/bin/bash
+one-window 'gnome-terminal' 'xdotool windowfocus $WID & xdotool key ctrl+shift+t'
diff --git a/bin/goear.sh b/bin/goear.sh
new file mode 100755
index 0000000..520a3d0
--- /dev/null
+++ b/bin/goear.sh
@@ -0,0 +1,70 @@
+#bin/bash
+resultado=/tmp/resultado
+canciones=/tmp/canciones
+enlaces=/tmp/enlaces
+titulos=/tmp/titulos
+#Pedimos al usuario el titulo de la canción.
+echo "Introduce el título de la canción o del artista:"
+read TITULO
+
+if [ ! -d "goear" ]
+then
+ mkdir goear
+fi
+#Descargamos el PHP correspondiente al título.
+wget http://goear.com/search.php?q="$TITULO" -O $resultado
+
+#La línea 130 contiene todos los enlaces a goear... de risa pero bueno.
+head -130 $resultado | tail -1 > $canciones
+
+#Mediante ER, obtenemos una lista de canciones y una lista de enlaces.
+egrep -o 'listen/......./[^"]*' $canciones > $enlaces
+egrep -o '"Escuchar[^"]*' $canciones > $titulos
+
+#Mostramos al usuario los que ha encontrado en la primera página.
+Linea=1
+cat $titulos | while read line;
+ do {
+ echo $Linea: ${line:9}
+ let 'Linea += 1'
+ }
+ done
+
+#Si no encuentra nada, sale.
+CONDICION=`wc -l $titulos | awk '{print $1}'`
+if [ $CONDICION == 0 ]; then
+ echo "No hay resultados. Prueba buscando otra cosa."
+ rm $resultado $canciones $enlaces $titulos
+ exit
+fi
+
+#Leemos qué canción quiere el usuario bajarse.
+echo "¿Cuál te quieres bajar? Indica el número:"
+read NUMERO
+
+#Concatenamos http://www.goear.com con el contenido de aBajar.txt.
+#PD: Alguien sabe hacerlo de manera más sencilla?
+GOEAR=http://www.goear.com/
+aBajar=`head -$NUMERO $enlaces | tail -1`
+for LISTEN in $aBajar
+do
+ ENLACE=${GOEAR}${LISTEN}
+done
+echo $ENLACE
+
+#A partir de aquí el script no es mío, pero es muy sencillo de leer.
+fileid=`echo $ENLACE | cut -d '/' -f 5`
+xmlurl="http://www.goear.com/tracker758.php?f="$fileid
+infoline=`wget -qO- $xmlurl | grep ".mp3"`
+mp3url=`echo $infoline | cut -d '"' -f6`
+artist=`echo $infoline | cut -d '"' -f10`
+title=`echo $infoline | cut -d '"' -f12`
+filename=goear/"$artist-$title.mp3"
+wget $mp3url -O "$filename"
+rm $resultado $canciones $enlaces $titulos
+
+echo "¿Quieres reproducirla?[Y/n]"
+read RES
+if [ -z "$RES" -o "$RES" = "Y" -o "$RES" = "y" ];then
+ mplayer "$filename";
+fi;
diff --git a/bin/i3-one-instance b/bin/i3-one-instance
new file mode 100755
index 0000000..e23aa77
--- /dev/null
+++ b/bin/i3-one-instance
@@ -0,0 +1,5 @@
+#!/bin/sh
+python2 $HOME/.bin/i3-wm-scripts/nextmatch.py $*
+if [ $? -gt 0 ]; then
+ $* & sleep 0.1 && python2 $HOME/.bin/i3-wm-scripts/nextmatch.py $*
+fi
diff --git a/bin/i3-one-tmux b/bin/i3-one-tmux
new file mode 100755
index 0000000..1b3f9ce
--- /dev/null
+++ b/bin/i3-one-tmux
@@ -0,0 +1,11 @@
+#!/bin/zsh
+python2 $HOME/.bin/i3-wm-scripts/nextmatch.py urxvt
+if [ $? -gt 0 ]; then
+ urxvt & sleep 0.1 && python2 $HOME/.bin/i3-wm-scripts/nextmatch.py urxvt
+fi
+if [ "$#" -gt 0 ]; then
+ eval "tmux neww"
+ sleep 0.2
+ xdotool type "$*"
+ xdotool key Return
+fi
diff --git a/bin/i3-wm-scripts b/bin/i3-wm-scripts
new file mode 160000
index 0000000..4773352
--- /dev/null
+++ b/bin/i3-wm-scripts
@@ -0,0 +1 @@
+Subproject commit 477335215dd1fff4f23b82afbb92e7ad1bd4552e
diff --git a/bin/icon_spotify.png b/bin/icon_spotify.png
new file mode 100644
index 0000000..a640f7d
Binary files /dev/null and b/bin/icon_spotify.png differ
diff --git a/bin/one-instance b/bin/one-instance
new file mode 100755
index 0000000..79206ed
--- /dev/null
+++ b/bin/one-instance
@@ -0,0 +1,24 @@
+#!/bin/bash
+if [ "x$1" != "x" ]
+then
+ pgrep -l -u "$USER" -f "$1" | egrep -v "/bin/bash|/bin/sh" | grep -vq "$$"
+ if [ $? == "0" ]
+ then
+ WID=`xdotool search --class $1 | head -1`
+ wmctrl -i -a $WID
+ if [ "x$2" != "x" ]
+ then
+ eval "$2"
+ fi
+ else
+ if [ "x$3" == "x" ]
+ then
+ /usr/bin/$1
+ else
+ eval "$3"
+ fi
+ fi
+else
+ echo "Usage: one-instance program-name [extra action]"
+fi
+
diff --git a/bin/one-instance (copy) b/bin/one-instance (copy)
new file mode 100755
index 0000000..d0f7e95
--- /dev/null
+++ b/bin/one-instance (copy)
@@ -0,0 +1,27 @@
+#!/bin/bash
+if [ "x$1" != "x" ]
+then
+ pgrep -fx $1 1> /dev/null
+ if [ $? == "0" ]
+ then
+ echo "running"
+ ps aux | grep terminator > /home/balkian/LOG
+ WID=`xdotool search --class $1 | head -1`
+ wmctrl -i -a $WID
+ if [ "x$2" != "x" ]
+ then
+ eval "$2"
+ fi
+ else
+ if [ "x$3" == "x" ]
+ then
+ echo "not running"
+ /usr/bin/$1
+ else
+ eval "$3"
+ fi
+ fi
+else
+ echo "Usage: one-instance program-name [extra action]"
+fi
+
diff --git a/bin/one-terminator.sh b/bin/one-terminator.sh
new file mode 100755
index 0000000..3cd18b4
--- /dev/null
+++ b/bin/one-terminator.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+pgrep -u "$USER" gnome-terminal | grep -qv "$$"
+if [ "$?" == "0" ]; then
+ WID=`xdotool search --class "terminator" | head -1`
+ xdotool windowfocus $WID
+ xdotool key ctrl+shift+t
+ #wmctrl -i -a $WID
+else
+ /usr/bin/gnome-terminal
+fi
diff --git a/bin/one-tmux b/bin/one-tmux
new file mode 100755
index 0000000..7b063c3
--- /dev/null
+++ b/bin/one-tmux
@@ -0,0 +1,27 @@
+#!/bin/bash
+TERMINAL_EMULATOR='urxvt'
+tab=1
+pgrep -l -u "$USER" -f "$TERMINAL_EMULATOR" | egrep -v "/bin/zsh|/bin/bash|/bin/sh" | grep -vq "$$"
+if [ $? != "0" ]
+then
+ /usr/bin/$TERMINAL_EMULATOR &
+ sleep 1
+ tab=0
+fi
+WID=`xdotool search --class $TERMINAL_EMULATOR | tail -1`
+wmctrl -i -a $WID
+if [ "x$1" != "x" ]
+then
+ if [ "x$1" == "xhelp" ]
+ then
+ echo "Usage: $0 [command-in-new-window]"
+ else
+ if [ $tab == 1 ]
+ then
+ eval "tmux neww"
+ sleep 0.2
+ fi
+ xdotool type "$*"
+ xdotool key Return
+ fi
+fi
diff --git a/bin/play-europa.sh b/bin/play-europa.sh
new file mode 100755
index 0000000..039d668
--- /dev/null
+++ b/bin/play-europa.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+rtmpdump --live --quiet --buffer 3000 -r rtmp://antena3fms35geobloqueolivefs.fplive.net/antena3fms35geobloqueolive-live/ --playpath stream-europafm | mplayer -vo null -idle -
diff --git a/bin/play-hitfm.sh b/bin/play-hitfm.sh
new file mode 100755
index 0000000..dcade42
--- /dev/null
+++ b/bin/play-hitfm.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+mplayer -vo null -idle http://ice.5.c3.audiovideoweb.com:8000/4c3ice5500
diff --git a/bin/play-kiss.sh b/bin/play-kiss.sh
new file mode 100755
index 0000000..b9a446b
--- /dev/null
+++ b/bin/play-kiss.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+rtmpdump --live --quiet --buffer 3000 -r rtmp://kissfm.es.flash3.glb.ipercast.net/kissfm.es-live --playpath aac.stream | mplayer -vo null -idle -
diff --git a/bin/play-m80.sh b/bin/play-m80.sh
new file mode 100755
index 0000000..f4cc511
--- /dev/null
+++ b/bin/play-m80.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+curl -s http://4633.live.streamtheworld.com/M80RADIOAAC | mplayer -vo null -idle -
diff --git a/bin/playlist-radios.xml b/bin/playlist-radios.xml
new file mode 100644
index 0000000..c8cf0e6
--- /dev/null
+++ b/bin/playlist-radios.xml
@@ -0,0 +1,36 @@
+
+
+ Playlist
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bin/replace-accented.sh b/bin/replace-accented.sh
new file mode 100755
index 0000000..95af1f5
--- /dev/null
+++ b/bin/replace-accented.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+# Note: input should end with a newline
+
+LINES=''
+while read LINE; do
+ echo $LINE | sed 's/á/a/g' | sed 's/Á/A/g' | sed 's/é/e/g' | sed 's/É/E/g' | sed 's/í/i/g' | sed 's/Í/I/g' | sed 's/ó/o/g' | sed 's/Ó/O/g' | sed 's/ú/u/g' | sed 's/Ú/U/g' | sed 's/ñ/n/g' | sed 's/Ñ/N/g' | sed 's/ü/u/g' | sed 's/Ü/U/g'
+done
+
+exit 0
diff --git a/bin/single-monitor b/bin/single-monitor
new file mode 120000
index 0000000..0b0e919
--- /dev/null
+++ b/bin/single-monitor
@@ -0,0 +1 @@
+single-monitor.sputnik
\ No newline at end of file
diff --git a/bin/single-monitor.sputnik b/bin/single-monitor.sputnik
new file mode 100755
index 0000000..e24550b
--- /dev/null
+++ b/bin/single-monitor.sputnik
@@ -0,0 +1 @@
+ xrandr --auto --output eDP1 --primary --output DP1 --off
diff --git a/bin/spotify-notify.py b/bin/spotify-notify.py
new file mode 100644
index 0000000..f3e299a
--- /dev/null
+++ b/bin/spotify-notify.py
@@ -0,0 +1,417 @@
+#!/usr/bin/python
+
+# Spotify-notify
+#
+# v0.6d (28th aug 11)
+# by JonW (jon.neverwinter@gmail.com)
+# patched 20110907 by Jansen Price (sumpygump@gmail.com)
+# patched 20120729 by Jansen Price (sumpygump@gmail.com) and brandl.matthaeus
+#
+# Original by SveinT (sveint@gmail.com)
+# up to v0.5.2 (27th jan 11)
+
+
+import dbus
+
+import gobject, gtk, os, tempfile, sys, time, re, indicate, urllib2
+from optparse import OptionParser
+from subprocess import *
+
+# The url to use when fetching spotify track information.
+SPOTIFY_OPEN_URL = "http://open.spotify.com/track/"
+
+# The path to this application's directory.
+APPLICATION_DIR = sys.path[0] + "/"
+
+# The file path to spotify. If empty, it will try to auto detect.
+SPOTIFY_PROCESS_NAME = 'spotify'
+
+# How often to check if spotify has been closed (in milliseconds).
+SPOTIFY_CLOSED_CHECK = 20000
+
+class SpotifyNotify():
+
+ spotifyPath = ''
+
+ tryToReconnect = False
+
+ tmpfile = False
+
+ def __init__(self, debugger):
+ self.debug = debugger
+ self.spotifyservice = False
+
+ self.prev = 0
+ self.new = False
+ self.prevMeta = {}
+ self.notifyid = 0
+
+ self.connect()
+
+ def __del__(self):
+ if SpotifyNotify and SpotifyNotify.tmpfile:
+ SpotifyNotify.tmpfile.close()
+
+ def connect(self):
+ self.debug.out("Connecting to spotify.")
+ self.bus = dbus.Bus(dbus.Bus.TYPE_SESSION)
+
+ try:
+ self.spotifyservice = self.bus.get_object(
+ 'com.spotify.qt',
+ '/org/mpris/MediaPlayer2'
+ )
+ SpotifyNotify.tryToReconnect = False
+ except Exception, e:
+ self.spotifyservice = False
+ self.debug.out("Failed to connect.")
+ self.debug.out(e)
+
+ def executeCommand(self, key):
+ if not key:
+ return
+
+ self.connect()
+ self.debug.out("Running command: {0}".format(key))
+ self.cmd = self.spotifyservice.get_dbus_method(key, 'org.mpris.MediaPlayer2.Player')
+ self.cmd()
+
+ def pollChange(self):
+ try:
+ self.spotifyservice = self.bus.get_object('com.spotify.qt', '/')
+ self.cmd = self.spotifyservice.get_dbus_method(
+ 'GetMetadata',
+ 'org.freedesktop.MediaPlayer2'
+ )
+ self.new = self.cmd()
+ except Exception, e:
+ self.debug.out('Spotify service not connected.')
+ SpotifyNotify.tryToReconnect = True
+
+ if (self.prev != self.new):
+ self.trackChange(self.new)
+ self.prev = self.new
+
+ return 1
+
+ def trackChange(self, *trackChange):
+ if not trackChange[0]:
+ return
+
+ self.prev = trackChange[0]
+
+ trackInfo = {}
+ trackMap = {
+ 'artist' : 'xesam:artist',
+ 'album' : 'xesam:album',
+ 'title' : 'xesam:title',
+ 'year' : 'xesam:contentCreated',
+ 'trackhash' : 'mpris:trackid',
+ 'arturl' : 'mpris:artUrl'
+ }
+
+ # Fetch the track information for the notification window.
+ for key in trackMap:
+ if not trackMap[key] in trackChange[0]:
+ continue
+ piece = trackChange[0][trackMap[key]]
+ if key == 'year':
+ piece = str(piece[:4])
+ elif isinstance(piece, list):
+ piece = ", ".join(piece)
+
+ if not isinstance(piece, str):
+ piece = str(piece)
+
+ trackInfo[key] = piece.encode('utf-8')
+
+ if not self.prevMeta\
+ or not SpotifyNotify.tmpfile\
+ or 'iconfilename' not in self.prevMeta\
+ or self.prevMeta['artist'] != trackInfo['artist']\
+ or self.prevMeta['album'] != trackInfo['album']:
+ trackInfo['iconfilename'] = self.retrieveCoverImage(trackInfo)
+
+ cover_image = ''
+
+ if 'iconfilename' in trackInfo:
+ cover_image = trackInfo['iconfilename']
+ elif 'iconfilename' in self.prevMeta:
+ cover_image = self.prevMeta['iconfilename']
+ trackInfo['iconfilename'] = cover_image
+
+ if cover_image == '':
+ cover_image = APPLICATION_DIR + 'icon_spotify.png'
+
+ self.prevMeta = trackInfo
+
+ # Connect to notification interface on DBUS.
+ self.notifyservice = self.bus.get_object(
+ 'org.freedesktop.Notifications',
+ '/org/freedesktop/Notifications'
+ )
+ self.notifyservice = dbus.Interface(
+ self.notifyservice,
+ "org.freedesktop.Notifications"
+ )
+ notifyText = "{0}\n{1}".format(
+ trackInfo['title'],
+ trackInfo['album']
+ )
+ if len(trackInfo['year']) > 0:
+ notifyText += " ({0})".format(trackInfo['year'])
+
+ # Send track change information to stdout
+ print "Changing track : {0} | {1} | {2} ({3})".format(
+ trackInfo['artist'],
+ trackInfo['title'],
+ trackInfo['album'],
+ trackInfo['year']
+ )
+
+ # The second param is the replace id, so get the notify id back,
+ # store it, and send it as the replacement on the next call.
+ self.notifyid = self.notifyservice.Notify(
+ "Spotify-notify",
+ self.notifyid,
+ cover_image,
+ trackInfo['artist'],
+ notifyText,
+ [],
+ {},
+ 2000
+ )
+
+ def retrieveCoverImage(self, trackInfo):
+ if 'arturl' in trackInfo:
+ self.debug.out("Simply retrieving image from {0}".format(trackInfo['arturl']))
+ iconfilename = self.fetchCoverImage(trackInfo['arturl'])
+ else:
+ #if (trackInfo['trackhash'][0:14] == 'spotify:local:'):
+ # self.debug.out("Track is a local file. No art available.")
+ # return ''
+
+ self.debug.out("Attempting to fetch image from spotify")
+ iconfilename = self.fetchCoverImageSpotify(
+ trackInfo['artist'],
+ trackInfo['album'],
+ trackInfo['trackhash']
+ )
+ return iconfilename
+
+ def fetchCoverImageSpotify(self, artist, album, trackhash):
+ try:
+ trackid = trackhash.split(":")[2]
+ url = SPOTIFY_OPEN_URL + trackid
+ tracksite = urllib2.urlopen(url).read()
+
+ # Attempt to get the image url from the open graph image meta tag.
+ imageurl = False
+ metaMatch = re.search(
+ ']*property\s*=\s*["\']og:image["\'][^\>]*/?>',
+ tracksite
+ )
+ if metaMatch:
+ contentMatch = re.search(
+ 'content\s*=\s*["\']([^\"\']*)["\']',
+ metaMatch.group(0)
+ )
+ if contentMatch:
+ imageurl = contentMatch.group(1)
+
+ if not imageurl:
+ self.debug.out("No cover available.")
+ raise()
+
+ return self.fetchCoverImage(imageurl)
+ except Exception, e:
+ self.debug.out("Couldn't fetch cover image.")
+ self.debug.out(e)
+
+ return ''
+
+ def fetchCoverImage(self, url):
+ # Close the temporary image file, we are going to make a new one.
+ if SpotifyNotify.tmpfile:
+ SpotifyNotify.tmpfile.close()
+ SpotifyNotify.tmpfile = False
+
+ try:
+ SpotifyNotify.tmpfile = tempfile.NamedTemporaryFile()
+ tmpfilename = SpotifyNotify.tmpfile.name
+ self.debug.out("Album art tmp filepath: {0}".format(tmpfilename))
+
+ coverfile = urllib2.urlopen(url)
+ SpotifyNotify.tmpfile.write(coverfile.read())
+ SpotifyNotify.tmpfile.flush()
+ return tmpfilename
+ except Exception, e:
+ self.debug.out("Couldn't fetch cover image.")
+ self.debug.out(e)
+
+ return ''
+
+ @staticmethod
+ def startSpotify(Debug):
+ if not SpotifyNotify.spotifyPath:
+ Debug.out("No spotify process identifier found.")
+ return
+
+ ident = SpotifyNotify.spotifyPath
+ Debug.out("Looking for spotify as: {0}".format(ident))
+
+ procs = SpotifyNotify.checkForProcess(
+ 'ps x | grep "{0}" | grep -v grep'.format(ident),
+ Debug
+ )
+ if len(procs):
+ Debug.out("Spotify process found as: {0}".format(" ".join(procs[0])))
+ return
+
+ Debug.out("Starting new Spotify now.")
+
+ FNULL = open('/dev/null', 'w')
+ spid = Popen([ident], stdout=FNULL, stderr=FNULL).pid
+ if spid:
+ Debug.out("Spotify started, pid: {0}.".format(spid))
+ else:
+ Debug.out("Spotify could not be started.")
+
+ @staticmethod
+ def checkForClosedSpotify(SN, Debug):
+ if not SpotifyNotify.spotifyPath:
+ Debug.out("No spotify process identifier found.")
+ return False
+
+ ident = SpotifyNotify.spotifyPath
+ Debug.out("Looking for spotify as: {0}".format(ident))
+
+ procs = SpotifyNotify.checkForProcess(
+ 'ps x | grep "{0}" | grep -v grep'.format(ident),
+ Debug
+ )
+ if len(procs):
+ Debug.out("Spotify process found as: {0}".format(" ".join(procs[0])))
+
+ if (SpotifyNotify.tryToReconnect):
+ SN.connect()
+
+ return True
+
+ if SpotifyNotify.tmpfile:
+ SpotifyNotify.tmpfile.close()
+
+ Debug.out("Spotify has been closed, therefore I die.")
+ exit(0)
+
+ @staticmethod
+ def preventDuplicate(Debug):
+ mypid = os.getpid()
+ Debug.out("My pid: {0}".format(mypid))
+
+ proc = SpotifyNotify.checkForProcess('ps -p {0}'.format(mypid), Debug)
+ if not proc[0][3]:
+ return
+
+ process = proc[0][3]
+ search = 'ps -C {0}'.format(process)
+
+ Debug.out("Looking for other processes named: {0}".format(process).strip())
+
+ if process == 'python':
+ if not sys.argv[0]:
+ Debug.out("Process started using python, cannot determine script name.")
+ return
+
+ search = 'ps ax | grep "python {0}" | grep -v grep'.format(sys.argv[0])
+
+ for line in SpotifyNotify.checkForProcess(search, Debug):
+ if int(line[0]) != mypid:
+ print("This program was already running.")
+ Debug.out("I am a duplicate. I shall end myself. ({0})".format(" ".join(line)))
+ exit(0)
+
+ @staticmethod
+ def checkForProcess(proc, Debug):
+ output = []
+
+ for line in Popen(proc, shell=True, stdout=PIPE).stdout:
+ fields = line.split()
+ if not fields[0].isdigit():
+ continue
+
+ output.append(fields)
+
+ return output
+
+class DebugMe():
+ def __init__(self, toggle):
+ if toggle:
+ self.output = True
+ else:
+ self.output = False
+
+ def out(self, msg):
+ if not self.output:
+ return
+
+ print(">> {0}".format(msg))
+
+if __name__ == "__main__":
+ parser = OptionParser()
+ parser.add_option(
+ '-a',
+ '--action',
+ dest = 'action',
+ default = None,
+ type = 'choice',
+ choices = ['playPause', 'play', 'pause', 'next', 'previous'],
+ help = 'Music player actions (playPause/play/pause/next/previous).'
+ )
+ parser.add_option(
+ '-n',
+ '--skip_notify',
+ dest = 'skipNotify',
+ action = 'store_true',
+ default = False,
+ help = 'Song change notifications will be turned off.'
+ )
+ parser.add_option(
+ '-d',
+ '--debug',
+ dest = 'debug',
+ action = 'store_true',
+ default = False,
+ help = 'Debug messages will be displayed.'
+ )
+
+ (options, args) = parser.parse_args()
+
+ Debug = DebugMe(options.debug)
+ print("Spotify-notify v0.6")
+
+ if SPOTIFY_PROCESS_NAME:
+ SpotifyNotify.spotifyPath = SPOTIFY_PROCESS_NAME
+ else:
+ print "Spotify is not running"
+ sys.exit(0)
+
+ SN = SpotifyNotify(Debug)
+
+ if options.action:
+ action = options.action
+ action = action[0:1].upper() + action[1:]
+ SN.executeCommand(action)
+
+ SpotifyNotify.preventDuplicate(Debug)
+
+ try:
+ indicateserver = indicate.indicate_server_ref_default()
+ indicateserver.set_type("music.spotify")
+ indicateserver.set_desktop_file("/usr/share/applications/spotify.desktop")
+ indicateserver.show()
+ except:
+ pass
+ SN.pollChange()
+ print "Done"
+
diff --git a/bin/spotify-remote.py b/bin/spotify-remote.py
new file mode 100755
index 0000000..a85d44f
--- /dev/null
+++ b/bin/spotify-remote.py
@@ -0,0 +1,420 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+'''
+ Spoify-remote
+ Modified version of Spotify-notify that runs once when invoked instead of looping.
+ v0.6d (28th aug 11)
+ by JonW (jon.neverwinter@gmail.com)
+ patched 20110907 by Jansen Price (sumpygump@gmail.com)
+ patched 20120729 by Jansen Price (sumpygump@gmail.com) and brandl.matthaeus
+ modified 20130405 by Fernando Sánchez (balkian@gmail.com)
+
+ Original by SveinT (sveint@gmail.com)
+ up to v0.5.2 (27th jan 11)
+'''
+
+import dbus
+
+import gobject, gtk, os, tempfile, sys, time, re, indicate, urllib2
+from optparse import OptionParser
+from subprocess import *
+
+# The url to use when fetching spotify track information.
+SPOTIFY_OPEN_URL = "http://open.spotify.com/track/"
+
+# The path to this application's directory.
+APPLICATION_DIR = sys.path[0] + "/"
+
+# The file path to spotify. If empty, it will try to auto detect.
+SPOTIFY_PROCESS_NAME = 'spotify'
+
+# How often to check if spotify has been closed (in milliseconds).
+SPOTIFY_CLOSED_CHECK = 20000
+
+class SpotifyNotify():
+
+ spotifyPath = ''
+
+ tryToReconnect = False
+
+ tmpfile = False
+
+ def __init__(self, debugger):
+ self.debug = debugger
+ self.spotifyservice = False
+
+ self.prev = 0
+ self.new = False
+ self.prevMeta = {}
+ self.notifyid = 0
+
+ self.connect()
+
+ def __del__(self):
+ if SpotifyNotify and SpotifyNotify.tmpfile:
+ SpotifyNotify.tmpfile.close()
+
+ def connect(self):
+ self.debug.out("Connecting to spotify.")
+ self.bus = dbus.Bus(dbus.Bus.TYPE_SESSION)
+
+ try:
+ self.spotifyservice = self.bus.get_object(
+ 'com.spotify.qt',
+ '/org/mpris/MediaPlayer2'
+ )
+ SpotifyNotify.tryToReconnect = False
+ except Exception, e:
+ self.spotifyservice = False
+ self.debug.out("Failed to connect.")
+ self.debug.out(e)
+
+ def executeCommand(self, key):
+ if not key:
+ return
+
+ self.connect()
+ self.debug.out("Running command: {0}".format(key))
+ self.cmd = self.spotifyservice.get_dbus_method(key, 'org.mpris.MediaPlayer2.Player')
+ self.cmd()
+
+ def pollChange(self):
+ try:
+ self.spotifyservice = self.bus.get_object('com.spotify.qt', '/')
+ self.cmd = self.spotifyservice.get_dbus_method(
+ 'GetMetadata',
+ 'org.freedesktop.MediaPlayer2'
+ )
+ self.new = self.cmd()
+ except Exception, e:
+ self.debug.out('Spotify service not connected.')
+ SpotifyNotify.tryToReconnect = True
+
+ if (self.prev != self.new):
+ self.trackChange(self.new)
+ self.prev = self.new
+
+ return 1
+
+ def trackChange(self, *trackChange):
+ if not trackChange[0]:
+ return
+
+ self.prev = trackChange[0]
+
+ trackInfo = {}
+ trackMap = {
+ 'artist' : 'xesam:artist',
+ 'album' : 'xesam:album',
+ 'title' : 'xesam:title',
+ 'year' : 'xesam:contentCreated',
+ 'trackhash' : 'mpris:trackid',
+ 'arturl' : 'mpris:artUrl'
+ }
+
+ # Fetch the track information for the notification window.
+ for key in trackMap:
+ if not trackMap[key] in trackChange[0]:
+ continue
+ piece = trackChange[0][trackMap[key]]
+ if key == 'year':
+ piece = str(piece[:4])
+ elif isinstance(piece, list):
+ piece = ", ".join(piece)
+
+ if not isinstance(piece, str):
+ piece = str(piece)
+
+ trackInfo[key] = piece.encode('utf-8')
+
+ if not self.prevMeta\
+ or not SpotifyNotify.tmpfile\
+ or 'iconfilename' not in self.prevMeta\
+ or self.prevMeta['artist'] != trackInfo['artist']\
+ or self.prevMeta['album'] != trackInfo['album']:
+ trackInfo['iconfilename'] = self.retrieveCoverImage(trackInfo)
+
+ cover_image = ''
+
+ if 'iconfilename' in trackInfo:
+ cover_image = trackInfo['iconfilename']
+ elif 'iconfilename' in self.prevMeta:
+ cover_image = self.prevMeta['iconfilename']
+ trackInfo['iconfilename'] = cover_image
+
+ if cover_image == '':
+ cover_image = APPLICATION_DIR + 'icon_spotify.png'
+
+ self.prevMeta = trackInfo
+
+ # Connect to notification interface on DBUS.
+ self.notifyservice = self.bus.get_object(
+ 'org.freedesktop.Notifications',
+ '/org/freedesktop/Notifications'
+ )
+ self.notifyservice = dbus.Interface(
+ self.notifyservice,
+ "org.freedesktop.Notifications"
+ )
+ notifyText = "{0}\n{1}".format(
+ trackInfo['title'],
+ trackInfo['album']
+ )
+ if len(trackInfo['year']) > 0:
+ notifyText += " ({0})".format(trackInfo['year'])
+
+ # Send track change information to stdout
+ print "Changing track : {0} | {1} | {2} ({3})".format(
+ trackInfo['artist'],
+ trackInfo['title'],
+ trackInfo['album'],
+ trackInfo['year']
+ )
+
+ # The second param is the replace id, so get the notify id back,
+ # store it, and send it as the replacement on the next call.
+ self.notifyid = self.notifyservice.Notify(
+ "Spotify-notify",
+ self.notifyid,
+ cover_image,
+ trackInfo['artist'],
+ notifyText,
+ [],
+ {},
+ 2000
+ )
+
+ def retrieveCoverImage(self, trackInfo):
+ if 'arturl' in trackInfo:
+ self.debug.out("Simply retrieving image from {0}".format(trackInfo['arturl']))
+ iconfilename = self.fetchCoverImage(trackInfo['arturl'])
+ else:
+ #if (trackInfo['trackhash'][0:14] == 'spotify:local:'):
+ # self.debug.out("Track is a local file. No art available.")
+ # return ''
+
+ self.debug.out("Attempting to fetch image from spotify")
+ iconfilename = self.fetchCoverImageSpotify(
+ trackInfo['artist'],
+ trackInfo['album'],
+ trackInfo['trackhash']
+ )
+ return iconfilename
+
+ def fetchCoverImageSpotify(self, artist, album, trackhash):
+ try:
+ trackid = trackhash.split(":")[2]
+ url = SPOTIFY_OPEN_URL + trackid
+ tracksite = urllib2.urlopen(url).read()
+
+ # Attempt to get the image url from the open graph image meta tag.
+ imageurl = False
+ metaMatch = re.search(
+ ']*property\s*=\s*["\']og:image["\'][^\>]*/?>',
+ tracksite
+ )
+ if metaMatch:
+ contentMatch = re.search(
+ 'content\s*=\s*["\']([^\"\']*)["\']',
+ metaMatch.group(0)
+ )
+ if contentMatch:
+ imageurl = contentMatch.group(1)
+
+ if not imageurl:
+ self.debug.out("No cover available.")
+ raise()
+
+ return self.fetchCoverImage(imageurl)
+ except Exception, e:
+ self.debug.out("Couldn't fetch cover image.")
+ self.debug.out(e)
+
+ return ''
+
+ def fetchCoverImage(self, url):
+ # Close the temporary image file, we are going to make a new one.
+ if SpotifyNotify.tmpfile:
+ SpotifyNotify.tmpfile.close()
+ SpotifyNotify.tmpfile = False
+
+ try:
+ SpotifyNotify.tmpfile = tempfile.NamedTemporaryFile()
+ tmpfilename = SpotifyNotify.tmpfile.name
+ self.debug.out("Album art tmp filepath: {0}".format(tmpfilename))
+
+ coverfile = urllib2.urlopen(url)
+ SpotifyNotify.tmpfile.write(coverfile.read())
+ SpotifyNotify.tmpfile.flush()
+ return tmpfilename
+ except Exception, e:
+ self.debug.out("Couldn't fetch cover image.")
+ self.debug.out(e)
+
+ return ''
+
+ @staticmethod
+ def startSpotify(Debug):
+ if not SpotifyNotify.spotifyPath:
+ Debug.out("No spotify process identifier found.")
+ return
+
+ ident = SpotifyNotify.spotifyPath
+ Debug.out("Looking for spotify as: {0}".format(ident))
+
+ procs = SpotifyNotify.checkForProcess(
+ 'ps x | grep "{0}" | grep -v grep'.format(ident),
+ Debug
+ )
+ if len(procs):
+ Debug.out("Spotify process found as: {0}".format(" ".join(procs[0])))
+ return
+
+ Debug.out("Starting new Spotify now.")
+
+ FNULL = open('/dev/null', 'w')
+ spid = Popen([ident], stdout=FNULL, stderr=FNULL).pid
+ if spid:
+ Debug.out("Spotify started, pid: {0}.".format(spid))
+ else:
+ Debug.out("Spotify could not be started.")
+
+ @staticmethod
+ def checkForClosedSpotify(SN, Debug):
+ if not SpotifyNotify.spotifyPath:
+ Debug.out("No spotify process identifier found.")
+ return False
+
+ ident = SpotifyNotify.spotifyPath
+ Debug.out("Looking for spotify as: {0}".format(ident))
+
+ procs = SpotifyNotify.checkForProcess(
+ 'ps x | grep "{0}" | grep -v grep'.format(ident),
+ Debug
+ )
+ if len(procs):
+ Debug.out("Spotify process found as: {0}".format(" ".join(procs[0])))
+
+ if (SpotifyNotify.tryToReconnect):
+ SN.connect()
+
+ return True
+
+ if SpotifyNotify.tmpfile:
+ SpotifyNotify.tmpfile.close()
+
+ Debug.out("Spotify has been closed, therefore I die.")
+ exit(0)
+
+ @staticmethod
+ def preventDuplicate(Debug):
+ mypid = os.getpid()
+ Debug.out("My pid: {0}".format(mypid))
+
+ proc = SpotifyNotify.checkForProcess('ps -p {0}'.format(mypid), Debug)
+ if not proc[0][3]:
+ return
+
+ process = proc[0][3]
+ search = 'ps -C {0}'.format(process)
+
+ Debug.out("Looking for other processes named: {0}".format(process).strip())
+
+ if process == 'python':
+ if not sys.argv[0]:
+ Debug.out("Process started using python, cannot determine script name.")
+ return
+
+ search = 'ps ax | grep "python {0}" | grep -v grep'.format(sys.argv[0])
+
+ for line in SpotifyNotify.checkForProcess(search, Debug):
+ if int(line[0]) != mypid:
+ print("This program was already running.")
+ Debug.out("I am a duplicate. I shall end myself. ({0})".format(" ".join(line)))
+ exit(0)
+
+ @staticmethod
+ def checkForProcess(proc, Debug):
+ output = []
+
+ for line in Popen(proc, shell=True, stdout=PIPE).stdout:
+ fields = line.split()
+ if not fields[0].isdigit():
+ continue
+
+ output.append(fields)
+
+ return output
+
+class DebugMe():
+ def __init__(self, toggle):
+ if toggle:
+ self.output = True
+ else:
+ self.output = False
+
+ def out(self, msg):
+ if not self.output:
+ return
+
+ print(">> {0}".format(msg))
+
+if __name__ == "__main__":
+ parser = OptionParser()
+ parser.add_option(
+ '-a',
+ '--action',
+ dest = 'action',
+ default = None,
+ type = 'choice',
+ choices = ['playPause', 'play', 'pause', 'next', 'previous'],
+ help = 'Music player actions (playPause/play/pause/next/previous).'
+ )
+ parser.add_option(
+ '-n',
+ '--skip_notify',
+ dest = 'skipNotify',
+ action = 'store_true',
+ default = False,
+ help = 'Song change notifications will be turned off.'
+ )
+ parser.add_option(
+ '-d',
+ '--debug',
+ dest = 'debug',
+ action = 'store_true',
+ default = False,
+ help = 'Debug messages will be displayed.'
+ )
+
+ (options, args) = parser.parse_args()
+
+ Debug = DebugMe(options.debug)
+ print("Spotify-notify v0.6")
+
+ if SPOTIFY_PROCESS_NAME:
+ SpotifyNotify.spotifyPath = SPOTIFY_PROCESS_NAME
+ else:
+ print "Spotify is not running"
+ sys.exit(0)
+
+ SN = SpotifyNotify(Debug)
+
+ if options.action:
+ action = options.action
+ action = action[0:1].upper() + action[1:]
+ SN.executeCommand(action)
+
+ SpotifyNotify.preventDuplicate(Debug)
+
+ try:
+ indicateserver = indicate.indicate_server_ref_default()
+ indicateserver.set_type("music.spotify")
+ indicateserver.set_desktop_file("/usr/share/applications/spotify.desktop")
+ indicateserver.show()
+ except:
+ pass
+ SN.pollChange()
+ print "Done"
+
diff --git a/bin/spotify-remote.sh b/bin/spotify-remote.sh
new file mode 100755
index 0000000..49d760e
--- /dev/null
+++ b/bin/spotify-remote.sh
@@ -0,0 +1,114 @@
+#!/bin/bash
+# Extracted from: http://ubuntuforums.org/showthread.php?t=1797848
+# User: azzid
+# Modified by Balkian
+# Collect DBUS_SESSION_BUS_ADDRESS from running process
+function set_dbus_adress
+{
+ USER=$1
+ PROCESS=$2
+ PID=`pgrep -o -u $USER $PROCESS`
+ ENVIRON=/proc/$PID/environ
+
+ if [ -e $ENVIRON ]
+ then
+ export `grep -z DBUS_SESSION_BUS_ADDRESS $ENVIRON`
+ else
+ echo "Unable to set DBUS_SESSION_BUS_ADDRESS."
+ exit 1
+ fi
+}
+
+function spotify_cmd
+{
+ dbus-send --print-reply --dest=org.mpris.MediaPlayer2.spotify /org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player.$1 1> /dev/null
+}
+
+function spotify_query
+{
+ qdbus org.mpris.MediaPlayer2.spotify /org/mpris/MediaPlayer2 org.freedesktop.DBus.Properties.Get org.mpris.MediaPlayer2.Player PlaybackStatus
+}
+
+function spotify_metadata
+{
+ qdbus com.spotify.qt / org.freedesktop.MediaPlayer2.GetMetadata
+}
+
+function spotify_notify
+{
+ metadata=`spotify_metadata`
+ echo "Metadata: $metadata"
+ title=`echo "$metadata" | grep title | cut -d' ' -f2-`
+ artist=`echo "$metadata" | grep artist | cut -d' ' -f2-`
+ image=`echo "$metadata" | grep artUrl | cut -d' ' -f3`
+ wget $image -O /tmp/image
+ echo "notifying $title"
+ notify-send "$artist" "$title" --icon=/tmp/image
+}
+
+function quit_message
+{
+ echo "Usage: `basename $0` {play|pause|playpause|next|previous|stop|playstatus|}"
+ exit 1
+}
+
+# Count arguments, must be 1
+if [ "$#" -ne "1" ]
+then
+ echo -e "You must supply exactly one argument!\n"
+ quit_message
+fi
+
+# Check if DBUS_SESSION is set
+if [ -z $DBUS_SESSION_BUS_ADDRESS ]
+ then
+ #echo "DBUS_SESSION_BUS_ADDRESS not set. Guessing."
+ set_dbus_adress `whoami` spotify
+fi
+
+case "$1" in
+ play)
+ spotify_cmd Play
+ spotify_notify
+ ;;
+ pause)
+ spotify_cmd Pause
+ spotify_notify
+ ;;
+ playpause)
+ spotify_cmd PlayPause
+ spotify_notify
+ ;;
+ next)
+ spotify_cmd Next
+ spotify_notify
+ ;;
+ previous)
+ spotify_cmd Previous
+ spotify_notify
+ ;;
+ stop)
+ spotify_cmd Stop
+ spotify_notify
+ ;;
+ spotify:user:*)
+ spotify_cmd "OpenUri string:$1"
+ spotify_cmd Play
+ spotify_notify
+ ;;
+ spotify:*:*)
+ spotify_cmd "OpenUri string:$1"
+ ;;
+ playstatus)
+ spotify_query
+ ;;
+ metadata)
+ spotify_metadata;
+ ;;
+ *)
+ echo -e "Bad argument.\n"
+ quit_message
+ ;;
+esac
+
+exit 0
diff --git a/bin/start-eclipse.sh b/bin/start-eclipse.sh
new file mode 100644
index 0000000..b2ccd8b
--- /dev/null
+++ b/bin/start-eclipse.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+cd $(dirname $(dirname $0))
+eclipse/eclipse -vm jre1.6.0_20/bin/ || failed=1
+if [ $failed -eq 1 ]; then
+
+ zenity --warning --title "Error executing eclipse's launcher" --text "Eclipse couldn't be found in $(pwd), please, modify $0 script to point to the right location" || exit
+ file=$(zenity --file-selection --title "Select the right executable");
+ vm=$(zenity --file-selection --title "Select virtual machine");
+
+ echo $file;
+ if [ -n "$file" ]; then
+ if [ -n "$vm" ]; then
+ virt=" -vm $vm";
+ fi
+ $file $virt
+ fi
+fi
diff --git a/bin/terminator b/bin/terminator
new file mode 100755
index 0000000..e0cde22
--- /dev/null
+++ b/bin/terminator
@@ -0,0 +1,2 @@
+#!/bin/bash
+one-window "terminator" "xdotool windowfocus \$WID & xdotool key ctrl+shift+t;" "/usr/bin/terminator $*"
diff --git a/bin/tmux-zoom.sh b/bin/tmux-zoom.sh
new file mode 100755
index 0000000..65b4991
--- /dev/null
+++ b/bin/tmux-zoom.sh
@@ -0,0 +1,65 @@
+#!/bin/bash
+
+# Copyright (c) 2012 Juan Ignacio Pumarino, jipumarino@gmail.com
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+# Instructions
+# ------------
+#
+# 1. Install this script and give it execute permission somewhere in your PATH.
+# For example:
+#
+# $ mkdir -p ~/bin
+# $ wget https://raw.github.com/jipumarino/tmux-zoom/master/tmux-zoom.sh -O ~/bin/tmux-zoom.sh
+# $ chmod +x ~/bin/tmux-zoom.sh
+#
+# 2. Add a shortcut in your ~/.tmux.conf file:
+#
+# bind C-k run "tmux-zoom.sh"
+#
+# 3. When using this shortcut, the current tmux pane will open in a new window by itself.
+# Running it again in the zoomed window will return it to its original pane. You can have
+# as many zoomed windows as you want.
+
+current=$(tmux display-message -p '#W-#I-#P')
+list=$(tmux list-window)
+
+[[ "$current" =~ ^(.*)-([0-9]+)-([0-9]+) ]]
+current_window=${BASH_REMATCH[1]}
+current_pane=${BASH_REMATCH[2]}-${BASH_REMATCH[3]}
+new_zoom_window=ZOOM-$current_pane
+
+if [[ $current_window =~ ZOOM-([0-9]+)-([0-9+]) ]]; then
+ if [ "$(tmux list-panes | wc -l)" -gt 1 ]; then
+ tmux display-message "other panes exist"
+ exit 0
+ fi
+ old_zoom_window=ZOOM-${BASH_REMATCH[1]}-${BASH_REMATCH[2]}
+ tmux select-window -t ${BASH_REMATCH[1]} \; select-pane -t ${BASH_REMATCH[2]} \; swap-pane -s $old_zoom_window.0 \; kill-window -t $old_zoom_window
+elif [[ $list =~ $new_zoom_window ]]; then
+ tmux select-window -t $new_zoom_window
+else
+ if [ "$(tmux list-panes | wc -l)" -eq 1 ]; then
+ tmux display-message "already zoomed"
+ exit 0
+ fi
+ tmux new-window -d -n $new_zoom_window \; swap-pane -s $new_zoom_window.0 \; select-window -t $new_zoom_window
+fi
diff --git a/bin/unlock-livebox.py b/bin/unlock-livebox.py
new file mode 100644
index 0000000..4db9c8a
--- /dev/null
+++ b/bin/unlock-livebox.py
@@ -0,0 +1,63 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import telnetlib, time
+
+# Poner esto a 0 para deshabilitar las opciones del menú
+# o a 1 para habilitarlas
+HABILITAR = 1;
+
+tn = telnetlib.Telnet ( "192.168.1.1" );
+tn.read_until("login: ");
+tn.write("LBV2techno\n");
+tn.read_until("Password: ");
+tn.write("1901b95ae4295d613abf9eabae0b9d40\n");
+for i in `range(1,3)`:
+ tn.write("\n");
+ tn.write("rg_conf_set wbm/settings/pages/backuprestore %i\n" % HABILITAR);
+ tn.read_until("Returned 0");
+ tn.write("rg_conf_set wbm/settings/pages/vpn %i\n" % HABILITAR);
+ tn.read_until("Returned 0");
+ tn.write("rg_conf_set wbm/settings/pages/fax %i\n" % HABILITAR);
+ tn.read_until("Returned 0");
+ tn.write("rg_conf_set wbm/settings/pages/log %i\n" % HABILITAR);
+ tn.read_until("Returned 0");
+ tn.write("rg_conf_set wbm/settings/pages/licence %i\n" % HABILITAR);
+ tn.read_until("Returned 0");
+ tn.write("rg_conf_set wbm/settings/pages/community %i\n" % HABILITAR);
+ tn.read_until("Returned 0");
+ tn.write("rg_conf_set wbm/settings/pages/visio %i\n" % HABILITAR);
+ tn.read_until("Returned 0");
+ tn.write("rg_conf_set wbm/settings/pages/livezoom %i\n" % HABILITAR);
+ tn.read_until("Returned 0");
+ tn.write("rg_conf_set wbm/settings/pages/hsiab %i\n" % HABILITAR);
+ tn.read_until("Returned 0");
+ tn.write("rg_conf_set wbm/settings/network/dhcp %i\n" % HABILITAR);
+ tn.read_until("Returned 0");
+ tn.write("rg_conf_set wbm/settings/network/ftlock %i\n" % HABILITAR);
+ tn.read_until("Returned 0");
+ tn.write("rg_conf_set wbm/settings/network/ftlock %i\n" % HABILITAR);
+ tn.read_until("Returned 0");
+ tn.write("rg_conf_set wbm/settings/network/tvrouted %i\n" % HABILITAR);
+ tn.read_until("Returned 0");
+ tn.write("rg_conf_set wbm/settings/services/professionnal %i\n" % HABILITAR);
+ tn.read_until("Returned 0");
+ tn.write("rg_conf_set wbm/settings/services/rtcphone %i\n" % HABILITAR);
+ tn.read_until("Returned 0");
+ tn.write("rg_conf_set wbm/settings/services/universal_phone %i\n" % HABILITAR);
+ tn.read_until("Returned 0");
+ tn.write("rg_conf_set wbm/settings/services/wifipushbutton %i\n" % HABILITAR);
+ tn.read_until("Returned 0");
+ tn.write("rg_conf_set wbm/settings/services/msgwaiting %i\n" % HABILITAR);
+ tn.read_until("Returned 0");
+ tn.write("rg_conf_set wbm/settings/test/sipdev %i\n" % HABILITAR);
+ tn.read_until("Returned 0");
+ tn.write("rg_conf_set wbm/settings/test/fmdev %i\n" % HABILITAR);
+ tn.read_until("Returned 0");
+ tn.write("rg_conf_set wbm/settings/network/h323 %i\n" % HABILITAR);
+ tn.read_until("Returned 0");
+ tn.write("rg_conf_set wbm/settings/services/wpspushbutton %i\n" % HABILITAR);
+ tn.read_until("Returned 0");
+ tn.write("save\n");
+ time.sleep(2);
+ tn.write("reboot\n");
diff --git a/bin/volume-down.sh b/bin/volume-down.sh
new file mode 100755
index 0000000..ce11826
--- /dev/null
+++ b/bin/volume-down.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+pacmd dump|awk --non-decimal-data '$1~/set-sink-volume/{system ("pacmd "$1" "$2" "$3-1000)}'
diff --git a/bin/volume-toggle-mute.sh b/bin/volume-toggle-mute.sh
new file mode 100755
index 0000000..089bf89
--- /dev/null
+++ b/bin/volume-toggle-mute.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+pacmd dump|awk --non-decimal-data '$1~/set-sink-mute/{system ("pacmd "$1" "$2" "($3=="yes"?"no":"yes"))}'
diff --git a/bin/volume-up.sh b/bin/volume-up.sh
new file mode 100755
index 0000000..626be12
--- /dev/null
+++ b/bin/volume-up.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+pacmd dump|awk --non-decimal-data '$1~/set-sink-volume/{system ("pacmd "$1" "$2" "$3+1000)}'
diff --git a/bin/winfuncs.sh b/bin/winfuncs.sh
new file mode 100755
index 0000000..f3e20db
--- /dev/null
+++ b/bin/winfuncs.sh
@@ -0,0 +1,194 @@
+#!/bin/bash
+# Extracted from:http://forum.xfce.org/viewtopic.php?id=6841 ¡
+#todo:
+# cancel for tile functions
+# determine what windows are maximized and re-max after the "window select" function
+# determine what windows are non-resizable by the user so that the script doesn't resize them
+# cascade also shaded windows
+
+# which workspace we're on
+function get_workspace {
+ if [[ "$DTOP" == "" ]] ; then
+ DTOP=`xdotool get_desktop`
+ fi
+}
+
+function is_desktop {
+ xwininfo -id "$*" | grep '"Desktop"'
+ return "$?"
+}
+
+function get_visible_window_ids {
+ if (( ${#WDOWS[@]} == 0 )) ; then
+ WDOWS=(`xdotool search --desktop $DTOP --onlyvisible "" 2>/dev/null`)
+ fi
+}
+
+function get_desktop_dim {
+ #desktop dimensions
+ if (( ${#DIM[@]} == 0 )) ; then
+ DIM=(`wmctrl -d | egrep "^0" | sed 's/.*DG: \([0-9]*x[0-9]*\).*/\1/g' | sed 's/x/ /g'`)
+ fi
+}
+
+function win_showdesktop {
+ get_workspace
+ get_visible_window_ids
+
+ command="search --desktop $DTOP \"\""
+
+ if (( ${#WDOWS[@]} > 0 )) ; then
+ command="$command windowminimize %@"
+ else
+ command="$command windowraise %@"
+ fi
+
+ echo "$command" | xdotool -
+}
+
+function win_tile_two {
+ get_desktop_dim
+ wid1=`xdotool selectwindow 2>/dev/null`
+
+ is_desktop "$wid1" && return
+
+ wid2=`xdotool selectwindow 2>/dev/null`
+
+ is_desktop "$wid2" && return
+
+ half_w=`expr ${DIM[0]} / 2`
+
+ commands="windowsize $wid1 $half_w ${DIM[1]}"
+ commands="$commands windowsize $wid2 $half_w ${DIM[1]}"
+ commands="$commands windowmove $wid1 0 0"
+ commands="$commands windowmove $wid2 $half_w 0"
+ commands="$commands windowraise $wid1"
+ commands="$commands windowraise $wid2"
+
+ wmctrl -i -r $wid1 -b remove,maximized_vert,maximized_horz
+ wmctrl -i -r $wid2 -b remove,maximized_vert,maximized_horz
+
+ echo "$commands" | xdotool -
+}
+
+function win_tile {
+ get_workspace
+ get_visible_window_ids
+
+ (( ${#WDOWS[@]} < 1 )) && return;
+
+ get_desktop_dim
+
+ # determine how many rows and columns we need
+ cols=`echo "sqrt(${#WDOWS[@]})" | bc`
+ rows=$cols
+ wins=`expr $rows \* $cols`
+
+ if (( "$wins" < "${#WDOWS[@]}" )) ; then
+ cols=`expr $cols + 1`
+ wins=`expr $rows \* $cols`
+ if (( "$wins" < "${#WDOWS[@]}" )) ; then
+ rows=`expr $rows + 1`
+ wins=`expr $rows \* $cols`
+ fi
+ fi
+
+ (( $cols < 1 )) && cols=1;
+ (( $rows < 1 )) && rows=1;
+
+ win_w=`expr ${DIM[0]} / $cols`
+ win_h=`expr ${DIM[1]} / $rows`
+
+ # do tiling
+ x=0; y=0; commands=""
+ for window in ${WDOWS[@]} ; do
+ wmctrl -i -r $window -b remove,maximized_vert,maximized_horz
+
+ commands="$commands windowsize $window $win_w $win_h"
+ commands="$commands windowmove $window `expr $x \* $win_w` `expr $y \* $win_h`"
+
+ x=`expr $x + 1`
+ if (( $x > `expr $cols - 1` )) ; then
+ x=0
+ y=`expr $y + 1`
+ fi
+ done
+
+ echo "$commands" | xdotool -
+}
+
+function win_cascade {
+ get_workspace
+ get_visible_window_ids
+
+ (( ${#WDOWS[@]} < 1 )) && return;
+
+ x=0; y=0; commands=""
+ for window in ${WDOWS[@]} ; do
+ wmctrl -i -r $window -b remove,maximized_vert,maximized_horz
+
+ commands="$commands windowsize $window 640 480"
+ commands="$commands windowmove $window $x $y"
+
+ x=`expr $x + 22`
+ y=`expr $y + 22`
+ done
+
+ echo "$commands" | xdotool -
+}
+
+function win_select {
+ get_workspace
+ get_visible_window_ids
+
+ (( ${#WDOWS[@]} < 1 )) && return;
+
+ # store window positions and widths
+ i=0
+ for window in ${WDOWS[@]} ; do
+ GEO=`xdotool getwindowgeometry $window | grep Geometry | sed 's/.* \([0-9].*\)/\1/g'`;
+ height[$i]=`echo $GEO | sed 's/\(.*\)x.*/\1/g'`
+ width[$i]=`echo $GEO | sed 's/.*x\(.*\)/\1/g'`
+
+ # ( xwininfo gives position not ignoring titlebars and borders, unlike xdotool )
+ POS=(`xwininfo -stats -id $window | grep 'geometry ' | sed 's/.*[-+]\([0-9]*[-+][0-9*]\)/\1/g' | sed 's/[+-]/ /g'`)
+ posx[$i]=${POS[0]}
+ posy[$i]=${POS[1]}
+
+ i=`expr $i + 1`
+ done
+
+ # tile windows
+ win_tile
+
+ # select a window
+ wid=`xdotool selectwindow 2>/dev/null`
+
+ is_desktop "$wid" && return
+
+ # restore window positions and widths
+ i=0; commands=""
+ for (( i=0; $i<${#WDOWS[@]}; i++ )) ; do
+ commands="$commands windowsize ${WDOWS[i]} ${height[$i]} ${width[$i]}"
+ commands="$commands windowmove ${WDOWS[i]} ${posx[$i]} ${posy[$i]}"
+ done
+
+ commands="$commands windowraise $wid"
+
+ echo "$commands" | xdotool -
+}
+
+for command in ${@} ; do
+ if [[ "$command" == "tile" ]] ; then
+ win_tile
+ elif [[ "$command" == "select" ]] ; then
+ win_select
+ elif [[ "$command" == "tiletwo" ]] ; then
+ win_tile_two
+ elif [[ "$command" == "cascade" ]] ; then
+ win_cascade
+ elif [[ "$command" == "showdesktop" ]] ; then
+ win_showdesktop
+ fi
+done
+