Mehrere Bilder aus Audiodateien (mp3, wma, flac) lesen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Hazzard
User
Beiträge: 26
Registriert: Dienstag 4. Dezember 2007, 21:09

Ich will die Bilder (Cover etc) aus Audiodateien lesen. Dazu verwende ich die Mutagen Bibliothek.

Bei .flac Dateien ist das am einfachsten

Code: Alles auswählen

from mutagen.flac import FLAC, Picture as FLAC_Picture
picture_list = FLAC(filename).pictures
So bekomm ich eine Liste mit mutagen.flac.Picture Objekten die ich auch wunderbar auslesen kann

Bei .mp3 Dateien muss ich mir (soweit ich weiß) diese Liste erst selbe erstellen:

Code: Alles auswählen

from mutagen.mp3 import MP3
from mutagen.id3 import APIC

picture_list = []
tags = MP3(filename)
for tag in tags:
    if tag.startswith(u"APIC:"):
    picture_list.append(tags[tag])
Hier bekomm ich eine Liste mit mutagen.id3.APIC Objekten. Diese Objekte haben das Attribut ".desc" für Description (Beschreibung) um die verschiedenen Bilder auseinanderhalten zu können. Eine .mp3 Datei kann zb. die Tags APIC:FrontCover und APIC:BackCover enthalten. Wenn dieses desc Attribut vorhanden ist, hab ich auch keine Probleme die Bilder auszulesen. Wenn allerdings mehrere Tags mit der selben Beschreibung (oder ohne) vorhanden sind bekomm ich nur das erste Bild, alle anderen werden übergangen. Wenn ich allerdings das Tag dann lösch, sind alle Bilder mit dieser Beschreibung weg.
Diese .mp3 Datei bekommt man zb. wenn man mit der mp3tag software http://www.mp3tag.de mehrere Cover hinzufügt.

Lange Rede kurzer Sinn: Ich würde gerne alle Bilder auslesen können.

So und zum letzten Dateityp: .wma:
Hier weiß ich irgendwie gar nicht wie ich da an die Bilder bekomm. Ich weiß dass das Tag "WM/Picture" heißt. Wenn ich das Tag ausles bekomm ich aber nur ein rohen Bytestring und keine schönes Object wie bei den .mp3/.flac. Das Objekt bei mp3/flac bietet zb. neben den rohen Bilddaten ein Mimetyp Attribut und so an.

Ich hab bis jetzt auch noch keine ausführliche Dokumentation von Mutagen gefunden, wo man die Sachen nachlesen kann. Wenn sich also jemand damit auskennt oder ne gute Dokumentation kennt, würde ich mich sehr über eine Antwort freuen.

Viele Grüße, Hazzard
ms4py
User
Beiträge: 1178
Registriert: Montag 19. Januar 2009, 09:37

Analysiere den Byte-Stream doch einfach mal mit http://docs.python.org/library/imghdr.html#imghdr.what
(Um keine temporäre Datei zu erzeugen, kannst du außerdem StringIO verwenden)
„Lieber von den Richtigen kritisiert als von den Falschen gelobt werden.“
Gerhard Kocher

http://ms4py.org/
Hazzard
User
Beiträge: 26
Registriert: Dienstag 4. Dezember 2007, 21:09

Damit erkenne ich den Dateityp leider auch nicht. Da ist warscheinlich noch irgendein Header vor der Datei.
PappaBär83
User
Beiträge: 21
Registriert: Freitag 5. September 2014, 09:16

Hallo
Ich habe da ein Python3-Projekt und auf Code-Schnipsel-suche bin ich hier gelandet
Mir geht es erstmal darum ein bild aus der MP3 zu bekommen um es später in tkinter anzeigen zu lassen.
das mit dem StringIO hab ich ausprobiert. =>
"
Traceback (most recent call last):
File "ForumFrage.py", line 14, in <module>
versuch = StringIO(artwork)
TypeError: initial_value must be str or None, not bytes
"

Code: Alles auswählen

from mutagen import File
from mutagen.mp3 import MP3
from PIL import Image
from io import StringIO

fileext = '.mp3'
audiofile = 'Auf zum Sport.mp3'

audio = MP3(audiofile)
   
mytagstring = audio.pprint()   
if mytagstring.find('APIC') >= 0:
    artwork = audio.tags['APIC:'].data
    versuch = StringIO(artwork)                     
    dt = Image.open(versuch)            
    rgbimg = dt.convert('RGB')                             
    rgbimg = rgbimg.resize((100, 100), Image.ANTIALIAS)    
    rgbimg.show()
else:
    print ("wieder kein erfolg")
Ich weiß es kann wieder nur ein Anfängerfehler sein aber ich komm nicht weiter. :K
BlackJack

