summaryrefslogtreecommitdiff
path: root/Tools/master-timer
diff options
context:
space:
mode:
Diffstat (limited to 'Tools/master-timer')
-rw-r--r--Tools/master-timer/LIESMICH106
-rw-r--r--Tools/master-timer/README52
-rw-r--r--Tools/master-timer/Todo10
-rwxr-xr-xTools/master-timer/master-timer.pl1169
-rwxr-xr-xTools/master-timer/process_summary.pl79
-rw-r--r--Tools/master-timer/sample/channels-to-scan8
-rw-r--r--Tools/master-timer/sample/config14
-rw-r--r--Tools/master-timer/sample/deepblack79
-rw-r--r--Tools/master-timer/sample/done1
-rw-r--r--Tools/master-timer/sample/subtitle-movie41
-rw-r--r--Tools/master-timer/sample/torecord32
-rwxr-xr-xTools/master-timer/scan-channels8
12 files changed, 1599 insertions, 0 deletions
diff --git a/Tools/master-timer/LIESMICH b/Tools/master-timer/LIESMICH
new file mode 100644
index 0000000..9694875
--- /dev/null
+++ b/Tools/master-timer/LIESMICH
@@ -0,0 +1,106 @@
+ Master-Timer
+ ============
+
+
+1. Einleitung
+-------------
+
+Master-Timer ist ein System zum automatischen Aufnehmen von Serien und Filmen.
+
+2. Voraussetzungen
+------------------
+
+VDR liefert die "epg.data".
+
+3. Konfigurationsdateien
+------------------------
+
+Alle Konfigurationsdateien liegen unter "<HOME>.master-timer"
+
+config: Eine Ansammlung von Key-Value Paaren. Alle sind "optional" und
+ erhalten dann die angegebenen Default-Werte
+
+(# = Kommentarzeilen)
+marginstart (Default 600)
+ Anzahl der "Sicherheits" Sekunden die ein Timer frueher beginnen soll
+
+marginstop (Default 600)
+ Anzahl der "Sicherheits" Sekunden die ein Timer laenger dauern soll
+
+defaultprio (Default 50)
+ Die Prioritaet die fuer Timer verwendet wird wo keine Prioritaet
+ angegeben ist
+
+DVBCards (Default 1)
+ Anzahl der vorhandenen DVB-Karten (Derzeit nicht verwendet)
+
+Dest-Host (Default "localhost")
+ Host-name oder IP des Rechners auf dem VDR laeuft
+
+Dest-Port (Default "2001")
+ Port der VDR verwendet
+
+jointimers (Default 0)
+ Sollen aufeinanderfolgende Timer auf den gleichen Kanal zusammengefasst
+ werden (0 = "Nein", alles andere "Ja")
+
+debug (Default 0)
+ Debug-Level, die einzelnen Debug-Werte muessen aus folgenden Werten
+ zusammengezaehlt werden
+ 1 : Dump "torecord"
+ 2 : Dump all timers
+ 4 : Show when a timer will be deleted
+ 8 : Dump the "Done" REs
+ 16 : Verbose Config-Reading
+
+deepblack: Eine Liste von Titeln die man NIEMALS NIMMER sehen will
+ Jede Zeile = 1 Titel
+
+subtitle-movies: Eine Liste der "Subtitel" die ein Zeichen fuer einen Film sind
+ (Soweit die von den Sendern richtig ausgefuellt sind.)
+ Jede Zeile = 1 Subtitel
+
+torecord: Die Sachen die man Aufnehmen will
+ Jede Zeile = 1 Timer
+# Format: (Every field is "optional".
+# [Title RE|Subtitle RE|Description RE|Channel-Name|Timeframe|Prio|Timer-Title]
+#
+# To record something at least one of the "Title", "Subtitle" or "Description"
+# Fields has to be provided. This 3 fields are "include" and the rest are
+# "exclude" fields!
+#
+# More than one channel definition can be provided. The delimiter is ";"
+# Additionaly you can make a "blacklist" of Channels when you prepent a "!" to the first Channel Definition
+# The "!" is only tested for the FIRST Channel definition.
+# You can only have a white or a blacklist (Mixing doesn't make sense!)
+#
+# ex. Record the series "Deep Space Nine" on Sci-Fantasy in the timeframe 09:00 - 14:00
+# Deep Space Nine|||Sci-Fantasy|0900-1400|99|DS9
+#
+# Record all "Actionfilm"s with "Schwarzenegger"
+# |Actionfilm|Schwarzenegger
+#
+
+
+done: The titles/subtitles which are already recorded/should not be recorded
+ (Programmed Timers which got inserted into "done" will be deleted
+ automaticaly)
+
+4. Notices
+----------
+
+- Recordings "overlapping" on the same channel, will be joined into one Timer
+- Title/Subtitle/Descriptions are "fixed" for Channel that don't fill them
+ out "correctly" (Currently the "Bugs" from Pro-7/VOX/VIVA)
+ Pro7: Remove the Title from the Subtitle '<Title> / <Subtitle>'
+ VOX/VIVA: Subtitle is enclosed into "" and after ". " is the description
+ VIVA: When the Subtitle beginns with space the subtitle is moved to
+ description
+ All (except the second VIVA one) fixes are tried onto ALL Subtitles.
+
+5. Known-Bugs
+-------------
+
+- It isn't checked if there are enough DVB-Cards
+- Overlapping Timers, on the same channel, are always joined
+- JOINed timers which are "done" don't get deleted automaticaly
diff --git a/Tools/master-timer/README b/Tools/master-timer/README
new file mode 100644
index 0000000..c71e6e1
--- /dev/null
+++ b/Tools/master-timer/README
@@ -0,0 +1,52 @@
+ Master-Timer (w) by Matthias Schniedermeyer (ms@citd.de)
+ ============
+
+
+1. Introduction
+---------------
+
+Master-Timer ist a system for recording Films/Series automaticaly
+
+2. Requierements
+----------------
+
+epg.data
+
+3. Config-Files
+---------------
+
+For all files: One Entry per Line. Each line is a "Regular Expresion"
+ So you can use all Perl-Style REs you want.
+ The RE are matched with "i" so they are case insensitive!
+ (Except for the "done"-list, these must match excatly!)
+
+deepblack: Blacklist of "Titles" you NEVER EVER want to get to you eyes
+
+subtitle-movies: A list of "Subtitles" which indicate a movie.
+ (For Channels that correctly fill out the Subtitle.
+ e.g. it won't work for *eRTL*)
+
+torecord: The titles/subtitles/Description you want to record
+
+done: The titles/subtitles which are already recorded/should not be recorded
+ (Programmed Timers which got inserted into "done" will be deleted
+ automaticaly)
+
+4. Notices
+----------
+
+- Recordings "overlapping" on the same channel, will be joined into one Timer
+- Title/Subtitle/Descriptions are "fixed" for Channel that don't fill them
+ out "correctly" (Currently the "Bugs" from Pro-7/VOX/VIVA)
+ Pro7: Remove the Title from the Subtitle '<Title> / <Subtitle>'
+ VOX/VIVA: Subtitle is enclosed into "" and after ". " is the description
+ VIVA: When the Subtitle beginns with space the subtitle is moved to
+ description
+ All (except the second VIVA one) fixes are tried onto ALL Subtitles.
+
+5. Known-Bugs
+-------------
+
+- It isn't checked if there are enough DVB-Cards
+- Overlapping Timers, on the same channel, are always joined
+- JOINed timers which are "done" don't get deleted automaticaly
diff --git a/Tools/master-timer/Todo b/Tools/master-timer/Todo
new file mode 100644
index 0000000..722ee96
--- /dev/null
+++ b/Tools/master-timer/Todo
@@ -0,0 +1,10 @@
+
+- "Intelligenter" Kanal-Scanner (z.B. nur 1 Kanal fuer ein
+ Sender-"Gruppe")
+- Filtern nach Serie/Film
+- "Komfortable" Anzeige, mit Black & Whitelisten, fuer Genres/Titeln usw.
+- Unterstueztung von 1xVDR pro Karte
+- Abspielen (mit automatischen "killen" des "Frontend"-VDRs) von
+ Aufzeichnungen
+- "View"-Timer d.h. Timer der nicht Aufnimmt sondern nur den Kanal aendert
+- "unwichtige" Timer "verdraengen" wenn andere Aufnahmen anstehen.
diff --git a/Tools/master-timer/master-timer.pl b/Tools/master-timer/master-timer.pl
new file mode 100755
index 0000000..3b98acc
--- /dev/null
+++ b/Tools/master-timer/master-timer.pl
@@ -0,0 +1,1169 @@
+#!/usr/bin/perl -w
+
+use strict;
+# For the TCP-Connection to VDR
+use Socket;
+# For converting the Timers, read from VDR, back to Unix-Timestamps
+use Time::Local;
+
+# Debugmode
+# You have to add the following numbers to build the debug-var
+# 1 : Dump the "torecord"
+# 2 : Dump all timers
+# 4 : Show when a timer will be deleted
+# 8 : Dump the "Done" REs
+# 16 : Verbose Config-Reading
+my $debug = 0;
+
+# The Supervariable Program
+# %Program{$title}{$channel}{$time}{duration}
+# {subtitle}
+# {description}
+
+# The Supervariable Timer
+# %Timer{$time}{$channel}{$title}{duration}
+# {subtitle}
+# {prio}
+# {real_title}
+# {VDR} (Already programmed)
+# The Value of VDR is ">0" for the position in the Timer-List or "R" for a "Repeating" Timer.
+# A Value of >1.000.000 is a Master Timer-Timer which is already programmed into VDR
+
+# Variable-Definition
+my (%Program, @channels, %channels, %Timer);
+
+# Which Subtitles are Movies
+my ($subtitle_movie);
+
+# Blacklist
+my ($title_deepblack);
+
+# What is already recorded/Should not be recorded
+my ($title_done, $subtitle_done);
+
+# What to record
+my ($title_torecord, $subtitle_torecord, $description_torecord, @title_torecord, @subtitle_torecord, @description_torecord, @channel_torecord, @timeframe_torecord, @prio_torecord, @timer_title_torecord, $num_torecord, @marginstart_torecord, @marginstop_torecord, @machine_torecord);
+
+# Default Priority for Timers (Config: defaultprio)
+my $default_prio = 50;
+
+# How many DVB-S cards are there (Config: DVBCards)
+my $DVB_cards = 1;
+
+# How many seconds to substract from the time and to add to the duration
+my $marginstart = 60*10; # Config: Marginstart
+my $marginstop = 60*10; # Config: Marginstop
+
+# Shall Timers, on the same channel, be joined if they overlap
+my $jointimers = 0;
+
+# Hostname/IP of DVB-Computer and the Port of VDR
+my @Dest = ("localhost:2001"); # Config: Dest
+
+# Which VDR-Instance shall be used
+my $currentVDR = 1;
+
+# Working-Variables
+my ($title, $duration, $subtitle, $channel, $time, $description, $hit);
+my (@time, @date);
+
+sub sub_die
+ {
+ my ($error) = @_;
+ &closesocket();
+ die "$error";
+ }
+
+
+if ($ARGV[0])
+ {
+ $currentVDR = $ARGV[0];
+ }
+
+&init();
+&dumpdone() if ($debug & 8);
+&dumptorecord() if ($debug & 1);
+&fetchVDRTimers();
+&process_torecord();
+print "Timers before joining\n" if ($debug & 2 && $jointimers);
+&dumptimers() if ($debug & 2);
+if ($jointimers)
+ {
+ &jointimers();
+ print "Timers after joining\n" if ($debug & 2);
+ &dumptimers() if ($debug & 2);
+ }
+
+&printtimers();
+&transfertimers();
+&closesocket();
+
+#
+# Subfunctions
+#
+
+sub dumpdone()
+ {
+ print "Start Done-dump\n";
+ print "Titledone: \"$title_done\"\n";
+ print "Subtitledone \"$subtitle_done\"\n";
+ print "End Done-dump\n";
+ }
+
+sub dumptorecord()
+ {
+ print "Start Torecord-dump\n";
+ print "Regex-Title: $title_torecord\n";
+ print "Regex-Subtitle: $subtitle_torecord\n";
+ print "Regex-Description: $description_torecord\n";
+ foreach my $num (0 .. $num_torecord)
+ {
+ print "Timer Number $num: ";
+
+ print "Title: \"$title_torecord[$num]\" " if ($title_torecord[$num]);
+ print "Title: \"\" " unless ($title_torecord[$num]);
+
+ print "Subtitle: \"$subtitle_torecord[$num]\" "if ($subtitle_torecord[$num]);
+ print "Subtitle: \"\" " unless ($subtitle_torecord[$num]);
+
+ print "Description: \"$description_torecord[$num]\" " if ($description_torecord[$num]);
+ print "Description: \"\" " unless ($description_torecord[$num]);
+
+ print "Timeframe: \"$timeframe_torecord[$num]\" " if ($timeframe_torecord[$num]);
+ print "Timeframe: \"\" " unless ($timeframe_torecord[$num]);
+
+ print "Channel: \"". join (";",@{$channel_torecord[$num]})."\" " if ($channel_torecord[$num]);
+ print "Channel: \"\" " unless ($channel_torecord[$num]);
+
+ print "Prio: \"$prio_torecord[$num]\" " if ($prio_torecord[$num]);
+ print "Prio: \"\" " unless ($prio_torecord[$num]);
+
+ print "Timertitle: \"$timer_title_torecord[$num]\" " if ($timer_title_torecord[$num]);
+ print "Timertitle: \"\" " unless ($timer_title_torecord[$num]);
+
+ print "Marginstart: \"$marginstart_torecord[$num]\" " if ($marginstart_torecord[$num]);
+ print "Marginstart: \"\" " unless ($marginstart_torecord[$num]);
+
+ print "Marginstop: \"$marginstop_torecord[$num]\" " if ($marginstop_torecord[$num]);
+ print "Marginstop: \"\" " unless ($marginstop_torecord[$num]);
+
+ print "Machine: \"$machine_torecord[$num]\" " if ($machine_torecord[$num]);
+ print "Machine: \"\" " unless ($machine_torecord[$num]);
+
+ print "\n";
+ }
+ print "End Torecord-dump\n";
+ }
+
+sub dumptimers()
+ {
+ print "Start Timers-dump\n";
+ foreach $time (sort {$a <=> $b} keys %Timer)
+ {
+ foreach $channel (sort keys %{%Timer->{$time}})
+ {
+ foreach $title (sort keys %{%Timer->{$time}->{$channel}})
+ {
+ my ($prio, @time, @date, @time2);
+ my ($realtitle);
+ @time = &GetTime ($time);
+ @date = &GetDay ($time);
+ @time2 = &GetTime ($time + $Timer{$time}{$channel}{$title}{duration});
+ $subtitle = $Timer{$time}{$channel}{$title}{subtitle};
+ $prio = $Timer{$time}{$channel}{$title}{prio};
+ $realtitle = $Timer{$time}{$channel}{$title}{real_title};
+ print "2:$channels{$channel}{number}:$date[1]:$time[0]$time[1]:$time2[0]$time2[1]:$prio:99:$title:Title: \"$realtitle\"||Subtitle: \"$subtitle\":$Timer{$time}{$channel}{$title}{VDR}\n";
+ }
+ }
+ }
+ print "End Timers-dump\n";
+ }
+
+sub printtimers()
+ {
+ foreach $time (sort {$a <=> $b} keys %Timer)
+ {
+ foreach $channel (sort keys %{%Timer->{$time}})
+ {
+ foreach $title (sort keys %{%Timer->{$time}->{$channel}})
+ {
+ my ($prio, @time, @date, @time2);
+ if ($Timer{$time}{$channel}{$title}{VDR} eq 0)
+ {
+ my ($realtitle);
+ @time = &GetTime ($time);
+ @date = &GetDay ($time);
+ @time2 = &GetTime ($time + $Timer{$time}{$channel}{$title}{duration});
+ $subtitle = $Timer{$time}{$channel}{$title}{subtitle};
+ $prio = $Timer{$time}{$channel}{$title}{prio};
+ $realtitle = $Timer{$time}{$channel}{$title}{real_title};
+
+ print "2:$channels{$channel}{number}:$date[1]:$time[0]$time[1]:$time2[0]$time2[1]:$prio:99:$title:Title: \"$realtitle\"||Subtitle: \"$subtitle\"\n";
+ }
+ }
+ }
+ }
+ }
+
+sub transfertimers()
+ {
+ foreach $time (sort {$a <=> $b} keys %Timer)
+ {
+ foreach $channel (sort keys %{%Timer->{$time}})
+ {
+ foreach $title (sort keys %{%Timer->{$time}->{$channel}})
+ {
+ my ($prio, @time, @date, @time2, $realtitle, $result);
+ if ($Timer{$time}{$channel}{$title}{VDR} eq 0)
+ {
+ @time = &GetTime ($time);
+ @date = &GetDay ($time);
+ @time2 = &GetTime ($time + $Timer{$time}{$channel}{$title}{duration});
+ $subtitle = $Timer{$time}{$channel}{$title}{subtitle};
+ $prio = $Timer{$time}{$channel}{$title}{prio};
+ $realtitle = $Timer{$time}{$channel}{$title}{real_title};
+
+ ($result) = GetSend ("newt 2:$channels{$channel}{number}:$date[1]:$time[0]$time[1]:$time2[0]$time2[1]:$prio:99:$title:Title: \"$realtitle\"||Subtitle: \"$subtitle\"");
+ print "Timer: $result" if ($debug & 2);
+ }
+ }
+ }
+ }
+ }
+
+# Convert the Unix-Time-Stamp into "month" and "Day of month"
+sub GetDay
+ {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(shift);
+ $mon++;
+ $mon = sprintf ("%02i",$mon);
+ $mday = sprintf ("%02i",$mday);
+ return ($mon, $mday);
+ }
+
+# Convert the Unix-Time-Stramp into "hour" and "minute"
+sub GetTime
+ {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(shift);
+ $hour = sprintf ("%02i",$hour);
+ $min = sprintf ("%02i",$min);
+ return ($hour, $min);
+ }
+
+# Workaround some EPG-Bugs
+sub correct_epg_data
+ {
+ if ($subtitle)
+ {
+ # For Pro-7. Remove $title from $subtitle
+ $subtitle =~ s/$title\s\/\s//;
+
+ # For VOX & VIVA. The Format it '"<Subtitle>". <Description>'
+ if ($subtitle =~ /^\"(.*?)\"\.\s(.*)/)
+ {
+ # Lets see if there are Channels that where the VOX/VIVA scheme matches, but also have a description
+ if ($description)
+ {
+ sub_die ("Subtitle: \"$subtitle\"\nDescription\"$description\"\n");
+ }
+ $subtitle = $1;
+ $description = $2;
+ }
+ elsif ($channel eq "VIVA")
+ {
+ if ($subtitle =~ /^\s(.*)/)
+ {
+ $subtitle = "";
+ $description = $1;
+ }
+ }
+ }
+
+ # Workaround for the broken PRO-7/Kabel-1 EPG-Date. If Time is between 00.00 and 05.00 the time is shifted forward by a day
+ if ($channel eq "Pro-7" || $channel eq "Kabel-1")
+ {
+ my (@time);
+ @time = GetTime ($time);
+ if ($time[0] >= 0 && ($time[0] <= 4 || ($time[0] == 5 && $time[1] == 0)))
+ {
+ $time += 24*60*60;
+ }
+ }
+}
+
+# Add a Recording into the "to record"-List
+sub addtimer
+ {
+ my ($hit, $title, $realtitle, $subtitle, $channel, $time, $duration, $prio, $VDR, $time2, $title2, $channel2, $marginstart, $marginstop);
+ ($title, $realtitle, $subtitle, $channel, $time, $duration, $prio, $VDR, $marginstart, $marginstop) = @_;
+# print "Title: \"$title\" Realtitle: \"$realtitle\" Subtitle: \"$subtitle\" Channel: \"$channel\" Time: \"$time\" Duration: \"$duration\" Prio: \"$prio\" VDR: \"$VDR\"\n";
+
+ $hit = 1;
+
+ foreach $time2 (sort keys %Timer)
+ {
+ foreach $title2 (sort keys %{%Timer->{$time2}->{$channel}})
+ {
+ my ($ctime, $ctime2);
+ $ctime = $time2;
+ $ctime2 = $time2 + $Timer{$time2}{$channel}{$title2}{duration};
+
+ if (($time >= $ctime) && ($time <= $ctime2))
+ {
+ undef $hit;
+ }
+ }
+ }
+
+
+ if ($hit)
+ {
+ $time -= $marginstart;
+ $duration += $marginstart + $marginstop;
+ $Timer{$time}{$channel}{$title}{duration}=$duration;
+ $Timer{$time}{$channel}{$title}{subtitle}=$subtitle;
+ $Timer{$time}{$channel}{$title}{prio}=$prio;
+ $Timer{$time}{$channel}{$title}{VDR}=$VDR;
+ $Timer{$time}{$channel}{$title}{real_title}=$realtitle;
+ }
+ }
+
+sub deltimer()
+ {
+ my ($time, $channel, $title, $delete_from_VDR);
+ ($time, $channel, $title, $delete_from_VDR) = @_;
+
+# if ($delete_from_VDR)
+# {
+# if ($Timer{$time}{$channel}{$title}{VDR})
+# {
+# if ($Timer{$time}{$channel}{$title}{VDR} =~ s/ ^R/)
+# {
+# print "Error: A Repeating-Timer can't be deleted from VDR: \"$title\"\n";
+# }
+# elsif ($Timer{$time}{$channel}{$title}{VDR} < 1000000)
+# {
+# print "A User-Programmed Timer has been deleted from VDR: \"$title\"\n";
+# }
+# else
+# {
+#
+# }
+# }
+# }
+
+ delete $Timer{$time}{$channel}{$title}{duration};
+ delete $Timer{$time}{$channel}{$title}{subtitle};
+ delete $Timer{$time}{$channel}{$title}{prio};
+ delete $Timer{$time}{$channel}{$title}{VDR};
+ delete $Timer{$time}{$channel}{$title}{real_title};
+ delete $Timer{$time}{$channel}{$title};
+ delete $Timer{$time}{$channel} if (keys %{ $Timer{$time}{$channel} } == 1);
+ delete $Timer{$time} if (keys %{ $Timer{$time} } == 1);
+ }
+
+sub jointimers
+ {
+ #
+ # FIXME: 2 Timers on the same channel will always be joined.
+ # It should be checked if there is another DVB-Card available.
+ #
+ # FIXME2: When one timer is already programmed in VDR, delete that timer in VDR.
+ my ($running, $counter, @times, $channel, $title, $channel2, $title2);
+ $running = 1;
+ outer: while ($running)
+ {
+ $counter = 0;
+ @times = sort {$a <=> $b} keys %Timer;
+
+ # We only need to check till the second last timer. The last one can't have a overlapping one.
+ while ($counter < $#times)
+ {
+ foreach $channel (sort keys %{%Timer->{$times[$counter]}})
+ {
+ foreach $title (sort keys %{%Timer->{$times[$counter]}->{$channel}})
+ {
+ if ($times[$counter + 1] < ($times[$counter] + $Timer{$times[$counter]}{$channel}{$title}{duration}))
+ {
+ foreach $channel2 (sort keys %{%Timer->{$times[$counter + 1]}})
+ {
+ foreach $title2 (sort keys %{%Timer->{$times[$counter + 1]}->{$channel}})
+ {
+ if ($channel eq $channel2)
+ {
+ my ($duration, $subtitle, $prio, $realtitle, $duration2, $subtitle2, $prio2, $realtitle2);
+ # Values from Lower-Timer
+ $duration = $Timer{$times[$counter]}{$channel}{$title}{duration};
+ $subtitle = $Timer{$times[$counter]}{$channel}{$title}{subtitle};
+ $prio = $Timer{$times[$counter]}{$channel}{$title}{prio};
+ $realtitle = $Timer{$times[$counter]}{$channel}{$title}{real_title};
+
+ # Values from Higher-Timer
+ $duration2 = $Timer{$times[$counter + 1]}{$channel2}{$title2}{duration};
+ $subtitle2 = $Timer{$times[$counter + 1]}{$channel2}{$title2}{subtitle};
+ $prio2 = $Timer{$times[$counter + 1]}{$channel2}{$title2}{prio};
+ $realtitle2 = $Timer{$times[$counter + 1]}{$channel2}{$title2}{real_title};
+
+ # Use the Higher Priority for the new Timer
+ $prio = ($prio > $prio2) ? $prio : $prio2;
+
+ # Delete the two "Obsolet" Timers
+ &deltimer ($times[$counter], $channel, $title);
+ &deltimer ($times[$counter + 1], $channel2, $title2);
+
+ # And set the new one
+ &addtimer ("$title + $title2", "$realtitle\~$realtitle2", "$subtitle\~$subtitle2", $channel, $times[$counter], $duration2 + ($times[$counter + 1 ] - $times[$counter]),$prio,0,0,0);
+
+ # Now a Value is "missing", so we will redo the whole thing. (This will do three-times JOIN correct)
+ redo outer;
+ }
+ }
+ }
+ }
+ }
+ }
+ $counter++;
+ }
+ undef $running;
+ }
+ }
+
+sub process_torecord
+ {
+ my ($first_hit, $prio, $timer_title);
+ foreach $title (sort keys %Program)
+ {
+ foreach $channel (sort keys %{%Program->{$title}})
+ {
+ foreach $time (sort {$a <=> $b} keys %{%Program->{$title}->{$channel}})
+ {
+ undef $hit;
+
+ # First look if any of the Title/Subtitle/Description REs match
+ if ($title =~ /$title_torecord/i)
+ {
+ $hit = 1;
+ }
+ elsif ($Program{$title}{$channel}{$time}{subtitle} && $Program{$title}{$channel}{$time}{subtitle} =~ /$subtitle_torecord/i)
+ {
+ $hit = 1;
+ }
+ elsif ($Program{$title}{$channel}{$time}{description} && $Program{$title}{$channel}{$time}{description} =~ /$description_torecord/i)
+ {
+ $hit = 1;
+ }
+
+ # Now look if we have a "exact" hit
+ if ($hit)
+ {
+ my ($counter);
+ undef $hit;
+ foreach $counter (0 .. $num_torecord)
+ {
+
+ if ($title_torecord[$counter])
+ {
+ if (!($title =~ /$title_torecord[$counter]/i))
+ {
+ next;
+ }
+ }
+
+ if ($subtitle_torecord[$counter])
+ {
+ if (!($Program{$title}{$channel}{$time}{subtitle} =~ /$subtitle_torecord[$counter]/i))
+ {
+ next;
+ }
+ elsif (!$title_torecord[$counter] && !$description_torecord[$counter])
+ {
+ next;
+ }
+ }
+
+ if ($description_torecord[$counter])
+ {
+ if ($Program{$title}{$channel}{$time}{description})
+ {
+ if (!($Program{$title}{$channel}{$time}{description} =~ /$description_torecord[$counter]/i))
+ {
+ next;
+ }
+ }
+ elsif (!$title_torecord[$counter] && !$subtitle_torecord[$counter])
+ {
+ next;
+ }
+ }
+
+ if ($channel_torecord[$counter])
+ {
+ my ($hit);
+ # Blacklist-Mode
+ if ($channel_torecord[$counter][0] =~ /^!/)
+ {
+ $hit = 1;
+ foreach (0 .. $#{$channel_torecord[$counter]})
+ {
+ # Strip a possibel "!" Charactar
+ $channel_torecord[$counter][$_] =~ /^!?(.*)/;
+ if ($channel =~ /^$1$/)
+ {
+ undef $hit;
+ last;
+ }
+ }
+ }
+ # Whitelist-Mode
+ else
+ {
+ undef $hit;
+ foreach (0 .. $#{$channel_torecord[$counter]})
+ {
+ # Strip a possibel "!" Charactar
+ $channel_torecord[$counter][$_] =~ /^!?(.*)/;
+ if ($channel =~ /^$1$/)
+ {
+ $hit = 1;
+ last ;
+ }
+ }
+ }
+ if (!$hit)
+ {
+ next;
+ }
+ }
+
+ if ($timeframe_torecord[$counter])
+ {
+ my (@time, $time2, $ctime, $ctime2);
+ @time = GetTime($time);
+ $time2 = "$time[0]$time[1]";
+
+ ($ctime, $ctime2) = split (/\-/,$timeframe_torecord[$counter]);
+
+ if (!$ctime)
+ {
+ $ctime = "0";
+ }
+ if (!$ctime2)
+ {
+ $ctime2 = "2400";
+ }
+
+ if ($ctime < $ctime2)
+ {
+ if (!($time2 >= $ctime && $time2 <= $ctime2))
+ {
+ next;
+ }
+ }
+ else
+ {
+ if (!(($time2 >= $ctime && $time2 <= "2400") || ($time2 >= "0" && $time2 <= $ctime2)))
+ {
+ next;
+ }
+ }
+ }
+
+ if ($prio_torecord[$counter])
+ {
+ $prio = $prio_torecord[$counter];
+ }
+ else
+ {
+ $prio = 50;
+ }
+
+ # What Title to use for the timer
+ if ($timer_title_torecord[$counter])
+ {
+ $timer_title = $timer_title_torecord[$counter]
+ }
+ elsif ($title_torecord[$counter])
+ {
+ $timer_title = $title_torecord[$counter]
+ }
+ else
+ {
+ $timer_title = $title;
+ }
+
+ my ($subtitle);
+ if ($Program{$title}{$channel}{$time}{subtitle})
+ {
+ $subtitle = $Program{$title}{$channel}{$time}{subtitle};
+ }
+ else
+ {
+ $subtitle = "";
+ }
+
+ &addtimer ($timer_title,$title,$subtitle,$channel,$time,$Program{$title}{$channel}{$time}{duration},$prio,0,$marginstart_torecord[$counter],$marginstop_torecord[$counter]);
+ last;
+ }
+ }
+ }
+ }
+ }
+ }
+
+# Open the connection to VDR
+sub initsocket
+ {
+ my ($Dest, $Port) = split (/\:/,$Dest[$currentVDR - 1],2);
+ my $iaddr = inet_aton($Dest);
+ my $paddr = sockaddr_in($Port, $iaddr);
+
+ socket(SOCKET, PF_INET, SOCK_STREAM, getprotobyname('tcp'));
+ connect(SOCKET, $paddr) or sub_die ("Can't connect to VDR\n");
+ select(SOCKET); $| = 1;
+ select(STDOUT);
+
+ while (<SOCKET>) {
+ last if substr($_, 3, 1) ne "-";
+ }
+ }
+
+# Send a command to VDR and read back the result
+sub GetSend
+ {
+ my ($command, @retval);
+
+ while ($command = shift)
+ {
+ print SOCKET "$command\r\n";
+ while (<SOCKET>) {
+ (@retval) = (@retval, $_);
+ last if substr($_, 3, 1) ne "-";
+ }
+ }
+
+ foreach my $retval (@retval)
+ {
+ $retval =~ s/\x0d//g;
+ }
+ return (@retval);
+ }
+
+# Close the socket to VDR
+sub closesocket
+ {
+ print SOCKET "Quit\r\n";
+ close(SOCKET);
+ }
+
+
+# Fetch the timers-List from VDR via SVDR and process it.
+sub fetchVDRTimers
+ {
+ my (@timers, $timer, $position, $active, $channel, $day, $start, $end, $prio, $ttl, $title, $subtitle, $minute, $duration);
+ my ($utime, $utime2);
+
+ # First fetch the timers-list from VDR
+ @timers = GetSend ("lstt");
+
+ foreach $timer (@timers)
+ {
+# $timer =~ s/\x0d//g;
+ chomp $timer;
+ # a Valid Timer-line beginns with "250"
+ if ($timer =~ s/250-|250\s//)
+ {
+ # Extract the Position in front of the line
+ ($position, $timer) = split (/\s/,$timer,2);
+
+# print "Position: \"$position\" Timer: \"$timer\"\n";
+ # Split the : seperated values
+ ($active, $channel, $day, $start, $end, $prio, $ttl, $title, $subtitle) = split (/\:/,$timer,9);
+
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
+
+ # If the string is exactly 7 char wide, then its a "repeating"-timer
+ if ($active >= 1)
+ {
+ if ($day =~ /(.)(.)(.)(.)(.)(.)(.)/)
+ {
+ my (@days);
+ @days = ($1, $2, $3, $4, $5, $6, $7);
+ ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
+
+ $start =~ /(\d\d)(\d\d)/;
+ $hour = $1;
+ $minute = $2;
+ $utime = timelocal 0, $minute, $hour, $mday, $mon, $year;
+ $end =~ /(\d\d)(\d\d)/;
+ $hour = $1;
+ $minute = $2;
+ $utime2 = timelocal 0, $minute, $hour, $mday, $mon, $year;
+ if ($end < $start)
+ {
+ $utime2 += 24*60*60;
+ }
+ $duration = $utime2 - $utime;
+
+ # "Normalize" the timestamp to monday
+ $utime = $utime - ($wday * 24 * 60 *60);
+
+ foreach my $num (0 .. $#days)
+ {
+ if ($days[$num] ne "-")
+ {
+ my $utime3;
+ # Todays before today will be shifted in the next week
+ if (($num + 1) < $wday)
+ {
+ $utime3 = $utime + (($num + 7 + 1) * 24 * 60 * 60);
+ }
+ else
+ {
+ $utime3 = $utime + (($num + 1) * 24 * 60 * 60);
+ }
+ &addtimer ($title,$title,$subtitle,$channels[$channel],$utime3,$duration,$prio,"R$position",0,0);
+ }
+ }
+ }
+
+ # When the Day-Value is between 1 and 31, then its a "One time" Timer
+ elsif (($day >= 1) && ($day <= 31))
+ {
+ if ($active == "2")
+ {
+ $position += 1000000;
+ }
+ # When the Day is before the Current-Day, then the Timer is for the next month
+ if ($day < $mday)
+ {
+ $mon++;
+ if ($mon == 12)
+ {
+ $mon = 0;
+ $year ++;
+ }
+ }
+ $start =~ /(\d\d)(\d\d)/;
+ $hour = $1;
+ $minute = $2;
+ $utime = timelocal 0, $minute, $hour, $day, $mon, $year;
+ $end =~ /(\d\d)(\d\d)/;
+ $hour = $1;
+ $minute = $2;
+ $utime2 = timelocal 0, $minute, $hour, $day, $mon, $year;
+ if ($end < $start)
+ {
+ $utime2 += 24*60*60;
+ }
+ $duration = $utime2 - $utime;
+
+ &addtimer ($title,$title,$subtitle,$channels[$channel],$utime,$duration,$prio,$position,0,0);
+ }
+ }
+ }
+ }
+ }
+
+# Parse file "epg.data"
+sub initepgdata
+ {
+ open (FI,"epg.data") or sub_die ("Can't open file \"epg.data\"\n");
+
+ while (<FI>)
+ {
+ # Begin Channel
+ if (/^C\s(\d+)\s+(.+)/)
+ {
+ $channel = $2;
+ while (<FI>)
+ {
+ # End Channel
+ if (/^c$/)
+ {
+ last;
+ }
+ # Begin Timer
+ elsif (/^E\s(\d+)\s+(\d+)\s+(\d+)$/)
+ {
+ # Undef this Variables because it is possibel that not every timer uses this values
+ undef $duration;
+ undef $subtitle;
+ undef $description;
+
+ $time=$2;
+ $duration=$3;
+ }
+ # Title
+ elsif (/^T\s(.*)/)
+ {
+ $title = $1;
+ }
+ # Subtitle
+ elsif (/^S\s(.*)/)
+ {
+ $subtitle=$1;
+ }
+ # Description
+ elsif (/^D\s(.*)/)
+ {
+ $description=$1;
+ }
+ # End Timer
+ elsif (/^e$/)
+ {
+ # Only accept timers that are in the future
+ if ($time < time)
+ {
+ next;
+ }
+
+ # Work around the diffrent Bugs in the data
+ &correct_epg_data();
+
+ # Check if the title is in the DEEP-Blacklist
+ if ($title =~ /$title_deepblack/i)
+ {
+ next;
+ }
+
+ # Check if the Title & Subtitle is in the Done-List
+ if ($title =~ /$title_done/)
+ {
+ if ($subtitle)
+ {
+ if ($subtitle =~ /$subtitle_done/)
+ {
+ next;
+ }
+ }
+ }
+
+ $Program{$title}{$channel}{$time}{duration}=$duration;
+ if ($subtitle)
+ {
+ $Program{$title}{$channel}{$time}{subtitle}=$subtitle;
+ }
+ if ($description)
+ {
+ $Program{$title}{$channel}{$time}{description}=$description;
+ }
+ }
+ }
+ }
+ }
+ close (FI);
+ }
+
+# What is a Movie (When correctly stored into Subtitle)
+sub initmovie
+ {
+ my (@list,$list);
+ open (FI,"$ENV{HOME}/.master-timer/subtitle-movie") or return;
+ @list = <FI>;
+ close(FI);
+
+ foreach $list (@list)
+ {
+ chomp $list;
+ }
+ $subtitle_movie = join ('|',@list);
+ }
+
+# What should be blacklistet
+sub initblacklist
+ {
+ my (@list,$list);
+ if (open (FI,"$ENV{HOME}/.master-timer/deepblack"))
+ {
+ @list = <FI>;
+ close(FI);
+
+ foreach $list (@list)
+ {
+ chomp $list;
+ }
+ $title_deepblack = join ('|',@list);
+ }
+ else
+ {
+ $title_deepblack = "^\$";
+ }
+ }
+
+# What is already recorded/Should not be recorded
+sub initdone
+ {
+ my (@list,$list, %title_done, %subtitle_done, $title_temp, $subtitle_temp);
+ if (open (FI,"$ENV{HOME}/.master-timer/done"))
+ {
+ @list = <FI>;
+ close (FI);
+
+ foreach $list (@list)
+ {
+ chomp $list;
+ ($title_temp,$subtitle_temp) = split (/\|/,$list);
+ if ($title_temp)
+ {
+ $title_done{"^$title_temp\$"} = 1;
+ }
+ if ($subtitle_temp)
+ {
+ $subtitle_done{"^$subtitle_temp\$"} = 1;
+ }
+ }
+ $title_done = join ('|',sort keys %title_done);
+ $subtitle_done = join ('|',sort keys %subtitle_done);
+
+ # Ein paar Zeichen Escapen
+ $title_done =~ s/\?/\\\?/g;
+ $title_done =~ s/\+/\\\+/g;
+ $subtitle_done =~ s/\?/\\\?/g;
+ $subtitle_done =~ s/\+/\\\+/g;
+
+ # Now delete Timers in VDR that are already in the done-List
+ my ($position, $timer, $active, $g, $title, $subtitle, $counter, @todel);
+ $counter = 0;
+ @list = GetSend ("LSTT");
+
+ foreach $timer (@list)
+ {
+# $timer =~ s/0x0d//g;
+ chomp $timer;
+ if ($timer =~ s/250-|250\s//)
+ {
+ ($position, $timer) = split (/\s/,$timer,2);
+ # Split the : seperated values
+ ($active, $g, $g, $g, $g, $g, $g, $title, $subtitle) = split (/\:/,$timer,9);
+ if ($active == 2)
+ {
+ # Title: "Shakespeare in Love"||Subtitle: "Romanze"
+ my ($ctitle, $csubtitle);
+ if ($subtitle && $subtitle =~ /^Title\:\s\"(.*)\"\|\|Subtitle\:\s\"(.*)\"/)
+ {
+ $title = $1;
+ $subtitle = $2;
+ if ($subtitle)
+ {
+ my (@titles, @subtitles, $num, $hit);
+ undef $hit;
+ @titles = split (/\~/,$title);
+ @subtitles = split (/\~/,$subtitle);
+ foreach $num (0 .. $#titles)
+ {
+ if ($titles[$num] =~ /$title_done/ && $subtitles[$num] =~ /$subtitle_done/)
+ {
+ $hit = 1;
+ }
+ else
+ {
+ undef $hit;
+ last;
+ }
+ }
+
+ if ($hit)
+ {
+ my ($result);
+ print "Delete Timer: $title $subtitle\n" if ($debug & 4);
+ $position -= $counter;
+ ($result) = GetSend ("DELT $position");
+ print "Result: $result" if ($debug & 4);
+ if ($result =~ /^250/)
+ {
+ $counter++;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+# What should be recorded
+sub inittorecord
+ {
+ my (@list, $list, $title, $subtitle, $description, $channel, $timeframe, $prio, $timer_title, $margin, $machine, @title_list, @subtitle_list, @description_list);
+ my $counter = 0;
+ open (FI,"$ENV{HOME}/.master-timer/torecord") or sub_die ("Can't open file \"torecord\"\n");
+ @list = <FI>;
+ close(FI);
+
+ foreach $list (0 .. $#list)
+ {
+ chomp $list[$list];
+ if ($list[$list] && !($list[$list] =~ /^\#/))
+ {
+ ($title, $subtitle, $description, $channel, $timeframe, $prio, $timer_title, $margin, $machine) = split (/\|/,$list[$list]);
+
+ # Accept torecord only if it is for the current machine
+ if ((!$machine && $currentVDR == 1) || $machine == $currentVDR)
+ {
+ if ($title)
+ {
+ $title_torecord[$counter] = $title;
+ $title_list[$#title_list + 1] = $title;
+ }
+ if ($subtitle)
+ {
+ $subtitle_torecord[$counter] = $subtitle;
+ $subtitle_list[$#subtitle_list + 1] = $subtitle;
+ }
+ if ($description)
+ {
+ $description_torecord[$counter] = $description;
+ $description_list[$#description_list + 1] = $description;
+ }
+ if ($channel)
+ {
+ my (@temp);
+ @temp = split (/\;/,$channel);
+ foreach (0 .. $#temp)
+ {
+ $channel_torecord[$counter][$_] = $temp[$_];
+ }
+ }
+ if ($timeframe)
+ {
+ $timeframe_torecord[$counter] = $timeframe;
+ }
+ if ($prio)
+ {
+ $prio_torecord[$counter] = $prio;
+ }
+ else
+ {
+ $prio_torecord[$counter] = $default_prio;
+ }
+ if ($timer_title)
+ {
+ $timer_title_torecord[$counter] = $timer_title;
+ }
+ if ($margin)
+ {
+ my ($start, $stop);
+ ($start, $stop) = split (/;/,$margin, 2);
+ $marginstart_torecord[$counter] = $start if ($start);
+ $marginstop_torecord[$counter] = $stop if ($stop);
+ }
+ # Set Default-Margins if not margins defined
+ $marginstart_torecord[$counter] = $marginstart if (!$marginstart_torecord[$counter]);
+ $marginstop_torecord[$counter] = $marginstop if (!$marginstop_torecord[$counter]);
+ $counter++;
+ }
+ }
+ }
+
+ $num_torecord = $counter - 1;
+
+ $title_torecord = join ('|',@title_list);
+ $subtitle_torecord = join ('|',@subtitle_list);
+ $description_torecord = join ('|',@description_list);
+
+ if (!$title_torecord)
+ {
+ $title_torecord = "^Dieseshierwirdgarantiertnieundnimmeraufirgendetwassinnvollesmatchen\$";
+ }
+ if (!$subtitle_torecord)
+ {
+ $subtitle_torecord = "^Dieseshierwirdgarantiertnieundnimmeraufirgendetwassinnvollesmatchen\$";
+ }
+ if (!$description_torecord)
+ {
+ $description_torecord = "^Dieseshierwirdgarantiertnieundnimmeraufirgendetwassinnvollesmatchen\$";
+ }
+ }
+
+# Parse the "channels.conf" of VDR
+sub initchannellist
+ {
+ my ($counter, $chan, $garbage, $card, @temp_channels, $temp, $i);
+
+ @temp_channels = GetSend ("LSTC");
+
+ foreach $i (0 .. $#temp_channels)
+ {
+ $temp = $temp_channels[$i];
+# $temp =~ s/\x0d//g;
+ chomp $temp;
+
+ if ($temp =~ s/250-|250\s//)
+ {
+ ($counter, $temp) = split (/\s/,$temp,2);
+ ($chan, $garbage,$garbage, $garbage, $garbage, $garbage, $garbage, $card, $garbage) = split (/\:/,$temp);
+ $channels[$counter] = $chan;
+ $channels{$chan}{number} = $counter;
+ $channels{$chan}{card} = $card;
+ $counter++;
+ }
+ }
+ }
+
+sub initconfigfile
+ {
+ open (FI,"$ENV{HOME}/.master-timer/config") or return;
+ while (<FI>)
+ {
+ s/\#.*//;
+ chomp;
+ if ($_)
+ {
+ my ($key, $value);
+ ($key, $value) = split (/\s+=\s+/);
+ if ($key =~ /^debug$/i)
+ {
+ $debug = $value;
+ print "Debug-Level = $value\n" if ($debug & 16);
+ }
+ elsif ($key =~ /^marginstart$/i)
+ {
+ print "Marginstart = $value\n" if ($debug & 16);
+ $marginstart = $value;
+ }
+ elsif ($key =~ /^marginstop$/i)
+ {
+ print "Marginstop = $value\n" if ($debug & 16);
+ $marginstop = $value;
+ }
+ elsif ($key =~ /^DVBCards$/i)
+ {
+ print "DVB_Cards = $value\n" if ($debug & 16);
+ $DVB_cards = $value;
+ }
+ elsif ($key =~ /^defaultprio$/i)
+ {
+ print "Default Priority = $value\n" if ($debug & 16);
+ $default_prio = $value;
+ }
+ elsif ($key =~ /^Dest$/i)
+ {
+ print "Destination Host/IP:Port = $value\n" if ($debug & 16);
+ @Dest = split (/\s+/,$value);
+ }
+ elsif ($key =~ /^jointimers$/i)
+ {
+ print "Join Timers = $value\n" if ($debug & 16);
+ $jointimers = $value;
+ }
+ else
+ {
+ print "Unkown Key: \"$key\" with Value: \"$value\"\n";
+ }
+ }
+ }
+ print "End Config\n" if ($debug & 16);
+ }
+
+sub init
+ {
+ &initconfigfile();
+ &initsocket();
+ &initmovie();
+ &initblacklist();
+ &initdone();
+ &initchannellist();
+ &initepgdata();
+ &inittorecord();
+ }
diff --git a/Tools/master-timer/process_summary.pl b/Tools/master-timer/process_summary.pl
new file mode 100755
index 0000000..ebe6300
--- /dev/null
+++ b/Tools/master-timer/process_summary.pl
@@ -0,0 +1,79 @@
+#!/usr/bin/perl -w
+
+$dir = "/home/ms/.master-timer";
+
+open (FI,"$dir/done") or die "Can't open \"done\"\n";
+while (<FI>)
+ {
+ chomp;
+ if ($_)
+ {
+ ($title,$subtitle) = split (/\|/,$_,2);
+ $Done{$title}{$subtitle}=1;
+ }
+ }
+close (FI);
+
+&traverse('/video');
+
+if ($hit)
+ {
+ rename ("$dir/done","$dir/done.bak");
+ open (FO,">$dir/done");
+ foreach $title (sort keys %Done)
+ {
+ foreach $subtitle (sort keys %{%Done->{$title}})
+ {
+ print FO "$title\|$subtitle\n";
+ }
+ }
+ }
+
+sub traverse
+ {
+ local($dir) = shift;
+ local($path);
+ unless (opendir(DIR, $dir))
+ {
+ warn "Can't open $dir\n";
+ closedir(DIR);
+ return;
+ }
+ foreach (readdir(DIR))
+ {
+ next if $_ eq '.' || $_ eq '..';
+ $path = "$dir/$_";
+ if (-d $path) # a directory
+ {
+ &traverse($path);
+ }
+ if ($_ eq "summary.vdr")
+ {
+ open (FI,"$path") or die "Can't open \"$path\"\n";
+ @lines = <FI>;
+ close (FI);
+ if ($lines[0] =~ /^Title\:\s\"(.*)\"/)
+ {
+ @titles = split (/\~/,$1);
+ if ($lines[2] && $lines[2] =~ /^Subtitle\:\s\"(.*)\"/)
+ {
+ @subtitles = split (/\~/,$1);
+ foreach $num (0 .. $#titles)
+ {
+ if ($titles[$num] && $subtitles[$num])
+ {
+ if (!$Done{$titles[$num]}{$subtitles[$num]})
+ {
+ $Done{$titles[$num]}{$subtitles[$num]}=1;
+ $hit = 1;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ closedir(DIR);
+ }
+
+
diff --git a/Tools/master-timer/sample/channels-to-scan b/Tools/master-timer/sample/channels-to-scan
new file mode 100644
index 0000000..6acf157
--- /dev/null
+++ b/Tools/master-timer/sample/channels-to-scan
@@ -0,0 +1,8 @@
+1
+2
+3
+4
+5
+13
+18
+21
diff --git a/Tools/master-timer/sample/config b/Tools/master-timer/sample/config
new file mode 100644
index 0000000..d01c8a8
--- /dev/null
+++ b/Tools/master-timer/sample/config
@@ -0,0 +1,14 @@
+# How Many Seconds "too early" should the timer begin
+marginstart = 600
+# How Many Seocnds "too long" should the timer end
+marginstop = 600
+# When the Prio isn't provied in the config-File use this value
+defaultprio = 50
+# How many DVB-Cards are installed in the Computer (Not used yet)
+DVBCards = 3
+# IP/Hostname:Port of the Destinations (Space is used for delimiter)
+Dest-Host = localhost:2001
+# Should Timers on the same channels be joined when they overlapp (0 = off)
+jointimers = 1
+# Debug-Level
+debug = 0
diff --git a/Tools/master-timer/sample/deepblack b/Tools/master-timer/sample/deepblack
new file mode 100644
index 0000000..63b4f9e
--- /dev/null
+++ b/Tools/master-timer/sample/deepblack
@@ -0,0 +1,79 @@
+Für alle Fälle Stefanie
+'MAX' - Das ganze Leben!
+10 vor 11
+17:30 live
+18:30
+24 Stunden
+Andreas Türck
+Arabella
+^BIZZ$
+Big Brother
+Britt - Der Talk um Eins
+Bärbel Schäfer
+Call TV
+Chicago Hope - Endstation Hoffnung
+Chicago Hope
+DIE REDAKTION
+Dauerwerbesendungen
+Die Harald Schmidt Show
+Die Oliver Geissen Show
+Die Quiz Show
+Doppelter Einsatz
+Dr. Stefan Frank - Der Arzt, dem die Frauen vertrauen
+EXCLUSIV
+EXTRA
+Ehekriege
+Ein Bayer auf Rügen
+Emergency Room
+Explosiv - Das Magazin
+GIRLSCAMP
+Glücksrad
+Gute Zeiten, schlechte Zeiten
+Hallo, Onkel Doc!
+Hans Meiser
+Hercules
+Hinter Gittern - Der Frauenknast
+Infomercials
+Jeder gegen Jeden
+K1 DIE REPORTAGE
+K1 Das Magazin
+K1 Nachrichten
+Kickers
+Kochduell
+Nachrichten
+Nicole - Entscheidung am Nachmittag
+OP ruft Dr. Bruckner
+PREMIERE WORLD - Das Programm
+PROSIEBEN REPORTAGE
+Peter Imhof
+Programm ab
+Programm von
+Punkt 12
+Punkt 6
+Punkt 9
+RTL II News
+RTL SHOP
+RTL aktuell
+RTL-Nachtjournal
+SAT.1-FRÜHSTÜCKSFERNSEHEN
+Spiegel TV-Reportage
+UEFA Champions
+fussball
+fßball
+Vera am Mittag
+Wolffs Revier
+Zapping
+alphateam
+peep!
+s.a.m.
+taff.
+^blitz$
+SK Kölsch
+^Becker$
+Kommissar Rex
+Fit For Fun TV
+Nur die Liebe zählt
+Unsere kleine Farm
+Die Waltons
+^Die Zwei$
+^Sieben$
diff --git a/Tools/master-timer/sample/done b/Tools/master-timer/sample/done
new file mode 100644
index 0000000..76819c7
--- /dev/null
+++ b/Tools/master-timer/sample/done
@@ -0,0 +1 @@
+Alles Routine|Komödie
diff --git a/Tools/master-timer/sample/subtitle-movie b/Tools/master-timer/sample/subtitle-movie
new file mode 100644
index 0000000..3b5a0ab
--- /dev/null
+++ b/Tools/master-timer/sample/subtitle-movie
@@ -0,0 +1,41 @@
+^Abenteuerfilm$
+^Actionfilm$
+^Actionkomödie$
+^Actionthriller$
+^Agentenfilm$
+^Biografie$
+^Biographie$
+^Computeranimation$
+^Drama$
+^Episodenfilm$
+^Erotikfilm$
+^Familiendrama$
+^Fantasy$
+^Fantasykomödie$
+^Gangsterfilm$
+^Gerichtsfilm$
+^Gesellschaftsdrama$
+^Horrorfilm$
+^Horrorkomödie$
+^Kinderfilm$
+^Komödie$
+^Kriegsfilm$
+^Krimikomödie$
+^Kriminalfilm$
+^Liebesfilm$
+^Melodram$
+^Melodrama$
+^Musical$
+^Politthriller$
+^Psychothriller$
+^Road Movie$
+^Romanze$
+^Satire$
+^Science-Fiction$
+^Spielfilm$
+^TV Movie$
+^TV-Drama$
+^Thriller$
+^Western$
+^Zeichentrick$
+^Zeichentrickkomödie$
diff --git a/Tools/master-timer/sample/torecord b/Tools/master-timer/sample/torecord
new file mode 100644
index 0000000..0306830
--- /dev/null
+++ b/Tools/master-timer/sample/torecord
@@ -0,0 +1,32 @@
+# Format: (Every field is "optional".
+# [Title RE|Subtitle RE|Description RE|Channel-Name|Timeframe|Prio|Timer-Title|Marginstart;Marginstop|VDR-Instance]
+#
+# To record something at least one of the "Title", "Subtitle" or "Description"
+# Fields has to be provided. This 3 fields are "include" and the rest are
+# "exclude" fields!
+#
+# More than one channel definition can be provided. The delimiter is ";"
+# Additionaly you can make a "blacklist" of Channels when you prepent a "!" to the first Channel Definition
+# The "!" is only tested for the FIRST Channel definition.
+# You can only have a white or a blacklist (Mixing doesn't make sense!)
+#
+# ex. Record the series "Deep Space Nine" on Sci-Fantasy in the timeframe 09:00 - 14:00 with 60 Seconds Marginstart and -60 Seconds Marginstop
+# Deep Space Nine|||Sci-Fantasy|0900-1400|99|DS9|60;-60
+#
+# Record all "Actionfilm"s with "Schwarzenegger"
+# |Actionfilm|Schwarzenegger
+#
+Babylon 5|||!Pro-7||99|60;-60|1
+Deep Space Nine|||||99|DS9|60;-60|2
+Seven Days|||||99|
+Stargate|||||99|
+Futurama||||2100-2300|50|
+Ally McBeal|||||99|
+Snoops|||||50|
+^Friends$|||||99|Friends|
+Pensacola|||||50|
+seaQuest|||||50|
+||Paltrow|Sci Fantasy;13th Street;Star Kino;Cine Action;Cine Comedy;Romantic Movies;Studio Universal;Premiere||99|
+||Aniston|||99|
+Matrix
+
diff --git a/Tools/master-timer/scan-channels b/Tools/master-timer/scan-channels
new file mode 100755
index 0000000..324181b
--- /dev/null
+++ b/Tools/master-timer/scan-channels
@@ -0,0 +1,8 @@
+#!/bin/sh
+old=`svdrpsend.pl chan | grep 250 | cut -d " " -f2`
+for dat in `cat $HOME/.master-timer/channels-to-scan`
+do
+ svdrpsend.pl "chan $dat"
+ sleep 30s
+done
+svdrpsend.pl "chan $old"