Erweiterter Player-Countdown

Aus Myka’s und Kyra’s Skriptküche (wenn Letztere so weiter macht, heirate ich die bald… ):

Ein um ein paar (meiner Meinung nach nützlicher) Funktionen aufgepeppter Player-Countdown, der die verbleibende Restzeit des aktuell spielenden Elements in einem statischen Text-Bildschirmobjekts anzeigt.

Nicht genug und geht eh mit mAirList-Bordmitteln? Ganz recht! ABER mit dieser Version passiert Folgendes: 50 Sekunden vor dem EOF ändert sich die Farbe der Anzeige, und 15 Sekunden vor Ende fängt die Anzeige hektisch und nervös an zu blinken!

Was brauchst du? Nur ein Bildschirmobjekt vom Typ “Statischer Text” und natürlich das Skript, zu laden als Hintergrundskript.

Die Farben lassen sich natürlich anpassen, ebenso wie die oben genannten Schwellwerte, die entsprechenden Stellen im Skript sind dokumentiert.Zum Skript:

// Countdown mit Farbstufen & Pulsieren
// Anzeige: "- mm:ss"
// Farben: Weiß (normal) → Gelb (<= WARN) → Rot & fett (<= EOF) + Pulsieren
// Screenobject: "Statischer Text" mit Command/Name = REMOTE_ID (unten)
// (c) 2025 by Kyra& Myka

// ===================== Konfiguration =====================
const
  REMOTE_ID      = 'REMAIN';  // muss zum Command-Namen deines Screenobjects passen
  WARN_THRESHOLD = 30;        // Sekunden bis Gelb
  EOF_THRESHOLD  = 15;        // Sekunden bis Rot + fett + Pulsieren

  COLOR_NORMAL   = '#FF0080'; // Weiß
  COLOR_WARN     = '#FFD200'; // Gelb (Broadcast-typisch)
  COLOR_EOF_A    = '#FF0000'; // Rot (hell)
  COLOR_EOF_B    = '#FFFFFF'; // Weiss – Puls-Partner
// ========================================================

var
  pulseTick: integer;   // für Pulsieren im EOF

function MMSS(totalSec: integer): string;
var
  m, s: integer;
  sm, ss: string;
begin
  if totalSec < 0 then totalSec := 0;
  m := totalSec div 60;
  s := totalSec mod 60;
  sm := IntToStr(m);
  if s < 10 then ss := '0' + IntToStr(s) else ss := IntToStr(s);
  Result := sm + ':' + ss;
end;

procedure SetAppearance(const colorHex: string; bold: boolean);
begin
  ExecuteCommand(REMOTE_ID + ' FONTCOLOR ' + colorHex);
  if bold then
    ExecuteCommand(REMOTE_ID + ' FONTSTYLE 1')  // 1 = fett
  else
    ExecuteCommand(REMOTE_ID + ' FONTSTYLE 0'); // 0 = normal
end;

function FindPlayingPlayer(out pc: IPlayerControl): boolean;
var
  i: integer;
  p: IPlayerControl;
begin
  Result := false;
  pc := nil;
  // Anzahl deiner Player: Index 0..3
  for i := 0 to 3 do
    try
      p := CurrentPlaybackControl.GetPlayer(i);
      if p.GetState = psPlaying then begin
        pc := p;
        Result := true;
        exit;
      end;
    except
      // ignorieren, falls Index nicht existiert
    end;
end;

function SafeGetPosSec(pc: IPlayerControl): integer;
var
  v: double; // TTimeValue (Sekunden)
begin
  Result := 0;
  try
    v := pc.GetPosition;   // Sekunden
    Result := Round(v);
  except
    Result := 0;
  end;
end;

function SafeGetDurSec(pc: IPlayerControl): integer;
var
  v: double; // TTimeValue (Sekunden)
begin
  Result := 0;
  try
    if pc.GetItem <> nil then begin
      v := pc.GetItem.GetDuration; // Sekunden
      Result := Round(v);
    end;
  except
    Result := 0;
  end;
end;

