Metadaten per Stillelement abspielen wenn externer Player läuft

Hallo mairlist Community,

Aktuell arbeite ich an einem Script, das automatisch Stilleelemente abspielt, während wir eine Show über einen externen Player am laufen haben. Warum Stilleelemente? Damit wir Airplays zählen könnnen (und Daten auf der Webseite anzeigen), senden wir die Info über gespielte Elemente per HTTP Post und über Icecast. Nur, die externe Show läuft über einen separaten, von mairlist unabhängigen Player, der nicht von uns programmiert ist. Ergo: wir senden keine Daten der gespielten Songs, was für die Artists ziemlich doof ist, da ihre Airplays so nicht gezählt werden.

Die Lösung also ist, ein Script in mAirlist das diese Info von der URL liest und dann über mAirlist sendet. Es gäb jetzt zwei Möglichkeiten: Entweder direkt per HHTPPost die Daten senden, nur das Problem ist, dann senden wir keine Info per Icecast. Und ich habe auch keine Möglichkeit gefunden, dies direkt in die Icecast Verbindung zu senden die wir mit mAirlist haben.
Deswegen habe ich Variante zwei gewählt: Ein Stilleelement erstellen, Titel und Artist der Elements umbenennen und das dann abspielen. Dann werden die Info an alle Verbindungen die wir in mAirlist haben versendet.

Die Logik läuft so:

  • Script prüft, ob die Sendung läuft (Check ob Element mit bestimmter ID aktuell in einem der Player abgespielt wird)
  • Falls ja, Datenabfrage der URL
  • Stilleelement erstellen und in einem separaten Player abspielen

Jetzt hänge ich aber an mehreren Punkten fest und würde bei euch gerne um Rat fragen:

  • Wenn ich das richtig sehe, kann ein Element nur gleichzeitig abgespielt werden, wenn wir im assist Modus sind, nicht im Auto Modus. Kann ich das evtl umgehen und auch im Auto Modus ein zweites Element gleichzeitig abspielen?
  • Falls das im Auto Modus nicht geht, vermute ich, dass ich wohl Auto im Script deaktivieren und wieder am Schluss aktivieren muss?
  • Wenn ich im Assist Modus ein Element in einem Player abspielen will, dann klappt das iwie nicht immer bzw immer erst beim nächsten Befehl. Kann das daran liegen, dass das Stillelement erst noch fertig im player geladen sein muss bzw noch nicht bereit zum abspielen ist? Welche Vorgehensweise ist hier am Besten?
  • Alle Umlaute die ich aus dem XML der URL lese werden zu � und Sonderzeichen werden zu &amp. Das passiert bereits mit dem HHTPGET Befehl von mAirlist selbst. Das XML ist UTF-8 codiert. Ich vermute mAirlist macht daraus was anderes. Gibt es eine Möglichkeit dies zu verhindern?

Vielen Dank im Voraus für eure Unterstützung!

Hier das aktuelle Script. Der main loop ist zu unterst bei “OnTimer”. Aktuell Endet es mit “CreateSilentItem”, da ich aktuell festhänge wie ich das Element richtig abspielen möchte, entsprechend meine Fragen oben.

(Ps: Die XML Funktionen zum extrahieren der Daten habe ich mir grösstenteils per Deepseek schreiben lassen. Der Rest ist sonst alles von mir.)

var
	LastDisplayedTitleArtist: string; // Saved as string that we use to compare: 'Title Artist'
	CurrentTitle: string;
	CurrentArtist: string;
	DataAvailable: boolean;
	isProcedureRunning: boolean;

const
	TargetDatabaseID = '6123'; // The item we will check if it is running
	silentID = '6122'; // This is the Database ID of the silent object we will insert to play
	DataURL = ''; // URL with XML data
	playerZ = 2;  // Player for the Silent objects


procedure OnLoad;
begin
	EnableTimer(4000);
end;


function IsShowPlaying: Boolean;
// Return True if one of the players is playing the Swiss Music only show
// We will check this by comparing the Database ID of the show and the currently playing item 
var
	player: IPlaylistPlayerControl;
	CurrentItem: IPlaylistItem;
	i: integer;
begin
	Result := False;

	for i := 0 to CurrentPlaybackControl.GetPlayerCount - 1 do // For all players that we have
	begin
		player := CurrentPlaybackControl.GetPlayer(i);
		
		if player.GetState = psPlaying then // Is player playing?
		begin
			CurrentItem := player.GetItem;
			
			// Compare current playing items Database ID with the item we look for
			if CurrentItem.GetDatabaseID = TargetDatabaseID then 
			begin
				Result := True;
				Exit; // Exit early since we found a match
			end;
		end;
	end;
end;


procedure CreateSilentItem(Title, Artist: String);
// Create the silent object, change the Title and name, then play it
var
	item: IPlaylistItem;
begin
	item:= Database(0).CreatePlaylistItem(silentID);
	item.SetTitle(Title);
	item.SetArtist(Artist);
	CurrentPlaylist.Insert(0, item);
end;

//
// XML Functions
//

function ExtractAfter(const Source, Search: string; StartPos: Integer): string;
var
  PosFound: Integer;
begin
  Result := '';
  PosFound := Pos(Search, Copy(Source, StartPos, Length(Source) - StartPos + 1));
  if PosFound > 0 then
    Result := Copy(Source, StartPos + PosFound - 1, Length(Source));
end;


function GetValueBetween(const Source, StartTag, EndTag: string; AfterPos: Integer): string;
var
  StartPos, EndPos: Integer;
  TempStr: string;
begin
  Result := '';
  
  // Get substring starting from AfterPos
  TempStr := Copy(Source, AfterPos, Length(Source) - AfterPos + 1);
  
  // Find start tag
  StartPos := Pos(StartTag, TempStr);
  if StartPos > 0 then
  begin
    StartPos := StartPos + Length(StartTag);
    
    // Find end tag
    EndPos := Pos(EndTag, Copy(TempStr, StartPos, Length(TempStr) - StartPos + 1));
    if EndPos > 0 then
      Result := Copy(TempStr, StartPos, EndPos - 1);
  end;
end;


procedure ExtractXML(XMLContent: string);
var
	PresentPos: Integer;
begin
	DataAvailable := False; // Disable as long as we don't have clean new data
	
	// Find "present" item
	PresentPos := Pos('sequence="present"', XMLContent);

	if PresentPos > 0 then
	begin
		// Extract values and write new data
		CurrentArtist := GetValueBetween(XMLContent, '<artist>', '</artist>', PresentPos);
		CurrentTitle := GetValueBetween(XMLContent, '<title>', '</title>', PresentPos);
		DataAvailable := True; // Re-enable
	end;
end;


procedure FetchData;
// Getting the Data off the website and process it
var
	XMLdata: string;
begin
	XMLdata := HTTPGet(DataURL);
	// SystemLog(XMLdata);
	ExtractXML(XMLdata);
end;


procedure OnTimer; // Main loop
begin

	if IsShowPlaying then
	begin
		if not isProcedureRunning then begin
			SystemLog('Swiss Music Only is running')
			isProcedureRunning := True
		end;
		FetchData; // Fetch XML Data off the website
		
		if DataAvailable then
		begin
		
			// Check if the last data we fetched is NOT what we last displayed
			if CurrentTitle + CurrentArtist <> LastDisplayedTitleArtist then
			begin
				CreateSilentItem(CurrentTitle, CurrentArtist)
				LastDisplayedTitleArtist := CurrentTitle + CurrentArtist
				SystemLog('Swiss Music Only: ' + CurrentArtist + ' ' + CurrentTitle)
			end
		end
	end
	else begin
		if isProcedureRunning then begin
			SystemLog('Swiss Music Only has stopped')
			isProcedureRunning := False
			LastDisplayedTitleArtist := ''
		end;
	end;
end;
 
begin
end.

.                  

Lasse die Stilleelemente in einer zweiten Playlist laufen, dann kollidieren sie nicht mit dem Hauptaudio.

Ich kann in der selben mAirlist Instanz eine zweite playlist im Hintergrund abspielen?

Ja.
Du kannst in der Konfiguration (aus dem Windows Startmenü, nicht die Systemsteuerung aus dem Playout) die Anzahl der Playlisten und die Anzahl der jeweiligen Player je Playlist definieren. Danach mAirList Playout neu starten, dann stehen dir die Playlisten und die Player wie gewünscht zur Verfügung,

Du kannst optional sogar angeben, ob die Playlisten zugleich sichtbar sein sollen oder ob du sie als Reiter (Tabs) im Playlist-Header angezeigt bekommen möchtest.

Da das alles hier unter “Script” läuft: Die Playlisten lassen sich numerisch abfragen und ansprechen.

Das hört sich super an. Senden aber alle Playlisten die gleichen Daten die in der Konfig festgelegt wurden? Wir haben einen Http Post und eine icecast Verbindung über die wir metadaten senden. Ich befürchte nämlich, dass das pro playliste ist? Falls das so wär, dann klappt dieser Weg nicht, da wie nur maximal eine Verbindung mit icecast machen können.

Falls man aber scripttechnisch Daten über die hintergelegten Verbindungen (spezifisch icecast) Daten schicken könnte, dann würde sich das mit den stilleelementen erledigt haben und nicht nötig sein. Ich habe dazu aber nichts gefunden.

… und zwar nullbasiert.
 

Pro Encoder. Mit wievielen Playlists Du ihn befeuerst ist gleichgültig.

Ergänzend: Du kannst die Elementtypen als Filter nutzen. In der Konfiguration jedes einzelnen Players (!) kannst du angeben, welche Elementtypen er abspielen darf und welche nicht.

So kannst du einem Player der zweiten Playlist (1-0) den Auftrag geben, ausschließlich Stille zu spielen und sonst nichts. Und bei den anderen Playern genau andersrum: Bloß keine Stille-Elemente spielen.

Damit kannst du entsprechend markierte Elemente ganz automatisch dem richtigen Player zuweisen lassen.

Ihr seid super, danke! Dann sollte das problemlos umsetzbar sein. Sobald ich die nächsten Tage Zeit habe, werde ich das so umsetzen.

Darf ich fragen, wie genau macht ihr das mit dem Abspielen der Stille? Ich habe es mit PlayerAutoLoad und gefolgt mit .start probiert. Das funktioniert aber irgendwie nur erst jedes zweite Mal. Ich vermute, dass ich zu schnell bin bevor das item effektiv bereit ist zum laden? Muss ich hier erst eine Abfrage machen, ob das item ready ist?

Und die letzte Frage, wie oben erwähnt, der HTTPGet Befehl von mAirlist scheint die UTF-8 XML umzukodieren ohne Sonderzeichen. Gibt es hierzu einen Workaround?

Danke Tausend für eure Unterstützung!

Nur Assist-Betrieb.  

Jedenfalls nicht in der Automation, da ich derzeit keine Automation mit mAirList betreue.

Im Live-ASSIST nutze ich Stille-Elemente, um von mir gewünschte Metadaten während der Moderation auszusenden. Das kann sogar schon vor EOF des gerade spielenden Elements erfolgen, da mAirList die Metadaten des zuletzt gestarteten (und vom Encoder erlaubten Elementtyps) aussendet - ganz unabhängig vom aktuellen PlayerState.
:wink: