Andrés Blog

Krams. Und so.


ctrlmedia.sh

Um den von mir genutzten Mediaplayer bequem bedienen zu können, benutze ich verschiedene Tastenkombinationen (und, falls vorhanden, die Mediatasten) für Play/Pause/Stop und Nächstes/Vorheriges. Wenn man jedoch den Player wechselt oder mehrere Programme für verschiedene Formate (z.B. Audio und Video) benutzt, so ist das umständlich. Zur Zeit benutze ich cmus, um Musik abzuspielen, und mpv, um Filme, Serien, Musik- oder Youtubevideos zu gucken. Diese Programme laufen öfters im Hintergrund und es lässt sich immer nur eines davon über die Tastenkombinationen steuern. Man hat die Wahl, ob man das Fenster des anderen Programmes jedes mal sucht oder ob man einen weiteren Satz an Tasten für das zweite Programm festlegt.

Oder aber, man schreibt sich ein kleines Wrapperscript! Dieses kann verschiedene Player gleichzeitig unterstützen und es kann festgelegt werden, welcher Player wann kontrolliert werden soll. Für meine bisherigen Anwendungsfälle lässt sich das super umsetzen!

  1. Ich kann mit den gleichen Tastenkombinationen cmus und mpv kontrollieren.
  2. Falls beide Programme gestartet wurden, so wird mpv gesteuert.
  3. Falls keines lokal läuft, wird ein anderer Rechner im Netzwerk kontrolliert.

cmus läuft meistens im Hintergrund, auch wenn ich gerade keine Musik höre. mpv wird immer zum Abspielen einer (oder mehrerer) Dateien gestartet und beendet sich nach erfolgter Wiedergabe. Eine laufende Instanz deutet also auf aktive Benutzung hin und soll in dem Fall gesteuert werden.

Wenn ich mit meinem Laptop auf dem Bett liege und Musik hören möchte - dann benutze ich cmus auf meinem Rechner. Die Lautsprecher sind denen des Laptops weit überlegen und fangen nicht schon knapp über Flüsterlautstärke an zu knarzen. Mit dem Wrapperscript kann ich cmus über ssh steuern, genau wie ich den lokalen cmus steuern würde.

Wenn man cmus über ssh eine höhere Priorität als der lokalen Instanz einräumt, dann kann dies zu spürbaren Verzögerungen führen. Um diese zu reduzieren, wird zunächst mit einem Ping überprüft, ob der Host erreichbar ist. Dazu dient die Funktion hostonline. Das Tool ping aus den iputils wartet mindestens eine Sekunde auf einen Timeout. Das Tool fping ermöglicht es, den Timeout auf 50ms zu reduzieren. Fürs lokale Netzwerk reicht das aus und die Verzögerung ist kaum mehr zu spüren.

#!/usr/bin/env bash

# "bash strict mode" ohne -e
set -uo pipefail
IFS=$'\n\t'

if [ $# -lt 1 ]; then
    echo "Usage: $0 <play|pause|prev|next>"
    echo "This script looks for running media players and controls them."
    exit 1
fi

case "$1" in
    play|pause|stop|next|prev)
        cmd=$1
        ;;

    *)
        echo "Command not found."
        exit 1
esac

function hostonline {
    # fping can set smaller timeouts (50ms vs 1s)
    command -v fping > /dev/null
    if [ $? -eq 0 ]; then
        local pingcmd="fping -q $1 -t50"
    else
        local pingcmd="ping -q $1 -c 1 -W 1"
    fi

    $pingcmd > /dev/null
    if [ $? -eq 0 ]; then
        echo 1
    else
        echo 0
    fi
}

function media-mpv {
    mpvid=$(xdotool search --class mpv)
    if [ $? -eq 0 ]; then
        case "$1" in
            play|pause)
                xdotool key --window ${mpvid} space > /dev/null
                ;;
            stop)
                xdotool key --window ${mpvid} q > /dev/null
                ;;
            prev)
                xdotool type --window ${mpvid} '<' > /dev/null
                ;;
            next)
                xdotool type --window ${mpvid} '>' > /dev/null
                ;;

            *)
                echo "Command not found."
                exit 1
        esac
        exit 0
    fi
}

function media-cmus {
    if [ $(pidof cmus) ]; then
        case "$1" in
            play|pause)
                cmus-remote -u
                ;;
            stop)
                cmus-remote -s
                ;;
            prev)
                cmus-remote -r
                ;;
            next)
                cmus-remote -n
                ;;

            *)
                echo "Command not found."
                exit 1
        esac
        exit 0
    fi
}

function media-cmus-remote {
    if [ $HOSTNAME != $2 ]; then
        if [ $(ssh $2 pidof cmus) ]; then
            case "$1" in
                play|pause)
                    ssh $2 cmus-remote -u
                    ;;
                stop)
                    ssh $2 cmus-remote -s
                    ;;
                prev)
                    ssh $2 cmus-remote -r
                    ;;
                next)
                    ssh $2 cmus-remote -n
                    ;;

                *)
                    echo "Command not found."
                    exit 1
            esac
            exit 0
        fi
    fi
}

# Sort this list to change priorities
media-mpv $1
if [ $(hostonline computername) -eq 1 ]; then
    media-cmus-remote $1 computername
fi
media-cmus $1

echo "No supported media player running."
exit 1

Jetzt ärgere ich mich nur noch darüber, dass ich nicht wesentlich eher auf diese Idee gekommen bin! ;)

Update: Mir ist ein Fehler unterlaufen, da der “bash strict mode” beim prüfen auf laufende Programme das Script abbricht. set -e lässt das Bashscript abbrechen, sobald ein aufgerufenes Programm einen Fehlercode != 0 zurückgibt. Da hier keine kritischen Befehle aufgerufen werden, entschärfe ich hier den “bash strict mode” erstmal.