Пример #1
0
def get_image_attributes(file_or_path, close=False):
    """
    Returns a tuple with the animated(boolean) and format(str) of an image.
    Firstly the file will be accessed directly at a supplied path. Otherwise
    set `close` to True if `file_or_path` is a file to ensure it is closed.

    If there is an error when accessing the file or `IMAGEBOARD_FORMATS` is not
    configured correctly the defaults will be returned (False, None).
    """
    from imageboard.conf import IMAGEBOARD_FORMATS as formats
    from imageboard.utils import force_close_reader
    from imageio import get_reader
    from os.path import exists

    _animated = False
    _format = None
    f = None

    if hasattr(file_or_path, 'path'):
        if not exists(file_or_path.path):
            # No file at path. Should raise an error here but we will return
            # defaults instead.
            return _animated, _format
        file = file_or_path.path
        close = False
    elif hasattr(file_or_path, 'read'):
        file = file_or_path
        if not exists(file):
            # No file at path. Should raise an error here but we will return
            # defaults instead.
            return _animated, _format
        file.open()
        file.seek(0)
    else:
        raise AttributeError("No file or path given.")

    try:
        f = get_reader(file)
        try:
            _format = formats[f.format.name]['code']
            _animated = formats[f.format.name]['animated']
        except KeyError:
            # Format type or animated not in formats. See `imageboard.conf` for
            # information on how to configure the IMAGEBOARD_FORMATS setting
            # to support formats correctly.
            raise ValueError

        # Check if GIF has multiple frames. Extra support is required for other
        # formats with either single or multiple frames.
        # Request support here: https://github.com/arcynum/pifti/issues
        if _animated and _format == 'GIF':
            try:
                f.get_data(1)
            except ValueError:
                # GIF only has one frame, is not animated
                _animated = False
    except ValueError:
        # No reader or format, fail silently
        pass
    except OSError:
        # Out of memory for a new reader (bug #48)
        pass
    finally:
        # Close the file
        if close:
            file.close()
        # Close the reader
        force_close_reader(f)

    return _animated, _format
Пример #2
0
    def to_python(self, data):
        """
        Checks that the file-upload field data contains a valid source file
        (GIF, JPG, PNG, WEBM, MP4, etc) using imageio plugins.
        """
        f = super(ThumbnailerExtField, self).to_python(data)
        if f is None:
            return None

        # Check filesize limit
        # Nginx client_max_body_size is 5MB
        if f.size > 5 * 1024 * 1024:
            raise ValidationError(self.error_messages['max_size'],
                                  code='max_size')

        from pathlib import PurePath

        # Check for file extension
        ext = PurePath(f.name).suffix
        if ext == '':
            raise ValidationError(self.error_messages['invalid_extension'],
                                  code='invalid_extension')

        # Get temporary file path or raw bytes
        if hasattr(data, 'temporary_file_path'):
            file = data.temporary_file_path()
        else:
            if hasattr(data, 'read'):
                file = BytesIO(data.read())
            else:
                file = BytesIO(data['content'])

        from PIL import Image
        from imageio import get_reader

        try:
            # Firstly try and recognise BytesIO
            file = get_reader(file)
            # Send data to PIL
            f.image = Image.fromarray(file.get_data(0))
            # Close the reader
            force_close_reader(file)
        except ValueError:
            # Create a local file
            with NamedTemporaryFile(suffix=ext) as n:
                for chunk in data.chunks():
                    n.write(chunk)
                try:
                    # Try and read from temporary file
                    file = get_reader(n.name)
                    # Send data to PIL
                    f.image = Image.fromarray(file.get_data(0))
                except OSError:
                    # FFmpeg cannot read video meta
                    # May be out of memory for a new reader (bug #48)
                    params = { 'file': f.name }
                    raise ValidationError(self.error_messages['corrupt_file'],
                                          code='corrupt_file',
                                          params=params)
                except Exception:
                    # imageio doesn't recognize it as an image.
                    params = { 'types': self.types }
                    raise ValidationError(self.error_messages['invalid_file'],
                                          code='invalid_file',
                                          params=params)
                finally:
                    # Close Named Tempoary File
                    n.close()
                    # Make sure any open reader is closed
                    force_close_reader(file)

        # Check if this format is enabled
        # See ``imageboard.settings`` for more information
        if file.format.name in formats.keys():
            # MIME type is not supported with imageio
            # Thus if the content type is not detected, it will remain as None
            f.content_type = Image.MIME.get(f.image.format)

            if hasattr(f, 'seek') and callable(f.seek):
                f.seek(0)

            return f
        else:
            params = { 'types': self.types }
            raise ValidationError(self.error_messages['unsupported'],
                                  code='unsupported',
                                  params=params)