Andrés Blog

Krams. Und so.

Mai 13, 2016

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.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
#!/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.

Mär 22, 2016

Shutdownscript Nr. 2

Eine neue Iteration des Shutdownscripts, welches die Shell blockiert, bis kein Traffic mehr fliesst. Diesmal sind alle Einstellmöglichkeiten rausgeworfen worden, da diese zum Teil hinderlich oder unnötig waren. So wird jetzt der Traffic aller Netzwerkschnittstellen summiert und ein fester Schwellwert von 1kb/s ist zu unterschreiten als Mittelwert über 5 Sekunden. Auch ist der Befehl zum Shutdown verschwunden. Einfach das Script mit

scriptname && systemctl suspend

aufrufen, um den gewünschten Effekt zu erzielen ;)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/bin/bash

# "bash strict mode"
set -euo pipefail
IFS=$'\n\t'

verbose=1

function networkidle() {
    local traffic_last=$(awk '/:/ {if ($1 != "lo:") {SUM += $2}} END {print SUM}' /proc/net/dev)
    while true; do
        sleep 5
        local traffic_now=$(awk '/:/ {if ($1 != "lo:") {SUM += $2}} END {print SUM}' /proc/net/dev)
        local traffic_diff=$(( (${traffic_now} - ${traffic_last}) / 1024 / 5 ))
        local traffic_last=${traffic_now}
        if [[ ${verbose} -ne 0 ]]; then echo "${traffic_diff} kb/s"; fi
        if [[ ${traffic_diff} -le 1 ]]; then
            if [[ ${verbose} -ne 0 ]]; then echo "Traffic below 1 kb/s."; fi
            return
        fi
    done
}

networkidle

Den inoffiziellen "bash strict mode" habe ich übrigens im Blog von Aaron Maxwell gefunden. Werde ich in Zukunft wohl öfters in meine Scripte einbinden.

Dez 28, 2015

Das System automatisch herunterfahren, sobald alle Downloads abgeschlossen sind

Ich bin über die Feiertage und den Jahreswechsel bei meinen Eltern. Dort ist die Internetleitung nicht so breitbandig, wie ich es zuhause gewohnt bin. Da gerade der 32c3 läuft und ich mir ein paar der Talks gerne ansehen möchte, stößt die Leitung bei den Downloads der Aufzeichnungen schnell an ihre Grenze - zumal ich nicht der alleinige Nutzer bin. Daher habe ich ein kleines Script geschrieben, welches den Rechner nach (hoffentlich erfolgreichem) Download automatisch in den Ruhezustand schickt. So kann der Download über Nacht laufen und der Rechner ist nicht länger an, als benötigt. Dazu wird alle paar Sekunden der Traffic einer Netzwerkschnittstelle ausgelesen und wenn dieser unter einen Schwellwert sinkt, dann wird ein definierter Befehl ausgeführt. So muss man nicht von vornherein die Downloads genau planen, sondern legt einfach los ;)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#!/bin/bash

function networkidle() {
    local device=enp4s0
    local traffic_last=$(grep $device /proc/net/dev | awk '{print $2}')
    local framesize=5
    local traffic_min=20
    local shutdowncommand="systemctl suspend"

    while true; do
        sleep $framesize
        traffic_now=$(grep $device /proc/net/dev | awk '{print $2}')
        traffic_diff=$(expr ${traffic_now} - ${traffic_last})
        traffic_diff=$(expr ${traffic_diff} / 1024)
        traffic_persec=$(expr ${traffic_diff} / $framesize)
        traffic_last=$traffic_now
        echo $traffic_diff kb in $framesize seconds ~ $traffic_persec kb/s
        if [[ $traffic_persec -lt $traffic_min ]];
        then
            echo "Traffic below ${traffic_min} kb/s. Suspending!"
            $shutdowncommand
            return
        fi
    done
}

networkidle