diff --git a/api/__init__.py b/api/__init__.py index f555e2f..f6a90da 100755 --- a/api/__init__.py +++ b/api/__init__.py @@ -129,15 +129,17 @@ class ResponseHelper: """ if not isinstance(dictionary, dict): raise TypeError('Expecting a dict') + if not all(map(lambda x: isinstance(x, basestring), dictionary.keys())): + raise TypeError('Dictionary keys must be strings') subelems = { k: v for k, v in dictionary.iteritems() if isinstance(v, dict) } sequences = { k: v for k, v in dictionary.iteritems() if isinstance(v, list) } attributes = { k: v for k, v in dictionary.iteritems() if k != '_value_' and k not in subelems and k not in sequences } if '_value_' in dictionary: - elem.text = dictionary['_value_'] + elem.text = ResponseHelper.value_tostring(dictionary['_value_']) for attr, value in attributes.iteritems(): - elem.set(attr, str(value).lower() if isinstance(value, bool) else str(value)) + elem.set(attr, ResponseHelper.value_tostring(value)) for sub, subdict in subelems.iteritems(): subelem = ElementTree.SubElement(elem, sub) ResponseHelper.dict2xml(subelem, subdict) @@ -146,6 +148,14 @@ class ResponseHelper: subelem = ElementTree.SubElement(elem, seq) ResponseHelper.dict2xml(subelem, subdict) + @staticmethod + def value_tostring(value): + if isinstance(value, basestring): + return value + if isinstance(value, bool): + return str(value).lower() + return str(value) + def get_entity(req, ent, param = 'id'): eid = req.args.get(param) if not eid: diff --git a/api/media.py b/api/media.py index 40fb73a..9b3caf4 100755 --- a/api/media.py +++ b/api/media.py @@ -4,6 +4,7 @@ from flask import request, send_file, Response import os.path from PIL import Image import subprocess +import codecs import config, scanner from web import app @@ -145,9 +146,16 @@ def lyrics(): for track in query: lyrics_path = os.path.splitext(track.path)[0] + '.txt' if os.path.exists(lyrics_path): - print lyrics_path - with open(lyrics_path, 'r') as lyrics_file: - lyrics = lyrics_file.read() + app.logger.debug('Found lyrics file: ' + lyrics_path) + + try: + lyrics = read_file_as_unicode(lyrics_path) + except UnicodeError: + # Lyrics file couldn't be decoded. Rather than displaying an error, try with the potential next files or + # return no lyrics. Log it anyway. + app.logger.warn('Unsupported encoding for lyrics file ' + lyrics_path) + continue + return request.formatter({ 'lyrics': { 'artist': track.album.artist.name, 'title': track.title, @@ -156,3 +164,21 @@ def lyrics(): return request.formatter({ 'lyrics': {} }) +def read_file_as_unicode(path): + """ Opens a file trying with different encodings and returns the contents as a unicode string """ + + encodings = [ 'utf-8', 'latin1' ] # Should be extended to support more encodings + + for enc in encodings: + try: + contents = codecs.open(path, 'r', encoding = enc).read() + app.logger.debug('Read file {} with {} encoding'.format(path, enc)) + # Maybe save the encoding somewhere to prevent going through this loop each time for the same file + return contents + except UnicodeError: + pass + + # Fallback to ASCII + app.logger.debug('Reading file {} with ascii encoding'.format(path)) + return unicode(open(path, 'r').read()) +