0

I'm using moviepy to cut a clip into multiple equally-sized segments, and it's reading from a txt file of start and end values to cut the clip properly. The following is my code:

required_video_file = VideoFileClip(filename)
with open("times.txt") as f:
  times = f.readlines()
times = [x.strip() for x in times] 
for time in times:
  starttime = int(time.split("-")[0])
  endtime = int(time.split("-")[1])
  required_video_file = required_video_file.subclip(starttime, endtime)
  filen = str(times.index(time)+1)+".mp4"
  required_video_file.write_videofile(filen, audio_codec='aac')

The current filename value is a 20-ish minute clip which has the times.txt file cut up into 30-second portions.

0-30
30-60
60-90
90-120
120-150

And so on. However, I get a valueerror when it tries to write the third file:

ValueError: t_start (60.00) should be smaller than the clip's duration (30.00).

I'm not sure why this error is happening, because the clip it's trying to create a subclip of is 20 minutes long, not 30 seconds.

FULL ERROR:

<moviepy.video.io.VideoFileClip.VideoFileClip object at 0x105e589a0>
Traceback (most recent call last):
  File "vidsplitter.py", line 76, in <module>
    required_video_file = required_video_file.subclip(starttime, endtime)
  File "<decorator-gen-35>", line 2, in subclip
  File "/Users/hypnoticocelot/Library/Python/3.8/lib/python/site-packages/moviepy/decorators.py", line 89, in wrapper
    return f(*new_a, **new_kw)
  File "<decorator-gen-34>", line 2, in subclip
  File "/Users/hypnoticocelot/Library/Python/3.8/lib/python/site-packages/moviepy/decorators.py", line 32, in apply_to_mask
    newclip = f(clip, *a, **k)
  File "<decorator-gen-33>", line 2, in subclip
  File "/Users/hypnoticocelot/Library/Python/3.8/lib/python/site-packages/moviepy/decorators.py", line 43, in apply_to_audio
    newclip = f(clip, *a, **k)
  File "/Users/hypnoticocelot/Library/Python/3.8/lib/python/site-packages/moviepy/Clip.py", line 391, in subclip
    raise ValueError(
ValueError: t_start (60.00) should be smaller than the clip's duration (30.00).
  • If you just want to split the video to segments of 30 seconds them `ffmpeg` can be a great alternative by simply using `ffmpeg -i myClip.mp4 -f segment -segment_time 30 -c copy -reset_timestamps true out%03d.mp4` – Eular Aug 02 '22 at 18:03
  • I was actually using this code before, which works but all files past the first one are corrupted: `ffmpeg_extract_subclip(required_video_file, starttime, endtime, targetname=str(times.index(time)+1)+".mp4")` It looks like the `-reset_timestamps 1` flag would work to fix this, but I'm unsure how to add it to a script – HypnoticOcelot Aug 02 '22 at 18:26
  • maybe use print() to see which part of code is executed and what you have in variables - maybe you run it for different file and it is shorter than you expect. – furas Aug 02 '22 at 20:42
  • No, my file is definitely the correct file, the filename variable is the file converted_fg01.mp4 which is 22:32 in length – HypnoticOcelot Aug 02 '22 at 20:44
  • all problem can be because you assing new clip to the same variable `required_video_file = required_video_file. ...` so in next loop it uses shorter clip. You should use different variable - `new_clip = required_video_file.subclip(...)` – furas Aug 02 '22 at 20:44
  • That seems to work, thank you! Mind creating an answer so I can mark it as solved? – HypnoticOcelot Aug 02 '22 at 20:46

1 Answers1

1

All problem can be because you assing new clip to the same variable

required_video_file = required_video_file.subclip(...)

so in next loop it uses shorter clip - with duration 30s.

You should use different variable

new_clip = required_video_file.subclip(...)

EDIT:

You should get full duration at start

full_duration = required_video_file.duration

and inside loop you should check if endtime is not too big and crop it.

    if endtime > full_duration:
        endtime = full_duration

You should also check if starttime is not too big and skip rest of code.

    if startime > full_duration:
        break  # exit `for`-loop

I would do (with some other changes)

required_video_file = VideoFileClip(filename)

full_duration = required_video_file.duration

with open("times.txt") as f:
    times = [x.strip().split('-') for x in f]

for number, (start, end) in enumerate(times, 1):
    starttime = int(start)
    endtime   = int(end)
    
    if starttime > full_duration:
        print(f"video too short to get {startime}-{endtime} (full duration: {full_duration})")
        break  # exit `for`-loop
    
    if endtime > full_duration:
        print(f"crop endtime {endtime} to {full_duration}")
        endtime = full_duration
    
    new_clip = required_video_file.subclip(starttime, endtime)
    
    new_filename = f"{number}.mp4"
    
    new_clip.write_videofile(new_filename, audio_codec='aac')
furas
  • 134,197
  • 12
  • 106
  • 148
  • It looks like that works, but the final file in the sequence outputs an OSerror: `OSError: Error in file converted_fg01.mp4, Accessing time t=1352.40-1352.45 seconds, with clip duration=1352.400000 seconds, `. The times.txt file is requesting seconds 1350-1380 here, is there a way to make it so that the final file in the sequence will request as much clip length as there is left? – HypnoticOcelot Aug 02 '22 at 20:49
  • first you should get file duration and next use `endtime = min(endtime, full_duration)` for both values. OR use `if endtime > full_duration: endtime = full_duration:` – furas Aug 02 '22 at 20:53
  • I'm using an if statement to set endtime to requiredvideofile.duration, but it's still outputting the same error? Using print, I know that it's registering the fact that it's over but it's still not quite working for some reason. My code is just `if endtime > required_video_file.duration: endtime = required_video_file.duration` – HypnoticOcelot Aug 02 '22 at 21:05
  • I added code to answer but I didn't test it. – furas Aug 02 '22 at 21:06
  • I added `( )` in in line `for number, (start, end) in ...` – furas Aug 02 '22 at 21:09
  • Your code should work, but for some reason it's still getting the same error? I'm not sure if this is a quirk with moviepy or not. – HypnoticOcelot Aug 02 '22 at 21:16
  • do you get the same error with `Accessing time t=1352.40-1352.45 seconds` ? `float` values can't keep all values and sometimes it can gives error - ie. `0.1 + 0.2 == 0.3` gives `False` but it should give `True`. Maybe use `if endtime > full_duration: endtime = None` and it should use duration from file. – furas Aug 02 '22 at 21:24
  • or maybe you should use `>=` when it check starttime - `if starttime >= full_duration:` – furas Aug 02 '22 at 21:29
  • I've tried setting it to none, but it still outputs the same error as every other solution - `OSError: Error in file converted_fg01.mp4, Accessing time t=1352.40-1352.45 seconds, with clip duration=1352.400000 seconds, ` It does say, "Cropping endtime from 1380 to 1352.4!" when I set it to "none". Would you like the full traceback? – HypnoticOcelot Aug 02 '22 at 21:29
  • maybe create new question on new page - you will have more space for full traceback and description. And maybe other people will see - and maybe they will have also some ideas. – furas Aug 02 '22 at 21:31
  • Thanks, I've created a new thread here: https://stackoverflow.com/questions/73214071/ – HypnoticOcelot Aug 02 '22 at 21:36