Author: dmeyer Date: Wed Dec 6 20:52:29 2006 New Revision: 2160 Modified: trunk/metadata/TODO trunk/metadata/bin/mminfo trunk/metadata/src/__init__.py trunk/metadata/src/audio/eyed3info.py trunk/metadata/src/audio/flacinfo.py trunk/metadata/src/audio/ogginfo.py trunk/metadata/src/audio/webradioinfo.py trunk/metadata/src/disc/discinfo.py trunk/metadata/src/disc/dvdinfo.py trunk/metadata/src/factory.py trunk/metadata/src/fourcc.py trunk/metadata/src/image/IPTC.py trunk/metadata/src/image/core.py trunk/metadata/src/image/jpginfo.py trunk/metadata/src/image/pnginfo.py trunk/metadata/src/image/tiffinfo.py trunk/metadata/src/mediainfo.py trunk/metadata/src/misc/dirinfo.py trunk/metadata/src/video/asfinfo.py trunk/metadata/src/video/mkvinfo.py trunk/metadata/src/video/movinfo.py trunk/metadata/src/video/mpeginfo.py trunk/metadata/src/video/ogminfo.py trunk/metadata/src/video/riffinfo.py
Log: Major cleanup of the code, it was way to ugly. The __dict__ access is now gone, all attributes are class member functions. Use the table code when possible to avoid duplicate code. It is also possible now to convert the MediaInfo to a dict and back. Modified: trunk/metadata/TODO ============================================================================== --- trunk/metadata/TODO (original) +++ trunk/metadata/TODO Wed Dec 6 20:52:29 2006 @@ -21,13 +21,6 @@ Check attributes. Some are very similar and could be merged -Cleanup stuff about what is stored in the __dict__ and what is a -memeber variable. OK, after some thinking: MediaInfo should inherit -from dict. That is the only way we can support attributes with -whitespaces, etc. No more self.length, it is self['length'] in that -case. That means all parser have to be updated, maybe temp. in -WIP/metadata until it is done. THIS NEEDS CLEANUP! - If possible, attributes should match http://www.freedesktop.org/wiki/Standards_2fshared_2dfilemetadata_2dspec and mime type should match Modified: trunk/metadata/bin/mminfo ============================================================================== --- trunk/metadata/bin/mminfo (original) +++ trunk/metadata/bin/mminfo Wed Dec 6 20:52:29 2006 @@ -92,7 +92,7 @@ if len(file) > 70: print "filename : %s[...]%s" % (file[:30], file[len(file)-30:]) else: - print "filename : %s" % file + print " filename: %s" % file if medium: print medium print Modified: trunk/metadata/src/__init__.py ============================================================================== --- trunk/metadata/src/__init__.py (original) +++ trunk/metadata/src/__init__.py Wed Dec 6 20:52:29 2006 @@ -39,6 +39,7 @@ # import factory code for kaa.metadata access from factory import * from disc.discinfo import cdrom_disc_id as getid +from mediainfo import MediaInfo # use network functions USE_NETWORK = 1 Modified: trunk/metadata/src/audio/eyed3info.py ============================================================================== --- trunk/metadata/src/audio/eyed3info.py (original) +++ trunk/metadata/src/audio/eyed3info.py Wed Dec 6 20:52:29 2006 @@ -186,7 +186,7 @@ tab[f.header.id] = f else: log.debug(f.__class__) - self.appendtable('id3v2', tab, 'en') + self._appendtable('id3v2', tab) if id3.tag.frames['TCON']: genre = None @@ -204,10 +204,10 @@ except KeyError: self.genre = str(genre) # and some tools store it as trackno/trackof in TRCK - if not self['trackof'] and self['trackno'] and \ - self['trackno'].find('/') > 0: - self['trackof'] = self['trackno'][self['trackno'].find('/')+1:] - self['trackno'] = self['trackno'][:self['trackno'].find('/')] + if not self.trackof and self.trackno and \ + self.trackno.find('/') > 0: + self.trackof = self.trackno[self.trackno.find('/')+1:] + self.trackno = self.trackno[:self.trackno.find('/')] if id3: self.length = id3.getPlayTime() except (KeyboardInterrupt, SystemExit): @@ -297,8 +297,7 @@ if self.bitrate is None or self.samplerate is None: return - self.mode = _modes[mode] - self.keys.append('mode') + self._set('mode', _modes[mode]) factory.register( 'audio/mp3', ('mp3',), mediainfo.TYPE_MUSIC, eyeD3Info ) Modified: trunk/metadata/src/audio/flacinfo.py ============================================================================== --- trunk/metadata/src/audio/flacinfo.py (original) +++ trunk/metadata/src/audio/flacinfo.py Wed Dec 6 20:52:29 2006 @@ -101,7 +101,7 @@ if header.has_key('TRACKNUMBER'): self.trackno = header['TRACKNUMBER'] - self.appendtable('VORBISCOMMENT', header) + self._appendtable('VORBISCOMMENT', header) elif type == 5: # CUESHEET pass Modified: trunk/metadata/src/audio/ogginfo.py ============================================================================== --- trunk/metadata/src/audio/ogginfo.py (original) +++ trunk/metadata/src/audio/ogginfo.py Wed Dec 6 20:52:29 2006 @@ -105,7 +105,7 @@ self.type = 'OGG Vorbis' self.subtype = '' self.length = self._calculateTrackLength(file) - self.appendtable('VORBISCOMMENT',header) + self._appendtable('VORBISCOMMENT',header) def _extractHeaderString(self,f): Modified: trunk/metadata/src/audio/webradioinfo.py ============================================================================== --- trunk/metadata/src/audio/webradioinfo.py (original) +++ trunk/metadata/src/audio/webradioinfo.py Wed Dec 6 20:52:29 2006 @@ -40,13 +40,16 @@ # http://205.188.209.193:80/stream/1006 -ICY_tags = { 'title': 'icy-name', - 'genre': 'icy-genre', - 'bitrate': 'icy-br', - 'caption': 'icy-url', - } +ICY = { 'icy-name': 'title', + 'icy-genre': 'genre', + 'icy-br': 'bitrate', + 'icy-url': 'caption' + } class WebRadioInfo(mediainfo.MusicInfo): + + table_mapping = { 'ICY' : ICY } + def __init__(self, url): mediainfo.MusicInfo.__init__(self) tup = urlparse.urlsplit(url) @@ -88,13 +91,11 @@ tab[icyline[:cidx].strip()] = icyline[cidx+2:].strip() if fi: fi.close() - self.appendtable('ICY', tab) - self.tag_map = { ('ICY', 'en') : ICY_tags } - # Copy Metadata from tables into the main set of attributes - for k in self.tag_map.keys(): - map(lambda x:self.setitem(x,self.gettable(k[0],k[1]), - self.tag_map[k][x]), - self.tag_map[k].keys()) + self._appendtable('ICY', tab) + + + def _finalize(self): + mediainfo.MusicInfo._finalize(self) self.bitrate = string.atoi(self.bitrate)*1000 Modified: trunk/metadata/src/disc/discinfo.py ============================================================================== --- trunk/metadata/src/disc/discinfo.py (original) +++ trunk/metadata/src/disc/discinfo.py Wed Dec 6 20:52:29 2006 @@ -209,11 +209,13 @@ class DiscInfo(mediainfo.CollectionInfo): + + _keys = mediainfo.CollectionInfo._keys + [ 'mixed', 'label' ] + def isDisc(self, device): (type, self.id) = cdrom_disc_id(device, handle_mix=1) if type != 2: if type == 4: - self.keys.append('mixed') self.mixed = 1 type = 1 return type @@ -229,5 +231,4 @@ else: self.label = self.id[16:] - self.keys.append('label') return type Modified: trunk/metadata/src/disc/dvdinfo.py ============================================================================== --- trunk/metadata/src/disc/dvdinfo.py (original) +++ trunk/metadata/src/disc/dvdinfo.py Wed Dec 6 20:52:29 2006 @@ -71,13 +71,13 @@ class DVDTitle(mediainfo.AVInfo): + + _keys = mediainfo.AVInfo._keys + [ 'chapters', 'subtitles', 'angles' ] + def __init__(self, info): mediainfo.AVInfo.__init__(self) self.chapters = info[0] self.angles = info[1] - self.keys.append('chapters') - self.keys.append('subtitles') - self.keys.append('angles') self.mime = 'video/mpeg' self.video.append(DVDVideo(info[2:8])) @@ -91,6 +91,9 @@ class DVDInfo(DiscInfo): + + _keys = DiscInfo._keys + [ 'length' ] + def __init__(self, device): DiscInfo.__init__(self) self.context = 'video' @@ -103,7 +106,6 @@ else: self.parseDisc(device) - self.keys.append('length') self.length = 0 first = 0 @@ -129,7 +131,7 @@ ti = DVDTitle(title) ti.trackno = pos + 1 ti.trackof = len(info) - self.appendtrack(ti) + self.tracks.append(ti) def parseDVDdir(self, dirname): Modified: trunk/metadata/src/factory.py ============================================================================== --- trunk/metadata/src/factory.py (original) +++ trunk/metadata/src/factory.py Wed Dec 6 20:52:29 2006 @@ -79,25 +79,15 @@ return Factory().get(mimetype,extensions) -if TIME_DEBUG: - import time +def parse(filename, force=True): + """ + parse a file + """ + result = Factory().create(filename, force) + if result: + result._finalize() + return result - def parse(filename, force=True): - """ - parse a file - """ - t1 = time.time() - result = Factory().create(filename, force) - t2 = time.time() - log.info('%s took %s seconds' % (filename, (t2-t1))) - return result -else: - def parse(filename, force=True): - """ - parse a file - """ - return Factory().create(filename, force) - class _Factory: """ @@ -194,7 +184,7 @@ if not force: log.info('No Type found by Extension. Give up') return None - + log.info('No Type found by Extension. Trying all') for e in self.types: @@ -276,7 +266,6 @@ f.close() if r: r.url = 'file://%s' % os.path.abspath(filename) - r.correct_data() return r return None Modified: trunk/metadata/src/fourcc.py ============================================================================== --- trunk/metadata/src/fourcc.py (original) +++ trunk/metadata/src/fourcc.py Wed Dec 6 20:52:29 2006 @@ -6,8 +6,8 @@ """ if isinstance(code, (int, long)): if code in TWOCC: - return u'0x%x' % code, unicode(TWOCC[code]) - return u'0x%x' % code, u'Unknown' + return u'0x%04x' % code, unicode(TWOCC[code]) + return u'0x%04x' % code, u'Unknown' if code.upper() in FOURCC: return unicode(code.upper()), unicode(FOURCC[code.upper()]) if code.upper().startswith('MS'): @@ -16,8 +16,8 @@ return unicode(code), u'Unknown' code = (ord(code[0]) << 8) + ord(code[1]) if code in TWOCC: - return u'0x%x' % code, unicode(TWOCC[code]) - return u'0x%x' % code, u'Unknown' + return u'0x%04x' % code, unicode(TWOCC[code]) + return u'0x%04x' % code, u'Unknown' TWOCC = { Modified: trunk/metadata/src/image/IPTC.py ============================================================================== --- trunk/metadata/src/image/IPTC.py (original) +++ trunk/metadata/src/image/IPTC.py Wed Dec 6 20:52:29 2006 @@ -36,6 +36,18 @@ from struct import unpack from kaa.strutils import str_to_unicode +mapping = { + 'by-line title': 'title', + 'headline': 'title', + 'date created': 'date', + 'keywords': 'keywords', + 'writer/editor': 'artist', + 'credit': 'artist', + 'country/primary location name': 'country', + 'caption/abstract': 'caption', + 'city': 'city' +} + # These names match the codes defined in ITPC's IIM record 2. # copied from iptcinfo by Josh Carter, [EMAIL PROTECTED] c_datasets = { Modified: trunk/metadata/src/image/core.py ============================================================================== --- trunk/metadata/src/image/core.py (original) +++ trunk/metadata/src/image/core.py Wed Dec 6 20:52:29 2006 @@ -45,28 +45,24 @@ # attributes for image files ATTRIBUTES = ['description', 'people', 'location', 'event', 'width', 'height', - 'thumbnail','software','hardware', 'dpi', 'city', 'rotation'] + 'thumbnail','software','hardware', 'dpi', 'city', 'rotation' ] class ImageInfo(mediainfo.MediaInfo): """ Digital Images, Photos, Pictures. """ - def __init__(self): - mediainfo.MediaInfo.__init__(self) - for k in ATTRIBUTES: - setattr(self,k,None) - self.keys.append(k) + _keys = ATTRIBUTES - def correct_data(self): + def _finalize(self): """ Add additional information and correct data. FIXME: parse_external_files here is very wrong """ if self.url and self.url.startswith('file://'): self.parse_external_files(self.url[7:]) - mediainfo.MediaInfo.correct_data(self) + mediainfo.MediaInfo._finalize(self) def parse_external_files(self, filename): @@ -94,10 +90,7 @@ key = str(child.getattr('name')) if not key or not child.content: continue - self[key] = child.content - if not key in ATTRIBUTES + mediainfo.MEDIACORE: - # if it's in desc it must be important - self.keys.append(key) + self._set(key, child.content) def parse_dot_comment(self, filename): Modified: trunk/metadata/src/image/jpginfo.py ============================================================================== --- trunk/metadata/src/image/jpginfo.py (original) +++ trunk/metadata/src/image/jpginfo.py Wed Dec 6 20:52:29 2006 @@ -66,8 +66,17 @@ 0xCF : "Differential lossless, arithmetic coding", } +EXIFMap = { + 'Image DateTime': 'date', + 'Image Artist': 'artist', + 'Image Model': 'hardware', + 'Image Software': 'software', +} + class JPGInfo(core.ImageInfo): + table_mapping = { 'EXIF': EXIFMap, 'IPTC': IPTC.mapping } + def __init__(self,file): core.ImageInfo.__init__(self) self.mime = 'image/jpeg' @@ -85,7 +94,7 @@ file.seek(2) app = file.read(4) self.meta = {} - + while (len(app) == 4): (ff,segtype,seglen) = struct.unpack(">BBH", app) if ff != 0xff: break @@ -111,12 +120,10 @@ exif = EXIF.process_file(fakefile) fakefile.close() if exif: - self.setitem( 'date', exif, 'Image DateTime', True) - self.setitem( 'artist', exif, 'Image Artist', True) - self.setitem( 'hardware', exif, 'Image Model', True) - self.setitem( 'software', exif, 'Image Software', True) - self.setitem( 'thumbnail', exif, 'JPEGThumbnail', True) - self.appendtable( 'EXIF', exif ) + self.thumbnail = exif.get('JPEGThumbnail', None) + if self.thumbnail: + self.thumbnail = str(self.thumbnail) + self._appendtable('EXIF', exif) if 'Image Orientation' in exif: orientation = str(exif['Image Orientation']) @@ -136,16 +143,7 @@ elif segtype == 0xed: iptc = IPTC.parseiptc(file.read(seglen-2)) if iptc: - self.setitem( 'title', iptc, 'by-line title') - self.setitem( 'title', iptc, 'headline') - self.setitem( 'date' , iptc, 'date created') - self.setitem( 'keywords', iptc, 'keywords') - self.setitem( 'artist', iptc, 'writer/editor') - self.setitem( 'artist', iptc, 'credit') - self.setitem( 'country', iptc, 'country/primary location name') - self.setitem( 'caption', iptc, 'caption/abstract') - self.setitem( 'city', iptc, 'city') - self.appendtable( 'IPTC', iptc ) + self._appendtable('IPTC', iptc) elif segtype == 0xe7: # information created by libs like epeg @@ -172,12 +170,10 @@ app = file.read(4) if len(self.meta.keys()): - self.appendtable( 'JPGMETA', self.meta ) + self._appendtable( 'JPGMETA', self.meta ) for key, value in self.meta.items(): if key.startswith('Thumb:') or key == 'Software': - setattr(self, key, value) - if not key in self.keys: - self.keys.append(key) - + self._set(key, value) + factory.register( 'image/jpeg', ('jpg','jpeg'), mediainfo.TYPE_IMAGE, JPGInfo ) Modified: trunk/metadata/src/image/pnginfo.py ============================================================================== --- trunk/metadata/src/image/pnginfo.py (original) +++ trunk/metadata/src/image/pnginfo.py Wed Dec 6 20:52:29 2006 @@ -71,12 +71,10 @@ while self._readChunk(file): pass if len(self.meta.keys()): - self.appendtable( 'PNGMETA', self.meta ) + self._appendtable( 'PNGMETA', self.meta ) for key, value in self.meta.items(): if key.startswith('Thumb:') or key == 'Software': - setattr(self, key, value) - if not key in self.keys: - self.keys.append(key) + self._set(key, value) def _readChunk(self,file): Modified: trunk/metadata/src/image/tiffinfo.py ============================================================================== --- trunk/metadata/src/image/tiffinfo.py (original) +++ trunk/metadata/src/image/tiffinfo.py Wed Dec 6 20:52:29 2006 @@ -54,6 +54,8 @@ class TIFFInfo(core.ImageInfo): + table_mapping = { 'IPTC': IPTC.mapping } + def __init__(self,file): core.ImageInfo.__init__(self) self.iptc = None @@ -112,16 +114,7 @@ raise mediainfo.KaaMetadataParseError() if iptc: - self.setitem( 'title', iptc, 'by-line title') - self.setitem( 'title', iptc, 'headline') - self.setitem( 'date' , iptc, 'date created') - self.setitem( 'keywords', iptc, 'keywords') - self.setitem( 'artist', iptc, 'writer/editor') - self.setitem( 'artist', iptc, 'credit') - self.setitem( 'country', iptc, 'country/primary location name') - self.setitem( 'caption', iptc, 'caption/abstract') - self.setitem( 'city', iptc, 'city') - self.appendtable( 'IPTC', iptc ) + self._appendtable('IPTC', iptc) factory.register( 'image/tiff', ('tif','tiff'), mediainfo.TYPE_IMAGE, TIFFInfo) Modified: trunk/metadata/src/mediainfo.py ============================================================================== --- trunk/metadata/src/mediainfo.py (original) +++ trunk/metadata/src/mediainfo.py Wed Dec 6 20:52:29 2006 @@ -53,33 +53,20 @@ TYPE_HYPERTEXT = 8 TYPE_MISC = 10 -MEDIACORE = ['title', 'caption', 'comment', 'artist', 'size', 'type', - 'subtype', 'date', 'keywords', 'country', 'language', 'url'] +MEDIACORE = ['title', 'caption', 'comment', 'size', 'type', 'subtype', 'date', + 'keywords', 'country', 'language', 'url', 'media', 'artist'] AUDIOCORE = ['channels', 'samplerate', 'length', 'encoder', 'codec', 'format', - 'samplebits', 'bitrate' ] + 'samplebits', 'bitrate', 'fourcc' ] VIDEOCORE = ['length', 'encoder', 'bitrate', 'samplerate', 'codec', 'format', - 'samplebits', 'width', 'height', 'fps', 'aspect', 'trackno' ] + 'samplebits', 'width', 'height', 'fps', 'aspect', 'trackno', 'fourcc' ] -MUSICCORE = ['trackno', 'trackof', 'album', 'genre','discs', 'thumbnail' ] - -# AVCORE is such a funny list. Who created it? I don't see any parser -# that could ever fill out stuff like 'production designer'. Making this -# list smaller would make kaa.metadata faster. +MUSICCORE = ['trackno', 'trackof', 'album', 'genre', 'discs', 'thumbnail' ] AVCORE = ['length', 'encoder', 'trackno', 'trackof', 'copyright', 'product', - 'genre', 'secondary genre', 'subject', 'writer', 'producer', - 'cinematographer', 'production designer', 'edited by', - 'costume designer', 'music by', 'studio', 'distributed by', - 'rating', 'starring', 'ripped by', 'digitizing date', - 'internet address', 'source form', 'medium', 'source', - 'archival location', 'commisioned by', 'engineer', 'cropped', - 'sharpness', 'dimensions', 'lightness', 'dots per inch', - 'palette setting', 'default audio stream', 'logo url', - 'watermark url', 'info url', 'banner image', 'banner url', - 'infotext', 'delay', 'image' ] - + 'genre', 'writer', 'producer', 'studio', 'rating', 'starring', + 'delay', 'image', 'video', 'audio', 'subtitles', 'chapters' ] EXTENSION_DEVICE = 'device' EXTENSION_DIRECTORY = 'directory' @@ -92,51 +79,67 @@ class KaaMetadataParseError: pass -class MediaInfo: + +class MediaInfo(object): """ MediaInfo is the base class to all Media Metadata Containers. It defines the basic structures that handle metadata. MediaInfo and its derivates contain a common set of metadata attributes that is listed in keys. Specific derivates contain additional keys to the dublin core set that is defined in MediaInfo. - MediaInfo also contains tables of addional metadata. These tables are maps - of keys to values. The keys themselves should remain in the format that is - defined by the metadata (I.E. Hex-Numbers, FOURCC, ...) and will be - translated to more readable and i18nified values by an external entity. """ - def __init__(self): - self.keys = [] + _keys = MEDIACORE + table_mapping = {} + + def __init__(self, hash=None): + if hash is not None: + # create mediainfo based on dict + for key, value in hash.items(): + if isinstance(value, list) and value and isinstance(value[0], dict): + value = [ MediaInfo(x) for x in value ] + self._set(key, value) + return + + self._keys = self._keys[:] self._tables = {} - for k in MEDIACORE: - setattr(self,k,None) - self.keys.append(k) - # get media type by parsing the __class__ information - media = str(self.__class__) - media = media[media.find('kaa.metadata.') + 13:] - self.media = media[:media.find('.')] - self.keys.append('media') + for key in self._keys: + setattr(self, key, None) + + # + # unicode and string convertion for debugging + # def __unicode__(self): - keys = copy.copy(self.keys) - hidden = [] - for k in UNPRINTABLE_KEYS: - if k in keys: - keys.remove(k) - hidden.append(k) - - result = reduce( lambda a,b: self[b] and b != u'url' and \ - u'%s\n %s: %s' % \ - (a, unicode(b), unicode(self[b])) or a, keys, u'' ) - for h in hidden: - if self[h]: - result += u'\n %s: <unprintable data>' % h - if log.level < 30: + result = u'' + + # print normal attributes + lists = [] + for key in self._keys: + value = getattr(self, key, None) + if value == None or key == 'url': + continue + if isinstance(value, list): + if value: + lists.append((key, value)) + continue + if key in UNPRINTABLE_KEYS: + value = '<unprintable data>' + result += u' %10s: %s\n' % (unicode(key), unicode(value)) + + # print lists + for key, l in lists: + result += u'\n %5s list:' % key + for item in l: + result += '\n ' + unicode(item).replace('\n', '\n ') + u'\n' + + # print tables + if log.level >= 10: for name, table in self._tables.items(): result += '\n\n Table %s' % str(name) for key, value in table.items(): try: - value = unicode(value) + value = unicode(value) if len(value) > 50: value = '<unprintable data>' except UnicodeDecodeError: @@ -148,62 +151,84 @@ def __str__(self): return unicode_to_str(unicode(self)) - - def appendtable(self, name, hashmap, language='en'): + + def __repr__(self): + return '<%s %s>' % (str(self.__class__)[8:-2], self.url) + + + # + # internal functions + # + + def _appendtable(self, name, hashmap): """ Appends a tables of additional metadata to the Object. If such a table already exists, the given tables items are added to the existing one. """ - if not self._tables.has_key((name, language)): - self._tables[(name, language)] = hashmap + if not self._tables.has_key(name): + self._tables[name] = hashmap else: # Append to the already existing table for k in hashmap.keys(): - self._tables[(name, language)][k] = hashmap[k] + self._tables[name][k] = hashmap[k] - def correct_data(self): + def _set(self, key, value): + """ + Set key to value and add the key to the internal keys list if + missing. + """ + if value is None and getattr(self, key, None) is None: + return + if isinstance(value, str): + value = str_to_unicode(value) + setattr(self, key, value) + if not key in self._keys: + self._keys.append(key) + + + def _finalize(self): """ Correct same data based on specific rules """ # make sure all strings are unicode - for key in self.keys: + for key in self._keys: if key in UNPRINTABLE_KEYS: continue value = getattr(self, key) + if value is None: + continue if isinstance(value, str): setattr(self, key, str_to_unicode(value)) if isinstance(value, unicode): setattr(self, key, value.strip().rstrip().replace(u'\0', u'')) - - - def gettable(self, name, language='en'): - """ - returns a table of the given name and language - """ - return self._tables.get((name, language), {}) - - - def setitem(self, item, dict, key, convert_to_unicode=False): - """ - Set item to a specific value for the dict. - """ - value = dict.get(key) - if not value: - return - if isinstance(value, str): - value = str_to_unicode(value) - elif convert_to_unicode and not isinstance(value, unicode): - value = str_to_unicode(str(value)) - self.__dict__[item] = value - + if isinstance(value, list) and value and isinstance(value[0], MediaInfo): + for submenu in value: + submenu._finalize() + + # copy needed tags from tables + for name, table in self._tables.items(): + mapping = self.table_mapping.get(name, {}) + for tag, attr in mapping.items(): + value = table.get(tag, None) + if value is not None: + if not isinstance(value, (str, unicode)): + value = unicode(str(value)) + elif isinstance(value, str): + value = str_to_unicode(value) + value = value.strip().rstrip().replace(u'\0', u'') + setattr(self, attr, value) + + # + # data access + # def __contains__(self, key): """ Test if key exists in the dict """ - return key in self.__dict__ + return hasattr(self, key) def get(self, key, default = None): @@ -211,94 +236,79 @@ Returns key in dict, otherwise defaults to 'default' if key doesn't exist. """ - if key not in self: - return default - return self[key] + return getattr(self, key, default) - def __getitem__(self,key): + def __getitem__(self, key): """ get the value of 'key' """ - if self.__dict__.has_key(key): - return self.__dict__[key] - elif hasattr(self, key): - return getattr(self, key) - return None + return getattr(self, key, None) - def __setitem__(self, key, val): + def __setitem__(self, key, value): """ - set the value of 'key' to 'val' + set the value of 'key' to 'value' """ - self.__dict__[key] = val + setattr(self, key, value) def has_key(self, key): """ check if the object has a key 'key' """ - return self.__dict__.has_key(key) or hasattr(self, key) + return hasattr(self, key) - def __delitem__(self, key): + def convert(self): """ - delete informations about 'key' + Convert mediainfo to dict. """ - try: - del self.__dict__[key] - except (KeyboardInterrupt, SystemExit): - sys.exit(0) - except: - pass - if hasattr(self, key): - setattr(self, key, None) + result = {} + for k in self._keys: + value = getattr(self, k, None) + if isinstance(value, list) and value and isinstance(value[0], MediaInfo): + value = [ x.convert() for x in value ] + result[k] = value + return result + + + def keys(self): + """ + Return all keys. + """ + return self._keys class AudioInfo(MediaInfo): """ Audio Tracks in a Multiplexed Container. """ - def __init__(self): - MediaInfo.__init__(self) - for k in AUDIOCORE: - setattr(self,k,None) - self.keys.append(k) + _keys = MediaInfo._keys + AUDIOCORE + media = 'audio' - def __unicode__(self): - result = u'' - for key in self.keys: - value = self[key] - if value == None: - continue - if key == 'codec': - f, value = fourcc.resolve(value) - result += u'\n fourcc: %s' % f - result += u'\n %s: %s' % (unicode(key), unicode(value)) - return result + def _finalize(self): + if self.codec is not None: + self.fourcc, self.codec = fourcc.resolve(self.codec) class MusicInfo(AudioInfo): """ Digital Music. """ - def __init__(self): - MediaInfo.__init__(self) - for k in AUDIOCORE+MUSICCORE: - setattr(self,k,None) - self.keys.append(k) + _keys = AudioInfo._keys + AUDIOCORE + media = 'audio' - - def correct_data(self): + def _finalize(self): """ - correct trackof to be two digest + Correct same data based on specific rules """ - AudioInfo.correct_data(self) - if self['trackof']: + AudioInfo._finalize(self) + if self.trackof: try: # XXX Why is this needed anyway? - if int(self['trackno']) < 10: - self['trackno'] = '0%s' % int(self['trackno']) + if int(self.trackno) < 10: + self.trackno = '0%s' % int(self.trackno) except (KeyboardInterrupt, SystemExit): sys.exit(0) except: @@ -309,46 +319,33 @@ """ Video Tracks in a Multiplexed Container. """ - def __init__(self): - MediaInfo.__init__(self) - for k in VIDEOCORE: - setattr(self,k,None) - self.keys.append(k) + _keys = MediaInfo._keys + VIDEOCORE + media = 'video' + def _finalize(self): + if self.codec is not None: + self.fourcc, self.codec = fourcc.resolve(self.codec) - def __unicode__(self): - result = u'' - for key in self.keys: - value = self[key] - if value == None: - continue - if key == 'codec': - f, value = fourcc.resolve(value) - result += u'\n fourcc: %s' % f - result += u'\n %s: %s' % (unicode(key), unicode(value)) - return result class ChapterInfo(MediaInfo): """ Chapter in a Multiplexed Container. """ + _keys = ['name', 'pos', 'enabled'] + def __init__(self, name="", pos=0): MediaInfo.__init__(self) - self.keys = ['name', 'pos', 'enabled'] - setattr(self,'name', name) - setattr(self,'pos', pos) - setattr(self,'enabled', True) + self.name = name + self.pos = pos + self.enabled = True class SubtitleInfo(MediaInfo): """ Subtitle Tracks in a Multiplexed Container. """ - def __init__(self): - MediaInfo.__init__(self) - self.keys = ['language', 'trackno', 'title'] - for k in self.keys: - setattr(self, k, None) + _keys = ['language', 'trackno', 'title'] + media = 'subtitle' class AVInfo(MediaInfo): @@ -356,92 +353,34 @@ Container for Audio and Video streams. This is the Container Type for all media, that contain more than one stream. """ + _keys = MediaInfo._keys + AVCORE + def __init__(self): MediaInfo.__init__(self) - for k in AVCORE: - setattr(self,k,None) - self.keys.append(k) self.audio = [] self.video = [] self.subtitles = [] self.chapters = [] - def correct_data(self): + def _finalize(self): """ - correct length to be an int + Correct same data based on specific rules """ - MediaInfo.correct_data(self) - if not self['length'] and len(self.video) and self.video[0]['length']: - self['length'] = self.video[0]['length'] + MediaInfo._finalize(self) + if not self.length and len(self.video) and self.video[0].length: + self.length = self.video[0].length for container in [ self ] + self.video + self.audio: - if container['length']: - container['length'] = int(container['length']) - for subitem in self.video + self.audio: - subitem.correct_data() - - - def find_subtitles(self, filename): - """ - Search for subtitle files. Right now only VobSub is supported - """ - base = os.path.splitext(filename)[0] - if os.path.isfile(base+'.idx') and \ - (os.path.isfile(base+'.sub') or os.path.isfile(base+'.rar')): - file = open(base+'.idx') - if file.readline().find('VobSub index file') > 0: - for line in file.readlines(): - if line.find('id') == 0: - sub = SubtitleInfo() - sub.language = line[4:6] - sub.trackno = base + '.idx' # Maybe not? - self.subtitles.append(sub) - file.close() - - - def __unicode__(self): - result = u'Attributes:' - result += MediaInfo.__unicode__(self) - if len(self.video) + len(self.audio) + len(self.subtitles) > 0: - result += "\n Stream list:" - if len(self.video): - result += reduce( lambda a,b: a + u' \n Video Stream:' + \ - unicode(b), self.video, u'' ) - if len(self.audio): - result += reduce( lambda a,b: a + u' \n Audio Stream:' + \ - unicode(b), self.audio, u'' ) - if len(self.subtitles): - result += reduce( lambda a,b: a + u' \n Subtitle Stream:' +\ - unicode(b), self.subtitles, u'' ) - - if not isinstance(self.chapters, int) and len(self.chapters) > 0: - result += u'\n Chapter list:' - for i in range(len(self.chapters)): - pos = self.chapters[i]['pos'] - result += u'\n %2s: "%s" %02d:%02d:%02d.%03d' % \ - (i+1, unicode(self.chapters[i]['name']), - int(pos)/60/60, int(pos/60) % 60, - int(pos)%60, (pos-int(pos))*1000) - return result + if container.length: + container.length = int(container.length) class CollectionInfo(MediaInfo): """ Collection of Digial Media like CD, DVD, Directory, Playlist """ + _keys = MediaInfo._keys + [ 'id', 'tracks' ] + def __init__(self): MediaInfo.__init__(self) self.tracks = [] - self.keys.append('id') - self.id = None - - def __unicode__(self): - result = MediaInfo.__unicode__(self) - result += u'\nTrack list:' - for counter in range(0,len(self.tracks)): - result += u' \nTrack %d:\n%s' % \ - (counter+1, unicode(self.tracks[counter])) - return result - - def appendtrack(self, track): - self.tracks.append(track) Modified: trunk/metadata/src/misc/dirinfo.py ============================================================================== --- trunk/metadata/src/misc/dirinfo.py (original) +++ trunk/metadata/src/misc/dirinfo.py Wed Dec 6 20:52:29 2006 @@ -74,10 +74,10 @@ f = open(info) for l in f.readlines(): if l.startswith('Icon='): - self.image = l[5:].strip() - if not self.image.startswith('/'): - self.image = os.path.join(directory, self.image[2:]) - self.keys.append('image') + image = l[5:].strip() + if not image.startswith('/'): + image = os.path.join(directory, image[2:]) + self._set('image', image) if l.startswith('Name='): self.title = l[5:].strip() if l.startswith('Comment='): @@ -102,13 +102,9 @@ image = os.path.join(directory, unicode_to_str(child.content)) if not os.path.isfile(image): continue - self.image = image - self.keys.append('image') + self._set('image', image) continue - self[key] = child.content - if not key in MEDIACORE: - # if it's in desc it must be important - self.keys.append(key) + self._set(key, child.content) # register to kaa.metadata core register('directory', EXTENSION_DIRECTORY, TYPE_MISC, DirInfo) Modified: trunk/metadata/src/video/asfinfo.py ============================================================================== --- trunk/metadata/src/video/asfinfo.py (original) +++ trunk/metadata/src/video/asfinfo.py Wed Dec 6 20:52:29 2006 @@ -265,9 +265,8 @@ flags = struct.unpack('>QIIH4x', s[56:78]) strno = flags & 63 encrypted = flags >> 15 - if encrypted and not 'encrypted' in self.keys: - self.encrypted = True - self.keys.append('encrypted') + if encrypted: + self._set('encrypted', True) if streamtype == GUIDS['ASF_Video_Media']: vi = mediainfo.VideoInfo() vi.width, vi.height, depth, \ @@ -323,7 +322,7 @@ d = self._parsekv(s[pos:]) pos += d[0] descriptor[d[1]] = d[2] - self.appendtable('ASFDESCRIPTOR', descriptor) + self._appendtable('ASFDESCRIPTOR', descriptor) elif guid == GUIDS['ASF_Metadata_Object']: (count,) = struct.unpack('<H', s[24:26]) @@ -336,7 +335,7 @@ descriptor[d[1]] = d[2] # TODO: Find the stream in self.audio and self.video and # append it there instead of here - self.appendtable('ASFMETADATA%d'%d[3], descriptor) + self._appendtable('ASFMETADATA%d'%d[3], descriptor) elif guid == GUIDS['ASF_Language_List_Object']: count = struct.unpack('<H', s[24:26])[0] @@ -369,8 +368,8 @@ elif guid == GUIDS['ASF_Content_Encryption_Object'] or \ guid == GUIDS['ASF_Extended_Content_Encryption_Object']: self.encrypted = True - if not 'encrypted' in self.keys: - self.keys.append('encrypted') + if encrypted: + self._set('encrypted', True) else: # Just print the type: for h in GUIDS.keys(): Modified: trunk/metadata/src/video/mkvinfo.py ============================================================================== --- trunk/metadata/src/video/mkvinfo.py (original) +++ trunk/metadata/src/video/mkvinfo.py Wed Dec 6 20:52:29 2006 @@ -121,7 +121,7 @@ 'A_PCM/FLOAT/IEEE': 0x003, 'A_TTA1': 0x77a1 } - + class EbmlEntity: """ This is class that is responsible to handle one Ebml entity as described in @@ -386,9 +386,9 @@ track.aspect = float(vidtab[MATROSKA_DISPLAY_VID_WIDTH_ID].get_value()) / \ vidtab[MATROSKA_DISPLAY_VID_HEIGHT_ID].get_value() if MATROSKA_VID_INTERLACED in vidtab: - self.keys.append('interlaced') - self.interlaced = int(vidtab[MATROSKA_VID_INTERLACED].get_value()) - + value = int(vidtab[MATROSKA_VID_INTERLACED].get_value()) + self._set('interlaced', value) + except Exception, e: log.debug("No other info about video track !!!") self.media = 'video' @@ -451,8 +451,8 @@ if elem.get_id() == MATROSKA_CHAPTER_ATOM_ID: self.process_chapter_atom(elem) indice += elem.get_total_len() + elem.get_crc_len() - - + + def process_chapter_atom(self, atom): tabelem = self.process_one_level(atom) chap = mediainfo.ChapterInfo() Modified: trunk/metadata/src/video/movinfo.py ============================================================================== --- trunk/metadata/src/video/movinfo.py (original) +++ trunk/metadata/src/video/movinfo.py Wed Dec 6 20:52:29 2006 @@ -53,12 +53,20 @@ # chapter_5_section_2.html#//apple_ref/doc/uid/TP40000939-CH206-BBCBIICE # Note: May need to define custom log level to work like ATOM_DEBUG did here +QTUDTA = { + 'nam': 'title', + 'aut': 'artist', + 'cpy': 'copyright' +} class MovInfo(mediainfo.AVInfo): + + table_mapping = { 'QTUDTA': QTUDTA } + def __init__(self,file): mediainfo.AVInfo.__init__(self) self.context = 'video' - self.references = [] + self._references = [] self.mime = 'video/quicktime' self.type = 'Quicktime Video' @@ -81,13 +89,8 @@ while self._readatom(file): pass - info = self.gettable('QTUDTA', 'en') - self.setitem('title', info, 'nam') - self.setitem('artist', info, 'aut') - self.setitem('copyright', info, 'cpy') - - if self.references: - self.keys.append('references') + if self._references: + self._set('references', self._references) def _readatom(self, file): @@ -129,12 +132,12 @@ pos += datasize if len(i18ntabl.keys()) > 0: for k in i18ntabl.keys(): - if QTLANGUAGES.has_key(k): - self.appendtable('QTUDTA', i18ntabl[k], QTLANGUAGES[k]) - self.appendtable('QTUDTA', tabl, QTLANGUAGES[k]) + if QTLANGUAGES.has_key(k) and QTLANGUAGES[k] == 'en': + self._appendtable('QTUDTA', i18ntabl[k]) + self._appendtable('QTUDTA', tabl) else: log.debug('NO i18') - self.appendtable('QTUDTA', tabl) + self._appendtable('QTUDTA', tabl) elif atomtype == 'trak': atomdata = file.read(atomsize-8) @@ -210,8 +213,7 @@ # jpeg is no video, remove it from the list self.video.remove(vi) info = None - print codec - + elif mdia[1] == 'dinf': dref = unpack('>I4s', atomdata[pos+8:pos+8+8]) log.debug(' --> %s, %s' % mdia) @@ -323,7 +325,7 @@ pos += datasize if url: - self.references.append((url, quality, datarate)) + self._references.append((url, quality, datarate)) else: if not atomtype in ('wide', 'free'): Modified: trunk/metadata/src/video/mpeginfo.py ============================================================================== --- trunk/metadata/src/video/mpeginfo.py (original) +++ trunk/metadata/src/video/mpeginfo.py Wed Dec 6 20:52:29 2006 @@ -227,11 +227,9 @@ pass elif ext == 1: if (ord(buffer[pos+5]) >> 3) & 1: - self.keys.append('progressive') - self.progressive = 1 + self._set('progressive', True) else: - self.keys.append('interlaced') - self.interlaced = 1 + self._set('interlaced', True) return True else: log.debug('ext', ext) @@ -373,8 +371,7 @@ break else: self.audio.append(mediainfo.AudioInfo()) - self.audio[-1].id = id - self.audio[-1].keys.append('id') + self.audio[-1]._set('id', id) return 0 if 0xE0 <= id <= 0xEF: @@ -384,8 +381,7 @@ break else: self.video.append(mediainfo.VideoInfo()) - self.video[-1].id = id - self.video[-1].keys.append('id') + self.video[-1]._set('id', id) return 0 if id == SEQ_HEAD: @@ -405,9 +401,8 @@ break else: self.audio.append(mediainfo.AudioInfo()) - self.audio[-1].id = id + self.audio[-1]._set('id', id) self.audio[-1].codec = 'AC3' - self.audio[-1].keys.append('id') return 0 if id == SYS_PKT: @@ -520,8 +515,7 @@ break else: self.audio.append(mediainfo.AudioInfo()) - self.audio[-1].id = id - self.audio[-1].keys.append('id') + self.audio[-1]._set('id', id) elif ord(buffer[3]) & 0xF0 == 0xE0: id = id or ord(buffer[3]) & 0xF @@ -530,8 +524,7 @@ break else: self.video.append(mediainfo.VideoInfo()) - self.video[-1].id = id - self.video[-1].keys.append('id') + self.video[-1]._set('id', id) # new mpeg starting if buffer[header_length+9:header_length+13] == \ @@ -549,9 +542,8 @@ break else: self.audio.append(mediainfo.AudioInfo()) - self.audio[-1].id = id + self.audio[-1]._set('id', id) self.audio[-1].codec = 'AC3' - self.audio[-1].keys.append('id') else: # unknown content @@ -724,9 +716,6 @@ if not self.sequence_header_offset: return 0 - if hasattr(self, 'start') and self.start: - self.keys.append('start') - # fill in values for support functions: self.__seek_size__ = 10000000 # 10 MB self.__sample_size__ = 100000 # 100 k scanning Modified: trunk/metadata/src/video/ogminfo.py ============================================================================== --- trunk/metadata/src/video/ogminfo.py (original) +++ trunk/metadata/src/video/ogminfo.py Wed Dec 6 20:52:29 2006 @@ -54,20 +54,23 @@ STREAM_HEADER_VIDEO = '<4sIQQIIHII' STREAM_HEADER_AUDIO = '<4sIQQIIHHHI' -VORBISCOMMENT_tags = { 'title': 'TITLE', - 'album': 'ALBUM', - 'artist': 'ARTIST', - 'comment': 'COMMENT', - 'date': 'DATE', - 'encoder': 'ENCODER', - 'trackno': 'TRACKNUMBER', - 'language': 'LANGUAGE', - 'genre': 'GENRE', - } +VORBISCOMMENT = { 'TITLE': 'title', + 'ALBUM': 'album', + 'ARTIST': 'artist', + 'COMMENT': 'comment', + 'DATE': 'date', + 'ENCODER': 'encoder', + 'TRACKNUMBER': 'trackno', + 'LANGUAGE': 'language', + 'GENRE': 'genre', + } MAXITERATIONS = 10 class OgmInfo(mediainfo.AVInfo): + + table_mapping = { 'VORBISCOMMENT' : VORBISCOMMENT } + def __init__(self, file): mediainfo.AVInfo.__init__(self) self.samplerate = 1 @@ -110,7 +113,7 @@ self.length = max(self.all_streams[i].length, self.length) # get meta info - for key in self.all_streams[i].keys: + for key in self.all_streams[i].keys(): if self.all_header[i].has_key(key): self.all_streams[i][key] = self.all_header[i][key] del self.all_header[i][key] @@ -162,13 +165,7 @@ # Copy Metadata from tables into the main set of attributes for header in self.all_header: - self.appendtable('VORBISCOMMENT', header) - - self.tag_map = { ('VORBISCOMMENT', 'en') : VORBISCOMMENT_tags } - for k in self.tag_map.keys(): - map(lambda x:self.setitem(x,self.gettable(k[0],k[1]), - self.tag_map[k][x]), - self.tag_map[k].keys()) + self._appendtable('VORBISCOMMENT', header) def _parseOGGS(self,file): @@ -306,8 +303,8 @@ elif htype[:4] == 'text': subtitle = mediainfo.MediaInfo() - subtitle.keys.append('language') - subtitle.type = 'subtitle' + subtitle._set('language', None) + subtitle.type = 'subtitle' subtitle.length = 0 self.all_streams.append(subtitle) Modified: trunk/metadata/src/video/riffinfo.py ============================================================================== --- trunk/metadata/src/video/riffinfo.py (original) +++ trunk/metadata/src/video/riffinfo.py Wed Dec 6 20:52:29 2006 @@ -31,6 +31,7 @@ # python imports import re +import os import struct import string import logging @@ -49,23 +50,20 @@ # http://www.divx-digest.com/software/avitags_dll.html # File Format: google for odmlff2.pdf -AVIINFO_tags = { 'title': 'INAM', - 'artist': 'IART', - 'product': 'IPRD', - 'date': 'ICRD', - 'comment': 'ICMT', - 'language': 'ILNG', - 'keywords': 'IKEY', - 'trackno': 'IPRT', - 'trackof': 'IFRM', - 'producer': 'IPRO', - 'writer': 'IWRI', - 'genre': 'IGNR', - 'copyright': 'ICOP', - 'trackno': 'IPRT', - 'trackof': 'IFRM', - 'comment': 'ICMT', - } +AVIINFO = { 'INAM': 'title', + 'IART': 'artist', + 'IPRD': 'product', + 'ICRD': 'date', + 'ICMT': 'comment', + 'ILNG': 'language', + 'IKEY': 'keywords', + 'IPRT': 'trackno', + 'IFRM': 'trackof', + 'IPRO': 'producer', + 'IWRI': 'writer', + 'IGNR': 'genre', + 'ICOP': 'copyright' + } PIXEL_ASPECT = { # Taken from libavcodec/mpeg4data.h (pixel_aspect struct) 1: (1, 1), @@ -77,6 +75,9 @@ class RiffInfo(mediainfo.AVInfo): + + table_mapping = { 'AVIINFO' : AVIINFO } + def __init__(self,file): mediainfo.AVInfo.__init__(self) # read the header @@ -90,7 +91,6 @@ self.junkStart = None self.infoStart = None self.type = h[8:12] - self.tag_map = { ('AVIINFO', 'en') : AVIINFO_tags } if self.type == 'AVI ': self.mime = 'video/avi' elif self.type == 'WAVE': @@ -101,23 +101,35 @@ except IOError: log.exception('error in file, stop parsing') - self.find_subtitles(file.name) + self._find_subtitles(file.name) - # Copy Metadata from tables into the main set of attributes - for k in self.tag_map.keys(): - map(lambda x:self.setitem(x,self.gettable(k[0],k[1]), - self.tag_map[k][x]), - self.tag_map[k].keys()) if not self.has_idx: log.debug('WARNING: avi has no index') - self.corrupt = 1 - self.keys.append('corrupt') + self._set('corrupt', True) def _extractHeaderString(self,h,offset,len): return h[offset:offset+len] + def _find_subtitles(self, filename): + """ + Search for subtitle files. Right now only VobSub is supported + """ + base = os.path.splitext(filename)[0] + if os.path.isfile(base+'.idx') and \ + (os.path.isfile(base+'.sub') or os.path.isfile(base+'.rar')): + file = open(base+'.idx') + if file.readline().find('VobSub index file') > 0: + for line in file.readlines(): + if line.find('id') == 0: + sub = mediainfo.SubtitleInfo() + sub.language = line[4:6] + sub.trackno = base + '.idx' # Maybe not? + self.subtitles.append(sub) + file.close() + + def parseAVIH(self,t): retval = {} v = struct.unpack('<IIIIIIIIIIIIII',t[0:56]) @@ -299,7 +311,7 @@ def parseLISTmovi(self, size, file): """ - Digs into movi list, looking for a Video Object Layer header in an + Digs into movi list, looking for a Video Object Layer header in an mpeg4 stream in order to determine aspect ratio. """ i = 0 @@ -317,7 +329,7 @@ key, sz = struct.unpack('<4sI', data) if key[2:] != 'dc' or sz > 1024*500: - # This chunk is not video or is unusually big (> 500KB); + # This chunk is not video or is unusually big (> 500KB); # skip it. file.seek(sz, 1) i += 8 + sz @@ -335,7 +347,7 @@ # logic for this is taken from libavcodec, h263.c pos = 0 startcode = 0xff - def bits(v, o, n): + def bits(v, o, n): # Returns n bits in v, offset o bits. return (v & 2**n-1 << (64-n-o)) >> 64-n-o @@ -385,7 +397,7 @@ if i < size: # Seek past whatever might be remaining of the movi list. file.seek(size-i,1) - + def parseLIST(self,t): @@ -488,7 +500,7 @@ self.video[-1].format in ('DIVX', 'XVID', 'FMP4'): # any others? # If we don't have the aspect (i.e. it isn't in odml vprp # header), but we do know the video's dimensions, and - # we're dealing with an mpeg4 stream, try to get the aspect + # we're dealing with an mpeg4 stream, try to get the aspect # from the VOL header in the mpeg4 stream. self.parseLISTmovi(size-4, file) return True @@ -496,16 +508,16 @@ log.debug('RIFF LIST "%s" to long to parse: %s bytes' % (key, size)) t = file.seek(size-4,1) return True - + t = file.read(size-4) log.debug('parse RIFF LIST "%s": %d bytes' % (key, size)) value = self.parseLIST(t) self.header[key] = value if key == 'INFO': self.infoStart = pos - self.appendtable( 'AVIINFO', value ) + self._appendtable( 'AVIINFO', value ) elif key == 'MID ': - self.appendtable( 'AVIMID', value ) + self._appendtable( 'AVIMID', value ) elif key in ('hdrl', ): # no need to add this info to a table pass ------------------------------------------------------------------------- Take Surveys. Earn Cash. Influence the Future of IT Join SourceForge.net's Techsay panel and you'll get the chance to share your opinions on IT & business topics through brief surveys - and earn cash http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV _______________________________________________ Freevo-cvslog mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/freevo-cvslog