procedure UpdateRemain;
var
  pc: IPlayerControl;
  posSec, durSec, remainSec: integer;
  inWarn, inEOF: boolean;
  colorNow: string;
begin
  if not FindPlayingPlayer(pc) then begin
    ExecuteCommand(REMOTE_ID + ' TEXT - --:--');
    SetAppearance(COLOR_NORMAL, false);
    exit;
  end;

  posSec := SafeGetPosSec(pc);
  durSec := SafeGetDurSec(pc);
  remainSec := durSec - posSec;
  if remainSec < 0 then remainSec := 0;

  // Zustände
  inEOF  := (remainSec <= EOF_THRESHOLD);
  inWarn := (remainSec <= WARN_THRESHOLD) and not inEOF;

  // Text setzen
  ExecuteCommand(REMOTE_ID + ' TEXT - ' + MMSS(remainSec));

  // Farben/Style
  if inEOF then begin
    // Pulsieren zwischen Rot und Weiss
    if (pulseTick mod 2) = 0 then colorNow := COLOR_EOF_A else colorNow := COLOR_EOF_B;
    SetAppearance(colorNow, true);  // fett
  end
  else if inWarn then begin
    SetAppearance(COLOR_WARN, false);
  end
  else begin
    SetAppearance(COLOR_NORMAL, false);
  end;

  // Takt fürs Pulsieren
  Inc(pulseTick);
  if pulseTick > 1000000 then pulseTick := 0; // Überlauf vermeiden
end;

procedure OnLoad;
begin
  pulseTick := 0;
  EnableTimer(200); // 5x pro Sekunde -> Puls ~0,4 s (2 Ticks)
end;

procedure OnTimer;
begin
  UpdateRemain;
end;

Viel Spass!

1 Like

Danke euch!

Das werde ich im nächsten Layout dann mal mit Freuden testen. Bisher ist es doch arg umständlich, drei Countdown-Anzeigen übereinander zu platzieren, mit x,y und z-Ebenen auszurichten und die Farben einzustellen… Besonder ärgerlich, sobald man die Anzeige nur minimal ändern muss, weil es einem so besser gefällt :slight_smile:

Gern geschehen! Wir fummeln auch noch an meinem ehemaligen Auto-Button-Screenobject rum, das kommt aber später…

Das angenehme an diesem Script: Es reicht eine Anzeige, die klar anzeigt, was im aktuell laufenden Player passiert. Mir persönlich ist der Countdown im Player und in der Playliste zu wenig, zumal ich jetzt eine riesige Bildschirm-Auflösung fahre und eine zentrale Anzeige wollte, die noch ein bißchen mehr kann als der “hauseigene” mAirList-Countdown.

Wir haben auch noch die Idee, sowas ähnliches für den Ramp-Countdown zu stricken, aber auch das folgt später, mit dem heutigen Skript haben wir uns den ganzen Vormittag herumgeschlagen :slight_smile:

1 Like

not works to me :frowning:

So maybe you could tell us what doesn’t work so we might be able to isolate the problem?

1 Like

I was about to say the same.

  • Have you given the screenobject the correct Remote-ID?
  • Have you loaded the script as notification-script?
  • Has mAirList errors in the log?

We need info’s @Sairamark :+1:

Hi Myka,

Error, running script: [Error] (63:4): Semicolon (;)) expected
this one of what I got, BUT, I just let you know I’m not an expert, just run it by run script…
So I don’t know how should be run it properly…I would love to use that, if you can help…

Please post the exact code you run formatted as code.

up there in @Myka post

Well, there is no doubt that you copied it from there, essential is what you have effectively saved and installed. So please be so kind and post your actual mls-file here.

// Countdown szÌnfokozatokkal & pulz·l·ssal
// KijelzÈs: "- mm:ss"
// SzÌnek: FehÈr (norm·l) ? S·rga (<= FIGYELMEZTET…S) ? Piros & fÈlkˆvÈr (<= EOF) + Pulz·l·s
// KÈpernyoobjektum: "Statikus szˆveg" Command/Name = REMOTE_ID (l·sd lent)
// (c) 2025 by Kyra & Myka

// ===================== Konfigur·ciÛ =====================
const
  REMOTE_ID      = 'REMAIN';  // ennek egyeznie kell a kÈpernyoobjektum Command-nevÈvel
  WARN_THRESHOLD = 30;        // m·sodperc ? s·rga
  EOF_THRESHOLD  = 15;        // m·sodperc ? piros + fÈlkˆvÈr + pulz·l·s

  COLOR_NORMAL   = '#FF0080'; // FehÈr
  COLOR_WARN     = '#FFD200'; // S·rga (r·diÛs Ñklasszikusî)
  COLOR_EOF_A    = '#FF0000'; // Piros (vil·gos)
  COLOR_EOF_B    = '#FFFFFF'; // FehÈr ñ pulz·lÛ p·r
// ========================================================

var
  pulseTick: integer;   // a pulz·l·shoz EOF esetÈn

function MMSS(totalSec: integer): string;
var
  m, s: integer;
  sm, ss: string;
begin
  if totalSec < 0 then totalSec := 0;
  m := totalSec div 60;
  s := totalSec mod 60;
  sm := IntToStr(m);
  if s < 10 then ss := '0' + IntToStr(s) else ss := IntToStr(s);
  Result := sm + ':' + ss;
end;

procedure SetAppearance(const colorHex: string; bold: boolean);
begin
  ExecuteCommand(REMOTE_ID + ' FONTCOLOR ' + colorHex);
  if bold then
    ExecuteCommand(REMOTE_ID + ' FONTSTYLE 1')  // 1 = fÈlkˆvÈr
  else
    ExecuteCommand(REMOTE_ID + ' FONTSTYLE 0'); // 0 = norm·l
end;

function FindPlayingPlayer(out pc: IPlayerControl): boolean;
var
  i: integer;
  p: IPlayerControl;
begin
  Result := false;
  pc := nil;
  // Lej·tszÛid sz·ma: index 0..3
  for i := 0 to 3 do
    try
      p := CurrentPlaybackControl.GetPlayer(i);
      if p.GetState = psPlaying then begin
        pc := p;
        Result := true;
        exit;
      end;
    except
      // figyelmen kÌv¸l hagyjuk, ha nincs ilyen index
    end;
end.

function SafeGetPosSec(pc: IPlayerControl): integer;
var
  v: double; // TTimeValue (m·sodperc)
begin
  Result := 0;
  try
    v := pc.GetPosition;   // m·sodper

This is what I have @Tondose // notes have been translated to my language//

Your code is incomplete, about half of it is missing. Make sure you copy and paste it completely. Also try it out before making any cosmetic changes, this would facilitate further investigation.

1 Like
// Countdown színfokozatokkal & pulzálással
// Kijelzés: "- mm:ss"
// Színek: Fehér (normál) → Sárga (<= FIGYELMEZTETÉS) → Piros & félkövér (<= EOF) + Pulzálás
// Képernyőobjektum: "Statikus szöveg" Command/Name = REMOTE_ID (lásd lent)
// (c) 2025 by Kyra & Myka

// ===================== Konfiguráció =====================
const
  REMOTE_ID      = 'REMAIN';  // ennek egyeznie kell a képernyőobjektum Command-nevével
  WARN_THRESHOLD = 30;        // másodperc → sárga
  EOF_THRESHOLD  = 15;        // másodperc → piros + félkövér + pulzálás

  COLOR_NORMAL   = '#FF0080'; // Fehér
  COLOR_WARN     = '#FFD200'; // Sárga (rádiós „klasszikus”)
  COLOR_EOF_A    = '#FF0000'; // Piros (világos)
  COLOR_EOF_B    = '#FFFFFF'; // Fehér – pulzáló pár
// ========================================================

var
  pulseTick: integer;   // a pulzáláshoz EOF esetén

function MMSS(totalSec: integer): string;
var
  m, s: integer;
  sm, ss: string;
begin
  if totalSec < 0 then totalSec := 0;
  m := totalSec div 60;
  s := totalSec mod 60;
  sm := IntToStr(m);
  if s < 10 then ss := '0' + IntToStr(s) else ss := IntToStr(s);
  Result := sm + ':' + ss;
