diff options
Diffstat (limited to 'Tools')
-rw-r--r-- | Tools/epg2timers/README | 151 | ||||
-rw-r--r-- | Tools/epg2timers/epg2timers.cxx | 578 | ||||
-rw-r--r-- | Tools/epg2timers/epg_channel_names | 400 | ||||
-rwxr-xr-x | Tools/epg2timers/get_merkliste.pl | 82 | ||||
-rwxr-xr-x | Tools/epg2timers/loadvdr.pl | 89 | ||||
-rwxr-xr-x | Tools/epg2timers/update_timers | 22 | ||||
-rwxr-xr-x | Tools/epg2timers/update_timers.old | 24 | ||||
-rw-r--r-- | Tools/master-timer/LIESMICH | 154 | ||||
-rw-r--r-- | Tools/master-timer/README | 54 | ||||
-rw-r--r-- | Tools/master-timer/THANKS | 18 | ||||
-rw-r--r-- | Tools/master-timer/Todo | 4 | ||||
-rwxr-xr-x | Tools/master-timer/convert-DTV2VDR.pl | 151 | ||||
-rwxr-xr-x | Tools/master-timer/convert-oldtorecord.pl | 61 | ||||
-rwxr-xr-x | Tools/master-timer/master-timer.pl | 2231 | ||||
-rw-r--r-- | Tools/master-timer/sample/channels-to-scan | 4 | ||||
-rw-r--r-- | Tools/master-timer/sample/config | 38 | ||||
-rw-r--r-- | Tools/master-timer/sample/convert-channel-list | 26 | ||||
-rw-r--r-- | Tools/master-timer/sample/deepblack | 251 | ||||
-rw-r--r-- | Tools/master-timer/sample/subtitle-movie | 11 | ||||
-rw-r--r-- | Tools/master-timer/sample/torecord | 116 |
20 files changed, 3132 insertions, 1333 deletions
diff --git a/Tools/epg2timers/README b/Tools/epg2timers/README new file mode 100644 index 0000000..53888b1 --- /dev/null +++ b/Tools/epg2timers/README @@ -0,0 +1,151 @@ +Overview. +========= + +The 4 modules in this directory are designed to allow vdr timer +programming via the http://tvtv.de web EPG (Electronic Program Guide). + +Once you have these modules properly configured and installed, +you should be able to simply click on the things you want vdr +to record in the http://tvtv.de web EPG and be done with it. +Everything else can be handled automatically. + + + +Module description. +=================== + +The http://tvtv.de web EPG creates a so-called "merkliste" +("a list of items to remember") containing all the broadcasts +you selected. + +1. The perl script "get_merkliste.pl" transfers this "merkliste" + from the http://tvtv.de web site to a local file "merkliste.html". + +2. The C++ program "epg2timers" converts this HTML file into vdr's + timers.conf format. + +3. The perl script "loadvdr.pl" pumps these new timer entries + into a running vdr using telnet and the SVDRP protocol. + +4. The shell script "update_timers" implements the overall + control of the entire process. + It retrieves the latest merkliste from http://tvtv.de, + converts it to timers.conf format and sends the timer entries + to vdr. + + + +Configuration. +============== + +get_merkliste.pl requires configuration of the "files_to_fetch" +variable preset. +Log in to your http://tvtv.de account and click on the "Bookmark" +item in the "Setup" submenu of the "Mein Programm" side bar menu. +This will open a window with a URL in the location field that ends +with an ID value. Replace the xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +in the "files_to_fetch" variable preset with your ID value. +If you are using an HTTP proxy, uncomment the line containing +the $ua->proxy call and put your proxy details in. + +epg2timers.cxx allows various items to be configured, but it +should work out of the box. See "tvtv.de channel names" below. + +update_timers must know where to find the get_merkliste.pl +perl script and the compiled epg2timers binary. Both must be +in a directory pointed to by the TOOLDIR variable. +update_timers also must know where to find vdr's current +channels.conf file. Put that into the CHANPATH. + +Depending on the price of your internet access, you may want to +run update_timers more or less frequently. It may also be a good +idea to run it at times where it is unlikely to interfere with +your current use of vdr. Configure a crontab entry according to +these personal preferences. Here is the entry I use: +1 2 * * * /home/cko/bin/update_timers +It runs update_timers only once a night at 02:01 a.m. + + +Installation. +============= +Create your TOOLDIR directory if it does not already exist. +Copy get_merkliste.pl and loadvdr.pl into it, compile epg2timers.cxx +with the command: + g++ epg2timers.cxx -o epg2timers +and move the epg2timers binary into the TOOLDIR directory. + +The get_merkliste.pl script requires certain packages to run. +Besides of course perl, install perl-libwww-perl (at least +that's the name on SuSE 7.2, it may have a different name in +your distribution). + +If you have problems with SVDRP and loadvdr.pl, you may want to +try out the update_timers.old script, which replaces the timers.conf +file directly and kills vdr (assuming that it will be restarted +by the runvdr script) to make vdr reload the timers.conf file. + + +tvtv.de channel names. +====================== +The file epg_channel_names contains the names of all channels +currently (as of September 9, 2001) supported by the tvtv.de +web EPG. The variable "channel_map" in epg2timers.cxx maps +these names into PNRs (aka Service IDs). I have initialized +this table with provider names converted from a d-box channel +scan of Astra 19.2E, so the PNRs should be correct for that +satellite, but most of the names propably aren't yet- I simply +had not enough time yet to go through epg_channel_names +and insert all its channel names at the proper places in the +channel map. Consider the map supplied an example. ;-) +If you fix any of the entries, please send me a patch. +For my own humble purposes, the table works well as it is. +Of course, your channels.conf must contain the matching +PNRs (last field in each line). + + +To Do. +====== +These are just ideas. They MAY get implemented. +If you want them to happen, contribute a patch. ;-) + +* Support vdr hierarchical directories (after vdr does) + by mapping the http://tvtv.de genre texts into + directory names. +* start_time_safety_margin for epg2timers. + + +Authors. +======== +Carsten Koch: epg2timers.cxx, update_timers, this README file. + +Axel Gruber and +Rolf Hakenes: get_merkliste.pl + +Peter Ahlert: loadvdr.pl + + +Credits. +======== +I am grateful (in chronological order) to + +* Klaus Schmidinger for his excellent vdr program and for + keeping an open mind in all directions. + +* Suse (my wife, not the Linux distributor ;-) for encouraging me to + write epg2timers in June 2000 and for her constant patience and support. + +* Andreas Steinhauser for periodically criticizing the epg2timers + "manual mode" until I came up with the idea to fully automatize it + and for contributing ideas. + +* Axel Gruber for reminding me half a year later, for pushing + the idea until it got implemented, for asking for new features + all the time and for contributing ideas. + +* Axel Gruber and Rolf Hakenes for contributing the get_merkliste.pl + perl script. + +* Peter Ahlert for contributing the loadvdr.pl perl script. + + +Carsten, September 2001. diff --git a/Tools/epg2timers/epg2timers.cxx b/Tools/epg2timers/epg2timers.cxx index 07e8182..94e1c8a 100644 --- a/Tools/epg2timers/epg2timers.cxx +++ b/Tools/epg2timers/epg2timers.cxx @@ -1,13 +1,13 @@ /* - * epg2timers.cxx: Convert an EPG "merkliste" page (http://www.tvtv.de) to a timers.conf - * file for Klaus Schmidinger's vdr (http://www.cadsoft.de/people/kls/vdr). + * epg2timers.cxx: Convert an EPG "merkliste" HTML page (http://tvtv.de) + * to timers.conf format for Klaus Schmidinger's vdr + * (http://www.cadsoft.de/people/kls/vdr). * - * Copyright (C) 2000 Carsten Koch + * Copyright (C) 2000, 2001 Carsten Koch * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. + * version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -23,65 +23,349 @@ */ +#include <malloc.h> #include <stdio.h> #include <string.h> +#include <unistd.h> +// User-configurable options. -static const char date_line[] = "\t<td align=center valign=middle colspan=3><span id=fb-b10>"; -static const char start_time_line[] = " \t\t<td bgcolor=\"#7f98bf\" align=center><span id=\"fb-w14\"><nobr> "; -static const char stop_time_line[] = "\t\t\t<tr><td bgcolor=\"#002b64\" align=center><span id=\"fn-w9\">bis "; -static const char channel_line[] = "\t\t\t<tr><td bgcolor=\"#002b64\" align=center><span id=\"fb-w9\">"; -static const char title_line[] = "\t\t\t\t<td bgcolor=\"#002b64\" align=left width=100%><span id=\"fb-w10\">"; -static const char summary_line[] = "\t\t\t<table border=0 cellpadding=10 cellspacing=0 bgcolor=\"white\" width=100%>"; -static const char * const channel_names[] = +static const int stop_time_safety_margin = 10; // add 10 minutes to stop time in case start was delayed +static const int recording_priority = 50; // vdr recording priority setting for all timer entries generated +static const int recording_lifetime = 98; // vdr recording life time setting for all timer entries generated + + +// Usually, you should not want to change any of these. + +static const int max_title = 256; // maximum length+1 of title file name generated +static const int max_genre = 32; // maximum length+1 of genre text parsed +static const int max_line = 1024; // line buffer (not used when parsing summary text) +static const int max_summary = 9000; // Summary can be up to 9000 bytes long (a bit shorter than vdr's SVDRP command buffer) +static const int max_vdr_channel = 1000; // maximum size+1 of your channels.conf + +// The following table maps http://tvtv.de channel names into Astra 19.2E PIDs. +// It is incomplete. Contributions welcome. + +typedef struct { -"3sat", -"ARTE", -"*B1 Berlin", -"BR3", -"Bloomberg TV", -"BR Alpha", -"CNN", -"ARD", -"*DW-tv", -"Eins Extra", -"Eins Festival", -"Eins MuXx", -"euroNEWS", -"HR3", -"Kabel1", -"Kinderkanal", -"MDR", -"MTV", -"NDR", -"NTV", -"ORB", -"*ORF1", -"Phoenix", -"PRO7", -"RTL", -"RTL2", -"SAT1", -"skynews", -"SWF", -"Super RTL", -"TM3", -"TW1", -"VOX", -"WDR", -"Theaterkanal", -"ZDF", -"ZDF.doku", -"ZDF.info", -"" + const char * tvtv_name; + unsigned short pnr; +} map_entry; + + +static const map_entry channel_map[] = +{ + // Deutschsprachig + {"13th Street", 42}, + {"3sat", 28007}, + {"ARTE", 28109}, + {"B1", 28206}, + {"BR3", 28107}, + {"BR-alpha", 28112}, + {"ARD", 28106}, + {"Discovery", 14}, + {"Disney Channel", 34}, + {"Eins Extra", 28201}, + {"Eins Festival", 28202}, + {"Eins MuXx", 28203}, + {"Filmpalast", 516}, + {"FOX KIDS", 28}, + {"Heimatkanal", 517}, + {"HR", 28108}, + {"Junior", 19}, + {"Kabel 1", 899}, + {"Kinderkanal", 28008}, + {"Krimi&Co", 23}, + {"K-Toon", 12}, + {"Liberty TV.com", 12199}, + {"MDR", 28204}, + {"NDR", 28224}, + {"NEUN LIVE", 897}, + {"ORB", 28205}, + {"ORF1", 13001}, + {"ORF2", 13002}, + {"Phoenix", 28114}, + {"Planet", 13}, + {"Premiere 1", 10}, + {"Premiere 2", 11}, + {"Premiere 3", 43}, + {"Premiere Action", 20}, + {"Premiere Comedy", 29}, + {"Premiere SCI-FI", 41}, + {"Premiere Star", 9}, + {"PREMIERE WORLD", 8}, + {"ProSieben", 898}, + {"RTL", 12003}, + {"RTL2", 12020}, + {"SAT.1", 46}, + {"SeaSonS", 33}, + {"SR", 28110}, + {"Studio Universal", 36}, + {"Sunset", 16}, + {"Super RTL", 12040}, + {"Test-Z1", 28305}, + {"TW1", 13013}, + {"Via 1 - Schöner Reise", 44}, + {"VOX", 12060}, + {"WDR", 28111}, + {"ZDF", 28006}, + {"ZDF.doku", 28014}, + {"ZDF.info", 28011}, + // Movies + {"AXN", 29506}, + {"CANAL+", 29100}, + {"CANAL+ AZUL", 29101}, + {"CANAL+ ROJO", 29102}, + {"CANAL+ VERT", 8208}, + {"CANAL+ 16/9", 8204}, + {"CANAL+ 16|9", 29024}, + {"C+ROOD", 4005}, + {"CINE CINEMA I", 8206}, + {"CINE CINEMA II", 8002}, + {"CINE CINEMA III", 8003}, + {"CINE CLASSICS", 8709}, + {"CINE CINEMA 16/9", 8301}, + {"cinecinemas", 4008}, + {"CINECLASSICS", 29203}, + {"Cinedom 1", 176}, + {"Cinedom 1B", 178}, + {"Cinedom 1C", 180}, + {"Cinedom 1D", 190}, + {"Cinedom 2", 179}, + {"Cinedom 2B", 183}, + {"Cinedom 2C", 184}, + {"Cinedom 2D", 188}, + {"Cinedom 2E", 193}, + {"Cinedom 3", 182}, + {"Cinedom 3B", 185}, + {"Cinedom 3C", 192}, + {"Cinedom 3D", 195}, + {"Cinedom 4", 181}, + {"Cinedom 4B", 187}, + {"Cinedom 4C", 191}, + {"Cinedom 5", 186}, + {"Cinedom 5B", 194}, + {"Cindedom Deluxe", 189}, + {"CINEMANÍA AZUL", 29501}, + {"CINEMANÍA ROJO", 29605}, + {"CINEMANÍA", 29500}, + {"K1", 8401}, + {"K2", 8402}, + {"K3", 8403}, + {"K4", 8404}, + {"K5", 8405}, + {"K6", 8406}, + {"K7", 8407}, + {"K9", 8409}, + {"K12", 8412}, + {"TAQUILLA 1", 29206}, + {"TAQUILLA 2", 29207}, + {"TAQUILLA 3", 29502}, + {"TAQUILLA 4", 29503}, + {"TAQUILLA 5", 29504}, + {"TAQUILLA 6", 29301}, + {"TAQUILLA 7", 29302}, + {"TAQUILLA 8", 29303}, + {"TAQUILLA 11", 29316}, + {"TAQUILLA 12", 29610}, + {"TAQUILLA 13", 29402}, + {"TAQUILLA 14", 29212}, + {"TAQUILLA 16|9", 29606}, + // Music + {"40 LATINO", 29031}, + {"40 TV", 29110}, + {"CANAL+ JAUNE", 8203}, + {"CLASSICA", 15}, + {"GOLDSTAR TV", 518}, + {"MCM 2", 8305}, + {"MCM AFRICA", 8307}, + {"MCM", 8302}, + {"MTV 2", 28649}, + {"MTV 6", 28641}, + {"MTV Base", 28645}, + {"MTV Central", 28643}, + {"MTV F", 28642}, + {"MTV Hits", 28644}, + {"MUZZIK", 8007}, + {"RFM TV", 17008}, + {"TMF", 5015}, + {"VH1 Classic", 28647}, + {"VH1", 28646}, + {"Video Italia", 12220}, + {"VIVA ZWEI", 12120}, + {"VIVA", 12732}, + {"ZIK'/XXL", 17004}, + // News + {"BBC WORLD", 17007}, + {"Bloomberg TV", 12160}, + {"CNBC", 28010}, + {"CNBC", 35}, + {"CNBC-NBC", 29202}, + {"CNN", 28512}, + {"DW-tv", 9005}, + {"EuroNews", 28015}, + {"FOX NEWS", 29032}, + {"N24", 47}, + {"n-tv", 12730}, + {"Sky News", 3995}, + // Netherlands + {"NED1", 4011}, + {"NED2", 4012}, + {"NED3", 4013}, + {"NET5", 5004}, + {"RTL4", 2004}, + {"RTL5", 2005}, + {"SBS6", 5005}, + {"V8/Fox Kids", 5020}, + {"Yorin", 5010}, + // Porn + {"BEATE-UHSE.TV", 21}, + {"Blue Movie1", 513}, + {"Blue Movie2", 514}, + {"Blue Movie3", 515}, + {"K10", 8410}, + {"TAQUILLA X", 29213}, + {"TAQUILLA X", 29602}, + {"TAQUILLA XX", 29607}, + {"X-ZONE", 4009}, + // Sports + {"C+BLAUW", 4006}, + {"DSF", 900}, + {"EUROSPORT", 8101}, + {"Eurosport", 28009}, + {"EUROSPORT", 29310}, + {"EUROSPORTNEWS", 29037}, + {"PATHE SPORT|", 8009}, + {"PREMIERE SPORT 1", 17}, + {"PREMIERE SPORT 2", 27}, + {"SUPERDOM", 26}, + // French + {"13EME RUE", 8703}, + {"AB 1", 17001}, + {"AB MOTEURS", 17000}, + {"ACTION", 17010}, + {"ALLOCINE TV", 8308}, + {"ANIMAUX", 17002}, + {"ARTE", 9009}, + {"BLOOMBERG TV", 8004}, + {"CA TV", 8610}, + {"CANAL+", 8201}, + {"CANAL+ BLEU", 8202}, + {"CANAL J", 8108}, + {"CANAL JIMMY", 8006}, + {"CANALCLUB", 8812}, + {"Cartoon Network", 28511}, + {"CLUB TELEACHAT", 8303}, + {"COMEDIE !", 8702}, + {"CONTACT TV", 8804}, + {"CUISINE.TV", 8112}, + {"DEMAIN !", 8701}, + {"DISNEY CHANNEL", 8207}, + {"DT CSAT 10", 9159}, + {"ENCYCLOPEDIA", 17003}, + {"ESCALES", 17005}, + {"EURONEWS", 8505}, + {"FORUM", 8707}, + {"FRANCE 2", 8801}, + {"FRANCE 3", 8802}, + {"GAME ONE", 8717}, + {"i TELEVISION", 8010}, + {"KIOSQUE", 8704}, + {"KTO", 8304}, + {"LA CHAINE METEO", 8008}, + {"LA CINQUIEME", 8501}, + {"LaChaîneHistoire", 17006}, + {"LCI", 8107}, + {"LCP", 8506}, + {"L'EQUIPE TV", 8706}, + {"LibertyTV.com", 12280}, + {"MANGAS", 17011}, + {"MONTECARLO TMC", 8102}, + {"Motors TV", 12300}, + {"NAT GEOGRAPHIC", 8310}, + {"PAD", 8211}, + {"PARIS PREMIERE", 8104}, + {"PLANETE 2", 8507}, + {"PLANETE", 8103}, + {"PMU sur Canal+", 8210}, + {"RFO SAT", 8708}, + {"SANTE - VIE", 8110}, + {"SEASONS", 8001}, + {"TCM", 28515}, + {"TEST CDN 1", 8616}, + {"TEST CDN 3", 8627}, + {"TiJi", 8309}, + {"TV 5", 9001}, + {"TV BREIZH", 8502}, + {"TV Puls", 20601}, + {"TV5 Europe", 12240}, + {"VOYAGE", 8105}, + // Spanish + {"ANDALUCÍA TV", 29011}, + {"Bloomberg", 12721}, + {"CALLE 13", 29609}, + {"Canal Canarias", 29700}, + {"Cartoon Network", 29314}, + {"CNN+", 29020}, + {"DISCOVERY", 29116}, + {"DISNEY CHANNEL", 29111}, + {"DOCUMANÍA", 29200}, + {"ESTILO", 29305}, + {"ETB", 29035}, + {"FASHION TV", 29115}, + {"FOX KIDS", 29209}, + {"FOX", 29507}, + {"MOSAICO", 29315}, + {"MÉTEO", 29014}, + {"Nat Geo Channel", 29034}, + {"NICK-PARAMOUNT", 29312}, + {"RTPI", 9006}, + {"SEASONS", 29204}, + {"TAQUILLA 0", 29205}, + {"TCM.", 28516}, + {"TVC INT.", 29701}, + {"VIAJAR", 29306}, + // Miscellaneous + {"Alice", 12200}, + {"Canal Algerie", 9008}, + {"CANALPRO TV", 8516}, + {"ESC1 - EGYPTE", 9003}, + {"FASHION TV.COM", 17009}, + {"Home Shopping Euro", 45}, + {"Home Shopping Euro", 40}, + {"Kabel 1 Austria", 20004}, + {"Kabel 1 Schweiz", 20003}, + {"Polonia 1/Top Sho", 20366}, + {"ProSieben A", 20002}, + {"ProSieben Schweiz", 20001}, + {"QVC GERMANY", 12100}, + {"RAI 1", 9004}, + {"REAL MADRID TV", 29019}, + {"RealityTV", 20309}, + {"RTL TELE Letzebuerg", 3994}, + {"RTM - MAROC", 9002}, + {"SÜDWEST BW", 28113}, + {"SÜDWEST RP", 28231}, + {"Super 1", 20364}, + {"Travel", 28001}, + {"TV7", 9007}, + {"TV-NIEP II", 12740}, + {"Wishline", 12320} }; + + +// Nothing user-configurable below this line. + +static const char date_line[] = "\t<td align=center valign=middle colspan=3><span id=fb-b10>"; +static const char start_time_line[] = " \t\t<td id=\"jobview-box-date\" align=center><nobr> "; +static const char stop_time_line[] = "\t\t\t<tr><td id=\"line\" align=center><span id=\"fn-w9\">bis "; +static const char channel_line[] = "\t\t\t<tr><td align=center><span id=\"fb-w9\">"; +static const char title_line[] = "\t\t\t\t<td align=left width=100%><span id=\"fb-w10\">"; +static const char summary_line[] = "<span id=\"fn-b8\">"; +static const char genre_line[] = "\t\t\t\t<td align=right valign=center nowrap><span id=\"fn-w10\">"; + static const int month_lengths[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; -static const int max_channel = sizeof(channel_names)/sizeof(char *); -static const int max_title = 50; // maximum length of title file name generated -static const int max_line = 1024; // line buffer (not used when parsing summary text) -static const int max_summary = 5000; // Summary can be up to 5000 bytes long -static const int stop_time_safety_margin = 10; // add 10 minutes to stop time in case start was delayed @@ -102,6 +386,10 @@ char map_special_char(const char * const word) return 'Ü'; else if (strcmp(word, "szlig") == 0) return 'ß'; + else if (strcmp(word, "nbsp") == 0) + return ' '; + else if (strcmp(word, "amp") == 0) + return '&'; return ' '; } @@ -109,40 +397,52 @@ char map_special_char(const char * const word) -void read_file_name(const char * const line, char * const file_name) +void read_file_name_and_title(const char * const line, char * const file_name, char * const title) { int line_index = sizeof(title_line) - 1; int title_index = 0; - char ch = line[line_index++]; + int file_name_index = 0; + char ch; do { + ch = line[line_index++]; if (ch == '&') { char word[10]; int i = 0; while ((line[line_index + i] != ';') && (i < 9)) - word[i++] = line[line_index + i]; - word[i] = 0; - ch = map_special_char(word); - line_index += i; + { + word[i] = line[line_index + i]; i++; + } + if (line[line_index + i] == ';') + { + word[i] = 0; + ch = map_special_char(word); + line_index += i; + } } switch (ch) { - case 'ä': file_name[title_index++] = 'a'; file_name[title_index++] = 'e'; break; - case 'ö': file_name[title_index++] = 'o'; file_name[title_index++] = 'e'; break; - case 'ü': file_name[title_index++] = 'u'; file_name[title_index++] = 'e'; break; - case 'Ä': file_name[title_index++] = 'A'; file_name[title_index++] = 'e'; break; - case 'Ö': file_name[title_index++] = 'O'; file_name[title_index++] = 'e'; break; - case 'Ü': file_name[title_index++] = 'U'; file_name[title_index++] = 'e'; break; - case 'ß': file_name[title_index++] = 's'; file_name[title_index++] = 's'; break; + case 'ä': file_name[file_name_index++] = 'a'; file_name[file_name_index++] = 'e'; break; + case 'ö': file_name[file_name_index++] = 'o'; file_name[file_name_index++] = 'e'; break; + case 'ü': file_name[file_name_index++] = 'u'; file_name[file_name_index++] = 'e'; break; + case 'Ä': file_name[file_name_index++] = 'A'; file_name[file_name_index++] = 'e'; break; + case 'Ö': file_name[file_name_index++] = 'O'; file_name[file_name_index++] = 'e'; break; + case 'Ü': file_name[file_name_index++] = 'U'; file_name[file_name_index++] = 'e'; break; + case 'ß': file_name[file_name_index++] = 's'; file_name[file_name_index++] = 's'; break; + case ' ': file_name[file_name_index++] = '_'; break; + case '&': + file_name[file_name_index++] = 'u'; file_name[file_name_index++] = 'n'; file_name[file_name_index++] = 'd'; + break; default: - if (((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z')) || ((ch >= '0') && (ch <= '9'))) - file_name[title_index++] = ch; + if (((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z')) || ((ch >= '0') && (ch <= '9')) || (ch == '-')) + file_name[file_name_index++] = ch; } - ch = int(line[line_index++]); - } while ((title_index < max_title-1) && (ch != '<') && (ch != 0) && (line_index < max_line-1)); - file_name[title_index] = 0; + title[title_index++] = ch; + } while ((file_name_index < max_title-5) && (ch != '<') && (ch != 0)); + file_name[file_name_index] = 0; + title[title_index-1] = 0; } @@ -184,7 +484,7 @@ void read_summary(char * const summary) word[word_index++] = ch; } while ((word_index < 6) && (ch != '>') && (ch != EOF)); while ((ch != '>') && (ch != EOF)) ch = getchar(); - if (strncmp("/table", word, 6) == 0) + if (strncmp("/span", word, 4) == 0) done = true; } break; @@ -207,62 +507,150 @@ void read_summary(char * const summary) +int find_channel_number(const unsigned short * const vdr_pnrs, const char * const channel_name) + +{ + for (int tvtv_channel_number = 0; tvtv_channel_number < sizeof(channel_map)/sizeof(map_entry); tvtv_channel_number++) + if (strcmp(channel_name, channel_map[tvtv_channel_number].tvtv_name) == 0) + for (int vdr_channel_number = 0; vdr_pnrs[vdr_channel_number] != 0xFFFF; vdr_channel_number++) + if (vdr_pnrs[vdr_channel_number] == channel_map[tvtv_channel_number].pnr) + return vdr_channel_number; + fprintf(stderr, "Error - channel '%s' not recognized.\n", channel_name); + exit(1); + /*NOTREACHED*/ +} + + -main() +unsigned short * read_vdr_pnrs(const char * const channels_conf_file_name) + { - int channel = 0; + FILE * channels_conf = fopen(channels_conf_file_name, "r"); + if (channels_conf == NULL) + { + perror("unable to open channels.conf."); + exit(1); + } + unsigned short * vdr_pnrs = (unsigned short *) malloc(max_vdr_channel * sizeof(unsigned short)); + int vdr_channel_number = 0; + while (!feof(channels_conf) && (vdr_channel_number < max_vdr_channel-1)) + { + char line[1024]; + fgets(line, sizeof(line)-1, channels_conf); + int pnr; + if ((line[0] != ':') && + (sscanf(line, "%*[^:]:%*[^:]:%*[^:]:%*[^:]:%*[^:]:%*[^:]:%*[^:]:%*[^:]:%*[^:]:%d", &pnr) == 1)) + vdr_pnrs[vdr_channel_number++] = pnr; + } + vdr_pnrs[vdr_channel_number++] = 0xFFFF; // sentinel + fprintf(stderr, "%d pnrs.\n", vdr_channel_number); + return (unsigned short *) realloc(vdr_pnrs, vdr_channel_number * sizeof(unsigned short)); +} + + + + +void process_input(const unsigned short * const vdr_pnrs) + +{ + + int channel = -1; int day = -1; int next_day = -1; int start_time = -1; - int stop_time = -1; + int stop_hour = -1; + int stop_minute= -1; + char genre[max_genre] = {0}; char summary[max_summary] = {0}; char file_name[max_title] = {0}; + char title[max_title] = {0}; while (!feof(stdin)) { char line[max_line]; fgets(line, max_line-1, stdin); + line[max_line-1] = 0; if (strncmp(line, date_line, sizeof(date_line)-1) == 0) { const int month = (line[sizeof(date_line) + 6]- '0') * 10 + line[sizeof(date_line) + 7]-'0'; day = (line[sizeof(date_line) + 3]- '0') * 10 + line[sizeof(date_line) + 4]-'0'; - next_day = day == month_lengths[month]? 1 : day + 1; + next_day = day == month_lengths[month-1]? 1 : day + 1; } else if (strncmp(line, start_time_line, sizeof(start_time_line)-1) == 0) - { start_time = (line[sizeof(start_time_line) - 1] - '0') * 1000 + (line[sizeof(start_time_line) ] - '0') * 100 + (line[sizeof(start_time_line) + 2] - '0') * 10 + (line[sizeof(start_time_line) + 3] - '0'); - } else if (strncmp(line, stop_time_line, sizeof(stop_time_line)-1) == 0) { - stop_time = ((line[sizeof(stop_time_line) - 1] - '0') * 1000 + - (line[sizeof(stop_time_line) ] - '0') * 100 + - (line[sizeof(stop_time_line) + 2] - '0') * 10 + - (line[sizeof(stop_time_line) + 3] - '0') + stop_time_safety_margin) % 2400; - if ((day < 0) || (start_time < 0) || (file_name[0] == 0) || (channel == max_channel)) + stop_hour = (line[sizeof(stop_time_line) - 1] - '0') * 10 + + (line[sizeof(stop_time_line) ] - '0'); + stop_minute = (line[sizeof(stop_time_line) + 2] - '0') * 10 + + (line[sizeof(stop_time_line) + 3] - '0') + + stop_time_safety_margin; + if (stop_minute > 59) + { + stop_minute -= 60; + if (stop_hour == 23) + stop_hour = 0; + else + stop_hour++; + } + if ((day < 0) || (start_time < 0) || (file_name[0] == 0) || (channel == -1)) + { fprintf(stderr, "Input data error.\n"); + exit(1); + } else - printf("1:%03d:%02d:%04d:%04d:2:7:%s:%s\n", channel+1, start_time < 600? next_day : day, start_time, stop_time, file_name, summary); - start_time = -1; stop_time = -1; file_name[0] = 0; summary[0] = 0; channel = max_channel; + printf("1:%03d:%02d:%04d:%02d%02d:%d:%d:%s:\"%s\" %s||%s||||||(epg2timers)\n", + channel+1, start_time < 600? next_day : day, start_time, stop_hour, stop_minute, + recording_priority, recording_lifetime, file_name, + title, genre, summary); + start_time = -1; channel = -1; + file_name[0] = 0; summary[0] = 0; genre[0] = 0; } else if (strncmp(line, title_line, sizeof(title_line)-1) == 0) - read_file_name(line, file_name); + read_file_name_and_title(line, file_name, title); else if (strncmp(line, channel_line, sizeof(channel_line)-1) == 0) { int i = sizeof(channel_line); - while ((i < max_line-1) && (line[i] != '<')) i++; + while ((line[i] != '<') && (line[i] != 0)) i++; line[i] = 0; // end of string - for (channel = 0; (channel < max_channel) && - (strcmp(line + sizeof(channel_line) - 1, channel_names[channel]) != 0); - channel++); - if (channel == max_channel) - fprintf(stderr, "Error - channel '%s' not recognized.\n", line + sizeof(channel_line) - 1); + channel = find_channel_number(vdr_pnrs, line + sizeof(channel_line) - 1); } else if (strncmp(line, summary_line, sizeof(summary_line)-1) == 0) read_summary(summary); + else if (strncmp(line, genre_line, sizeof(genre_line)-1) == 0) + { + int genre_index; + for (genre_index = 0; genre_index < max_genre-1; genre_index++) + { + const char ch = line[genre_index + sizeof(genre_line)-1]; + if ((ch == 0) || (ch == '&') || (ch == '<')) + break; + genre[genre_index] = ch; + } + genre[genre_index] = 0; + } } } + + + +main(int argc, char *argv[]) + +{ + fprintf(stderr, "epg2timers Version 0.5, 15-Sep-2001.\n"); + + if (argc != 2) + { + fprintf(stderr, "usage: %s channels.conf\n", argv[0]); + exit(1); + } + + const unsigned short * const vdr_pnrs = read_vdr_pnrs(argv[1]); + process_input(vdr_pnrs); + exit(0); +} diff --git a/Tools/epg2timers/epg_channel_names b/Tools/epg2timers/epg_channel_names new file mode 100644 index 0000000..2633a99 --- /dev/null +++ b/Tools/epg2timers/epg_channel_names @@ -0,0 +1,400 @@ +ATV,at +ORF1,at +ORF2,at +TW1,at + +Club RTL,be +Ketnet/Canvas,be +TVI,be +tv1,be + +SF1,ch +sf2,ch + +13th Street,de +3sat,de,at,ch +ARD,de +ARTE,de +B1,de +BBC World,de +BEATE-UHSE.TV,de +BR-alpha,de +BR3,de +Bet on Jazz,de +Bloomberg TV,de +Blue Movie1,de +Blue Movie2,de +Blue Movie3,de +CNBC,de +CNN,de +Cartoon,de +Cindedom Deluxe,de +Cinedom 1,de +Cinedom 2,de +Cinedom 3,de +Cinedom 4,de +Cinedom 5,de +Classica,de +DSF,de +Discovery,de +Disney Channel,de +Eins Extra,de +Eins Festival,de +Eins MuXx,de +EuroNews,de +Eurosport,de +FOX KIDS,de +Filmpalast,de +Goldstar TV,de +HH-1,de +HR,de +Heimatkanal,de +Junior,de +K-Toon,de +Kabel 1,de +Kinderkanal,de +Krimi&Co,de +MDR,de +MTV,de +MTV2,de +N24,de +NBC,de +NDR,de +NEUN LIVE,de +ONYX,de +ORB,de +Phoenix,de +Planet,de +Premiere 1,de +Premiere 2,de +Premiere 3,de +Premiere Action,de +Premiere Comedy,de +Premiere SCI-FI,de +Premiere Sport1,de +Premiere Sport2,de +Premiere Sport3,de +Premiere Star,de +Premiere analog,de +ProSieben,de +QVC,de +RTL,de +RTL2,de +SAT.1,de +SR,de +SWR BW,de +SWR RP,de +SeaSonS,de +Studio Universal,de +Sunset,de +Super RTL,de +TNT,de +TRT,de,tr +TV.BERLIN,de +Theaterkanal,de +VIVA,de +VIVA2,de +VOX,de +WDR,de +ZDF,de +ZDF.doku,de +ZDF.info,de +n-tv,de +skynews,de +tv.m,de + +Danmark 1,dk +TV2 Danmark,dk + +MTV3,fi +Nelonen 4,fi +Subtv,fi +TV1,fi +TV2,fi + +13ème Rue,fr +AB 1,fr +Arte,fr +Arte Sat,fr +Canal J,fr +Canal Jimmy,fr +Canal+,fr +Canal+ Bel. bleu,fr +Canal+ Bel. jaune,fr +Canal+ Belgique,fr +Canal+ bleu,fr +Canal+ jaune,fr +Canal+ vert,fr +Ciné Cinémas 1,fr +Ciné Cinémas 2,fr +Ciné Cinémas 3,fr +Ciné Classics,fr +Cinéfaz,fr +Cinéstar 1,fr +Cinéstar 2,fr +Cinétoile,fr +Comédie !,fr +Disney Channel,fr +Escale,fr +Eurosport,fr +Festival,fr +Fox Kids,fr +France 2,fr +France 3,fr +Histoire,fr +LCI,fr +La Cinquième Sat,fr +La chaîne histoire,fr +La cinquième,fr +M6,fr +M6 Music,fr +MCM,fr +MTV,fr +Mangas,fr +Mezzo,fr +Muzzik,fr +Odyssée,fr +Paris Première,fr +Pathé sport,fr +Planète,fr +RTBF 1,fr +RTBF 2,fr +RTL 9,fr +Série Club,fr +TCM,fr +TF1,fr +TMC,fr +TSR 1,fr +TSR 2,fr +TV5,fr +Télétoon,fr +Téva,fr +Voyage,fr +XXL,fr + +ANIMALplanet,hu +Budapest TV,hu +Duna TV,hu +FILMMÚZEUM,hu +FOX KIDS/SPORTS,hu +FÕNIX TV,hu +Game Channel,hu +HBO,hu +Hallmark,hu +MAGYAR ATV,hu +Minimax,hu +National Geographic,hu +Nickelodeon,hu +RTL Klub,hu +Romantica,hu +SATeLIT,hu +SPORT1,hu +Spektrum TV,hu +TV2,hu +VIASAT3,hu +VIVA+,hu +fix.tv,hu +m1,hu +m2,hu + ++ Calcio,it ++ F1,it +Adult +,it +CANALE 5,it +Canal Jimmy,it +Canale Viaggi,it +Cartoon Network,it +Cine Classics,it +CineCinemas 1,it +CineCinemas 2,it +Classica,it +Cult Network,it +Discovery Channel,it +Disney Channel,it +Euro News,it +Eurosport,it +Hallmark,it +Happy Channel,it +ITALIA 1,it +La 7,it +MTV,it +MTV - TMC 2,it +Marcopolo,it +Match Music,it +Milan Channel,it +Nuvolari Motor,it +Odeon,it +Planete,it +Primafila,it +RAI 1,it +RAI 2,it +RAI 3,it +RAI Sat Fiction,it +RETE 4,it +Raisat Album,it +Raisat Art,it +Raisat Cinema,it +Raisat Educational,it +Raisat Gambero Rosso,it +Raisat Nettuno 1,it +Raisat Nettuno 2,it +Raisat Ragazzi,it +Raisat Show,it +Raisat Sport,it +SNAI Sat,it +Salute e benessere,it +Sat 2000,it +Satisfation Club TV,it +Seasons,it +Stream Calcio,it +Stream News,it +Studio Universal,it +TSI 1,it +TSI 2,it +TVL,it +Tele+ 16:9,it +Tele+ Bianco,it +Tele+ Grigio,it +Tele+ Nero,it +VIVA - Rete A,it + +Netherland 1,nl +Netherland 2,nl +Netherland 3,nl + +ATV Avrupa,tr +Kanal D,tr +Kral TV,tr +NTV Turkey,tr +Show TV,tr +Star TV,tr + +Adult Channel,uk +Adventure One,uk +Anglia,uk +BBC Choice,uk +BBC Choice NI,uk +BBC Choice Scotland,uk +BBC Choice Wales,uk +BBC Knowledge,uk +BBC News 24,uk +BBC Parliament,uk +BBC Prime,uk +BBC World,uk +BBC1,uk +BBC1 North. Ireland,uk +BBC1 Scotland,uk +BBC1 Wales,uk +BBC2,uk +BBC2 North. Ireland,uk +BBC2 Scotland,uk +BBC2 Wales,uk +Bangla TV,uk +Bloomberg TV,uk +Border,uk +Bravo (Analogue),uk +Bravo (Digital),uk +British Eurosport,uk +CNBC Europe,uk +CNN,uk +Carlton Central,uk +Carlton Cinema,uk +Carlton Food,uk +Carlton Westcountry,uk +Carlton/LWT,uk +Cartoon Network,uk +Challenge TV,uk +Channel 4,uk +Channel 5,uk +Channel Television,uk +Disc. Animal Planet,uk +Disc. Channel (Ana.),uk +Disc. Civilisations,uk +Disc. Home Leisure,uk +Disc. Sci-Trek,uk +Disc. Travel Advent.,uk +Discovery Channel,uk +Discovery Kids,uk +Discovery Wings,uk +Disney,uk +Euronews,uk +Fantasy Ch. Dig.,uk +Fantasy Channel,uk +Film Four,uk +Fox Kids,uk +Grampian,uk +Granada,uk +Granada Breeze,uk +Granada Men & Motors,uk +Granada Plus,uk +HTV Wales,uk +HTV West,uk +Hallmark,uk +History Channel,uk +ITN News Channel,uk +ITV Sport Channel,uk +ITV Sport Plus,uk +ITV2,uk +Landscape,uk +Living,uk +MTV UK,uk +MUTV,uk +Meridian,uk +National Geographic,uk +Network 2,uk +Nick Junior,uk +Nick Junior Analogue,uk +Nickelodeon (dig.),uk +Nickleodeon (ana.),uk +Pakistani Channel,uk +Paramount Comedy,uk +Performance,uk +Play UK,uk +Playboy TV,uk +QVC,uk +RTE1,uk +Racing Channel,uk +Rapture TV,uk +S2,uk +S4C,uk +S4C digidol,uk +SKY Cinema,uk +SKY Movie Max(SDig.),uk +SKY Movie Max(ana.),uk +SKY News,uk +SKY Premier(Digital),uk +SKY Premier(OnDigi),uk +SKY Sports 1,uk +SKY Sports 2,uk +SKY Sports 3,uk +Sci-Fi Cable,uk +Sci-Fi Satelite,uk +Scottish,uk +Screenshop,uk +Shop!,uk +Sky Movie Max(OnDig),uk +Sky One,uk +Sky One (OnDigital),uk +Sky Premier(Ana.),uk +Sky Sports .com,uk +Sky Sports Extra,uk +Sky Travel,uk +Sony Entertainment,uk +TCM,uk +TCM (Analogue),uk +TG4,uk +TV3,uk +Tara Television,uk +The Box,uk +Trouble Analogue,uk +Trouble Digital,uk +Tyne Tees Television,uk +UK Drama,uk +UK Gold,uk +UK Gold 2,uk +UK Horizons,uk +UK Style,uk +Ulster ,uk +VH1,uk +Yorkshire,uk +Zee TV,uk diff --git a/Tools/epg2timers/get_merkliste.pl b/Tools/epg2timers/get_merkliste.pl new file mode 100755 index 0000000..bb23fa3 --- /dev/null +++ b/Tools/epg2timers/get_merkliste.pl @@ -0,0 +1,82 @@ +#!/usr/bin/perl +# Create a user agent object + +use HTML::Entities; +use HTML::Parser; +use LWP::UserAgent; +use IO::Handle; + +STDOUT->autoflush(1); + +$ua = new LWP::UserAgent; +$ua->agent("Mozilla/9.1 " . $ua->agent); +# $ua->proxy('http', 'http://localhost:8080/'); + +$filename = "merkliste.html"; +$base_url = "http://www.tvtv.de"; +# Hier das Bookmark von TVTV eintragen: +@files_to_fetch = ("/cgi-bin/bookmark.cgi?id=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); + +$num = 0; +$state = 0; + +$p = HTML::Parser->new( api_version => 3, + start_h => [\&fparser_start, "tagname, attr"], + unbroken_text => 1 ); + +foreach $url (@files_to_fetch) { + $nurl = $base_url . $url; + print "Getting " . $nurl . "...\n"; + $req = new HTTP::Request GET => $nurl; + $res = $ua->request($req); + if ($res->is_success) { + open (OUTFILE, ">" . $filename); + print OUTFILE $res->content; + close (OUTFILE); + $p->parse ($res->content); + $p->eof; + } else { + print "...FAILED\n"; + } +} +# Zielordner fuer die Speicherung der Merkliste: + +print "...saved to 'merkliste.html'\n"; +sub fparser_start { + my($tagname, $attr_t) = @_; + my(%attr) = %$attr_t; + + if ($tagname eq "frame") { + if ($state == 1) { + if (($attr{name} eq "frame_main") || + ($attr{name} eq "frame_nav") || + ($attr{name} eq "frame_nav_bottom")) { + push @files_to_fetch, $attr{src}; + } + } + if ($state == 2) { + if (($attr{name} eq "frame_content")) { + push @files_to_fetch, $attr{src}; + } + } + } + if ($tagname eq "a") { + if ($attr{href} ne "") { + $last_href = $attr{href}; + if ($state == 0) { + push @files_to_fetch, $last_href; + $state = 1; + } + } + } + if ($tagname eq "img") { + if ($state == 1) { + if ($attr{src} =~ /b_joblist/i) { + $state = 2; + push @files_to_fetch, $last_href; + } + } + } +} + + diff --git a/Tools/epg2timers/loadvdr.pl b/Tools/epg2timers/loadvdr.pl new file mode 100755 index 0000000..485b0a4 --- /dev/null +++ b/Tools/epg2timers/loadvdr.pl @@ -0,0 +1,89 @@ +#!/usr/bin/perl +# +# 0.01 loadvdr (peter) +# 0.02 delete old entries before updating (peter) +# 0.03 dumped Net::Telnet because of lost connections +# +# please submit diffs to petera@gmx.net +# +# ./epg2timers < merkliste.html | perl -w loadvdr.pl +# +# + +use Socket; +use Getopt::Std; + +@resp = (); + +$Dest = "localhost"; +$Port = 2001; + +$Timeout = 10; # max. seconds to wait for response + +$SIG{ALRM} = sub { Error("timeout"); }; +alarm($Timeout); + +$iaddr = inet_aton($Dest) || Error("no host: $Dest"); +$paddr = sockaddr_in($Port, $iaddr); + +$proto = getprotobyname('tcp'); +socket(SOCK, PF_INET, SOCK_STREAM, $proto) || Error("socket: $!"); +connect(SOCK, $paddr) || Error("connect: $!"); +select(SOCK); $| = 1; +Receive_void(); + +Send("lstt"); + +foreach $item (reverse @resp){ + if ($item =~ /^250.(\d{1,2}).*\(epg2timers\)/) { + Send_void("DELT $1"); + } +} + +while (defined ($line = <STDIN>)) { + chomp $line; + Send_void("UPDT $line"); +} + +Send("quit"); +close(SOCK) || Error("close: $!"); + + + +sub Send +{ + my $cmd = shift || Error("no command to send"); + print SOCK "$cmd\r\n"; + Receive(); +} + +sub Send_void +{ + my $cmd = $_[0]; + print SOCK "$cmd\r\n"; + Receive_void(); +} + +sub Receive +{ + while (<SOCK>) { + chomp; + push @resp,$_; + last if substr($_, 3, 1) ne "-"; + } +} + +sub Receive_void +{ + while (<SOCK>) { + last if substr($_, 3, 1) ne "-"; + } +} + +sub Error +{ + print STDERR "@_\n"; + close(SOCK); + exit 0; +} + diff --git a/Tools/epg2timers/update_timers b/Tools/epg2timers/update_timers new file mode 100755 index 0000000..3849841 --- /dev/null +++ b/Tools/epg2timers/update_timers @@ -0,0 +1,22 @@ +#!/bin/sh + +# update_timers: retrieve a new "merkliste" from http://tvtv.de, +# convert it to vdr format and transmit it to vdr via SVDRP. + +TOOLDIR="/home/cko/bin" +CHANPATH="/home/cko/VDR/channels.conf" + +cd /tmp +rm -f merkliste.html + +# if you have a slow dial up connection to your name server and/or ISP, +# this will avoid a timeout in get_merkliste.pl. +ping -c 2 www.tvtv.de + +# get the "merkliste". +$TOOLDIR/get_merkliste.pl + +if [ -s merkliste.html ] ; then + # convert merkliste.html to timers.conf format and transmit it to vdr. + $TOOLDIR/epg2timers $CHANPATH < merkliste.html | $TOOLDIR/loadvdr.pl +fi diff --git a/Tools/epg2timers/update_timers.old b/Tools/epg2timers/update_timers.old new file mode 100755 index 0000000..c4c3f5c --- /dev/null +++ b/Tools/epg2timers/update_timers.old @@ -0,0 +1,24 @@ +#!/bin/sh + +# update_timers: load a new "merkliste" from http://tvtv.de +# and create a new VDR timer configuration file (timers.conf) +# from it. Restart VDR if the timers have changed. + +TOOLDIR="/home/cko/bin" +VDRDIR="/home/cko/VDR" + +cd /tmp +rm -f merkliste.html epgtimers.new epgtimers.old vdrtimers.old +ping -c 2 www.tvtv.de +$TOOLDIR/get_merkliste.pl +if [ -s merkliste.html ] ; then + $TOOLDIR/epg2timers $VDRDIR/channels.conf < merkliste.html | sort -t: +2.0 -5.0 > epgtimers.new + fgrep '(epg2timers)' $VDRDIR/timers.conf | sort -t: +2.0 -5.0 > epgtimers.old + if ! cmp -s epgtimers.old epgtimers.new ; then + /sbin/killproc $VDRDIR/vdr + fgrep -v '(epg2timers)' $VDRDIR/timers.conf > vdrtimers.old + cat epgtimers.new vdrtimers.old | sort -t: +2.0 -5.0 > $VDRDIR/timers.conf + echo "Timers updated." + fi +fi +rm -f merkliste.html epgtimers.new epgtimers.old vdrtimers.old diff --git a/Tools/master-timer/LIESMICH b/Tools/master-timer/LIESMICH index 9694875..8e53da9 100644 --- a/Tools/master-timer/LIESMICH +++ b/Tools/master-timer/LIESMICH @@ -1,106 +1,70 @@ - Master-Timer - ============ +Master-Timer +============ +(w) by Matthias Schniedermeyer (ms@citd.de) 1. Einleitung ------------- Master-Timer ist ein System zum automatischen Aufnehmen von Serien und Filmen. +Beim Aufruf werden die Konfigurationsdateien gelesen, die Datei "epg.data" im +aktuellen Verzeichnis nach passenden Sendungen durchsucht und die ermittelten +Timer per SVDRP in VDR programmiert. Danach beendet sich Master-Timer. -2. Voraussetzungen ------------------- +Entsprechend empfiehlt es sich, Master-Timer per cronjob aufzurufen. -VDR liefert die "epg.data". - -3. Konfigurationsdateien +2. 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 -------------- +Alle Konfigurationsdateien liegen unter "~/.master-timer". Es werden +regulaere Ausdruecke ohne Unterscheidung der Gross-/Kleinschreibung +benutzt. + +config: Die Hauptkonfigurationsdatei. + +deepblack: Eine Negativliste von Titeln, die man NIEMALS NIMMER sehen will. + Die epg-datensaetze werden beim Parsen einfach entfernt. + +subtitle-movies: Eine Liste der "Subtitel", die ein Zeichen fuer einen Film + sind (soweit die von den Sendern richtig ausgefuellt sind). Die + Einstellungen in dieser Datei werden fuer das Makro "MOVIE" in torecord + benutzt. + Kommentarzeilen sind nicht erlaubt. + +torecord: Liste der aufzunehmenden Filme. + +done: Liste der Titel/Subtitel, die bereits aufgenommen wurden. Timer, die + hier auftauchen, werden in VDR automatisch geloescht. + +channels-to-scan: Diese Kanaele werden vom Skript "scan-channels" durch- + geschaltet (zwecks Einlesen der EPG-Daten). Die Datei wird von Master- + Timer selbst nicht benutzt. + +3. Anmerkungen +-------------- + +- einige Sender fuellen die EPG-Felder nicht korrekt aus. Diese Fehler + werden von Master-Timer automatisch korrigiert. + + Pro7: Title aus dem Subtitle entfernen '<Title> / <Subtitle>' + Pro7: Timer zwischen 00:00 und 05:00 werden einen Tag in die Zukunft + verschoben (Als unguten Nebeneffekt hat dies zur Folge das zum + aktuellen Tag NICHT die epg-Daten das folgenden Tags (=Das was in + der Fernsehzeitschrift noch auf der gleichen Seite ist) mehr gesendet + werden. Wenn man also "Last Minute" etwas aufnehmen will, dann muss + man (leider) einen Timer "per Hand" in VDR einprogrammieren!) + VOX/VIVA: Subtitle ist in "" eingeschlossen nd nach ". " steht die description + VIVA: Wenn der Subtitle mit einem Space beginnt, dann wird der komplette + Subtitle in die Description verschoben + RTL2: Wenn der EPG-Datensatz eine Dauer von kleiner/gleich 1 Sekunde hat, + wird er einfach verworfen. + +Bis auf den jeweils 2ten VIVA&Pro-7 Bug werden die Fixes an jedem +epg-Datensatz ausprobiert. + +4. Bekannte 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 +- Es wird nicht geprueft ob noch genug DVB-Karten vorhanden sind +- "Joined timers" werden nur automatisch geloescht wenn alle dazugehoerigen + Sendungen "done" sind. diff --git a/Tools/master-timer/README b/Tools/master-timer/README index c71e6e1..05cc41a 100644 --- a/Tools/master-timer/README +++ b/Tools/master-timer/README @@ -1,43 +1,49 @@ - Master-Timer (w) by Matthias Schniedermeyer (ms@citd.de) - ============ +Master-Timer +============ +(w) by Matthias Schniedermeyer (ms@citd.de) 1. Introduction --------------- -Master-Timer ist a system for recording Films/Series automaticaly +Master-Timer is a system designed for automatically recording movies. +Upon execution it reads its configuration files, scans the file "epg.data" +in the current directory for matching titles and programs them via SVDRP +into VDR. -2. Requierements ----------------- +You may for example run Master-Timer as a cron job. -epg.data - -3. Config-Files +2. 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!) +Configuration files are located in "~/.master-timer". Each entry is a +regular expression so you can use all Perl style REs you want. They are +processed case insensitive. + +config: Main configuration file. -deepblack: Blacklist of "Titles" you NEVER EVER want to get to you eyes +deepblack: Blacklist of "titles" you NEVER EVER want to see. -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*) +subtitle-movies: A list of "Subtitles" which indicate a movie (used + by the "MOVIE" macro in torecord). + For channels that correctly fill out the subtitle e.g. it will not + work with *eRTL*. -torecord: The titles/subtitles/Description you want to record +torecord: The titles 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) + Programmed timers which got inserted into "done" will be deleted + automatically. + +channels-to-scan: Used only by the separate "scan-channels" script which + switches through channels in order to get EPG data. 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) +- Recordings overlapping on the same channel will be joined into one timer +- Title/Subtitle/Descriptions are "fixed" for channels that don't fill them + out correctly (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 @@ -48,5 +54,5 @@ done: The titles/subtitles which are already recorded/should not be recorded ------------- - 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 +- Overlapping timers on the same channel are always joined +- Joined timers which are "done" don't get deleted automatically diff --git a/Tools/master-timer/THANKS b/Tools/master-timer/THANKS new file mode 100644 index 0000000..04bb13b --- /dev/null +++ b/Tools/master-timer/THANKS @@ -0,0 +1,18 @@ +Klaus Schmidinger + - VDR + +Malte Kiesel + - Suggestions + - Bug Reports + - Documentation Updates for README/LIESMICH/torecord/deepblack/config + +Guido Fiala + - Suggestions + - Bug Repots + - finding bugs i found just before i read that part of his mails + (First the bug, then the errormessages! Otherwise i will search/find + the bug myself :-))) ) + +Axel Gruber + - Suggestions + - Bug Reports diff --git a/Tools/master-timer/Todo b/Tools/master-timer/Todo index 722ee96..1757ade 100644 --- a/Tools/master-timer/Todo +++ b/Tools/master-timer/Todo @@ -1,9 +1,5 @@ -- "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 diff --git a/Tools/master-timer/convert-DTV2VDR.pl b/Tools/master-timer/convert-DTV2VDR.pl new file mode 100755 index 0000000..fb2a783 --- /dev/null +++ b/Tools/master-timer/convert-DTV2VDR.pl @@ -0,0 +1,151 @@ +#!/usr/bin/perl -w + +use strict; + +# The EPG-Entrys +my (%Entry, %channel, $mode); + +# 0 = VDR -> DTV +# 1 = DTV -> VDR +$mode = 0; + +read_channel_list(); +if ($mode) { + &read_dtv(); + &read_epgdata(); +} else { + &read_epgdata(); + &read_dtv(); +} +&print_VDR(); + +sub read_epgdata { + my ($channel, $duration, $title, $subtitle, $description, $time); + open (FI,"epg.data") or 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$/) { + if ($mode) { + # DTV -> VDR + $Entry{$channel}{$time}{subtitle}=$subtitle if ($subtitle); + if ($description) { + if ($Entry{$channel}{$time}{description}) { + $Entry{$channel}{$time}{description} = "DTV: '$Entry{$channel}{$time}{description}' VDR: '$description'"; + } else { + $Entry{$channel}{$time}{description} = "DTV: '' VDR: '$description'"; + } + } + } else { + # VDR -> DTV + $Entry{$channel}{$time}{title}=$title; + $Entry{$channel}{$time}{duration}=$duration; + $Entry{$channel}{$time}{subtitle}=$subtitle if ($subtitle); + $Entry{$channel}{$time}{description}=$description if ($description); + } + } + } + } + } + close (FI); +} + +sub read_dtv { + my ($channel, $time, $duration, $title, $category, $subtitle, $description); + open (FI,$ARGV[0]) or die "Can't open DTV-File"; + + while (<FI>) { + chomp; + ($channel, $time, $duration, $title, $category, $subtitle, $description) = split (/\|/); + if (!$channel{$channel}) { + next; + } + $channel = $channel{$channel}; + if ($mode) { + # DTV -> VDR + if (!$subtitle && $description =~ /^\"(.*?)\"\:\s(.*)/) { + $Entry{$channel}{$time}{subtitle} = $1; + $description = $2; + } + $Entry{$channel}{$time}{title} = $title; + $Entry{$channel}{$time}{duration} = $duration; + $Entry{$channel}{$time}{subtitle} = $subtitle if ($subtitle); + $Entry{$channel}{$time}{category} = $category if ($category); + $Entry{$channel}{$time}{description} = $description if ($description); + } else { + # VDR -> DTV + $Entry{$channel}{$time}{category} = $category if ($category); + if ($description) { + if (!$Entry{$channel}{$time}{subtitle} && $description =~ /^\"(.*?)\"\:\s(.*)/) { + $Entry{$channel}{$time}{subtitle} = $1; + $description = $2; + } + if ($Entry{$channel}{$time}{description}) { + $Entry{$channel}{$time}{description} = "DTV: '$description' VDR: '$Entry{$channel}{$time}{description}'"; + } else { + $Entry{$channel}{$time}{description} = "DTV: '$description' VDR: ''"; + } + } + } + } + close (FI); +} + +sub read_channel_list { + my ($old, $new); + open (FI,"$ENV{HOME}/.master-timer/convert-channel-list") or die ("Can't read channel-List"); + while (<FI>) { + chomp; + ($old, $new) = split (/\|/); + $channel{$old} = $new; + } + close (FI); +} + +sub print_VDR() { + my ($channel, $title, $time); + foreach $channel (sort keys %Entry) { + print "C 1 $channel\n"; + foreach $time (sort keys %{%Entry->{$channel}}) { + if ($Entry{$channel}{$time}{duration}) { + print "E 1 $time $Entry{$channel}{$time}{duration}\n"; + print "K $Entry{$channel}{$time}{category}\n" if ($Entry{$channel}{$time}{category}); + print "T $Entry{$channel}{$time}{title}\n"; + print "S $Entry{$channel}{$time}{subtitle}\n" if ($Entry{$channel}{$time}{subtitle}); + print "D $Entry{$channel}{$time}{description}\n" if ($Entry{$channel}{$time}{description}); + print "e\n"; + } + } + print "c\n"; + } +} diff --git a/Tools/master-timer/convert-oldtorecord.pl b/Tools/master-timer/convert-oldtorecord.pl new file mode 100755 index 0000000..853c20b --- /dev/null +++ b/Tools/master-timer/convert-oldtorecord.pl @@ -0,0 +1,61 @@ +#!/usr/bin/perl + +while (<>) + { + chomp; + if ($_ && !(/^\#/)) + { + ($title, $subtitle, $description, $channel, $timeframe, $prio, $timer_title, $margin, $machine) = split (/\|/,$_); + + if ($timer_title) { + print "[$timer_title]\n"; + } elsif ($title) { + print "[$title]\n"; + } elsif ($subtitle) { + print "[$subtitle]\n"; + } elsif ($description) { + print "[$description]\n"; + } else { + die ("Illegal Format"); + } + + # Accept torecord only if it is for the current machine + if ($title) + { + print "Title = $title\n"; + } + if ($subtitle) + { + print "Subtitle = $subtitle\n"; + } + if ($description) + { + print "Description = $description\n"; + } + if ($channel) + { + print "Channel = $channel\n"; + } + if ($timeframe) + { + print "Timeframe = $timeframe\n"; + } + if ($prio) + { + print "Prio = $prio\n"; + } + if ($timer_title) + { + print "Timertitle = $timer_title\n"; + } + if ($margin) + { + print "Margin = $margin\n"; + } + if ($machine) + { + print "Instance = $machine\n"; + } + print "\n"; + } + } diff --git a/Tools/master-timer/master-timer.pl b/Tools/master-timer/master-timer.pl index 3b98acc..5ddf909 100755 --- a/Tools/master-timer/master-timer.pl +++ b/Tools/master-timer/master-timer.pl @@ -5,6 +5,8 @@ use strict; use Socket; # For converting the Timers, read from VDR, back to Unix-Timestamps use Time::Local; +# For parsing the command line +use Getopt::Std; # Debugmode # You have to add the following numbers to build the debug-var @@ -13,7 +15,9 @@ use Time::Local; # 4 : Show when a timer will be deleted # 8 : Dump the "Done" REs # 16 : Verbose Config-Reading -my $debug = 0; +# 32 : Dump Program Variable +# 64 : Excessive deepblack/torecord debuging +my $debug = 6; # The Supervariable Program # %Program{$title}{$channel}{$time}{duration} @@ -23,26 +27,51 @@ my $debug = 0; # The Supervariable Timer # %Timer{$time}{$channel}{$title}{duration} # {subtitle} +# {description} # {prio} +# {lifetime} # {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 +# The Supervariable torecord/deepblack +# $torecord{timercount} +# {titleRE} +# {subtitleRE} +# {descriptionRE} +# {title}[COUNT] +# {subtitle}[COUNT] +# {description}[COUNT] +# {timeframe}[COUNT] +# {blackchannel}[COUNT] or {channel}[COUNT] +# {weekday}[COUNT] +# {minlength}[COUNT] +# {maxlength}[COUNT] +# {prio}[COUNT] +# {timertitle}[COUNT] +# {marginstart}[COUNT] +# {marginstop}[COUNT] +# {instance}[COUNT] + # Variable-Definition my (%Program, @channels, %channels, %Timer); # Which Subtitles are Movies my ($subtitle_movie); +my ($test_subtitle_movie); # Blacklist -my ($title_deepblack); +my (%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); +my (%torecord); + +# The Commandline +my (%Opts); # Default Priority for Timers (Config: defaultprio) my $default_prio = 50; @@ -63,1107 +92,1321 @@ 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); +# Where are the Config-Files +my $configdir = "$ENV{HOME}/.master-timer"; -sub sub_die - { - my ($error) = @_; - &closesocket(); - die "$error"; - } +# Should the description be transfered to VDR? +my $Description = 0; +# Working-Variables +my ($title, $duration, $subtitle, $channel, $time, $description, $category, $hit); +my (@time, @date); -if ($ARGV[0]) - { - $currentVDR = $ARGV[0]; - } +END { + &closesocket(); +} &init(); &dumpdone() if ($debug & 8); -&dumptorecord() if ($debug & 1); +&dumptorecord("torecord") if ($debug & 1); +&dumptorecord("deepblack") if ($debug & 1); +print "Subtitle-Movie \"$subtitle_movie\"\n" if($debug & 1); +# If we only have to dump the running series then exit after dumping them +if ($Opts{s}) { + &dumpepgdata; + exit 0; +} +&processdone(); &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); - } + +if ($jointimers) { + &jointimers(); + print "Timers after joining\n" if ($debug & 2); + &dumptimers() if ($debug & 2); +} + +&dumpepgdata if ($debug & 32); &printtimers(); &transfertimers(); -&closesocket(); + +# +# End of Program +# # # Subfunctions # -sub dumpdone() - { - print "Start Done-dump\n"; - print "Titledone: \"$title_done\"\n"; - print "Subtitledone \"$subtitle_done\"\n"; - print "End Done-dump\n"; +sub dumpdone() { + print "Start Done-dump\n"; + print "Titledone: \"$title_done\"\n"; + print "Subtitledone \"$subtitle_done\"\n"; + print "End Done-dump\n"; +} + +sub dumpepgdata () { + print "Start EPG-Dump\n"; + foreach $title (sort keys %Program) { + foreach $channel (sort keys %{%Program->{$title}}) { + foreach $time (sort {$a <=> $b} keys %{%Program->{$title}->{$channel}}) { + print "Title: \"$title\" "; + if (!$Opts{s}) { + print "Subtitle: \"$Program{$title}{$channel}{$time}{subtitle}\" " if ($Program{$title}{$channel}{$time}{subtitle}); + print "Time: \"$time\""; + } + print "Channel: \"$channel\""; + print "\n"; + if ($Opts{s}) { + last; + } + } + } + } + print "End EPG-Dump\n"; +} + + +sub dumptorecord() { + my ($context) = shift; + my ($rContext); + + if ($context eq "torecord") { + $rContext = \%torecord; + } elsif ($context eq "deepblack") { + $rContext = \%deepblack; + } else { + die ("Illegal Context"); } -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 "Start $context-dump\n"; + print "Regex-Title: $$rContext{titleRE}\n"; + print "Regex-Subtitle: $$rContext{subtitleRE}\n"; + print "Regex-Description: $$rContext{descriptionRE}\n"; + foreach my $num (0 .. $$rContext{timercount}) { + print "Entry Number $num: "; + + print "Title: \"$$rContext{title}[$num]\" " if ($$rContext{title}[$num]); + print "Title: \"\" " unless ($$rContext{title}[$num]); + + print "Subtitle: \"$$rContext{subtitle}[$num]\" "if ($$rContext{subtitle}[$num]); + print "Subtitle: \"\" " unless ($$rContext{subtitle}[$num]); + + print "Description: \"$$rContext{description}[$num]\" " if ($$rContext{description}[$num]); + print "Description: \"\" " unless ($$rContext{description}[$num]); + + print "Category: \"$$rContext{category}[$num]\" " if ($$rContext{category}[$num]); + print "Category: \"\" " unless ($$rContext{category}[$num]); - print "Description: \"$description_torecord[$num]\" " if ($description_torecord[$num]); - print "Description: \"\" " unless ($description_torecord[$num]); + print "Timeframe: \"$$rContext{timeframe}[$num]\" " if ($$rContext{timeframe}[$num]); + print "Timeframe: \"\" " unless ($$rContext{timeframe}[$num]); - print "Timeframe: \"$timeframe_torecord[$num]\" " if ($timeframe_torecord[$num]); - print "Timeframe: \"\" " unless ($timeframe_torecord[$num]); + print "Weekday: \"$$rContext{weekday}[$num]\" " if ($$rContext{weekday}[$num]); + print "Weekday: \"\" " unless ($$rContext{weekday}[$num]); - print "Channel: \"". join (";",@{$channel_torecord[$num]})."\" " if ($channel_torecord[$num]); - print "Channel: \"\" " unless ($channel_torecord[$num]); + print "Channel: \"$$rContext{channel}[$num]\" " if ($$rContext{channel}[$num]); + print "Channel: \"\" " unless ($$rContext{channel}[$num]); - print "Prio: \"$prio_torecord[$num]\" " if ($prio_torecord[$num]); - print "Prio: \"\" " unless ($prio_torecord[$num]); + print "Blackchannel: \"$$rContext{blackchannel}[$num]\" " if ($$rContext{blackchannel}[$num]); + print "Blackchannel: \"\" " unless ($$rContext{blackchannel}[$num]); - print "Timertitle: \"$timer_title_torecord[$num]\" " if ($timer_title_torecord[$num]); - print "Timertitle: \"\" " unless ($timer_title_torecord[$num]); + print "Prio: \"$$rContext{prio}[$num]\" " if ($$rContext{prio}[$num]); + print "Prio: \"\" " unless ($$rContext{prio}[$num]); + + print "Timertitle: \"$$rContext{timertitle}[$num]\" " if ($$rContext{timertitle}[$num]); + print "Timertitle: \"\" " unless ($$rContext{timertitle}[$num]); - print "Marginstart: \"$marginstart_torecord[$num]\" " if ($marginstart_torecord[$num]); - print "Marginstart: \"\" " unless ($marginstart_torecord[$num]); + print "Marginstart: \"$$rContext{marginstart}[$num]\" " if ($$rContext{marginstart}[$num]); + print "Marginstart: \"\" " unless ($$rContext{marginstart}[$num]); - print "Marginstop: \"$marginstop_torecord[$num]\" " if ($marginstop_torecord[$num]); - print "Marginstop: \"\" " unless ($marginstop_torecord[$num]); + print "Marginstop: \"$$rContext{marginstop}[$num]\" " if ($$rContext{marginstop}[$num]); + print "Marginstop: \"\" " unless ($$rContext{marginstop}[$num]); - print "Machine: \"$machine_torecord[$num]\" " if ($machine_torecord[$num]); - print "Machine: \"\" " unless ($machine_torecord[$num]); + print "Minlength: \"$$rContext{minlength}[$num]\" " if ($$rContext{minlength}[$num]); + print "Minlength: \"\" " unless ($$rContext{minlength}[$num]); - print "\n"; - } - print "End Torecord-dump\n"; + print "Maxlength: \"$$rContext{maxlength}[$num]\" " if ($$rContext{maxlength}[$num]); + print "Maxlength: \"\" " unless ($$rContext{maxlength}[$num]); + + print "Instance: \"$$rContext{instance}[$num]\" " if ($$rContext{instance}[$num]); + print "Instance: \"\" " unless ($$rContext{instance}[$num]); + + print "\n"; } + print "End $context-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"; - } - } +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, $lifetime, @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}; + $lifetime = $Timer{$time}{$channel}{$title}{lifetime}; + $realtitle = $Timer{$time}{$channel}{$title}{real_title}; + print "2:$channels{$channel}{number}:$date[1]:$time[0]$time[1]:$time2[0]$time2[1]:$prio:$lifetime:$title:Title: \"$realtitle\"||Subtitle: \"$subtitle\":$Timer{$time}{$channel}{$title}{VDR}\n"; } - print "End Timers-dump\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 printtimers() { + foreach $time (sort {$a <=> $b} keys %Timer) { + foreach $channel (sort keys %{%Timer->{$time}}) { + foreach $title (sort keys %{%Timer->{$time}->{$channel}}) { + my ($prio, $lifetime, @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}; + $lifetime = $Timer{$time}{$channel}{$title}{lifetime}; + $realtitle = $Timer{$time}{$channel}{$title}{real_title}; + + print "2:$channels{$channel}{number}:$date[1]:$time[0]$time[1]:$time2[0]$time2[1]:$prio:$lifetime:$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); - } - } +sub transfertimers() { + foreach $time (sort {$a <=> $b} keys %Timer) { + foreach $channel (sort keys %{%Timer->{$time}}) { + foreach $title (sort keys %{%Timer->{$time}->{$channel}}) { + my ($prio, $lifetime, $description, @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}; + $lifetime = $Timer{$time}{$channel}{$title}{lifetime}; + if ($Description) { + $description = "||Description :\"$Timer{$time}{$channel}{$title}{description}\""; + } else { + $description = ""; } + $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:$lifetime:$title:Title: \"$realtitle\"||Subtitle: \"$subtitle\"$description"); + 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); - } +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 Weekday +sub GetWDay { + my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(shift); + return ($wday); +} # 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); - } +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//; +sub correct_epg_data { + if ($subtitle) { + # For Pro-7. Remove $title from $subtitle + $subtitle =~ s/\Q$title\E\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; - } - } + # 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) { + my $one = $1; + my $two = $2; + if ($description =~ /^DTV\:\s\'(.*)\' VDR:\s\'\'$/) { + $description = "DTV: '$1' VDR: '$two'"; + $subtitle = $one; + } else { + die ("Title: \"$title\" Channel: \"$channel\" Subtitle: \"$subtitle\"\nDescription: \"$description\"\n"); + } } - - # 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; - } + $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; - } - } +sub addtimer { + my ($title, $realtitle, $subtitle, $channel, $time, $duration, $prio, $lifetime, $description, $VDR, $time2, $title2, $channel2, $marginstart, $marginstop); + ($title, $realtitle, $subtitle, $description, $channel, $time, $duration, $prio, $lifetime, $VDR, $marginstart, $marginstop) = @_; +# print "Title: \"$title\" Realtitle: \"$realtitle\" Subtitle: \"$subtitle\" Channel: \"$channel\" Time: \"$time\" Duration: \"$duration\" Prio: \"$prio\" VDR: \"$VDR\"\n"; + + 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)) { + return; } + } + } + $time -= $marginstart; + $duration += $marginstart + $marginstop; + $Timer{$time}{$channel}{$title}{duration}=$duration; + $Timer{$time}{$channel}{$title}{subtitle}=$subtitle; + $Timer{$time}{$channel}{$title}{description}=$description; + $Timer{$time}{$channel}{$title}{prio}=$prio; + $Timer{$time}{$channel}{$title}{lifetime}=$lifetime; + $Timer{$time}{$channel}{$title}{VDR}=$VDR; + $Timer{$time}{$channel}{$title}{real_title}=$realtitle; +} - 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) = @_; -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 -# { -# -# } -# } +# 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); +} - 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 delprogram() { + my ($title, $channel, $time); + ($title, $channel, $time) = @_; -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; - } - } - } - } - } + delete $Program{$title}{$channel}{$time}; + delete $Program{$title}{$channel} if (keys %{ $Program{$title}{$channel} } == 1); + delete $Program{$title} if (keys %{ $Program{$title} } == 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, $description, $prio, $lifetime, $realtitle, $duration2, $subtitle2, $description2, $prio2, $lifetime2, $realtitle2); + # Values from Lower-Timer + $duration = $Timer{$times[$counter]}{$channel}{$title}{duration}; + $subtitle = $Timer{$times[$counter]}{$channel}{$title}{subtitle}; + $description = $Timer{$times[$counter]}{$channel}{$title}{description}; + $prio = $Timer{$times[$counter]}{$channel}{$title}{prio}; + $lifetime = $Timer{$times[$counter]}{$channel}{$title}{lifetime}; + $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}; + $description2 = $Timer{$times[$counter + 1]}{$channel2}{$title2}{description}; + $prio2 = $Timer{$times[$counter + 1]}{$channel2}{$title2}{prio}; + $lifetime2 = $Timer{$times[$counter + 1]}{$channel2}{$title2}{lifetime}; + $realtitle2 = $Timer{$times[$counter + 1]}{$channel2}{$title2}{real_title}; + + # Use the Higher Priority/Lifetime for the new Timer + $prio = ($prio > $prio2) ? $prio : $prio2; + $lifetime = ($lifetime > $lifetime2) ? $lifetime : $lifetime2; + + # 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", "$description\~$description2", $channel, $times[$counter], $duration2 + ($times[$counter + 1 ] - $times[$counter]),$prio,$lifetime,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; + } } + $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; - } - } - } +sub process_torecord { + my ($subtitle, $description, $prio, $lifetime, $timertitle, $counter); + foreach $title (sort keys %Program) { + foreach $channel (sort keys %{%Program->{$title}}) { + foreach $time (sort {$a <=> $b} keys %{%Program->{$title}->{$channel}}) { + + $counter = &testtimer("torecord", $title, $channel, $time); + if ($counter ne "Nothing") { + + # What Priority + if ($torecord{prio}[$counter]) { + $prio = $torecord{prio}[$counter]; + } + else { + $prio = 50; + } + + # What Lifetime + if ($torecord{lifetime}[$counter]) { + $lifetime = $torecord{lifetime}[$counter]; + } + else { + $lifetime = 50; + } + + # What Title to use for the timer + if ($torecord{timertitle}[$counter]) { + $timertitle = $torecord{timertitle}[$counter] } + elsif ($torecord{title}[$counter]) { + $timertitle = $torecord{title}[$counter] + } + else { + $timertitle = $title; + } + + # What subtitle to use + if ($Program{$title}{$channel}{$time}{subtitle}) { + $subtitle = $Program{$title}{$channel}{$time}{subtitle}; + } + else { + $subtitle = ""; + } + + # What Description to use + if ($Program{$title}{$channel}{$time}{description}) { + $description = $Program{$title}{$channel}{$time}{description}; + } + else { + $description = ""; + } + + &addtimer ($timertitle,$title,$subtitle,$description,$channel,$time,$Program{$title}{$channel}{$time}{duration},$prio,$lifetime,0,$torecord{marginstart}[$counter],$torecord{marginstop}[$counter]); + } } + } } +} -# 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 "-"; - } +# Test if a torecord/deepblack Entry matches the current EPG-Data-Field +sub testtimer { + my ($context) = shift; + my ($title) = shift; + my ($channel) = shift; + my ($time) = shift; + my ($counter, $rContext); + + if ($context eq "torecord") { + $rContext = \%torecord; + } elsif ($context eq "deepblack") { + $rContext = \%deepblack; + } else { + die ("Illegal Context"); } -# 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 "-"; + if ($debug & 64) { + print "\n"; + print "Context: \"$context\"\nTitle: \"$title\"\n"; + print "Subtitle: \"$Program{$title}{$channel}{$time}{subtitle}\"\n" if ($Program{$title}{$channel}{$time}{subtitle}); + print "Description \"$Program{$title}{$channel}{$time}{description}\"\n" if ($Program{$title}{$channel}{$time}{description}); + print "Category \"$Program{$title}{$channel}{$time}{category}\"\n" if ($Program{$title}{$channel}{$time}{category}); + print "Channel: $channel\n"; + print "Time: $time\n"; + print "Duration: $Program{$title}{$channel}{$time}{duration}\n"; + } + + # First look if any of the Title/Subtitle/Description REs match + if ($title =~ /$$rContext{titleRE}/i) { + print "Title hit\n" if ($debug & 64); + } + elsif ($Program{$title}{$channel}{$time}{subtitle} && $Program{$title}{$channel}{$time}{subtitle} =~ /$$rContext{subtitleRE}/i) { + print "SubTitle hit\n" if ($debug & 64); + }elsif ($Program{$title}{$channel}{$time}{subtitle} && $test_subtitle_movie && $Program{$title}{$channel}{$time}{subtitle} =~ /$subtitle_movie/) { + print "SubTitle-Movie hit\n" if ($debug & 64); + } + elsif ($Program{$title}{$channel}{$time}{description} && $Program{$title}{$channel}{$time}{description} =~ /$$rContext{descriptionRE}/i) { + print "Description hit\n" if ($debug & 64); + } else { + # No "Fast"-hit. Exiting + return "Nothing"; + } + + # Now look if we have a "exact" hit + print "In Exact Hit Loop\n" if ($debug & 64); + foreach my $counter (0 .. $$rContext{timercount}) { + + print "Before Title Match\n" if ($debug & 64); + if ($$rContext{title}[$counter]) { + print "In Title Match \"$$rContext{title}[$counter]\"\n" if ($debug & 64); + if (!($title =~ /$$rContext{title}[$counter]/i)) { + print "Title rejected\n" if ($debug & 64); + next; + } + } + + print "Before Subtitle Match\n" if ($debug & 64); + if ($$rContext{subtitle}[$counter]) { + print "In Subtitle Match \"$$rContext{subtitle}[$counter]\"\n" if ($debug & 64); + if ($Program{$title}{$channel}{$time}{subtitle}) { + if ($$rContext{subtitle}[$counter] =~ /^movie$/i) { + if (!($Program{$title}{$channel}{$time}{subtitle} =~ /$subtitle_movie/i)) { + print "Subtitle rejected 1\n" if ($debug & 64); + next; + } + } + elsif ($$rContext{subtitle}[$counter] =~ /^\!movie$/i) { + if (($Program{$title}{$channel}{$time}{subtitle} =~ /$subtitle_movie/i)) { + print "Subtitle rejected 2\n" if ($debug & 64); + next; + } + } + elsif (!($Program{$title}{$channel}{$time}{subtitle} =~ /$$rContext{subtitle}[$counter]/i)) { + print "Subtitle rejected 3\n" if ($debug & 64); + next; + } + } else { + # We had a Subtitle, but epg.data did not have a subtitle for this record so no chance to record this + print "Subtitle rejected 4\n" if ($debug & 64); + next; + } + } + + print "Before Description Match\n" if ($debug & 64); + if ($$rContext{description}[$counter]) { + print "In Description Match \"$$rContext{description}[$counter]\"\n" if ($debug & 64); + if ($Program{$title}{$channel}{$time}{description}) { + if (!($Program{$title}{$channel}{$time}{description} =~ /$$rContext{description}[$counter]/i)) { + print "Description rejected 1\n" if ($debug & 64); + next; + } + } + elsif (!$$rContext{title}[$counter] && !$$rContext{subtitle}[$counter]) { + print "Description rejected 2\n" if ($debug & 64); + next; + } + } + + print "Before Category Match\n" if ($debug & 64); + if ($$rContext{category}[$counter]) { + print "In Category Match \"$$rContext{category}[$counter]\"\n" if ($debug & 64); + if ($Program{$title}{$channel}{$time}{category}) { + my ($left, $right); + ($left, $right) = split (/\//, $$rContext{category}[$counter]); + if ($left) { + print "In Category Match Left \"$left\"\n" if ($debug & 64); + if (!($Program{$title}{$channel}{$time}{category} =~ /^$left\//)) { + print "Category rejected 1\n" if ($debug & 64); + next; + } + } + if ($right) { + print "In Category Match Right \"$right\"\n" if ($debug & 64); + if (!($Program{$title}{$channel}{$time}{category} =~ /\/$right$/)) { + print "Category rejected 2\n" if ($debug & 64); + next; + } + } + } else { + # We had a Category, but the epg.data not. So discard this Entry + print "Category rejected 3\n" if ($debug & 64); + next; + } + } + + print "Before Channel Match\n" if ($debug & 64); + if ($$rContext{channel}[$counter]) { + print "In Channel Match Whitelist-Mode \"$$rContext{channel}[$counter]\"\n" if ($debug & 64); + if (!($channel =~ /$$rContext{channel}[$counter]/)) { + print "Channel rejected\n" if ($debug & 64); + next; + } + } + + if ($$rContext{blackchannel}[$counter]) { + print "In Channel Match Blacklist-Mode \"$$rContext{blackchannel}[$counter]\"\n" if ($debug & 64); + if ($channel =~ /$$rContext{blackchannel}[$counter]/) { + print "Channel rejected\n" if ($debug & 64); + next; + } + } + + print "Before Timeframe Match\n" if ($debug & 64); + if ($$rContext{timeframe}[$counter]) { + print "In Timeframe Match \"$$rContext{timeframe}[$counter]\"\n" if ($debug & 64); + my (@time, $time2, $ctime, $ctime2); + @time = GetTime($time); + $time2 = "$time[0]$time[1]"; + + ($ctime, $ctime2) = split (/\-/,$$rContext{timeframe}[$counter]); + + if (!$ctime) { + $ctime = "0"; + } + if (!$ctime2) { + $ctime2 = "2400"; + } + + if ($ctime < $ctime2) { + if (!($time2 >= $ctime && $time2 <= $ctime2)) { + print "Timeframe rejected 1\n" if ($debug & 64); + next; + } + } + else { + if (!(($time2 >= $ctime && $time2 <= "2400") || ($time2 >= "0" && $time2 <= $ctime2))) { + print "Timeframe rejected 2\n" if ($debug & 64); + next; } } + } + + print "Before Weekday Match\n" if ($debug & 64); + if ($$rContext{weekday}[$counter]) { + print "In Weekday Match \"$$rContext{weekday}\"\n" if ($debug & 64); + my ($wday); + $wday = getWDay($time); + $$rContext{weekday}[$counter] =~ /(.)(.)(.)(.)(.)(.)(.)/; + if ($$wday eq "-") { + print "Weekday rejected\n" if ($debug & 64); + next; + } + } + + print "Before Minlength Match\n" if ($debug & 64); + if ($$rContext{minlength}[$counter]) { + print "In Minlength Match \"$$rContext{minlength}[$counter]\"\n" if ($debug & 64); + if ($Program{$title}{$channel}{$time}{duration} < $$rContext{minlength}[$counter]) { + print "Minlength rejected\n" if ($debug & 64); + next; + } + } - foreach my $retval (@retval) - { - $retval =~ s/\x0d//g; + print "Before Maxlength Match\n" if ($debug & 64); + if ($$rContext{maxlength}[$counter]) { + print "In Maxlength Match \"$$rContext{maxlength}[$counter]\"\n" if ($debug & 64); + if ($Program{$title}{$channel}{$time}{duration} > $$rContext{maxlength}[$counter]) { + print "Maxlength rejected\n" if ($debug & 64); + next; } - return (@retval); + } + + # All test passed. Accept this timer + print "All Tests passed entry accepted/blacklisted\n" if ($debug & 64); + return ($counter); + } + # Foreach ran out without a hit + return "Nothing"; +} + +# 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); + my $Timeout = 10; # max. seconds to wait for response + + $SIG{ALRM} = sub { die("Timeout while connecting to VDR"); }; + alarm($Timeout); + + socket(SOCKET, PF_INET, SOCK_STREAM, getprotobyname('tcp')); + connect(SOCKET, $paddr) or die ("Can't connect to VDR\n"); + select(SOCKET); $| = 1; + select(STDOUT); + + while (<SOCKET>) { + last if substr($_, 3, 1) ne "-"; } + alarm(0); +} + +# 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>) { + s/\x0d//g; + (@retval) = (@retval, $_); + last if substr($_, 3, 1) ne "-"; + } + } + return (@retval); +} # Close the socket to VDR -sub closesocket - { - print SOCKET "Quit\r\n"; - close(SOCKET); - } +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); - } +sub fetchVDRTimers { + my (@timers, $timer, $position, $active, $channel, $day, $start, $end, $prio, $lifetime, $title, $subtitle, $minute, $duration); + my ($utime, $utime2); + + # First fetch the timers-list from VDR + @timers = GetSend ("lstt"); + + foreach $timer (@timers) { + 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, $lifetime, $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; + # Days 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,$lifetime,"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,$lifetime,$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; - } - } - } +sub initepgdata { + open (FI,"epg.data") or 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; + undef $category; + + $time=$2; + $duration=$3; + } + # Title + elsif (/^T\s(.*)/) { + $title=$1; + } + # Subtitle + elsif (/^S\s(.*)/) { + $subtitle=$1; } + # Description + elsif (/^D\s(.*)/) { + $description=$1; + } + elsif (/^K\s(.*)/) { + $category=$1; + } + # End Timer + elsif (/^e$/) { + # Only accept timers that are in the future + if ($time < time) { + next; + } + # Only accept timers that are at least 2 Seconds long + if ($duration <= 1) { + next; + } + + # Work around the different Bugs in the data + &correct_epg_data(); + + # Check if the Title & Subtitle is in the Done-List (Only if Subtitle exists) + if ($subtitle && $title =~ /$title_done/ && $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; + } + if ($category) { + $Program{$title}{$channel}{$time}{category}=$category; + } + # Check if the title is in the DEEP-Blacklist + if (&testtimer("deepblack", $title, $channel, $time) ne "Nothing") { + print "Deepblack: \"$title\"" if ($debug & 64); + print " $subtitle" if ($debug & 64 && $subtitle); + print "\n" if ($debug & 64); + &delprogram ($title, $channel, $time); + } + } + } } - close (FI); - } + } + 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); +sub initmovie { + my (@list,$list); + open (FI,"${configdir}/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); + open (FI,"${configdir}/done") or return; + @list = <FI>; + close (FI); + + foreach $list (@list) { + chomp $list; + ($title_temp,$subtitle_temp) = split (/\|/,$list); + if ($title_temp) { + $title_done{"^\Q$title_temp\E\$"} = 1; + } + if ($subtitle_temp) { + $subtitle_done{"^\Q$subtitle_temp\E\$"} = 1; + } + } + $title_done = join ('|',sort keys %title_done); + $subtitle_done = join ('|',sort keys %subtitle_done); +} + +sub processdone { + # Now delete Timers in VDR that are already in the done-List + my ($list, @list, $position, $timer, $active, $g, $title, $subtitle, $counter, @todel); + $counter = 0; + @list = GetSend ("LSTT"); + + foreach $timer (@list) { + 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 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; - } +# What should be recorded +sub inittorecord { + my ($context) = shift; + my ($rContext); + my (@title_list, @subtitle_list, @description_list, $line); + my (%Input); + my $counter = 0; + + if ($context eq "torecord") { + $rContext = \%torecord; + open (FI,"${configdir}/${context}") or die ("Can't open file \"$context\"\n"); + } elsif ($context eq "deepblack") { + $rContext = \%deepblack; + open (FI,"${configdir}/${context}") or return; + } else { + die ("Illegal Context"); + } + + + outer: while (<FI>) { + chomp if ($_); + if ($_ && !(/^\#/) && /^\[.*\]$/) { + $line = $.; + undef %Input; + while (<FI>) { + chomp; + if ($_ && !(/^\#/)) { + if (/^\[.*?\]$/) { + last; } - $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++; - } - } - } - } - } - } + + my ($key, $value); + ($key, $value) = split (/\s+=\s+/); + + if ($key =~ /^title$/i) { + if ($Input{title}) { + $Input{title} .= "|$value"; + } else { + $Input{title} = $value; + } + print "Titel = $value\n" if ($debug & 16); + } + elsif ($key =~ /^subtitle$/i) { + if ($Input{subtitle}) { + $Input{subtitle} .= "|$value"; + } else { + $Input{subtitle} = $value; + } + print "Subtitel = $value\n" if ($debug & 16); + } + elsif ($key =~ /^description$/i) { + if ($Input{description}) { + $Input{description} .= "|$value"; + } else { + $Input{description} = $value; + } + print "Description = $value\n" if ($debug & 16); + } + elsif ($key =~ /^category$/i) { + $Input{category} = $value; + print "Category = $value\n" if ($debug & 16); + } + elsif ($key =~ /^channel$/i) { + if ($Input{channel}) { + $Input{channel} .= "|^$value\$"; + } else { + $Input{channel} = $value; + } + print "Channel = $value\n" if ($debug & 16); + } + elsif ($key =~ /^timeframe$/i) { + $Input{timeframe} = $value; + print "Timeframe = $value\n" if ($debug & 16); } + elsif ($key =~ /^weekday$/i) { + $Input{weekday} = $value; + print "Weekday = $value\n" if ($debug & 16); + } + elsif ($key =~ /^minlength$/i) { + $Input{minlength} = $value; + print "Minlength = $value\n" if ($debug & 16); + } + elsif ($key =~ /^maxlength$/i) { + $Input{maxlength} = $value; + print "Maxlength = $value\n" if ($debug & 16); + } + elsif ($key =~ /^prio$/i) { + $Input{prio} = $value; + print "Prio = $value\n" if ($debug & 16); + } + elsif ($key =~ /^lifetime$/i) { + $Input{lifetime} = $value; + print "Lifetime = $value\n" if ($debug & 16); + } + elsif ($key =~ /^timertitle$/i) { + $Input{timertitle} = $value; + print "Timertitel = $value\n" if ($debug & 16); + } + elsif ($key =~ /^margin$/i) { + $Input{margin} = $value; + print "Margin = $value\n" if ($debug & 16); + } + elsif ($key =~ /^instance$/i) { + $Input{instance} = $value; + print "Instance = $value\n" if ($debug & 16); + } else { + print "Unkown Key: \"$key\" with Value: \"$value\"\n"; + } + } } - } -# 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++; - } + # Accept entry only if it is for the current instance or for "no" instance + if (($Opts{s} && $Input{instance} && $Input{instance} eq "s") || !$Input{instance} || ($Input{instance} ne "s" && $Input{instance} == $currentVDR)) { + # Accept entry only if at least a Title/Subtitle/Description is provied + if (!$Input{title} && !$Input{subtitle} && !$Input{description}) { + print "No Title/Subtitle/Description Field. $context entry ignored. Block beginning at Line $line\n"; + redo outer; + } + + if ($Input{title}) { + $$rContext{title}[$counter] = $Input{title}; + $title_list[$#title_list + 1] = $Input{title}; + } + if ($Input{subtitle}) { + if ($Input{subtitle} =~ /^movie$/i || $Input{subtitle} =~ /^\!movie$/i) { + $test_subtitle_movie = 1; + } + $$rContext{subtitle}[$counter] = $Input{subtitle}; + $subtitle_list[$#subtitle_list + 1] = $Input{subtitle}; + } + if ($Input{description}) { + $$rContext{description}[$counter] = $Input{description}; + $description_list[$#description_list + 1] = $Input{description}; + } + if ($Input{category}) { + $$rContext{category}[$counter] = $Input{category}; + } + if ($Input{channel}) { + if ($Input{channel} =~ /\!/) { + $Input{channel} =~ s/\!//g; + $$rContext{blackchannel}[$counter] = $Input{channel}; + } else { + $$rContext{channel}[$counter] = $Input{channel}; } + } + if ($Input{timeframe}) { + $$rContext{timeframe}[$counter] = $Input{timeframe}; + } + if ($Input{weekday}) { + $$rContext{weekday}[$counter] = $Input{weekday}; + } + if ($Input{minlength}) { + if ($Input{minlength} =~ /^(\d+)m$/) { + $Input{minlength} = $1 * 60 + } elsif ($Input{minlength} =~ /^(\d+)h$/) { + $Input{minlength} = $1 * 60 * 60 + } + $$rContext{minlength}[$counter] = $Input{minlength}; + } + if ($Input{maxlength}) { + if ($Input{maxlength} =~ /^(\d+)m$/) { + $Input{maxlength} = $1 * 60 + } elsif ($Input{maxlength} =~ /^(\d+)h$/) { + $Input{maxlength} = $1 * 60 * 60 + } + $$rContext{maxlength}[$counter] = $Input{maxlength}; + } + if ($Input{prio}) { + $$rContext{prio}[$counter] = $Input{prio}; + } + if ($Input{lifetime}) { + $$rContext{lifetime}[$counter] = $Input{lifetime}; + } + else { + $$rContext{prio}[$counter] = $default_prio; + } + if ($Input{timertitle}) { + $$rContext{timertitle}[$counter] = $Input{timertitle}; + } + if ($Input{margin}) { + my ($start, $stop); + ($start, $stop) = split (/;/,$Input{margin}, 2); + $$rContext{marginstart}[$counter] = $start if ($start); + $$rContext{marginstop}[$counter] = $stop if ($stop); + } + # Set Default-Margins if no margins defined + $$rContext{marginstart}[$counter] = $marginstart if (!$$rContext{marginstart}[$counter]); + $$rContext{marginstop}[$counter] = $marginstop if (!$$rContext{marginstop}[$counter]); + $counter++; + if ($Input{instance}) { + $$rContext{instance}[$counter] = $Input{instance}; + } } + redo outer; + } + } + + $$rContext{timercount} = $counter - 1; + + $$rContext{titleRE} = join ('|',@title_list); + if ($$rContext{titleRE} && $$rContext{titleRE} =~ /\|.\|/) { + $$rContext{titleRE} = "."; + } + $$rContext{subtitleRE} = join ('|',@subtitle_list); + if ($$rContext{subtitleRE} && $$rContext{subtitleRE} =~ /\|.\|/) { + $$rContext{subtitleRE} = "."; + } + $$rContext{descriptionRE} = join ('|',@description_list); + if ($$rContext{descriptionRE} && $$rContext{descriptionRE} =~ /\|.\|/) { + $$rContext{descriptionRE} = "."; + } + + if (!$$rContext{titleRE}) { + $$rContext{titleRE} = "^Dieseshierwirdgarantiertnieundnimmeraufirgendetwassinnvollesmatchen\$"; + } + if (!$$rContext{subtitleRE}) { + $$rContext{subtitleRE} = "^Dieseshierwirdgarantiertnieundnimmeraufirgendetwassinnvollesmatchen\$"; + } + if (!$$rContext{descriptionRE}) { + $$rContext{descriptionRE} = "^Dieseshierwirdgarantiertnieundnimmeraufirgendetwassinnvollesmatchen\$"; + } +} + +# Parse "LSTC"-Command of VDR +sub initchannellist { + my ($counter, $chan, $garbage, $card, @temp_channels, $temp, $i); - $num_torecord = $counter - 1; + @temp_channels = GetSend ("LSTC"); - $title_torecord = join ('|',@title_list); - $subtitle_torecord = join ('|',@subtitle_list); - $description_torecord = join ('|',@description_list); + foreach $i (0 .. $#temp_channels) { + $temp = $temp_channels[$i]; + chomp $temp; - if (!$title_torecord) - { - $title_torecord = "^Dieseshierwirdgarantiertnieundnimmeraufirgendetwassinnvollesmatchen\$"; + 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,"${configdir}/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); } - if (!$subtitle_torecord) - { - $subtitle_torecord = "^Dieseshierwirdgarantiertnieundnimmeraufirgendetwassinnvollesmatchen\$"; + elsif ($key =~ /^marginstart$/i) { + print "Marginstart = $value\n" if ($debug & 16); + $marginstart = $value; } - if (!$description_torecord) - { - $description_torecord = "^Dieseshierwirdgarantiertnieundnimmeraufirgendetwassinnvollesmatchen\$"; + elsif ($key =~ /^marginstop$/i) { + print "Marginstop = $value\n" if ($debug & 16); + $marginstop = $value; } - } - -# 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++; - } + 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; + } + elsif ($key =~ /^description$/i) { + print "Description = $value\n" if ($debug & 16); + $Description = $value; + } + else { + print "Unkown Key: \"$key\" with Value: \"$value\"\n"; } + } } + print "End Config\n" if ($debug & 16); +} -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 initcommandline() { + my $Usage = qq{ +Usage: $0 [options] [Instance]... + +Options: -d hostname:Port hostname/ip:Port (localhost:2001) + -c configdir Directory where all config files are located + (~/.master-timer) + -i instance Which VDR-Instance, from the config-file, should be + used + -s Print all series from epg.data and exit + -v debuglevel Level of debug-messages to print + -h This Help-Page +}; + + # Only process commandline if not already processed + if (!$Opts{done}) { + die $Usage if (!getopts("d:p:c:i:sv:h",\%Opts)); } + die $Usage if ($Opts{h}); + # Mark the options as already processed + $Opts{done} = 1; -sub init - { - &initconfigfile(); - &initsocket(); - &initmovie(); - &initblacklist(); - &initdone(); - &initchannellist(); - &initepgdata(); - &inittorecord(); + if ($Opts{v}) { + $debug = $Opts{v}; + } + if ($Opts{i}) { + $currentVDR = $Opts{i}; + } + if ($Opts{d}) { + @Dest = ($Opts{d}); } + if ($Opts{c}) { + $configdir = $Opts{c}; + } +} + +sub init { + &initcommandline(); + &initconfigfile(); + # Process commandline a second time, so that configs from the config-file are overwritten + &initcommandline(); + &initsocket(); + &initmovie(); + &initdone(); + &initchannellist(); + &inittorecord("deepblack"); + &initepgdata(); + &inittorecord("torecord"); +} diff --git a/Tools/master-timer/sample/channels-to-scan b/Tools/master-timer/sample/channels-to-scan index 6acf157..22f473d 100644 --- a/Tools/master-timer/sample/channels-to-scan +++ b/Tools/master-timer/sample/channels-to-scan @@ -3,6 +3,4 @@ 3 4 5 -13 -18 -21 +49 diff --git a/Tools/master-timer/sample/config b/Tools/master-timer/sample/config index d01c8a8..3180943 100644 --- a/Tools/master-timer/sample/config +++ b/Tools/master-timer/sample/config @@ -1,14 +1,32 @@ -# How Many Seconds "too early" should the timer begin +# Master-Timer config file. Values shown here are defaults. + +# How many seconds "too early" should the timer begin marginstart = 600 -# How Many Seocnds "too long" should the timer end + +# How many seconds "too long" should the timer end marginstop = 600 -# When the Prio isn't provied in the config-File use this value + +# When the Prio isn't provided 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 + +# How many DVB cards are installed in the computer (not used yet) +DVBCards = 1 + +# IP/Hostname:Port of the destination (space is used for delimiter) +Dest = localhost:2001 + +# Should timers on the same channels be joined when they overlap (0 = off) +jointimers = 0 + +# Should the description be transfered to VDR? +description = 0 + +# Debug level +# 1 : Dump "torecord" +# 2 : Dump all timers +# 4 : Show when a timer will be deleted +# 8 : Dump the "Done" REs +# 16 : Verbose config reading +# 32 : Dump program variables +# 64 : Excessive deepblack/torecord debuging debug = 0 diff --git a/Tools/master-timer/sample/convert-channel-list b/Tools/master-timer/sample/convert-channel-list new file mode 100644 index 0000000..a97f3c5 --- /dev/null +++ b/Tools/master-timer/sample/convert-channel-list @@ -0,0 +1,26 @@ +Kabel 1|Kabel 1 +MTV|MTV Central +PRW 13TH Street|13th Street +PRW Discovery Channel|Discovery Channel +PRW Disney Channel|Disney Channel +PRW FOX KIDS|Fox Kids +PRW Junior|Junior +PRW K-Toon|K-Toon +PRW Krimi & Co|Krimi &Co +PRW Planet|Planet +PRW Sci-Fantasy|Premiere Sci-Fi +PRW Studio Universal|Studio Universal +PRW Sunset|Sunset +Premiere Action|Premiere Action +Premiere Comedy|Premiere Comedy +Premiere STAR|Premiere Star +Premiere World 1|Premiere 1 +Premiere World 2|Premiere 2 +Premiere World 3|premiere 3 +Pro Sieben|Pro-7 +RTL|RTL +RTL 2|RTL2 +Sat.1|Sat.1 +Super RTL|Super RTL +Viva|VIVA +Vox|VOX diff --git a/Tools/master-timer/sample/deepblack b/Tools/master-timer/sample/deepblack index 63b4f9e..12884fe 100644 --- a/Tools/master-timer/sample/deepblack +++ b/Tools/master-timer/sample/deepblack @@ -1,79 +1,172 @@ -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$ +# [<Anytext>] +# This marks the beginning of a deepblack-entry +# Title = <text> +# This matches a title +# Subtitle = <text> +# This matches a subtitle +# Description = <text> +# This matches a description +# Category = <left>/<right> +# This matches a DTV-Category +# Channel = <Channel> +# Restricts a deepblack-entry to a specific channel. +# A single "!" at start of channel list negates the selection. +# Timeframe = <begin>-<end> +# Restricts a deepblack-entry to a specific timeframe. +# No timers with start time in the timeframe will be programmed. +# minlength = <Number> +# Restricts a deepblack-entry to a specific minimum length +# (postfix "m" for minutes, "h" for hours.) +# maxlength = <Number> +# Restricts a deepblack-entry to a specific maximum length +# (postfix "m" for minutes, "h" for hours.) +# weekday = MTWTFSS +# Restricts a deepblack-entry to a specific weekday +# instance = <Number> +# Only apply this deepblack-entry for a specific Instance +# "s" is a special value used for "-s"-Mode +# +# The "Title", "Subtitle", "Description", "Channel"-Lines can be +# supplied any number of times for a specific entry +# +# To deepblack something at least one of the "Title", "Subtitle" or +# "Description" (If you don't have anything "better" use "Title = ." +# for this matches everything) fields has to be provided. +# These three fields are "include" and the rest are "exclude" fields. + +[Blacklist all Talkshows] +Title = . +Category = Talk + +[Blacklist all Lifestyles] +Title = . +Category = /Lifestyles + +[Blacklist Sport/Tennis] +Title = . +Category = Sport/Tennis + +[Record only ZDF and Pro7] +Title = . +Channel = !ZDF +Channel = ProSieben + +[Blacklist a timeframe] +Title = . +Timeframe = 1000-1400 + +[Blacklist everything with less than 5 minutes duration] +Title = . +maxlength = 5m + +[Sinnlose Serien] +Title = Für alle Fälle Stefanie +Title = Chicago Hope - Endstation Hoffnung +Title = Chicago Hope +Title = Doppelter Einsatz +Title = Dr. Stefan Frank - Der Arzt, dem die Frauen vertrauen +Title = Ehekriege +Title = Ein Bayer auf Rügen +Title = Emergency Room +Title = Gute Zeiten, schlechte Zeiten +Title = Hallo, Onkel Doc! +Title = Hercules +Title = Hinter Gittern - Der Frauenknast +Title = OP ruft Dr. Bruckner +Title = Wolffs Revier +Title = alphateam +Title = SK Kölsch +Title = ^Becker$ +Title = Kommissar Rex +Title = Nur die Liebe zählt +Title = Unsere kleine Farm +Title = Die Waltons +Title = ^Die Zwei$ + +[Glueckspiele] +Title = Die Quiz Show +Title = Glücksrad +Title = Jeder gegen Jeden +Title = Kochduell + +[Infotainment und Boulevardzeug] +Title = 'MAX' - Das ganze Leben! +Title = ^BIZZ$ +Title = Big Brother +Title = GIRLSCAMP +Title = Call TV +Title = DIE REDAKTION +Title = EXCLUSIV +Title = EXTRA +Title = Explosiv - Das Magazin +Title = K1 DIE REPORTAGE +Title = K1 Das Magazin +Title = PROSIEBEN REPORTAGE +Title = Fit For Fun TV +Title = peep! +Title = s.a.m. +Title = taff. +Title = ^blitz$ +Title = Die Harald Schmidt Show +Title = Spiegel TV-Reportage + +[Nachrichten] +Title = 10 vor 11 +Title = 17:30 live +Title = 18:30 +Title = 24 Stunden +Title = Punkt 12 +Title = Punkt 6 +Title = Punkt 9 +Title = RTL II News +Title = RTL aktuell +Title = RTL-Nachtjournal +Title = K1 Nachrichten +Title = Nachrichten + +[Talkshows] +Title = Andreas Türck +Title = Arabella +Title = Britt - Der Talk um Eins +Title = Bärbel Schäfer +Title = Die Oliver Geissen Show +Title = Peter Imhof +Title = Vera am Mittag +Title = Hans Meiser +Title = Nicole - Entscheidung am Nachmittag +Title = Franklin + +[So richtig Sinnloses] +Title = Dauerwerbesendungen +Title = Infomercials +Title = Kickers +Title = RTL SHOP +Title = SAT.1-FRÜHSTÜCKSFERNSEHEN +Title = Zapping + +[PREMIERE WORLD - Das Programm] +Title = PREMIERE WORLD - Das Programm +Title = Programm ab +Title = Programm von + +[Fussball] +Title = fussball +Title = fußball +Title = UEFA Champions + +#Sonstiges +[^Sieben$] +Title = ^Sieben$ + +[Starporträt Kevin Spacey] +Title = Starporträt Kevin Spacey + + +[All Movies for -s] +Subtitle = MOVIE +Instance = s + +[All >= 65m for -s] +Title = . +minlength = 65m +Instance = s diff --git a/Tools/master-timer/sample/subtitle-movie b/Tools/master-timer/sample/subtitle-movie index 3b5a0ab..9bd3579 100644 --- a/Tools/master-timer/sample/subtitle-movie +++ b/Tools/master-timer/sample/subtitle-movie @@ -3,6 +3,7 @@ ^Actionkomödie$ ^Actionthriller$ ^Agentenfilm$ +^Beziehungskomödie$ ^Biografie$ ^Biographie$ ^Computeranimation$ @@ -12,19 +13,24 @@ ^Familiendrama$ ^Fantasy$ ^Fantasykomödie$ +^Fantasy-Komödie$ ^Gangsterfilm$ ^Gerichtsfilm$ ^Gesellschaftsdrama$ +^Historiendrama$ ^Horrorfilm$ ^Horrorkomödie$ +^Jugenddrama$ ^Kinderfilm$ ^Komödie$ ^Kriegsfilm$ ^Krimikomödie$ ^Kriminalfilm$ ^Liebesfilm$ +^Liebeskomödie$ ^Melodram$ ^Melodrama$ +^Monumentalfilm$ ^Musical$ ^Politthriller$ ^Psychothriller$ @@ -32,10 +38,15 @@ ^Romanze$ ^Satire$ ^Science-Fiction$ +^Science-Fiction-Komödie$ ^Spielfilm$ ^TV Movie$ ^TV-Drama$ +^Teil .$ +^Teil 0.$ ^Thriller$ +^Tragikomödie$ ^Western$ +^Westernkomödie$ ^Zeichentrick$ ^Zeichentrickkomödie$ diff --git a/Tools/master-timer/sample/torecord b/Tools/master-timer/sample/torecord index 0306830..0c97b91 100644 --- a/Tools/master-timer/sample/torecord +++ b/Tools/master-timer/sample/torecord @@ -1,32 +1,90 @@ -# Format: (Every field is "optional". -# [Title RE|Subtitle RE|Description RE|Channel-Name|Timeframe|Prio|Timer-Title|Marginstart;Marginstop|VDR-Instance] +# [<Anytext>] +# This marks the beginning of a timer entry +# Title = <text> +# This matches a title +# Subtitle = <text> +# This matches a subtitle. +# You may use the magic "MOVIE" or "!MOVIE" which matches +# all entries from file "subtitle-movie". +# Description = <text> +# This matches a description +# Category = <left>/<right> +# This matches a DTV-Category +# Channel = <Channel> +# Restricts a time to a specific channel. +# A single "!" at start of channel list negates the selection. +# Timeframe = <begin>-<end> +# Restricts timer to a specific timeframe. +# Only timers with start time in the timeframe will be programmed. +# minlength = <Number> +# Restricts timer entry to a specific minimum length +# (postfix "m" for minutes, "h" for hours.) +# maxlength = <Number> +# Restricts a timer entry to a specific maximum length +# (postfix "m" for minutes, "h" for hours.) +# weekday = MTWTFSS +# Restricts a timer to a specific weekday +# Timertitle = <text> +# The title used for this timer. +# If this is not provided "Title" will be used. +# If "Title" is not provided the EPG title will be used. +# Margin = <Number>;<Number> +# Seconds added to the beginning and end of the timer. +# Positive numbers will lengthen the recording. +# instance = <Number> +# The instance of VDR for which this timer is. +# If this is not provided the timer is valid for ALL instances. # -# 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 +# The "Title", "Subtitle", "Description", "Channel"-Lines can be +# supplied any number of times for a specific entry # -# 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 +# To record something at least one of the "Title", "Subtitle" or "Description" +# fields has to be provided. These three fields are "include" and the rest are +# "exclude" fields! + +[Dies ist ein Test-Timer] +Title = Titel +Subtitle = Subtitel +Description = Description +Category = Serie/Krimi +Channel = Pro-7 +Channel = VIVA +Timeframe = 1230-1830 +Prio = 50 +Lifetime = 50 +minlength = 10m +maxlength = 3h +weekday = ---T--- +Timertitle = Test +Margin = 600;600 +instance = 2 + +# Record Babylon 5 only if NOT playing on Pro 7; +# recording starts one minute too early and ends +# one minute too early (to skip following ads). +[Babylon 5] +Title = Babylon 5 +Channel = !Pro-7 +Prio = 99 +Margin = 60;-60 + +[DS9] +Title = Deep Space Nine +Prio = 99 +Timertitle = DS9 +Margin = 60;-60 + +[Seven Days] +Title = Seven Days +Prio = 99 + +[Stargate] +Title = Stargate +Prio = 99 + +[Aniston] +Description = Aniston +Prio = 99 +[Matrix] +Title = Matrix |