338 lines
11 KiB
Python
338 lines
11 KiB
Python
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 |