@PappaBär83: In Python 3 möchtest Du `BytesIO` statt `StringIO` verwenden denn die Bildaten sind ja Bytes und keine Zeichen(kette).
PappaBär83
User
Beiträge: 21
Registriert: Freitag 5. September 2014, 09:16

:idea: :D
Passt!!!

Code: Alles auswählen

from mutagen import File
from mutagen.mp3 import MP3
from PIL import Image
from io import BytesIO

fileext = '.mp3'
audiofile = 'Auf zum Sport.mp3'

audio = MP3(audiofile)
   
mytagstring = audio.pprint()   
if mytagstring.find('APIC') >= 0:
    artwork = audio.tags['APIC:'].data
    versuch = BytesIO(artwork)                     
    dt = Image.open(versuch)            
    rgbimg = dt.convert('RGB')                             
    rgbimg = rgbimg.resize((300, 300), Image.ANTIALIAS)    
    rgbimg.show()
else:
    print ("wieder kein erfolg")
Dank dir.
Das is das was ich jetzt nen Monat gesucht hab.
Fetzt, Tausend Dank und 3 Bier ;)
BlackJack

@PappaBär83: Anstelle von `find()` würde man den ``in``-Operator benutzen. Aber hier noch nicht einmal das, sondern man würde einfach die Ausnahme behandeln die man bekommt wenn das Tag 'APIC:' nicht vorhanden ist. Ungetestet:

Code: Alles auswählen

from io import BytesIO

from mutagen.mp3 import MP3
from PIL import Image

 
def main():
    filename = 'test.mp3'
     
    audio = MP3(filename)
    try:       
        artwork = audio.tags['APIC:'].data
    except KeyError:
        print('Kein Bild')
    else:
        image = (
            Image.open(BytesIO(artwork))
                .convert('RGB')
                .resize((300, 300), Image.ANTIALIAS)
        )
        image.show()
    

if __name__ == '__main__':
    main()
PappaBär83
User
Beiträge: 21
Registriert: Freitag 5. September 2014, 09:16

Das sieht natürlich auch sehr Edel aus. Werd ich bei Gelegenheit auch mal ausprobieren :arrow: feeback folgt
PappaBär83
User
Beiträge: 21
Registriert: Freitag 5. September 2014, 09:16

So Sieht das Funktionierende Code bei mir dann aus

Code: Alles auswählen

def Bild_aus_MP3(MP3Pfad, Länge, Höhe):
    audio = MP3(MP3Pfad)
    mytagstring = audio.pprint()   
    if mytagstring.find('APIC') >= 0:
        Bild = (Image.open(BytesIO(audio.tags['APIC:'].data))
            .convert('RGB')
            .resize((Länge, Höhe), Image.ANTIALIAS)
            .save("Fertig.gif"))
    else:
        print (MP3Pfad)
        print ("Kein bild in MP3")
        Bild = (Image.open(MP3Default)            
            .convert('RGB')
            .resize((Länge, Höhe), Image.ANTIALIAS)
            .save("Fertig.gif"))
"Später" dann wird -Fertig.gif- dann bei Tkinter als Image eingebunden.

Danke BlackJack
BlackJack

@PappaBär83: „Funktionierend” aber auch nur solange bis diese fehleranfällige Art zu prüfen ob es ein Bild gibt, nicht auf die Nase fällt. Die Teilzeichenkette 'APIC' kann da ja auch drin vorkommen ohne das es ein Tag mit dem Namen gibt. Warum nicht ganz normal den `KeyError` behandeln der auftritt wenn es den Tagnamen nicht gibt‽

Der Code in den beiden Zweigen ist auch fast identisch — das sollte man dort heraus ziehen.

Die Namen halten sich so gar nicht an den Style Guide for Python Code. Aus dem Funktionsnamen ist nicht ersichtlich das der eine Bilddatei speichert. Wobei: Muss das denn überhaupt gespeichert werden? Und dann noch unter einem ”konstanten”, hart kodierten Namen, der dann auch noch zweimal literal im Quelltext steht (was man durch das herausziehen des redundanten Codes aus den beiden Zweigen zumindest auf einmal reduzieren kann).

GIF ist ja auch ein Anachronismus. Vor allem bei Coverbildern möchte man sich doch nicht unnötig auf eine 256-Farben-Palette beschränken.

Edit: Ungetestet:

Code: Alles auswählen

def save_cover_image(mp3_filename, size, cover_filename='cover.gif'):
    mp3 = MP3(mp3_filename)
    try:
        image_tag = mp3.tags['APIC:']
    except KeyError:
        image = Image.open(DEFAULT_COVER_IMAGE_FILENAME)
    else:
        image = Image.open(BytesIO(image_tag.data))
    image.convert('RGB').resize(size, Image.ANTIALIAS).save(cover_filename)
Antworten