Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Multimedia (https://www.delphipraxis.net/16-multimedia/)
-   -   Delphi M3U (https://www.delphipraxis.net/121329-m3u.html)

Nils_13 25. Sep 2008 21:20


M3U
 
Hi,

ich bin - wegen einigen nervigen Problemen - erst jetzt bei den Playlisten-Ladeprozeduren meines neuen Players angekommen. Der alte Player konnte mit einigen Abwandlungen von M3U nicht umgehen. Das liegt aber auch daran, dass M3U nicht wirklich festgelegt ist. Es gibt das simple und das erweiterte Format, welches total einfach auseinanderzuhalten ist, ansonsten gibt es jedoch üble Unterschiede in den Playlisten. Kann mir jemand sagen, welche Dialkete es gibt und was ich zu beachten habe ? Denn in vielen Fällen wird einfach mal etwas weggelassen, zum Beispiel das #EXTINF:. Wenn man nun das #EXTINF: weglässt, stellt sich die Frage, ob dann jede zweite Zeile eine #EXTINF:-ohne-#EXTINF:-Zeile wäre, oder ob es dann noch Leerzeilen dazwischen geben darf. Wie könnte man überhaupt Exceptions usw. vermeiden oder falsches laden, weil was in der Datei nicht stimmt ? Der folgende Code funktioniert gar nicht mal schlecht, aber nur unvollständig. Abgesehen davon bleibt die Frage mit dem #EXTINF: noch offen. Hier eine Playlist (von Wikipedia kopiert, ich benutze selbst keine M3U-Playlisten):
Zitat:

#EXTM3U
#EXTINF:221,Queen - Bohemian Rhapsody
Titel 1.mp3
#EXTINF:473,Dire Straits - Walk Of Life
Pop\Meine Auswahl\Titel 2.mp3
#EXTINF:264,asd – Bolero
C:\Dokumente und Einstellungen\All Users\Dokumente\Eigene Musik\Titel irgendeinenummer.mp3
Queen - Bohemian Rhapsody und Dire Straits - Walk Of Life werden korrekt ausgelesen. Beim letzten ist es seltsam: Ich gebe einfach mal nach jedem Schleifendurchlauf eine Nachricht wie folgt aus: ShowMessage('||'+Arr[High(Arr)].Artist+'||'+Arr[High(Arr)].Titel+'||'+Arr[High(Arr)].Dauer+'||'); Überall kommt was, nur beim asd - Bolero erscheint eine komplett leere Nachricht. Habt ihr eine Idee warum ?

Hier der bisherige Code, ich denke er muss noch verbessert werden, hat aber schon einen halbwegs brauchbaren Ansatz:
Delphi-Quellcode:
for i := 1 to Pred(sl.Count) do // in der StringList befindet sich die rohe Datei.
begin
  SetLength(Arr, Succ(Length(Arr))); // Arr ist ein Array of TTagInfo. Dort werden alle Tags reingespeichert.
  p1 := Pos('#EXTINF:', sl[i]); // Die Position von #EXTINF: wird später noch gebraucht, daher speicher ich sie in p1.
  if (sl[i] <> '') and (p1 < 1) then // Wenn kein #EXTINF: vorhanden ist, jedoch die Zeile nicht leer ist, gehe ich davon aus, dass es sich um die Zeile mit dem Dateinamen handelt.
  begin
    Arr[High(Arr)].Dateiname := sl[i]; // Dateiname in langer Form zuweisen.
    Arr[High(Arr)].kDateiname := ExtractFileName(sl[i]); // Und noch in kurzer Form.
  end else
  begin
    p2 := Pos(',', sl[i]); // Da später noch Position von , benötigt, speichere ich sie in p2.
    p3 := Pos('-', sl[i]); // Da später noch Position von - benötigt, speichere ich sie in p3.
    if (p1 > 0) and (p2 > p1) then // p1 muss vorhanden sein und das , (p2) muss sich hinter p1 befinden.
      Arr[High(Arr)].Dauer := SecToOut(StrToInt(Copy(sl[i], p1+8, p2-(p1+8)))); // So lässt sich die Dauer bestimmen. Was die 8 betrifft, muss man einfach nachrechnen, es geht auf. SecOut konvertiert Sekunden in das [hh:mm:ss]-Format.
    if p3 > p2 then // Der - (p3) muss hinter dem , (p2) sein.
    begin
      Arr[High(Arr)].Artist := Copy(sl[i], Succ(p2), Pred(p3)-Succ(p2)); // Copyrei.
      Arr[High(Arr)].Titel := Copy(sl[i], Succ(p3), Length(sl[i])); // Hier ebenfalls.
    end else
      Arr[High(Arr)].Artist := Copy(sl[i], Succ(p2), Length(sl[i])); // Falls es kein - (p3) gibt, wird einfach alles hinter dem , (p2) in Artist geschrieben.
  end;
end;
Das Array (Arr) wird später nochmal auseinandergenommen. Es wird bevor es in meine eigene Listbox kommt nochmal geschaut, ob Artist und Titel zugewiesen, ob Tags vorhanden usw. sind. Das sieht so aus:
Delphi-Quellcode:
lb.Clear;
lb.BeginUpdate;
for i := 0 to Pred(Length(Arr)) do
  if (Trim(Arr[i].Artist) = '') and (Trim(Arr[i].Titel) = '') then // Da Artist und Titel nicht vorhanden, wird der Dateiname und die Dauer angegeben.
    lb.Add(Arr[i].kDateiname + ' [' + Arr[i].Dauer + ']')
  else
  if (Trim(Arr[i].Artist) <> '') and (Trim(Arr[i].Titel) = '') then // Da der Titel nicht vorhanden ist, wurde alles ab dem , (p2) übernommen, Arr[i].Titel muss logischerweise nicht ausgegeben werden.
    lb.Add(Arr[i].Artist + ' [' + Arr[i].Dauer + ']')
  else // Wenn alles in Ordnung ist, wie gewöhnlich ausgeben.
    lb.Add(Arr[i].Artist + ' - ' + Arr[i].Titel + ' [' + Arr[i].Dauer + ']');
lb.EndUpdate;
lb.Draw;

omata 25. Sep 2008 22:53

Re: M3U
 
Liste der Anhänge anzeigen (Anzahl: 1)
...

mr_emre_d 26. Sep 2008 00:11

Re: M3U
 
Ich weiß nicht genau, um was es geht

aaabeerrr

Wenn du willst, kannst du dir die "Playlistread.pas" Unit von meinem iBan anschauen !

Nils_13 26. Sep 2008 22:31

Re: M3U
 
omata: Dein Code ist sehr hilfreich. Ich habe ihn in meinen Code integriert. Dies tue ich generell nicht ohne ihn zu verstehen. Ich habe noch eine Frage zum Code:
Delphi-Quellcode:
if ExtractFilePath(Filename) = '' then
  Dateiname := ExtractFilePath(Application.ExeName)+Zeile
else
  Dateiname := ExtractFilePath(Filename)+Zeile;
Warum genügt nicht folgender Code ?
Delphi-Quellcode:
Dateiname := ExtractFilePath(Filename)+Zeile;
Abgesehen davon: Bei meiner an sich identischen Version im Code liest mir der Code aus irgendeinem Grund ??? – Boléro nicht aus. Ich erhalte wie bei meinem alten Code nichts weiter als eine komplett leere Ausgabe. Ich habe nicht viel geändert im Vergleich zu deinem Code. Abgesehen von der Ersetzung von Textfile durch StringList habe ich nichts geändert und die eine Stelle mit dem Dateiname (siehe oben) habe ich wie oben gezeigt verändert. Also hier nochmal der ganze Code, sieht jemand den Fehler ? Ich habe extra dein Projekt mal kompiliert und es ging alles sehr gut. Woran könnte das liegen ? Ist da eventuell ein verstecktes Zeichen in der Datei welches wegen anderem Auslesen deines Codes keine Rolle spielt ? Alle anderen Lieder liest auch mein Code einwandfrei aus.
Delphi-Quellcode:
var sl                     : TStrings;
    Zeile, Titel, Dateiname : String; // Titel=Artist+Titel
    p, Sekunden            : Integer;
begin
  sl := TStringList.Create;
  sl.LoadFromFile(f);
  if sl.Count > 1 then
  begin
    if sl[0] = '#EXTM3U' then
    begin
      Sekunden := -1;
      Titel   := '';
      for i := 1 to Pred(sl.Count) do // 1, weil erste Zeile #EXTM3U
      begin
        Zeile := Trim(sl[i]);
        Dateiname := '';
        if Pos('#EXTINF', Zeile) = 1 then
        begin
          Delete(Zeile, 1, 8);
          p := Pos(',', Zeile);
          if p > 0 then
          begin
            if not TryStrToInt(Copy(Zeile, 1, Pred(p)), Sekunden) then
              Sekunden := -1;
            Delete(Zeile, 1, p);
            Titel := Zeile;
          end;
        end else
        begin
          Dateiname := Zeile;
          {$IFDEF WIN}
          if Pos(':', Zeile) > 0 then
            Dateiname := Zeile
          else
          {$ENDIF}
          {$IFDEF UNIX}
          if (Length(Zeile) > 0) and (s[1] = '/') then
            Dateiname := Zeile
          else
          {$ENDIF}
            Dateiname := ExtractFilePath(f)+Zeile;
        end;
        if Dateiname <> '' then
        begin
          SetLength(Arr, Succ(Length(Arr)));
          Arr[High(Arr)].Dauer     := SecToOut(Sekunden);
          Arr[High(Arr)].Artist    := Titel;
          Arr[High(Arr)].Dateiname := Dateiname;
          Arr[High(Arr)].kDateiname := ExtractFileName(Dateiname);

          ShowMessage(Arr[High(Arr)].Artist);

          Sekunden := -1;
          Titel   := '';
        end;
      end;
    end;  
  end;
  sl.Free;
end;

omata 27. Sep 2008 00:01

Re: M3U
 
Zitat:

Zitat von Nils_13
Warum genügt nicht folgender Code?

Stell dir vor, du öffnest die m3u-Datei ohne Pfadangabe. Normalerweise ist das dann der Programmpfad. Deshalb der aufwendigere Quellcode.

Warum liest du deine m3u-Datei in eine Liste ein? Stell dir vor, die Datei ist 100MB groß. Dein Quellcode würde diese Datei vollständig in den Arbeitsspeicher laden.
Die Variante mit assignfile, in meinem Beispiel, liest immer nur eine Zeile aus und verarbeitet diese. Das funktioniert auch mit einer 100MB Datei.

Wieso soll ich also einen Fehler in deinem Quellcode suchen? Ich habe dir doch einen sinnvollen Weg gezeigt? Vermeide einfach die StringList.

Nils_13 27. Sep 2008 22:22

Re: M3U
 
Mit dem AssignFile hast du wirklich recht. Keine Ahnung warum ich das überhaupt durch eine StringList ersetzt habe, war definitiv ein Fehler. Nun aber eine Frage dazu: Spielt das Clear eine Rolle ? Denn Lazarus kennt das nicht. Ich habe es daher einfach mal weggelassen, in Beispielen zu AssignFile in Lazarus taucht das Clear auch nicht auf.
Bei "??? – Boléro" erscheint weiterhin nur eine leere Meldung (=leerer Listboxeintrag). Hier die aktuelle Version, sie entspricht deinem zurecht bevorzugtem Stil mit AssignFile. In meinen Augen ist es identisch zu deinem Code, bloß dass eben die ein oder andere Variable anders heißt, aber mehr auch wirklich nicht.
Delphi-Quellcode:
var Datei                  : TextFile;
    Zeile, Titel, Dateiname : String; // Titel=Artist+Titel
    p, Sekunden            : Integer;  
begin
  AssignFile(Datei, f);
  try
    Reset(Datei);
    Sekunden := -1;
    Titel   := '';
    while not EOF(Datei) do
    begin
      ReadLn(Datei, Zeile);
      Zeile := Trim(Zeile);
      Dateiname := '';
      if Pos('#EXTM3U', Zeile) = 1 then
      begin

      end else
      if Pos('#EXTINF:', Zeile) = 1 then
      begin
        Delete(Zeile, 1, 8);
        p := Pos(',', Zeile);
        if p > 0 then
        begin
          if not TryStrToInt(Copy(Zeile, 1, Pred(p)), Sekunden) then
            Sekunden := -1;
          Delete(Zeile, 1, p);
          Titel := Zeile;
        end;
      end else
      begin
        Dateiname := Zeile;
        {$IFDEF Win32}
        if Pos(':', Zeile) > 0 then
          Dateiname := Zeile
        else
        {$ENDIF}
        {$IFDEF UNIX}
        if (Length(Zeile) > 0) and (s[1] = '/') then
          Dateiname := Zeile
        else
        {$ENDIF}
          Dateiname := ExtractFilePath(f)+Zeile;
      end;
      if Dateiname <> '' then
      begin
        SetLength(Arr, Succ(Length(Arr)));
        Arr[High(Arr)].Dauer     := SecToOut(Sekunden);
        Arr[High(Arr)].Artist    := Titel;
        Arr[High(Arr)].Dateiname := Dateiname;
        Arr[High(Arr)].kDateiname := ExtractFileName(Dateiname);

        ShowMessage(Arr[High(Arr)].Artist);

        Sekunden := -1;
        Titel   := '';
      end;
    end;
  finally
    CloseFile(Datei);
  end;                
end;

omata 27. Sep 2008 22:27

Re: M3U
 
Welches Clear meinst du jetzt? Das Clear der StringList löscht diese. Lazarus hat diese Möglichkeit nicht? Das kann ich gar nicht glauben. Wenn es dort eine StringList gibt, dann kann die bestimmt auch geleert werden.
Das Clear beim ersten Ausführen der Routine natürlich unwichtig, weil die Liste dann ja leer ist. Bei einem dann folgenden Aufruf ist das Clear dann wichtig, um eben alle vorherigen Einträge zu löschen. Aber das Clear ist doch bei der AssignFile-Variante gar nicht vorhanden.
Also ich weiss nicht wirklich, was gerade dein Problem ist.

Nils_13 27. Sep 2008 22:31

Re: M3U
 
Das ist ein Codeausschnitt aus der TM3UList.LoadFromFile:
Delphi-Quellcode:
Clear;
assignfile(Datei, Filename);
try
  reset(Datei);
  Sekunden:=-1;
...
Wofür steht dort das Clear ?

omata 27. Sep 2008 22:41

Re: M3U
 
Bei so etwas musst du den Zusammenhang sehen. Drück z.B. mal Strg + Maustaste auf dem Methodenaufruf, dann springt Delphi genau zu der Routine die dort aufgerufen wird.
In diesem Fall wird einfach das Clear der Klasse TM3UList aufgerufen. Diese Methode löscht alle Einträge der Liste und gibt den Speicher wieder frei. Bei einem Neuladen wird eben erstmal der eventuell verwendente Speicher freigegeben.
Ein Benutzer der Klasse kann diese also auch falsch einsetzen (eben nach einem Laden kein Clear aufrufen) trotzdem entsteht kein Speicherleck, die Klasse korrigiert diesen Programmiererfehler.

Nils_13 27. Sep 2008 22:44

Re: M3U
 
Oh, Entschuldigung, das hätte ich eigentlich sehen müssen. :oops:

Aber nochmal zu der Sache mit ??? - Bolero: Siehst du da irgendeinen Grund für ?

omata 27. Sep 2008 22:59

Re: M3U
 
Mein Beispiel liesst die Datei aber richtig ein?

Setz doch mal einen Breakpoint und werte deine Programmzeilen Zeile für Zeile aus. Dann wirst du irgendwo erkennen, das die Abarbeitung nicht genau das macht was du dir überlegt hast. Diese Stelle must du dann verstehen und ändern.

Nils_13 28. Sep 2008 18:21

Re: M3U
 
Unser Code ist identisch. Bloß handhabt Lazarus die Stringverarbeitung etwas anders: Man muss aufpassen, dass man WideStrings benutzt. Am Ende schadet ein AnsiToUTF8 nicht gerade und wenn man genau das getan hat, funktioniert alles.

Was sonst noch zu sagen ist: Danke für deine Hilfe!


Alle Zeitangaben in WEZ +1. Es ist jetzt 22:01 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz