Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Does Sorl Thumbnail Support GIFs? #687

Open
jaradc opened this issue Feb 7, 2022 · 2 comments
Open

Does Sorl Thumbnail Support GIFs? #687

jaradc opened this issue Feb 7, 2022 · 2 comments

Comments

@jaradc
Copy link

jaradc commented Feb 7, 2022

Default image format
supported formats are: 'JPEG', 'PNG'. This also implicitly sets the filename extension. This can be overridden by individual options.

I have THUMBNAIL_PRESERVE_FORMAT = True and noticed sorl.thumbnail.base.EXTENSIONS contains a 'GIF' mapping... and I know my GIF is getting uploaded as a GIF, but the cache is not caching a GIF, it's caching a non-animated gif (probably the first frame).

@jaradc
Copy link
Author

jaradc commented Feb 7, 2022

Answer was no. Nobody will ever answer this so if this helps anyone, I made a special case for GIFs by creating a subclass of Pil Engine. You also need to modify _create_thumbnail() to bypass all the transformations. My gif is exactly the size I want it to be when uploaded so size may occur (don't know, didn't test).

import logging
import os.path
from io import BytesIO

from PIL import ImageFile
from sorl.thumbnail import default
from sorl.thumbnail.base import ThumbnailBackend, EXTENSIONS
from sorl.thumbnail.conf import settings
from sorl.thumbnail.engines.pil_engine import Engine
from sorl.thumbnail.helpers import tokey, serialize
from sorl.thumbnail.parsers import parse_geometry

logger = logging.getLogger(__name__)


class CustomThumbnailBackend(ThumbnailBackend):

    def _create_thumbnail(self, source_image, geometry_string, options,
                          thumbnail):
        """
        Creates the thumbnail by using default.engine
        """
        logger.debug('Creating thumbnail file [%s] at [%s] with [%s]',
                     thumbnail.name, geometry_string, options)
        ratio = default.engine.get_image_ratio(source_image, options)
        geometry = parse_geometry(geometry_string, ratio)
        if source_image.format != "GIF":
            image = default.engine.create(source_image, geometry, options)
        else:
            image = source_image
        default.engine.write(image, options, thumbnail)
        # It's much cheaper to set the size here
        size = default.engine.get_image_size(image)
        thumbnail.set_size(size)


class CustomPilEngine(Engine):
    """Allows the caching of GIF files"""

    def _get_raw_data(self, image, format_, quality, image_info=None, progressive=False):
        # Increase (but never decrease) PIL buffer size
        ImageFile.MAXBLOCK = max(ImageFile.MAXBLOCK, image.size[0] * image.size[1])
        buffer = BytesIO()

        if format_ == "GIF":

            params = {
                'format': format_,
                'save_all': True,
                'loop': 0
            }

            raw_data = None

            try:
                image.save(buffer, **params)
            except OSError:
                pass
            else:
                raw_data = buffer.getvalue()
            finally:
                buffer.close()

            return raw_data

        else:

            params = {
                'format': format_,
                'quality': quality,
                'optimize': 1,
            }

            # keeps icc_profile
            if 'icc_profile' in image_info:
                params['icc_profile'] = image_info['icc_profile']

            raw_data = None

            if format_ == 'JPEG' and progressive:
                params['progressive'] = True
            try:
                # Do not save unnecessary exif data for smaller thumbnail size
                params.pop('exif', {})
                image.save(buffer, **params)
            except OSError:
                # Try without optimization.
                params.pop('optimize')
                image.save(buffer, **params)
            else:
                raw_data = buffer.getvalue()
            finally:
                buffer.close()

            return raw_data

@Headmaster11
Copy link

Above solution #687 (comment) will only return the actual GIF to you, without doing any crop

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants