import os import helpers from mutagen import File as MFile from mutagen.flac import Picture class AudioHelper(object): def __init__(self, filename): self.filename = filename def AudioHelpers(filename): filename = helpers.unicodize(filename) try: tag = MFile(filename, None, True) except Exception, e: Log('Error getting file details for %s: %s' % (filename, e)) return None if tag is not None: for cls in [ ID3AudioHelper, MP4AudioHelper, FLACAudioHelper, OGGAudioHelper ]: if cls.is_helper_for(type(tag).__name__): return cls(filename) return None def parse_genres(genre): if genre.find(';') != -1: genre_list = genre.split(';') else: genre_list = genre.split('/') return genre_list ##################################################################################################################### class ID3AudioHelper(AudioHelper): @classmethod def is_helper_for(cls, tagType): return tagType in ('EasyID3', 'EasyMP3', 'EasyTrueAudio', 'ID3', 'MP3', 'TrueAudio', 'AIFF') # All of these file types use ID3 tags like MP3 def get_album_sort_title(self): return self.tags.get('TSOA') def get_track_sort_title(self): return self.tags.get('TSOT') def get_track_genres(self): genre_list = [] try: self.tags = tags = MFile(self.filename) genres = self.tags.get('TCON') if genres is not None and len(genres.text) > 0: for genre in genres.text: for sub_genre in parse_genres(genre): genre_list.append(sub_genre.strip()) except Exception, e: Log('Exception reading TCON (genre): ' + str(e)) return genre_list def get_artist_sort_title(self): try: self.tags = tags = MFile(self.filename) tag = self.tags.get('TSO2') if tag: return tag return self.tags.get('TSOP') except: pass return None def process_metadata(self, metadata): Log('Reading ID3 tags from: ' + self.filename) try: self.tags = tags = MFile(self.filename) Log('Found tags: ' + str(tags.keys())) except: Log('An error occurred while attempting to read ID3 tags from ' + self.filename) return # Release Date (original_available_at) try: year = tags.get('TDRC') if year is not None and len(year.text) > 0: available = Datetime.ParseDate('01-01-' + str(year.text[0])).date() if metadata.originally_available_at is None: metadata.originally_available_at = available elif (available > metadata.originally_available_at): # more then one date: use highest one metadata.originally_available_at = available except Exception, e: Log('Exception reading TDRC (year): ' + str(e)) # Release Date (available_at) try: year = tags.get('TDRL') if year is not None and len(year.text) > 0: available = Datetime.ParseDate('01-01-' + str(year.text[0])).date() if metadata.available_at is None: metadata.available_at = available elif (available > metadata.available_at): # more then one date: use highest one metadata.available_at = available except Exception, e: Log('Exception reading TDRL (year): ' + str(e)) # Genres try: genres = tags.get('TCON') if genres is not None and len(genres.text) > 0: for genre in genres.text: for sub_genre in parse_genres(genre): if sub_genre.strip() not in metadata.genres: metadata.genres.add(sub_genre.strip()) except Exception, e: Log('Exception reading TCON (genre): ' + str(e)) # Posters try: valid_posters = [] frames = [f for f in tags if f.startswith('APIC:')] for frame in frames: if (tags[frame].mime == 'image/jpeg') or (tags[frame].mime == 'image/jpg'): ext = 'jpg' elif tags[frame].mime == 'image/png': ext = 'png' elif tags[frame].mime == 'image/gif': ext = 'gif' else: ext = '' poster_name = hashlib.md5(tags[frame].data).hexdigest() valid_posters.append(poster_name) if poster_name not in metadata.posters: Log('Adding embedded APIC art: ' + poster_name) metadata.posters[poster_name] = Proxy.Media(tags[frame].data, ext = ext) except Exception, e: Log('Exception adding posters: ' + str(e)) return valid_posters ##################################################################################################################### class MP4AudioHelper(AudioHelper): @classmethod def is_helper_for(cls, tagType): return tagType in ['MP4','EasyMP4'] def get_track_sort_title(self): try: tags = MFile(self.filename, easy=True) return tags.get('titlesort')[0] # 'sonm' except: return None def get_album_sort_title(self): try: tags = MFile(self.filename, easy=True) return tags.get('albumsort')[0] # 'soal' except: return None def get_artist_sort_title(self): try: tags = MFile(self.filename, easy=True) tag = tags.get('albumartistsort') # 'soaa' if tag: return tag[0] return tags.get('artistsort')[0] # 'soar' except: return None def get_track_genres(self): genre_list = [] try: tags = MFile(self.filename) genres = tags.get('\xa9gen') if genres is not None and len(genres) > 0: for genre in genres: for sub_genre in parse_genres(genre): genre_list.append(sub_genre.strip()) except Exception, e: Log('Exception reading (genre): ' + str(e)) return genre_list def process_metadata(self, metadata): Log('Reading MP4 tags from: ' + self.filename) try: tags = MFile(self.filename) Log('Found tags: ' + str(tags.keys())) except: Log('An error occurred while attempting to parse the MP4 file: ' + self.filename) return # Genres try: genres = tags.get('\xa9gen') if genres is not None and len(genres) > 0: for genre in genres: for sub_genre in parse_genres(genre): if sub_genre.strip() not in metadata.genres: metadata.genres.add(sub_genre.strip()) except Exception, e: Log('Exception reading \xa9gen (genre): ' + str(e)) # Release Date try: release_date = tags.get('\xa9day') if release_date is not None and len(release_date) > 0: available = Datetime.ParseDate(release_date[0].split('T')[0]).date() if metadata.originally_available_at is None: metadata.originally_available_at = available elif (available > metadata.originally_available_at): # more then one date: use highest one metadata.originally_available_at = available except Exception, e: Log('Exception reading \xa9day (release date)' + str(e)) # Posters valid_posters = [] try: covers = tags.get('covr') if covers is not None and len(covers) > 0: for cover in covers: poster_name = hashlib.md5(cover).hexdigest() valid_posters.append(poster_name) if poster_name not in metadata.posters: Log('Adding embedded cover art: ' + poster_name) metadata.posters[poster_name] = Proxy.Media(cover) except Exception, e: Log('Exception adding posters: ' + str(e)) return valid_posters ##################################################################################################################### class FLACAudioHelper(AudioHelper): @classmethod def is_helper_for(cls, tagType): return tagType in ['FLAC'] def process_metadata(self, metadata): Log('Reading FLAC tags from: ' + self.filename) try: tags = MFile(self.filename) Log('Found tags: ' + str(tags.keys())) except: Log('An error occurred while attempting to parse the FLAC file: ' + self.filename) return # Genres try: genres = tags.get('genre') if genres is not None and len(genres) > 0: metadata.genres.clear() for genre in genres: for sub_genre in parse_genres(genre): metadata.genres.add(sub_genre.strip()) except Exception, e: Log('Exception reading genre: ' + str(e)) # Release Date try: release_date = tags.get('date') if release_date is not None and len(release_date) > 0: metadata.originally_available_at = Datetime.ParseDate(release_date[0]) except Exception, e: Log('Exception reading release date' + str(e)) # Posters valid_posters = [] try: covers = tags.pictures if covers is not None and len(covers) > 0: for cover in covers: poster_name = hashlib.md5(cover.data).hexdigest() valid_posters.append(poster_name) if poster_name not in metadata.posters: Log('Adding embedded cover art: ' + poster_name) metadata.posters[poster_name] = Proxy.Media(cover.data) except Exception, e: Log('Exception adding posters: ' + str(e)) return valid_posters ##################################################################################################################### class OGGAudioHelper(AudioHelper): @classmethod def is_helper_for(cls, tagType): return tagType in ['OggVorbis'] def process_metadata(self, metadata): Log('Reading OGG tags from: ' + self.filename) try: tags = MFile(self.filename) Log('Found tags: ' + str(tags.keys())) except: Log('An error occured while attempting to parse the OGG file: ' + self.filename) return # Genres try: genres = tags.get('genre') if genres is not None and len(genres) > 0: metadata.genres.clear() for genre in genres: for sub_genre in parse_genres(genre): metadata.genres.add(sub_genre.strip()) except Exception, e: Log('Exception reading genre: ' + str(e)) # Release Date try: release_date = tags.get('date') if release_date is not None and len(release_date) > 0: metadata.originally_available_at = Datetime.ParseDate(release_date[0]) except Exception, e: Log('Exception reading release date' + str(e)) # Posters valid_posters = [] try: covers = tags.get('metadata_block_picture') if covers is not None and len(covers) > 0: for cover in covers: poster = Picture(base64.standard_b64decode(cover)) poster_name = hashlib.md5(poster.data).hexdigest() valid_posters.append(poster_name) if poster_name not in metadata.posters: Log('Adding embedded cover art: ' + poster_name) metadata.posters[poster_name] = Proxy.Media(poster.data) except Exception, e: Log('Exception adding posters: ' + str(e)) return valid_posters