Wir haben einige Syndications und Wiederholungen, die ich weitestgehend automatisieren möchte.
Die Syndications werden inzwischen immer mit dem gleichen Dateinamen und einem Cue-Sheet angeliefert.
Ähnlich wie hier beschrieben: https://forum.mairlist.com/index.php/topic,9849.msg63781.html#msg63781
Ich möchte also folgendes erreichen. Zum einen möchte ich irgendwie triggern das Cue-In, Fade-Out und Cue-Out neu ermittelt werden, die sind im DB Eintrag gelöscht. (vielleicht optimalerweise bevor die Playliste für die nächste Stunde geladen wird, die das betrifft also noch in der DB) Ausserdem spätestens beim laden der Playliste das neu-einlesen des Cue-Sheets (gleicher Dateiname wie die MP3 Datei)
Trigger entweder über den mAirlist Event Scheduler oder gerne auch über das REST Interface, dann kann ich das gleich auslösen, wenn ich per Batch-Script die neuen Dateien ziehe. MP3 Gain drüber und mAirlist-Cue triggern.
mAirlist DB Server steht auch zur Verfügung und versteht auch etwas REST, wenn ich das richtig verstanden habe.
Danke, könntest Du das für den Hardwaremokel etwas detaillierter beschreiben?
Ich finde im Wiki nix zu dem Suchbegriff und hier im Forum auch nicht. Auch die Scriptinghelp liefert kein suchergebnis.
Dort habe ich nur
OK, ich stehe immer noch total auf dem Schlauch.
Bisher habe ich an mAirlist Basisfunktionen wie Encoder herumgescriptet. Ich muss erst mal verstehen, wie ich das Script dazu bewege, bestimmte Elemente einer Playliste zu bearbeiten.
Geht das nur in der Hauptplayliste oder kann ich das schon in der Datenbank triggern, bevor die ausspielende Instanz die Playliste überhaupt geladen hat?
Ich habe hier von KFB ein Script gefunden, das müsste als Ausgangsbasis ja funktionieren, nur eben nicht Hooks bearbeiten sondern CuePunkte und Cuesheet:
const
MAX_COUNT = 2;
var
pl: IPlaylist;
hook: IPlaylistItem;
i: integer;
begin
pl := Factory.CreatePlaylist;
CurrentPlaylist.BeginUpdate;
try
for i := CurrentPlaylist.GetNextIndex to CurrentPlaylist.GetCount - 1 do
if CurrentPlaylist.GetItem(i).GetItemType = pitMusic then begin
pl.Add(CurrentPlaylist.GetItem(i));
if pl.GetCount = MAX_COUNT then break;
end;
hook := Factory.CreateHookContainer(pl);
container.GetPlaylist.Add(hook);
finally
CurrentPlaylist.EndUpdate;
end;
end.[/code]
Ich habe das also mal so umgebaut, evetl. muss ich den Elementtyp noch mal ändern, spielt aber für die Funktion, erstmal keine Rolle.
const
MAX_COUNT = 3;
var
pl: IPlaylist;
cue: IPlaylistItem;
i: integer;
begin
pl := Factory.CreatePlaylist;
CurrentPlaylist.BeginUpdate;
try
for i := CurrentPlaylist.GetNextIndex to CurrentPlaylist.GetCount - 1 do
if CurrentPlaylist.GetItem(i).GetItemType = pitMusic then begin
pl.Add(CurrentPlaylist.GetItem(i));
if pl.GetCount = MAX_COUNT then break;
end;
cue := item.AutoSearchPosition(ptCueIn);
cue := item.AutoSearchPosition(ptFadeOut);
cue := item.AutoSearchPosition(ptCueOut);
finally
CurrentPlaylist.EndUpdate;
end;
end.
EDIT: gehört also eigentlich eher in die Foren Kategorie Scripting, ggf. bitte verschieben.
CurrentPlaylist steht, wieder Name schon vermuten lässt, für die “aktuelle Playlist”. Je nach Kontext, in dem das Script läuft. Als Event oder manuell gestartet ist das die aktuelle Playlist im Ausspieler. Als “Nachbearbeitungs-Script” in einer Datenbank-Aktion hingegen bezieht sich CurrentPlaylist auf die aus der DB geladene Playlist, bevor sie dann in die Ausspielung kopiert wird.
Natürlich kann man auch Scripts bauen, die sich einen Sendeplan aus der DB laden, ihn bearbeiten und dann zurückschreiben.
Wenn man ein bestimmtes Element sucht, geht man normalerweise einfach die CurrentPlaylist von 0 bis GetCount-1 durch und vergleicht Interpret oder Titel oder irgendein Attribut. Du musst halt wissen, woran du deine Datei erkennst
Übrigens liefert AutoSearchPosition keinen Wert zurück, sondern trägt ihn direkt in das IPlaylistItem ein.
OK, sieht so aus als wäre das Nachbearbeitungsscript, das was ich brauche. Mal sehen ob ich etwas dazu finde, wie das getriggert wird. Ich versinke also mal etwas in der Doku. Bitte nicht spoilern, nur wer selber sucht, lernt auch was.
Ja, das ist klar soweit. Ich bin mir noch nicht sicher ob ich die Sendung in einem Stück verwende oder die Version, die in 3 Teilen angeliefert wird aber das spielt ja keine Rolle. Ich bin hier von 3 Elementen pro Sendestunde ausgegangen, die ich dann entsprechend Markiere, vermutlich werde ich nach dem Elemettyp Sendung, suchen, der dann 3 mal auftaucht und bearbeitet werden soll.
OK, ich brauche die Variable cue also gar nicht und wenn ich einfach alle durchlaufe kann ich break und MAXCOUNT auch rauswerfen.
const
MAX_COUNT = 3;
var
pl: IPlaylist;
i: integer;
begin
pl := Factory.CreatePlaylist;
CurrentPlaylist.BeginUpdate;
try
for i := CurrentPlaylist.GetNextIndex to CurrentPlaylist.GetCount - 1 do
if CurrentPlaylist.GetItem(i).GetItemType = pitShow then begin
pl.Add(CurrentPlaylist.GetItem(i));
if pl.GetCount = MAX_COUNT then break;
end;
item.AutoSearchPosition(ptCueIn);
item.AutoSearchPosition(ptFadeOut);
item.AutoSearchPosition(ptCueOut);
finally
CurrentPlaylist.EndUpdate;
end;
end.
Das einlesen des Cuesheets fehlt auch noch dazu müsste ich aus dem Item das ich gerade bearbeite den Pfad samt Dateinamen extrahieren und ein .cue anhängen um es der entsprechenden Funktion zu übergeben, das Sheet neu zu lesen.
Auf meinem Weg zur Vollautomatischen Ausspielung unserer Wiederholgunen, bin ich nun mit den externen Elementen ein Stück weiter. (Finaler Test steht noch aus und kann wegen des laufenden Programms, gerade nicht durchgeführt werden)
Jetzt geht es hier weiter, mit dem Eincuen, das sollte mit dem Script oben, funktionieren und was mir auch noch fehlt:
OK, ich habe hier einen Fehler, wenn das Script ausgeführt werden soll.
const
MAX_COUNT = 3;
var
pl: IPlaylist;
i: integer;
begin
pl := Factory.CreatePlaylist;
CurrentPlaylist.BeginUpdate;
try
for i := CurrentPlaylist.GetNextIndex to CurrentPlaylist.GetCount - 1 do
if CurrentPlaylist.GetItem(i).GetItemType = pitShow then begin
pl.Add(CurrentPlaylist.GetItem(i));
if pl.GetCount = MAX_COUNT then break;
end;
item.AutoSearchPosition(ptCueIn);
item.AutoSearchPosition(ptFadeOut);
item.AutoSearchPosition(ptCueOut);
finally
CurrentPlaylist.EndUpdate;
end;
end.
Da erscheint dieser Fehler, scheint also irgend etwas nicht zu stimmen.
Neuer Fehler, ob ich das jemals lerne… Dabei habe ich damals in der Schule sogar etwas Pascal gelernt. Auf Hochmodernen 286ern mit Bernsteinfarbenen Monitoren.
const
MAX_COUNT = 3;
var
pl: IPlaylist;
i: integer;
begin
pl := Factory.CreatePlaylist;
CurrentPlaylist.BeginUpdate;
try
for i := 0 to CurrentPlaylist.GetCount - 1 do
if CurrentPlaylist.GetItem(i).GetItemType = pitShow then begin
pl.Add(CurrentPlaylist.GetItem(i));
if pl.GetCount = MAX_COUNT then break;
end;
item.AutoSearchPosition(ptCueIn);
item.AutoSearchPosition(ptFadeOut);
item.AutoSearchPosition(ptCueOut);
finally
CurrentPlaylist.EndUpdate;
end;
end.
Vor allem fehlt die Deklaration von “item”. Für welche Items soll das AutoCue denn durchgeführt werden? Für alle, die vorher in der Liste “pl” notiert wurden? Dann wohl eher so:
for i := 0 to pl.GetCount - 1 do begin
pl.GetItem(i).AutoSearchPosition(ptCueIn);
pl.GetItem(i).AutoSearchPosition(ptFadeOut);
pl.GetItem(i).AutoSearchPosition(ptCueOut);
end;
Danke, das war tatsächlich nur ein Copy and Paste Fehler hier im Forum. Im Script hatte ich das end. mit drin. Ich korrigiere das weiter oben in meinen Posts.
d.H. ich brauche eine 2. Schleife. Die erste, die mir den Typ Sendung (Show) raus sucht aber nur maximal die ersten 3 Elemente durchsucht und die 2. Schleife die die Elemente bearbeitet.
Laufen die einfach nacheinander? Ich hätte jetzt angenommen, die müssten in einander verschachtelt sein?
Dann komme ich auf dieses Konstrukt.
const
MAX_COUNT = 3;
var
pl: IPlaylist;
i: integer;
begin
pl := Factory.CreatePlaylist;
CurrentPlaylist.BeginUpdate;
try
for i := 0 to CurrentPlaylist.GetCount - 1 do
if CurrentPlaylist.GetItem(i).GetItemType = pitShow then begin
pl.Add(CurrentPlaylist.GetItem(i));
if pl.GetCount = MAX_COUNT then break;
end;
for i := 0 to pl.GetCount - 1 do begin
pl.GetItem(i).AutoSearchPosition(ptCueIn);
pl.GetItem(i).AutoSearchPosition(ptFadeOut);
pl.GetItem(i).AutoSearchPosition(ptCueOut);
end;
finally
CurrentPlaylist.EndUpdate;
end;
end.
EDIT nach dem Stundenwechsel: Sehr schön, der Fehler ist schon mal weg.
Der nächste Schritt, den ich machen möchte ist: Den Titel und Interpreten aus dem MP3 Tag neu lesen.
Ich nehme an, dass das irgendwie mit: IMetadataHandler > ReadNativeTags funktioniert.
Die untere for Schleife um sowas erweitern oder ist das fehlt mir da wieder irgend etwas?
pl.GetItem(i).IMetadataHandler(ReadNativeTags);
Das würde ich an oberster Stelle einfügen, bevor die Cue-Werte ermittelt werden.
EDIT oder ist es: IFilePlaylistItem > function GetFileTagData ( iTag : string ) : string
?
Hatte diesen Beitrag gefunden: Zugriff auf ID3-Tags mit mAirList-Script
Weiß aber nicht ob der a) noch aktuell ist und b) kann ich daraus auch nicht das richtige Script ableiten.
Wenn du sie nacheinander schreibst, laufen sie auch nacheinander Natürlich könnte man auch auf die zweite Schleife verzichten und alles direkt in der ersten machen (und dabei mitzählen, wie viele man schon bearbeitet hat).
const
MAX_COUNT = 3;
var
i: integer;
count: integer;
begin
CurrentPlaylist.BeginUpdate;
try
count := 0;
for i := 0 to CurrentPlaylist.GetCount - 1 do
if CurrentPlaylist.GetItem(i).GetItemType = pitShow then begin
CurrentPlaylist.GetItem(i).AutoSearchPosition(ptCueIn);
CurrentPlaylist.GetItem(i).AutoSearchPosition(ptFadeOut);
CurrentPlaylist.GetItem(i).AutoSearchPosition(ptCueOut);
count := count + 1;
if count = MAX_COUNT then break;
end;
finally
CurrentPlaylist.EndUpdate;
end;
end.
Das ist genau so’n Ding was ich dann immer nicht verstehe. (Das war Westfälisch: Immer nicht verstehe )
Die Cue Daten kann ich einfach vom Item Lesen aber um das lesen der Tags, muss ich wieder eine weitere Funktion drum herum bauen.
Ob ich das jemals in meinen Kopf kriege…
Factory ist ein Objekt, das Methoden zur Verfügung stellt, über die man alle möglichen anderen Arten von Objekten erzeugen kann.
In diesem Fall wollen wir über die Funktion CreateMetadataHandler ein Objekt vom Typ IMetadataHandler erzeugen, mit dem wir die Tags auslesen können.
Die Funktion CreateMetadataHandler bekommt dabei als Parameter das PlaylistItem übergeben, auf dem der Handler arbeiten soll - damit der direkt weiß, mit welcher Datei und welchem Item er es zu tun hat.
Dann wird dessen Methode ReadNativeTags aufgerufen, um die ID3-Tags zu lesen und in den PlaylistItem-Datensatz zu kopieren.
“Unabgekürzt” (und besser zu verstehen) würde das so aussehen:
Durch die Kurzschreibweise ersparen wir uns die ganzen Variablendeklarationen.
Du siehst, die Interna von mAirList sind manchmal sehr kompliziert. Aber bieten doch eine Menge Funktionalität. Die Script-Engine ist euer Guckloch in diese Welt.
Um die Sache etwas intuitiver zu machen, könnte ich dem IPlaylistItem eine Methode CreateMetadataHandler geben, die nur eine Abkürzung für Factory.CreateMetadataHandler(item) ist. Dann würde es so aussehen:
Übrigens, bei den Typen mit “I” vorne (IPlaylistItem, IMetadataHandler, …) handelt es sich allesamt um “Interfaces”, also “indirekte” Verweise auf Objekte. Die dann wiederum Methoden haben, die man aufrufen kann.
Die Verwendung von Interfaces statt normaler Objektreferenzen erlaubt es in Delphi, dass man “reference counting” macht. Also Objekte, die sich selbst aus dem Speicher löschen, sobald niemand mehr auf sie zugreift. Daher der Umweg über Interfaces. Beim Programmieren immer die doppelte Arbeit, da man sowohl das Interface als auch das implementierende Objekt definieren muss. Aber am Ende hat es nur Vorteile.