Python Modul zum Steuern einer VDR-Instanz via SVDRP

  • Beim Init des Moduls stelle ich eigentlich auf den Zeichensatz des VDR um.

    Das hindert den VDR nicht daran Bytes zu senden, die nicht zum angekündigten Encoding passen.

    Ich hänge mal den Output von mehreren Versuchen an, mit einem print(line) in __init__.py", nach line 87.

    Das ist zu spät, das Decoding passiert ja schon implizit im readline() in der Zeile drüber. Wenn man nicht will, dass das fehlschlägt, muss man den socket mit einem passend gesetzten errors Argument öffnen oder Rohdaten vom Socket lesen und dann später selber dekodieren.


    In der channels.conf sehe ich beim Eintrag ein 8-Bit Zeichen für das "Ü" in "FÜRTH - PAULI", das hatte ich gestern auf meinem Rechner auch. Dass die Zeile etwas springt, hängt vermutlich damit zusammen, in welchen Chunks die Daten vom Socket gelesen und aus dem Buffer dekodiert werden, bevor sie an den Zeilengrenzen aufgetrennt werden.

    yaVDR-Dokumentation (Ceterum censeo enchiridia esse lectitanda.)

  • Das ist zu spät, das Decoding passiert ja schon implizit im readline() in der Zeile drüber.

    Das war mir klar, ich hatte aber das Problem dass jeder print den ich früher eingebaut hatte, sofort zum crash führte.

    Über die channel.conf kann man aber sehen, in welcher Zeile er crashed, die Zeile nach den print Output.

  • Ich packe die channels.conf später mal auf meinen VDR und schaue mal wie ich den Fehler sauber behandle und vor allem was der VDR damit anstellt.


    Wenn ich den VDR anweise auf UTF-8 zu laufen dann sollte er ja auch die channels.conf in dem Format erwarten. Wird der Kanalname im VDR dann auch falsch dargestellt oder hat der VDR hier intern ein Fallback auf einen anderen Zeichensatz? Und wenn letzteres, dann frage ich mich ob man das in Python so hingebastelt bekommt das auch hier ein Fallback stattfindet und solche Zeichen dann doch halbwegs sinnvoll verarbeitet werden. Wenn es zum VDR zurück dann "reinrassiges" UTF-8 wird, dann ist mir das dann sogar recht.

  • Und wenn letzteres, dann frage ich mich ob man das in Python so hingebastelt bekommt das auch hier ein Fallback stattfindet und solche Zeichen dann doch halbwegs sinnvoll verarbeitet werden.

    Eine Möglichkeit wäre Rohdaten vom Socket zu lesen, sie an b'\n' aufzutrennen, zu zerlegen und die Zeilen bzw. die benötigten Felder selbst du dekodieren. Wenn das mit dem erwarteten Encoding nicht klappt, kann man einen Fallback auf ein 8-Bit Encoding wie ISO 8859-9 machen oder z.B. mit cchardet raten, welches Encoding es sein könnte (klappt besser wenn man mehr Text hat, was bei EPG-Einträgen eher der Fall ist als bei Kanalnamen):

    Code
    >>> import cchardet
    >>> s = b'F\xdcRTH - PAULI'
    >>> cchardet.detect(s)
    {'encoding': 'ISO-8859-1', 'confidence': 0.8053116798400879}
    >>> s.decode(cchardet.detect(s).get('encoding'))
    'FÜRTH - PAULI'

    yaVDR-Dokumentation (Ceterum censeo enchiridia esse lectitanda.)

  • Wenn es an einem tatsächlich existieren Zeichen in einem Kanalnamen liegen würde, dass müsste doch der Crash immer an der gleichen Stelle erfolgen, oder ?

    svdrpsend LSTC liefert doch die Kanäle immer in der Reihenfolge der channels.conf. Er crashed aber immer an einer anderen Stelle. Kann es sein, dass beim Lesen vom Socket Zeichen verfälscht oder ausgelassen werden ? Und warum immer so bei ca. 400 Kanälen ? Wenn das C++ wäre, würde ich vermuten, da läuft ein Buffer über.

  • Eine Möglichkeit wäre Rohdaten vom Socket zu lesen, sie an b'\n' aufzutrennen, zu zerlegen und die Zeilen bzw. die benötigten Felder selbst du dekodieren.

    Das wäre sicher eine Option. Allerdings nimmt einem der "dateiähnliche" Zugriff schon einiges an Arbeit ab. Ich weiß ja garnicht wie viele Bytes ich lesen will. Das einzige das ich weiß ist, dass es "zeilenweise" erfolgen soll. "Zeilenweise" kennt Python aber nur im "Text-Modus".


    Als mögliche Alternative ist mir der "surrogateescape" Fehler-Handler aufgefallen. Hier werden "fehlerhafte" Bytes in einen definierten Bereich geschoben. Wenn dieses Daten zum VDR zurückgesendet werden, dann wird der ursprüngliche Zustand wiederhergestellt. Kommt halt stark darauf an was der VDR treibt mit dem fehlerhaften Zeichen. Wenn es in der Kanalliste sauber ist, dann müsste man das wohl sauberer lösen.


    Edit: Eventuell ist das eine elegante Lösung: https://docs.python.org/3/libr…tml#codecs.register_error

    Eigenen "Fehler" registrieren und dort das problematische Byte im ISO-Zeichensatz decodieren.

  • Das wäre sicher eine Option. Allerdings nimmt einem der "dateiähnliche" Zugriff schon einiges an Arbeit ab. Ich weiß ja garnicht wie viele Bytes ich lesen will. Das einzige das ich weiß ist, dass es "zeilenweise" erfolgen soll. "Zeilenweise" kennt Python aber nur im "Text-Modus".

    Die Zeilen enden immer mit Zeilenumbrüchen - mal als Beispiel der SVDRP-Code, den ich in meinen Notizen hatte um cchardet ergänzt:


    Bei der Kanalliste auf meinem Test-VDR stolpert er da beim UTF-8 Decoding aktuell über zwei Sky-Sender, die er mit dem von cchardet bestimmten Encoding korrekt dekodieren kann:


    Als mögliche Alternative ist mir der "surrogateescape" Fehler-Handler aufgefallen. Hier werden "fehlerhafte" Bytes in einen definierten Bereich geschoben. Wenn dieses Daten zum VDR zurückgesendet werden, dann wird der ursprüngliche Zustand wiederhergestellt. Kommt halt stark darauf an was der VDR treibt mit dem fehlerhaften Zeichen. Wenn es in der Kanalliste sauber ist, dann müsste man das wohl sauberer lösen.

    Solange man sich nicht für einen String interessiert, der für den Nutzer gut aussieht, kann man das machen, ich würde das aber eher als ultima ratio sehen.

    yaVDR-Dokumentation (Ceterum censeo enchiridia esse lectitanda.)

    Einmal editiert, zuletzt von seahawk1986 ()

  • Ich habe mal die Minimalversion davon eingebaut:


    https://github.com/M-Reimer/py…ff2faa4f6c5069e4fff8ddb11


    Noch habe ich das aber nicht im Detail getestet. Mit meinem aktuellen Stand auf meinem VDR geht es, aber da ging auch die alte Version. Die problematische channels.conf spiele ich später am Abend mal ein.


    Genau solche Anpassungen brauche ich. Das macht am Ende eine gutes Modul für SVDRP aus. Mit sowas will ich mich in dem Programm, das die Library später nutzt, dann nämlich wirklich nicht rumschlagen müssen.


    Noch habe ich auf "telnetlib" verzichtet. Ich tue mir etwas schwer in SVDRP ein "Telnet" zu sehen. Für mich ist Telnet ein Protokoll von dem heute eigentlich jeder die Finger lassen sollte (weil SSH in so ziemlich jeder Hinsicht besser ist). Und mit "makefile" ging das auch problemlos. Man muss nur "binär öffnen". Zum Glück geht "readline()" trotzdem.


    Ich überlege noch ob ich das __enter__und __exit__ einbauen sollte. Das kannte ich noch nicht. Könnte aber durchaus von Vorteil sein.

  • Noch habe ich auf "telnetlib" verzichtet. Ich tue mir etwas schwer in SVDRP ein "Telnet" zu sehen.

    Solange da bidirektional Text in dem Muster Befehl -> Antwort über eine TCP-Verbindung ausgetauscht werden, passt das ganz gut und wird auch so im Kommentar zu Beginn in der svdrp.c erwähnt:

    Code: https://projects.vdr-developer.org/git/vdr.git/tree/svdrp.c#n7
     * The "Simple Video Disk Recorder Protocol" (SVDRP) was inspired
     * by the "Simple Mail Transfer Protocol" (SMTP) and is fully ASCII
     * text based. Therefore you can simply 'telnet' to your VDR port
     * and interact with the Video Disk Recorder - or write a full featured
     * graphical interface that sits on top of an SVDRP connection.

    Letztendlich ist die telnetlib auch nur eine Abstraktionsschicht, die auf dem socket- und dem selectors-Modul aufbaut und den Fokus mehr auf die interaktive Kommunikation als wie bei deinem Ansatz auf ein Datei-basiertes Interface legt, solange es funktioniert, dürfte den meisten die genaue Implementierung relativ egal sein :)

    yaVDR-Dokumentation (Ceterum censeo enchiridia esse lectitanda.)

  • Danke für die Rückmeldung. Also wieder ein Schritt weiter.


    Jetzt muss ich überlegen was ich zuerst mache. Ich werde aber wohl erstmal den Kanal-Editor etwas vorantreiben.


    Nächster Schritt für SVDRP wäre nun für mich EPG. Ich will da über einen Internet-Dienst das EPG der englischen Kanäle einspielen können.


    Bezüglich der Objekte zum Speichern der EPG-Daten will ich mich am VDR selbst orientieren. Der VDR kennt "Schedules", "Schedule" und "Event".


    Eine "Schedule" ist eine Liste von "Event"s

    Und "Schedules" ist eine Liste von "Schedule"s


    So in der Art könnte ich mir vorstellen das dann 1:1 nach Python zu bringen. Gibt dann vermutlich auch ein relativ elegantes Interface um selber so ein Format zu erzeugen und beim Wandeln nach String baut Python den ganzen Kram rekursiv in das Format das der VDR sehen will.



    Das Programm zum Löschen von OBSOLETEs erweitere ich später auch noch. Auf jeden Fall kommt da noch ein einfaches Makefile rein und ein systemd-Service und systemd-Timer. Wer will installiert sich das dann einfach und aktiviert den Timer um automatisch einmal am Tag die Liste aufzuräumen.

  • Erste Version steht auf PyPI:

    https://pypi.org/project/pysvdrp/


    Ist das erste Mal das ich bei PyPI hochlade. Hoffe es ist nicht allzuviel falsch gelaufen.


    In der Version die jetzt als "0.0.1" online steht ist EPG sagen wir zu 90% implementiert. Man kann damit direkt aus Python dem VDR neue EPG-Einträge zuspielen ohne sich mit den Eigenarten von SVDRP allzu sehr befassen zu müssen. An einem Beispiel arbeite ich noch. Der wesentliche Teil läuft aber auf diese wenigen Codezeilen hinaus:



    Das mit dem "try...except" gefällt mir noch nicht. "clear_epg" wird noch einen Boolean-Rückgabewert bekommen der "True" ist wenn EPG-Daten gelöscht wurden und "False" falls keine Daten da waren. "Fehlende Daten" ist meiner Meinung nach kein Grund für "clear_epg" eine Exception zu werfen.

Jetzt mitmachen!

Sie haben noch kein Benutzerkonto auf unserer Seite? Registrieren Sie sich kostenlos und nehmen Sie an unserer Community teil!