Repository: Zulko/moviepy

Issue:
'VideoFileClip' object has no attribute 'coreader'
<!--
when i run the examples:
"moviepy/examples/example_with_sound.py"
I got an error : 'VideoFileClip' object has no attribute 'coreader'
-->
### Expected Behavior
run the example in moviepy/examples/example_with_sound.py

### Actual Behavior
Only "moviepy/audio/io/AudioFileClip.py" has coreader function, I can't find coreader function in 
 "moviepy/vedio/io/VedioFileClip"
### Steps to Reproduce the Problem
```
from moviepy.editor import *
from moviepy.video.tools.drawing import color_split

duration = 6 # duration of the final clip

main_clip = VideoFileClip("../../videos/charadePhone.mp4")
W,H = main_clip.size
                            
mask = color_split((2*W/3,H),
                   p1=(W/3,H), p2=(2*W/3,0),
                   col1=1, col2=0,
                   grad_width=2)
                   
mask_clip = ImageClip(mask, ismask=True)
                   
clip_left = (main_clip.coreader()
                      .subclip(0,duration)
                      .crop( x1=60, x2=60 + 2*W/3)
                      .set_mask(mask_clip))
```


### Specifications

  - Python Version: 3.6
  - Moviepy Version: I had try 1.0.0 and 0.2.3.5
  - Platform Name: windows
  - Platform Version:win10


Discussion:
coveralls: 
[![Coverage Status](https://coveralls.io/builds/33938934/badge)](https://coveralls.io/builds/33938934)

Coverage decreased (-0.03%) to 67.498% when pulling **46eb5d3769e2eb9b5cdf0554e3c0f4c651b191d0 on ToxicOverload:VideoCoreader** into **0e4bf6d1c1b448a477c9be49e49c97b104ba1830 on Zulko:master**.

tburrows13: Thanks for the pull request, this looks perfect!

Patch:
diff --git a/.github/workflows/format_check.yml b/.github/workflows/format_check.yml
index a785b01..3a8c491 100644
--- a/.github/workflows/format_check.yml
+++ b/.github/workflows/format_check.yml
@@ -7,12 +7,11 @@ jobs:
   black:
     runs-on: ubuntu-latest
     steps:
-      - name: Checkout
-        uses: actions/checkout@v2    
+      - uses: actions/checkout@v2
+      - name: Set up Python
+        uses: actions/setup-python@v2
       - name: Black
-        uses: lgeiger/black-action@v1.0.1    
-        with:
-          args: "--check --diff --target-version py36 $GITHUB_WORKSPACE"
+        uses: psf/black@stable  # already includes args "--check --diff"
   flake8:
     runs-on: ubuntu-latest
     steps:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0c6298e..d6fd9dc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 ### Important Announcements
 
 ### Added <!-- for new features -->
+- `VideoFileClip.coreader()` that creates a new clip created from the same source file as the original (to complement the existing `AudioFileClip.coreader()`) [#1332]
 
 ### Changed <!-- for changes in existing functionality -->
 
@@ -20,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 ### Removed <!-- for now removed features -->
 
 ### Fixed <!-- for any bug fixes -->
+- Fixed BitmapClip with fps != 1 not returning the correct frames or crashing [#1333]
 
 
 ## [v2.0.0.dev2](https://github.com/zulko/moviepy/tree/v2.0.0.dev2)
diff --git a/moviepy/video/VideoClip.py b/moviepy/video/VideoClip.py
index 6c29ce3..3fcd92f 100644
--- a/moviepy/video/VideoClip.py
+++ b/moviepy/video/VideoClip.py
@@ -1411,7 +1411,7 @@ class BitmapClip(VideoClip):
 
         VideoClip.__init__(
             self,
-            make_frame=lambda t: frame_array[int(t)],
+            make_frame=lambda t: frame_array[int(t * fps)],
             ismask=ismask,
             duration=duration,
         )
diff --git a/moviepy/video/io/VideoFileClip.py b/moviepy/video/io/VideoFileClip.py
index fd7b38f..24d6254 100644
--- a/moviepy/video/io/VideoFileClip.py
+++ b/moviepy/video/io/VideoFileClip.py
@@ -145,6 +145,12 @@ class VideoFileClip(VideoClip):
                 nbytes=audio_nbytes,
             )
 
+    def coreader(self):
+        """Returns a copy of the VideoFileClip, i.e. a new entrance point
+        to the video file. Use copy when you have different clips
+        watching the video file at different times."""
+        return VideoFileClip(self.filename, audio_buffersize=self.audio.buffersize)
+
     def close(self):
         """ Close the internal reader. """
         if self.reader:


Test patch:
diff --git a/tests/test_BitmapClip.py b/tests/test_BitmapClip.py
index b4eb2d8..527f3e9 100644
--- a/tests/test_BitmapClip.py
+++ b/tests/test_BitmapClip.py
@@ -45,5 +45,15 @@ def test_setting_duration():
     assert clip.duration == 6
 
 
+def test_to_bitmap():
+    bitmap = [["R"], ["R"], ["B"], ["B"], ["G"], ["G"]]
+    clip1 = BitmapClip(bitmap, fps=0.345)
+    clip2 = BitmapClip(bitmap, fps=1)
+    clip3 = BitmapClip(bitmap, fps=3.12345)
+    assert bitmap == clip1.to_bitmap()
+    assert bitmap == clip2.to_bitmap()
+    assert bitmap == clip3.to_bitmap()
+
+
 if __name__ == "__main__":
     pytest.main()