Zulko / moviepy

Video editing with Python
https://zulko.github.io/moviepy/
MIT License
12.66k stars 1.59k forks source link

fps is None error when writing to file #1900

Open PashaM999 opened 1 year ago

PashaM999 commented 1 year ago

Hello, I am not sure whether it is a but or just something specific to my system, but whenever I try running the write_videofile function I get the following error:

TypeError                                 Traceback (most recent call last)
Cell In [3], line 10
      5 from moviepy.editor import VideoFileClip
      8 clip = VideoFileClip('./background_videos/'+name)
---> 10 clip.write_videofile('./background_videos/'+name, codec='libx264', fps=30)

File /shared-libs/python3.9/py-core/lib/python3.9/site-packages/decorator.py:232, in decorate.<locals>.fun(*args, **kw)
    230 if not kwsyntax:
    231     args, kw = fix(args, kw, sig)
--> 232 return caller(func, *(extras + args), **kw)

File ~/venv/lib/python3.9/site-packages/moviepy/decorators.py:54, in requires_duration(f, clip, *a, **k)
     52     raise ValueError("Attribute 'duration' not set")
     53 else:
---> 54     return f(clip, *a, **k)

File /shared-libs/python3.9/py-core/lib/python3.9/site-packages/decorator.py:232, in decorate.<locals>.fun(*args, **kw)
    230 if not kwsyntax:
    231     args, kw = fix(args, kw, sig)
--> 232 return caller(func, *(extras + args), **kw)

File ~/venv/lib/python3.9/site-packages/moviepy/decorators.py:135, in use_clip_fps_by_default(f, clip, *a, **k)
    130 new_a = [fun(arg) if (name=='fps') else arg
    131          for (arg, name) in zip(a, names)]
    132 new_kw = {k: fun(v) if k=='fps' else v
    133          for (k,v) in k.items()}
--> 135 return f(clip, *new_a, **new_kw)

File /shared-libs/python3.9/py-core/lib/python3.9/site-packages/decorator.py:232, in decorate.<locals>.fun(*args, **kw)
    230 if not kwsyntax:
    231     args, kw = fix(args, kw, sig)
--> 232 return caller(func, *(extras + args), **kw)

File ~/venv/lib/python3.9/site-packages/moviepy/decorators.py:22, in convert_masks_to_RGB(f, clip, *a, **k)
     20 if clip.ismask:
     21     clip = clip.to_RGB()
---> 22 return f(clip, *a, **k)

File ~/venv/lib/python3.9/site-packages/moviepy/video/VideoClip.py:300, in VideoClip.write_videofile(self, filename, fps, codec, bitrate, audio, audio_fps, preset, audio_nbytes, audio_codec, audio_bitrate, audio_bufsize, temp_audiofile, rewrite_audio, remove_temp, write_logfile, verbose, threads, ffmpeg_params, logger)
    292 if make_audio:
    293     self.audio.write_audiofile(audiofile, audio_fps,
    294                                audio_nbytes, audio_bufsize,
    295                                audio_codec, bitrate=audio_bitrate,
    296                                write_logfile=write_logfile,
    297                                verbose=verbose,
    298                                logger=logger)
--> 300 ffmpeg_write_video(self, filename, fps, codec,
    301                    bitrate=bitrate,
    302                    preset=preset,
    303                    write_logfile=write_logfile,
    304                    audiofile=audiofile,
    305                    verbose=verbose, threads=threads,
    306                    ffmpeg_params=ffmpeg_params,
    307                    logger=logger)
    309 if remove_temp and make_audio:
    310     if os.path.exists(audiofile):

File ~/venv/lib/python3.9/site-packages/moviepy/video/io/ffmpeg_writer.py:219, in ffmpeg_write_video(clip, filename, fps, codec, bitrate, preset, withmask, write_logfile, audiofile, verbose, threads, ffmpeg_params, logger)
    213 logger(message='Moviepy - Writing video %s\n' % filename)
    214 with FFMPEG_VideoWriter(filename, clip.size, fps, codec = codec,
    215                             preset=preset, bitrate=bitrate, logfile=logfile,
    216                             audiofile=audiofile, threads=threads,
    217                             ffmpeg_params=ffmpeg_params) as writer:
--> 219     nframes = int(clip.duration*fps)
    221     for t,frame in clip.iter_frames(logger=logger, with_times=True,
    222                                     fps=fps, dtype="uint8"):
    223         if withmask:

TypeError: unsupported operand type(s) for *: 'float' and 'NoneType'

This probably has to do with ffmpeg installation in my system, but I can't figure it out. I have the latest versions of both ffmpeg and moviepy and I have set the environment variable for ffmpeg path after installing it with apt, but the error doesn't change at all. What could be causing this?

LBdN commented 1 year ago
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/venv/Lib/site-packages/moviepy/video/VideoClip.py b/venv/Lib/site-packages/moviepy/video/VideoClip.py
--- a/venv/Lib/site-packages/moviepy/video/VideoClip.py 
+++ b/venv/Lib/site-packages/moviepy/video/VideoClip.py (date 1675398707394)
@@ -131,7 +131,7 @@
     @requires_duration
     @use_clip_fps_by_default
     @convert_masks_to_RGB
-    def write_videofile(self, filename, fps=None, codec=None,
+    def write_videofile(self, filename, *, fps=None, codec=None,
                         bitrate=None, audio=True, audio_fps=44100,
                         preset="medium",
                         audio_nbytes=4, audio_codec=None,
PashaM999 commented 1 year ago

@LBdN What does this supposed to mean? If it is somehow a solution, could you, please, elaborate?

LBdN commented 1 year ago

Fair enough. It was late but I understand this was a bit too raw. The bug comes from the difference between python 3 (3.8 I think )and python 2. The fps keyword argument is interpreted as a positional argument in python 3. And, as a consequence, is missing in the keywords arguments triggering the error. The multiple decorators applied on write_videofile make harder to see the issue but this seems to be the root cause. I just added the keyword argument separator (*) to mark the beginning of the keyword arguments, which puts the fps argument back at the right place. My fix works only with python 3. It works for me. I'll let @Zulko judge whether it is the best solution for everybody.

PashaM999 commented 1 year ago

@LBdN Thanks, I will try this out, however, this issue doesn't make sense considering the fact that in Google's Colab the library works fine, and it uses python 3.8.10.

keikoro commented 1 year ago

@PashaM999 Please always include your specs like we ask for in our issue templates (MoviePy version, platform used etc.) as well as code samples for reproducability aong with logs for errors, thank you.

PashaM999 commented 1 year ago

@keikoro I use Debian GNU/Linux 10 (buster), moviepy version is 1.0.3 and ffmpeg version is 4.1.10-0+deb10u1 (I have also tried 4.3.5-0+deb11u1)

JadanPoll commented 10 months ago

The problem becomes exasperatingly obvious when you try something like this: from decorator import decorator

@decorator def my_decorator(f, *args, *kwargs): print("Arguments:", args) print("Keyword arguments:", kwargs) return f(args, **kwargs)

@my_decorator def example_function(x=0, y=0, z=0): print(f"Arguments: {x}, {y}, {z}")

example_function(x=4, y=5, z=6)

And get completely the wrong output:Hint: The keyword arguments should be printed not the positional

Hey @PashaM999 ! Here's what I found: In the decorator library, there's a setting called parameter setting kwsyntax. By default, it's set to False, but if you set it to True, it ensures that all keyword arguments (kwargs) are treated as kwargs, and not inexplicable being reinterpreted into positional arguments (args).

Now, the issue comes up in the use_clip_fps_by_default decorator, where a specific line of code relies on the structure of kwargs simply being there(like its supposed to). Since kwsyntax is set to False by default in the decorator library(not decorators.py, decorator), it misinterprets kwargs as args, causing some complications I lost much sleep figuring out. However, by switching kwsyntax to True, we can fix this issue and allow kwargs to be correctly recognized.

For a quick fix, you can temporarily comment out @use_clip_fps_by_default above the write_videofile function and hope you never break the code. Alternatively, if you more sustainable solution, you can head over to decorator.py and change both instances of the parameter kwsyntax to True.

I hope that helps clarify things!

keikoro commented 9 months ago

Is this a duplicate of what was later reported in #1986? Does upgrading to the latest changes in the repo fix the issue, as suggested in the other ticket? If so, I suggest closing this issue as duplicate of the other one.