Open Source im professionellen Einsatz
Linux-Magazin 08/2006

Digitaler Internet-Videorecorder in Perl

Ich glotz' TV

Nina Hagens Medien-Nutzungsverhalten zu kopieren ist für einen Auslandsdeutschen gar nicht einfach. Doch ein Internet-Fernsehportal, die abgekupferte Technik eines Geräts und Perl verhelfen Exilanten sogar zu zeitversetztem Pantoffelkino. Hier Gebliebene haben natürlich genauso viel Spaß.

1306

Der von Onlinetvrecorder.com angebotene Service ist super. Dort kann jedermann kostenlos beliebige Sendungen des deutschen Fernsehprogramms aufzeichnen und runterladen. Zwar kränkelt die Website hin und wieder beim Recording und der Download geht nur tief in der Nacht einigermaßen zügig. Aber um im Ausland deutsches Fernsehen zu genießen, nehme ich das gern in Kauf.

Haben schließlich im Laufe einiger Wochen Dutzende Sendungen die Festplatte voll gemüllt, stellt sich die Frage nach einer Verwaltung(ssoftware). Die sollte mir eine Wahl aus den verfügbaren Sendungen gestatten und die alten Schinken, die ich eine Zeit lang nicht angerührt habe, von der Platte tilgen.

Hier drängt sich die Analogie zu einem Tivo auf. Der digitale Fernsehrekorder der gleichnamigen Firma und seine Klone sind fester Bestandteil amerikanischer TV-Gerätekultur - jedes Kind kennt die Marke. Die Geräte bieten eine einfach bedienbare Oberfläche, um Fernsehsendungen aufzuzeichnen und auf einer Festplatte zum späteren Sehgenuss zu speichern.

Damit sind nicht nur Werbeblöcke schnell durchquert, auch das so genannte Time-Shifting ist möglich: Mit massenweise eingelagerten Sendungen sieht der Konsument nicht mehr fern, wenn eine Sendung ausgestrahlt wird, sondern erst dann, wenn er Zeit dazu hat.

Abbildung 1 zeigt eine kleine Auswahl aufgezeichneter Programme auf meinem fünf Jahre alten (aufgebohrten) Tivo. Das Gerät nimmt Sendungen automatisch auf. Wegen des begrenzten Plattenplatzes löscht der Tivo alte Programme nach einigen Tagen selbsttätig, es sei denn, der Benutzer hat sie eigenhändig als »Save until I delete« markiert. Der Tivo unterscheidet zwischen Aufnahmen, die kurz vor dem Löschen stehen (Ausrufezeichen), mehrere Tage (keine Markierung) oder nur einen Tag (gelber Punkt) Gnadenfrist haben, und jenen, die er unbegrenzt aufbewahrt (grüner Punkt).

Abbildung 1: Eine Auswahl aufgezeichneter Fernsehprogramme auf dem digitalen Videorekorder Tivo.

Das vorgestellte Skript »tv« bildet eine einfache Version dieser Benutzerschnittstelle nach. Statt der bekannten grafischen Toolkits wie Perl/Tk, GTK oder Wx-Widgets nutzt es die auf der Curses-Bibliothek fußende Widget-Sammlung Curses::UI. Mit ihr lassen sich typische GUI-Elemente wie Dialoge, Menüs oder Listboxen in einem Ascii-Terminal einfach programmieren. Der Look von 1980 ist wieder da - Nostalgie pur!

Die Videodateien erwartet das Skript in einem vorgegebenen Verzeichnis, das es alle 60 Sekunden durchforstet. Stellt es Änderungen fest, frischt es die Oberfläche auf. Auch prüft es laufend, ob die Gesamtheit aller Videodateien eine zulässige Größe überschreitet, voreingestellt sind 20 GByte. Ist dies der Fall, löscht es die ältesten Dateien - falls nicht anderweitig markiert - ohne nachzufragen von der Platte, bis die Höchstmarke wieder unterschritten ist.

An den Keyboards: Perl

Die Navigation in der von »tv« dargestellten Listbox erfolgt entweder mit den Cursortasten (samt [Page-Up]/[Down]) oder mit den Vi-Nutzern bekannten Tasten [K] (nach oben) und [J] (nach unten). Zum manuellen Löschen einer Datei drückt der User die [D]-Taste (Delete). Erhält der anschließend gezeigte Bestätigungsdialog (Abbildung 3) ein »Y« oder drückt der Bediener die [Return]-Taste, während der Cursor über dem »OK« steht, löscht »tv« die Datei von der Platte und frischt die Listbox auf.

Um eine Datei mit einem Stern zu markieren, sie also vor dem automatischen Löschen des minütlich erscheinenden Plattenplatzkontrolleurs zu schützen, drückt der User die Taste [*] auf einem angewählten Listbox-Eintrag. Um die Sendung mit dem Mplayer abzuspielen, genügt es, [Return] auf der gewählten Datei zu drücken. Der unter [2] erhältliche Tausendsassa spielt alle gängigen Videoformate ab. Ein [Q] beendet einen einmal gestarteten Mplayer, der sich auch mittels Tastaturkommandos vor- und zurückspulen lässt. Um das Programm »tv« zu beenden, genügt ebenfalls ein Druck auf die Taste [Q].

Multitasking für Multimedia

Zunächst zieht Listing 1 die Module Curses::UI::POE und Curses rein, die beide auf dem CPAN erhältlich sind. Die praktischen Curses-Widgets enthält das Modul Curses::UI. Damit das Skript Multitasking-fähig ist, um zum Beispiel die periodischen Auffrischungsarbeiten auszuführen, definiert Curses::UI::POE eine abgeleitete Klasse, die das GUI in die Eventschleife des POE-Framework einbindet. POE kam im Snapshot schon öfter zu Ehren, meist um grafische GUIs mit kooperativem Multitasking ruckelfrei laufen zu lassen, obwohl das steuernde Programm aufreibenden Nebentätigkeiten nachgeht.

Abbildung 2: Das Perl-Skript »tv« bildet mit Curses::UI eine dem Tivo ähnliche Oberfläche nach.

Der in Zeile 10 aufgerufene Konstruktor legt mit der Option »color_support« fest, dass das neue Terminal-GUI Farben unterstützt. Der Parameter »inline_states« definiert den Startzustand »_start«, den der POE-Kernel kurz nach dem Anlaufen automatisch anspringt. Dort sorgt die Methode »delay()« dafür, dass der POE-Kernel nach exakt 60 Sekunden den Zustand »wake_up« aufruft, der die ab Zeile 89 definierte Funktion »wake_up_handler« ablaufen lässt.

Dort untersucht die Methode »rescan()« des Moduls Videodir (siehe unten) das Videoverzeichnis und notiert sich die Namen aller Dateien und deren Datumsstempel. An derselben Stelle liegt eine kleine Datenbank, in der steht, wie lange der Benutzer die einzelnen Filme behalten möchte. Das alles liest »Videodir::rescan()« ein und speichert es in einer internen Datenstruktur, die die anschließend gerufene Funktion »redraw()« holt und die Listbox des GUI aktualisiert.

Listing 1:
»tv«

001 #!/usr/bin/perl -w
002 use strict;
003 use Videodir;
004 use Curses::UI::POE;
005 use Curses;
006 
007 my $MPLAYER = "/usr/bin/mplayer";
008 my $V = Videodir->new();
009 
010 my $CUI = Curses::UI::POE->new(
011   -color_support => 1,
012   inline_states  => {
013     _start => sub {
014         $poe_kernel->delay('wake_up', 60);
015     },
016     wake_up => &wake_up_handler,
017 });
018 
019 my $WIN = $CUI->add(qw( win_id Window ));
020 
021 my $TOP = $WIN->add(qw( top Label
022   -y 0 -width -1 -paddingspaces 1
023   -fg white -bg blue
024   ), -text => top_text());
025 
026 my $LBOX = $WIN->add(qw( lb Listbox
027   -padtop 1 -padbottom 1 -border 1 ),
028   -onchange    => &selected,
029   -onselchange => &changed,
030 );
031 
032 my $BOTTOM = $WIN->add(qw( bottom Label
033   -y -1 -width -1 -paddingspaces 1
034   -fg white -bg blue
035   ), -text => bottom_text(),
036 );
037 
038 $CUI->set_binding(sub { selected($LBOX)
039                       }, KEY_ENTER());
040 $CUI->set_binding(sub { exit 0; }, "q");
041 $CUI->set_binding(&delete_confirm, "d");
042 $CUI->set_binding(&keep, "*");
043 
044 redraw(); # draw inital listbox content
045 $CUI->mainloop;
046 
047 ###########################################
048 sub ttl_icon {
049 ###########################################
050   my($ttl) = @_;
051   return $ttl <  0 ? "!" :
052          $ttl <= 5 ? " " : "*" ;
053 }
054 
055 ###########################################
056 sub changed {
057 ###########################################
058     $BOTTOM->text(bottom_text());
059 }
060 
061 ###########################################
062 sub selected {
063 ###########################################
064   my $cmd = "$MPLAYER " .
065             active_item()->{path} .
066             ">/dev/null 2>&1";
067   `$cmd &`;
068 }
069 
070 ###########################################
071 sub bottom_text {
072 ###########################################
073   my $item = active_item();
074 
075     # Work around PGdown bug
076   return unless defined $item;
077 
078   my $str = sprintf "%d/%d | %.1f days" .
079     " old | %s GB | TTL %s",
080     $LBOX->get_active_id() + 1,
081     scalar @{$V->{items}},
082     $item->{age}, $item->{size},
083     $item->{ttl};
084 
085   return $str;
086 }
087 
088 ###########################################
089 sub wake_up_handler {
090 ###########################################
091     $V->rescan(); # Get newly added files
092     redraw();
093 
094     redraw() if $V->shrink();
095         # Re-enable timer
096     $poe_kernel->delay('wake_up', 60);
097 }
098 
099 ###########################################
100 sub top_text {
101 ###########################################
102     return "tv1.0 | " . $V->{total_size}
103      . " GB total | $V->{max_gigs} GB max";
104 }
105 
106 ###########################################
107 sub delete_confirm {
108 ###########################################
109   my $item = active_item();
110 
111   my $yes = $CUI->dialog(
112     -title     => "Confirmation required",
113     -buttons   => ['yes', 'no'],
114     -message => "Are you sure you want " .
115              "to delete $item->{file}?",
116     qw( -tbg white -tfg red -bg white
117         -fg red -bbg white -bfg red ));
118   if($yes) {
119     $V->remove($item->{file});
120     redraw();
121   }
122 }
123 
124 ###########################################
125 sub redraw {
126 ###########################################
127   $LBOX->{-values} =
128     [ map { $_->{file} } @{$V->{items}} ];
129 
130   $LBOX->{-labels} = {
131     map { $_->{file} =>
132       ttl_icon($_->{ttl}) . " $_->{file}"
133     } @{$V->{items}}
134   };
135 
136   $LBOX->draw(1);
137   $TOP->text(top_text());
138   $BOTTOM->text(bottom_text());
139 }
140 
141 ###########################################
142 sub keep {
143 ###########################################
144   my $it = active_item();
145   $V->{meta}->{$it->{file}}->{keep} = 1000;
146   $V->meta_save();
147   $V->rescan();
148   redraw();
149 }
150 
151 ###########################################
152 sub active_item {
153 ###########################################
154   return $V->{items}->[
155       $LBOX->get_active_id() ];
156 }

Diesen Artikel als PDF kaufen

Express-Kauf als PDF

Umfang: 5 Heftseiten

Preis € 0,99
(inkl. 19% MwSt.)

Linux-Magazin kaufen

Einzelne Ausgabe
 
Abonnements
 
TABLET & SMARTPHONE APPS
Bald erhältlich
Get it on Google Play

Deutschland

Ähnliche Artikel

  • Episodenfilm

    Ein Perl-Skript mit GTK-2-Oberfläche merkt sich, wie weit sein Anwender gespeicherte Videos angesehen hat, und fährt auf Wunsch an der Stelle der letzten Unterbrechung wieder fort.

  • Mein eigenes Album

    Dateisysteme sind vielen Anwendern und Entwicklern fern - sie sind Angebote, die der Kernel bereitstellt. Dabei lassen sich ohne viel Aufwand mit Fuse und Python anwendungsspezifische Sichten auf den eigenen Datenbestand definieren - etwa ein Fotoalbum.

  • Perl-Snapshot

    Zum Stöbern in vorbeirauschenden Paketen im lokalen Netzwerk leistet Platzhirsch Wireshark gute Dienste. Wer lieber eigene Tools baut, greift auf die Kommandozeilenversion Tshark zurück.

  • Überall Projekte

    Wie erhält der neu gekaufte Laptop schnellstmöglich Kopien aller aktiv genutzten Git-Repositories? Ein Meta-Repository führt eine Projektliste und Perl-Skripte automatisieren alle Aufspür- und Klonvorgänge.

  • Py Side

    Die Bibliothek Py Side kombiniert Python mit den GUI-Fähigkeiten von Qt. Unter anderem bietet sie plattformunabhängige Techniken für Nebenläufigkeit und Netzwerkprogrammierung.

comments powered by Disqus

Ausgabe 04/2017

Digitale Ausgabe: Preis € 6,40
(inkl. 19% MwSt.)

Artikelserien und interessante Workshops aus dem Magazin können Sie hier als Bundle erwerben.