From a493072efa54882018c006bef6ce4517e734150b Mon Sep 17 00:00:00 2001 From: Allen Wittenauer Date: Sun, 7 May 2023 09:53:14 -0700 Subject: [PATCH] Beginning support of AVIF; fix some image bugs (#797) --- CHANGELOG.md | 2 ++ nowplaying/utils.py | 32 +++++++++++++++++++++++++++++--- requirements-run.txt | 3 ++- tests/images/1x1.jpg | Bin 0 -> 3450 bytes tests/test_utils.py | 25 +++++++++++++++++++++++++ 5 files changed, 58 insertions(+), 4 deletions(-) create mode 100644 tests/images/1x1.jpg diff --git a/CHANGELOG.md b/CHANGELOG.md index aefcd6bd..6c944830 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ * Experimental feature: Given an option to use Musicbrainz to fill in missing metadata based only on artist and title (and album if available). +* Add support for AVIF graphics. At some point, all of the templates will be + updated to handle multiple formats so be prepared! * On Windows, the ability to read from Windows Media Transport compatible software, such as Amazon Music, Soundcloud, and likely others. (Ironically, Windows Media Player doesn't appear to use it for whatever reason.) diff --git a/nowplaying/utils.py b/nowplaying/utils.py index 22139206..b7ec37cb 100755 --- a/nowplaying/utils.py +++ b/nowplaying/utils.py @@ -16,6 +16,7 @@ import jinja2 import normality import PIL.Image +import pillow_avif # pylint: disable=unused-import STRIPWORDLIST = ['clean', 'dirty', 'explicit', 'official music video'] STRIPRELIST = [ @@ -145,13 +146,38 @@ def image2png(rawdata): return rawdata try: - origimage = rawdata - imgbuffer = io.BytesIO(origimage) + imgbuffer = io.BytesIO(rawdata) logging.getLogger('PIL.TiffImagePlugin').setLevel(logging.CRITICAL + 1) logging.getLogger('PIL.PngImagePlugin').setLevel(logging.CRITICAL + 1) image = PIL.Image.open(imgbuffer) + imgbuffer = io.BytesIO(rawdata) if image.format != 'PNG': - image.convert(mode='RGB').save(imgbuffer, format='png') + image.convert(mode='RGB').save(imgbuffer, format='PNG') + except Exception as error: #pylint: disable=broad-except + logging.debug(error) + return None + logging.debug("Leaving image2png") + return imgbuffer.getvalue() + + +def image2avif(rawdata): + ''' convert an image to png ''' + + if not rawdata: + return None + + if rawdata.startswith(b'\x00\x00\x00 ftypavif'): + logging.debug('already AVIF, skipping convert') + return rawdata + + try: + imgbuffer = io.BytesIO(rawdata) + logging.getLogger('PIL.TiffImagePlugin').setLevel(logging.CRITICAL + 1) + logging.getLogger('PIL.PngImagePlugin').setLevel(logging.CRITICAL + 1) + image = PIL.Image.open(imgbuffer) + imgbuffer = io.BytesIO(rawdata) + if image.format != 'AVIF': + image.convert(mode='RGB').save(imgbuffer, format='AVIF') except Exception as error: #pylint: disable=broad-except logging.debug(error) return None diff --git a/requirements-run.txt b/requirements-run.txt index 3fcd56c8..06ea7438 100644 --- a/requirements-run.txt +++ b/requirements-run.txt @@ -11,6 +11,7 @@ nltk==3.8.1 normality==2.4.0 pid==3.0.4 pillow==9.5.0 +pillow_avif_plugin==1.3.1 pyacoustid==1.2.2 pypresence==4.2.1 PySide6==6.5.0 @@ -43,4 +44,4 @@ python-dateutil # # required to install # -versioningit==2.2.0 \ No newline at end of file +versioningit==2.2.0 diff --git a/tests/images/1x1.jpg b/tests/images/1x1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0049552370b1d288ac691a5e461c226cf9ff502d GIT binary patch literal 3450 zcmeHKTW}Lq82-=hCfT$+G;KjGopIT=TA3!h*<2tyq#c@4T4ti9R6rh_*(BR0w#kOg zVwTMK0H`QW;uU==cmW+BM8yj}D2k$vFL(tUXRLTZXB??F)CbmoH!*d*%rL&1e>P|T z-ShqbIp25k@U3;q`V?B$4UY~3Awc>_AAmIn@2oL%lK{yiEC&D<+@uv~%n*V@2e_OZ zU?W*t&u=8XH4KHAAm9jX_?z6F1Ez+r;N4Df6{nMPAIq8QOkOcGog33NE))piEOQs* zbZE!?sadkH`YcuNMrP@S9C09tQb-mfvKZwgNfsqp3L_iuiL7B1WkD#E0!l_pse!bX z7fhun1OuV~s|L)XlAcx#E~QT83UU6smtN+%Tqe$MiX_EkF`;I2BNbiUP`P#@U71eD zGWn96GLl;*N%*?+c0tc#!Z`V*pkku zlT+E;O(kR7&1G|YW!J5@-M;&dJNMju&%O8E|G>TnA3E^xBac3I@X+H=JpIhGN1l8B zg%^*$`r7f=-#GE+TW`Pn-uoYX_|d7;XXZXT`}r4Nes%8aZ|1-M;m4nTzVOShzuCOd zSXXJ`SY`GXUOwbSF$~RkY+i&a+r)j0Ygy3Ux_Z4w+1kcSJJ|NYz55Rz^LB;C&o55t zC!3aZhv$0cZE6m)OCz@PKQXHlTj2FEB=Bp-4hk=tqVT9_493ma;r6(l@YI5J!fQu! uJuGZO>`Iz;;nmE#+4|fiC)OwUeL}1=u!tu3XQF*D02eRz9@qoc>E8htkAYPH literal 0 HcmV?d00001 diff --git a/tests/test_utils.py b/tests/test_utils.py index 2473d5c1..025b3c3a 100755 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -158,3 +158,28 @@ def test_basicstrip_ovm4(): title = nowplaying.utils.titlestripper_basic(title=metadata['title']) assert metadata['title'] == 'Clean - Official Music Video' assert title == 'Clean' + + +def test_image2png(getroot): + ''' check png image conversion ''' + filename = getroot.joinpath('tests', 'images', '1x1.jpg') + with open(filename, 'rb') as fhin: + image = fhin.read() + + pngdata = nowplaying.utils.image2png(image) + + pngdata2 = nowplaying.utils.image2png(pngdata) + assert pngdata.startswith(b'\211PNG\r\n\032\n') + assert pngdata2 == pngdata + + +def test_image2avif(getroot): + ''' check png image conversion ''' + filename = getroot.joinpath('tests', 'images', '1x1.jpg') + with open(filename, 'rb') as fhin: + image = fhin.read() + + avifdata = nowplaying.utils.image2avif(image) + avifdata2 = nowplaying.utils.image2avif(avifdata) + assert avifdata.startswith(b'\x00\x00\x00 ftypavif') + assert avifdata2 == avifdata