IPS-Serienrecorder für Enigma basierte Linux-Receiver

Hallo Kollegen,

ich habe festgestellt, dass doch so einige mit Enigma-Boxen receiver-mässig unterwegs sind. Ich könnte mir vorstellen, dass manche von dem Serienrekorder-Plugin Problem (plugin funktioniert nicht mehr, da automatische abfragen momentan gezielt ausgesperrt werden) betroffen sind, wenn nicht, auch gut. Ich habe mich jedenfalls dazu entschlossen den derzeitigen Stand (Achtung BETA!) meines Serienrecorder-Scripts zu teilen, falls es jemand brauchen kann.
Ich hab mich drangesetzt und versuch die Grundfunktion in IP-Symcon zu integrieren, mit Vor- aber auch einigen Nachteilen.

Vorteile:

  • kann die Aufnahmen auf mehrere Receiver verteilen
  • kann mehr als die vorhandenen Tuner nutzen, wenn die Aufnahmen auf den gleichen Sendern (Frequs/Bands) laufen
  • kann komfortabel über das Web programmiert werden

Nachteile:

  • läuft nicht native auf der Box
  • kann nicht via Fernsteuerung programmiert werden

Funktionsweise:
Auch dieses script nutzt die wunschliste.de. Einfach kostenlos registrieren und die gewünschten Lieblingsserien hinzufügen. Wunschliste stellt für den TV-Planer einen rss-feed zu Verfügung der die nächsten 30 Serien-Ausstrahlungen beinhaltet. Dieser Feed wird ausgewertet und der Receiver automatisch via Webif-Api programmiert. Ein gemeinsames Aufnahmeverziechnis bei mehreren Boxen sollte Usus sein, damit auf allen Boxen der Inhalt konsumiert werden kann.

Vorgangsweise
Der feed wird unter der der adresse „https://www.wunschliste.de/xml/rss.pl?user_id=XXXXXXXX&key=YYYYYYYYYYY“ zu Verfügung gestellt. Bitte XXXXXXX und YYYYYYYYYYYY speichern, die brauchen wir als Input-Parameter für das script.
Als nächstes muss man die Channel-Liste erstellen. Ich gehe davon, dass alle gewünschten Channels in einem (welchem ist egal, kann vorgegeben werden) Bouquet gespeichert sind. Die Channel-Liste wird bei Start des scripts mit der Vorgabe $writechannels = 1 im Verzeichnis /IP-Symcon/Enigma/channels.txt erzeugt und hat folgenden Inhalt:
Service-Ref|Sendername
Beispiel:
1:0:19:132F:3EF:1:C00000:0:0:0:|ORF1 HD
1:0:19:33F8:3ED:1:C00000:0:0:0:|ORF2O HD
1:0:19:283D:3FB:1:C00000:0:0:0:|Das Erste HD
Einfach die unnötigen Sender löschen (Zeile löschen) und darauf achten, dass ihr keine Doppeleinträge habt wie „DAS Erste HD“ und „Das Erste“ oder so…
Channelliste immer als Kopie sichern.

Weitere Einstellungsparameter im Skript:
$writechannels = 0; // wenn 1 dann wird nur channelliste erzeugt * empfehle sicherung der datei* , wenn 0 werden die timer erstellt
$wunschliste_id = „XXXXXXXX“; // aus rss auf wunschliste.de abkritzeln
$wunschliste_key = „YYYYYYYYYYY“; // aus rss auf wunschliste.de abkritzeln
$debug = false; //auf true stellen wenn (viel!) mehr meldungen gewünscht
$recordings_dir = „/media/net/Aufnahmen/“; // z.B für das gemountete Aufnahmenverzeichnis für die Auto-Timer
$smb_dir = „//smbserver/smbpath/“; //SMB Verzecinis für das Erstellen der Aufnahmenverzeichnisse (z.B. NAS Box)
$vorlauf_s = 300; //Anzahl der Sekunden die vor geplantem Start aufgenommen werden sollen
$nachlauf_s = 300; //Anzahl der Sekunden die nach geplantem Ende aufgenommen werden sollen
$logvar = 31633 /[Hardware\Enigma\Addons\Ausgabe]/; //Logvar muss händisch als String ~TextBox angelegt werden und dient zur Anzeige der Meldungen… (z.B. Alle 2h löschen)
$timervar = 29129; // enthält die Liste der heute angelegtenTimer und sollte alle 24 h gelöscht werden

################### Receiverliste definieren (Enigmaboxen)
$receiverlist = array(
array(‚Nr‘ => 0, ‚ip‘ => ‚10.10.10.216‘, ‚Name‘ => ‚TVServer1‘,‚UseTuners‘ => 10), //wenn Ihr p
array(‚Nr‘ => 1, ‚ip‘ => ‚10.10.10.229‘, ‚Name‘ => ‚TVServer2‘,‚UseTuners‘ => 10),
);

Wenn Ihr so wie ich dedizierte Aufnahmeboxen habt, so sollten wir alle Möglichkeiten nutzen (‚UseTuners‘ => 10). Wenn Ihr mindestes einen Tuner für Fernsehen freihalten möchtet, so definiert ‚UseTuners‘ mit Anzahlt Tuner - 1;

################ Conditions definieren
// in Sendung -> den Sendungsnamen
// in Condseason die Seasonnr kann > < oder =
// zur Seasonnummer sein
// ditto für die Episodennr
$condlist = array(
array(‚Sendung‘ => ‚Tatort‘, ‚CondSeason‘ => ‚=‘,‚S‘ => 0,‚CondEpisode‘ => ‚>‘,‚E‘ => 928)// erst die 2015er Serien
);

Oder Season > 3 Episode > 0 = Erst ab Staffel 4
Oder…
############## Titel korrigieren - Immer wieder werden unterschiedliche Titel/Serie verwendet, das könnt ihr hier korrigieren
$titlemappings = array(
array(‚Titel‘ => ‚Navy CIS New Orleans‘, ‚Ersatz‘ => ‚NCIS New Orleans‘),
);
############## Channels korrigieren: Hier könnt ihr (witzige :wink: Channel-Einträge der wunschliste auf Eure Kanalnamen ummappen
$channelmappings = array(
array(‚Titel‘ => ‚ORF eins‘, ‚Ersatz‘ => ‚ORF1 HD‘),
array(‚Titel‘ => ‚ORF2‘, ‚Ersatz‘ => ‚ORF2O HD‘),
array(‚Titel‘ => ‚ZDFneo‘, ‚Ersatz‘ => ‚zdf_neo‘),
array(‚Titel‘ => ‚ProSieben Fun‘,‚Ersatz‘ => ‚Pro7 FUN HD‘),
);

################### Bouquet Query
Die bouquet-query erhält man folgendemassen: http://ipadresse/web/getservices im browser deiner wahl eingeben (auf kleinschreibung achten) das bouquet heraussuchen, das ihr für die Aufnahmen wählt -> über Namen identifizieren unter <e2servicename> z.B.: <e2servicename>Favourites (TV)</e2servicename>
Die bouquet-query findet ihr dann unter <e2servicereference>
z.B.: <e2servicereference>1:7:1:0:0:0:0:0:0:0:FROM BOUQUET „userbouquet.favourites.tv“ ORDER BY bouquet</e2servicereference>

$bouquet_vu = ‚1:7:1:0:0:0:0:0:0:0:FROM BOUQUET „userbouquet.favourites.tv“ ORDER BY bouquet‘;

So das wars dann eigentlich auch schon. Ich feuere das script alle 10 min ab mit $writechannels = 0 (habe aber auch > 70 Serien-Einträge) und halte das Skript im Auge. Die String-Parserei ist ziemlich gewachsen im Verlauf der letzten Tage, wie gesagt für > 99% funktioniert es für mich.Elegante Script-Verbesserungen sind mehr als willkommen!!!

Comments very welcome - auf Klassen u.ä. habe ich bewusst verzichtet.
Viel Spass
hoep


//########################Config################################################################
  //
  // IPS_Serienrecorder
  // Autor: hoep
  // Datum 8.10.15
  // V: 0.7.9
  //
  //##############################################################################################
  /*
   * 
   * The IPS_Serienrecorder 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 3 of the License, or
   * (at your option) any later version.
   *
   * The IPS_Serienrecorder is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
   * GNU General Public License for more details.
   *
   * For the licese file, see http://www.gnu.org/licenses/gpl.txt.
   */

  $writechannels = 0; // wenn 1 dann wird nur channelliste erzeugt * empfehle sicherung der datei* , wenn 0 werden die timer erstellt
  $wunschliste_id = "xxxxxxxxxxx";  // aus rss auf wunschliste.de abkritzeln
  $wunschliste_key = "yyyyyyyyyyyyyyyy"; // aus rss auf wunschliste.de abkritzeln
  $debug = false; //auf true stellen wenn (viel !!!) mehr meldungen gewünscht
  $recordings_dir = "/media/net/Aufnahmen/"; // Aufnahmenverzeichnis für die Auto-Timer
  $smb_dir = "//SMBSERVER/SMBPATH/"; //SMB Verzecinis für das Erstellen der Aufnahmenverzeichnisse
  $vorlauf_s = 300; //Anzahl der Sekunden die vor geplantem Start aufgenommen werden sollen
  $nachlauf_s = 300; //Anzahl der Sekunden die nach geplantem Ende aufgenommen werden sollen
  $logvar = 31633 /*[Hardware\Enigma\Addons\Ausgabe]*/; //Logvar muss händisch als String ~TextBox angelegt werden und dient zur Anzeige der Meldungen...
  $timervar = 29129;
  
  //receiverliste definieren (Enigmaboxen)
  $receiverlist = array(
            array('Nr' => 0, 'ip' => 'X:X:X:X', 'Name' => 'TVServer1','UseTuners' => 10),
            array('Nr' => 1, 'ip' => 'Y.Y.Y.Y', 'Name' => 'TVServer2','UseTuners' => 10),
            );
  ///////////////////// Conditions definieren
  // in Sendung -> den Sendungsnamen
  // in Condseason die Seasonnr kann > < oder =
  // zur Seasonnummer sein
  // ditto für die Episodennr
  $condlist = array(
				  array('Sendung' => 'Tatort', 'CondSeason' => '=','S' => 0,'CondEpisode' => '>','E' => 928)// erst die 2015er Serien
				  );
  //Titel korrigieren
  $titlemappings = array(
						 array('Titel' => 'Navy CIS New Orleans', 'Ersatz' => 'NCIS New Orleans'),
						 );
  //Channels korrigieren
  $channelmappings = array(
                     array('Titel' => 'ORF eins',		'Ersatz' => 'ORF1 HD'),
                     array('Titel' => 'ORF2',			'Ersatz' => 'ORF2O HD'),
                     array('Titel' => 'ZDFneo', 		'Ersatz' => 'zdf_neo'),
                     array('Titel' => 'ProSieben Fun','Ersatz' => 'Pro7 FUN HD'),
                     );
  // die bouquet-query erhält man folgendemassen:
  // http://ipadresse/web/getservices im browser deiner wahl eingeben (auf kleinschreibung achten)
  // das bouquet heraussuchen, das ihr für die aufnahmen wählt -> über namen identifizieren unter <e2servicename> z.B.: <e2servicename>Favourites (TV)</e2servicename>
  // die bouquet-query findet ihr unter <e2servicereference> z.B.: <e2servicereference>1:7:1:0:0:0:0:0:0:0:FROM BOUQUET "userbouquet.favourites.tv" ORDER BY bouquet</e2servicereference>
  
  $bouquet_vu = '1:7:1:0:0:0:0:0:0:0:FROM BOUQUET "userbouquet.favourites.tv" ORDER BY bouquet';
  
  //###################### ab hier auf eigene Gefahr##############################################

 //Los gehts
  if (!IPS_SemaphoreEnter($IPS_SELF, 1)) {
    write2log($logvar,"######## FEHLER! Script läuft bereits - breche daher diesen Durchlauf ab! ######
");
    return;
  }
  $start = time();
  $log =" ##########################################   LOG vom ".date("d.m.Y H:i:s",time())." #################################################
";
  write2log($logvar,$log);
  $timerdaten = array();
  $channels = array();
  $serviceref = array();
  $finished_timers = array();
  $zaehler = 0;
  $chanfile = IPS_GetKernelDir()."\\Enigma\channels.txt";
  if ($writechannels == 1) {
	 $bouquet_str = urlencode($bouquet_vu);
	 //Channels vom Receiver einlesen
    $filehandle = fopen($chanfile,"w");
    $chanlist = new SimpleXMLElement(file_get_contents("http://".$receiverlist[0]['ip']."/web/getservices?sRef=".$bouquet_str));
    foreach ($chanlist->e2service as $chan) {
    if ($debug) echo $chan->e2servicereference."  ".utf8_decode($chan->e2servicename)."
";
	   fwrite($filehandle,$chan->e2servicereference."|".utf8_decode($chan->e2servicename)."
");
	   $channels[] = (string) strtolower(utf8_decode($chan->e2servicename));
	   $serviceref[] = $chan->e2servicereference;
    }
    	fclose($filehandle);
    	goto ChannelFileOnly;
   }
   else {
	  //Channelliste existiert, aus der Datei einlesen
	  $chanlist = file($chanfile);
	  foreach ($chanlist as $chan) {
		 $h = explode("|",$chan);
		 $channels[] = (string) strtolower(utf8_decode($h[1]));
		 $serviceref[] = $h[0];
	  }
   }
  //Lieblingsserien von der Wunschliste holen
  $response = @file_get_contents("http://www.wunschliste.de/xml/rss.pl?user_id=".$wunschliste_id."&key=".$wunschliste_key);
  if ($response == false) {
	 write2log($logvar,"######## FEHLER! Wunschliste.de nicht erreichbar!! ######
");
    die('FEHLER - Wunschliste.de nicht erreichbar!');
  }
  $wunschliste = new SimpleXMLElement($response);
  foreach ($wunschliste->channel->item as $sendung) {
	 $NoTimer = false;
	 //String - Mist entfernen
    $zeile = str_replace(' (Schweiz)',"",str_replace(' (Österreich)',"",str_replace(" (Pay-TV)","",utf8_decode($sendung->title))));
    //Nach Belieben erweitern
    $cnt = substr_count($zeile,"/");
    $sender = str_replace(" Channel","",str_replace(" Universal","",trim(explode("/",$zeile)[$cnt])));
    // Channelmappings durchführen
		foreach($channelmappings as $mapping) {
		  if ($sender == $mapping['Titel'])
			 $sender = $mapping['Ersatz'];
		}
    //Titel ermitteln
    //wie of kommt der ":" vor
    $count = substr_count($zeile,":");
    //wie oft kommt die "(" vor z.B Titel - (1).(1.1) bei mehrteiligen episoden
    $count2 = substr_count($zeile,"(");
    if ($count == 1)
      $title = substr($zeile,0,strpos($zeile,":"));
	 else {
		// pattern, replacement, string, limit
		$zeile = preg_replace('/:/', '#', $zeile, 1);
		if (substr_count($zeile," - ") == 1)
        $title = str_replace("#","",substr($zeile,0,strpos($zeile,":")));
		else {
		  $title = substr($zeile,0,strpos($zeile,"#"));
		}
	 }
	 	 
	 if ($count2 > 1) {
	   $zeile = str_replace("(5)"," - Teil 5",str_replace("(4)"," - Teil 4",str_replace("(3)"," - Teil 3",str_replace("(2)"," - Teil 2",str_replace("(1)"," - Teil 1",$zeile))))); // Mehr solltens wirklich nicht sein
	   // oder wenn andere Titulierung wie Blacklist - (Nr.67) - (6.1) -
	   $zhelp = extractStringBetween("(",")",$zeile);
		if (count($zhelp) > 1){
		  $seasonepisode = $zhelp[1];
		  $repl = "(".$zhelp[0].")";
		  $replto = "~".$zhelp[0]."~";
		  $zeile = str_replace($repl,$replto,$zeile);
		  $poshelp = strpos($zeile,"(");
		  Goto SpecialHandling;
		  }
	   }
    //Season und episode ermitteln
	 $poshelp = strpos($zeile,"(");
	 if ($poshelp === false) {
		$seasonepisode ="S00E00";
		$seasonnr ="00";
		$episodenr ="00";
		$store_season = "S";
		goto NOSeasonEpisode;
	 }
    $seasonepisode = extractStringBetween("(",")",$zeile)[0];
  SpecialHandling:
    $help = explode(".",$seasonepisode);
    if ($debug) print_r($help);
    if (isset($help[1])) { //ganz normale season & episode daten
		if (count($help[1]) == 1)
	     $episodenr =  str_pad($help[1], 2 ,'0', STR_PAD_LEFT);
		else
		  if (is_numeric($episodenr))
          $episodenr =  str_pad($help[1], count($help[1]) ,'0', STR_PAD_LEFT);
		  else {
          $episodenr =  str_pad($help[1], 3 ,'0', STR_PAD_LEFT);
		  }
	   $seasonnr = str_pad($help[0], 2 ,'0', STR_PAD_LEFT);
	   $store_season = $help[0];
	 }
	 else {
		//nur nummerierung, also season auf 00 setzen und episodennummer anhängen
		$seasonnr ="00";
		$episodenr = str_pad($help[0], 2 ,'0', STR_PAD_LEFT);
      if ($debug) echo "Episoden-Nr: ".$episodenr."
";
		if (!is_numeric($episodenr)) $episodenr = '00';
		$store_season = "0";
	 }
	
NOSeasonEpisode:
	 
	 foreach ($condlist as $cond) {
		 if ($cond['Sendung'] == $title)
			{
			  if (($cond["CondSeason"]) == ">")
             if ($store_season < $cond["S"]) {echo $msg = "Timer für Serie ".$title." wird nicht zwischengespeichert, da Season ".$store_season." < Bedingung Season ".$cond["S"]." ist. Springe zum nächsten Eintrag
";  write2log($logvar,$msg);goto Next;}
           if (($cond["CondSeason"]) == "=")
             if (($store_season < $cond["S"]) or ($store_season > $cond["S"])) {echo $msg = "Timer für Serie ".$title." wird nicht zwischengespeichert, da Season ".$store_season." <> Bedingung Season ".$cond["S"]." ist. Springe zum nächsten Eintrag
";  write2log($logvar,$msg);goto Next;}
           if (($cond["CondSeason"]) == "<")
             if ($store_season > $cond["S"]) {echo $msg = "Timer für Serie ".$title." wird nicht zwischengespeichert, da Season ".$store_season." > Bedingung Season ".$cond["S"]." ist. Springe zum nächsten Eintrag
";  write2log($logvar,$msg);goto Next;}
           if (($cond["CondEpisode"]) == ">")
             if ($episodenr < $cond["E"]) {echo $msg = "Timer für Serie ".$title." wird nicht zwischengespeichert, da Episode ".$episodenr." < Bedingung Episode ".$cond["E"]." ist. Springe zum nächsten Eintrag
";  write2log($logvar,$msg);goto Next;}
           if (($cond["CondEpisode"]) == "=")
             if (($episodenr < $cond["E"]) or ($episodenr > $cond["E"])) {echo $msg = "Timer für Serie ".$title." wird nicht zwischengespeichert, da Episode ".$episodenr." <> Bedingung Episode ".$cond["E"]." ist. Springe zum nächsten Eintrag
";  write2log($logvar,$msg);goto Next;}
           if (($cond["CondEpisode"]) == "<")
             if ($episodenr > $cond["E"]) {echo $msg = "Timer für Serie ".$title." wird nicht zwischengespeichert, da Episode ".$episodenr." > Bedingung Episode ".$cond["E"]." ist. Springe zum nächsten Eintrag
";  write2log($logvar,$msg);goto Next;}
			 }
		}
	 //episodennamen ermitteln
    if ($poshelp !==false) $episodename = trim(extractStringBetween(":","(",$zeile)[0]);
    else {
		$ep = str_replace($title.": ","",$zeile);
		$h13 = explode(" - ",$ep);
		if (substr_count($zeile,"-") > 1) // Titel mit Bindestrich
		  $episodename = $h13[0]." - ".$h13[1]; else $episodename = $h13[0];
		   if ($debug) echo $msg = substr_count($zeile,"-")." - ".$zeile." - ".$title." - ".$episodename."
";
	 }
	  //Episodename umformatierung wieder rückgängig machen
    if ($count2 > 1) {
	   $episodename = str_replace(" - Teil 5","(5)",str_replace(" - Teil 4","(4)",str_replace(" - Teil 3","(3)",str_replace(" - Teil 2","(2)",str_replace(" - Teil 1","(1)",$episodename))))); // Mehr solltens wirklich nicht sein
	   if (count($zhelp) > 1){
	     $episodename = str_replace($replto,$repl,$episodename);
	     }
	   }
    $episodename = str_replace("/"," - ",$episodename);
    if ($debug) echo $episodename."
";
   
    //timernamen zusammenschustern
    $timername = $title." - S".$seasonnr."E".$episodenr." - ".$episodename;
    $timerdescription = "S".$seasonnr."E".$episodenr." - ".$episodename;
    $zeit = $sendung->pubDate;
    $zhelp = explode(" ",$zeit);
    //furchtbar elegantes stückchen code um aus dem channel array den richtigen channel zu extrahieren
	 $needle = strtolower($sender);
	 $ret = array_keys(array_filter($channels, function($var) use ($needle){
    return strpos($var, $needle) !== false;}));
    
	 if ($debug) print_r($ret);
	 if (count($ret) == 0){ //Kanal offensichtlich nicht in Kanalliste für Aufnahmen
	   echo $msg = date("d.m.Y H:i:s",time())."| ACHTUNG !! - Sender nicht in Senderliste gefunden -- Sender: ".$sender."
";
	   write2log($logvar,$msg);
	   goto Next;
	 }
	 // channel und service string ermitteln
	 $est_chan = $channels[$ret[0]];
	 $est_service = $serviceref[$ret[0]];
	 
	 //wunschlisten daten mit dem epg aus der box vergleichen
	 try {
		libxml_use_internal_errors(true);

	 $epgdata = new SimpleXMLElement(file_get_contents("http://".$receiverlist[0]['ip']."/web/epgservice?sRef=".$est_service));
	 $eventid = 0;
	 $eventstart = 0;
	 $eventend = 0;
	 $eventduration = 0;

	 foreach ($epgdata as $epg) {
  
	 $epg->eventdescription = str_replace(chr(26),'',$epg->eventdescription);
		$pos = strpos(utf8_decode($epg->e2eventdescription),$episodename);
		if (utf8_decode($epg->e2eventdescription) == $episodename) {
		  //Suche nach exaktem Episodennamen
		  $eventid = (int) $epg->e2eventid;
		  $eventstart = (int) $epg->e2eventstart;
		  $eventend = (int)$epg->e2eventstart+(int)$epg->e2eventduration;
		  $eventduration = (int)$epg->e2eventduration;
		  break;
		}
		else if ($pos !== false) {
		  //episodennamen klappt nicht, also Suche via strpos
        $eventid = (int) $epg->e2eventid;
		  $eventstart = (int) $epg->e2eventstart;
		  $eventend = (int)$epg->e2eventstart+(int)$epg->e2eventduration;
		  $eventduration = (int)$epg->e2eventduration;
		  break;
		}
		else {
		  //episodennamen klappt nicht, strpos klappt auch nicht, aber wir haben ja noch die Startzeit
		  $helpzeit = explode(" ",$zeit)[3];
		  //echo $helpzeit."
";
		  $hournow = date("H",time());
		  //heutigen Tag ermitteln
		  $day = (int) date("d",time());
		  $month = (int) date("m",time());
		  $year = (int) date("Y",time());
		  $hour = (int) explode(":",$helpzeit)[0];
		  if ($hour < $hournow) // ofensichtlich Datumssprung
			 $day = date("d",time()+86400);
		  $minute = (int) explode(":",$helpzeit)[1];
		  $second = (int) explode(":",$helpzeit)[2];
		  $timestamp = mktime($hour,$minute,$second,$month,$day,$year);
		  $timelower = $timestamp-300; //5 min buffer berücksichtigen
		  $timeupper = $timestamp+300; //5 min buffer berücksichtigen
		  if (((int) $epg->e2eventstart >= $timelower ) and ((int) $epg->e2eventstart <= $timeupper)) {
		  //endlich gefunden
          $eventid = (int) $epg->e2eventid;
		  	 $eventstart = (int) $epg->e2eventstart;
		    $eventend = (int)$epg->e2eventstart+(int)$epg->e2eventduration;
		    $eventduration = (int)$epg->e2eventduration;
		    break;
		  }
		}
	  }
    }
    catch (Exception $e) {}
    
    //alter schwede war das arbeit...
    if ($debug) echo $timername." - Sender: ".$sender." estimated Sender: ".$est_chan." Start ".date("d.m.Y H:i:s",$eventstart)." Dauer: ".gmdate("H:i:s",$eventduration)." Wunschliste-Zeit: ".$zeit."
";
    
    
    // Titlemappings durchführen
    foreach ($titlemappings as $titlemapping) {
		  $pos = strPos($title,$titlemapping['Titel']);
		 
		  if ($pos !== false) {
		  $title = str_replace($titlemapping['Titel'],$titlemapping['Ersatz'],$title);
		  if ($debug) echo "Position :".$pos." Titel ".$title."
";
		}
    }
    //checken ob die Folge schon in der Liste der aufgenommenen episoden ist
    $helpseries = $title;
    $helpseries = str_replace(".","_",$helpseries);
    $seriesfilename =  IPS_GetKernelDir()."Enigma\\".$helpseries.".series";
    if (file_exists($seriesfilename)) {
	    $ex_recs = file_get_contents($seriesfilename);
		 if ($debug) print_r($ex_recs);
	    $existing_recordings = explode("
",$ex_recs);
	    foreach($existing_recordings as $recording_entry) {
			$SE = explode("|",$recording_entry)[0];
			if ($SE != 'S00E00') {  //passt wir haben S und E <> 00
			$SE_Timer = "S".$seasonnr."E".$episodenr;
			if ($SE == $SE_Timer) { // Episode in der Liste der aufgenommenen, timer muss nicht mehr angelegt werden
			  $NoTimer = true;
			  //break;
			}
		  } // ende 	if ($SE != 'S00E00')
		  else { //sonst mit Episodennamen vergleichen
			 $zh = explode("|",$recording_entry)[1];
			  $postitlehelp = strpos($zh,$episodename);
			   if ($debug) echo "Vorhanden: ".$zh." - Neuer Titel: ".$episodename." Pos: ".$postitlehelp."
";
				if ($postitlehelp !== false) $NoTimer = true; //Episodennamen gefunden
		  }
	    }
    } else {
		echo $msg = 'Serienfile für '.$helpseries." nicht gefunden
";
		write2log($logvar,$msg);
	 }
    if ($NoTimer == false) {
      if ($eventstart < time()) { //Sendung läuft schon, wir wollen keine Halb-Aufnahmen
		  //echo $eventstart ." ".time(). "EventID :".$eventid."
";
        echo $msg = "Timer wird nicht zwischengespeichert ### ".$timername." ### läuft bereits - es wird auf Wiederholung gewartet
";
        write2log($logvar,$msg);
        goto Next;
        }
        
      $timerdaten[$zaehler]['title'] = $title;
      $timerdaten[$zaehler]['episodename'] = ($episodename);
      $timerdaten[$zaehler]['description'] = $timerdescription;
      $timerdaten[$zaehler]['timername'] = ($timername);
      $timerdaten[$zaehler]['season'] = ($seasonnr);
      $timerdaten[$zaehler]['episode'] = ($episodenr);
      $timerdaten[$zaehler]['channel'] = (trim($est_chan));
      $timerdaten[$zaehler]['serviceref'] = ($est_service);
      $timerdaten[$zaehler]['eventid'] = ($eventid);
      $timerdaten[$zaehler]['eventstart'] = ($eventstart-$vorlauf_s);
      $timerdaten[$zaehler]['eventend'] = ($eventend+$nachlauf_s);
      $timerdaten[$zaehler]['eventduration'] = ($eventduration+$vorlauf_s+$nachlauf_s);
      $rec_dir = $recordings_dir.$title."/Season ".$store_season."/";
      $timerdaten[$zaehler]['recordingdir'] = ($rec_dir);
      echo $msg = "Timer wird als Timer[".$zaehler."] zwischengespeichert ### ".$timername." ### Verzeichnis: ".$rec_dir."
";
      write2log($logvar,$msg);
      $zaehler++;
    } else {
        echo $msg = "Timer wird nicht zwischengespeichert ### ".$timername." ### ist bereits im Aufnahmeverzeichnis oder in der Datenbank vorhanden
";
        write2log($logvar,$msg);
	   }
 Next:
  }
  if ($debug) print_r($wunschliste);
  if ($debug) print_r($timerdaten);
  
  // Ab hier wirds ernst mit den Timern.....
  
  // zuerst lesen wir existierende timer ein
  foreach ($receiverlist as $receiver){
     $timerlist_existing =  simplexml_load_file("http://".$receiver['ip']."/web/timerlist");
     if ($debug) print_r($timerlist_existing);
     $i = 0;
     foreach ($timerdaten as $new_timer) { //-- timerdaten
     $used_tuner = 0; //tuner für timeslot resetten
		 foreach($timerlist_existing as $programmed_timer) { //--- Programmierte timer
		   if ($new_timer['eventid'] == (int) $programmed_timer->e2eit) { // Timer schon in der Timerliste
			  echo $msg = 'Timer['.$i.'] ###'.urldecode($new_timer['timername']). " ### bereits programmiert. Springe zum nächsten Timer!"."
";
			   write2log($logvar,$msg);
			   $finished_timers[] = $i;
			  goto NextTimer;
			}  // Timer schon in timerliste
			// Zeitslot checken                                                                                                    //               -----------------
			if ((($new_timer['eventend'] >= (int) $programmed_timer->e2timebegin) and ($new_timer['eventend'] <= (int) $programmed_timer->e2timend)) or //          ---------------------
            (($new_timer['eventstart'] >= (int) $programmed_timer->e2timebegin) and ($new_timer['eventstart'] <= (int) $programmed_timer->e2timeend))or  //                               ------------------
            (($new_timer['eventstart'] >= (int) $programmed_timer->e2timebegin) and ($new_timer['eventend'] <= (int) $programmed_timer->e2timeend))or//                          ----------
            (($new_timer['eventstart'] <= (int) $programmed_timer->e2timebegin) and ($new_timer['eventend'] >= (int) $programmed_timer->e2timeend)))//                    -----------------------------
			 {  // checken wieviele timer im timeslot schon existieren
				if ($debug) echo "-- Timer anlegen ".date("H:i:s",$new_timer['eventstart'])." bis ".date("H:i:s",$new_timer['eventend'])." existierender Timer: ".date("H:i:s",(int) $programmed_timer->e2timebegin)." bis ".date("H:i:s",(int) $programmed_timer->e2timeend)."
";
			   $used_tuner++; //belegte tuner hochzählen
			   if ($debug) echo "Verbrauchter Tuner ".$used_tuner." mit Timer ".(string)$programmed_timer->e2name."
";
			 }
		 }
	    if ($used_tuner > $receiver['UseTuners']){ // die Receiver können am selben Bouquet mehr als 1 timer anlegen, also sollten wirs probieren

			echo $msg = date("d.m.Y H:i:s",time())."| Alle Tuner belegt! Springe zum nächsten Receiver ".urldecode($new_timer['timername'])."
";
			write2log($logvar,$msg);
			//echo "verbrauchte Tuner: ".($used_tuner)."
";
		 } else { //timer anlegen
				$result = mk_timer($new_timer,$receiver,$smb_dir,$recordings_dir,$logvar,$timervar);
				if ($result == true){
				  $finished_timers[]=$i;
				  }
				else {
				  echo $msg = ' #### Fehler beim Erstellen des Timers '.$new_timer['timername']." auf Receiver: ".$receiver['Name']."
"; 				}
			} // ende timer anlegen
	  NextTimer:
	  $i++;
     }
      if (count($finished_timers) == count($timerdaten)) goto TimersFinished;
      for ($m = 0;$m < count($finished_timers);$m++) {
		 unset($timerdaten[$finished_timers[$m]]);
     }
	  $timerdaten = array_values($timerdaten);
	  //var_dump($timerdaten);
     
     // Next Tuner
     if ($debug) echo "NextTuner-Eisnprungsmarke
";
  }

TimersFinished:
//print_r($timerdaten);
ChannelFileOnly:
  if ($writechannels == 1){
   echo $msg = "Channel file geschrieben!
";
   write2log($logvar,$msg);
   $log.= date("d.m.Y H:i:s",time())."|	Channel file geschrieben!
";
   $ende = time();
   IPS_SemaphoreLeave($IPS_SELF);
  }
  else {
	 $ende =time();
	 $diff = $ende-$start;
	 $runtime = date("i:s",$diff);
    $msg = date("d.m.Y H:i:s",time())."| Durchlauf beendet - Laufzeit: ".$runtime."
";
    write2log($logvar,$msg);
  //print_r($finished_timers);
  $logfile = IPS_GetKernelDir()."Enigma\\logfile.log";
  $filehandle = fopen($logfile,"a");
  fwrite($filehandle,$log);
  fclose($filehandle);
  if ($debug) echo count($finished_timers)." - ".count($timerdaten);
  $str = "";
        $timestamp = time();

        $str.= date("H:i:s", $timestamp)." - Receiver Timer gechecked...";
        SetValue(35853 /*[Steuerung\Log\Log]*/,$str);
  IPS_SemaphoreLeave($IPS_SELF);
  //print_r($finished_timers);
}

function mk_timer($timer,$receiver,$smb_dir,$recordings_dir,$logvar,$timervar){
  $dirname = str_replace($recordings_dir,$smb_dir,urldecode($timer['recordingdir']));
  $dirname = substr($dirname, 0, -1);
   echo $msg = 'Timer anlegen '.urldecode($timer['timername']).' in Verzeichnis '.urldecode($timer['recordingdir'])." SMB: ".$dirname."
";
  write2log($logvar,$msg);
  if (!file_exists($dirname)) {
    $result = mkdir($dirname, 0777, true);
    if ($result == true) {
	    echo $msg = 'Verzeichnis '.$dirname.' angelegt'."
";
	    write2log($logvar,$msg);
	 }
  }
  //else echo $dirname." existiert"."
";

  $html = urlencode($timer["serviceref"])."&begin=".$timer["eventstart"]."&end=".$timer["eventend"]."&name=".urlencode(utf8_encode($timer["timername"]))."&description=".urlencode(utf8_encode($timer["description"]))."&disabled=0&justplay=0&afteevent=3&repeated=0&dirname=".urlencode($timer["recordingdir"])."&vpsplugin_enabled=1&vpsplugin_overwrite=0";
  $html = "http://".$receiver["ip"]."/web/timeradd?sRef=".($html);
  //if ($debug) echo $html."
";
  $result = simplexml_load_file($html);
  //$result = false;
  if (trim((string)$result->e2state) == 'True'){
	  $success = true;
	  echo $msg = " ####### Erfolg! ### ".utf8_decode((string)$result->e2statetext)." auf Receiver ".$receiver['Name']."
";
	  SetValueString($timervar,GetValueString($timervar).date("H:i",time())." ".utf8_decode((string)$result->e2statetext)." auf Receiver ".$receiver['Name']."
");
	  write2log($logvar,$msg);
	  // Episode in Datenbank aufnehmen
	  $helpseries = str_replace('.','_',$timer['title']);
	  $filename = IPS_GetKernelDir()."//Enigma/".$helpseries.".series";
	  $filehandle = fopen($filename,"a");
     fwrite($filehandle,"S".$timer['season']."E".$timer['episode']."|".$dirname."/".$timer['timername'].".ts"."
");
     fclose($filehandle);
  }
  else {
   $success  =false;
   echo $msg = " ####### Fehler! ### ".utf8_decode((string)$result->e2statetext)." auf Receiver ".$receiver['Name']."
";
   write2log($logvar,$msg);
   }
  //var_dump($result);
  return $success;
  //print_r($timer);
  //return true;
}

function write2log($logvar,$msg){
  SetValueString($logvar,GetValueString($logvar).$msg);
}

function extractStringBetween($cFirstChar, $cSecondChar, $sString)
{
    preg_match_all("/\\".$cFirstChar."(.*?)\\".$cSecondChar."/", $sString, $aMatches);
    return $aMatches[1];
}
?>

Ein kleines Hilfsskript um existierende Aufnahmen zu katalogisieren…

Bloss Integer-Variable für Anzahl der Episoden und Pfad im Skript einstellen.

Gruß
hoep


<?
ini_set( 'max_execution_time', 180);
$dir = "//SMBSERVER/SMBPath/";
$anz = 14691; // Integer Variable

function dir_rekursiv($verzeichnis)
{
  $filename = IPS_GetKernelDir()."/Enigma/recordings.txt";
  $handle =  opendir($verzeichnis);
  $filehandle =  fopen($filename,"a");
    while ($datei = readdir($handle))
    {
        if ($datei != "." && $datei != "..")
        {
            if (is_dir($verzeichnis.$datei)) // Wenn Verzeichniseintrag ein Verzeichnis ist
            {
                // Erneuter Funktionsaufruf, um das aktuelle Verzeichnis auszulesen
                dir_rekursiv($verzeichnis.$datei.'/');
            }
            else
            {
                // Wenn Verzeichnis-Eintrag eine Datei ist, diese ausgeben
                $ext = pathinfo($datei)['extension'];
                if ($ext == 'ts') {
					   fwrite($filehandle,realpath($verzeichnis)."/".$datei."
");
					   }
                }
        }
    }
    closedir($handle);
    fclose($filehandle);
}

$filename = IPS_GetKernelDir()."//Enigma/recordings.txt";
$filehandle =  fopen($filename,"w");
fclose($filehandle);
dir_rekursiv($dir);
$i = 0;
$filecount = 0;
$files = file($filename);
foreach ($files as $file){
  $filecount++;
  $path_parts = pathinfo($file);
  $count = substr_count($path_parts['filename'],"-");
  if ($count > 2 ) {
	 $test = explode(" - ",$path_parts['filename']);
	 if ((substr($test[1],0,1) != "S") and (substr($test[1],3,1) != "E"))
      $path_parts['filename'] = preg_replace('/-/', '#', $path_parts['filename'], 1);
  }
  $seriesinfo = explode(" - ",$path_parts['filename']);
  if (!isset($seriesinfo[2])) goto Next;
  $seriesinfo[0] = str_replace("#","-",$seriesinfo[0]);
  $filename = IPS_GetKernelDir()."//Enigma/".$seriesinfo[0].".series";
  
  if (file_exists($filename)) {
	    $ex_recs = file_get_contents($filename);
	    $existing_recordings = explode("
",$ex_recs);
	    foreach($existing_recordings as $recording_entry) {
			$SE = explode("|",$recording_entry)[0];
			//$SE_Timer = "S".$seasonnr."E".$episodenr;
			if ($SE == strtoupper($seriesinfo[1]));
			  goto Next;
			}
	    }
    }
  
  $i++;
  $filehandle = fopen($filename,"a");
  $file = str_replace("#","-",$file);
  fwrite($filehandle,strtoupper($seriesinfo[1])."|".$file);
  fclose($filehandle);
Next:
}
SetValueInteger($anz,$filecount);
echo $i.' Dateien in die Datenbank aufgenommen!'."
";
echo "Alles erledigt!";
?>


Hallo hoep.

ich habe ebenfall das „Serienrecorder-Problem“ :mad:
… und bin total begeistert über die von Dir hier aufgezeigte Lösungsmöglichkeit. :loveips:

Spätestens am Wochenende muss ich Deine Scripte ausprobieren.

Zu den Einstellungsparametern habe ich jedoch noch eine Frage.
Ich besitze nur eine Vu+Duo² und verwende kein NAS für die Ablage der Aufnahmen.
Was muss ich bei den Parametern „$recordings_dir“ und „$smb_dir“ eintragen ?
Jeweils das identische Verzeichnis auf der Vu+ ?

Gruß

Swifty

Hallo swifty,

habe oben mal den letzten Stand des scriptes reingepostet (Parsen der serien verbessert, bug bezgl. Sendungen am nächsten Tag ausgebessert).
Zu Deinen Fragen:

Ich bilde hier das Anlegen der Verzeichnisse/Unterverzeichnisse des Serienrecorders via IPS nach, dazu muss IPS natürlich auf das Verzeichnis via smb zugreifen können. Kannst Du auf Das Aufnahmeverzeichnis deiner Duo² (die ich übrigends auch verwende) via Win zugreifen ? Dann muss ins $smb_dir der SMB-Pfad aus Sicht des Windows Rechners. Ins $recordings_dir muss die Pfadsicht aus Linux-Sicht (z.B.: mnt/hdd/Aufnahmen oder so…). Das Script benutzt diese Infos um 1) das dir anzulegen und 2) als Parameter für den Timer zu übergeben, damit die Aufnahme im richtigen Verzeichnis abgelegt wird.

Grüße
hoep

Hallo heop,

vielen Dank für dein Script.

Komme aber noch nicht damit klar, Ich habe auch eine Duo².

Das Aufnahmeverzeichnis von Win10 $smb_dir heisst bei mir so „\VUDUO2\Harddisk\movie“ ist das richtig?
Und das Verzeichnis $recordings_dir heisst bei mir mnt/hdd/movie
Welche IP muss in die Zeile 249, ich habe die der Duo² genommen, bekomme aber eine Fehlermeldung das er das Verzeichnis nicht öffnen kann.

Und beim Script „Seriendatenbank aus existierenden Aufnahmen erzeugen“ was muss ich bei $dir = angeben, ich habe
das selbe wie bei $smb_dir angeben bekomme da auch eine Fehlermeldung das er das Verzeichnis nicht öffnen kann.

Tschau Hein09

Hallo Hein09,

gut, dass Du mich darauf hinweist. Um auf SMB zugreifen zu können, darf der Ip-Symcon Dienst nicht unter dem System-Konto ausgeführt werden, sondern muss mit einem realen User_Konto, das admin-Rechte und Zugriffsrechte auf das smb-Verzeichnis hat laufen. Diesen Hinweis, gibt es irgendwo von Paresy.
Desweiteren musst Du „“ escapen d.h. aus jedem „“ wird ein „\“, ich wähle daher immer die „/“ Schreibweise…
Meine Verzeichnisse schauen so aus:

$recordings_dir = „/media/net/Aufnahmen/“; // Aufnahmenverzeichnis für die Auto-Timer
$smb_dir = „//NAS/VUAufnahmen/“;

wobeo NAS der Name der NAS ist…

Gruß
hoep

Update auf 0.7.9 -> Errorhandling für Format-Fehler im Enigma-EPG zugefügt, Skript oben upgedated