[Tutor] How to map different keys together ?

Damon Timm damontimm at gmail.com
Sun Apr 18 19:42:46 CEST 2010


Hi Alan, et al - thanks for your response and your ideas.  I sat down
and did a little more coding so that I might tackle what I can and
bring back where I am having trouble.  I have implemented the basic
'tag_map' you suggested without a hitch using my own class and
getitem/setitem builtins.  Right now, if there is a one-to-one
correlation between my *generic* key (as a standard between my music
files) and the specific tag for the filetype, I am doing well.

However, everything is not so clear cut in the world of metatagging!
My first stumbling block is that M4A files store the track number and
track total in a single tuple ... but I need them as separate fields
(which is how some of the other formats do it).  This is going to be
one of many hurdles -- I need a way to accomplish more than a
one-to-one data map.

See my code, below, as well as some command line examples where I am
having trouble.  I feel there may be a way to pass functions through
my tag_map dictionary (maybe a lambda?!) but I can't get my head
around what approach is best (I can't think of any approach, right
now, actually).

Code follows. Thanks again.

class Tags(object):
    '''Wrapper class for a mutagen music file object.'''
    tag_map = {}

    def __init__(self, mutagen):
        self._mutagen = mutagen
        self.tags = {}

    def keys(self):
        '''Get list of generic tag keys in use'''
        keys = []
        for k in self.tag_map.keys():
            try:
                self._mutagen[self.tag_map[k]]
                keys.append(k)
            except KeyError:
                pass

        return keys

    def save(self):
        '''Save the mutagen changes.'''
        self._mutagen.save()

class MP4Tags(Tags):
    tag_map = {
    #   GENERIC         : SPECIFIC
        'title'         : '\xa9nam',
        'album'         : '\xa9alb',
        'artist'        : '\xa9ART',
        'albumartist'   : 'aART',
        'comment'       : '\xa9cmt',
        'compilation'   : 'cpil',
        'composer'      : '\xa9wrt',
        'genre'         : '\xa9gen',
        'discnumber'    : 'disk', # returns: (2,10) need lmbda or something ?!
        'disctotal'     : 'disk', # returns: (2,10) need lmbda or something ?!
        'year'          : '\xa9day',
        'tracknumber'   : 'trkn', # returns: (2,10) need lmbda or something ?!
        'tracktotal'    : 'trkn'  # returns: (2,10) need lmbda or something ?!

    }

    def __getitem__(self, key):
        try:
            return self._mutagen[self.tag_map[key]]
        except KeyError:
            pass

    def __setitem__(self, key, value):
        self._mutagen[self.tag_map[key]] = value

#EOF

**Here is how it works:

>>> import tagging
>>> from mutagen.mp4 import MP4
>>> mp4 = MP4('../tests/data/Compressed/M4A-256.m4a')
>>> mp4_tags = tagging.MP4Tags(mp4)
>>> mp4_tags['title']
[u'bob the builder']  # woo hoo! it works!
>>> mp4_tags['title'] = [u'I can change the title!']
>>> mp4_tags['title']
[u'I can change the title!']  # changing the titles works too
>>> mp4_tags['discnumber']
[(1, 1)]  # TODO - I need to return disk[0][0] ... not the tuple
>>> mp4_tags.save()

So, I need to modify how the data is shown to me as well as how I
would go about writing the data something like:

return_tag(disk): return disk[0][0]
save_tag(num): return [(%s, %s)] % ( num,
somehow_get_the_original_second_value_before_re_saving)

Thanks again and any advice or guidance about how to approach this is
greatly appreciated.

Damon



On Sat, Apr 17, 2010 at 3:55 PM, Alan Gauld <alan.gauld at btinternet.com> wrote:
>
> "Damon Timm" <damontimm at gmail.com> wrote
>
>> I am struggling, on a theoretical level, on how to map the various
>> filetype's tag/key naming conventions.  I have already created my own
>> MusicFile objects to handle each filetype (with functions for
>> encoding/decoding) but I can't wrap my head around how to map the
>> tags.
>
> I'd define a mapping table per file type that maps from a standad
> set of keys to the file specific tag.
>
> Then define a class that works with the generic tags.
> You can then eirther subclass per file type and use the file specific
> mapping(a class variable) to translate internally or just create a function
> that takes the file mapping as a parameter(maybe in the init() ) and sets
> it up for the generic methods to use.
>
>> And here is what I would need to do to find the song's TITLE text:
>>
>>>>> flac['title']
>>
>> [u'Christmas Waltz']
>>>>>
>>>>> mp3['TIT2'].text  #notice this one takes another additional step, as
>>>>> well, by specifying text !
>>
>> [u'Christmas Waltz']
>>>>>
>>>>> mp4['\xa9nam']
>>
>> [u"Christmas Waltz"]
>
> So if the file is mp3 the generic
>
> foo.setTag('title')
>
> will inside the setTag method do
>
> def setTag(self, tag, value)
>      key = self.mapping[tag]   # get file specific tag
>      fileData[key] = value       # and use it
>      # or
>      self.value = fileData[key]
>
>> approach this.  I know I could manually assign each key to a class
>> property (using the @property tag) ... but this seems tedious:
>
> That would definitely be overklilll and not bvery flexible for adding new
> file types.
>
> HTH,
>
>
> --
> Alan Gauld
> Author of the Learn to Program web site
> http://www.alan-g.me.uk/
>
> _______________________________________________
> Tutor maillist  -  Tutor at python.org
> To unsubscribe or change subscription options:
> http://mail.python.org/mailman/listinfo/tutor
>


More information about the Tutor mailing list