Skip to content

Commit

Permalink
fixed problems with flac tag reading, coverage is 99%
Browse files Browse the repository at this point in the history
  • Loading branch information
devsnd committed Mar 14, 2014
1 parent d7354a0 commit bf36544
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 27 deletions.
19 changes: 13 additions & 6 deletions tinytag/__init__.py
Expand Up @@ -27,7 +27,7 @@
import struct
import os

__version__ = '0.4.0'
__version__ = '0.5.0'


class TinyTag(object):
Expand Down Expand Up @@ -115,10 +115,12 @@ class ID3(TinyTag):
'TPE1': 'artist', 'TP1': 'artist',
'TIT2': 'title', 'TT2': 'title',
}
def __init__(self, filehandler, filesize, estimation_length_sec=30):
TinyTag.__init__(self, filehandler, filesize)
self.estimation_length_sec = estimation_length_sec

def _determine_length(self, fh):
max_estimation_sec = 30
max_estimation_frames = (max_estimation_sec*44100) // 1152
max_estimation_frames = (self.estimation_length_sec*44100) // 1152
frame_size_mean = 0
# set sample rate from first found frame later, default to 44khz
file_sample_rate = 44100
Expand Down Expand Up @@ -324,7 +326,12 @@ def _parse_vorbis_comment(self, fh):
key, value = keyvalpair[:splitidx], keyvalpair[splitidx+1:]
fieldname = mapping.get(key.lower())
if fieldname:
self._set_field(fieldname, value)
if fieldname == 'track' and '/' in value:
track, tracktotal = value.split('/')
self._set_field('track', track)
self._set_field('track_total', tracktotal)
else:
self._set_field(fieldname, value)

def _parse_pages(self, fh):
# for the spec, see: https://wiki.xiph.org/Ogg
Expand Down Expand Up @@ -363,7 +370,7 @@ def _determine_length(self, fh):
# and: https://en.wikipedia.org/wiki/WAV
riff, size, fformat = struct.unpack('4sI4s', fh.read(12))
if riff != b'RIFF' or fformat != b'WAVE':
print('not a wave file!')
return # not a valid wave file!
channels, samplerate, bitdepth = 2, 44100, 16 # assume CD quality
chunk_header = fh.read(8)
while len(chunk_header) > 0:
Expand Down Expand Up @@ -434,7 +441,7 @@ def _parse_tag(self, fh):
while len(header_data):
meta_header = struct.unpack('B3B', header_data)
size = self._bytes_to_int(meta_header[1:4])
if meta_header[0] == 4:
if (meta_header[0] & 7) == 4: # VORBIS_COMMENT
oggtag = Ogg(fh, 0)
oggtag._parse_vorbis_comment(fh)
self.update(oggtag)
Expand Down
Binary file added tinytag/test/samples/longer_flac.flac
Binary file not shown.
90 changes: 69 additions & 21 deletions tinytag/test/test_tinytag.py
Expand Up @@ -6,26 +6,28 @@
from tinytag import *

test_sample_folder = path.join(path.dirname(__file__), 'samples')
testfiles = {'vbri.mp3': {'track_total': None, 'length': 0.5224489795918368, 'album': 'I Can Walk On Water I Can Fly', 'year': '2007', 'title': 'I Can Walk On Water I Can Fly', 'artist': 'Basshunter', 'track': '01'},
'cbr.mp3': {'track_total': None, 'length': 0.4963265306122449, 'album': 'I Can Walk On Water I Can Fly', 'year': '2007', 'title': 'I Can Walk On Water I Can Fly', 'artist': 'Basshunter', 'track': '01'},
'id3v22-test.mp3': {'track_total': '11', 'length': 0.156734693877551, 'album': 'Hymns for the Exiled', 'year': '2004', 'title': 'cosmic american', 'artist': 'Anais Mitchell', 'track': '3'},
'silence-44-s-v1.mp3': {'track_total': None, 'length': 3.7355102040816326, 'album': 'Quod Libet Test Data', 'year': '2004', 'title': 'Silence', 'artist': 'piman', 'track': '2'},
'UTF16.mp3': {'length': 0.052244897959183675, 'track_total': '11', 'track': '07', 'artist': 'The National', 'year': '2010', 'album': 'High Violet', 'title': 'Lemonworld'},
'empty.ogg': {'track_total': None, 'length': 3.684716553287982, 'album': None, '_max_samplenum': 162496, 'year': None, 'title': None, 'artist': None, 'track': None, '_tags_parsed': False},
'multipagecomment.ogg': {'track_total': None, 'length': 3.684716553287982, 'album': None, '_max_samplenum': 162496, 'year': None, 'title': None, 'artist': None, 'track': None, '_tags_parsed': False},
'multipage-setup.ogg': {'track_total': None, 'length': 4.128798185941043, 'album': 'Timeless', 'year': '2006', 'title': 'Burst', 'artist': 'UVERworld', 'track': '7', '_tags_parsed': False},
'test.ogg': {'track_total': None, 'length': 1.0, 'album': 'the boss', 'year': '2006', 'title': 'the boss', 'artist': 'james brown', 'track': '1', '_tags_parsed': False},
'test.wav': {'length': 1.0},
'test3sMono.wav': {'length': 3.0},
'test-tagged.wav': {'length': 1.0},

'flac1sMono.flac': {'track_total': None, 'album': None, 'year': None, 'length': 1.0, 'title': None, 'track': None, 'artist': None},
'flac1.5sStereo.flac': {'track_total': None, 'album': None, 'year': None, 'length': 1.4995238095238095, 'title': None, 'track': None, 'artist': None},
'flac_application.flac': {'track_total': None, 'album': 'Belle and Sebastian Write About Love', 'year': '2010-10-11', 'length': 273.64, 'title': 'I Want the World to Stop', 'track': '4/11', 'artist': 'Belle and Sebastian'},
'no-tags.flac': {'track_total': None, 'album': None, 'year': None, 'length': 3.684716553287982, 'title': None, 'track': None, 'artist': None},
'variable-block.flac': {'track_total': None, 'album': 'Appleseed Original Soundtrack', 'year': '2004', 'length': 261.68, 'title': 'DIVE FOR YOU', 'track': '01', 'artist': 'Boom Boom Satellites'},
'emptyfile.mp3': {'track_total': None, 'album': None, 'year': None, 'length': 0, 'title': None, 'track': None, 'artist': None},
}
testfiles = {
'vbri.mp3': {'track_total': None, 'length': 0.5224489795918368, 'album': 'I Can Walk On Water I Can Fly', 'year': '2007', 'title': 'I Can Walk On Water I Can Fly', 'artist': 'Basshunter', 'track': '01'},
'cbr.mp3': {'track_total': None, 'length': 0.4963265306122449, 'album': 'I Can Walk On Water I Can Fly', 'year': '2007', 'title': 'I Can Walk On Water I Can Fly', 'artist': 'Basshunter', 'track': '01'},
'id3v22-test.mp3': {'track_total': '11', 'length': 0.156734693877551, 'album': 'Hymns for the Exiled', 'year': '2004', 'title': 'cosmic american', 'artist': 'Anais Mitchell', 'track': '3'},
'silence-44-s-v1.mp3': {'track_total': None, 'length': 3.7355102040816326, 'album': 'Quod Libet Test Data', 'year': '2004', 'title': 'Silence', 'artist': 'piman', 'track': '2'},
'UTF16.mp3': {'length': 0.052244897959183675, 'track_total': '11', 'track': '07', 'artist': 'The National', 'year': '2010', 'album': 'High Violet', 'title': 'Lemonworld'},
'empty.ogg': {'track_total': None, 'length': 3.684716553287982, 'album': None, '_max_samplenum': 162496, 'year': None, 'title': None, 'artist': None, 'track': None, '_tags_parsed': False},
'multipagecomment.ogg': {'track_total': None, 'length': 3.684716553287982, 'album': None, '_max_samplenum': 162496, 'year': None, 'title': None, 'artist': None, 'track': None, '_tags_parsed': False},
'multipage-setup.ogg': {'track_total': None, 'length': 4.128798185941043, 'album': 'Timeless', 'year': '2006', 'title': 'Burst', 'artist': 'UVERworld', 'track': '7', '_tags_parsed': False},
'test.ogg': {'track_total': None, 'length': 1.0, 'album': 'the boss', 'year': '2006', 'title': 'the boss', 'artist': 'james brown', 'track': '1', '_tags_parsed': False},
'test.wav': {'length': 1.0},
'test3sMono.wav': {'length': 3.0},
'test-tagged.wav': {'length': 1.0},
'flac1sMono.flac': {'track_total': None, 'album': 'alb', 'year': '2014', 'length': 1.0, 'title': 'track', 'track': '23', 'artist': 'art'},
'flac1.5sStereo.flac': {'track_total': None, 'album': 'alb', 'year': '2014', 'length': 1.4995238095238095, 'title': 'track', 'track': '23', 'artist': 'art'},
'flac_application.flac': {'track_total': '11', 'album': 'Belle and Sebastian Write About Love', 'year': '2010-10-11', 'length': 273.64, 'title': 'I Want the World to Stop', 'track': '4', 'artist': 'Belle and Sebastian'},
'longer_flac.flac': {'track_total': None, 'album': 'Belle and Sebastian Write About Love', 'year': '2010', 'length': 0.5742176870748299, 'title': 'I Want the World to Stop', 'track': '4', 'artist': 'Belle and Sebastian'},
'no-tags.flac': {'track_total': None, 'album': None, 'year': None, 'length': 3.684716553287982, 'title': None, 'track': None, 'artist': None},
'variable-block.flac': {'track_total': None, 'album': 'Appleseed Original Soundtrack', 'year': '2004', 'length': 261.68, 'title': 'DIVE FOR YOU', 'track': '01', 'artist': 'Boom Boom Satellites'},
'emptyfile.mp3': {'track_total': None, 'album': None, 'year': None, 'length': 0, 'title': None, 'track': None, 'artist': None},
}
testfile_mp3_3sec = path.join(test_sample_folder, 'silence-44-s-v1.mp3')


def get_info(testfile, expected):
Expand All @@ -35,7 +37,8 @@ def get_info(testfile, expected):
for key, value in expected.items():
result = getattr(tag, key)
fmt_string = 'field "%s": got %s (%s) expected %s (%s)!'
fmt_values = (key, repr(result), type(result), repr(value), type(value))
fmt_values = (key, repr(result), type(result),
repr(value), type(value))
assert result == value, fmt_string % fmt_values
print(tag)
print('')
Expand All @@ -45,9 +48,54 @@ def test_generator():
for testfile, expected in testfiles.items():
yield get_info, testfile, expected


@raises(LookupError)
def test_unsupported_filetype():
get_info(path.join(test_sample_folder, 'unsupported.filetype'), {})


@raises(NotImplementedError)
def test_unimplemented_length_method():
TinyTag(None, 0)._determine_length(None)


@raises(NotImplementedError)
def test_unimplemented_tag_method():
TinyTag(None, 0)._parse_tag(None)


def test_mp3_length_estimation():
with open(testfile_mp3_3sec, 'rb') as af:
tag = ID3(af, 0, estimation_length_sec=1)
tag.load(tags=True, length=True)


def test_invalid_ogg_file():
with open(testfile_mp3_3sec, 'rb') as af:
oggtag = Ogg(af, 0)
oggtag.load(True, True)
emptytag = TinyTag(None, 0)
for attr in (a for a in vars(oggtag) if not a.startswith('_')):
eq_(getattr(emptytag, attr), getattr(oggtag, attr))


def test_invalid_wav_file():
with open(testfile_mp3_3sec, 'rb') as af:
wavetag = Wave(af, 0)
wavetag.load(True, True)
emptytag = TinyTag(None, 0)
for attr in (a for a in vars(wavetag) if not a.startswith('_')):
eq_(getattr(emptytag, attr), getattr(wavetag, attr))


def test_invalid_flac_file():
with open(testfile_mp3_3sec, 'rb') as af:
flactag = Flac(af, 0)
flactag.load(True, True)
emptytag = TinyTag(None, 0)
for attr in (a for a in vars(flactag) if not a.startswith('_')):
eq_(getattr(emptytag, attr), getattr(flactag, attr))


if __name__ == '__main__':
nose.runmodule()

0 comments on commit bf36544

Please sign in to comment.