Fernsehprogramm DOIF (aktuell und primetime | non blocking | minimaler traffic)

Begonnen von mumpitzstuff, 12 Juni 2020, 21:47:48

Vorheriges Thema - Nächstes Thema

Per

Zwei Fragen:
1. wäre es nicht sinnvoll, die Quelle als attr zu hinterlegen? Dann können die vielen # aus dem Code raus und er ist schnell angepasst, wenn der Server mal wieder wechselt.
2. kann man channelfilter nicht aus den TPL nehmen/erstellen? Ist dann nicht der kürzeste/effektivste Code, aber viel leichter zu warten. Und sooo oft wird diese Funktion nicht aufgerufen, dass es den dramatischen Performanceeinbruch geben wird.

Ersteres traue ich mir noch selbst zu, ich will aber keinen Fork aufmachen. Fürs zweite fehlt mir der Durchblick im Code.

mumpitzstuff

1. Klar. Das kann man tun. Bisher war das aber relativ stabil. Bei mir läuft das Ganze reibungslos seit vielen Monaten. Deshalb habe ich da nicht so viel Wert darauf gelegt. Du kannst aber den Code gern anpassen und posten, dann kann ich das im ersten Beitrag übernehmen.

2. Ich glaube nicht, dass das wirklich irgendwie funktioniert bzw. dann gäbe es zu viele Abhängigkeiten und Kompromisse, selbst wenn man das dort irgendwie raus lösen könnte. Das macht für mich wenig Sinn und hat wenig bis keinen Mehrwert. Eventuell kann man aber den Filter ebenfalls als Attribut aufnehmen, um auch hier einfacher Änderungen vornehmen zu können.

Per

Meine Änderungen:

Zusätzliche Attribute
attr TV_Programm userattr Server
attr TV_Programm Server http://epg.vuplus-community.net


Änderung der Subs
  sub tvDownload()\
  {\
    my $output = '';; \
    \
    $output .= qx(wget $_Server/rytecDE_Basic.xz -O $_path/rytecDE_Basic.xz 2>&1);;\
    $output .= qx(xz -df $_path/rytecDE_Basic.xz 2>&1);;\
    $output .= qx(wget $_Server/rytecDE_Common.xz -O $_path/rytecDE_Common.xz 2>&1);;\
    $output .= qx(xz -df $_path/rytecDE_Common.xz 2>&1);;\
\

...tvDownloadMerge ebenso, Rest wie gehabt

Änderung des Initialteils der Subs:
  $_Server = AttrVal("$SELF","Server","");; \

Nicht dabei ist ein Abfangen, wenn kein attr gesetzt ist, war es aber vorher (falscher Server oder so) auch nur nicht.

dyna

Moin mumpitzstuff,

gibt es eine einfache Möglichkeit die List in FTUI2 / FUIP einzubinden?

Viele Grüße
Jens

mumpitzstuff

Zitat von: dyna am 15 Dezember 2022, 19:14:59
Moin mumpitzstuff,

gibt es eine einfache Möglichkeit die List in FTUI2 / FUIP einzubinden?

Viele Grüße
Jens

Im ersten Beitrag hatte ich mal was ganz ganz einfaches gestrickt für FTUI und als zip dort abgelegt. Ich kenne mich damit aber so gar nicht aus und das nicht weiter verfolgt bzw. verbessert oder verschönert.

mumpitzstuff

@Per Ich schau mir das nächste Woche an (da habe ich dann Urlaub) und aktualisiere den Beitrag. Vielen Dank!

Per

Dann spiele ich bis dahin noch etwas rum :D

Z.B. eine Variante mit nur einer Liste, welche dann fürs aktuelle und fürs Prime 2x durchlaufen wird.

Per

Wenn man weiß, wie es geht, ist es gar nicht sooo schwer. Aber die Syntax war, aufgrund des Fhem-Perl-ui-Mix, viel trail'n'error.
Ist jetzt reduziert auf ein Minimum, außer den Ausgaben des aktullen Wertes zum Verständnis. Mit besseren RegEx Kenntnissen (die gehen bei mir gegen Null, aber von unten ;)) wäre es sinnvoll, den Eintrag erst zu löschen (ersetzen mit ""), wenn er schon vorhaben ist und dann zu adden. Dürfte schneller gehen als zu testen, ob er vorhanden ist und dann zu übergehen.

defmod TV_Test DOIF ##
attr TV_Test uiTable
{\
package ui_Table;;\
\
sub add\
{\
my ($wert) = @_;;\
$_Senderliste .= $wert . "\\.|";;\
return $wert;;\
}\
}\
\
DEF TPL_TV(add ("$3")|$_Senderliste)\
\
$_Senderliste = "";;"SL=$_Senderliste"\
TPL_TV($SELF,prime,DasErste,ard)\
TPL_TV($SELF,prime,ZDF,zdf)\
::fhem("setreading $SELF Sender " . $_Senderliste);;"SL=$_Senderliste"


Als Nächstes schreibe ich dir Unfold um, damit die Syntax der TPL leichter wird...

Tante Edit meint, ich solle es in die Senderlogo Sub einbauen. Mache ich doch glatt.

Per

Soooo, sehr langes Ooooo.

Prinzipiell bin ich "fertig".
Was geht:
- die Senderliste wird erstellt und als Reading abgelegt.
- die Senderliste wird eingelesen und das qr erstellt.
- die TPL sind vereinfacht (Unfold aufgebohrt).
- Server für die rytech-Files als attr.

Was nicht geht:
- die Filterliste filtert nicht. Ich bekomme Unmengen Readings von nicht ausgewählten Sendern. Sie geht aber auch nicht, wenn ich die fest vorgebe.
- sind keine rytech-Files da, geht der Rechner in die Knie. Den Fall hatte ich nebenbei, weil der letzte gepostete Server (epg.vuplus-community.net) nicht wollte. *)

Beim Ersten spiele ich mal noch etwas rum **), bei letzteren habe ich keine Idee, wo mal das abfangen muss.

*) im normalen Betrieb sollte das nicht passieren, da ich die Dateien aber gelöscht hatte, war kein Backup für das Script da. Inzwischen weiss ich, dass ich diese Files fürs Testen der Filter nicht löschen muss.

**) Noch gar nicht abgeschickt und schon festgestellt: das ist mir zu hoch! Ich bin mir aber sicher, dass ich die überflüssigen Readings früher auch schon hatte, bevor ich an der Filter-Automatik rumgespielt hatte. Zum Vergleich habe ich mir nen jungfräulichen Code von Seite 1 gezogen: Bingo, dort geht der Filter auch nicht (zumindest nicht so, wie ich ihn erwarte). Damit warte ich auf die Reaktion eines RegEx-Native-Speakers ;)

PS: ich hänge den aktuellen Stand trotzdem mal an, der Filter/Reading wird erstellt, aber nicht genutzt.

defmod TV_Programm DOIF subs\
{\
  use utf8;;\
  use Date::Parse;;\
  # sudo apt-get install libxml-bare-perl\
  use XML::Bare 0.53 qw(forcearray);;\
  use Blocking;;\
  #use Encode qw(encode_utf8 decode_utf8);;\
  \
  ### CONFIG AREA ###\
  my $channelFilter = ReadingsVal("$SELF","Sender","ZDF\.");;\
  $channelFilter =~ s/\|$//;;\
  $_channelFilter_channelFilter = qr/^(?:$channelFilter)/;;\
  # telnet port must not be password protected (open)\
  # this is used as fallback if telnet port cannot created automatically\
  $_telnetPort = 7072;;\
  # can be used to adjust the program times (mostly not needed!)\
  $_timeAdjust = 0;;\
  $_path = '/opt/fhem/';;\
  $_dataFile = $_path.'rytecDE_Basic';;\
  $_Server = AttrVal("$SELF","Server","");; \
  # enable/disable unused channel filtering on filemerge (enabled = small file = faster)\
  $_filterChannels = 1;;\
  # enable/disable updates based on starttimes (enabled = update channels only if needed = faster)\
  $_updateBasedOnStarttimes = 1;;\
  # enable/disable use of Time::Piece (timepiece is faster but not instaled on some systems)\
  $_timepiece = 0;;\
  \
  # internal variables\
  $_startTimes = ();;\
  \
  \
  sub filterText($)\
  {\
    my $text = shift;;\
\
    $text =~ s/[\x{0022}\x{0060}\x{003b}\x{0027}\"\`;;\'\r]//g;;\
    #$text =~ s/[\"\`;;\'\r]//g;;\
    $text =~ s/[\n]/<br>/g;;\
\
    return $text;;\
  }\
  \
  sub xmltv2epoch($)\
  {\
    my $t = shift;;\
\
    if ($_timepiece)\
    {\
      use Time::Piece;;\
      \
      # fast version\
      return Time::Piece->strptime($t, '%Y%m%d%H%M%S %z')->epoch;;\
    }\
    else\
    {\
      # slow but compatible version\
      substr($t, 8, 0) = 'T';;\
\
      return str2time($t);;\
    }\
  }\
\
  sub FmtDateTime($)\
  {\
    my @t = localtime(shift);;\
    return sprintf("%04d-%02d-%02d %02d:%02d:%02d", $t[5]+1900, $t[4]+1, $t[3], $t[2], $t[1], $t[0]);;\
  }\
  \
  sub createTelnet($)\
  {\
    my $device = shift;;\
    my $telnet = undef;;\
    \
    foreach my $d (sort keys %::defs) \
    { \
      next if ($d !~ /telnetForTvUpdateFn_\d+/);;\
      my $h = $::defs{$d};;\
      next if (!$h->{TYPE} || $h->{TYPE} ne 'telnet' || $h->{SNAME});;\
      next if (::AttrVal($d, 'allowfrom', '127.0.0.1') ne '127.0.0.1');;\
      next if ($h->{DEF} !~ /^\d+( global)?$/);;\
      next if ($h->{DEF} =~ /IPV6/);;\
\
      $telnet = $d;;\
      last;;\
    }    \
    \
    if (!defined($telnet))\
    {\
      $telnet = 'telnetForTvUpdateFn_'.time();;\
      my $ret = ::CommandDefine(undef, "-temporary $telnet telnet 0");;\
    \
      if (defined($ret))\
      {\
        ::Log3 $device, 1, $device.': Cannot create telnet port ('.$ret.')';;\
        return undef;;\
      }\
    \
      $::attr{$telnet}{room} = 'hidden';;\
      $::attr{$telnet}{allowfrom} = '127.0.0.1';;\
    }\
    \
    return $::defs{$telnet}{PORT};;\
  }\
\
  sub tvParse($$$)\
  {\
    my ($device, $mode, $port) = @_;;\
    my $obj;;\
    my $xml;;\
    my $lastChannel = '';;\
    my $reading = '';;\
    my $i = 999;;\
    my $n = 999;;\
    my $k = 0;;\
    my $primeTime = substr(FmtDateTime(time() + $_timeAdjust), 0, 11).'20:14:00';;\
    my $sendTelnet = '';;\
    \
    $obj = XML::Bare->new(file => $_dataFile);;\
    $xml = $obj->parse();;\
\
    if (!$@)\
    {\
      my $old = time() + $_timeAdjust;;\
\
      foreach (@{forcearray($xml->{'tv'}{'programme'})})\
      {\
        if ((0 != $_filterChannels) ||\
            ($_->{'channel'}{'value'} =~ $_channelFilter))\
        {\
          my $stop = xmltv2epoch($_->{'stop'}{'value'});;\
\
          # filter old stuff\
          if ($stop > $old)\
          {\
            if ($lastChannel ne $_->{'channel'}{'value'})\
            {\
              $lastChannel = $_->{'channel'}{'value'};;\
              $reading = $_->{'channel'}{'value'};;\
              $reading =~ s/[\.\s]//g;;\
              $reading =~ s/de$//;;\
              $n = 0;;\
               \
              if ((0 == $_updateBasedOnStarttimes) || !exists($_startTimes{$reading}) || ($_startTimes{$reading} <= $old))\
              {\
                $i = 0;;\
\
                if (0 != $_updateBasedOnStarttimes)\
                {\
                  $_startTimes{$reading} = $stop;;\
                }\
              }\
              else\
              {\
                ::Log3 $device, 4, $device.': '.$reading.' is blocked because actual program is not finished (start: '.$_startTimes{$reading}.', old: '.$old.').';;\
              }\
            }\
\
            if ($i < 3 && 'next' eq $mode)\
            {\
              my $fi = sprintf("%03d", $i);;\
              my $start = xmltv2epoch($_->{'start'}{'value'});;\
              my $readingName;;\
              my $readingValue;;\
\
              $readingName = 'next_'.$reading.'_'.$fi.'_bdate';;\
              $readingValue = substr(FmtDateTime($start), 0, 10);;\
              $sendTelnet .= ";;setreading $device $readingName $readingValue";;\
\
              $readingName = 'next_'.$reading.'_'.$fi.'_btime';;\
              $readingValue = substr(FmtDateTime($start), 11, 8);;\
              $sendTelnet .= ";;setreading $device $readingName $readingValue";;\
\
              $readingName = 'next_'.$reading.'_'.$fi.'_title';;\
              $readingValue = filterText(@{forcearray($_->{'title'})}[0]->{'value'});;\
              $sendTelnet .= ";;setreading $device $readingName $readingValue";;\
\
              $readingName = 'next_'.$reading.'_'.$fi.'_stitle';;\
              if (exists($_->{'sub-title'}{'value'}))\
              {\
                $readingValue = filterText($_->{'sub-title'}{'value'});;\
              }\
              else\
              {\
                $readingValue = 'na';;\
              }\
              $sendTelnet .= ";;setreading $device $readingName $readingValue";;\
\
              $readingName = 'next_'.$reading.'_'.$fi.'_desc';;\
              if (exists($_->{'desc'}{'value'}))\
              {\
                $readingValue = filterText($_->{'desc'}{'value'});;\
              }\
              else\
              {\
                $readingValue = 'na';;\
              }\
              $sendTelnet .= ";;setreading $device $readingName $readingValue";;\
\
              $k++;;\
              $i++;;\
            }\
\
            if ($n < 3 && 'prime' eq $mode)\
            {\
              my $start = xmltv2epoch($_->{'start'}{'value'});;\
              my $fmtStart = FmtDateTime($start);;\
              my $bdate = substr($fmtStart, 0, 10);;\
              my $btime = substr($fmtStart, 11, 8);;\
\
              if ($bdate.' '.$btime gt $primeTime)\
              {\
                my $fn = sprintf("%03d", $n);;\
                my $readingName;;\
                my $readingValue;;\
\
                $readingName = 'prime_'.$reading.'_'.$fn.'_bdate';;\
                $readingValue = substr(FmtDateTime($start), 0, 10);;\
                $sendTelnet .= ";;setreading $device $readingName $readingValue";;\
\
                $readingName = 'prime_'.$reading.'_'.$fn.'_btime';;\
                $readingValue = substr(FmtDateTime($start), 11, 8);;\
                $sendTelnet .= ";;setreading $device $readingName $readingValue";;\
\
                $readingName = 'prime_'.$reading.'_'.$fn.'_title';;\
                $readingValue = filterText(@{forcearray($_->{'title'})}[0]->{'value'});;\
                $sendTelnet .= ";;setreading $device $readingName $readingValue";;\
\
                $readingName = 'prime_'.$reading.'_'.$fn.'_stitle';;\
                if (exists($_->{'sub-title'}{'value'}))\
                {\
                  $readingValue = filterText($_->{'sub-title'}{'value'});;\
                }\
                else\
                {\
                  $readingValue = 'na';;\
                }\
                $sendTelnet .= ";;setreading $device $readingName $readingValue";;\
\
                $readingName = 'prime_'.$reading.'_'.$fn.'_desc';;\
                if (exists($_->{'desc'}{'value'}))\
                {\
                  $readingValue = filterText($_->{'desc'}{'value'});;\
                }\
                else\
                {\
                  $readingValue = 'na';;\
                }\
                $sendTelnet .= ";;setreading $device $readingName $readingValue";;\
\
                $k++;;\
                $n++;;\
              }\
            }\
\
            if ($k >= 10)\
            {\
              #::Log3 $device, 5, $device.': '.encode_utf8($sendTelnet);;\
              \
              `perl /opt/fhem/fhem.pl $port "$sendTelnet"`;;\
              \
              $k = 0;;\
              $sendTelnet = '';;\
            }\
          }\
        }\
      }\
\
      if ('' ne $sendTelnet)\
      {\
        #::Log3 $device, 5, $device.': '.encode_utf8($sendTelnet);;\
        \
        `perl /opt/fhem/fhem.pl $port "$sendTelnet"`;;\
      }\
    }\
    \
    return %{$_startTimes};;\
  }\
  \
  sub tvMerge($;;$)\
  {\
    my ($dstName, $srcName) = @_;;\
    my $fh;;\
    my $dst;;\
    my $src;;\
    my $start = '';;\
    my $channels1 = '';;\
    my $channels1_flt = '';;\
    my $channels2 = '';;\
    my $channels2_flt = '';;\
    my $programms1 = '';;\
    my $programms1_flt = '';;\
    my $programms2 = '';;\
    my $programms2_flt = '';;\
    my $end = '';;\
    my $pos;;\
\
    open($fh, '<', $dstName) or die "Can't open file $!";;\
    read($fh, $dst, -s $fh);;\
    close($fh);;\
\
    if (defined($srcName))\
    {\
      open($fh, '<', $srcName) or die "Can't open file $!";;\
      read($fh, $src, -s $fh);;\
      close($fh);;\
    }\
\
    if (-1 != ($pos = index($dst, '<channel ')))\
    {\
      $start = substr($dst, 0, $pos);;\
    }\
\
    if (-1 != ($pos = rindex($dst, '</programme>')))\
    {\
      $end = substr($dst, $pos + 12);;\
    }\
\
    while ($dst =~ /(\s*<channel\s.*?id="(.*?)".*?<\/channel>)/sg)\
    {\
      if (0 != $_filterChannels)\
      {\
        $_ = $1;;\
\
        if ($2 =~ $_channelFilter)\
        {\
          $channels1_flt .= $_;;\
        }\
      }\
      else\
      {\
        $channels1 .= $1;;\
      }\
    }\
\
    while ($dst =~ /(\s*<programme\s.*?channel="(.*?)".*?<\/programme>)/sg)\
    {\
      if (0 != $_filterChannels)\
      {\
        $_ = $1;;\
\
        if ($2 =~ $_channelFilter)\
        {\
          $programms1_flt .= $_;;\
        }\
      }\
      else\
      {\
        $programms1 .= $1;;\
      }\
    }\
\
    if (defined($srcName))\
    {\
      while ($src =~ /(\s*<channel\s.*?id="(.*?)".*?<\/channel>)/sg)\
      {\
        if (0 != $_filterChannels)\
        {\
          $_ = $1;;\
\
          if ($2 =~ $_channelFilter)\
          {\
            $channels2_flt .= $_;;\
          }\
        }\
        else\
        {\
          $channels2 .= $1;;\
        }\
      }\
\
      while ($src =~ /(\s*<programme\s.*?channel="(.*?)".*?<\/programme>)/sg)\
      {\
        if (0 != $_filterChannels)\
        {\
          $_ = $1;;\
\
          if ($2 =~ $_channelFilter)\
          {\
            $programms2_flt .= $_;;\
          }\
        }\
        else\
        {\
          $programms2 .= $1;;\
        }\
      }\
    }\
\
    if (0 != $_filterChannels)\
    {\
      open($fh, '>', $dstName) or die "Can't open file $!";;\
\
      if (defined($srcName))\
      {\
        print $fh $start.$channels1_flt.$channels2_flt.$programms1_flt.$programms2_flt.$end;;\
      }\
      else\
      {\
        print $fh $start.$channels1_flt.$programms1_flt.$end;;\
      }\
\
      close($fh);;\
    }\
    else\
    {\
      open($fh, '>', $dstName) or die "Can't open file $!";;\
\
      if (defined($srcName))\
      {\
        print $fh $start.$channels1.$channels2.$programms1.$programms2.$end;;\
      }\
      else\
      {\
        print $fh $start.$channels1.$programms1.$end;;\
      }\
\
      close($fh);;\
    }\
  }\
\
  sub tvDownload()\
  {\
    my $output = '';; \
    \
    $output .= qx(wget $_Server/rytecDE_Basic.xz -O $_path/rytecDE_Basic.xz 2>&1);;\
    $output .= qx(xz -df $_path/rytecDE_Basic.xz 2>&1);;\
    $output .= qx(wget $_Server/rytecDE_Common.xz -O $_path/rytecDE_Common.xz 2>&1);;\
    $output .= qx(xz -df $_path/rytecDE_Common.xz 2>&1);;\
\
    if (0 != $_filterChannels)\
    {\
      tvMerge($_dataFile);;\
    }\
    \
    return $output;;\
  }\
\
  sub tvDownloadMerge()\
  {\
    my $output = '';; \
    \
    $output .= qx(wget $_Server/rytecDE_Basic.xz -O $_path/rytecDE_Basic.xz 2>&1);;\
    $output .= qx(xz -df $_path/rytecDE_Basic.xz 2>&1);;\
    $output .= qx(wget $_Server/rytecDE_Common.xz -O $_path/rytecDE_Common.xz 2>&1);;\
    $output .= qx(xz -df $_path/rytecDE_Common.xz 2>&1);;\
\
    tvMerge($_dataFile, $_path.'rytecDE_Common');;\
\
    # download and merge other files here if needed\
    \
    return $output;;\
  }\
    \
  sub startDownload($)\
  {\
    my $name = shift;;\
    \
    # prevent download spamming\
    if (-e $_dataFile)\
    {\
      my $ftime = ((time() - (stat($_dataFile))[9]) / 60.0 / 60.0 / 24.0);;\
      \
      if ($ftime < 1.0)\
      {\
        ::Log3 $name, 1, $name.': Download of TV data skipped because file is not older than 1 day ('.($ftime).').';;\
        return;;\
      }\
    }\
    \
    if (defined($_blockingcalls{PID_DOWNLOAD}))\
    {\
      ::Log3 $name, 3, $name.': Blocking call already running (download).';;\
    \
      ::BlockingKill($_blockingcalls{PID_DOWNLOAD});;\
    }\
   \
    $_blockingcalls{PID_DOWNLOAD} = ::BlockingCall('DOIF::doDownload', $name, 'DOIF::endDownload', 300, 'DOIF::abortDownload', $name);;\
  }\
  \
  sub DOIF::doDownload($)\
  {\
    my $name = shift;;\
    my $output = '';;\
    \
    $output = tvDownloadMerge();;\
    \
    return $name.'|'.$output;;\
  }\
  \
  sub DOIF::endDownload($)\
  {\
    my ($name, $output) = split("\\|", shift);;\
    \
    ::Log3 $name, 5, $name.': '.$output;;\
    ::Log3 $name, 4, $name.': Blocking call finished to download tv data.';;\
    \
    delete($_blockingcalls{PID_DOWNLOAD});;\
  }\
  \
  sub DOIF::abortDownload($)\
  {\
    my $name = shift;;\
    \
    delete($_blockingcalls{PID_DOWNLOAD});;\
    \
    ::Log3 $name, 1, $name.': Blocking call aborted (download).';;\
  }\
  \
  sub startParse($$)\
  {\
    my ($name, $mode) = @_;;\
    my $port;;\
    \
    if (defined($_blockingcalls{PID_PARSE}))\
    {\
      ::Log3 $name, 3, $name.': Blocking call already running (parse).';;\
\
      ::BlockingKill($_blockingcalls{PID_PARSE});;\
    }\
    \
    $port = createTelnet($name);;\
    $port = $_telnetPort if (!defined($port));;\
    \
    $_blockingcalls{PID_PARSE} = ::BlockingCall('DOIF::doParse', $name.'|'.$mode.'|'.$port, 'DOIF::endParse', 300, 'DOIF::abortParse', $name);;\
  }\
  \
  sub DOIF::doParse($)\
  {\
    my ($name, $mode, $port) = split("\\|", shift);;\
    my $ret = $name;;\
    my %startTimes = tvParse($name, $mode, $port);;\
    \
    foreach (keys(%startTimes))\
    {\
      $ret .= '|'.$_.'|'.$startTimes{$_};;\
    }\
    \
    return $ret;;\
  }\
  \
  sub DOIF::endParse($)\
  {\
    my ($name, @startTimes) = split("\\|", shift);;\
        \
    for (my $i = 0;; $i < (scalar(@startTimes) / 2);; $i += 2)\
    {\
      $_startTimes{$startTimes[$i]} = $startTimes[$i + 1];;\
    }\
    \
    ::Log3 $name, 4, $name.': Blocking call finished to parse tv data.';;\
    \
    delete($_blockingcalls{PID_PARSE});;\
  }\
  \
  sub DOIF::abortParse($)\
  {\
    my $name = shift;;\
    \
    delete($_blockingcalls{PID_PARSE});;\
    \
    ::Log3 $name, 1, $name.': Blocking call aborted (parse).';;\
  }\
}\
init\
{\
  startDownload("$SELF");;\
  set_Exec('init_next', 300, 'startParse("$SELF", "next")');;\
  set_Exec('init_prime', 600, 'startParse("$SELF", "prime")');;\
}\
{\
  if ([00:05|Mo Do])\
  {\
    startDownload("$SELF");;\
  }\
  \
  if ([+00:01])\
  {\
    startParse("$SELF", 'next');;\
  }\
  \
  if ([00:30])\
  {\
    startParse("$SELF", 'prime');;\
  }\
}

attr TV_Programm userattr Server
attr TV_Programm Server http://epg.vuplus-community.net
attr TV_Programm alias Aktuelles TV-Programm

attr TV_Programm uiTable {\
  package ui_Table;;\
\
  $SHOWNOSTATE = 1;;\
  $ATTRIBUTESFIRST = 1;;\
  my $PRIMEROW = 39;;\
\
  $TR{0,40} = "style='color:red;;text-align:center;;font-weight:bold;;font-size:18px'";;\
  $TR{1..($PRIMEROW - 1),($PRIMEROW + 2)..($PRIMEROW * 2)} = "style='font-size:16px'";;\
  $TR{$PRIMEROW} = "style='border-top-style:solid;;border-bottom-style:solid;;border-color:#CCCCCC;;border-top-width:1px;;border-bottom-width:1px;;'";;\
  $TD{0..($PRIMEROW * 2)}{0} = "align='center'";;\
  $TD{0..($PRIMEROW - 1),($PRIMEROW + 1)..($PRIMEROW * 2)}{1,3,5} = "style='border-left-style:solid;;border-color:#CCCCCC;;border-left-width:1px;;'";;\
\
  sub showIcon\
  {\
    my ($icon, $show, $device, $state) = @_;;\
    $_Senderliste =~ s/$icon\\.\|//;;\
    $_Senderliste .= $icon . "\\.|";;\
    \
    if (defined($device) && defined($state))\
    {\
      return "<a href=\"$::FW_ME?cmd=set $device $state$::FW_CSRF\">".ICON("tv/$icon")."</a>";;\
    }\
    else\
    {\
      return ICON("$show");;\
    }\
  }\
  \
  sub showIconIP\
  {\
    my ($icon, $show, $device, $state) = @_;;\
    $_Senderliste =~ s/$icon\\.\|//;;\
    $_Senderliste .= $icon . "\\.|";;\
\
    \
    if (defined($device) && defined($state))\
    {\
      return "<a href=\"". ::ReadingsVal("$SELF","$device","") =~ s/.state/$state/r ."\" target=\"IPTV\">".ICON("$show")."</a>";;\
    }\
    else\
    {\
      return ICON("$show");;\
    }\
  }\
  sub unfold\
  {\
    my ($ReadingPre) = @_;;\
    my $title = ::ReadingsVal("$SELF","${ReadingPre}_title","-");;\
    my $desc = ::ReadingsVal("$SELF","${ReadingPre}_stitle","na")."\n\n". ::ReadingsVal("$SELF","${ReadingPre}_desc","na") ;;\
    \
    $title =~ s/(.{1,45}|\S{46,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g;;\
    $desc =~ s/<br>/\n/g;;\
    $desc =~ s/(.{1,65}|\S{66,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g;;\
    $desc =~ s/[\r\'\"]/ /g;;\
    $desc =~ s/[\n]|\\n/<br>/g;;\
\
     return substr(::ReadingsVal("$SELF","${ReadingPre}_btime",""),0,5) . \
            "</td><td><a href=\"#!\" onclick=\"FW_okDialog(';".$desc."';)\">".$title."</a>";;\
  }\
}\
\
## parameter: mode (next or prime), channel name (see xml data file), icon name (filename of channel logo)\
DEF TPL_TV(showIcon("$2","$3",undef,undef)|unfold("$1_$2_000")|unfold("$1_$2_001")|unfold("$1_$2_002"))\
\
## parameter: mode (next or prime), channel name (see xml data file), icon name (filename of channel logo), device name for set command, command\
DEF TPL_TVSET(showIcon("$2","$3","$4","$5")|unfold("$1_$2_000")|unfold("$1_$2_001")|unfold("$1_$2_002"))\
\
## parameter: mode (next or prime), channel name (see xml data file), icon name (filename of channel logo), device name for set command, command\
DEF TPL_TVIP(showIconIP("$2","$3","$4","$5")|unfold("$1_$2_000")|unfold("$1_$2_001")|unfold("$1_$2_002"))\
\
$_Senderliste = "";;"Sender"|"ab"|"Aktuelle Sendung"|"ab"|"Naechste Sendung"|"ab"|"Sendung"\
TPL_TVIP(next,DasErste,01 ARD,VIEW1,29438503040)\
TPL_TV(next,HGTV,428 HGTV)\
"&nbsp;;"|"&nbsp;;"|"&nbsp;;"|"&nbsp;;"|"&nbsp;;"|"&nbsp;;"|"&nbsp;;"\
"Sender"|"ab"|"Sendung"|"ab"|"Sendung"|"ab"|"Sendung"\
TPL_TV(prime,DasErste,ard)\
TPL_TV(prime,ZDF,zdf)\
TPL_TV(prime,History,history)\
TPL_TV(prime,TLC,tlc)\
::fhem("setreading $SELF Sender $_Senderliste");;

Da ist die von mir geeänderte Optik der Tabelle (Farben) und der Zugriff auf IP-TV noch drin, lasse ich jetzt auch so.

mumpitzstuff

Ich glaube löschen und dann einfügen ist sehr viel teurer als zu testen ob etwas da ist. Die Filterliste hängt von deinem Server ab. Leider unterscheiden sich die Ausgaben der Server und die Filterliste muss unter Umständen angepasst werden. Da du dir die aktuell zusammen setzt, müsstest du mir mal deinen aktuellen Filter posten, dann kann ich dir sagen, was daran falsch ist.

Privat verwende ich z.b. folgenden Server mit folgendem Filter:

http://www.xmltvepg.nl/rytecDE_Basic.xz
http://www.xmltvepg.nl/rytecDE_Common.xz

$_channelFilter = qr/^(?:DasErste\.|ZDF\.|Sat1\.|RTL2?\.|Pro(Sieben|SiebenMaxx)\.|DMax\.|Vox\.|KabelEins(?:Classic|Doku)?\.|ntv\.|Sixx\.|TLC\.|N24Doku\.|SonyEntertainmentTV\.|AandE\.|TNT(?:Serie|Film)\.|AnimalPlanet\.|History\.|Kinowelt\.|NatGeoHD\.|PLANET\.|Silverline\.|13thStreet\.|AXN\.|SciFi\.)/;

Der Filter hängt somit vom Server ab. Deshalb ist es wie gesagt auch keine gute Idee das so zu mchen, wie du es planst. Als Attribut abzulegen in Ordnung (wobei ds bei den ganzen Sonderzeichen eventuell auch Probleme macht) aber aus den Sendernamen das Ganze rauszuholen macht keinen Sinn. Wenn man mal den Server ändern muss, müsste man auch seine Sendernamen anpassen und eventuell auch die Namen der Icons. Das ist halt schon irgendwie suboptimal. Außerdem gibt es halt eventuell Besonderheiten zu beachten, die du mit einem zusammen gebastelten Regex nicht abbilden kannst.

Siehe dazu im direkten Vergleich:

http://epg.vuplus-community.net/rytecDE_Basic.xz
http://epg.vuplus-community.net/rytecDE_Common.xz

$_channelFilter = qr/^(?:ARD\.|ZDF\.|Sat1\.|RTL2?\.|Pro(?:7|SiebenMaxx)\.|DMax\.|Vox\.|Kabel(?:EinsClassic|EinsDoku)?\.|ntv\.|Sixx\.|TLC\.|N24Doku\.|SonyEntertainmentTV\.|AandE\.|TNT(?:Serie|Film)\.|AnimalPlanet\.|History\.|Kinowelt\.|NatGeoHD\.|PLANET\.|Silverline\.|13thStreet\.|AXN\.|SciFi\.)/;;\


Um deinen Filter zu testen kannst du folgendes tun:

1.) download der Dateien vom verwendeten Server und entpacken
2.) die seite https://regex101.com/ öffnen und aus allen runtergeladenen entpackten dateien alle channel tags (sind immer am anfang dr datei) in die regex seite bei TEST STRING einfügen (einfach hintereinander wenns mehrere Dateien sind)
3.) alles was bei channelFilter zwischen qr/<kopiere mich>/ steht kopieren und bei REGULAR EXPRESSION eintragen und ganz vorn das ^ zeichen durch " ersetzen

Und Schwups kannst du sehen was alles matcht und was nicht.



https://regex101.com/

Per

Prinzipiell gebe ich dir Recht, was fehlerhafte Filter angeht, ich habe für mich dein Muster aus Beitrag 1 angepasst. ABER: Schon dein Filter aus Beitrag 1 mit dem original Download (vor ca. 2 Stunden) zeigt das Ergebnis, dass nicht gefiltert wird. Waru weiß ich natürlich nicht, dafür kenne ich mich damit zu wenig aus. Aber ich brauche meinen Filter nicht anzupassen, wenn es mit deinem schon nicht geht.

Ansonsten wird meiner aus dem TPLs in der Form erstellt, wie deiner auch aussieht, nur halt jeder Sender einzeln. Und das Ergebnis ist halt auch das Gleiche: jeder Sender wird geparsed.

Und ja, wenn sich was ändert (ARD -> DasErste, Pro7 -> ProSieben) müssen die TPL eh angepasst werden, aber nicht TPL UND Filter. Und wenn aus Pro(7|SiebenMaxx) -> ProSieben( :o |Maxx) wird, bin wahrscheinlich nicht nur ich überfordert. Hatte mir mit ProSiebe(n|nMaxx) geholfen, aber jeden einzeln sollte ja auch funktionieren.

Und um deine Frage zu beantworten:
VOXup\.|3sat\.|ARTE\.|ServusHD\.|NDRFernsehen\.|WDRFernsehen\.|HRFernsehen\.|SWRFernsehen\.|MDRS-Anhalt\.|BRFernsehen\.|ComedyCentralVIVA\.|SuperRTL\.|RTLNitro\.|Tele5\.|Sat1Gold\.|phoenix\.|ZDFinfo\.|Sport1HD\.|Eurosport1\.|WELT\.|tagesschau24\.|MotorVision\.|HGTV\.|DasErste\.|ZDF\.|Sat1\.|RTL\.|RTL2\.|ProSieben\.|DMax\.|Vox\.|KabelEins\.|KabelEinsDoku\.|KabelEinsClassic\.|ProSiebenMaxx\.|Sixx\.|ntv\.|N24Doku\.|History\.|TLC\.|

Dazu dann die am Anfang der Subs
  my $channelFilter = ReadingsVal("$SELF","Sender","ZDF\.");
  $channelFilter =~ s/\|$//;
  $_channelFilter_channelFilter = qr/^(?:$channelFilter)/;



mumpitzstuff

Den Filter den ich verwende filtert mit Sicherheit (ich habe genau nur die Readings, die ich auch haben möchte im Device) und deshalb auch der aus dem Originalbeitrag, da bin ich mir ebenfalls sicher. Es wird zuerst beim Download die Dateigröße reduziert (vorgefiltert) und dann bei den Readings passiert das noch einmal. An einer Stelle könnte man sich das wahrscheinlich schenken. Den Filter kann man oben in der Config Area aber deaktivieren.

Im Anhang sieht man das direkt. Die 2 Dateien die ich merge, haben zusammen normal über 20MB. Durch den Filter aber keine 4MB mehr.

Per

Wie gesagt, ich kann es von der Programmierung nicht nachvollziehen. Nur nochmal soviel: den Code von Post 1 kopiert und ausgeführt, ohne irgendwelche Änderungen, ergab trotzdem die Unmengen Readings. Somit kann ich am Filter machen was ich will, es ändert sich nichts an den Readings.


Außer ich mache nen Syntaxfehler und Fhem gibt Fehler aus.

mumpitzstuff

Du hast auch alle existierenden Readings vorher gelöscht? Hast du dir mal die Datei angesehen bzw. die Größe der Datendatei? Wenn man die beiden Standard Dateien ohne Filter mergen würde, müsste die Datei 20+MB umfassen. Sobald der Filter wirkt, ist sie wesentlich kleiner.
Zudem ist es ein Unterschied ob gar nichts gefiltert wird oder nicht alles rausgefiltert wird, was du erwarten würdest. Eventuell werden noch zu viele Sender durch das Regex durchgelassen.

Ich habe deine Änderungen bei mir mal lokal übernommen und beobachte mal das Verhalten. Die Änderungen an der Unfold Funktion habe ich erst einmal außen vor gelassen, das war mir im Moment zu heikel. Btw deine Layout Änderungen habe ich auch noch nicht übernommen. Könntest du mir bitte einen kleinen Screenshot schicken, um zu sehen, ob ich das gut finden würde? Die Tests laufen aber noch ein wenig, da ich mal einen vollen Zyklus durchlaufen lassen möchte.

mumpitzstuff

Mein Test sieht aktuell so aus:

defmod doif_TEST DOIF subs\
{\
  use utf8;;\
  use Date::Parse;;\
  ## sudo apt-get install libxml-bare-perl\
  use XML::Bare 0.53 qw(forcearray);;\
  use Blocking;;\
  ##use Encode qw(encode_utf8 decode_utf8);;\
\
  ### CONFIG AREA ###\
  $_channelFilter = qr/^(?:DasErste\.|ZDF\.|Sat1\.|RTL2?\.|Pro(Sieben|SiebenMaxx)\.|DMax\.|Vox\.|KabelEins(?:Classic|Doku)?\.|ntv\.|Sixx\.|TLC\.|N24Doku\.|SonyEntertainmentTV\.|AandE\.|TNT(?:Serie|Film)\.|AnimalPlanet\.|History\.|Kinowelt\.|NatGeoHD\.|PLANET\.|Silverline\.|13thStreet\.|AXN\.|SciFi\.)/;;\
  $_channelFilterNew = AttrVal("$SELF", "channelFilter", "^(?:DasErste\.|ZDF\.|Sat1\.|RTL2?\.|Pro(Sieben|SiebenMaxx)\.|DMax\.|Vox\.|KabelEins(?:Classic|Doku)?\.|ntv\.|Sixx\.|TLC\.|N24Doku\.|SonyEntertainmentTV\.|AandE\.|TNT(?:Serie|Film)\.|AnimalPlanet\.|History\.|Kinowelt\.|NatGeoHD\.|PLANET\.|Silverline\.|13thStreet\.|AXN\.|SciFi\.)");;\
  $_channelFilterNew = qr/$_channelFilterNew/;;\
  ## telnet port must not be password protected (open)\
  ## this is used as fallback if telnet port cannot created automatically\
  $_telnetPort = 7072;;\
  ## can be used to adjust the program times (mostly not needed!)\
  $_timeAdjust = 0;;\
  $_path = '/opt/fhem';;\
  $_dataFile = $_path.'/rytecDE_Basic';;\
  $_server = AttrVal("$SELF", "server", "http://www.xmltvepg.nl");;\
  ## enable/disable unused channel filtering on filemerge (enabled = small file and less readings = faster)\
  $_filterChannels = 1;;\
  ## enable/disable updates based on starttimes (enabled = update channels only if needed = faster)\
  $_updateBasedOnStarttimes = 1;;\
  ## enable/disable use of Time::Piece (timepiece is faster but not installed on some systems)\
  $_timepiece = 0;;\
\
  ## internal variables\
  $_startTimes = ();;\
\
\
  sub filterText($)\
  {\
    my $text = shift;;\
\
    $text =~ s/[\x{0022}\x{0060}\x{003b}\x{0027}\"\`;;\'\r]//g;;\
    ##$text =~ s/[\"\`;;\'\r]//g;;\
    $text =~ s/[\n]/<br>/g;;\
\
    return $text;;\
  }\
\
  sub xmltv2epoch($)\
  {\
    my $t = shift;;\
\
    if ($_timepiece)\
    {\
      use Time::Piece;;\
\
      ## fast version\
      return Time::Piece->strptime($t, '%Y%m%d%H%M%S %z')->epoch;;\
    }\
    else\
    {\
      ## slow but compatible version\
      substr($t, 8, 0) = 'T';;\
\
      return str2time($t);;\
    }\
  }\
\
  sub FmtDateTime($)\
  {\
    my @t = localtime(shift);;\
    return sprintf("%04d-%02d-%02d %02d:%02d:%02d", $t[5]+1900, $t[4]+1, $t[3], $t[2], $t[1], $t[0]);;\
  }\
\
  sub createTelnet($)\
  {\
    my $device = shift;;\
    my $telnet = undef;;\
\
    foreach my $d (sort keys %::defs) \
    {\
      next if ($d !~ /telnetForTvUpdateFn_\d+/);;\
      my $h = $::defs{$d};;\
      next if (!$h->{TYPE} || $h->{TYPE} ne 'telnet' || $h->{SNAME});;\
      next if (::AttrVal($d, 'allowfrom', '127.0.0.1') ne '127.0.0.1');;\
      next if ($h->{DEF} !~ /^\d+( global)?$/);;\
      next if ($h->{DEF} =~ /IPV6/);;\
\
      $telnet = $d;;\
      last;;\
    }    \
\
    if (!defined($telnet))\
    {\
      $telnet = 'telnetForTvUpdateFn_'.time();;\
      my $ret = ::CommandDefine(undef, "-temporary $telnet telnet 0");;\
\
      if (defined($ret))\
      {\
        ::Log3 $device, 1, $device.': Cannot create telnet port ('.$ret.')';;\
        return undef;;\
      }\
\
      $::attr{$telnet}{room} = 'hidden';;\
      $::attr{$telnet}{allowfrom} = '127.0.0.1';;\
    }\
\
    return $::defs{$telnet}{PORT};;\
  }\
\
  sub tvParse($$$)\
  {\
    my ($device, $mode, $port) = @_;;\
    my $obj;;\
    my $xml;;\
    my $lastChannel = '';;\
    my $reading = '';;\
    my $i = 999;;\
    my $n = 999;;\
    my $k = 0;;\
    my $primeTime = substr(FmtDateTime(time() + $_timeAdjust), 0, 11).'20:14:00';;\
    my $sendTelnet = '';;\
\
    $obj = XML::Bare->new(file => $_dataFile);;\
    $xml = $obj->parse();;\
\
    if (!$@)\
    {\
      my $old = time() + $_timeAdjust;;\
\
      foreach (@{forcearray($xml->{'tv'}{'programme'})})\
      {\
        my $stop = xmltv2epoch($_->{'stop'}{'value'});;\
\
        ## filter old stuff\
        if ($stop > $old)\
        {\
          if ($lastChannel ne $_->{'channel'}{'value'})\
          {\
            $lastChannel = $_->{'channel'}{'value'};;\
            $reading = $_->{'channel'}{'value'};;\
            $reading =~ s/[\.\s]//g;;\
            $reading =~ s/de$//;;\
            $n = 0;;\
\
            if ((0 == $_updateBasedOnStarttimes) || !exists($_startTimes{$reading}) || ($_startTimes{$reading} <= $old))\
            {\
              $i = 0;;\
\
              if (0 != $_updateBasedOnStarttimes)\
              {\
                $_startTimes{$reading} = $stop;;\
              }\
            }\
            else\
            {\
              ::Log3 $device, 4, $device.': '.$reading.' is blocked because actual program is not finished (start: '.$_startTimes{$reading}.', old: '.$old.').';;\
            }\
          }\
\
          if ($i < 3 && 'next' eq $mode)\
          {\
            my $fi = sprintf("%03d", $i);;\
            my $start = xmltv2epoch($_->{'start'}{'value'});;\
            my $readingName;;\
            my $readingValue;;\
\
            $readingName = 'next_'.$reading.'_'.$fi.'_bdate';;\
            $readingValue = substr(FmtDateTime($start), 0, 10);;\
            $sendTelnet .= ";;setreading $device $readingName $readingValue";;\
\
            $readingName = 'next_'.$reading.'_'.$fi.'_btime';;\
            $readingValue = substr(FmtDateTime($start), 11, 8);;\
            $sendTelnet .= ";;setreading $device $readingName $readingValue";;\
\
            $readingName = 'next_'.$reading.'_'.$fi.'_title';;\
            $readingValue = filterText(@{forcearray($_->{'title'})}[0]->{'value'});;\
            $sendTelnet .= ";;setreading $device $readingName $readingValue";;\
\
            $readingName = 'next_'.$reading.'_'.$fi.'_stitle';;\
            if (exists($_->{'sub-title'}{'value'}))\
            {\
              $readingValue = filterText($_->{'sub-title'}{'value'});;\
            }\
            else\
            {\
              $readingValue = 'na';;\
            }\
            $sendTelnet .= ";;setreading $device $readingName $readingValue";;\
\
            $readingName = 'next_'.$reading.'_'.$fi.'_desc';;\
            if (exists($_->{'desc'}{'value'}))\
            {\
              $readingValue = filterText($_->{'desc'}{'value'});;\
            }\
            else\
            {\
              $readingValue = 'na';;\
            }\
            $sendTelnet .= ";;setreading $device $readingName $readingValue";;\
\
            $k++;;\
            $i++;;\
          }\
\
          if ($n < 3 && 'prime' eq $mode)\
          {\
            my $start = xmltv2epoch($_->{'start'}{'value'});;\
            my $fmtStart = FmtDateTime($start);;\
            my $bdate = substr($fmtStart, 0, 10);;\
            my $btime = substr($fmtStart, 11, 8);;\
\
            if ($bdate.' '.$btime gt $primeTime)\
            {\
              my $fn = sprintf("%03d", $n);;\
              my $readingName;;\
              my $readingValue;;\
\
              $readingName = 'prime_'.$reading.'_'.$fn.'_bdate';;\
              $readingValue = substr(FmtDateTime($start), 0, 10);;\
              $sendTelnet .= ";;setreading $device $readingName $readingValue";;\
\
              $readingName = 'prime_'.$reading.'_'.$fn.'_btime';;\
              $readingValue = substr(FmtDateTime($start), 11, 8);;\
              $sendTelnet .= ";;setreading $device $readingName $readingValue";;\
\
              $readingName = 'prime_'.$reading.'_'.$fn.'_title';;\
              $readingValue = filterText(@{forcearray($_->{'title'})}[0]->{'value'});;\
              $sendTelnet .= ";;setreading $device $readingName $readingValue";;\
\
              $readingName = 'prime_'.$reading.'_'.$fn.'_stitle';;\
              if (exists($_->{'sub-title'}{'value'}))\
              {\
                $readingValue = filterText($_->{'sub-title'}{'value'});;\
              }\
              else\
              {\
                $readingValue = 'na';;\
              }\
              $sendTelnet .= ";;setreading $device $readingName $readingValue";;\
\
              $readingName = 'prime_'.$reading.'_'.$fn.'_desc';;\
              if (exists($_->{'desc'}{'value'}))\
              {\
                $readingValue = filterText($_->{'desc'}{'value'});;\
              }\
              else\
              {\
                $readingValue = 'na';;\
              }\
              $sendTelnet .= ";;setreading $device $readingName $readingValue";;\
\
              $k++;;\
              $n++;;\
            }\
          }\
\
          if ($k >= 10)\
          {\
            ##::Log3 $device, 5, $device.': '.encode_utf8($sendTelnet);;\
            \
            `perl /opt/fhem/fhem.pl $port "$sendTelnet"`;;\
            \
            $k = 0;;\
            $sendTelnet = '';;\
          }\
        }\
      }\
\
      if ('' ne $sendTelnet)\
      {\
        ##::Log3 $device, 5, $device.': '.encode_utf8($sendTelnet);;\
\
        `perl /opt/fhem/fhem.pl $port "$sendTelnet"`;;\
      }\
    }\
\
    return %{$_startTimes};;\
  }\
\
  sub tvMerge($;;$)\
  {\
    my ($dstName, $srcName) = @_;;\
    my $fh;;\
    my $dst;;\
    my $src;;\
    my $start = '';;\
    my $channels1 = '';;\
    my $channels1_flt = '';;\
    my $channels2 = '';;\
    my $channels2_flt = '';;\
    my $programms1 = '';;\
    my $programms1_flt = '';;\
    my $programms2 = '';;\
    my $programms2_flt = '';;\
    my $end = '';;\
    my $pos;;\
\
    open($fh, '<', $dstName) or die "Can't open file $!";;\
    read($fh, $dst, -s $fh);;\
    close($fh);;\
\
    if (defined($srcName))\
    {\
      open($fh, '<', $srcName) or die "Can't open file $!";;\
      read($fh, $src, -s $fh);;\
      close($fh);;\
    }\
\
    if (-1 != ($pos = index($dst, '<channel ')))\
    {\
      $start = substr($dst, 0, $pos);;\
    }\
\
    if (-1 != ($pos = rindex($dst, '</programme>')))\
    {\
      $end = substr($dst, $pos + 12);;\
    }\
\
    while ($dst =~ /(\s*<channel\s.*?id="(.*?)".*?<\/channel>)/sg)\
    {\
      if (0 != $_filterChannels)\
      {\
        $_ = $1;;\
\
        if ($2 =~ $_channelFilterNew)\
        {\
          $channels1_flt .= $_;;\
        }\
      }\
      else\
      {\
        $channels1 .= $1;;\
      }\
    }\
\
    while ($dst =~ /(\s*<programme\s.*?channel="(.*?)".*?<\/programme>)/sg)\
    {\
      if (0 != $_filterChannels)\
      {\
        $_ = $1;;\
\
        if ($2 =~ $_channelFilterNew)\
        {\
          $programms1_flt .= $_;;\
        }\
      }\
      else\
      {\
        $programms1 .= $1;;\
      }\
    }\
\
    if (defined($srcName))\
    {\
      while ($src =~ /(\s*<channel\s.*?id="(.*?)".*?<\/channel>)/sg)\
      {\
        if (0 != $_filterChannels)\
        {\
          $_ = $1;;\
\
          if ($2 =~ $_channelFilterNew)\
          {\
            $channels2_flt .= $_;;\
          }\
        }\
        else\
        {\
          $channels2 .= $1;;\
        }\
      }\
\
      while ($src =~ /(\s*<programme\s.*?channel="(.*?)".*?<\/programme>)/sg)\
      {\
        if (0 != $_filterChannels)\
        {\
          $_ = $1;;\
\
          if ($2 =~ $_channelFilterNew)\
          {\
            $programms2_flt .= $_;;\
          }\
        }\
        else\
        {\
          $programms2 .= $1;;\
        }\
      }\
    }\
\
    if (0 != $_filterChannels)\
    {\
      open($fh, '>', $dstName) or die "Can't open file $!";;\
\
      if (defined($srcName))\
      {\
        print $fh $start.$channels1_flt.$channels2_flt.$programms1_flt.$programms2_flt.$end;;\
      }\
      else\
      {\
        print $fh $start.$channels1_flt.$programms1_flt.$end;;\
      }\
\
      close($fh);;\
    }\
    else\
    {\
      open($fh, '>', $dstName) or die "Can't open file $!";;\
\
      if (defined($srcName))\
      {\
        print $fh $start.$channels1.$channels2.$programms1.$programms2.$end;;\
      }\
      else\
      {\
        print $fh $start.$channels1.$programms1.$end;;\
      }\
\
      close($fh);;\
    }\
  }\
\
  sub tvDownload()\
  {\
    my $output = '';;\
\
    ## other server see below\
    $output .= qx(wget $_server/rytecDE_Basic.xz -O $_path/rytecDE_Basic.xz 2>&1);;\
    $output .= qx(xz -df $_path/rytecDE_Basic.xz 2>&1);;\
\
    if (0 != $_filterChannels)\
    {\
      tvMerge($_dataFile);;\
    }\
\
    return $output;;\
  }\
\
  sub tvDownloadMerge()\
  {\
    my $output = '';;\
\
    ## other server\
    ## http://www.xmltvepg.nl/rytecDE_Basic.xz\
    ## http://91.121.106.172/~rytecepg/epg_data/rytecDE_Basic.xz\
    ## http://rytecepg.epgspot.com/epg_data/rytecDE_Basic.xz\
    ## http://epg.vuplus-community.net/rytecDE_Basic.xz\
    ## http://www.xmltvepg.nl/rytecDE_Common.xz\
    ## http://91.121.106.172/~rytecepg/epg_data/rytecDE_Common.xz\
    ## http://rytecepg.epgspot.com/epg_data/rytecDE_Common.xz\
    ## http://epg.vuplus-community.net/rytecDE_Common.xz\
    ## http://www.xmltvepg.nl/rytecDE_SportMovies.xz\
    ## http://91.121.106.172/~rytecepg/epg_data/rytecDE_SportMovies.xz\
    ## http://rytecepg.epgspot.com/epg_data/rytecDE_SportMovies.xz\
    $output .= qx(wget $_server/rytecDE_Basic.xz -O $_path/rytecDE_Basic.xz 2>&1);;\
    $output .= qx(xz -df $_path/rytecDE_Basic.xz 2>&1);;\
    $output .= qx(wget $_server/rytecDE_Common.xz -O $_path/rytecDE_Common.xz 2>&1);;\
    $output .= qx(xz -df $_path/rytecDE_Common.xz 2>&1);;\
\
    tvMerge($_dataFile, $_path.'/rytecDE_Common');;\
\
    ## download and merge other files here if needed\
\
    return $output;;\
  }\
\
  sub startDownload($)\
  {\
    my $name = shift;;\
\
    ## prevent download spamming\
    if (-e $_dataFile)\
    {\
      my $ftime = ((time() - (stat($_dataFile))[9]) / 60.0 / 60.0 / 24.0);;\
\
      if ($ftime < 1.0)\
      {\
        ::Log3 $name, 1, $name.': Download of TV data skipped because file is not older than 1 day ('.($ftime).').';;\
        return;;\
      }\
    }\
\
    if (defined($_blockingcalls{PID_DOWNLOAD}))\
    {\
      ::Log3 $name, 3, $name.': Blocking call already running (download).';;\
\
      ::BlockingKill($_blockingcalls{PID_DOWNLOAD});;\
    }\
\
    $_blockingcalls{PID_DOWNLOAD} = ::BlockingCall('DOIF::doDownload', $name, 'DOIF::endDownload', 300, 'DOIF::abortDownload', $name);;\
  }\
\
  sub DOIF::doDownload($)\
  {\
    my $name = shift;;\
    my $output = '';;\
\
    $output = tvDownloadMerge();;\
\
    return $name.'|'.$output;;\
  }\
  \
  sub DOIF::endDownload($)\
  {\
    my ($name, $output) = split("\\|", shift);;\
\
    ::Log3 $name, 5, $name.': '.$output;;\
    ::Log3 $name, 4, $name.': Blocking call finished to download tv data.';;\
\
    delete($_blockingcalls{PID_DOWNLOAD});;\
  }\
\
  sub DOIF::abortDownload($)\
  {\
    my $name = shift;;\
\
    delete($_blockingcalls{PID_DOWNLOAD});;\
\
    ::Log3 $name, 1, $name.': Blocking call aborted (download).';;\
  }\
\
  sub startParse($$)\
  {\
    my ($name, $mode) = @_;;\
    my $port;;\
\
    if (defined($_blockingcalls{PID_PARSE}))\
    {\
      ::Log3 $name, 3, $name.': Blocking call already running (parse).';;\
\
      ::BlockingKill($_blockingcalls{PID_PARSE});;\
    }\
\
    $port = createTelnet($name);;\
    $port = $_telnetPort if (!defined($port));;\
\
    $_blockingcalls{PID_PARSE} = ::BlockingCall('DOIF::doParse', $name.'|'.$mode.'|'.$port, 'DOIF::endParse', 300, 'DOIF::abortParse', $name);;\
  }\
\
  sub DOIF::doParse($)\
  {\
    my ($name, $mode, $port) = split("\\|", shift);;\
    my $ret = $name;;\
    my %startTimes = tvParse($name, $mode, $port);;\
\
    foreach (keys(%startTimes))\
    {\
      $ret .= '|'.$_.'|'.$startTimes{$_};;\
    }\
\
    return $ret;;\
  }\
\
  sub DOIF::endParse($)\
  {\
    my ($name, @startTimes) = split("\\|", shift);;\
\
    for (my $i = 0;; $i < (scalar(@startTimes) / 2);; $i += 2)\
    {\
      $_startTimes{$startTimes[$i]} = $startTimes[$i + 1];;\
    }\
\
    ::Log3 $name, 4, $name.': Blocking call finished to parse tv data.';;\
\
    delete($_blockingcalls{PID_PARSE});;\
  }\
\
  sub DOIF::abortParse($)\
  {\
    my $name = shift;;\
\
    delete($_blockingcalls{PID_PARSE});;\
\
    ::Log3 $name, 1, $name.': Blocking call aborted (parse).';;\
  }\
}\
init\
{\
  startDownload("$SELF");;\
  set_Exec('init_next', 300, 'startParse("$SELF", "next")');;\
  set_Exec('init_prime', 600, 'startParse("$SELF", "prime")');;\
}\
{\
  if ([00:05|Mo Do])\
  {\
    startDownload("$SELF");;\
  }\
\
  if ([+00:15])\
  {\
    startParse("$SELF", 'next');;\
  }\
\
  if ([00:30])\
  {\
    startParse("$SELF", 'prime');;\
  }\
}
attr doif_TEST alias Aktuelles TV-Programm
attr doif_TEST userattr server channelFilter
attr doif_TEST server http://www.xmltvepg.nl
attr doif_TEST channelFilter ^(?:DasErste\.|ZDF\.|Sat1\.|RTL2?\.|Pro(Sieben|SiebenMaxx)\.|DMax\.|Vox\.|KabelEins(?:Classic|Doku)?\.|ntv\.|Sixx\.|TLC\.|N24Doku\.|SonyEntertainmentTV\.|AandE\.|TNT(?:Serie|Film)\.|AnimalPlanet\.|History\.|Kinowelt\.|NatGeoHD\.|PLANET\.|Silverline\.|13thStreet\.|AXN\.|SciFi\.)
attr doif_TEST event-on-change-reading .*
attr doif_TEST room TV
attr doif_TEST uiTable {\
  package ui_Table;;\
\
  $SHOWNOSTATE = 1;;\
  $ATTRIBUTESFIRST = 1;;\
  $NUM_CHANNELS = 29;;\
\
  $TR{0,($NUM_CHANNELS + 2)} = "style='color:yellow;;text-align:center;;font-weight:bold;;font-size:18px'";;\
  $TD{0..$NUM_CHANNELS,($NUM_CHANNELS + 2)..($NUM_CHANNELS + 2 + $NUM_CHANNELS)}{2,4} = "style='font-size:16px;;border-right-style:solid;;border-color:#CCCCCC;;border-right-width:1px;;'";;\
  $TD{0..$NUM_CHANNELS,($NUM_CHANNELS + 2)..($NUM_CHANNELS + 2 + $NUM_CHANNELS)}{0} = "align='center' style='border-right-style:solid;;border-color:#CCCCCC;;border-right-width:1px;;'";;\
  $TD{0..($NUM_CHANNELS + 2 + $NUM_CHANNELS)}{1,3,5,6} = "style='font-size:16px;;'";;\
  $TD{($NUM_CHANNELS + 1)}{0..6} = "style='border-top-style:solid;;border-bottom-style:solid;;border-color:#CCCCCC;;border-top-width:1px;;border-bottom-width:1px;;'";;\
\
  sub showIcon\
  {\
    my ($icon, $device, $state) = @_;;\
\
    if (defined($device) && defined($state))\
    {\
      return "<a href=\"$::FW_ME?cmd=set $device $state$::FW_CSRF\">".ICON("tv/$icon")."</a>";;\
    }\
    else\
    {\
      return ICON("tv/$icon");;\
    }\
  }\
\
  sub showIconIP\
  {\
    my ($icon, $device, $state) = @_;;\
\
    if (defined($device) && defined($state))\
    {\
      return "<a href=\"". ::ReadingsVal("$SELF","$device","") =~ s/.state/$state/r ."\" target=\"IPTV\">".ICON("tv/$icon")."</a>";;\
    }\
    else\
    {\
      return ICON("tv/$icon");;\
    }\
  }\
\
  sub unfold\
  {\
    my ($title, $desc) = @_;;\
\
    $title = 'na' if (!defined($title));;\
    $desc = 'na'."\n\n".'na' if (!defined($desc));;\
\
    $title =~ s/(.{1,45}|\S{46,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g;;\
    $desc =~ s/<br>/\n/g;;\
    $desc =~ s/(.{1,65}|\S{66,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g;; \
    $desc =~ s/[\r\'\"]/ /g;;\
    $desc =~ s/[\n]|\\n/<br>/g;;\
\
    return "<a href=\"#!\" onclick=\"FW_okDialog(';".$desc."';)\">".$title."</a>";;\
  }\
}\
\
## parameter: device name, mode (next or prime), channel name (see xml data file), icon name (filename of channel logo)\
DEF TPL_TV(showIcon("$4",undef,undef)|substr([$1:$2_$3_000_btime],0,5)|unfold([$1:$2_$3_000_title],[$1:$2_$3_000_stitle]."\n\n".[$1:$2_$3_000_desc])|substr([$1:$2_$3_001_btime],0,5)|unfold([$1:$2_$3_001_title],[$1:$2_$3_001_stitle]."\n\n".[$1:$2_$3_001_desc])|substr([$1:$2_$3_002_btime],0,5)|unfold([$1:$2_$3_002_title],[$1:$2_$3_002_stitle]."\n\n".[$1:$2_$3_002_desc]))\
\
## parameter: device name, mode (next or prime), channel name (see xml data file), icon name (filename of channel logo), device name for set command, command\
## example: TPL_TVSET($SELF,next,DasErste,ard,<ir blaster device>,<ir blaster command>)\
DEF TPL_TVSET(showIcon("$4","$5","$6")|substr([$1:$2_$3_000_btime],0,5)|unfold([$1:$2_$3_000_title],[$1:$2_$3_000_stitle]."\n\n".[$1:$2_$3_000_desc])|substr([$1:$2_$3_001_btime],0,5)|unfold([$1:$2_$3_001_title],[$1:$2_$3_001_stitle]."\n\n".[$1:$2_$3_001_desc])|substr([$1:$2_$3_002_btime],0,5)|unfold([$1:$2_$3_002_title],[$1:$2_$3_002_stitle]."\n\n".[$1:$2_$3_002_desc]))\
\
## parameter: device name, mode (next or prime), channel name (see xml data file), icon name (filename of channel logo), device name for set command, command\
## example: TPL_TVIP($SELF,next,DasErste,ard,VIEW1,29438503040)\
DEF TPL_TVIP(showIconIP("$4","$5","$6")|substr([$1:$2_$3_000_btime],0,5)|unfold([$1:$2_$3_000_title],[$1:$2_$3_000_stitle]."\n\n".[$1:$2_$3_000_desc])|substr([$1:$2_$3_001_btime],0,5)|unfold([$1:$2_$3_001_title],[$1:$2_$3_001_stitle]."\n\n".[$1:$2_$3_001_desc])|substr([$1:$2_$3_002_btime],0,5)|unfold([$1:$2_$3_002_title],[$1:$2_$3_002_stitle]."\n\n".[$1:$2_$3_002_desc]))\
\
"Sender"|"ab"|"Aktuelle Sendung"|"ab"|"Nächste Sendung"|"ab"|"Sendung"\
TPL_TV($SELF,next,DasErste,ard)\
TPL_TV($SELF,next,ZDF,zdf)\
TPL_TV($SELF,next,Sat1,sat1)\
TPL_TV($SELF,next,RTL,rtl)\
TPL_TV($SELF,next,RTL2,rtl2)\
TPL_TV($SELF,next,ProSieben,pro7)\
TPL_TV($SELF,next,DMax,dmax)\
TPL_TV($SELF,next,Vox,vox)\
TPL_TV($SELF,next,KabelEins,kabel1)\
TPL_TV($SELF,next,KabelEinsClassic,kabel1classic)\
TPL_TV($SELF,next,13thStreet,13thstreet)\
TPL_TV($SELF,next,Silverline,silverline)\
TPL_TV($SELF,next,TNTFilm,tntfilm)\
TPL_TV($SELF,next,AXN,axn)\
TPL_TV($SELF,next,SonyEntertainmentTV,sonytv)\
TPL_TV($SELF,next,Kinowelt,kinowelt)\
TPL_TV($SELF,next,ProSiebenMaxx,pro7maxx)\
TPL_TV($SELF,next,Sixx,sixx)\
TPL_TV($SELF,next,TNTSerie,tntserie)\
TPL_TV($SELF,next,SciFi,syfy)\
TPL_TV($SELF,next,ntv,ntv)\
TPL_TV($SELF,next,N24Doku,n24)\
TPL_TV($SELF,next,History,history)\
TPL_TV($SELF,next,PLANET,planet)\
TPL_TV($SELF,next,KabelEinsDoku,kabel1doku)\
TPL_TV($SELF,next,AnimalPlanet,animalplanet)\
TPL_TV($SELF,next,NatGeoHD,natgeo)\
TPL_TV($SELF,next,TLC,tlc)\
TPL_TV($SELF,next,AandE,ae)\
"&nbsp;;"|"&nbsp;;"|"&nbsp;;"|"&nbsp;;"|"&nbsp;;"|"&nbsp;;"|"&nbsp;;"\
"Sender"|"ab"|"Sendung"|"ab"|"Sendung"|"ab"|"Sendung"\
TPL_TV($SELF,prime,DasErste,ard)\
TPL_TV($SELF,prime,ZDF,zdf)\
TPL_TV($SELF,prime,Sat1,sat1)\
TPL_TV($SELF,prime,RTL,rtl)\
TPL_TV($SELF,prime,RTL2,rtl2)\
TPL_TV($SELF,prime,ProSieben,pro7)\
TPL_TV($SELF,prime,DMax,dmax)\
TPL_TV($SELF,prime,Vox,vox)\
TPL_TV($SELF,prime,KabelEins,kabel1)\
TPL_TV($SELF,prime,KabelEinsClassic,kabel1classic)\
TPL_TV($SELF,prime,13thStreet,13thstreet)\
TPL_TV($SELF,prime,Silverline,silverline)\
TPL_TV($SELF,prime,TNTFilm,tntfilm)\
TPL_TV($SELF,prime,AXN,axn)\
TPL_TV($SELF,prime,SonyEntertainmentTV,sonytv)\
TPL_TV($SELF,prime,Kinowelt,kinowelt)\
TPL_TV($SELF,prime,ProSiebenMaxx,pro7maxx)\
TPL_TV($SELF,prime,Sixx,sixx)\
TPL_TV($SELF,prime,TNTSerie,tntserie)\
TPL_TV($SELF,prime,SciFi,syfy)\
TPL_TV($SELF,prime,ntv,ntv)\
TPL_TV($SELF,prime,N24Doku,n24)\
TPL_TV($SELF,prime,History,history)\
TPL_TV($SELF,prime,PLANET,planet)\
TPL_TV($SELF,prime,KabelEinsDoku,kabel1doku)\
TPL_TV($SELF,prime,AnimalPlanet,animalplanet)\
TPL_TV($SELF,prime,NatGeoHD,natgeo)\
TPL_TV($SELF,prime,TLC,tlc)\
TPL_TV($SELF,prime,AandE,ae)


Was mir aber beim rum spielen aufgefallen ist, ist das die Attribute nicht übernommen werden, wenn man die bei RAW Definition einfügt. Die muss man immer manuell nachziehen, was irgendwie recht beschissen ist, da man dann die ganzen doppelten Semikolons und Backslashes manuell vorher entfernen muss.