Repository: python-pillow/Pillow

Issue:
WebP plugin save animated webp need default duration value
### What did you do?
i only  convert a animate gif format to animate webP format 

### What did you expect to happen?
it can convert success

### What actually happened? 
`python3.7/site-packages/PIL/WebPImagePlugin.py"  , line 292, in _save_all
 timestamp += duration
TypeError: unsupported operand type(s) for +=: 'int' and 'NoneType'`


### What are your OS, Python and Pillow versions?

* OS:  macos Monterey version 12.2.1
* Python: 3.7.11
* Pillow:  8.4.0

through debug, i know the reason is that  pillow open  gif image use  method im.info.get("duration") is null,  when i convert to webP format with no Coustom duration param。  because duration come from duration = im.encoderinfo.get("duration", im.info.get("duration")) [WebPImagePlugin.py line 195]  be NoneType, it will  traceback in WebPImagePlugin.py 292 

```python
                # Update timestamp and frame index
                if isinstance(duration, (list, tuple)):
                    timestamp += duration[frame_idx]
                else:
                    timestamp += duration

```
traceback:

``` python
Traceback (most recent call last):
  File "/Users/jeevi/Documents/WWW/study/python/pillow_img/img/img.py", line 198, in <module>
    im.save(bts, "webP", save_all=True, quality=75,  optimize=True)
  File "/Users/jeevi/Documents/WWW/study/python/pillow_img/python/lib/python3.7/site-packages/PIL/Image.py", line 2240, in save
    save_handler(self, fp, filename)
  File "/Users/jeevi/Documents/WWW/study/python/pillow_img/python/lib/python3.7/site-packages/PIL/WebPImagePlugin.py", line 292, in _save_all
    timestamp += duration
TypeError: unsupported operand type(s) for +=: 'int' and 'NoneType'

```



**i think duration need a default value when not get** 

here is my code:
```python
file = "./YD_cnt_11_019CS3rXgAIN.gif"
    
    with Image.open(file) as im:
        print(im.format, im.size, im.mode, getattr(im, "is_animated", False), getattr(im, "n_frames", 1),
              getattr(im, "n_frames", 1) // 2)
        bts = BytesIO()
        im.save(bts, "webP", save_all=True, quality=75,  optimize=True)
        print("长度:{} \n".format(len(bts.getvalue())))
        with open("YD_cnt_11_019CS3rXgAIN.gif.webp", "wb") as f:
            f.write(bts.getvalue())
        t2 = time.time()
        cost = int(round(t2 * 1000)) - int(round(t * 1000))
        print("耗时:{}ms\n".format(cost))
```
the image file in my github : [https://github.com/jeevi-cao/study/blob/master/python/YD_cnt_11_019CS3rXgAIN.gif]( https://github.com/jeevi-cao/study/blob/master/python/YD_cnt_11_019CS3rXgAIN.gif)



Discussion:
You're right. I've created PR #6140 to resolve this.

Resolving patch:
diff --git a/src/PIL/WebPImagePlugin.py b/src/PIL/WebPImagePlugin.py
--- a/src/PIL/WebPImagePlugin.py
+++ b/src/PIL/WebPImagePlugin.py
@@ -192,7 +192,7 @@ def _save_all(im, fp, filename):
                 r, g, b = palette[background * 3 : (background + 1) * 3]
                 background = (r, g, b, 0)
 
-    duration = im.encoderinfo.get("duration", im.info.get("duration"))
+    duration = im.encoderinfo.get("duration", im.info.get("duration", 0))
     loop = im.encoderinfo.get("loop", 0)
     minimize_size = im.encoderinfo.get("minimize_size", False)
     kmin = im.encoderinfo.get("kmin", None)


Test patch:
diff --git a/Tests/test_file_webp.py b/Tests/test_file_webp.py
--- a/Tests/test_file_webp.py
+++ b/Tests/test_file_webp.py
@@ -8,6 +8,7 @@
 from PIL import Image, WebPImagePlugin, features
 
 from .helper import (
+    assert_image_equal,
     assert_image_similar,
     assert_image_similar_tofile,
     hopper,
@@ -105,6 +106,19 @@ def test_write_method(self, tmp_path):
         hopper().save(buffer_method, format="WEBP", method=6)
         assert buffer_no_args.getbuffer() != buffer_method.getbuffer()
 
+    @skip_unless_feature("webp_anim")
+    def test_save_all(self, tmp_path):
+        temp_file = str(tmp_path / "temp.webp")
+        im = Image.new("RGB", (1, 1))
+        im2 = Image.new("RGB", (1, 1), "#f00")
+        im.save(temp_file, save_all=True, append_images=[im2])
+
+        with Image.open(temp_file) as reloaded:
+            assert_image_equal(im, reloaded)
+
+            reloaded.seek(1)
+            assert_image_similar(im2, reloaded, 1)
+
     def test_icc_profile(self, tmp_path):
         self._roundtrip(tmp_path, self.rgb_mode, 12.5, {"icc_profile": None})
         if _webp.HAVE_WEBPANIM:
@@ -171,7 +185,6 @@ def test_file_pointer_could_be_reused(self):
             Image.open(blob).load()
             Image.open(blob).load()
 
-    @skip_unless_feature("webp")
     @skip_unless_feature("webp_anim")
     def test_background_from_gif(self, tmp_path):
         with Image.open("Tests/images/chi.gif") as im:
@@ -191,7 +204,6 @@ def test_background_from_gif(self, tmp_path):
         difference = sum(abs(original_value[i] - reread_value[i]) for i in range(0, 3))
         assert difference < 5
 
-    @skip_unless_feature("webp")
     @skip_unless_feature("webp_anim")
     def test_duration(self, tmp_path):
         with Image.open("Tests/images/dispose_bgnd.gif") as im: