-
-
Save seriousm4x/b5f795d7058f4e831270b047e25c3b9e to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3 | |
""" | |
Cut Adobe Premiere .edl into single clips with ffmpeg. | |
Make cuts in the video track in Premiere, and export it as an edl file. | |
python ffmpeg-edl.py -i <edl> <edl2> -o output_folder | |
You can give this script multiple edl files to process. | |
ffmpeg and ffprobe are required. | |
""" | |
import argparse | |
import json | |
import os | |
import re | |
import subprocess | |
ffmpeg = "ffmpeg" | |
ffprobe = "ffprobe" | |
def tcToSec(timecode, fps): | |
h, m, s, f = timecode.split(':') | |
return float(h) * 3600 + float(m) * 60 + float(s) + float(f) / float(eval(fps)) | |
def main(args): | |
# loop edl files from cli | |
for arg in args.i: | |
edl_ext = os.path.splitext(os.path.basename(arg))[1] | |
if edl_ext != ".edl": | |
print("File is not an .edl") | |
exit() | |
with open(arg, "r", encoding="utf-8") as f: | |
edl = f.read() | |
edl_path = os.path.dirname(os.path.realpath(arg)) | |
# loop through cut blocks in edl | |
for block in edl.split("\n\n"): | |
clip = re.findall(r'\* FROM CLIP NAME: (.*)', block) | |
if not clip: | |
continue | |
clip = clip[0] | |
clip_name, clip_ext = os.path.splitext(os.path.basename(clip)) | |
# loop through lines in a block | |
for line in block.split("\n"): | |
if not "V " in line: | |
continue | |
# collect clip and timecode infos | |
video_streams = subprocess.Popen([ffprobe, "-v", "warning", "-print_format", | |
"json", "-show_format", "-show_streams", clip], | |
stdout=subprocess.PIPE, shell=True) | |
data = json.loads(video_streams.stdout.read()) | |
fps = data["streams"][0]["r_frame_rate"] | |
splitted_line = line.split(" ")[::-1] | |
inpoint = tcToSec(splitted_line[3], fps) | |
outpoint = tcToSec(splitted_line[2], fps) | |
duration = str(outpoint - inpoint) | |
cut = line.split(" ")[0] | |
# ffmpeg | |
ffin = os.path.join(edl_path, clip) | |
ffout = os.path.join( | |
args.o, f"{clip_name} # cut{cut}{clip_ext}") | |
cmd = [ffmpeg, "-hide_banner", "-v", "warning", "-ss", str(inpoint), "-i", ffin, | |
"-t", duration, "-c", "copy", "-y", ffout] | |
print("Writing", os.path.abspath(ffout)) | |
subprocess.check_call(cmd) | |
if __name__ == '__main__': | |
parser = argparse.ArgumentParser() | |
parser.add_argument('-i', help='Premiere .edl file', nargs='*') | |
parser.add_argument('-o', help='Output folder', nargs="?", | |
default=os.path.dirname(os.path.abspath(__file__))) | |
args = parser.parse_args() | |
if not os.path.isdir(args.o): | |
os.mkdir(args.o) | |
main(args) |
Wow, I didn't even remember this thing... Looking at old code is weird.
Anyways, I've updated it. Please try again.
My apologies. It was an error on my part. I'm new to this and still learning. Both the old and new version of your script work.
Are you no longer using this? I think I will be using this a ton. Thank you for creating it.
My only request would be an option to choose an output folder. I'm an beginner editor and I don't always have the space on the source drive.
I've gotten the script to run, but I'm having a few issues.
- The script grabs the master timecodes (right), not the source timecodes (left). So it's writing out the entire video in clips, rather than my Premiere timeline. I got around that by removing the master timecodes in a text editor.
001 AX AA/V C 00:06:53:04 00:08:30:01
002 AX AA/V C 00:14:06:21 00:16:25:07
003 AX AA/V C 00:23:37:15 00:25:54:00
004 AX AA/V C 00:32:58:18 00:35:20:23
005 AX AA/V C 00:35:27:22 00:36:15:04
006 AX AA/V C 00:42:02:05 00:44:53:15
007 AX AA/V C 00:49:58:07 00:51:29:01
008 AX AA/V C 01:47:30:16 01:49:51:06
009 AX AA/V C 01:58:14:15 01:59:51:01
010 AX AA/V C 01:59:57:02 02:00:37:02
011 AX AA/V C 02:03:08:19 02:05:11:18
012 AX AA/V C 02:18:40:15 02:19:03:11
013 AX AA/V C 02:30:59:08 02:32:02:07
014 AX AA/V C 02:37:28:13 02:39:05:03
- The first clip is 100% accurate. The second has 2 extra seconds at the start and 2 seconds missing at the end, and the clips gradually become more misaligned. Clip 12 is misaligned by 8 seconds. Clip 14 is off by 10.
I did come across a tip that will make the script much faster. I noticed that each clip took longer and longer for ffmpeg to start processing it. Putting the -ss before -i makes ffmpeg seek instantly.
No, I don't use this script any longer.
- I've added an option to ask for a destination folder.
- For your first question, you are right. It always worked out for me so far, but I've changed it to use the other 2 timecodes.
- For your second question, I don't know where the offset comes from as my script takes the times from the edl. I've tried some things but couldn't figure it out.
- ffmpeg: I added the start timecode before the input and made ffmpeg less verbose
- I've also changed the way the clips are named. They get their name from the edl cut number at the beginning of the line.
Tell me what you think. Maybe we can figure out the offset issue.
The changes are nice. I do like to see the progress of each clip, so I've switched that back. It works with unedited EDLs, and it spits out cuts quickly now.
I'm using a batch file to run the script for multiple folders. I'd love to be able to pass a destination variable to the script. Something like this:
ffmpeg-edl.py "%%f" "%destination%
Maybe the script could check for that first?
The offset problem turns out to be a problem with my Adobe Premiere project. According to ffmpeg and various video players my video length is 02:49:31.06, but Premiere shows it as 02:49:20:20. So, I'll have to figure that out.
Over in a Premiere thread, it is thought that this script may not be handling the framerate of my video correctly. They think it's reading '24/1.001' from the FFprobe and rather than calculating the result it's just dumping everything after and including the '/' to end up with 24fps rather than 23.976.
They suggested something like this may work, but it produces errors for me.
fps = data["streams"][0]["r_frame_rate"].split("/")[0] / data["streams"][0]["r_frame_rate"].split("/")[1]
I've changed the script to use argparse instead of a popup window for the destination.
Run it like this: python ffmpeg-edl.py -i file1.edl file2.edl file3.edl -o output_path
.
If you don't set an output folder, it falls back to the same folder as the ffmpeg-edl.py file.
The fps problem made sense so I changed it as well. It just reads the output from ffprobe now and calculates the correct fps.
Thank so much for all your work.
I've just tried this on a EDL that refers to multiple videos, and it does not handle them. It creates all of the cuts from the first clip.
Results in:
upscale 01 # cut001.mov
all the way up to
upscale 01 # cut009.mov
001 AX AA/V C 00:06:53:16 00:08:30:10 00:00:00:00 00:01:36:22
* FROM CLIP NAME: upscale 01.mov
002 AX AA/V C 00:14:07:20 00:16:25:25 00:01:36:22 00:03:54:27
* FROM CLIP NAME: upscale 01.mov
003 AX AA/V C 00:23:38:24 00:25:55:09 00:03:54:27 00:06:11:13
* FROM CLIP NAME: upscale 01.mov
004 AX AA/V C 00:04:59:05 00:07:21:14 00:06:11:13 00:08:33:20
* FROM CLIP NAME: upscale 02.mov
005 AX AA/V C 00:07:28:15 00:08:15:24 00:08:33:20 00:09:21:00
* FROM CLIP NAME: upscale 02.mov
006 AX AA/V C 00:14:03:05 00:16:56:11 00:09:21:00 00:12:14:05
* FROM CLIP NAME: upscale 02.mov
007 AX AA/V C 00:21:59:16 00:23:30:19 00:12:14:05 00:13:45:07
* FROM CLIP NAME: upscale 02.mov
008 AX AA/V C 00:22:35:02 00:25:05:09 00:13:45:07 00:16:15:15
* FROM CLIP NAME: upscale 04.mov
009 AX AA/V C 00:04:55:12 00:06:31:28 00:16:15:15 00:17:51:29
* FROM CLIP NAME: upscale 05.mov
It was written to only use a single clip. I've changed some stuff to hopefully make it work with multiple clips. Try again.
That's working great. Thank you.
python3 /home/maddy/Desktop/edl-editing/second.py -i madesh.edl
same location of my media files also:
Error log:
Simple multimedia streams analyzer
usage: ffprobe [OPTIONS] [INPUT_FILE]
You have to specify one input file.
Use -h to get full help or, even better, run 'man ffprobe'.
Traceback (most recent call last):
File "/home/maddy/Desktop/edl-editing/second.py", line 76, in
main(args)
File "/home/maddy/Desktop/edl-editing/second.py", line 51, in main
data = json.loads(video_streams.stdout.read())
File "/usr/lib/python3.8/json/init.py", line 357, in loads
return _default_decoder.decode(s)
File "/usr/lib/python3.8/json/decoder.py", line 337, in decode
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
File "/usr/lib/python3.8/json/decoder.py", line 355, in raw_decode
raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
Can't get this to work. I can run the script with no args and get the no arg usage tip, but I get no response when I give it an edl file. FFMpeg and ffprobe are in my system path, so those should be no problem.
python ffmpeg-edl.py "D:\Test video.edl"
Nothing happens. No results, no errors. Thought maybe I needed name my edl "D:\Test video.mov.edl" but that doesn't work either.