end;

procedure SetAppearance(const colorHex: string; bold: boolean);
begin
  ExecuteCommand(REMOTE_ID + ' FONTCOLOR ' + colorHex);
  if bold then
    ExecuteCommand(REMOTE_ID + ' FONTSTYLE 1')  // 1 = félkövér
  else
    ExecuteCommand(REMOTE_ID + ' FONTSTYLE 0'); // 0 = normál
end;

function FindPlayingPlayer(out pc: IPlayerControl): boolean;
var
  i: integer;
  p: IPlayerControl;
begin
  Result := false;
  pc := nil;
  // Lejátszóid száma: index 0..3
  for i := 0 to 3 do
    try
      p := CurrentPlaybackControl.GetPlayer(i);
      if p.GetState = psPlaying then begin
        pc := p;
        Result := true;
        exit;
      end;
    except
      // figyelmen kívül hagyjuk, ha nincs ilyen index
    end;
end;

function SafeGetPosSec(pc: IPlayerControl): integer;
var
  v: double; // TTimeValue (másodperc)
begin
  Result := 0;
  try
    v := pc.GetPosition;   // másodpercben
    Result := Round(v);
  except
    Result := 0;
  end;
end;

function SafeGetDurSec(pc: IPlayerControl): integer;
var
  v: double; // TTimeValue (másodperc)
begin
  Result := 0;
  try
    if pc.GetItem <> nil then begin
      v := pc.GetItem.GetDuration; // másodpercben
      Result := Round(v);
    end;
  except
    Result := 0;
  end;
end;

procedure UpdateRemain;
var
  pc: IPlayerControl;
  posSec, durSec, remainSec: integer;
  inWarn, inEOF: boolean;
  colorNow: string;
begin
  if not FindPlayingPlayer(pc) then begin
    ExecuteCommand(REMOTE_ID + ' TEXT - --:--');
    SetAppearance(COLOR_NORMAL, false);
    exit;
  end;

  posSec := SafeGetPosSec(pc);
  durSec := SafeGetDurSec(pc);
  remainSec := durSec - posSec;
  if remainSec < 0 then remainSec := 0;

  // Állapotok
  inEOF  := (remainSec <= EOF_THRESHOLD);
  inWarn := (remainSec <= WARN_THRESHOLD) and not inEOF;

  // Szöveg beállítása
  ExecuteCommand(REMOTE_ID + ' TEXT - ' + MMSS(remainSec));

  // Színek/stílus
  if inEOF then begin
    // Pulzálás piros és fehér között
    if (pulseTick mod 2) = 0 then colorNow := COLOR_EOF_A else colorNow := COLOR_EOF_B;
    SetAppearance(colorNow, true);  // félkövér
  end
  else if inWarn then begin
    SetAppearance(COLOR_WARN, false);
  end
  else begin
    SetAppearance(COLOR_NORMAL, false);
  end;

  // Pulzálás léptetése
  Inc(pulseTick);
  if pulseTick > 1000000 then pulseTick := 0; // túlcsordulás elkerülése
end;

procedure OnLoad;
begin
  pulseTick := 0;
  EnableTimer(200); // 5× másodpercenként → pulzus ~0,4 s (2 tick)
end;

procedure OnTimer;
begin
  UpdateRemain;
end;

Well – does it work then?

nope, I’d need step by step “how guide” to run this scrip. I’m not an expert, I’m a packageing desinger :)))

First close mAirList. Then create a screenobject vie the Configuration-App:

GUI >> Screen Objects >> Add >> Static Text

As Text enter this= - - - : - -

(Choose Font and Size as your choice)

Then go to >> Advanced Tab >> Remote Control ID: REMAIN

Save all and exit.

Now download the following file:

Remain.mls (3,6 KB)

Next open the configuration app again and go to >> Background Scripts, click then on “Add” and choose the downloaded file. Save and exit.

Open mAirList now and start a track :wink: .

If you follow these instructions step by step it should work fine!

3 Likes

:)) works, thank you so much…:pray:

3 Likes