blog · git · desktop · images · contact & privacy · gopher


Musik syncen

Dachte mir, ich schreib’ einfach mal auf, wie mein Modell hier aussieht, um den Musikbestand auf Rechnern und mobilem Player konsistent zu halten. Schaden kann’s nicht.

Der Sync

Grundlegend sieht die Situation so aus:

Skizze des Setups

Ich habe also ein Notebook und einen stationären Rechner, außerdem noch den mobilen Player. Nun möchte ich natürlich – Speicher ist ja billig – überall dieselben Lieder und Podcasts zur Verfügung haben und auch die mpd-Playlist(s). Das soll natürlich möglichst einfach vonstattengehen und ohne proprietäre Sachen passieren, außerdem an der Shell stattfinden.

Normalerweise verwende ich Git für alles, was im Entferntesten mit Synchronisation zu tun hat. Bei großen Binärdaten hat man da allerdings das Problem der History: Git merkt sich halt alles. Wenn ich ein Lied lösche, ist es nur nicht mehr im Working Directory, aber durchaus noch im Repository. Das will man ja nicht.

Da kommt nun rsync ins Spiel. Man kann es dazu verwenden, einen Datenbestand an einem anderen Ort zu replizieren. Ein einfaches „cp“ würde das natürlich auch tun, aber rsync hat den Vorteil, nur geänderte Dateien zu kopieren. Das ist gerade bei großen Datenmengen wie Musik von Vorteil.

rsync arbeitet aber unidirektional. Was ich also am Desktop ändere, landet auf dem Laptop – befinden sich dort aber auch Änderungen, gehen sie eventuell verloren. Aus diesem Grund gibt es unison, was im Prinzip ein bidirektionales rsync ist: Zwei Datenbestände werden auf dasselbe Level gebracht. Lösche ich am Desktop etwas und füge am Laptop etwas Neues hinzu, bekommt der Desktop das Neue und am Laptop wird das Überschüssige gelöscht.

Das unison-Profil (auf dem Desktop) sieht im Wesentlichen dann so aus:

root = /home/void/
root = ssh://mobiltux//home/void/
log = false
times = true

path = music
path = podcasts

Zu beachten ist hier „times = true“: Dadurch werden auch die Timestamps der Dateien mitsynchronisiert, was normalerweise nicht der Fall ist. Wozu das gut ist, wird nachher klar. Eigentlich arbeitet unison nämlich nicht auf Basis von Timestamps, da es damit nicht möglich wäre, zu erkennen, wann eine Datei gelöscht wird. Stattdessen merkt sich unison u.a. die existierenden Dateien – so kann es auseinanderhalten, ob ich eine Datei lösche oder ob sie nur noch nicht auf der anderen Seite existiert hat.

Oben im Bild ist beim Pfeil zwischen Desktop und Laptop noch Git eingetragen: Das nutze ich dafür, um die mpd-Playlist(s) konsistent zu halten. Das passiert im Rahmen meines ohnehin existierenden dotfiles-Repositories. Nothing fancy.

Was den mobilen Player betrifft, ist der eigentlich ein Sonderfall. Desktop und Laptop sind „gleichberechtigt“ – auf beiden kann ich Dateien hinzufügen und löschen. Am Player tue ich aber weder noch, der Player spielt nur ab. Ich muss also keine Rücksicht darauf nehmen, ob sich am Player irgendetwas geändert hat, sondern kann meinen Datenbestand einfach auf ihn werfen – sprich, da kommt rsync zum Einsatz.

rsync lasse ich dann aber doch im Wesentlichen basierend auf Timestamps arbeiten, weil das für diesen Zweck ein guter Kompromiss aus Geschwindigkeit und Genauigkeit ist. Dafür ist aber wichtig, dass auch überall die gleichen Timestamps existieren, weswegen die „times = true“-Option bei unison essentiell ist. Ist diese Option aktiv, kann ich den exakten Datenbestand wie folgt auf dem Player replizieren und dabei spielt es keine Rolle, ob ich das vom Laptop oder vom Desktop aus tue:

$ rsync --modify-window=2 -rtv --delete ~/music/. /media/sansa_extern/MUSIC/
$ rsync --modify-window=2 -rtv --delete ~/podcasts/. /media/sansa_intern/PODCASTS/

Die einzelnen Optionen kann man in der Manpage nachschlagen, trotzdem zu „--modify-window=2“ noch ein Wort. Zumindest mein Player verwendet FAT als Dateisystem. Liest man diesen Artikel in der Wikipedia aufmerksam, stellt man fest, dass die Auflösung von Timestamps bei FAT extrem gering ist: nur auf 2 Sekunden genau. Das hätte normalerweise zur Folge, dass Dateien eventuell unnötig neu kopiert werden. Jedes Mal! (Kuriosum am Rande: „Jedesmal“ ist heute falsch, es gibt nur noch „jedes Mal“.)

Nun verspricht Microsoft, dass Timestamps beim Schreiben auf die nächstliegende gerade Zahl gerundet werden. Sprich, eigentlich würde ein Window von einer Sekunde ausreichen. Da ich aber nicht mit einem Microsoft-System auf das Dateisystem schreibe (unabhängig davon, ob die sich an ihre eigenen Regeln halten oder nicht), weiß ich nicht, ob mein Dateisystemtreiber das auch wirklich konsequent so umsetzt – wenn’s dumm läuft, könnte der Treiber immer abrunden und schon würde eine Sekunde nicht mehr ausreichen. Sowas könnte auch durch einen Bug passieren. Folglich habe ich das Zeitfenster lieber auf zwei Sekunden erhöht.

Tja, das war’s auch schon. Wollte man das erweitern, gäbe es zwei Möglichkeiten:

Alles in allem also ein sehr überschaubares System. Funktioniert und blinkt nicht.

Nachtrag: Zeitumstellungen

Nachtrag 31.10.2011: Zeitumstellung nervt, kann die bitte mal jemand abschaffen? Problem an oben beschriebener Methode ist, dass FAT die lokale Zeit speichert – und keine Information darüber, in welcher Zeitzone man ist. Das heißt, wenn hier mal wieder die Umstellung von Sommer- auf Winterzeit oder umgekehrt stattfindet, dann befinden sich die Zeitstempel auf dem Player nicht mehr im 2-Sekunden-Fenster und rsync möchte gerne den gesamten Inhalt neu übertragen.

Bei der letzten Umstellung hatte ich das einfach hingenommen, aber es geht auch schöner, indem man nur die Zeitstempel auf dem Player aktualisiert:

#!/bin/bash

fixup()
{
    from=$1
    to=$2

    find "$from" -print0 | while read -rd '' i
    do
        relfile="${i:${#from}}"
        frel="$i"
        trel="$to$relfile"

        if [[ -e "$trel" ]]
        then
            echo touch -r "$frel" "$trel"
            touch -r "$frel" "$trel"
        fi
    done
}

fixup ~/music /media/sansa_extern/MUSIC
fixup ~/podcasts /media/sansa_intern/PODCASTS