Listbox mit Index
Welcher Eintrag in der Listbox gerade selektiert ist, erkennt die Methode »get_active_id()« des Listbox-Objekts », die den Index des korrespondierenden Listenelements zurückgibt. Das Modul »Videodir.pm« (siehe weiter unten) hält sich eine Datenstruktur, die jedem Eintrag in der Listbox über seine ID die Metadaten seines Videos zuordnet.
Der zweite von der Listbox verarbeitete Event ist »-onchange«. Ihn löst der User aus, wenn er auf einem selektierten Eintrag die [Enter]-Taste betätigt oder mit der Maus auf einen Eintrag klickt. Das ist für »tv« das Signal, dass der User das Video ansehen will. Zeile 67 ruft den Mplayer mittels Backticks und »&« im Hintergrund auf. Das ist wichtig, denn das GUI muss weiter Tastatureingaben verarbeiten, sonst friert es ein.
Zusätzlich zu dieser Callback-Definition der Listbox legt Zeile 38 fest, dass das Betätigen von [Return] die Funktion »selected()« auslöst. Das Makro »KEY_ENTER()« ist im Modul »Curses« definiert und bezeichnet die [Return]- oder [Enter]-Taste. Dank des zuvor definierten Onchange-Eventhandlers der Listbox geschähe dies zwar auch ohne die explizite »set_binding«-Anweisung - denn jede Auswahl eines Listbox-Eintrags löst einen Onchange-Event aus. Doch scheiterte dies, sobald der User denselben Eintrag nochmals auswählt.
Die Zeilen 40 bis 42 belegen weitere Tasten. Des TV-Konsums Müde drücken [Q], und »tv« bricht mit »exit 0« ab. Die Taste [D] löscht über »delete_confirm()« ab Zeile 107 die selektierte Videodatei, ringt dem Benutzer aber zuvor per Dialog eine Bestätigung ab. Verleiht er einer Datei ein Sternchen (»*«), setzt das die Funktion »keep« in Gang, die in der Metadatenbank die Datei-TTL das Löschen verhindernd auf 1000 Tage setzt.
Die Methode »ttl_icon« ab Zeile 48 sorgt dafür, dass das GUI die Videos je nach TTL unterschiedlich darstellt. Ist sie kleiner als null, die Datei also zum Löschen freigegeben, erscheint ein Ausrufezeichen, bei einer TTL kleiner als fünf Tage gar nichts und sonst ein Sternchen. Nach all den Vorbereitungen ist das GUI nun vollständig aufgesetzt.
In Zeile 45 startet die »mainloop« des Moduls Curses::UI::POE, das den POE-Kernel mit dem damit verbundenen Multitasking-Reigen auslöst. Eine reine POE-Anwendung dürfte keine synchronen Plattenzugriffe ausführen. Dass »tv« hin und wieder rasch die Inode-Daten der Videodateien ausliest, geht aber gerade noch an. Das GUI kann so zwar etwas ruckeln, aber nie richtig einfrieren.
Ändert sich etwas im Videoverzeichnis, bekommt der »wake_up_handler()« das beim nächsten periodischen Aufruf, also spätestens nach 60 Sekunden über die in Zeile 91 aufgerufene Methode »rescan()« des Moduls »Videodir.pm« mit. Er frischt dann die innere Datenstruktur des Moduls auf. Diese Daten wandern dank der anschließend aufgerufenen Funktion »redraw()« ab Zeile 125 umgehend in die angezeigte Listbox. Deren »draw()«-Methode zeichnet die grafische Darstellung neu. Da sich eventuell auch die Anzahl der Dateien und deren Plattenplatzbedarf oder sogar der ausgewählte Eintrag ändert, zeichnet »redraw()« die Kopf- und Fußbalken auch gleich neu.

|
Abbildung 3: Der Dialog zum Löschen einer Sendung erscheint nach dem Drücken der Taste [D] und nötigt dem Benutzer eine Bestätigung seines Ansinnens ab.
|
Videoten aller Länder
Das Modul »Videodir.pm« in Listing 2 abstrahiert den Zugriff auf die Videodateien. Das auf »~/tv« eingestellte Verzeichnis enthält nicht nur alle Videofiles, sondern auch eine Datei ».meta«, die deren Verfallsdaten im YAML-Format speichert (Abbildung 4). Unter dem Schlüssel »keep« steht in ».meta«, wie viele volle Tage eine Datei nach ihrer Landung im Verzeichnis erhalten bleibt.
001 ###########################################
002 package Videodir;
003 ###########################################
004 use strict; use warnings;
005 use YAML qw(LoadFile DumpFile);
006 use File::Basename;
007
008 ###########################################
009 sub new {
010 ###########################################
011 my($class, %options) = @_;
012
013 my $self = {
014 dir => "$ENV{HOME}/tv",
015 meta_file => ".meta",
016 keep_default => 5,
017 meta => {},
018 max_gigs => 20,
019 %options };
020
021 $self->{meta_path} =
022 "$self->{dir}/$self->{meta_file}";
023
024 bless $self, $class;
025 $self->rescan();
026 return $self;
027 }
028
029 ###########################################
030 sub rescan {
031 ###########################################
032 my($self) = @_;
033 if(-f $self->{meta_path}) {
034 $self->{meta} =
035 LoadFile($self->{meta_path});
036 }
037
038 $self->{total_size} = 0;
039 my @items = ();
040
041 my $dir = $self->{dir};
042 for my $path (<$dir/*>) {
043
044 next unless -f $path;
045 my $file = basename $path;
046
047 $self->{meta}->{$file}->{keep} =
048 $self->{keep_default} unless defined
049 $self->{meta}->{$file}->{keep};
050
051 my $size = -s $path;
052 $self->{total_size} += $size;
053
054 my $age = age_in_days($path);
055
056 push @items, {
057 file => $file,
058 path => $path,
059 age => $age,
060 size => gb($size),
061 ttl =>
062 $self->{meta}->{$file}->{keep} -
063 $age,
064 };
065 }
066
067 $self->{total_size} =
068 gb($self->{total_size});
069
070 # Delete outdated entries
071 for my $k (keys %{$self->{meta}}) {
072 delete $self->{meta}->{$k} unless
073 -f "$self->{dir}/$k";
074 }
075
076 $self->meta_save();
077
078 # Sort by descending by age
079 $self->{items} = [
080 sort { $a->{age} <=> $b->{age} }
081 @items ];
082 return $self->{items};
083 }
084
085 ###########################################
086 sub gb { # Umrechnen in Gigabytes
087 ###########################################
088 my($val) = @_;
089 return sprintf "%.1f", $val / (1024**3);
090 }
091
092 ###########################################
093 sub remove {
094 ###########################################
095 my($self, $file) = @_;
096
097 my $path = "$self->{dir}/$file";
098 if(-f $path) {
099 unlink $path or
100 die "Cannot unlink $path";
101 }
102 $self->rescan();
103 }
104
105 ###########################################
106 sub age_in_days {
107 ###########################################
108 my($file) = @_;
109
110 return(sprintf "%.1f", (time() -
111 (stat $file)[9]) / 24 / 3600);
112 }
113
114 ###########################################
115 sub shrink {
116 ###########################################
117 my($self) = @_;
118
119 my $deleted = 0;
120 my @doomed = reverse
121 grep { $_->{ttl} < 0 }
122 @{$self->{items}};
123 while($self->{total_size} >
124 $self->{max_gigs}) {
125 last unless @doomed;
126 my $item = shift @doomed;
127 $deleted++;
128 $self->remove($item->{file});
129 }
130 return $deleted;
131 }
132
133 ###########################################
134 sub meta_save {
135 ###########################################
136 my($self) = @_;
137 DumpFile($self->{meta_path},
138 $self->{meta});
139 }
140
141 1;
|

|
Abbildung 4: Die Metadaten in »~/tv/.meta« liegen im YAML-Format vor. Sie speichern das Verfallsdatum der Videodateien.
|
Das im Dateisystem gespeicherte Modifikationsdatum der Datei dient als Zeitstempel. Um die Time to live der Datei zu bestimmen, berechnet »Videodir.pm« in der Funktion »age_in_days« zunächst die Differenz der gegenwärtigen Zeit und der Unix-Mtime der Datei in Tagen. Die TTL ergibt sich dann aus der Differenz des in der Metadatei festgelegten »keep«-Werts (in Tagen) und dem Dateialter (Zeile 62). Der Konstruktor »new« definiert ab Zeile 13 einige Defaultwerte für Konstanten, die der Aufrufer aber überschreiben darf. Erzeugt er zum Beispiel ein Objekt mit »new(max_gigs => 50)«, ist die Speicherplatzobergrenze nicht mehr 20, sondern 50 GByte.
Die Methode »rescan« ab Zeile 30 durchwandert sowohl das Videoverzeichnis (mit dem Glob »/*«, das ».meta« nicht findet) als auch die Metadatei, die »rescan« mit der Funktion »LoadFile()« des YAML-Moduls einliest. Die Methode frischt die unter dem Schlüssel »items« gespeicherte interne Datenstruktur dem aktuellen Zustand entsprechend auf. Jedes Element im Array unter »items« ist eine Referenz auf einen Hash, der Werte zu diesen Schlüsseln enthält:
- »file«: Dateiname
- »path«: absoluter Pfad
- »age«: Alter in Tagen
- »size«: Dateigröße in GByte
-
»ttl«: Zeit in Tagen bis zum Aufheben des
Löschschutzes
Neu entdeckte Dateien erhalten in der Metadatei automatisch einen »keep«-Wert von fünf Tagen (Parameter »keep_default«). Am Ende von »rescan« schreibt »Videodir.pm« die neuen »keep«-Werte mit »meta_save()« in die Metadatei zurück. Die Zeilen 71 bis 74 werfen zuvor Dateien aus der Metadatei, die seit dem letzten Scan von der Festplatte verschwunden sind.
Ist die Plattenplatzgrenze überschritten, löscht die Methode »shrink()« so lange freigegebene Dateien, bis das Videoverzeichnis sich wieder im Rahmen hält. Dabei filtert sie mit »grep« alle Einträge heraus, deren »ttl«-Eintrag kleiner null ist. Da die Einträge im Array unter »->{items}« absteigend nach dem Datum sortiert sind (neue kommen zuerst), dreht »reverse« diese Ergebnisliste um, um die Dateien in der Reihenfolge ihrer Fälligkeit zu sortieren. Ist die Obermarke noch nicht erreicht, kehrt »shrink()« tatenlos zurück und liefert den Wert 0. Dies nutzt die aufrufende Stelle im Skript »tv«, die die dargestellte Listbox nur auffrischen muss, falls tatsächlich Dateien verschwunden sind.
| Whitepaper |
|
Usage Landscape Enterprise Open Source Data Integration
Die Nachfrage nach Datenintegrationslösungen für Unternehmen ist zunehmend gestiegen und vor allem das Interesse an Open Source Technologien wird immer größer. Doch wie und von wem werden Open Source Datenintegrationslösungen genutzt und welches Nutzungsverhalten lässt sich daraus ableiten? Das vorliegende White Paper präsentiert die Erfahrungswerte von über 1000 Open Source Nutzern und liefert fundierte Antworten auf diese Fragen.
Download PDF (Registrierung erforderlich)
|
|
Daten Migration - Eine Publikation von Bloor Research
Datenmigrationsprojekte überschreiten häufig das Budget, neigen zu Verzögerung und werden unter Umständen komplett abgebrochen. Bloor Research ist eines der weltweit führenden IT-Forschungs-, Analyse- und Beratungsunternehmen und wird in dem vorliegenden White Paper die wichtigsten Aspekte dieser Problematik näher beleuchten. Ferner werden praktische Empfehlungen für erfolgreiche Migrationsprojekte gegeben, die Sie auf Ihr nächstes Projekt übertragen können.
Download PDF (Registrierung erforderlich)
|
Dieser Online-Artikel kann Links enthalten, die auf nicht mehr vorhandene Seiten verweisen. Wir ändern solche "broken links"
nur in wenigen Ausnahmefällen. Der Online-Artikel soll möglichst unverändert der gedrucken Fassung entsprechen.
|