quodlibet / mutagen

Python module for handling audio metadata

Home Page:https://mutagen.readthedocs.io

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

mutagen - 1.47.0 - Error with reading FLAC tags and values

jmac0001 opened this issue · comments

This code caused an error with a flac file that had tags.

import mutagen
from mutagen.flac import FLAC

if file_ext == 'flac':
track = FLAC(track_file)
...
if hasattr(track[tag_name], 'text'):

_vorbis.py", line 237, in getitem
key = key.lower()
AttributeError: 'tuple' object has no attribute 'lower'

I changed this at line 237:
key = key.lower()

to this:

key = key[0].lower()

It seems to work as expected so far. I'm still developing.

Looks like tag_name is not a string as would be expected. Please check the value and type of tag_name.

Looks like tag_name is not a string as would be expected. Please check the value and type of tag_name.

I will reply later today or tomorrow with more examples. In short, the same code works for mp4 and mp3, but there something different about the type retuned by flac. I would think the type retuned (string) would be uniformed between the formats, but not the actual keys and values.

Your code example does not show where tag_name is coming from. But for your code this is expected to be a string.

Also looking at if hasattr(track[tag_name], 'text') this does not make much sense for Vorbis. For Vorbis the keys are strings and the values are lists of strings. So the check above would be always false.

The check makes sense for ID3, where the values are a subclass of mutagen.id3.Frame.

Also looking at if hasattr(track[tag_name], 'text') this does not make much sense for Vorbis. For Vorbis the keys are strings and the values are lists of strings. So the check above would be always false.

The check makes sense for ID3, where the values are a subclass of mutagen.id3.Frame.

I'm probably not using the tool right, but this is how I'm gathering tags and values:

for track_file in track_files:
tag_names = []

id3_keys = mutagen.File(track_file).keys()
file_ext = track_file.split('.')[-1].lower()
print(file_ext)

if file_ext == 'mp3':
    track = ID3(track_file)

if file_ext == 'm4a':
    track = MP4(track_file)

if file_ext == 'mp4':
    track = MP4(track_file)

if file_ext == 'flac':
    track = FLAC(track_file)


for id3_key in id3_keys:
    
    tag_names = mutagen.File(track_file).tags
    
    for tag_name in tag_names:
        log(color='blue',msg =tag_name)
        
        if hasattr(track[tag_name], 'text'):

            tag_values = track[tag_name].text
            for tag_value in tag_values:

There are a couple of things here. In general the tags attribute is not the same across all tag formats. If you access file.tags for MP4 or ID3 it is a dictionary. For Vorbis it is a list of (key, value) tuples. You can get around this by using file.tags.items, this will always return tuples. Or you use

Also the values can be different. For Vorbis tags it is always a list of strings. For ID3 it is an instance of mutagen.id3.Frame, with different frame types having different values. Your code above seems to be interested in text values only.

Also you don't need to use all the different formats directly, you can use mutagen.File to load the various formats. Your code currently loads the tags twice.

Something like this might get you started:

f = mutagen.File(track_file)

for tag, values in f.tags.items():
    print("%s = %r" % (tag, values))

    if isinstance(values, mutagen.id3.Frame):
        if hasattr(values, 'text'):
            print("ID3 text values: %r" % values.text)

        # Handle other tags here

    else:
        # Handle other values here
        pass

You really need to handle the type of values, though. Only for Vorbis tags you always get a list of strings. For MP4 it is often a list of strings, but some tags are integers (e.g. disc no.), for some there are specific classes (e.g. for cover art), as MP4 has specific binary formats for different types of tags. For ASF there are specific types again.

In the end you might need to handle things differently based on the type of the file f in general, as both tag names and value types will be specific to the tag format. mutagen itself really allows you to add format specific details.

If you are looking for an abstraction over this with an API to treat all formats the same have a look at https://github.com/beetbox/mediafile , which adds such an abstraction on top of mutagen. But be aware that such an abstraction is always opinionated to some degree, mapping specific tag names to the proper tags in the different formats.

Thanks for the pointers. It looks like I was trying to re-invent the wheel for a wrapper for this part of my project. The other parts of mutagen have worked well for my code so far. I'll check out MediaFile. It probably has most of what I need.