We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
test_pipeline_images.py fails with "TypeError: Skipped expected string as 'msg' parameter, got 'bool' instead."
See e.g. https://github.com/scrapy/scrapy/pull/5019/checks?check_run_id=2012658916

This should be related to the skip attribute, though I'm not sure why did it start happening now.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `scrapy/pipelines/images.py`
Content:
```
1 """
2 Images Pipeline
3 
4 See documentation in topics/media-pipeline.rst
5 """
6 import functools
7 import hashlib
8 from contextlib import suppress
9 from io import BytesIO
10 
11 from itemadapter import ItemAdapter
12 from PIL import Image
13 
14 from scrapy.exceptions import DropItem
15 from scrapy.http import Request
16 from scrapy.pipelines.files import FileException, FilesPipeline
17 # TODO: from scrapy.pipelines.media import MediaPipeline
18 from scrapy.settings import Settings
19 from scrapy.utils.misc import md5sum
20 from scrapy.utils.python import to_bytes
21 
22 
23 class NoimagesDrop(DropItem):
24     """Product with no images exception"""
25 
26 
27 class ImageException(FileException):
28     """General image error exception"""
29 
30 
31 class ImagesPipeline(FilesPipeline):
32     """Abstract pipeline that implement the image thumbnail generation logic
33 
34     """
35 
36     MEDIA_NAME = 'image'
37 
38     # Uppercase attributes kept for backward compatibility with code that subclasses
39     # ImagesPipeline. They may be overridden by settings.
40     MIN_WIDTH = 0
41     MIN_HEIGHT = 0
42     EXPIRES = 90
43     THUMBS = {}
44     DEFAULT_IMAGES_URLS_FIELD = 'image_urls'
45     DEFAULT_IMAGES_RESULT_FIELD = 'images'
46 
47     def __init__(self, store_uri, download_func=None, settings=None):
48         super().__init__(store_uri, settings=settings, download_func=download_func)
49 
50         if isinstance(settings, dict) or settings is None:
51             settings = Settings(settings)
52 
53         resolve = functools.partial(self._key_for_pipe,
54                                     base_class_name="ImagesPipeline",
55                                     settings=settings)
56         self.expires = settings.getint(
57             resolve("IMAGES_EXPIRES"), self.EXPIRES
58         )
59 
60         if not hasattr(self, "IMAGES_RESULT_FIELD"):
61             self.IMAGES_RESULT_FIELD = self.DEFAULT_IMAGES_RESULT_FIELD
62         if not hasattr(self, "IMAGES_URLS_FIELD"):
63             self.IMAGES_URLS_FIELD = self.DEFAULT_IMAGES_URLS_FIELD
64 
65         self.images_urls_field = settings.get(
66             resolve('IMAGES_URLS_FIELD'),
67             self.IMAGES_URLS_FIELD
68         )
69         self.images_result_field = settings.get(
70             resolve('IMAGES_RESULT_FIELD'),
71             self.IMAGES_RESULT_FIELD
72         )
73         self.min_width = settings.getint(
74             resolve('IMAGES_MIN_WIDTH'), self.MIN_WIDTH
75         )
76         self.min_height = settings.getint(
77             resolve('IMAGES_MIN_HEIGHT'), self.MIN_HEIGHT
78         )
79         self.thumbs = settings.get(
80             resolve('IMAGES_THUMBS'), self.THUMBS
81         )
82 
83     @classmethod
84     def from_settings(cls, settings):
85         s3store = cls.STORE_SCHEMES['s3']
86         s3store.AWS_ACCESS_KEY_ID = settings['AWS_ACCESS_KEY_ID']
87         s3store.AWS_SECRET_ACCESS_KEY = settings['AWS_SECRET_ACCESS_KEY']
88         s3store.AWS_ENDPOINT_URL = settings['AWS_ENDPOINT_URL']
89         s3store.AWS_REGION_NAME = settings['AWS_REGION_NAME']
90         s3store.AWS_USE_SSL = settings['AWS_USE_SSL']
91         s3store.AWS_VERIFY = settings['AWS_VERIFY']
92         s3store.POLICY = settings['IMAGES_STORE_S3_ACL']
93 
94         gcs_store = cls.STORE_SCHEMES['gs']
95         gcs_store.GCS_PROJECT_ID = settings['GCS_PROJECT_ID']
96         gcs_store.POLICY = settings['IMAGES_STORE_GCS_ACL'] or None
97 
98         ftp_store = cls.STORE_SCHEMES['ftp']
99         ftp_store.FTP_USERNAME = settings['FTP_USER']
100         ftp_store.FTP_PASSWORD = settings['FTP_PASSWORD']
101         ftp_store.USE_ACTIVE_MODE = settings.getbool('FEED_STORAGE_FTP_ACTIVE')
102 
103         store_uri = settings['IMAGES_STORE']
104         return cls(store_uri, settings=settings)
105 
106     def file_downloaded(self, response, request, info, *, item=None):
107         return self.image_downloaded(response, request, info, item=item)
108 
109     def image_downloaded(self, response, request, info, *, item=None):
110         checksum = None
111         for path, image, buf in self.get_images(response, request, info, item=item):
112             if checksum is None:
113                 buf.seek(0)
114                 checksum = md5sum(buf)
115             width, height = image.size
116             self.store.persist_file(
117                 path, buf, info,
118                 meta={'width': width, 'height': height},
119                 headers={'Content-Type': 'image/jpeg'})
120         return checksum
121 
122     def get_images(self, response, request, info, *, item=None):
123         path = self.file_path(request, response=response, info=info, item=item)
124         orig_image = Image.open(BytesIO(response.body))
125 
126         width, height = orig_image.size
127         if width < self.min_width or height < self.min_height:
128             raise ImageException("Image too small "
129                                  f"({width}x{height} < "
130                                  f"{self.min_width}x{self.min_height})")
131 
132         image, buf = self.convert_image(orig_image)
133         yield path, image, buf
134 
135         for thumb_id, size in self.thumbs.items():
136             thumb_path = self.thumb_path(request, thumb_id, response=response, info=info)
137             thumb_image, thumb_buf = self.convert_image(image, size)
138             yield thumb_path, thumb_image, thumb_buf
139 
140     def convert_image(self, image, size=None):
141         if image.format == 'PNG' and image.mode == 'RGBA':
142             background = Image.new('RGBA', image.size, (255, 255, 255))
143             background.paste(image, image)
144             image = background.convert('RGB')
145         elif image.mode == 'P':
146             image = image.convert("RGBA")
147             background = Image.new('RGBA', image.size, (255, 255, 255))
148             background.paste(image, image)
149             image = background.convert('RGB')
150         elif image.mode != 'RGB':
151             image = image.convert('RGB')
152 
153         if size:
154             image = image.copy()
155             image.thumbnail(size, Image.ANTIALIAS)
156 
157         buf = BytesIO()
158         image.save(buf, 'JPEG')
159         return image, buf
160 
161     def get_media_requests(self, item, info):
162         urls = ItemAdapter(item).get(self.images_urls_field, [])
163         return [Request(u) for u in urls]
164 
165     def item_completed(self, results, item, info):
166         with suppress(KeyError):
167             ItemAdapter(item)[self.images_result_field] = [x for ok, x in results if ok]
168         return item
169 
170     def file_path(self, request, response=None, info=None, *, item=None):
171         image_guid = hashlib.sha1(to_bytes(request.url)).hexdigest()
172         return f'full/{image_guid}.jpg'
173 
174     def thumb_path(self, request, thumb_id, response=None, info=None):
175         thumb_guid = hashlib.sha1(to_bytes(request.url)).hexdigest()
176         return f'thumbs/{thumb_id}/{thumb_guid}.jpg'
177
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/scrapy/pipelines/images.py b/scrapy/pipelines/images.py
--- a/scrapy/pipelines/images.py
+++ b/scrapy/pipelines/images.py
@@ -9,9 +9,8 @@
 from io import BytesIO
 
 from itemadapter import ItemAdapter
-from PIL import Image
 
-from scrapy.exceptions import DropItem
+from scrapy.exceptions import DropItem, NotConfigured
 from scrapy.http import Request
 from scrapy.pipelines.files import FileException, FilesPipeline
 # TODO: from scrapy.pipelines.media import MediaPipeline
@@ -45,6 +44,14 @@
     DEFAULT_IMAGES_RESULT_FIELD = 'images'
 
     def __init__(self, store_uri, download_func=None, settings=None):
+        try:
+            from PIL import Image
+            self._Image = Image
+        except ImportError:
+            raise NotConfigured(
+                'ImagesPipeline requires installing Pillow 4.0.0 or later'
+            )
+
         super().__init__(store_uri, settings=settings, download_func=download_func)
 
         if isinstance(settings, dict) or settings is None:
@@ -121,7 +128,7 @@
 
     def get_images(self, response, request, info, *, item=None):
         path = self.file_path(request, response=response, info=info, item=item)
-        orig_image = Image.open(BytesIO(response.body))
+        orig_image = self._Image.open(BytesIO(response.body))
 
         width, height = orig_image.size
         if width < self.min_width or height < self.min_height:
@@ -139,12 +146,12 @@
 
     def convert_image(self, image, size=None):
         if image.format == 'PNG' and image.mode == 'RGBA':
-            background = Image.new('RGBA', image.size, (255, 255, 255))
+            background = self._Image.new('RGBA', image.size, (255, 255, 255))
             background.paste(image, image)
             image = background.convert('RGB')
         elif image.mode == 'P':
             image = image.convert("RGBA")
-            background = Image.new('RGBA', image.size, (255, 255, 255))
+            background = self._Image.new('RGBA', image.size, (255, 255, 255))
             background.paste(image, image)
             image = background.convert('RGB')
         elif image.mode != 'RGB':
@@ -152,7 +159,7 @@
 
         if size:
             image = image.copy()
-            image.thumbnail(size, Image.ANTIALIAS)
+            image.thumbnail(size, self._Image.ANTIALIAS)
 
         buf = BytesIO()
         image.save(buf, 'JPEG')

{"golden_diff": "diff --git a/scrapy/pipelines/images.py b/scrapy/pipelines/images.py\n--- a/scrapy/pipelines/images.py\n+++ b/scrapy/pipelines/images.py\n@@ -9,9 +9,8 @@\n from io import BytesIO\n \n from itemadapter import ItemAdapter\n-from PIL import Image\n \n-from scrapy.exceptions import DropItem\n+from scrapy.exceptions import DropItem, NotConfigured\n from scrapy.http import Request\n from scrapy.pipelines.files import FileException, FilesPipeline\n # TODO: from scrapy.pipelines.media import MediaPipeline\n@@ -45,6 +44,14 @@\n     DEFAULT_IMAGES_RESULT_FIELD = 'images'\n \n     def __init__(self, store_uri, download_func=None, settings=None):\n+        try:\n+            from PIL import Image\n+            self._Image = Image\n+        except ImportError:\n+            raise NotConfigured(\n+                'ImagesPipeline requires installing Pillow 4.0.0 or later'\n+            )\n+\n         super().__init__(store_uri, settings=settings, download_func=download_func)\n \n         if isinstance(settings, dict) or settings is None:\n@@ -121,7 +128,7 @@\n \n     def get_images(self, response, request, info, *, item=None):\n         path = self.file_path(request, response=response, info=info, item=item)\n-        orig_image = Image.open(BytesIO(response.body))\n+        orig_image = self._Image.open(BytesIO(response.body))\n \n         width, height = orig_image.size\n         if width < self.min_width or height < self.min_height:\n@@ -139,12 +146,12 @@\n \n     def convert_image(self, image, size=None):\n         if image.format == 'PNG' and image.mode == 'RGBA':\n-            background = Image.new('RGBA', image.size, (255, 255, 255))\n+            background = self._Image.new('RGBA', image.size, (255, 255, 255))\n             background.paste(image, image)\n             image = background.convert('RGB')\n         elif image.mode == 'P':\n             image = image.convert(\"RGBA\")\n-            background = Image.new('RGBA', image.size, (255, 255, 255))\n+            background = self._Image.new('RGBA', image.size, (255, 255, 255))\n             background.paste(image, image)\n             image = background.convert('RGB')\n         elif image.mode != 'RGB':\n@@ -152,7 +159,7 @@\n \n         if size:\n             image = image.copy()\n-            image.thumbnail(size, Image.ANTIALIAS)\n+            image.thumbnail(size, self._Image.ANTIALIAS)\n \n         buf = BytesIO()\n         image.save(buf, 'JPEG')\n", "issue": "test_pipeline_images.py fails with \"TypeError: Skipped expected string as 'msg' parameter, got 'bool' instead.\"\nSee e.g. https://github.com/scrapy/scrapy/pull/5019/checks?check_run_id=2012658916\r\n\r\nThis should be related to the skip attribute, though I'm not sure why did it start happening now.\n", "before_files": [{"content": "\"\"\"\nImages Pipeline\n\nSee documentation in topics/media-pipeline.rst\n\"\"\"\nimport functools\nimport hashlib\nfrom contextlib import suppress\nfrom io import BytesIO\n\nfrom itemadapter import ItemAdapter\nfrom PIL import Image\n\nfrom scrapy.exceptions import DropItem\nfrom scrapy.http import Request\nfrom scrapy.pipelines.files import FileException, FilesPipeline\n# TODO: from scrapy.pipelines.media import MediaPipeline\nfrom scrapy.settings import Settings\nfrom scrapy.utils.misc import md5sum\nfrom scrapy.utils.python import to_bytes\n\n\nclass NoimagesDrop(DropItem):\n    \"\"\"Product with no images exception\"\"\"\n\n\nclass ImageException(FileException):\n    \"\"\"General image error exception\"\"\"\n\n\nclass ImagesPipeline(FilesPipeline):\n    \"\"\"Abstract pipeline that implement the image thumbnail generation logic\n\n    \"\"\"\n\n    MEDIA_NAME = 'image'\n\n    # Uppercase attributes kept for backward compatibility with code that subclasses\n    # ImagesPipeline. They may be overridden by settings.\n    MIN_WIDTH = 0\n    MIN_HEIGHT = 0\n    EXPIRES = 90\n    THUMBS = {}\n    DEFAULT_IMAGES_URLS_FIELD = 'image_urls'\n    DEFAULT_IMAGES_RESULT_FIELD = 'images'\n\n    def __init__(self, store_uri, download_func=None, settings=None):\n        super().__init__(store_uri, settings=settings, download_func=download_func)\n\n        if isinstance(settings, dict) or settings is None:\n            settings = Settings(settings)\n\n        resolve = functools.partial(self._key_for_pipe,\n                                    base_class_name=\"ImagesPipeline\",\n                                    settings=settings)\n        self.expires = settings.getint(\n            resolve(\"IMAGES_EXPIRES\"), self.EXPIRES\n        )\n\n        if not hasattr(self, \"IMAGES_RESULT_FIELD\"):\n            self.IMAGES_RESULT_FIELD = self.DEFAULT_IMAGES_RESULT_FIELD\n        if not hasattr(self, \"IMAGES_URLS_FIELD\"):\n            self.IMAGES_URLS_FIELD = self.DEFAULT_IMAGES_URLS_FIELD\n\n        self.images_urls_field = settings.get(\n            resolve('IMAGES_URLS_FIELD'),\n            self.IMAGES_URLS_FIELD\n        )\n        self.images_result_field = settings.get(\n            resolve('IMAGES_RESULT_FIELD'),\n            self.IMAGES_RESULT_FIELD\n        )\n        self.min_width = settings.getint(\n            resolve('IMAGES_MIN_WIDTH'), self.MIN_WIDTH\n        )\n        self.min_height = settings.getint(\n            resolve('IMAGES_MIN_HEIGHT'), self.MIN_HEIGHT\n        )\n        self.thumbs = settings.get(\n            resolve('IMAGES_THUMBS'), self.THUMBS\n        )\n\n    @classmethod\n    def from_settings(cls, settings):\n        s3store = cls.STORE_SCHEMES['s3']\n        s3store.AWS_ACCESS_KEY_ID = settings['AWS_ACCESS_KEY_ID']\n        s3store.AWS_SECRET_ACCESS_KEY = settings['AWS_SECRET_ACCESS_KEY']\n        s3store.AWS_ENDPOINT_URL = settings['AWS_ENDPOINT_URL']\n        s3store.AWS_REGION_NAME = settings['AWS_REGION_NAME']\n        s3store.AWS_USE_SSL = settings['AWS_USE_SSL']\n        s3store.AWS_VERIFY = settings['AWS_VERIFY']\n        s3store.POLICY = settings['IMAGES_STORE_S3_ACL']\n\n        gcs_store = cls.STORE_SCHEMES['gs']\n        gcs_store.GCS_PROJECT_ID = settings['GCS_PROJECT_ID']\n        gcs_store.POLICY = settings['IMAGES_STORE_GCS_ACL'] or None\n\n        ftp_store = cls.STORE_SCHEMES['ftp']\n        ftp_store.FTP_USERNAME = settings['FTP_USER']\n        ftp_store.FTP_PASSWORD = settings['FTP_PASSWORD']\n        ftp_store.USE_ACTIVE_MODE = settings.getbool('FEED_STORAGE_FTP_ACTIVE')\n\n        store_uri = settings['IMAGES_STORE']\n        return cls(store_uri, settings=settings)\n\n    def file_downloaded(self, response, request, info, *, item=None):\n        return self.image_downloaded(response, request, info, item=item)\n\n    def image_downloaded(self, response, request, info, *, item=None):\n        checksum = None\n        for path, image, buf in self.get_images(response, request, info, item=item):\n            if checksum is None:\n                buf.seek(0)\n                checksum = md5sum(buf)\n            width, height = image.size\n            self.store.persist_file(\n                path, buf, info,\n                meta={'width': width, 'height': height},\n                headers={'Content-Type': 'image/jpeg'})\n        return checksum\n\n    def get_images(self, response, request, info, *, item=None):\n        path = self.file_path(request, response=response, info=info, item=item)\n        orig_image = Image.open(BytesIO(response.body))\n\n        width, height = orig_image.size\n        if width < self.min_width or height < self.min_height:\n            raise ImageException(\"Image too small \"\n                                 f\"({width}x{height} < \"\n                                 f\"{self.min_width}x{self.min_height})\")\n\n        image, buf = self.convert_image(orig_image)\n        yield path, image, buf\n\n        for thumb_id, size in self.thumbs.items():\n            thumb_path = self.thumb_path(request, thumb_id, response=response, info=info)\n            thumb_image, thumb_buf = self.convert_image(image, size)\n            yield thumb_path, thumb_image, thumb_buf\n\n    def convert_image(self, image, size=None):\n        if image.format == 'PNG' and image.mode == 'RGBA':\n            background = Image.new('RGBA', image.size, (255, 255, 255))\n            background.paste(image, image)\n            image = background.convert('RGB')\n        elif image.mode == 'P':\n            image = image.convert(\"RGBA\")\n            background = Image.new('RGBA', image.size, (255, 255, 255))\n            background.paste(image, image)\n            image = background.convert('RGB')\n        elif image.mode != 'RGB':\n            image = image.convert('RGB')\n\n        if size:\n            image = image.copy()\n            image.thumbnail(size, Image.ANTIALIAS)\n\n        buf = BytesIO()\n        image.save(buf, 'JPEG')\n        return image, buf\n\n    def get_media_requests(self, item, info):\n        urls = ItemAdapter(item).get(self.images_urls_field, [])\n        return [Request(u) for u in urls]\n\n    def item_completed(self, results, item, info):\n        with suppress(KeyError):\n            ItemAdapter(item)[self.images_result_field] = [x for ok, x in results if ok]\n        return item\n\n    def file_path(self, request, response=None, info=None, *, item=None):\n        image_guid = hashlib.sha1(to_bytes(request.url)).hexdigest()\n        return f'full/{image_guid}.jpg'\n\n    def thumb_path(self, request, thumb_id, response=None, info=None):\n        thumb_guid = hashlib.sha1(to_bytes(request.url)).hexdigest()\n        return f'thumbs/{thumb_id}/{thumb_guid}.jpg'\n", "path": "scrapy/pipelines/images.py"}], "after_files": [{"content": "\"\"\"\nImages Pipeline\n\nSee documentation in topics/media-pipeline.rst\n\"\"\"\nimport functools\nimport hashlib\nfrom contextlib import suppress\nfrom io import BytesIO\n\nfrom itemadapter import ItemAdapter\n\nfrom scrapy.exceptions import DropItem, NotConfigured\nfrom scrapy.http import Request\nfrom scrapy.pipelines.files import FileException, FilesPipeline\n# TODO: from scrapy.pipelines.media import MediaPipeline\nfrom scrapy.settings import Settings\nfrom scrapy.utils.misc import md5sum\nfrom scrapy.utils.python import to_bytes\n\n\nclass NoimagesDrop(DropItem):\n    \"\"\"Product with no images exception\"\"\"\n\n\nclass ImageException(FileException):\n    \"\"\"General image error exception\"\"\"\n\n\nclass ImagesPipeline(FilesPipeline):\n    \"\"\"Abstract pipeline that implement the image thumbnail generation logic\n\n    \"\"\"\n\n    MEDIA_NAME = 'image'\n\n    # Uppercase attributes kept for backward compatibility with code that subclasses\n    # ImagesPipeline. They may be overridden by settings.\n    MIN_WIDTH = 0\n    MIN_HEIGHT = 0\n    EXPIRES = 90\n    THUMBS = {}\n    DEFAULT_IMAGES_URLS_FIELD = 'image_urls'\n    DEFAULT_IMAGES_RESULT_FIELD = 'images'\n\n    def __init__(self, store_uri, download_func=None, settings=None):\n        try:\n            from PIL import Image\n            self._Image = Image\n        except ImportError:\n            raise NotConfigured(\n                'ImagesPipeline requires installing Pillow 4.0.0 or later'\n            )\n\n        super().__init__(store_uri, settings=settings, download_func=download_func)\n\n        if isinstance(settings, dict) or settings is None:\n            settings = Settings(settings)\n\n        resolve = functools.partial(self._key_for_pipe,\n                                    base_class_name=\"ImagesPipeline\",\n                                    settings=settings)\n        self.expires = settings.getint(\n            resolve(\"IMAGES_EXPIRES\"), self.EXPIRES\n        )\n\n        if not hasattr(self, \"IMAGES_RESULT_FIELD\"):\n            self.IMAGES_RESULT_FIELD = self.DEFAULT_IMAGES_RESULT_FIELD\n        if not hasattr(self, \"IMAGES_URLS_FIELD\"):\n            self.IMAGES_URLS_FIELD = self.DEFAULT_IMAGES_URLS_FIELD\n\n        self.images_urls_field = settings.get(\n            resolve('IMAGES_URLS_FIELD'),\n            self.IMAGES_URLS_FIELD\n        )\n        self.images_result_field = settings.get(\n            resolve('IMAGES_RESULT_FIELD'),\n            self.IMAGES_RESULT_FIELD\n        )\n        self.min_width = settings.getint(\n            resolve('IMAGES_MIN_WIDTH'), self.MIN_WIDTH\n        )\n        self.min_height = settings.getint(\n            resolve('IMAGES_MIN_HEIGHT'), self.MIN_HEIGHT\n        )\n        self.thumbs = settings.get(\n            resolve('IMAGES_THUMBS'), self.THUMBS\n        )\n\n    @classmethod\n    def from_settings(cls, settings):\n        s3store = cls.STORE_SCHEMES['s3']\n        s3store.AWS_ACCESS_KEY_ID = settings['AWS_ACCESS_KEY_ID']\n        s3store.AWS_SECRET_ACCESS_KEY = settings['AWS_SECRET_ACCESS_KEY']\n        s3store.AWS_ENDPOINT_URL = settings['AWS_ENDPOINT_URL']\n        s3store.AWS_REGION_NAME = settings['AWS_REGION_NAME']\n        s3store.AWS_USE_SSL = settings['AWS_USE_SSL']\n        s3store.AWS_VERIFY = settings['AWS_VERIFY']\n        s3store.POLICY = settings['IMAGES_STORE_S3_ACL']\n\n        gcs_store = cls.STORE_SCHEMES['gs']\n        gcs_store.GCS_PROJECT_ID = settings['GCS_PROJECT_ID']\n        gcs_store.POLICY = settings['IMAGES_STORE_GCS_ACL'] or None\n\n        ftp_store = cls.STORE_SCHEMES['ftp']\n        ftp_store.FTP_USERNAME = settings['FTP_USER']\n        ftp_store.FTP_PASSWORD = settings['FTP_PASSWORD']\n        ftp_store.USE_ACTIVE_MODE = settings.getbool('FEED_STORAGE_FTP_ACTIVE')\n\n        store_uri = settings['IMAGES_STORE']\n        return cls(store_uri, settings=settings)\n\n    def file_downloaded(self, response, request, info, *, item=None):\n        return self.image_downloaded(response, request, info, item=item)\n\n    def image_downloaded(self, response, request, info, *, item=None):\n        checksum = None\n        for path, image, buf in self.get_images(response, request, info, item=item):\n            if checksum is None:\n                buf.seek(0)\n                checksum = md5sum(buf)\n            width, height = image.size\n            self.store.persist_file(\n                path, buf, info,\n                meta={'width': width, 'height': height},\n                headers={'Content-Type': 'image/jpeg'})\n        return checksum\n\n    def get_images(self, response, request, info, *, item=None):\n        path = self.file_path(request, response=response, info=info, item=item)\n        orig_image = self._Image.open(BytesIO(response.body))\n\n        width, height = orig_image.size\n        if width < self.min_width or height < self.min_height:\n            raise ImageException(\"Image too small \"\n                                 f\"({width}x{height} < \"\n                                 f\"{self.min_width}x{self.min_height})\")\n\n        image, buf = self.convert_image(orig_image)\n        yield path, image, buf\n\n        for thumb_id, size in self.thumbs.items():\n            thumb_path = self.thumb_path(request, thumb_id, response=response, info=info)\n            thumb_image, thumb_buf = self.convert_image(image, size)\n            yield thumb_path, thumb_image, thumb_buf\n\n    def convert_image(self, image, size=None):\n        if image.format == 'PNG' and image.mode == 'RGBA':\n            background = self._Image.new('RGBA', image.size, (255, 255, 255))\n            background.paste(image, image)\n            image = background.convert('RGB')\n        elif image.mode == 'P':\n            image = image.convert(\"RGBA\")\n            background = self._Image.new('RGBA', image.size, (255, 255, 255))\n            background.paste(image, image)\n            image = background.convert('RGB')\n        elif image.mode != 'RGB':\n            image = image.convert('RGB')\n\n        if size:\n            image = image.copy()\n            image.thumbnail(size, self._Image.ANTIALIAS)\n\n        buf = BytesIO()\n        image.save(buf, 'JPEG')\n        return image, buf\n\n    def get_media_requests(self, item, info):\n        urls = ItemAdapter(item).get(self.images_urls_field, [])\n        return [Request(u) for u in urls]\n\n    def item_completed(self, results, item, info):\n        with suppress(KeyError):\n            ItemAdapter(item)[self.images_result_field] = [x for ok, x in results if ok]\n        return item\n\n    def file_path(self, request, response=None, info=None, *, item=None):\n        image_guid = hashlib.sha1(to_bytes(request.url)).hexdigest()\n        return f'full/{image_guid}.jpg'\n\n    def thumb_path(self, request, thumb_id, response=None, info=None):\n        thumb_guid = hashlib.sha1(to_bytes(request.url)).hexdigest()\n        return f'thumbs/{thumb_id}/{thumb_guid}.jpg'\n", "path": "scrapy/pipelines/images.py"}]}