diff --git a/docs/index.rst b/docs/index.rst index f9b892d..c31655b 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -141,6 +141,7 @@ v0.13.0 - Add a mapping compatible with Plex and ffmpeg for the "original date" fields. - Remove an unnecessary dependency on `six`. +- Replace `imghdr` with `filetype` to support Python 3.13. v0.12.0 ''''''' diff --git a/mediafile.py b/mediafile.py index 5f488d7..8a8e835 100644 --- a/mediafile.py +++ b/mediafile.py @@ -46,8 +46,8 @@ import codecs import datetime import enum +import filetype import functools -import imghdr import logging import math import os @@ -78,8 +78,6 @@ 'wav': 'WAVE', } -PREFERRED_IMAGE_EXTENSIONS = {'jpeg': 'jpg'} - # Exceptions. @@ -346,52 +344,17 @@ def _sc_encode(gain, peak): # Cover art and other images. -def _imghdr_what_wrapper(data): - """A wrapper around imghdr.what to account for jpeg files that can only be - identified as such using their magic bytes - See #1545 - See https://github.com/file/file/blob/master/magic/Magdir/jpeg#L12 - """ - # imghdr.what returns none for jpegs with only the magic bytes, so - # _wider_test_jpeg is run in that case. It still returns None if it didn't - # match such a jpeg file. - return imghdr.what(None, h=data) or _wider_test_jpeg(data) - - -def _wider_test_jpeg(data): - """Test for a jpeg file following the UNIX file implementation which - uses the magic bytes rather than just looking for the bytes that - represent 'JFIF' or 'EXIF' at a fixed position. - """ - if data[:2] == b'\xff\xd8': - return 'jpeg' - def image_mime_type(data): """Return the MIME type of the image data (a bytestring). """ - # This checks for a jpeg file with only the magic bytes (unrecognized by - # imghdr.what). imghdr.what returns none for that type of file, so - # _wider_test_jpeg is run in that case. It still returns None if it didn't - # match such a jpeg file. - kind = _imghdr_what_wrapper(data) - if kind in ['gif', 'jpeg', 'png', 'tiff', 'bmp']: - return 'image/{0}'.format(kind) - elif kind == 'pgm': - return 'image/x-portable-graymap' - elif kind == 'pbm': - return 'image/x-portable-bitmap' - elif kind == 'ppm': - return 'image/x-portable-pixmap' - elif kind == 'xbm': - return 'image/x-xbitmap' - else: - return 'image/x-{0}'.format(kind) + return filetype.guess_mime(data) def image_extension(data): - ext = _imghdr_what_wrapper(data) - return PREFERRED_IMAGE_EXTENSIONS.get(ext, ext) + ext = filetype.guess_extension(data) + # imghdr returned "tiff", so we should keep returning it with filetype. + return ext if ext != 'tif' else 'tiff' class ImageType(enum.Enum): diff --git a/pyproject.toml b/pyproject.toml index f558885..1f08159 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,6 +10,7 @@ home-page = "https://github.com/beetbox/mediafile" description-file = "README.rst" requires = [ "mutagen>=1.46", + "filetype>=1.2.0", ] requires-python = ">=3.7" classifiers = [ diff --git a/test/rsrc/only-magic-bytes.jpg b/test/rsrc/only-magic-bytes.jpg deleted file mode 100644 index ca0cf22..0000000 Binary files a/test/rsrc/only-magic-bytes.jpg and /dev/null differ diff --git a/test/test_mediafile_edge.py b/test/test_mediafile_edge.py index 337c610..4796525 100644 --- a/test/test_mediafile_edge.py +++ b/test/test_mediafile_edge.py @@ -78,17 +78,6 @@ def test_old_ape_version_bitrate(self): f = mediafile.MediaFile(media_file) self.assertEqual(f.bitrate, 0) - def test_only_magic_bytes_jpeg(self): - # Some jpeg files can only be recognized by their magic bytes and as - # such aren't recognized by imghdr. Ensure that this still works thanks - # to our own follow up mimetype detection based on - # https://github.com/file/file/blob/master/magic/Magdir/jpeg#L12 - magic_bytes_file = os.path.join(_common.RSRC, b'only-magic-bytes.jpg') - with open(magic_bytes_file, 'rb') as f: - jpg_data = f.read() - self.assertEqual( - mediafile._imghdr_what_wrapper(jpg_data), 'jpeg') - def test_soundcheck_non_ascii(self): # Make sure we don't crash when the iTunes SoundCheck field contains # non-ASCII binary data.