mirror of
https://github.com/rembo10/headphones.git
synced 2026-05-09 05:09:27 +01:00
metadata: Additional protection against NPEs
This commit is contained in:
@@ -199,13 +199,15 @@ def replace_all(text, dic, normalize=False):
|
||||
if normalize:
|
||||
new_dic = {}
|
||||
for i, j in dic.iteritems():
|
||||
try:
|
||||
if sys.platform == 'darwin':
|
||||
j = unicodedata.normalize('NFD', j)
|
||||
else:
|
||||
j = unicodedata.normalize('NFC', j)
|
||||
except TypeError:
|
||||
j = unicodedata.normalize('NFC', j.decode(headphones.SYS_ENCODING, 'replace'))
|
||||
if j is not None:
|
||||
try:
|
||||
if sys.platform == 'darwin':
|
||||
j = unicodedata.normalize('NFD', j)
|
||||
else:
|
||||
j = unicodedata.normalize('NFC', j)
|
||||
except TypeError:
|
||||
j = unicodedata.normalize('NFC',
|
||||
j.decode(headphones.SYS_ENCODING, 'replace'))
|
||||
new_dic[i] = j
|
||||
dic = new_dic
|
||||
return pathrender.render(text, dic)[0]
|
||||
|
||||
@@ -114,6 +114,7 @@ def _as_str(val):
|
||||
|
||||
|
||||
def _media_file_to_dict(mf, d):
|
||||
# type: (MediaFile, MutableMapping[basestring,basestring])->None
|
||||
"""
|
||||
Populate dict with tags read from media file.
|
||||
"""
|
||||
@@ -158,6 +159,18 @@ def _date_year(release):
|
||||
return date, year
|
||||
|
||||
|
||||
def _lower(s):
|
||||
# type: basestring->basestring
|
||||
"""
|
||||
Return s.lower() if not None
|
||||
:param s:
|
||||
:return:
|
||||
"""
|
||||
if s:
|
||||
return s.lower()
|
||||
return None
|
||||
|
||||
|
||||
def file_metadata(path, release):
|
||||
# type: (str,sqlite3.Row)->Tuple[Mapping[str,str],bool]
|
||||
"""
|
||||
@@ -208,7 +221,7 @@ def file_metadata(path, release):
|
||||
else:
|
||||
artist_name = release['ArtistName']
|
||||
|
||||
if artist_name.startswith('The '):
|
||||
if artist_name and artist_name.startswith('The '):
|
||||
sort_name = artist_name[4:] + ", The"
|
||||
else:
|
||||
sort_name = artist_name
|
||||
@@ -224,10 +237,10 @@ def file_metadata(path, release):
|
||||
Vars.YEAR: year,
|
||||
Vars.DATE: date,
|
||||
Vars.EXTENSION: ext,
|
||||
Vars.TITLE_LOWER: title.lower(),
|
||||
Vars.ARTIST_LOWER: artist_name.lower(),
|
||||
Vars.SORT_ARTIST_LOWER: sort_name.lower(),
|
||||
Vars.ALBUM_LOWER: album_title.lower(),
|
||||
Vars.TITLE_LOWER: _lower(title),
|
||||
Vars.ARTIST_LOWER: _lower(artist_name),
|
||||
Vars.SORT_ARTIST_LOWER: _lower(sort_name),
|
||||
Vars.ALBUM_LOWER: _lower(album_title),
|
||||
}
|
||||
res.add_items(override_values.iteritems())
|
||||
return res, from_metadata
|
||||
@@ -255,26 +268,34 @@ def album_metadata(path, release, common_tags):
|
||||
:return: metadata dictionary with substitution variables for rendering path.
|
||||
"""
|
||||
date, year = _date_year(release)
|
||||
artist = release['ArtistName'].replace('/', '_')
|
||||
album = release['AlbumTitle'].replace('/', '_')
|
||||
release_type = release['Type'].replace('/', '_')
|
||||
artist = release['ArtistName']
|
||||
if artist:
|
||||
artist = artist.replace('/', '_')
|
||||
album = release['AlbumTitle']
|
||||
if album:
|
||||
album = album.replace('/', '_')
|
||||
release_type = release['Type']
|
||||
if release_type:
|
||||
release_type = release_type.replace('/', '_')
|
||||
|
||||
if release['ArtistName'].startswith('The '):
|
||||
sort_name = release['ArtistName'][4:] + ", The"
|
||||
if artist and artist.startswith('The '):
|
||||
sort_name = artist[4:] + ", The"
|
||||
else:
|
||||
sort_name = release['ArtistName']
|
||||
sort_name = artist
|
||||
|
||||
if sort_name[0].isdigit():
|
||||
if not sort_name or sort_name[0].isdigit():
|
||||
first_char = u'0-9'
|
||||
else:
|
||||
first_char = sort_name[0]
|
||||
|
||||
orig_folder = u''
|
||||
for r, d, f in os.walk(path):
|
||||
try:
|
||||
orig_folder = os.path.basename(
|
||||
os.path.normpath(r).decode(headphones.SYS_ENCODING, 'replace'))
|
||||
break
|
||||
except:
|
||||
orig_folder = u''
|
||||
pass
|
||||
|
||||
override_values = {
|
||||
Vars.ARTIST: artist,
|
||||
@@ -285,12 +306,12 @@ def album_metadata(path, release, common_tags):
|
||||
Vars.TYPE: release_type,
|
||||
Vars.ORIGINAL_FOLDER: orig_folder,
|
||||
Vars.FIRST_LETTER: first_char.upper(),
|
||||
Vars.ARTIST_LOWER: artist.lower(),
|
||||
Vars.SORT_ARTIST_LOWER: sort_name.lower(),
|
||||
Vars.ALBUM_LOWER: album.lower(),
|
||||
Vars.TYPE_LOWER: release_type.lower(),
|
||||
Vars.FIRST_LETTER_LOWER: first_char.lower(),
|
||||
Vars.ORIGINAL_FOLDER_LOWER: orig_folder.lower()
|
||||
Vars.ARTIST_LOWER: _lower(artist),
|
||||
Vars.SORT_ARTIST_LOWER: _lower(sort_name),
|
||||
Vars.ALBUM_LOWER: _lower(album),
|
||||
Vars.TYPE_LOWER: _lower(release_type),
|
||||
Vars.FIRST_LETTER_LOWER: _lower(first_char),
|
||||
Vars.ORIGINAL_FOLDER_LOWER: _lower(orig_folder)
|
||||
}
|
||||
res = MetadataDict(common_tags)
|
||||
res.add_items(override_values.iteritems())
|
||||
@@ -306,13 +327,16 @@ def albumart_metadata(release, common_tags):
|
||||
:return: metadata dictionary with substitution variables for rendering path.
|
||||
"""
|
||||
date, year = _date_year(release)
|
||||
artist = release['ArtistName']
|
||||
album = release['AlbumTitle']
|
||||
|
||||
override_values = {
|
||||
Vars.ARTIST: release['ArtistName'],
|
||||
Vars.ALBUM: release['AlbumTitle'],
|
||||
Vars.ARTIST: artist,
|
||||
Vars.ALBUM: album,
|
||||
Vars.YEAR: year,
|
||||
Vars.DATE: date,
|
||||
Vars.ARTIST_LOWER: release['ArtistName'].lower(),
|
||||
Vars.ALBUM_LOWER: release['AlbumTitle'].lower()
|
||||
Vars.ARTIST_LOWER: _lower(artist),
|
||||
Vars.ALBUM_LOWER: _lower(album)
|
||||
}
|
||||
res = MetadataDict(common_tags)
|
||||
res.add_items(override_values.iteritems())
|
||||
|
||||
@@ -16,7 +16,9 @@
|
||||
"""
|
||||
Test module for metadata.
|
||||
"""
|
||||
import headphones as _h
|
||||
import headphones.metadata as _md
|
||||
import headphones.helpers as _hp
|
||||
from headphones.metadata import MetadataDict
|
||||
import datetime
|
||||
|
||||
@@ -146,3 +148,28 @@ class MetadataTest(TestCase):
|
||||
'$Variation': '5'
|
||||
}
|
||||
self.assertItemsEqual(expected, md, "check _row_to_dict() valid")
|
||||
|
||||
def test_album_metadata_with_None(self):
|
||||
"""metadata: check handling of None metadata values"""
|
||||
row = _MockDatabaseRow({
|
||||
'ArtistName': 'artist',
|
||||
'AlbumTitle': 'Album',
|
||||
'Type': None,
|
||||
'ReleaseDate': None,
|
||||
})
|
||||
mb = _md.AlbumMetadataBuilder()
|
||||
f1 = _MockMediaFile('artist', None, None, None, None, None)
|
||||
mb.add_media_file(f1)
|
||||
f2 = _MockMediaFile('artist', None, None, 2, 'track2', None)
|
||||
mb.add_media_file(f2)
|
||||
md = _md.album_metadata("/music/Artist - Album [2002]", row, mb.build())
|
||||
|
||||
# tests don't undergo normal Headphones init, SYS_ENCODING is not set
|
||||
if not _h.SYS_ENCODING:
|
||||
_h.SYS_ENCODING = 'UTF-8'
|
||||
|
||||
res = _hp.replace_all(
|
||||
"/music/$First/$Artist/$Artist - $Album{ [$Year]}", md, True)
|
||||
|
||||
self.assertEqual(res, u"/music/A/artist/artist - Album",
|
||||
"check correct rendering of None via replace_all()")
|
||||
|
||||
Reference in New Issue
Block a user