diff --git a/beets/autotag/__init__.py b/beets/autotag/__init__.py index e380b1ae1..59c348efd 100644 --- a/beets/autotag/__init__.py +++ b/beets/autotag/__init__.py @@ -111,6 +111,9 @@ def albums_in_dir(path): i = library.Item.from_path(os.path.join(root, filename)) except mediafile.FileTypeError: pass + except mediafile.UnreadableFileError: + #FIXME log an error + pass else: items.append(i) diff --git a/beets/library.py b/beets/library.py index b0d088df4..2b2a48cdb 100644 --- a/beets/library.py +++ b/beets/library.py @@ -20,7 +20,7 @@ import shutil import sys from string import Template import logging -from beets.mediafile import MediaFile, FileTypeError +from beets.mediafile import MediaFile, UnreadableFileError, FileTypeError MAX_FILENAME_LENGTH = 200 @@ -634,6 +634,8 @@ class BaseLibrary(object): item = Item.from_path(_normpath(f)) except FileTypeError: log.warn(f + ' of unknown type, skipping') + except UnreadableFileError: + log.error(f + ' is unreadable, skipping') self.add(item, copy) diff --git a/beets/mediafile.py b/beets/mediafile.py index 427388567..d62cea8f1 100644 --- a/beets/mediafile.py +++ b/beets/mediafile.py @@ -30,16 +30,26 @@ or the empty string). """ import mutagen +import mutagen.mp3 +import mutagen.flac +import mutagen.monkeysaudio import datetime import re __all__ = ['FileTypeError', 'MediaFile'] # Currently allowed values for type: -# mp3, mp4 -class FileTypeError(IOError): +# mp3, mp4, flac, ogg, ape + +# Raised for any file MediaFile can't read. +class UnreadableFileError(IOError): pass +# Raised for files that don't seem to have a type MediaFile supports. +class FileTypeError(UnreadableFileError): + pass + + @@ -417,10 +427,18 @@ class MediaFile(object): """ def __init__(self, path): + """Constructs a new MediaFile reflecting the file at path. May + throw UnreadableFileError. + """ + unreadable_exc = ( + mutagen.mp3.HeaderNotFoundError, + mutagen.flac.FLACNoHeaderError, + mutagen.monkeysaudio.MonkeysAudioHeaderError, + ) try: self.mgfile = mutagen.File(path) - except mutagen.mp3.HeaderNotFoundError: - raise FileTypeError('Mutagen could not read file') + except unreadable_exc: + raise UnreadableFileError('Mutagen could not read file') if self.mgfile is None: # Mutagen couldn't guess the type raise FileTypeError('file type unsupported by Mutagen') diff --git a/beets/ui.py b/beets/ui.py index e9a460d05..449043c4a 100644 --- a/beets/ui.py +++ b/beets/ui.py @@ -356,6 +356,9 @@ def import_files(lib, paths, copy=True, write=True, autot=True, logpath=None): item = library.Item.from_path(filepath) except FileTypeError: continue + except mediafile.UnreadableFileError: + #FIXME log an error + pass # Add the item to the library, copying if requested. if copy: diff --git a/test/test_mediafile.py b/test/test_mediafile.py index 7a354ab3b..c90e2e8d9 100644 --- a/test/test_mediafile.py +++ b/test/test_mediafile.py @@ -61,6 +61,33 @@ class EdgeTest(unittest.TestCase): self.assertEqual(f.disc, 4) self.assertEqual(f.disctotal, 5) +class SafetyTest(unittest.TestCase): + def _exccheck(self, fn, exc): + fn = os.path.join('rsrc', fn) + open(fn, 'a').close() # create an empty file (a la touch) + self.assertRaises(exc, beets.mediafile.MediaFile, fn) + os.unlink(fn) # delete the temporary file + + def test_corrupt_mp3_raises_unreadablefileerror(self): + # Make sure we catch Mutagen reading errors appropriately. + self._exccheck('corrupt.mp3', beets.mediafile.UnreadableFileError) + + def test_corrupt_mp4_raises_unreadablefileerror(self): + self._exccheck('corrupt.m4a', beets.mediafile.UnreadableFileError) + + def test_corrupt_flac_raises_unreadablefileerror(self): + self._exccheck('corrupt.flac', beets.mediafile.UnreadableFileError) + + def test_corrupt_ogg_raises_unreadablefileerror(self): + self._exccheck('corrupt.ogg', beets.mediafile.UnreadableFileError) + + def test_corrupt_monkeys_raises_unreadablefileerror(self): + self._exccheck('corrupt.ape', beets.mediafile.UnreadableFileError) + + def test_invalid_extension_raises_filetypeerror(self): + self._exccheck('something.unknown', beets.mediafile.FileTypeError) + + def suite(): return unittest.TestLoader().loadTestsFromName(__name__)