jb-alvarado
(Themenstarter)
Anmeldungsdatum: 28. November 2012
Beiträge: 345
|
Habe das Script, von der ursprünglich gewünschten Funktionsweise her, fast fertig. Was noch nicht geht, ist dass er korrekt zur neuen Playliste springt, habe da sicher noch einen dummen Denkfehler drin. Der letzte Clip aus der Playliste wird genau 5 mal wiederholt und dann verabschiedet sich das Script... Vielleicht kann man das ganze auch noch vereinfachen, aber erst mal will ich es überhaupt gescheit zum laufen bekommen. Hier ist der momentane Stand: ffplayout Was vielleicht noch in Zukunft interessant wäre, wäre eine "Live Injection"
|
Marc_BlackJack_Rintsch
Ehemalige
Anmeldungsdatum: 16. Juni 2006
Beiträge: 4694
Wohnort: Berlin
|
@jb-alvarado: Habe mal kurz drüber geschaut und 1½ Fehler gefunden. Einen hatte ich schon mal angemerkt: Wenn man die Standardausgabe und die Standardfehlerausgabe eines Prozesses ”piped”, dann muss man die auch beide (parallel) lesen, sonst kann es zu einem klassischen „deadlock“ kommen. Wenn man nur die Standardausgabe liest, kann es sein das der Prozess beim schreiben in die Standardfehlerausgabe hängen bleibt wenn der Puffer voll ist. Der Prozess wartet dann bis diese Daten ausgelesen werden bevor er weiter macht und wieder Daten in die Standardausgabe schreibt, und Dein Prozess wartet auf Daten von der Standardausgabe vom anderen Prozess — und dieser Zustand bleibt dann so. Beide warten bis zum St. Nimmerleins Tag. Vielleicht ist das ja die Ursache für Dein Problem. Der halbe Fehler ist der Vergleich von True mit is . In Python 3 ist das mittlerweile sicher, aber ich würde es trotzdem nicht machen. An der Stelle würde ich gar keinen Vergleich machen. Das elif erwartet einen Wahrheitswert. seek_in_clip ist bereits ein Wahrheitswert. Bei dem Vergleich kommt grundsätzlich immer wieder der gleiche Wert heraus den seek_in_clip sowieso schon hatte. Das ist als wenn man bei Rechnungen noch + 0 oder * 1 anhängen würde. Das ist normalerweise einfach nur überflüssig und verwirrt den Leser. Wenn der nämlich so einen Vergleich auf True liest, fragt er sich welche Werte ausser True und False die Variable noch annehmen kann. Solche Fälle gibt es ja auch, wenn man zwischen „Ja“, „Nein“, und „Weiss nicht“ unterscheiden möchte. Letzteres wird dann üblicherweise durch None repräsentiert. Ansonsten enthält das Skript einige unschöne Sachen (bitte nicht zu sehr davon erschlagen fühlen, ist alles nett und konstruktiv gemeint!): Einige Namen die sehr kryptisch sind. Beispiele: cur_ts , s_addr , s_pass , recip , l_y , l_m , l_d , c_p , clip_ls , clip_dur . Generell würde ich von Abkürzungen abraten die nicht mindestens in der Problemdomäne geläufig sind. Das geht höchstens bei Namen die nur eine sehr begrenzte Gültigkeit haben, zum Beispiel in „comprehensions“ oder Lambda-Ausdrücken. Was ist denn bei l_y , l_m , l_d gegenüber verständlichen Namen wie year , month , day gewonnen? Das das abgekürzt drei Buchstaben weniger sind kann es IMHO nicht sein. Namensgebung ist nicht nur für den Leser wichtig. Wobei man immer daran denken muss, das man als Programmierer vielleicht selbst in einem Jahr noch mal Leser ist, und sich dann mit den Namen zurecht finden muss, ohne das man das Programm noch komplett im Kopf hat. Aber auch beim Schreiben: Wenn man keinen vernünftigen Namen findet, der etwas präzise beschreibt, deutet das oft auf ein Problem hin. Zum Beispiel das man das zu lösende Problem an der Stelle noch nicht ganz durchdrungen hat, oder das die Klasse, die Funktion, oder der Wert irgendwie falsch ist. Das betrifft zum Beispiel die Funktion cur_ts() . Ich wage zu behaupten, dass man dort keinen ausgeschriebenen Namen finden kann, der präzise beschreibt was diese Funktion macht, und der nicht entweder schwammig oder sehr lang ist. Denn diese Funktion macht nicht eine Sache, sondern je nach dem Wert des ersten Arguments drei verschiedene Sachen. Und dieses erste Argument ist nur dazu da um aus den drei Wegen innerhalb der Funktion auszuwählen. Das sind also eigentlich drei Funktionen in Eine grepresst. Die Funktion geht auch ziemlich umständlich mit datetime um. Du erstellst ein datetime -Exemplar, formatierst daraus die Zeit in eine Zeichenkette, zerlegst die an : , und wandelst dann die Teilzeichenketten in int um. datetime -Exemplare haben diese Werte bereits als Attribute:
| In [7]: t = datetime.datetime.today()
In [8]: t.hour, t.minute, t.second
Out[8]: (14, 1, 0)
In [9]: t
Out[9]: datetime.datetime(2018, 1, 8, 14, 1, 0, 751142)
|
Das zweite Argument von cur_ts() sieht mir eher nach einem verkappten bool aus statt nach einer Zeichenkette. Das man da entweder 'today' oder irgend was anderes übergeben kann, ist irgendwie schräg. Da fragt man sich dann bei Aufrufen was das Argument '0' bewirkt, und die Antwort ist „nichts“ — man hätte da auch 'Honigkuchenpferd' , 'Ubuntu' , None , 42.23 oder sonst was übergeben können. Dann wäre da noch der Missbrauch von Klassen — in dem Programm steht keine einzige Klasse, das class -Schlüsselwort kommt aber fünf mal vor. Configparser hat nicht nur eine get() -Methode, sondern auch getint() , getboolean() , und getfloat() . Diese Grunddatentypen kann man also schon bei der Abfrage umwandeln. Wenn man sich das sparen möchte und auch so etwas wie literal_eval() braucht, ist es IMHO Zeit über JSON als Konfigurationsformat nach zu denken. Oder YAML wenn es auch etwas ausserhalb der Standardbibliothek sein darf.
Bei check_path() gibt es wieder so ein komisches Zeichenkettenargument das den Weg durch die Funktion bestimmt. Und welche von den Argumenten letztendlich verwendet werden. Auch würde niemand von einer Funktion die so heisst, erwarten, dass sie in bestimmten Fällen auch E-Mails verschickt. Das sieht nach einer falschen Aufteilung des Codes aus. Bei so etwas wie out_path = ['-i', in_file] frage ich mich auch ob out_path der richtige Name ist, denn die ffmpeg -Option -i und der Name in_file sagen irgendwie das Gegenteil aus‽ get_from_playlist() ist ziemlich umfangreich, die würde ich versuchen sinnvoll aufzuteilen. Die verwendet minidom — iiiiih, wie kommt man denn auf so was? 😉 Die ElementTree -API ist ”pythonischer”, also weniger ”javaesque”.
Die Schleife über i ist ”unpythonisch” man kann direkt über die Elemente iterieren, ohne den Umweg über einen Index. Wenn man den zusätzlich benötigt, nimmt man die enumerate() -Funktion. Ich würde das i aber in der Schleife gar nicht verwenden wollen. Du brauchst das ja nur um den letzten Clip identifizieren zu können. Das steht in der Schleife die auch die wohl Mehrzahl der nicht-letzten Clips verarbeitet. Ich würde die Schleife über alle Clips ausser dem letzten laufen lassen und den letzten dann nach der Schleife separat behandeln. Dann wird die Schleife einfacher. Und man muss sich auch explizit um den Fall kümmern, das die Liste lehr ist. Beim jetztigen Code kann ich so auf Anhieb nicht sagen ob der so okay ist, oder ob Du diesen Fall nicht berücksichtigt hast. Entfernst Du da tatsächlich ein 's' vom Ende einer Zeichenkette mit re.sub() ? Das ist der totale Overkill. Zum einen reguläre ausdrücke dafür zu verwenden, statt einfach die replace() -Methode auf der Zeichenkette, und zum anderen muss ja nicht die gesamte Zeichenkette durchsucht werden, wenn das s sowieso nur am Ende erwartet wird. Mit ElementTree und einer Schleife direkt über die Elemente könnte das einfach duration = clip_node.get('value').rstrip('s') sein. Das die Funktion drei Argumente übergeben bekommt, deren eventuell veränderten Werte sie auch als Rückgabewerte hat, riecht nach Zustand den man in eine Klasse stecken könnte/sollte. Dann könnte man sich die Playlist beispielsweise auch merken und nur neu laden wenn sich das Dateidatum geändert hat. Und aus der while True -Schleife in play_clips() könnte eine for -Schleife über die Clips werden, wie in meinen vorherigen Beiträgen ja schon geschrieben habe. Beziehungsweise in Deinem Code eine for -Schleife über die src_cmd -Werte. Und last_time , list_date , und seek_in_clip wären aus der play_clips() verschwunden. Wenn man dann noch das Objekt, welches die src_cmd -Werte liefert als Argument übergibt, hat man alles was nicht tatsächlich mit dem Abspielen von einer (unendlichen) Reihe von Clips zu tun hat, aus der Funktion raus, und die hat wirklich nur noch diese eine, unabhängige Funktion. Einige von den Kommentaren direkt vor Funktionen würden sich als Docstrings besser machen.
|
jb-alvarado
(Themenstarter)
Anmeldungsdatum: 28. November 2012
Beiträge: 345
|
Marc_BlackJack_Rintsch schrieb: @jb-alvarado: Habe mal kurz drüber geschaut und 1½ Fehler gefunden. Einen hatte ich schon mal angemerkt: Wenn man die Standardausgabe und die Standardfehlerausgabe eines Prozesses ”piped”, dann muss man die auch beide (parallel) lesen, sonst kann es zu einem klassischen „deadlock“ kommen. Wenn man nur die Standardausgabe liest, kann es sein das der Prozess beim schreiben in die Standardfehlerausgabe hängen bleibt wenn der Puffer voll ist. Der Prozess wartet dann bis diese Daten ausgelesen werden bevor er weiter macht und wieder Daten in die Standardausgabe schreibt, und Dein Prozess wartet auf Daten von der Standardausgabe vom anderen Prozess — und dieser Zustand bleibt dann so. Beide warten bis zum St. Nimmerleins Tag. Vielleicht ist das ja die Ursache für Dein Problem.
Ah ja, weiß gar nicht warum ich das noch drin hatte, habe in dem Zuge auch noch einen unnötigen stdout gefunden.
Der halbe Fehler ist der Vergleich von True mit is . In Python 3 ist das mittlerweile sicher, aber ich würde es trotzdem nicht machen. An der Stelle würde ich gar keinen Vergleich machen. Das elif erwartet einen Wahrheitswert. seek_in_clip ist bereits ein Wahrheitswert. Bei dem Vergleich kommt grundsätzlich immer wieder der gleiche Wert heraus den seek_in_clip sowieso schon hatte. Das ist als wenn man bei Rechnungen noch + 0 oder * 1 anhängen würde. Das ist normalerweise einfach nur überflüssig und verwirrt den Leser. Wenn der nämlich so einen Vergleich auf True liest, fragt er sich welche Werte ausser True und False die Variable noch annehmen kann. Solche Fälle gibt es ja auch, wenn man zwischen „Ja“, „Nein“, und „Weiss nicht“ unterscheiden möchte. Letzteres wird dann üblicherweise durch None repräsentiert.
Das muss ich mir mal generell angewöhnen, in php und Javascript ist es ja nicht anders.
Ansonsten enthält das Skript einige unschöne Sachen (bitte nicht zu sehr davon erschlagen fühlen, ist alles nett und konstruktiv gemeint!):
Ne das passt schon - bin ja froh, dass ich so eine super Erklärung bekommen, muss mir das nur mehrmals durchlesen, weil ich nicht gleich alles verstehe ☺.
Einige Namen die sehr kryptisch sind. Beispiele: cur_ts , s_addr , s_pass , recip , l_y , l_m , l_d , c_p , clip_ls , clip_dur . Generell würde ich von Abkürzungen abraten die nicht mindestens in der Problemdomäne geläufig sind. Das geht höchstens bei Namen die nur eine sehr begrenzte Gültigkeit haben, zum Beispiel in „comprehensions“ oder Lambda-Ausdrücken. Was ist denn bei l_y , l_m , l_d gegenüber verständlichen Namen wie year , month , day gewonnen? Das das abgekürzt drei Buchstaben weniger sind kann es IMHO nicht sein. Namensgebung ist nicht nur für den Leser wichtig. Wobei man immer daran denken muss, das man als Programmierer vielleicht selbst in einem Jahr noch mal Leser ist, und sich dann mit den Namen zurecht finden muss, ohne das man das Programm noch komplett im Kopf hat. Aber auch beim Schreiben: Wenn man keinen vernünftigen Namen findet, der etwas präzise beschreibt, deutet das oft auf ein Problem hin. Zum Beispiel das man das zu lösende Problem an der Stelle noch nicht ganz durchdrungen hat, oder das die Klasse, die Funktion, oder der Wert irgendwie falsch ist.
Versuche mich da schon dran zu halten, manchmal fällt mir nichts vernünftiges ein, dazu finde ich deine letzten beiden Sätze interessant - habe ich so noch nicht gesehen. Da ich auch gerade erst mit Python angefangen habe, verwende ich die IDE Erweiterung vom Atom Editor, und da bekomme ich jedes mal Warnungen wenn ich über 80 Zeichen in einer Linie erreicht habe. Klar kann ich ignorieren, aber da ich bei Shell öfters dazu geneigt war "Spagetticode" zu produzieren, wollte ich mich diesmal zusammenreißen und mich dran halten die Zeilen kurz zu fassen.
Das betrifft zum Beispiel die Funktion cur_ts() . Ich wage zu behaupten, dass man dort keinen ausgeschriebenen Namen finden kann, der präzise beschreibt was diese Funktion macht, und der nicht entweder schwammig oder sehr lang ist. Denn diese Funktion macht nicht eine Sache, sondern je nach dem Wert des ersten Arguments drei verschiedene Sachen. Und dieses erste Argument ist nur dazu da um aus den drei Wegen innerhalb der Funktion auszuwählen. Das sind also eigentlich drei Funktionen in Eine grepresst.
Du würdest also generell nur eine Sache in eine Funktion packen? Habe sie jetzt in zwei aufgeteilt und die sind dank deinem Hinweis trotzdem kürzer ☺.
Die Funktion geht auch ziemlich umständlich mit datetime um. Du erstellst ein datetime -Exemplar, formatierst daraus die Zeit in eine Zeichenkette, zerlegst die an : , und wandelst dann die Teilzeichenketten in int um. datetime -Exemplare haben diese Werte bereits als Attribute:
| In [7]: t = datetime.datetime.today()
In [8]: t.hour, t.minute, t.second
Out[8]: (14, 1, 0)
In [9]: t
Out[9]: datetime.datetime(2018, 1, 8, 14, 1, 0, 751142)
|
Das zweite Argument von cur_ts() sieht mir eher nach einem verkappten bool aus statt nach einer Zeichenkette. Das man da entweder 'today' oder irgend was anderes übergeben kann, ist irgendwie schräg. Da fragt man sich dann bei Aufrufen was das Argument '0' bewirkt, und die Antwort ist „nichts“ — man hätte da auch
'Honigkuchenpferd' , 'Ubuntu' , None , 42.23 oder sonst was übergeben können.
Stimmt - arbeite hier nur noch mit True und False.
Dann wäre da noch der Missbrauch von Klassen — in dem Programm steht keine einzige Klasse, das class -Schlüsselwort kommt aber fünf mal vor.
Das verstehe ich noch nicht ganze. Eine einfache Klasse tut doch Werte die zusammen gehören zusammenfassen. Wird der Inhalt der Klasse eigentlich jedes mal neu evaluiert, wenn sie aufgerufen wird?
Configparser hat nicht nur eine get() -Methode, sondern auch getint() , getboolean() , und getfloat() . Diese Grunddatentypen kann man also schon bei der Abfrage umwandeln. Wenn man sich das sparen möchte und auch so etwas wie literal_eval() braucht, ist es IMHO Zeit über JSON als Konfigurationsformat nach zu denken. Oder YAML wenn es auch etwas ausserhalb der Standardbibliothek sein darf.
Habe die Doc dazu nicht gründlich genug gelesen... JSON hatte ich auch überlegt, aber als Config-Format finde ich das irgendwie nicht so schön zu lesen und editieren. Da gefällt mir der "INI-Ansatz" besser. Bei check_path() gibt es wieder so ein komisches Zeichenkettenargument das den Weg durch die Funktion bestimmt. Und welche von den Argumenten letztendlich verwendet werden. Auch würde niemand von einer Funktion die so heisst, erwarten, dass sie in bestimmten Fällen auch E-Mails verschickt. Das sieht nach einer falschen Aufteilung des Codes aus. Bei so etwas wie out_path = ['-i', in_file] frage ich mich auch ob out_path der richtige Name ist, denn die ffmpeg -Option -i und der Name in_file sagen irgendwie das Gegenteil aus‽
Das mit der Benennung leuchtet ein, das "Zeichenkettenargument" verstehe ich noch nicht ganz. Je nach Anforderung braucht ja die gleiche Variable einen andere Wert, den muss ich ja irgendwie festlegen.
get_from_playlist() ist ziemlich umfangreich, die würde ich versuchen sinnvoll aufzuteilen. Die verwendet minidom — iiiiih, wie kommt man denn auf so was? 😉 Die ElementTree -API ist ”pythonischer”, also weniger ”javaesque”.
minidom erscheint recht früh bei Google, wenn man nach python xml parser sucht ☺. Habe es mal mit ElementTree versucht, aber da bin ich noch nicht auf einem grünen Zweig gelandet, um das letzte Element zu bestimmen. Denn das funktioniert nicht: | xml_path = "/playlists/2018/01/2018-01-07.xml"
clip_tree = ET.parse(xml_path).iter('video')
for clip_node in clip_tree:
if clip_node == clip_tree[-1]:
print(clip_node.attrib.get('src'))
|
Die Schleife über i ist ”unpythonisch” man kann direkt über die Elemente iterieren, ohne den Umweg über einen Index. Wenn man den zusätzlich benötigt, nimmt man die enumerate() -Funktion. Ich würde das i aber in der Schleife gar nicht verwenden wollen. Du brauchst das ja nur um den letzten Clip identifizieren zu können. Das steht in der Schleife die auch die wohl Mehrzahl der nicht-letzten Clips verarbeitet. Ich würde die Schleife über alle Clips ausser dem letzten laufen lassen und den letzten dann nach der Schleife separat behandeln. Dann wird die Schleife einfacher. Und man muss sich auch explizit um den Fall kümmern, das die Liste lehr ist. Beim jetztigen Code kann ich so auf Anhieb nicht sagen ob der so okay ist, oder ob Du diesen Fall nicht berücksichtigt hast.
Das scheint mir etwas schwierig zu sein, ich weiß doch erst wann der letzte Clip erreicht ist, wenn er wirklich dran ist, wie soll man ihn dann in einem separaten Durchganz behandeln. Auch weiß ich noch nicht, wie man die Schleifen bauen würde, die zwar alle Clips behandelt, aber den letzten auslässt.
Entfernst Du da tatsächlich ein 's' vom Ende einer Zeichenkette mit re.sub() ? Das ist der totale Overkill. Zum einen reguläre ausdrücke dafür zu verwenden, statt einfach die replace() -Methode auf der Zeichenkette, und zum anderen muss ja nicht die gesamte Zeichenkette durchsucht werden, wenn das s sowieso nur am Ende erwartet wird. Mit ElementTree und einer Schleife direkt über die Elemente könnte das einfach duration = clip_node.get('value').rstrip('s') sein.
Ja ☺, das war Bequemlichkeit...
Das die Funktion drei Argumente übergeben bekommt, deren eventuell veränderten Werte sie auch als Rückgabewerte hat, riecht nach Zustand den man in eine Klasse stecken könnte/sollte. Dann könnte man sich die Playlist beispielsweise auch merken und nur neu laden wenn sich das Dateidatum geändert hat. Und aus der while True -Schleife in play_clips() könnte eine for -Schleife über die Clips werden, wie in meinen vorherigen Beiträgen ja schon geschrieben habe. Beziehungsweise in Deinem Code eine for -Schleife über die src_cmd -Werte. Und last_time , list_date , und seek_in_clip wären aus der play_clips() verschwunden. Wenn man dann noch das Objekt, welches die src_cmd -Werte liefert als Argument übergibt, hat man alles was nicht tatsächlich mit dem Abspielen von einer (unendlichen) Reihe von Clips zu tun hat, aus der Funktion raus, und die hat wirklich nur noch diese eine, unabhängige Funktion.
Ja sowas hast du vorher schon erwähnt, habe das nicht einfach ignoriert, sondern nicht nachvollziehen können wie das von Statten gehen soll. Wenn die Cliplisten variable ist und sich jeder Zeit und jeden Tag neu aufbauen soll, wie kann ich daraus eine Endlos Schleife bauen. Dein Ansatz wäre ja genau anders herum wie ich ihn jetzt habe.
Mir gefällt der Ansatz mit der Klasse, da muss ich mich mehr mit befassen. Auch finde ich die Idee gut, die XML nur zu refreshen wenn sie verändert wurde, vielleicht könnte man da auch mit inotify arbeiten, müsste ich mir auch mal anschauen.
Einige von den Kommentaren direkt vor Funktionen würden sich als Docstrings besser machen.
Im Code Editor waren mir die Kommentare per Docstring zu prominent, daher wollte ich die lieber lassen. Edit:
Ok, habe das doch noch mit ElementTree hinbekommen: | xml_path = "/playlists/2018/01/2018-01-07.xml"
clip_tree = list(ET.parse(xml_path).iter('video'))
for clip_node in clip_tree:
if clip_node == clip_tree[-1]:
print(clip_node.attrib.get('src'))
|
|
Marc_BlackJack_Rintsch
Ehemalige
Anmeldungsdatum: 16. Juni 2006
Beiträge: 4694
Wohnort: Berlin
|
@jb-alvarado: Ja, ich würde nur eine Sache in eine Funktion packen. Das ist allgemein üblich das eine Funktion genau eine Sache macht. Die man dann zum Beispiel isoliert testen (manuell und automatisiert) und debuggen kann. Wenn man da mehr rein packt und zum Beispiel über ein Argument entscheidet was sie denn nun tatsächlich machen soll, dann hat man eigentlich unabhängige Ablaufpfade in der Funktion, die sich aber vielleicht doch gegenseitig beeinflussen können wenn davor, dazwischen, oder am Ende vielleicht noch gemeinsamer Code steht. Ausserdem ist es unübersichtlicher, weil die Funktion mehr Namen enthält als für eine der Einzelaufgaben gebraucht werden, und man muss erst einmal herausfinden welche Namen für welche Aufgabe(n) verwendet werden. Und was man bei Deinem Code dann auch sieht: es werden nicht für alle Wege auch immer alle Argumente benötigt. Man sieht aber ohne den ganzen Code der Funktion zu lesen aber nicht welche Argumente für welche Aufgabe benötigt werden und welche nicht. Das zweite Argument von cur_ts() wird ja nur für eine der drei Aufgaben benötigt, man muss es aber trotzdem angeben, auch wenn man das gar nicht braucht. Klassen fassen Werte *und* dazugehörige Funktionen zusammen. Wenn eine Klasse keine Methoden hat und niemals Exemplare davon erzeugt werden, dann ist es keine Klasse. Du missbrauchst die einfach als Namensräume. Dafür wäre types.SimpleNamespace vorgesehen, oder wenn die Werte nicht verändert werden, könnte man sich auch Typen mit collections.namedtuple() erstellen. Der Inhalt der Klasse wird nur einmal ausgewertet/festgelegt, nämlich dann wenn die class -Anweisung ausgeführt wird. Ich verstehe check_path() nicht wirklich. Das ist IMHO ziemlich wirr. Teilweise auch weil man den kompletten Code lesen muss, also nicht nur den der Funktion, sondern auch alle Aufrufstellen, um überhaupt mal einen Überblick über die möglichen Werte für das f_o_l -Argument zu bekommen. Was auch wieder bedeutet, das es mindestens einen Wert geben muss der total willkürlich gewählt werden kann, ohne das die Funktion da irgendwie anders reagieren würde. Die Funktion macht zu viel, oder das was sie macht, und das was andere Funktionen machen, ist falsch auf diese Funktionen aufgeteilt. Du hast bei dem ElementTree -Versuch einen Iterator erstellt. Die Elemente werden ”lazy” geliefert, also das nächste Element immer erst wenn man danach fragt. Also kann man auch nicht direkt auf das letzte Element zugreifen. Mit findall() bekommt man eine Sequenz, wo man dann auch direkt auf das letzte Element mit -1 als Index zugreifen kann. clip_tree ist als Name irreführend, denn das ist ja keine Baumstruktur sondern ein Iterator bzw. mit findall() eine Liste.
Die get() -Methode sollte man direkt auf clip_node() aufrufen. attrib sollte man nur verwenden wenn man tatsächlich alle Attribute als Wörterbuch haben möchte. Der letzte Clip ist erreicht wenn die Schleife davor alle bis auf den letzten Clip durchgegangen ist. Alles ausser das letzte Element einer Liste bekommt man durch „slicing“:
| for clip_node in clip_nodes[:-1]:
# Do something with `clip_node` and `break` if the right clip
# is found.
else:
# Do something with the last clip (`clip_nodes[-1]`) if no
# other clip was found. Here you also can/must handle the
# case of an empty `clip_nodes`.
|
Die Endlosschleife for -Schleife bekommst Du durch ein iterierbares Objekt das genau das macht was Du jetzt in der Funktion machst, aber eben ausserhalb. Also zum Beispiel über eine Generatorfunktion (Stichwort yield ) oder einen Iterator (Stichwort __next__ -Methode).
|
jb-alvarado
(Themenstarter)
Anmeldungsdatum: 28. November 2012
Beiträge: 345
|
Habe nun fast alle Tips von dir versucht um zusetzten. Auch habe ich die Funktion check_path() durch mehrere kleine Funktionen ersetzt. Das einzige was mir daran jetzt nicht gefällt ist, dass ich mit mehr if/else in der get_from_playlist() Funktion arbeiten muss. Auch fehlt momentan eine XML Validierung, werde vielleicht erst mal darauf verzichten, bis jetzt bin ich noch ohne externe Module klargekommen. Mein Arbeitskollege kommt aus der Informatik-Ecke und hat mir das mit dem Iterator versucht zu erklären. Wenn ich das richtig verstanden habe handelt es sich um einen Zeiger, der auf Objekte im Speicher zeigt. Die Objekte im Speicher selbst zeigen jeweils auf das nächste Objekt. Jetzt stellt sich für mich nur die Frage, wie bekomme ich diese Objekte, was in meinem Fall die XML Dateien sein müsste, in den Speicher und lasse sie immer auf das nächste zeigen. Ist vielleicht leichter als ich denke, aber stehe da irgendwie auf dem Schlauch... Muss mir noch mehr Beispiele anschauen. Edit: Ich habe jetzt den dummen Fehler gefunden warum der Stream jeden Tag um 6:25 abbricht... Auf dem Produktivsystem habe ich Ubuntu laufen mit dem SRS rtmp streaming server (Version 3). Auf dem Testsystem läuft Debian mit SRS Version 2, Zwischen Version 2 und 3 hat sich wohl der kill SIGNAL Befehl geändert für das Logrotate. Somit verabschiedet sich der Streaming Server jeden Morgen um 6:25 wenn ein Logrotate stattfindet... Ich dachte der Server würde crashen weil mein Script spinnt und den Server lahm legt, dabei ist es gerade umgekehrt.
|
Marc_BlackJack_Rintsch
Ehemalige
Anmeldungsdatum: 16. Juni 2006
Beiträge: 4694
Wohnort: Berlin
|
@jb-alvarado: Ein Iterator ist deutlich abstrakter. Was Du da beschreibst ist ein Iterator über eine verkettete Liste in einer Sprache die Zeiger als Datentyp hat. Das hat mit Python jetzt nicht viel zu tun, und auch andere Sprachen, zum Beispiel C++, wo es den Datentyp Zeiger gibt und wo man einen Iterator, wie von Dir beschrieben, zu einer verketteten Liste implementieren könnte (gibt's dort schon in der Standardbibliothek), sind Iteratoren generell abstrakter. Ein Iterator im einfachsten Fall ist ein Objekt von dem man ”das nächste Element” abfragen kann, und das es erlaubt festzustellen wann das Ende erreicht ist. Wobei es kein Ende geben muss! In Java hat die Iterator -Schnittstelle dafür zwei Methoden: hasNext() um zu prüfen ob man Ende angekommen ist, und next() um das nächste Element zu bekommen. In Python ist das zu einer Methode zusammengefasst: __next__() . Falls es keine Elemente mehr gibt, löst die Methode eine StopIteration -Ausnahme aus, damit man das Ende erkennen kann. Was „das nächste Element“ ist, und wie das zustande kommt, kann der Programmierer völlig frei definieren. Eben alles was in so eine Methode reinprogrammieren kann. Also beispielsweise auch eine Playlist auswerten, und das auch ohne das man dabei jemals ein ”Ende” erreicht. In Python ruft man die __next__() -Methode über die next() -Funktion auf; das aber eher selten, denn meistens möchte man in einer Schleife über die Elemente gehen, also eine for -Schleife verwenden. Denn nach dem in in for … in … muss ein iterierbares Objekt stehen, also eines von dem man mit iter() einen Iterator abfragen kann, der dann die __next__() -Methode hat. Iteratoren liefern bei iter() per Konvention sich selbst zurück, also sind Iteratoren selbst auch iterierbar. Was for in Python hinter den Kulissen macht ist das hier:
1
2
3
4
5
6
7
8
9
10
11
12 | for item in iterable:
# do something with `item`
# does this:
$iterator = iter(iterable)
while True:
try:
item = next($iterator)
except StopIteration:
break
# do something with `item`
|
Das mit dem Dollarzeichen in $iterator ist natürlich kein gültiger Name in Python. Das habe ich gemacht um zu verdeutlichen dass das ein lokaler Name ist, der nur in der Implementierung von for steckt und an den man von aussen nicht heran kommt. Und das passiert grundsätzlich immer bei for in Python, also auch bei jeder for -Schleife die Du bereits geschrieben hast. Es wird von dem Objekt hinter dem in immer ein Iterator abgefragt und dann für jeden Schleifendurchlauf mit next() das nächste Element geholt, bis dabei eine StopIteration auslöst wird statt ein weiteres Element zu liefern. Beispiel:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 | In [1]: xs = [42, 23, 4711]
In [2]: for x in xs:
...: print(x)
...:
42
23
4711
In [3]: it = iter(xs)
In [4]: next(it)
Out[4]: 42
In [5]: next(it)
Out[5]: 23
In [6]: next(it)
Out[6]: 4711
In [7]: next(it)
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-7-2cdb14c0d4d6> in <module>()
----> 1 next(it)
StopIteration:
In [8]: it
Out[8]: <listiterator at 0xa04c18c>
|
Bei Listen liefert der Iterator den man bekommt jeweils das nächste Element. Bei Dateien, bekommt man mit iter() einfach das Dateiobjekt selbst wieder zurück, denn Dateiobjekte sind selbst Iteratoren, und jeder Aufruf liefert die nächste Zeile aus der Datei:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 | In [9]: f = open('test.txt')
In [10]: it = iter(f)
In [11]: it
Out[11]: <open file 'test.txt', mode 'r' at 0xa4c3f98>
In [12]: next(it)
Out[12]: 'spam\n'
In [13]: next(it)
Out[13]: 'parrot\n'
In [14]: next(it)
Out[14]: 'cheeeese\n'
In [15]: next(it)
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-15-2cdb14c0d4d6> in <module>()
----> 1 next(it)
StopIteration:
|
Wirklich Klassen mit einer __next__() -Methode zu schreiben macht man allerdings nicht so oft seit es in Python Generatorfunktionen gibt. Solange man nicht während des Iterierens noch andere Sachen mit dem Objekt anstellen oder abfragen möchte, ist eine Generatorfunktion einfacher und mit weniger Syntax geschrieben.
|
jb-alvarado
(Themenstarter)
Anmeldungsdatum: 28. November 2012
Beiträge: 345
|
Ok, ich glaube ich fange an zu verstehen. Wobei es doch bei einem Iterator sein darf, dass wenn man durch eine Datei iteriert, diese sich weiter verändern darf. Habe das mal so versucht nach zubauen, nach dem Beispiel: | import xml.etree.cElementTree as ET
import time
xml_path = "2018-01-07.xml"
for event, entry in ET.iterparse(xml_path):
if entry.tag == "video":
print(entry.get("src"))
time.sleep(3)
entry.clear()
|
Wenn der Befehl läuft ist die Datei nicht mehr veränderbar. Hier ist was Ähnliches mit yield, da würde das funktionieren, aber das funktioniert nicht mit einem XML Parser. Auch fehlt mir noch der Ansatz wie ich xml_path austauschen sollen, wenn das Ende erreicht ist. Vielleicht so?: 1
2
3
4
5
6
7
8
9
10
11
12
13 | import xml.etree.cElementTree as ET
import time
xml_path = "2018-01-07.xml"
for event, entry in ET.iterparse(xml_path, events=("start", "end")):
if entry.tag == "video":
print(entry.get("src"))
time.sleep(3)
entry.clear()
if event == "end" and entry.tag == "smil":
xml_path = "2018-01-08.xml"
|
|
Marc_BlackJack_Rintsch
Ehemalige
Anmeldungsdatum: 16. Juni 2006
Beiträge: 4694
Wohnort: Berlin
|
@jb-alvarado: Ich verstehe gerade nicht was Du da versuchst/was das Ziel davon sein soll‽ Du hast doch schon funktionierenden Code der halt nur noch in der Schleife wo er jetzt steht, herausgezogen werden muss. Wobei ich gerade sehe das im aktuellen Stand neben src_cmd auch last_time in play_clips() verwendet wird und das get_from_playlist() potentiell zweimal aufgerufen wird. Und src_cmd für zwei verschiedene Dinge verwendet wird: Liste von Optionen für ffmpeg oder die Zeichenkette 'next' — Du hast echt einen Hang dazu Zeichenketten irgendwelche Bedeutungen zuzuordnen. Das ist echt keine gute Idee. Ein Rückgabewert sollte immer vom gleichen (Duck-)Typ sein und die gleiche Bedeutung haben. Sonst wird es unübersichtlich. Wobei None so eine Art ”Wildcard”-Duck-Typ ist, der ähnlich wie null /nil /… in anderen Programmiersprachen für ”Nichts” von jedem Typ stehen kann. Das könnte man hier für 'next' auch ganz gut verwenden: Es wird entweder eine Liste mit Optionen für einen Clip geliefert, oder None für keinen Clip. Wobei man das auch schon in der Funktion davor erledigen können sollte, damit get_from_playlist() immer einen Clip liefert. Warum soll sich der Aufrufer der Funktion mit diesem Problem herumschlagen müssen‽ Ich sehe vier Fehler im bisherigen Code: Wenn die XML-Datei kein einziges Video enthält, dann führt das in der get_from_playlist() zu einem IndexError in der Behandlung des letzten Tags (was es ja nicht gibt). Wenn es zwei XML-Dateien nacheinander gibt, die nur einen (letzten) Clip enthalten, kann es auch vorkommen das zweimal next hintereinander von get_from_playlist() kommt. Damit kann der Code nicht umgehen. Selbst wenn Du jetzt sagst der Fall kann in der Praxis nicht vorkommen, würde ich das trotzdem entsprechend programmieren, denn das sind Aussagen die von der Praxis dann doch manchmal widerlegt werden. Und zwar immer genau dann, wenn man es am wenigsten gebrauchen kann. 😉 Bei der Behandlung des letzten Clips wird list_date neu zugewiesen, aber dieser Wert wird dann nie irgendwo verwendet‽ check_file_exist() wird einmal ohne Argument aufgerufen.
Was zumindest bedenklich ist, falls nicht sogar zu Problemen führen kann, ist innerhalb eines Bedingungsausdrucks mehrfach die aktuelle Zeit abzufragen und dann noch mal in den Zweigen. Dir ist klar das die Zeit ja weiter läuft und nicht alle Aufrufe den gleichen Wert liefern? Komplett ungetestet und da ist noch eine Zeile mit einem BUG-Kommentar versehen den ich im Text oben noch nicht erwähnt habe:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92 | # read values from xml playlist
def iter_src_commands():
last_time = get_time('full_sec')
if 0 <= last_time < _playlist.start * 3600:
last_time += 86400
list_date = get_date(True)
seek = True
while True:
year, month, _day = re.split('-', list_date)
xml_path = os.path.join(_playlist.path, year, month, list_date + '.xml')
if check_file_exist(xml_path):
xml_root = ET.parse(xml_path).getroot()
clip_nodes = xml_root.findall('body/video')
# all clips in playlist except last one
for clip_node in clip_nodes[:-1]:
clip_path = clip_node.get('src')
clip_start = float(clip_node.get('clipBegin').rstrip('s'))
clip_len = float(clip_node.get('dur').rstrip('s'))
if seek:
# first time we end up here
if last_time < clip_start + clip_len:
# calculate seek time
seek_t = last_time - clip_start
if check_file_exist(clip_path):
src_cmd = seek_in_clip(clip_path, seek_t)
else:
src_cmd = gen_dummy(clip_len - seek_t)
seek = False
break
else:
if last_time < clip_start:
if check_file_exist(clip_path):
src_cmd = ['-i', clip_path]
else:
src_cmd = gen_dummy(clip_len)
break
# last clip in playlist
else:
clip_start = float(_playlist.start * 3600 - 5)
src_cmd = prepare_last_clip(clip_nodes[-1], clip_start)
list_date = get_date(False) # BUG Never used!
last_time = clip_start
list_date = get_date(True)
else:
src_cmd = gen_dummy(300)
last_time += 300
if src_cmd is not None:
yield src_cmd, last_time
# independent thread for clip preparation
def play_clips(out_file, src_commands):
# infinit loop
# send current file from xml playlist to stdin from buffer
for src_cmd, last_time in src_commands:
if last_time > 86400:
tm_str = str(timedelta(seconds=int(last_time - 86400)))
else:
tm_str = str(timedelta(seconds=int(last_time)))
logger.info('play at "{}": {}'.format(tm_str, src_cmd))
try:
filePiper = Popen(
[
'ffmpeg', '-v', 'error', '-hide_banner', '-nostats'
] + src_cmd +
[
'-s', '{}x{}'.format(_pre_comp.w, _pre_comp.h),
'-aspect', str(_pre_comp.aspect),
'-pix_fmt', 'yuv420p', '-r', str(_pre_comp.fps),
'-c:v', 'mpeg2video', '-g', '12', '-bf', '2',
'-b:v', '{}k'.format(_pre_comp.v_bitrate),
'-minrate', '{}k'.format(_pre_comp.v_bitrate),
'-maxrate', '{}k'.format(_pre_comp.v_bitrate),
'-bufsize', '{}k'.format(_pre_comp.v_bufsize),
'-c:a', 'mp2', '-b:a', '{}k'.format(_pre_comp.a_bitrate),
'-ar', str(_pre_comp.a_sample), '-ac', '2', '-f', 'mpegts',
'-threads', '2', '-'
],
stdout=PIPE
)
copyfileobj(filePiper.stdout, out_file)
finally:
filePiper.wait()
|
Die iter_src_commands() (und damit auch frühere get_from_playlist() ) ist mir ja viel zu lang und kompliziert.
|
jb-alvarado
(Themenstarter)
Anmeldungsdatum: 28. November 2012
Beiträge: 345
|
Marc_BlackJack_Rintsch schrieb: @jb-alvarado: Ich verstehe gerade nicht was Du da versuchst/was das Ziel davon sein soll‽ Du hast doch schon funktionierenden Code der halt nur noch in der Schleife wo er jetzt steht, herausgezogen werden muss.
Ich hatte versucht die Reihenfolge umzubauen, damit ich nicht jedes mal in der Playliste erst mal suchen muss wo ich überhaupt bin. Daher dachte ich gehen lieber die Playliste selbst durch, und für jeden Treffer rufe ich dann den Playbefehl auf. Dann müsste ich nämlich nur einmal am Anfangen suchen wo es losgeht und danach wird einfach der Reihe nach abgespielt. Aber in deinem Beispiel würde das ja mit yield auch geschehen. Wobei ich gerade sehe das im aktuellen Stand neben src_cmd auch last_time in play_clips() verwendet wird und das get_from_playlist() potentiell zweimal aufgerufen wird. Und src_cmd für zwei verschiedene Dinge verwendet wird: Liste von Optionen für ffmpeg oder die Zeichenkette 'next' — Du hast echt einen Hang dazu Zeichenketten irgendwelche Bedeutungen zuzuordnen. Das ist echt keine gute Idee. Ein Rückgabewert sollte immer vom gleichen (Duck-)Typ sein und die gleiche Bedeutung haben. Sonst wird es unübersichtlich. Wobei None so eine Art ”Wildcard”-Duck-Typ ist, der ähnlich wie null /nil /… in anderen Programmiersprachen für ”Nichts” von jedem Typ stehen kann. Das könnte man hier für 'next' auch ganz gut verwenden: Es wird entweder eine Liste mit Optionen für einen Clip geliefert, oder None für keinen Clip. Wobei man das auch schon in der Funktion davor erledigen können sollte, damit get_from_playlist() immer einen Clip liefert. Warum soll sich der Aufrufer der Funktion mit diesem Problem herumschlagen müssen‽
Ja das mit dem zweimal aufrufen von get_from_playlist() ist ungünstig. Ich würde da vielleicht einiges noch mal umbauen, was auch zur Folge hätte dass get_from_playlist() oder dein iter_src_commands() um einiges schlanker werden würde. Die ganze Geschichten mit if seek sollte ich auslagern. Hier geht es nur um das anspielen des ersten Clips beim ersten Start des Scripts, das muss nicht im gleichen Loop sein.
Ich sehe vier Fehler im bisherigen Code: Wenn die XML-Datei kein einziges Video enthält, dann führt das in der get_from_playlist() zu einem IndexError in der Behandlung des letzten Tags (was es ja nicht gibt). Wenn es zwei XML-Dateien nacheinander gibt, die nur einen (letzten) Clip enthalten, kann es auch vorkommen das zweimal next hintereinander von get_from_playlist() kommt. Damit kann der Code nicht umgehen. Selbst wenn Du jetzt sagst der Fall kann in der Praxis nicht vorkommen, würde ich das trotzdem entsprechend programmieren, denn das sind Aussagen die von der Praxis dann doch manchmal widerlegt werden. Und zwar immer genau dann, wenn man es am wenigsten gebrauchen kann. 😉
Ja das mit den Fehlern, zu der Zeit wenn man sie am wenigsten braucht, kenne ich gut, das möchte ich schon berücksichtigen. Bei der Behandlung des letzten Clips wird list_date neu zugewiesen, aber dieser Wert wird dann nie irgendwo verwendet‽
Ist ein Überbleibsel von der vorherigen Version und muss in der Tat weg. Bzw. möchte ich hier noch mal einen Funktionsaufruf einbauen. Beim analysieren der letzten abgespielten Stunden ist mir aufgefallen, dass ich recht gut errechnen kann, wie viel Sekunden Vorlauf ich im Buffer habe, daraus kann ich mir errechnen wie weit ich von der tatsächlichen Zeit entfernt bin. Falls dann nach einigen Tagen der Zeitversatz größer würde, könnte ich das korrigieren.
Was zumindest bedenklich ist, falls nicht sogar zu Problemen führen kann, ist innerhalb eines Bedingungsausdrucks mehrfach die aktuelle Zeit abzufragen und dann noch mal in den Zweigen. Dir ist klar das die Zeit ja weiter läuft und nicht alle Aufrufe den gleichen Wert liefern?
Das ist eigentlich schon so gewollt, kann ich aber noch mal kontrollieren. Komplett ungetestet und da ist noch eine Zeile mit einem BUG-Kommentar versehen den ich im Text oben noch nicht erwähnt habe:
Ja vermute, dass da was nicht richtig gehen wird, oder ich verstehe das falsch:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92 | # read values from xml playlist
def iter_src_commands():
last_time = get_time('full_sec')
if 0 <= last_time < _playlist.start * 3600:
last_time += 86400
list_date = get_date(True)
seek = True
while True:
year, month, _day = re.split('-', list_date)
xml_path = os.path.join(_playlist.path, year, month, list_date + '.xml')
if check_file_exist(xml_path):
xml_root = ET.parse(xml_path).getroot()
clip_nodes = xml_root.findall('body/video')
# all clips in playlist except last one
for clip_node in clip_nodes[:-1]:
clip_path = clip_node.get('src')
clip_start = float(clip_node.get('clipBegin').rstrip('s'))
clip_len = float(clip_node.get('dur').rstrip('s'))
if seek:
# first time we end up here
if last_time < clip_start + clip_len:
# calculate seek time
seek_t = last_time - clip_start
if check_file_exist(clip_path):
src_cmd = seek_in_clip(clip_path, seek_t)
else:
src_cmd = gen_dummy(clip_len - seek_t)
seek = False
break
else:
if last_time < clip_start:
if check_file_exist(clip_path):
src_cmd = ['-i', clip_path]
else:
src_cmd = gen_dummy(clip_len)
break
# last clip in playlist
else:
clip_start = float(_playlist.start * 3600 - 5)
src_cmd = prepare_last_clip(clip_nodes[-1], clip_start)
list_date = get_date(False) # BUG Never used!
last_time = clip_start
list_date = get_date(True)
else:
src_cmd = gen_dummy(300)
last_time += 300
if src_cmd is not None:
yield src_cmd, last_time
# independent thread for clip preparation
def play_clips(out_file, src_commands):
# infinit loop
# send current file from xml playlist to stdin from buffer
for src_cmd, last_time in src_commands:
if last_time > 86400:
tm_str = str(timedelta(seconds=int(last_time - 86400)))
else:
tm_str = str(timedelta(seconds=int(last_time)))
logger.info('play at "{}": {}'.format(tm_str, src_cmd))
try:
filePiper = Popen(
[
'ffmpeg', '-v', 'error', '-hide_banner', '-nostats'
] + src_cmd +
[
'-s', '{}x{}'.format(_pre_comp.w, _pre_comp.h),
'-aspect', str(_pre_comp.aspect),
'-pix_fmt', 'yuv420p', '-r', str(_pre_comp.fps),
'-c:v', 'mpeg2video', '-g', '12', '-bf', '2',
'-b:v', '{}k'.format(_pre_comp.v_bitrate),
'-minrate', '{}k'.format(_pre_comp.v_bitrate),
'-maxrate', '{}k'.format(_pre_comp.v_bitrate),
'-bufsize', '{}k'.format(_pre_comp.v_bufsize),
'-c:a', 'mp2', '-b:a', '{}k'.format(_pre_comp.a_bitrate),
'-ar', str(_pre_comp.a_sample), '-ac', '2', '-f', 'mpegts',
'-threads', '2', '-'
],
stdout=PIPE
)
copyfileobj(filePiper.stdout, out_file)
finally:
filePiper.wait()
|
Wie bekommt hier while True in iter_src_commands() den neuen Pfad zur nächsten XML, wenn die eine am Ende ist. Der Pfad wird ja nur einmal am Anfang definiert. Der yield Befehlt scheint mir nur den letzten Clip in der Liste auszugeben. Auch war ich nicht in der Lage, nach Start des Scripts die XML Datei noch zu verändern. Da wurden nur die alten Einträge genommen. Habe das so versucht auf die Schnelle zu testen: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 | import re
import os
from datetime import datetime, date, timedelta
from time import sleep
import xml.etree.ElementTree as ET
path = "/playlists"
# get date
def get_date(seek_day):
t = datetime.today()
if t.hour < 6 and seek_day:
yesterday = date.today() - timedelta(1)
return yesterday.strftime('%Y-%m-%d')
else:
return datetime.now().strftime('%Y-%m-%d')
# read values from xml playlist
def iter_src_commands():
t = datetime.today()
last_time = t.hour * 3600 + t.minute * 60 + t.second
if 0 <= last_time < 6 * 3600:
last_time += 86400
list_date = get_date(True)
year, month, _day = re.split('-', list_date)
xml_path = os.path.join(path, year, month, list_date + '.xml')
while True:
xml_root = ET.parse(xml_path).getroot() # Pfad und Datei ändern sich ab hier nicht mehr
clip_nodes = xml_root.findall('body/video')
for clip_node in clip_nodes:
src_cmd = clip_node.get('src')
yield src_cmd # Außerhalb der for Schleife wird nur der letzte Clip ausgegeben
def play_clips():
for src_cmd in iter_src_commands():
print(src_cmd)
sleep(0.5)
play_clips()
|
|
Marc_BlackJack_Rintsch
Ehemalige
Anmeldungsdatum: 16. Juni 2006
Beiträge: 4694
Wohnort: Berlin
|
@jb-alvarado: Der Pfad wir in der while -Schleife jedes mal neu definiert. Der ändert sich wenn list_date sich ändert. Wenn es das nicht so wie vorher tut, muss ich beim umbauen einen Fehler gemacht haben. Ich habe das ”mechanisch” gemacht, also nur geschaut ob ich nur äquivalente Umformungen mache, ohne mich näher mit der Bedeutung zu befassen. Bei dem was Du auf die schnelle zum testen geschrieben hast wird der Pfad in der Tat nur einmal, ausserhalb der while -Schleife erstellt. Da wird dann natürlich immer die gleiche XML-Datei geladen.
|
jb-alvarado
(Themenstarter)
Anmeldungsdatum: 28. November 2012
Beiträge: 345
|
Marc_BlackJack_Rintsch schrieb:
Bei dem was Du auf die schnelle zum testen geschrieben hast wird der Pfad in der Tat nur einmal, ausserhalb der while -Schleife erstellt. Da wird dann natürlich immer die gleiche XML-Datei geladen.
Ja klar, hast recht. Beleibt eigentlich nur noch die Frage was passiert hiermit: | xml_root = ET.parse(xml_path).getroot()
clip_nodes = xml_root.findall('body/video')
# all clips in playlist except last one
for clip_node in clip_nodes[:-1]
...
|
Wie kann ich der Schleife beibringen, dass sich der Inhalt von clip_nodes ändert während sie ausgeführt wird. Das ist doch eine Stärke der Iteratoren, bzw. Generatoren.
|
jb-alvarado
(Themenstarter)
Anmeldungsdatum: 28. November 2012
Beiträge: 345
|
Ich habe jetzt noch mal im kleineren Deinen Ansatz nach gebaut und festgestellt, dass der so doch stimmt. In meinem letzten Test habe ich auch dein yield nicht richtig übertrage, dachte das sollte zur for Schleife gehören, und nicht zur while . Sorry, war da zu voreilig! Hier mein Nachbau: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 | def iter_src_commands():
start = 6.00 * 3600 - 5
while True:
xml_path = "2018-01-07.xml"
xml_root = ET.parse(xml_path).getroot()
clip_nodes = xml_root.findall('body/video')
for clip_node in clip_nodes:
if float(clip_node.get('clipBegin').rstrip('s')) > start:
start = float(clip_node.get('clipBegin').rstrip('s'))
print(start)
src_cmd = clip_node.get('src')
break
yield src_cmd
for src_cmd in iter_src_commands():
print(src_cmd)
sleep(5)
|
Damit ist sehr wohl die XML Datei editierbar, während der Ausführung...
|