├── Dockerfile ├── README.md ├── Stopwatch.py ├── edl.py ├── edlcreate.py ├── edlcreate_timer.py ├── edledit.py ├── edltweak.py ├── edltweak_gui.py ├── edltweak_gui_mpv.py ├── requirements.txt └── setup.py /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:latest 2 | 3 | WORKDIR /usr/src/app 4 | 5 | COPY . . 6 | 7 | RUN apk add --no-cache py3-numpy py3-pillow py3-requests mediainfo && python3 ./setup.py install 8 | 9 | RUN python3 -c "import imageio; imageio.plugins.ffmpeg.download()" 10 | 11 | WORKDIR /edit 12 | 13 | ENTRYPOINT ["edledit"] 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | EDL Kit 2 | ======= 3 | 4 | Dependencies: A recent [ffmpeg](http://www.ffmpeg.org/download.html), the [MoviePy](https://github.com/Zulko/moviepy) Python module, and VLC (for edltweak and the edlcreate scripts). 5 | 6 | I'm developing this on Linux, but working on full Windows support in the future. Currently, if you have ffmpeg and the MoviePy Python module set up on Windows, the edledit script works. 7 | 8 | ##edledit 9 | 10 | This script takes in as arguments: 11 | * A video file. 12 | * An EDL file. 13 | * A file name to save the edited file to. 14 | 15 | Available options are: 16 | * -t, Number of process threads to use: A number from 0 to the number of CPU cores you have. Defaults to 2 if not specified. 17 | * -p, ffmpeg preset to use: Adjusts speed and efficiency of the compression. Defaults to "medium" if not specified. 18 | * -vb, Video bitrate: Automatically detected from original if not specified. 19 | * -ab, Audio bitrate: Automatically detected from original if not specified. 20 | 21 | Usage info is available using the "-h" option. 22 | 23 | Example command: `python3 edledit.py "Video.mp4" "Video.edl" "Video-edited.mp4" -t 3` 24 | 25 | The edits listed in the EDL file will be used to create an edited version of the original video. 26 | See [here](http://www.mplayerhq.hu/DOCS/HTML/en/edl.html) for instructions on making an EDL file. 27 | 28 | Currently, this script supports actions 1, 0 (Mute, and Cut Video), and my custom action (2) which 29 | cuts out the selected audio and speeds up the video enough to cover the audio cut and resync the video. 30 | 31 | 32 | ##edltweak_gui 33 | 34 | This script takes in as arguments: 35 | * A video file. 36 | * An EDL file. 37 | 38 | The script can also be run without arguments and the files selected in the GUI. 39 | 40 | Example command: `python edltweak_gui.py "My Video.mp4" "My Video.edl"` 41 | 42 | The script will start up a Python3 TTK (Tkinter) GUI that assists with making adjustments to (tweaking) 43 | the timings and edit types in EDL files. It also hooks into VLC to show a rendering of the adjusted segment of the video. 44 | The keyboard commands to operate the tool can be viewed by clicking the "Keyboard Controls" button after starting it. 45 | 46 | The functionality from the edlcreate script is now bundled into this as well. Pressing "o" (for Original) will 47 | play the video file in vlc, and you can mark edit points to save to an EDL file with "m" just like edlcreate while 48 | the original video is playing. 49 | 50 | 51 | ##edltweak 52 | 53 | This script does mostly the same thing as edltweak, but it has a text-based curses UI. 54 | 55 | Development has shifted to edltweak_gui now and this script will likely not be updated. 56 | 57 | 58 | ##edlcreate 59 | 60 | This script takes in as arguments: 61 | * A video file. 62 | * An EDL filename (to output to). 63 | 64 | Example command: `python edlcreate.py "My Video.mp4" "My Video.edl"` 65 | 66 | A curses-based UI and VLC start up and let you mark edit times in the video as you watch. Marked times will 67 | be written to the EDL file you specified. The keyboard commands are all listed in the UI when it starts. 68 | 69 | 70 | ##edlcreate_timer 71 | 72 | This script takes in as arguments: 73 | * An EDL filename (to output to). 74 | 75 | Example command: `python edlcreate.py "My Video.edl"` 76 | 77 | This one works exactly the same as the edlcreate script, but uses a timer to determine the time when you mark 78 | an edit instead of reading from VLC. This is useful if you want to mark edits while watching a live TV show 79 | that you're recording or going to acquire the video file for it later. 80 | -------------------------------------------------------------------------------- /Stopwatch.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """Stopwatch, a portable multi-lap timing utility class. 4 | Copyright (C) 2006 George F. Rice 5 | 6 | This program is free software; you can redistribute it and/or 7 | modify it under the terms of the GNU General Public License 8 | as published by the Free Software Foundation; either version 2 9 | of the License, or (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program; if not, write to the Free Software 18 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19 | """ 20 | 21 | import time 22 | 23 | class stopwatch: 24 | """ Implements a timer with multiple named lap timers. 25 | A newly created timer is NOT running. 26 | Use start() and stop() to begin/end. Check boolean '.running'. 27 | A lap timer is created on reference. 28 | """ 29 | def __init__(self, name = "stopwatch"): 30 | self.name = name 31 | self.startTime = time.time() 32 | self.elapsedTime = 0.0 33 | self.running = False 34 | self.lap = {} 35 | 36 | def start(self): 37 | """ Start or restart the timer. 38 | Note that while the timer is running, 39 | only the getElapsedTime() method is accurate 40 | for determining elapsed time. 41 | """ 42 | if not self.running: 43 | self.startTime = time.time() 44 | self.running = True 45 | 46 | def stop(self): 47 | """ Stop the timer and update the elapsedTime attribute. 48 | """ 49 | if self.running: 50 | self.elapsedTime += time.time() - self.startTime 51 | self.running = False 52 | 53 | def getElapsedTime(self): 54 | """ Returns the elapsed time as a float in seconds, 55 | regardless of the state of the timer. 56 | """ 57 | if self.running: 58 | return self.elapsedTime + (time.time() - self.startTime) 59 | else: 60 | return self.elapsedTime 61 | 62 | def stopLapTimer(self, lap = "lap timer"): 63 | """ Set (or reset) the named (or default) lap timer 64 | to the current elapsed time. The lap time is returned. 65 | """ 66 | self.lap[lap] = self.getElapsedTime() 67 | return self.lap[lap] 68 | 69 | def getLapTime(self, lap = "lap timer"): 70 | """ Return the named (or default) lap time. If it doesn't exist, 71 | it is created as the current elapsed time. 72 | """ 73 | try: 74 | return self.lap[lap] 75 | except: 76 | self.lap[lap] = self.getElapsedTime() 77 | return self.lap[lap] 78 | 79 | def getFormattedTime(self, lap = None): 80 | """ Return specified lap time (or elapsed time if omitted) 81 | formatted as HH:MM:SS.ds 82 | """ 83 | if lap == None: 84 | _et = self.getElapsedTime() 85 | else: 86 | _et = self.getLapTime(lap) 87 | _et += 0.005 # round to nearest hundredth 88 | _hh = int(_et / 3600) 89 | _et -= (_hh * 3600) 90 | _mm = int(_et / 60) 91 | _et -= (_mm * 60) 92 | _ss = int(_et) 93 | _et -= _ss 94 | _ds = int(_et * 100) 95 | return "%.2d:%.2d:%.2d.%.2d" % (_hh, _mm, _ss, _ds) 96 | -------------------------------------------------------------------------------- /edl.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | class Edit(object): 4 | def __init__(self, time1, time2, action): 5 | self.time1 = str(time1) 6 | self.time2 = str(time2) 7 | self.action = str(action) 8 | 9 | class EDL(object): 10 | def __init__(self, edlfile): 11 | self.edits = [] 12 | self.edlfile = edlfile 13 | 14 | if os.path.exists(self.edlfile) == False: 15 | open(self.edlfile, 'a').close() 16 | else: 17 | with open(self.edlfile) as f: 18 | for line in f.readlines(): 19 | if len(line.split()) == 3: 20 | self.edits.append(Edit(line.split()[0], line.split()[1], line.split()[2].split('\n')[0])) 21 | elif len(line.split()) == 2: 22 | self.edits.append(Edit(line.split()[0], line.split()[1], "-")) 23 | 24 | def sort(self): 25 | self.edits.sort(key=lambda x: float(x.time1)) 26 | 27 | def save(self): 28 | self.sort() 29 | with open(self.edlfile, 'w') as f: 30 | for edit in self.edits: 31 | f.writelines(str(edit.time1)+" "+str(edit.time2)+" "+edit.action+"\n") 32 | 33 | def add(self, time1, time2, action): 34 | self.edits.append(Edit(time1, time2, action)) 35 | self.sort() 36 | 37 | 38 | -------------------------------------------------------------------------------- /edlcreate.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | 3 | import os, sys, curses, edl, socket, subprocess, time 4 | 5 | 6 | def stdout_off(): 7 | _stderr = sys.stderr 8 | _stdout = sys.stdout 9 | null = open(os.devnull,'wb') 10 | sys.stdout = sys.stderr = null 11 | 12 | #stdout_off() 13 | 14 | try: 15 | from subprocess import DEVNULL 16 | except ImportError: 17 | # import os 18 | DEVNULL = open(os.devnull, 'wb') 19 | 20 | vlc = socket.socket() 21 | testConn = vlc.connect_ex(('127.0.0.1', 12000)) 22 | if testConn > 0: 23 | subprocess.Popen(["vlc","-q","--extraintf","rc","--rc-host","localhost:12000"], stdout=DEVNULL, stderr=subprocess.STDOUT) 24 | print("Starting VLC...") 25 | while testConn > 0: 26 | testConn = vlc.connect_ex(('127.0.0.1', 12000)) 27 | time.sleep(0.5) 28 | 29 | else: 30 | vlc.connect_ex(('127.0.0.1', 12000)) 31 | 32 | 33 | vlc.recv(2048) 34 | 35 | def rewind(): 36 | rwTime = int(getTime())-5 37 | vlc.send(b"seek "+rwTime.encode('utf-8')+b"\n") 38 | vlc.recv(2048) 39 | 40 | def play_pause(): 41 | vlc.send(b"pause\n") 42 | vlc.recv(2048) 43 | 44 | def stop(): 45 | vlc.send(b"stop\n") 46 | vlc.close() 47 | 48 | global result 49 | result = "" 50 | def getTime(): 51 | play_pause() 52 | play_pause() 53 | vlc.send(b"get_time\n") 54 | global result 55 | result = vlc.recv(100).split(b'\r') 56 | 57 | while b'> ' in result[0]: 58 | vlc.send(b"get_time\n") 59 | global result 60 | result = vlc.recv(100).split(b'\r') 61 | 62 | if len(result) == 3: 63 | time = result[1].split(b' ')[1] 64 | else: 65 | time = result[0] 66 | 67 | return time 68 | 69 | 70 | def addEdit(): 71 | time1 = float(int(getTime())) 72 | estruct.add(time1-1, time1-0.5, 1) 73 | estruct.sort() 74 | 75 | def showStruct(): 76 | linenum = 0 77 | while linenum <= len(estruct.edits)-1: 78 | stdscr.addstr(linenum+2, 58, " ") 79 | stdscr.addstr(linenum+2, 70, str(estruct.edits[linenum].time1)) 80 | linenum = linenum + 1 81 | 82 | stdscr.refresh() 83 | 84 | 85 | 86 | def stdout_on(): 87 | sys.stderr = _stderr 88 | sys.stdout = _stdout 89 | 90 | 91 | videofile = sys.argv[1] 92 | vlc.send(b"add "+videofile.encode('utf-8')+b"\n") 93 | 94 | edlfile = sys.argv[2] 95 | editline = 0 96 | 97 | # Open specified edl file. (create it if it doesn't exist) 98 | estruct = edl.EDL(edlfile) 99 | 100 | 101 | stdscr = curses.initscr() 102 | curses.cbreak() 103 | curses.noecho() 104 | curses.curs_set(0) 105 | stdscr.keypad(1) 106 | 107 | showStruct() 108 | 109 | stdscr.addstr(0,2,"EDL Kit: Creator") 110 | stdscr.addstr(8,2,"Keyboard Controls:") 111 | stdscr.addstr(9,2,"m Mark an edit time in the video.") 112 | stdscr.addstr(10,2,"p Play/pause video.") 113 | stdscr.addstr(11,2,"u Rewind video 5 seconds.") 114 | stdscr.addstr(11,2,"w Write edits to file. ") 115 | stdscr.addstr(12,2,"q Write edits to file and exit.") 116 | 117 | 118 | key = '' 119 | while key != ord('q'): 120 | key = stdscr.getch() 121 | stdscr.refresh() 122 | if key == ord('m'): 123 | addEdit() 124 | showStruct() 125 | elif key == ord('p'): 126 | play_pause() 127 | elif key == ord('u'): 128 | rewind() 129 | elif key == ord('w'): 130 | estruct.save() 131 | stdscr.addstr(0,68,"Saved") 132 | stdscr.refresh() 133 | 134 | stop() 135 | curses.endwin() 136 | 137 | -------------------------------------------------------------------------------- /edlcreate_timer.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | 3 | import os, sys, curses, edl, time, Stopwatch 4 | 5 | def showStruct(): 6 | linenum = 0 7 | while linenum <= len(estruct.edits)-1: 8 | stdscr.addstr(linenum+2, 58, " ") 9 | stdscr.addstr(linenum+2, 70, str(estruct.edits[linenum].time1)) 10 | linenum = linenum + 1 11 | 12 | stdscr.refresh() 13 | 14 | def pause_continue(): 15 | if sw.running == True: 16 | sw.stop() 17 | stdscr.addstr(5,30,"Paused at "+str("{0:.2f}".format(sw.getElapsedTime()))+" ") 18 | else: 19 | sw.start() 20 | stdscr.addstr(5,30,"Running... ") 21 | 22 | 23 | editline = 0 24 | 25 | # Open specified edl file. (create it if it doesn't exist) 26 | estruct = edl.EDL(sys.argv[1]) 27 | 28 | sw = Stopwatch.stopwatch() 29 | 30 | stdscr = curses.initscr() 31 | curses.cbreak() 32 | curses.noecho() 33 | curses.curs_set(0) 34 | stdscr.keypad(1) 35 | 36 | showStruct() 37 | 38 | stdscr.addstr(0,2,"EDL Kit: Creator with Timer") 39 | stdscr.addstr(8,2,"Keyboard Controls:") 40 | stdscr.addstr(9,2,"m Mark an edit time in the video.") 41 | stdscr.addstr(10,2,"p Pause/continue timer.") 42 | stdscr.addstr(11,2,"w Write edits to file. ") 43 | stdscr.addstr(12,2,"q Write edits to file and exit.") 44 | 45 | stdscr.addstr(5,30,"Press 'p' (unpause) to begin.") 46 | 47 | key = '' 48 | while key != ord('q'): 49 | key = stdscr.getch() 50 | stdscr.refresh() 51 | if key == ord('m'): 52 | time1 = float("%.2f" % sw.getElapsedTime()) 53 | estruct.add("{0:.2f}".format(time1-1), "{0:.2f}".format(time1-0.5), 1) 54 | showStruct() 55 | elif key == ord('p'): 56 | pause_continue() 57 | elif key == ord('w'): 58 | estruct.save() 59 | stdscr.addstr(0,68,"Saved") 60 | stdscr.refresh() 61 | 62 | 63 | estruct.save() 64 | sw.stop() 65 | curses.endwin() 66 | 67 | -------------------------------------------------------------------------------- /edledit.py: -------------------------------------------------------------------------------- 1 | from moviepy.editor import * 2 | import sys, os, re, edl, tempfile, argparse 3 | from pymediainfo import MediaInfo 4 | 5 | def render(videofile, estruct, outfile, videoBitrate="2000k", audioBitrate="400k", threadNum=2, ffmpegPreset="medium", vcodec=None, acodec=None, ffmpeg_params=None, writeLogfile=False): 6 | clipNum = 1 7 | global prevTime 8 | prevTime = 0 9 | actionTime = False 10 | v = VideoFileClip(videofile) 11 | duration = v.duration 12 | clips = v.subclip(0,0) #blank 0-time clip 13 | 14 | for edit in estruct.edits: 15 | nextTime = float(edit.time1) 16 | time2 = float(edit.time2) 17 | action = edit.action 18 | 19 | if nextTime > duration: 20 | nextTime = duration 21 | 22 | if prevTime > duration: 23 | prevTime = duration 24 | 25 | clip = v.subclip(prevTime,nextTime) 26 | clips = concatenate([clips,clip]) 27 | print("created subclip from " + str(prevTime) + " to " + str(nextTime)) 28 | 29 | prevTime = nextTime 30 | nextTime = time2 31 | 32 | if action == "1": 33 | # Muting audio only. Create a segment with no audio and add it to the rest. 34 | clip = VideoFileClip(videofile, audio = False).subclip(prevTime,nextTime) 35 | clips = concatenate([clips,clip]) 36 | print("created muted subclip from " + str(prevTime) + " to " + str(nextTime)) 37 | 38 | # Advance to next segment time. 39 | prevTime = nextTime 40 | 41 | elif action == "0": 42 | #Do nothing (video and audio cut) 43 | print("Cut video from "+str(prevTime)+" to "+str(nextTime)+".") 44 | prevTime = nextTime 45 | 46 | elif action == "2": 47 | # Cut audio and speed up video to cover it. 48 | #v = VideoFileClip(videofile) 49 | 50 | # Create two clips. One for the cut segment, one immediately after of equal length for use in the speedup. 51 | s1 = v.subclip(prevTime,nextTime).without_audio() 52 | s2 = v.subclip(nextTime,(nextTime + s1.duration)) 53 | 54 | # Put the clips together, speed them up, and use the audio from the second segment. 55 | clip = concatenate([s1,s2.without_audio()]).speedx(final_duration=s1.duration).set_audio(s2.audio) 56 | clips = concatenate([clips,clip]) 57 | print("Cutting audio from "+str(prevTime)+" to "+str(nextTime)+" and squeezing video from "+str(prevTime)+" to "+str(nextTime + s1.duration)+" into that slot.") 58 | # Advance to next segment time (+time from speedup) 59 | prevTime = nextTime + s1.duration 60 | 61 | else: 62 | # No edit action. Just put the clips together and continue. 63 | clip = v.subclip(prevTime,nextTime) 64 | clips = concatenate([clips,clip]) 65 | 66 | # Advance to next segment time. 67 | prevTime = nextTime 68 | 69 | 70 | videoLength = duration 71 | if prevTime > duration: 72 | prevTime = duration 73 | 74 | if ffmpeg_params != None: 75 | fparams = [] 76 | for x in ffmpeg_params.split(' '): 77 | fparams.extend(x.split('=')) 78 | else: 79 | fparams = None 80 | 81 | clip = v.subclip(prevTime,videoLength) 82 | print("created ending clip from " + str(prevTime) + " to " + str(videoLength)) 83 | clips = concatenate([clips,clip]) 84 | clips.write_videofile(outfile, codec=vcodec, fps=24, bitrate=videoBitrate, audio_bitrate=audioBitrate, audio_codec=acodec, ffmpeg_params=fparams, threads=threadNum, preset=ffmpegPreset, write_logfile=writeLogfile) 85 | 86 | 87 | def main(): 88 | parser = argparse.ArgumentParser() 89 | parser.add_argument("infile", help="Original input video file.") 90 | parser.add_argument("edlfile", help="EDL file with edit definitions.") 91 | parser.add_argument("outfile", help="Edited video file path/name.") 92 | parser.add_argument("-t", "--threads", type=int, help="Number of CPU threads to use.") 93 | parser.add_argument("-p", "--preset", choices=["ultrafast", "superfast", "fast", "medium", "slow", "superslow"], help="FFMPEG preset to use for optimizing the compression. Defaults to 'medium'.") 94 | parser.add_argument("-vb", "--videobitrate", help="Video bitrate setting. Auto-detected from original video unless specified.") 95 | parser.add_argument("-ab", "--audiobitrate", help="Audio bitrate setting. Auto-detected from original video unless specified.") 96 | parser.add_argument("-vc", "--vcodec", help="Video codec to use.") 97 | parser.add_argument("-ac", "--acodec", help="Audio codec to use.") 98 | parser.add_argument("-fp", "--ffmpegparams", help="Additional FFMpeg parameters to use. Example: '-crf=24 -s=640x480'.") 99 | args = parser.parse_args() 100 | 101 | estruct = edl.EDL(args.edlfile) 102 | 103 | videoBitrate = "" 104 | audioBitrate = "" 105 | 106 | if args.threads == None: 107 | threadNum = 2 108 | else: 109 | threadNum = args.threads 110 | 111 | if args.preset == None: 112 | ffmpegPreset = "medium" 113 | else: 114 | ffmpegPreset = args.preset 115 | 116 | mi = MediaInfo.parse(args.infile) 117 | if args.videobitrate == None: 118 | videoBitrate = str(int(mi.tracks[1].bit_rate / 1000)) + "k" 119 | print("Using original video bitrate: "+videoBitrate) 120 | else: 121 | videoBitrate = args.videobitrate 122 | if videoBitrate[-1] != 'k': 123 | videoBitrate = videoBitrate+'k' 124 | 125 | if args.audiobitrate == None: 126 | try: 127 | audioBitrate = str(int(mi.tracks[2].bit_rate / 1000)) + "k" 128 | except TypeError: 129 | audioBitrate = str(int(int(mi.tracks[2].bit_rate.split(' / ')[1]) / 1000)) + "k" 130 | 131 | print("Using original audio bitrate: "+audioBitrate) 132 | else: 133 | audioBitrate = args.audiobitrate 134 | if audioBitrate[-1] != 'k': 135 | audioBitrate = audioBitrate+'k' 136 | 137 | render(args.infile, estruct, args.outfile, videoBitrate, audioBitrate, threadNum=threadNum, vcodec=args.vcodec, acodec=args.acodec, ffmpeg_params=args.ffmpegparams, ffmpegPreset=ffmpegPreset) 138 | 139 | 140 | if __name__ == "__main__": 141 | main() 142 | 143 | -------------------------------------------------------------------------------- /edltweak.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | 3 | import os, sys, curses, edl, socket, subprocess, time 4 | from moviepy.editor import * 5 | 6 | num1 = 0 7 | num2 = 0 8 | action2 = 0 9 | 10 | def stdout_off(): 11 | _stderr = sys.stderr 12 | _stdout = sys.stdout 13 | null = open(os.devnull,'wb') 14 | sys.stdout = sys.stderr = null 15 | 16 | stdout_off() 17 | 18 | try: 19 | from subprocess import DEVNULL 20 | except ImportError: 21 | # import os 22 | DEVNULL = open(os.devnull, 'wb') 23 | 24 | vlc = socket.socket() 25 | testConn = vlc.connect_ex(('127.0.0.1', 12000)) 26 | if testConn > 0: 27 | subprocess.Popen(["vlc","-q","--extraintf","rc","--rc-host","localhost:12000"], stdout=DEVNULL, stderr=subprocess.STDOUT) 28 | print "Starting VLC..." 29 | while testConn > 0: 30 | testConn = vlc.connect_ex(('127.0.0.1', 12000)) 31 | time.sleep(0.5) 32 | 33 | else: 34 | vlc.connect_ex(('127.0.0.1', 12000)) 35 | 36 | def rewind(): 37 | vlc.send('prev\n') 38 | vlc.send('play\n') 39 | 40 | def play_pause(): 41 | vlc.send('pause\n') 42 | 43 | def stop(): 44 | vlc.send('shutdown\n') 45 | vlc.close() 46 | 47 | def reload(): 48 | vlc.send('clear\n') 49 | vlc.send('add /tmp/tweak.mp4\n') 50 | 51 | def load_original(): 52 | vlc.send('clear\n') 53 | vlc.send('add '+sys.argv[1]+'\n') 54 | 55 | def stdout_on(): 56 | sys.stderr = _stderr 57 | sys.stdout = _stdout 58 | 59 | 60 | videofile = sys.argv[1] 61 | edlfile = sys.argv[2] 62 | edlfile_read = open(edlfile, 'r') 63 | estruct = edl.struct(edlfile_read) 64 | editline = 0 65 | 66 | 67 | stdscr = curses.initscr() 68 | curses.cbreak() 69 | curses.noecho() 70 | curses.curs_set(0) 71 | stdscr.keypad(1) 72 | 73 | 74 | def createClip(start, end, time1, time2, action): 75 | print "Action: "+action 76 | 77 | clip1 = VideoFileClip(videofile).subclip(start,time1) 78 | 79 | if str(action) == "1": 80 | clip2 = VideoFileClip(videofile, audio = False).subclip(time1,time2) 81 | clip3 = VideoFileClip(videofile).subclip(time2,end) 82 | clips = concatenate([clip1,clip2,clip3]) 83 | elif str(action) == "0": 84 | clip3 = VideoFileClip(videofile).subclip(time2,end) 85 | clips = concatenate([clip1,clip3]) 86 | else: 87 | clips = VideoFileClip(videofile).subclip(start,end) 88 | 89 | 90 | clips.write_videofile("/tmp/tweak.mp4", codec="libx264", fps=24, preset="ultrafast", threads=2) 91 | reload() 92 | 93 | stdscr.addstr(5, 30, "Reloading edit...") 94 | stdscr.refresh() 95 | 96 | stdscr.addstr(0,2,"EDL Kit: Tweaker") 97 | stdscr.addstr(8,2,"Keyboard Controls:") 98 | stdscr.addstr(9,2,"s,f Move Time1 left and right by 0.01 seconds.") 99 | stdscr.addstr(10,2,"z,c Move Time1 left and right by 0.10 seconds.") 100 | stdscr.addstr(11,2,"j,l Move Time2 left and right by 0.01 seconds.") 101 | stdscr.addstr(12,2,"m,. Move Time2 left and right by 0.10 seconds.") 102 | stdscr.addstr(13,2,"1,0,- Switch between action 1 (mute), 0 (cut),") 103 | stdscr.addstr(14,2," and - (disable).") 104 | stdscr.addstr(15,2,"[,] Move up and down edl file entries.") 105 | stdscr.addstr(16,2," You can also use the arrow keys.") 106 | stdscr.addstr(17,2,"t Transfer edits to edl structure.") 107 | stdscr.addstr(18,2,"w Write edits to edl file.") 108 | stdscr.addstr(19,2,"r Recompile edits and display video.") 109 | stdscr.addstr(20,2,"u,p Rewind or pause the video.") 110 | stdscr.addstr(21,2,"o Play the source video file without changes.") 111 | stdscr.addstr(22,2,"q Quit") 112 | 113 | stdscr.refresh() 114 | 115 | 116 | 117 | 118 | def show_struct(): 119 | linenum = 0 120 | while linenum <= len(estruct.time1)-1: 121 | stdscr.addstr(linenum+2, 58, " ") 122 | if linenum == editline: 123 | stdscr.addstr(linenum+2, 68, "= "+estruct.time1[linenum]+" "+estruct.time2[linenum]+" "+estruct.action[linenum]+" =") 124 | else: 125 | stdscr.addstr(linenum+2, 70, estruct.time1[linenum]+" "+estruct.time2[linenum]+" "+estruct.action[linenum]) 126 | 127 | linenum = linenum + 1 128 | 129 | global num1 130 | num1 = float(estruct.time1[editline]) 131 | global num2 132 | num2 = float(estruct.time2[editline]) 133 | global action2 134 | action2 = str(estruct.action[editline]) 135 | stdscr.addstr(2, 20, "Time1: "+str(num1)+" ") 136 | stdscr.addstr(2, 40, "Time2: "+str(num2)+" ") 137 | 138 | if action2 == "1": 139 | stdscr.addstr(5, 30, "Set to mute mode.") 140 | elif action2 == "0": 141 | stdscr.addstr(5, 30, "Set to cut mode. ") 142 | else: 143 | stdscr.addstr(5, 30, "Set to disabled. ") 144 | 145 | stdscr.refresh() 146 | 147 | show_struct() 148 | 149 | 150 | stdscr.refresh() 151 | 152 | key = '' 153 | while key != ord('q'): 154 | key = stdscr.getch() 155 | stdscr.refresh() 156 | if key == ord('s'): 157 | #global num1 158 | num1 = num1-0.01 159 | stdscr.addstr(2, 20, "Time1: "+str(num1)+" ") 160 | stdscr.refresh() 161 | elif key == ord('z'): 162 | #global num1 163 | num1 = num1-0.10 164 | stdscr.addstr(2, 20, "Time1: "+str(num1)+" ") 165 | stdscr.refresh() 166 | elif key == ord('f'): 167 | #global num1 168 | num1 = num1+0.01 169 | stdscr.addstr(2, 20, "Time1: "+str(num1)+" ") 170 | stdscr.refresh() 171 | elif key == ord('c'): 172 | #global num1 173 | num1 = num1+0.10 174 | stdscr.addstr(2, 20, "Time1: "+str(num1)+" ") 175 | stdscr.refresh() 176 | elif key == ord('j'): 177 | #global num2 178 | num2 = num2-0.01 179 | stdscr.addstr(2, 40, "Time2: "+str(num2)+" ") 180 | stdscr.refresh() 181 | elif key == ord('m'): 182 | #global num2 183 | num2 = num2-0.10 184 | stdscr.addstr(2, 40, "Time2: "+str(num2)+" ") 185 | stdscr.refresh() 186 | elif key == ord('l'): 187 | #global num2 188 | num2 = num2+0.01 189 | stdscr.addstr(2, 40, "Time2: "+str(num2)+" ") 190 | stdscr.refresh() 191 | elif key == ord('.'): 192 | #global num2 193 | num2 = num2+0.10 194 | stdscr.addstr(2, 40, "Time2: "+str(num2)+" ") 195 | stdscr.refresh() 196 | elif key == ord('1'): 197 | stdscr.addstr(5, 30, "Set to mute mode.") 198 | #global action2 199 | action2 = 1 200 | stdscr.refresh() 201 | elif key == ord('0'): 202 | stdscr.addstr(5, 30, "Set to cut mode. ") 203 | #global action2 204 | action2 = 0 205 | stdscr.refresh() 206 | elif key == ord('-'): 207 | stdscr.addstr(5, 30, "Set to disabled. ") 208 | #global action2 209 | action2 = "-" 210 | stdscr.refresh() 211 | elif key == ord('r'): 212 | stdscr.addstr(5, 30, "Reloading edit...") 213 | stdscr.refresh() 214 | createClip(num1-3, num2+3, num1, num2, action2) 215 | if str(action2) == "1": 216 | stdscr.addstr(5, 30, "Set to mute mode.") 217 | elif str(action2) == "0": 218 | stdscr.addstr(5, 30, "Set to cut mode. ") 219 | else: 220 | stdscr.addstr(5, 30, "Set to disabled. ") 221 | stdscr.refresh() 222 | elif key == ord('u'): 223 | rewind() 224 | elif key == ord('p'): 225 | play_pause() 226 | elif key == ord('o'): 227 | load_original() 228 | elif key == ord('['): 229 | if editline != 0: 230 | editline = editline-1 231 | show_struct() 232 | elif key == ord(']'): 233 | if editline != len(estruct.time1)-1: 234 | editline = editline+1 235 | show_struct() 236 | elif key == curses.KEY_UP: 237 | if editline != 0: 238 | editline = editline-1 239 | show_struct() 240 | elif key == curses.KEY_DOWN: 241 | if editline != len(estruct.time1)-1: 242 | editline = editline+1 243 | show_struct() 244 | elif key == ord('t'): 245 | estruct.time1[editline] = str(num1) 246 | estruct.time2[editline] = str(num2) 247 | estruct.action[editline] = str(action2) 248 | stdscr.addstr(0,68," ") 249 | show_struct() 250 | elif key == ord('w'): 251 | edlfile_write = open(edlfile, 'w') 252 | ewriter = edl.writer(edlfile_write) 253 | ewriter.write_struct(estruct) 254 | edlfile_write.close() 255 | stdscr.addstr(0,68,"Saved") 256 | stdscr.refresh() 257 | 258 | 259 | stop() 260 | curses.endwin() 261 | -------------------------------------------------------------------------------- /edltweak_gui.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | 3 | import os, sys, curses, edl, socket, subprocess, time 4 | from moviepy.editor import * 5 | 6 | from tkinter import * 7 | from tkinter import ttk 8 | from tkinter import filedialog 9 | from pprint import pprint 10 | 11 | num1 = 0 12 | num2 = 0 13 | action2 = 0 14 | 15 | playing_original = False 16 | redo_seconds = 0 17 | 18 | root = Tk() 19 | root.title("EDL Kit: Tweaker") 20 | root.columnconfigure(0, weight=1) 21 | root.rowconfigure(0, weight=1) 22 | 23 | def updateDisplay(e): 24 | global num1 25 | global num2 26 | global action2 27 | item = struct.selection()[0] 28 | t1value.set(float(estruct.edits[int(struct.item(item, "text"))-1].time1)) 29 | num1 = float(t1value.get()) 30 | t2value.set(float(estruct.edits[int(struct.item(item, "text"))-1].time2)) 31 | num2 = float(t2value.get()) 32 | if estruct.edits[int(struct.item(item, "text"))-1].action == "1": 33 | actionvalue.set("Mute") 34 | action2 = 1 35 | elif estruct.edits[int(struct.item(item, "text"))-1].action == "0": 36 | actionvalue.set("Cut") 37 | action2 = 0 38 | elif estruct.edits[int(struct.item(item, "text"))-1].action == "-": 39 | actionvalue.set("None") 40 | action2 = "-" 41 | elif estruct.edits[int(struct.item(item, "text"))-1].action == "2": 42 | actionvalue.set("Cut audio, Speed up video") 43 | action2 = 2 44 | else: 45 | actionvalue.set("Unknown") 46 | action2 = "-" 47 | 48 | 49 | 50 | mainframe = ttk.Frame(root, padding=(3, 3, 12, 12)) 51 | mainframe.grid(sticky='nwse') 52 | for column in range(3): 53 | mainframe.columnconfigure(column, weight=1) 54 | mainframe.rowconfigure(1, weight=1) 55 | 56 | def stdout_off(): 57 | _stderr = sys.stderr 58 | _stdout = sys.stdout 59 | null = open(os.devnull,'wb') 60 | sys.stdout = sys.stderr = null 61 | 62 | #stdout_off() 63 | 64 | try: 65 | from subprocess import DEVNULL 66 | except ImportError: 67 | # import os 68 | DEVNULL = open(os.devnull, 'wb') 69 | 70 | vlc = socket.socket() 71 | testConn = vlc.connect_ex(('127.0.0.1', 12000)) 72 | if testConn > 0: 73 | subprocess.Popen(["vlc","-q","--extraintf","rc","--rc-host","localhost:12000"], stdout=DEVNULL, stderr=subprocess.STDOUT) 74 | print("Starting VLC...") 75 | while testConn > 0: 76 | testConn = vlc.connect_ex(('127.0.0.1', 12000)) 77 | time.sleep(0.5) 78 | 79 | else: 80 | vlc.connect_ex(('127.0.0.1', 12000)) 81 | 82 | def rewind(): 83 | if redo_seconds == 0: 84 | vlc.send(bytes('prev\n', 'UTF-8')) 85 | vlc.send(bytes('play\n', 'UTF-8')) 86 | else: 87 | vlc.send(('seek '+str(redo_seconds)+'\n').encode()) 88 | 89 | def play_pause(): 90 | vlc.send('pause\n'.encode()) 91 | vlc.recv(2048) 92 | 93 | def stop(): 94 | vlc.send('shutdown\n'.encode()) 95 | vlc.close() 96 | 97 | def reload(): 98 | vlc.send('clear\n'.encode()) 99 | vlc.send('add /tmp/tweak.mp4\n'.encode()) 100 | global playing_original 101 | global redo_seconds 102 | playing_original = False 103 | redo_seconds = 0 104 | 105 | def load_original(): 106 | vlc.send('clear\n'.encode()) 107 | vlc.send(('add '+videofile+'\n').encode()) 108 | global playing_original 109 | global redo_seconds 110 | playing_original = True 111 | redo_seconds = 0 112 | 113 | def redo_mark(seconds): 114 | vlc.send('clear\n'.encode()) 115 | vlc.send(('add '+videofile+'\n').encode()) 116 | global redo_seconds 117 | redo_seconds = seconds 118 | vlc.send(('seek '+str(seconds)+'\n').encode()) 119 | 120 | vlc.recv(2048) 121 | def getTime(): 122 | play_pause() 123 | play_pause() 124 | vlc.send(b"get_time\n") 125 | result = "" 126 | result = vlc.recv(100).split(b'\r') 127 | 128 | while b'> ' in result[0]: 129 | vlc.send(b"get_time\n") 130 | result = vlc.recv(100).split(b'\r') 131 | 132 | if len(result) == 3: 133 | time = result[1].split(b' ')[1] 134 | else: 135 | time = result[0] 136 | 137 | return time 138 | 139 | def stdout_on(): 140 | sys.stderr = _stderr 141 | sys.stdout = _stdout 142 | 143 | 144 | videofile = "" 145 | edlfile = "" 146 | #edlfile_read = "" 147 | estruct = "" 148 | editline = 0 149 | 150 | 151 | def videofileset(e): 152 | global videofile 153 | videofile = filedialog.askopenfilename() 154 | if videofile != "": 155 | videofilevalue.set(videofile.split('/')[-1]) 156 | 157 | if edlfile != "": 158 | show_struct() 159 | 160 | def edlfileset(e): 161 | global edlfile 162 | global estruct 163 | edlfile = filedialog.askopenfilename() 164 | if edlfile != "": 165 | edlfilevalue.set(edlfile.split('/')[-1]) 166 | estruct = edl.EDL(edlfile) 167 | 168 | if videofile != "": 169 | show_struct() 170 | 171 | #stdscr = curses.initscr() 172 | #curses.cbreak() 173 | #curses.noecho() 174 | #curses.curs_set(0) 175 | #stdscr.keypad(1) 176 | 177 | intervalLabelFrame = ttk.LabelFrame(mainframe, padding=5, text="Time Intervals") 178 | intervalLabelFrame.grid(padx=5, pady=5, ipady=2, sticky=(W,N), row=0, column=0) 179 | ttk.Label(intervalLabelFrame, text="Time 1: ").grid(column=0, row=0, sticky=(E)) 180 | t1value = StringVar() 181 | t1value.set("0.00") 182 | t1label = ttk.Label(intervalLabelFrame, textvariable=t1value, width=12).grid(column=1, row=0, sticky=(W)) 183 | ttk.Label(intervalLabelFrame, text="Time 2: ").grid(column=2, row=0, sticky=(E)) 184 | t2value = StringVar() 185 | t2value.set("0.00") 186 | t2label = ttk.Label(intervalLabelFrame, textvariable=t2value, width=8).grid(column=3, row=0, sticky=(W)) 187 | 188 | actionLabelFrame = ttk.LabelFrame(mainframe, padding=5, text="Action") 189 | actionLabelFrame.grid(padx=5, pady=5, ipady=2, sticky=(W,N), row=1, column=0) 190 | actionvalue = StringVar() 191 | actionvalue.set("None") 192 | actionlabel = ttk.Label(actionLabelFrame, textvariable=actionvalue, width=30).grid(column=0, row=0, sticky=(E), padx=10) 193 | 194 | statusLabelFrame = ttk.LabelFrame(root, padding=5, text="Status") 195 | #statusLabelFrame.grid(padx=5, pady=5, ipady=2, sticky=(W), row=2, column=0) 196 | statusLabelFrame.place(x=145, y=145, anchor='c') 197 | statusvalue = StringVar() 198 | statusvalue.set("Ready") 199 | statuslabel = ttk.Label(statusLabelFrame, textvariable=statusvalue, width=30).grid(column=0, row=0, sticky=(E), padx=10) 200 | 201 | filesLabelFrame = ttk.LabelFrame(root, padding=5, text="Files") 202 | #filesLabelFrame.grid(padx=5, pady=5, sticky=(W,N), row=3, column=0) 203 | filesLabelFrame.place(x=145, y=205, anchor='c') 204 | videofilevalue = StringVar() 205 | videofilelabel = ttk.Label(filesLabelFrame, textvariable=videofilevalue, width=30) 206 | videofilelabel.grid(column=0, row=0, sticky=(W, E), ipadx=10) 207 | videofilevalue.set("") 208 | 209 | edlfilevalue = StringVar() 210 | edlfilelabel = ttk.Label(filesLabelFrame, textvariable=edlfilevalue, width=30) 211 | edlfilelabel.grid(column=0, row=1, sticky=(W, E, N), ipadx=10) 212 | edlfilevalue.set("") 213 | 214 | struct = ttk.Treeview(mainframe, columns=("Time 1", "Time 2", "Action"), height=20) 215 | struct['show'] = 'headings' 216 | struct.grid(column=1, row=0, rowspan=7) 217 | struct.heading("Time 1", text="Time 1", anchor='center') 218 | struct.column("Time 1", width=70, anchor='center') 219 | struct.heading("Time 2", text="Time 2", anchor='center') 220 | struct.column("Time 2", width=70, anchor='center') 221 | struct.heading("Action", text="Action", anchor='center') 222 | struct.column("Action", width=60, anchor='center') 223 | s = ttk.Scrollbar(struct, orient="vertical", command=struct.yview) 224 | struct.configure(yscrollcommand=s.set) 225 | 226 | 227 | def createClip(start, end, time1, time2, action): 228 | #print("Action: "+action) 229 | v = VideoFileClip(videofile) 230 | clip1 = v.subclip(start,time1) 231 | 232 | if str(action) == "1": 233 | if time2 > v.duration: 234 | end = v.duration 235 | time2 = end 236 | 237 | clip2 = VideoFileClip(videofile, audio = False).subclip(time1,time2) 238 | clip3 = v.subclip(time2,end) 239 | clips = concatenate([clip1,clip2,clip3]) 240 | elif str(action) == "0": 241 | if time2 > v.duration: 242 | end = v.duration 243 | time2 = end 244 | 245 | clip3 = v.subclip(time2,end) 246 | clips = concatenate([clip1,clip3]) 247 | elif str(action) == "2": 248 | if time2 > v.duration: 249 | end = v.duration 250 | time2 = end 251 | 252 | s1 = v.subclip(time1,time2).without_audio() 253 | s2 = v.subclip(time2, (time2 + s1.duration)) 254 | clip2 = concatenate([s1,s2.without_audio()]).speedx(final_duration=s1.duration).set_audio(s2.audio) 255 | clip3 = v.subclip((time2 + s1.duration),end) 256 | clips = concatenate([clip1,clip2,clip3]) 257 | else: 258 | if time2 > v.duration: 259 | end = v.duration 260 | time2 = end 261 | 262 | clips = v.subclip(start,end) 263 | 264 | print("Creating clip with modified center from "+str(time1)+" to "+str(time2)) 265 | clips.write_videofile("/tmp/tweak.mp4", codec="libx264", fps=24, preset="ultrafast", threads=5) 266 | reload() 267 | 268 | 269 | def show_help(): 270 | helproot = Toplevel(root) 271 | helpframe = ttk.Frame(helproot, padding="3 3 12 12") 272 | helpframe.grid(column=0, row=0, sticky=(N, W, E, S)) 273 | helpframe.columnconfigure(0, weight=1) 274 | helpframe.rowconfigure(0, weight=1) 275 | ttk.Label(helpframe, text="Keyboard Controls:").grid(column=3, row=8, sticky=(W, E), columnspan=4) 276 | ttk.Label(helpframe, text="s,f\tMove Time1 left and right by 0.01 seconds.").grid(column=3, row=9, sticky=(W, E), columnspan=4) 277 | ttk.Label(helpframe, text="z,c\tMove Time1 left and right by 0.10 seconds.").grid(column=3, row=10, sticky=(W, E), columnspan=4) 278 | ttk.Label(helpframe, text="j,l\tMove Time2 left and right by 0.01 seconds.").grid(column=3, row=11, sticky=(W, E), columnspan=4) 279 | ttk.Label(helpframe, text="m,.\tMove Time2 left and right by 0.10 seconds.").grid(column=3, row=12, sticky=(W, E), columnspan=4) 280 | ttk.Label(helpframe, text="1,0,2,-\tSwitch between action 1 (mute), 0 (cut), 2 (cut audio, speed up video), and - (disable).").grid(column=3, row=13, sticky=(W, E), columnspan=4) 281 | ttk.Label(helpframe, text="t\tTransfer edits to edl structure.").grid(column=3, row=14, sticky=(W, E), columnspan=4) 282 | ttk.Label(helpframe, text="w\tWrite edits to edl file.").grid(column=3, row=15, sticky=(W, E), columnspan=4) 283 | ttk.Label(helpframe, text="r\tRecompile edits and display video.").grid(column=3, row=16, sticky=(W, E), columnspan=4) 284 | ttk.Label(helpframe, text="u,p\tRewind or pause the video.").grid(column=3, row=17, sticky=(W, E), columnspan=4) 285 | ttk.Label(helpframe, text="o\tPlay the source video file without changes.").grid(column=3, row=18, sticky=(W, E), columnspan=4) 286 | 287 | ttk.Button(mainframe, text="Keyboard Controls", command=show_help).grid(column=0, row=5) 288 | #stdscr.addstr(5, 30, "Reloading edit...") 289 | #stdscr.refresh() 290 | 291 | #stdscr.addstr(0,2,"EDL Kit: Tweaker") 292 | #stdscr.addstr(8,2,"Keyboard Controls:") 293 | #stdscr.addstr(9,2,"s,f Move Time1 left and right by 0.01 seconds.") 294 | #stdscr.addstr(10,2,"z,c Move Time1 left and right by 0.10 seconds.") 295 | #stdscr.addstr(11,2,"j,l Move Time2 left and right by 0.01 seconds.") 296 | #stdscr.addstr(12,2,"m,. Move Time2 left and right by 0.10 seconds.") 297 | #stdscr.addstr(13,2,"1,0,- Switch between action 1 (mute), 0 (cut),") 298 | #stdscr.addstr(14,2," and - (disable).") 299 | #stdscr.addstr(15,2,"[,] Move up and down edl file entries.") 300 | #stdscr.addstr(16,2," You can also use the arrow keys.") 301 | #stdscr.addstr(17,2,"t Transfer edits to edl structure.") 302 | #stdscr.addstr(18,2,"w Write edits to edl file.") 303 | #stdscr.addstr(19,2,"r Recompile edits and display video.") 304 | #stdscr.addstr(20,2,"u,p Rewind or pause the video.") 305 | #stdscr.addstr(21,2,"o Play the source video file without changes.") 306 | #stdscr.addstr(22,2,"q Quit") 307 | 308 | #stdscr.refresh() 309 | 310 | 311 | 312 | 313 | def show_struct(): 314 | linenum = 0 315 | struct.delete(*struct.get_children()) 316 | while linenum <= len(estruct.edits)-1: 317 | #stdscr.addstr(linenum+2, 58, " ") 318 | #if linenum == editline: 319 | # stdscr.addstr(linenum+2, 68, "= "+estruct.time1[linenum]+" "+estruct.time2[linenum]+" "+estruct.action[linenum]+" =") 320 | struct.insert('', 'end', text=str(linenum+1), values=(estruct.edits[linenum].time1, estruct.edits[linenum].time2, estruct.edits[linenum].action)) 321 | #else: 322 | # stdscr.addstr(linenum+2, 70, estruct.time1[linenum]+" "+estruct.time2[linenum]+" "+estruct.action[linenum]) 323 | 324 | linenum = linenum + 1 325 | 326 | global num1 327 | num1 = float(t1value.get()) 328 | #num1 = float(estruct.time1[editline]) 329 | global num2 330 | num2 = float(t2value.get()) 331 | #num2 = float(estruct.time2[editline]) 332 | global action2 333 | action2 = actionvalue.get() 334 | #action2 = str(estruct.action[editline]) 335 | #stdscr.addstr(2, 20, "Time1: "+str(num1)+" ") 336 | #stdscr.addstr(2, 40, "Time2: "+str(num2)+" ") 337 | 338 | #if action2 == "1": 339 | # stdscr.addstr(5, 30, "Set to mute mode.") 340 | #elif action2 == "0": 341 | # stdscr.addstr(5, 30, "Set to cut mode. ") 342 | #else: 343 | # stdscr.addstr(5, 30, "Set to disabled. ") 344 | 345 | #show_struct() 346 | 347 | 348 | def update_struct(): 349 | struct.set(struct.selection(), column="Time 1", value=t1value.get()) 350 | struct.set(struct.selection(), column="Time 2", value=t2value.get()) 351 | if actionvalue.get() == "Mute": 352 | struct.set(struct.selection(), column="Action", value="1") 353 | if actionvalue.get() == "Cut": 354 | struct.set(struct.selection(), column="Action", value="0") 355 | if actionvalue.get() == "None": 356 | struct.set(struct.selection(), column="Action", value="-") 357 | if actionvalue.get() == "Cut audio, Speed up video": 358 | struct.set(struct.selection(), column="Action", value="2") 359 | 360 | 361 | def keyCommand(e): 362 | key = e.char 363 | #print(key) 364 | global num1 365 | global num2 366 | global action2 367 | if key == 's': 368 | #global num1 369 | num1 = num1-0.01 370 | t1value.set("{0:.2f}".format(num1)) 371 | elif key == 'z': 372 | #global num1 373 | num1 = num1-0.10 374 | t1value.set("{0:.2f}".format(num1)) 375 | elif key == 'f': 376 | #global num1 377 | num1 = num1+0.01 378 | t1value.set("{0:.2f}".format(num1)) 379 | elif key == 'c': 380 | #global num1 381 | num1 = num1+0.10 382 | t1value.set("{0:.2f}".format(num1)) 383 | elif key == 'j': 384 | #global num2 385 | num2 = num2-0.01 386 | t2value.set("{0:.2f}".format(num2)) 387 | elif key == 'm': 388 | #global num2 389 | if playing_original: 390 | time1 = float(int(getTime())) 391 | estruct.add(time1-1, time1-0.5, 1) 392 | show_struct() 393 | elif redo_seconds != 0: 394 | time1 = float(int(getTime())) 395 | num1 = time1 396 | t1value.set("{0:.2f}".format(num1)) 397 | num2 = time1+0.5 398 | t2value.set("{0:.2f}".format(num2)) 399 | else: 400 | num2 = num2-0.10 401 | t2value.set("{0:.2f}".format(num2)) 402 | elif int(e.keycode) == 119: 403 | estruct.edits.remove([x for x in estruct.edits if float(x.time1) == float(num1)][0]) 404 | show_struct() 405 | elif key == 'l': 406 | #global num2 407 | num2 = num2+0.01 408 | t2value.set("{0:.2f}".format(num2)) 409 | elif key == '.': 410 | #global num20 411 | num2 = num2+0.10 412 | t2value.set("{0:.2f}".format(num2)) 413 | elif key == '1': 414 | actionvalue.set("Mute") 415 | #global action2 416 | action2 = 1 417 | elif key == '0': 418 | actionvalue.set("Cut") 419 | #global action2 420 | action2 = 0 421 | elif key == '2': 422 | actionvalue.set("Cut audio, Speed up video") 423 | action2 = 2 424 | elif key == '-': 425 | actionvalue.set("None") 426 | #global action2 427 | action2 = "-" 428 | elif key == 'r': 429 | statusvalue.set("Reloading Edit...") 430 | root.update() 431 | if num1-3 <= 0: 432 | num1before = 0 433 | else: 434 | num1before = num1-3 435 | 436 | if num2+3 >= VideoFileClip(videofile).duration: 437 | num2after = VideoFileClip(videofile).duration-3 438 | else: 439 | num2after = num2+3 440 | 441 | createClip(num1before, num2after, num1, num2, action2) 442 | statusvalue.set("Ready") 443 | #if str(action2) == "1": 444 | # stdscr.addstr(5, 30, "Set to mute mode.") 445 | #elif str(action2) == "0": 446 | # stdscr.addstr(5, 30, "Set to cut mode. ") 447 | #else: 448 | # stdscr.addstr(5, 30, "Set to disabled. ") 449 | #stdscr.refresh() 450 | elif key == 'u': 451 | rewind() 452 | elif key == 'p': 453 | play_pause() 454 | elif key == 'o': 455 | load_original() 456 | elif key == 'y': 457 | if int(float(t1value.get()))-6 <= 0: 458 | redo_mark(0) 459 | else: 460 | redo_mark(int(float(t1value.get()))-6) 461 | elif key == '[': 462 | if editline != 0: 463 | editline = editline-1 464 | show_struct() 465 | elif key == ']': 466 | if editline != len(estruct.edits)-1: 467 | editline = editline+1 468 | show_struct() 469 | # elif key == curses.KEY_UP: 470 | # if editline != 0: 471 | # editline = editline-1 472 | # show_struct() 473 | # elif key == curses.KEY_DOWN: 474 | # if editline != len(estruct.time1)-1: 475 | # editline = editline+1 476 | # show_struct() 477 | elif key == 't': 478 | item = struct.selection()[0] 479 | linenum = int(struct.item(item, "text"))-1 480 | estruct.edits[linenum].time1 = str("{0:.2f}".format(num1)) 481 | estruct.edits[linenum].time2 = str("{0:.2f}".format(num2)) 482 | estruct.edits[linenum].action = str(action2) 483 | update_struct() 484 | elif key == 'w': 485 | estruct.save() 486 | statusvalue.set("Saved changes.") 487 | elif key == 'q': 488 | exit() 489 | 490 | struct.bind('<>', updateDisplay) 491 | edlfilelabel.bind('', edlfileset) 492 | videofilelabel.bind('', videofileset) 493 | root.bind('', keyCommand) 494 | 495 | if len(sys.argv) > 1: 496 | videofile = sys.argv[1] 497 | edlfile = sys.argv[2] 498 | estruct = edl.EDL(edlfile) 499 | editline = 0 500 | videofilevalue.set(videofile.split('/')[-1]) 501 | edlfilevalue.set(edlfile.split('/')[-1]) 502 | show_struct() 503 | 504 | for child in mainframe.winfo_children(): child.grid_configure(padx=5, pady=5) 505 | 506 | root.mainloop() 507 | 508 | #stop() 509 | #curses.endwin() 510 | -------------------------------------------------------------------------------- /edltweak_gui_mpv.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | 3 | import os, sys, edl, mpv, time 4 | from moviepy.editor import * 5 | 6 | from tkinter import * 7 | from tkinter import ttk 8 | from tkinter import filedialog 9 | from pprint import pprint 10 | 11 | num1 = 0 12 | num2 = 0 13 | action2 = 0 14 | 15 | playing_original = False 16 | redo_seconds = 0 17 | 18 | # Initialize the GUI window 19 | root = Tk() 20 | root.title("EDL Kit: Tweaker") 21 | root.columnconfigure(0, weight=1) 22 | root.rowconfigure(0, weight=1) 23 | 24 | def updateDisplay(e): 25 | # Called when the edit selection, edit times, or edit type is changed. Updates the relevant GUI elements. 26 | global num1 27 | global num2 28 | global action2 29 | item = struct.selection()[0] 30 | t1value.set(float(estruct.edits[int(struct.item(item, "text"))-1].time1)) 31 | num1 = float(t1value.get()) 32 | t2value.set(float(estruct.edits[int(struct.item(item, "text"))-1].time2)) 33 | num2 = float(t2value.get()) 34 | if estruct.edits[int(struct.item(item, "text"))-1].action == "1": 35 | actionvalue.set("Mute") 36 | action2 = 1 37 | elif estruct.edits[int(struct.item(item, "text"))-1].action == "0": 38 | actionvalue.set("Cut") 39 | action2 = 0 40 | elif estruct.edits[int(struct.item(item, "text"))-1].action == "-": 41 | actionvalue.set("None") 42 | action2 = "-" 43 | elif estruct.edits[int(struct.item(item, "text"))-1].action == "2": 44 | actionvalue.set("Cut audio, Speed up video") 45 | action2 = 2 46 | else: 47 | actionvalue.set("Unknown") 48 | action2 = "-" 49 | 50 | # Set spacing options for the main frame in the GUI. 51 | mainframe = ttk.Frame(root, padding=(3, 3, 12, 12)) 52 | mainframe.grid(sticky='nwse') 53 | for column in range(3): 54 | mainframe.columnconfigure(column, weight=1) 55 | mainframe.rowconfigure(1, weight=1) 56 | 57 | # Create an mpv player object and activate the OSD and keyboard/mouse controls. 58 | player = mpv.MPV('osc', 'osd-bar', input_default_bindings=True, input_vo_keyboard=True) 59 | player.keep_open = True 60 | 61 | def rewind(): 62 | # "Rewind" the video to either the beginning, or a specified time. 63 | if redo_seconds == 0: 64 | player.play('/tmp/tweak.mp4') 65 | player.pause = False 66 | else: 67 | player.time_pos = redo_seconds 68 | player.pause = False 69 | 70 | def play_pause(): 71 | # Toggle play/pause state. 72 | if player.pause: 73 | player.pause = False 74 | else: 75 | player.pause = True 76 | 77 | def stop(): 78 | player.quit() 79 | 80 | def reload(): 81 | # Reload the video to see the most recent changes. 82 | player.play('/tmp/tweak.mp4') 83 | player.pause = False 84 | global playing_original 85 | global redo_seconds 86 | playing_original = False 87 | redo_seconds = 0 88 | 89 | def load_original(): 90 | # Play the original video file. 91 | player.play(videofile) 92 | player.pause = False 93 | global playing_original 94 | global redo_seconds 95 | playing_original = True 96 | redo_seconds = 0 97 | 98 | def redo_mark(seconds): 99 | # Play the original video file starting from a specified time 100 | # so an edit can be adjusted while watching the original. 101 | player.play(videofile) 102 | global redo_seconds 103 | redo_seconds = seconds 104 | player.time_pos = seconds 105 | player.pause = False 106 | 107 | def getTime(): 108 | # Get the current video time in seconds. 109 | return player.time_pos 110 | 111 | 112 | videofile = "" 113 | edlfile = "" 114 | estruct = "" 115 | editline = 0 116 | 117 | 118 | def videofileset(e): 119 | # Handle setting a video file when the option is clicked in the GUI. 120 | global videofile 121 | videofile = filedialog.askopenfilename() 122 | if videofile != "": 123 | videofilevalue.set(videofile.split('/')[-1]) 124 | 125 | if edlfile != "": 126 | show_struct() 127 | 128 | def edlfileset(e): 129 | # Handle setting an EDL file when the option is clicked in the GUI. 130 | global edlfile 131 | global estruct 132 | edlfile = filedialog.askopenfilename() 133 | if edlfile != "": 134 | edlfilevalue.set(edlfile.split('/')[-1]) 135 | estruct = edl.EDL(edlfile) 136 | 137 | if videofile != "": 138 | show_struct() 139 | 140 | # Create and set up a frame for the edit time intervals to be displayed. 141 | intervalLabelFrame = ttk.LabelFrame(mainframe, padding=5, text="Time Intervals") 142 | intervalLabelFrame.grid(padx=5, pady=5, ipady=2, sticky=(W,N), row=0, column=0) 143 | ttk.Label(intervalLabelFrame, text="Time 1: ").grid(column=0, row=0, sticky=(E)) 144 | t1value = StringVar() 145 | t1value.set("0.00") 146 | t1label = ttk.Label(intervalLabelFrame, textvariable=t1value, width=12).grid(column=1, row=0, sticky=(W)) 147 | ttk.Label(intervalLabelFrame, text="Time 2: ").grid(column=2, row=0, sticky=(E)) 148 | t2value = StringVar() 149 | t2value.set("0.00") 150 | t2label = ttk.Label(intervalLabelFrame, textvariable=t2value, width=8).grid(column=3, row=0, sticky=(W)) 151 | 152 | # Create and set up a frame for the edit action to be displayed. 153 | actionLabelFrame = ttk.LabelFrame(mainframe, padding=5, text="Action") 154 | actionLabelFrame.grid(padx=5, pady=5, ipady=2, sticky=(W,N), row=1, column=0) 155 | actionvalue = StringVar() 156 | actionvalue.set("None") 157 | actionlabel = ttk.Label(actionLabelFrame, textvariable=actionvalue, width=30).grid(column=0, row=0, sticky=(E), padx=10) 158 | 159 | # Create an set up a frame for the program status to be displayed. 160 | statusLabelFrame = ttk.LabelFrame(root, padding=5, text="Status") 161 | #statusLabelFrame.grid(padx=5, pady=5, ipady=2, sticky=(W), row=2, column=0) 162 | statusLabelFrame.place(x=145, y=145, anchor='c') 163 | statusvalue = StringVar() 164 | statusvalue.set("Ready") 165 | statuslabel = ttk.Label(statusLabelFrame, textvariable=statusvalue, width=30).grid(column=0, row=0, sticky=(E), padx=10) 166 | 167 | # Create and set up a frame for the video/EDL filenames to be selected/displayed. 168 | filesLabelFrame = ttk.LabelFrame(root, padding=5, text="Files") 169 | #filesLabelFrame.grid(padx=5, pady=5, sticky=(W,N), row=3, column=0) 170 | filesLabelFrame.place(x=145, y=205, anchor='c') 171 | videofilevalue = StringVar() 172 | videofilelabel = ttk.Label(filesLabelFrame, textvariable=videofilevalue, width=30) 173 | videofilelabel.grid(column=0, row=0, sticky=(W, E), ipadx=10) 174 | videofilevalue.set("") 175 | 176 | edlfilevalue = StringVar() 177 | edlfilelabel = ttk.Label(filesLabelFrame, textvariable=edlfilevalue, width=30) 178 | edlfilelabel.grid(column=0, row=1, sticky=(W, E, N), ipadx=10) 179 | edlfilevalue.set("") 180 | 181 | 182 | # Create and set up a list element for the edits to be displayed in for selection. 183 | struct = ttk.Treeview(mainframe, columns=("Time 1", "Time 2", "Action"), height=20) 184 | struct['show'] = 'headings' 185 | struct.grid(column=1, row=0, rowspan=7) 186 | struct.heading("Time 1", text="Time 1", anchor='center') 187 | struct.column("Time 1", width=70, anchor='center') 188 | struct.heading("Time 2", text="Time 2", anchor='center') 189 | struct.column("Time 2", width=70, anchor='center') 190 | struct.heading("Action", text="Action", anchor='center') 191 | struct.column("Action", width=60, anchor='center') 192 | s = ttk.Scrollbar(struct, orient="vertical", command=struct.yview) 193 | struct.configure(yscrollcommand=s.set) 194 | 195 | 196 | def createClip(start, end, time1, time2, action): 197 | # Create a clip of the original video from time to time with applied to the to time sub-range. 198 | v = VideoFileClip(videofile) 199 | clip1 = v.subclip(start,time1) 200 | 201 | if str(action) == "1": 202 | if time2 > v.duration: 203 | end = v.duration 204 | time2 = end 205 | 206 | clip2 = VideoFileClip(videofile, audio = False).subclip(time1,time2) 207 | clip3 = v.subclip(time2,end) 208 | clips = concatenate([clip1,clip2,clip3]) 209 | elif str(action) == "0": 210 | if time2 > v.duration: 211 | end = v.duration 212 | time2 = end 213 | 214 | clip3 = v.subclip(time2,end) 215 | clips = concatenate([clip1,clip3]) 216 | elif str(action) == "2": 217 | if time2 > v.duration: 218 | end = v.duration 219 | time2 = end 220 | 221 | s1 = v.subclip(time1,time2).without_audio() 222 | s2 = v.subclip(time2, (time2 + s1.duration)) 223 | clip2 = concatenate([s1,s2.without_audio()]).speedx(final_duration=s1.duration).set_audio(s2.audio) 224 | clip3 = v.subclip((time2 + s1.duration),end) 225 | clips = concatenate([clip1,clip2,clip3]) 226 | else: 227 | if time2 > v.duration: 228 | end = v.duration 229 | time2 = end 230 | 231 | clips = v.subclip(start,end) 232 | 233 | print("Creating clip with modified center from "+str(time1)+" to "+str(time2)) 234 | clips.write_videofile("/tmp/tweak.mp4", codec="libx264", fps=24, preset="ultrafast", threads=4) 235 | reload() 236 | 237 | 238 | def show_help(): 239 | # Display a help menu with keyboard controls when the "Keyboard Controls" button is pressed. 240 | helproot = Toplevel(root) 241 | helpframe = ttk.Frame(helproot, padding="3 3 12 12") 242 | helpframe.grid(column=0, row=0, sticky=(N, W, E, S)) 243 | helpframe.columnconfigure(0, weight=1) 244 | helpframe.rowconfigure(0, weight=1) 245 | ttk.Label(helpframe, text="Keyboard Controls:").grid(column=3, row=8, sticky=(W, E), columnspan=4) 246 | ttk.Label(helpframe, text="s,f\tMove Time1 left and right by 0.01 seconds.").grid(column=3, row=9, sticky=(W, E), columnspan=4) 247 | ttk.Label(helpframe, text="z,c\tMove Time1 left and right by 0.10 seconds.").grid(column=3, row=10, sticky=(W, E), columnspan=4) 248 | ttk.Label(helpframe, text="j,l\tMove Time2 left and right by 0.01 seconds.").grid(column=3, row=11, sticky=(W, E), columnspan=4) 249 | ttk.Label(helpframe, text="m,.\tMove Time2 left and right by 0.10 seconds.").grid(column=3, row=12, sticky=(W, E), columnspan=4) 250 | ttk.Label(helpframe, text="1,0,2,-\tSwitch between action 1 (mute), 0 (cut), 2 (cut audio, speed up video), and - (disable).").grid(column=3, row=13, sticky=(W, E), columnspan=4) 251 | ttk.Label(helpframe, text="t\tTransfer edits to edl structure.").grid(column=3, row=14, sticky=(W, E), columnspan=4) 252 | ttk.Label(helpframe, text="w\tWrite edits to edl file.").grid(column=3, row=15, sticky=(W, E), columnspan=4) 253 | ttk.Label(helpframe, text="r\tRecompile edits and display video.").grid(column=3, row=16, sticky=(W, E), columnspan=4) 254 | ttk.Label(helpframe, text="u,p\tRewind or pause the video.").grid(column=3, row=17, sticky=(W, E), columnspan=4) 255 | ttk.Label(helpframe, text="o\tPlay the source video file without changes.").grid(column=3, row=18, sticky=(W, E), columnspan=4) 256 | 257 | ttk.Button(mainframe, text="Keyboard Controls", command=show_help).grid(column=0, row=5) 258 | 259 | 260 | def show_struct(): 261 | # Display the EDL structure in its element on the GUI. 262 | linenum = 0 263 | struct.delete(*struct.get_children()) 264 | while linenum <= len(estruct.edits)-1: 265 | struct.insert('', 'end', text=str(linenum+1), values=(estruct.edits[linenum].time1, estruct.edits[linenum].time2, estruct.edits[linenum].action)) 266 | linenum = linenum + 1 267 | 268 | global num1 269 | num1 = float(t1value.get()) 270 | global num2 271 | num2 = float(t2value.get()) 272 | global action2 273 | action2 = actionvalue.get() 274 | 275 | def update_struct(): 276 | # Called when the EDL structure changes. Updates the relevant GUI elements. 277 | struct.set(struct.selection(), column="Time 1", value=t1value.get()) 278 | struct.set(struct.selection(), column="Time 2", value=t2value.get()) 279 | if actionvalue.get() == "Mute": 280 | struct.set(struct.selection(), column="Action", value="1") 281 | if actionvalue.get() == "Cut": 282 | struct.set(struct.selection(), column="Action", value="0") 283 | if actionvalue.get() == "None": 284 | struct.set(struct.selection(), column="Action", value="-") 285 | if actionvalue.get() == "Cut audio, Speed up video": 286 | struct.set(struct.selection(), column="Action", value="2") 287 | 288 | def keyCommand(e): 289 | # Handles the keyboard controls. 290 | key = e.char 291 | global num1 292 | global num2 293 | global action2 294 | if key == 's': 295 | # Subtract 0.01 seconds from Time1 296 | num1 = num1-0.01 297 | t1value.set("{0:.2f}".format(num1)) 298 | elif key == 'z': 299 | # Subtract 0.1 seconds from Time1 300 | num1 = num1-0.10 301 | t1value.set("{0:.2f}".format(num1)) 302 | elif key == 'f': 303 | # Add 0.01 seconds to Time1 304 | num1 = num1+0.01 305 | t1value.set("{0:.2f}".format(num1)) 306 | elif key == 'c': 307 | # Add 0.1 seconds to Time1 308 | num1 = num1+0.10 309 | t1value.set("{0:.2f}".format(num1)) 310 | elif key == 'j': 311 | # Subtract 0.01 seconds from Time2 312 | num2 = num2-0.01 313 | t2value.set("{0:.2f}".format(num2)) 314 | elif key == 'm': 315 | if playing_original: 316 | # If the original video is playing, mark an edit point at the current time. 317 | time1 = float(int(getTime())) 318 | estruct.add(time1-1, time1-0.5, 1) 319 | show_struct() 320 | elif redo_seconds != 0: 321 | # If we're redoing an edit, move the edit point to the current time. 322 | time1 = float(int(getTime())) 323 | num1 = time1 324 | t1value.set("{0:.2f}".format(num1)) 325 | num2 = time1+0.5 326 | t2value.set("{0:.2f}".format(num2)) 327 | else: 328 | # If no video is playing, then we're modifying an edit point normally, and will subtract 0.1 seconds from Time2 329 | num2 = num2-0.10 330 | t2value.set("{0:.2f}".format(num2)) 331 | elif int(e.keycode) == 119: 332 | # ("Delete" key) delete an edit point. 333 | # Remove it from the edit structure... 334 | estruct.edits.remove([x for x in estruct.edits if float(x.time1) == float(num1)][0]) 335 | # ...and re-display the structure in the GUI 336 | show_struct() 337 | elif key == 'l': 338 | # Add 0.01 seconds to Time2 339 | num2 = num2+0.01 340 | t2value.set("{0:.2f}".format(num2)) 341 | elif key == '.': 342 | # Add 0.1 seconds to Time2 343 | num2 = num2+0.10 344 | t2value.set("{0:.2f}".format(num2)) 345 | elif key == '1': 346 | # Set action to "Mute" 347 | actionvalue.set("Mute") 348 | action2 = 1 349 | elif key == '0': 350 | # Set action to "Cut" 351 | actionvalue.set("Cut") 352 | action2 = 0 353 | elif key == '2': 354 | # Set action to "Cut Audio, Speed up video" (speeds up video segment to hide a cut). 355 | actionvalue.set("Cut audio, Speed up video") 356 | action2 = 2 357 | elif key == '-': 358 | # Set action to "None". Used for when you need to see/hear the original segment during editing. 359 | actionvalue.set("None") 360 | action2 = "-" 361 | elif key == 'r': 362 | # Re-generate the edited segment and display it. 363 | statusvalue.set("Reloading Edit...") 364 | root.update() 365 | if num1-3 <= 0: 366 | num1before = 0 367 | else: 368 | num1before = num1-3 369 | 370 | if num2+3 >= VideoFileClip(videofile).duration: 371 | num2after = VideoFileClip(videofile).duration-3 372 | else: 373 | num2after = num2+3 374 | 375 | createClip(num1before, num2after, num1, num2, action2) 376 | statusvalue.set("Ready") 377 | elif key == 'u': 378 | # "Rewind" the current video/segment to the beginning. 379 | rewind() 380 | elif key == 'p': 381 | # Toggle pause/play 382 | play_pause() 383 | elif key == 'o': 384 | # Play the original video 385 | load_original() 386 | elif key == 'y': 387 | # Play the original video starting 6 seconds before the current edit point so the edit can be re-marked with "m". 388 | if int(float(t1value.get()))-6 <= 0: 389 | redo_mark(0) 390 | else: 391 | redo_mark(int(float(t1value.get()))-6) 392 | elif key == 't': 393 | # Transfer the modified edit times from the Time Intervals box to the EDL structure, and update the EDL list GUI element. 394 | item = struct.selection()[0] 395 | linenum = int(struct.item(item, "text"))-1 396 | estruct.edits[linenum].time1 = str("{0:.2f}".format(num1)) 397 | estruct.edits[linenum].time2 = str("{0:.2f}".format(num2)) 398 | estruct.edits[linenum].action = str(action2) 399 | update_struct() 400 | elif key == 'w': 401 | # Save (Write) the changed EDL structure to the EDL file. 402 | estruct.save() 403 | statusvalue.set("Saved changes.") 404 | elif key == 'q': 405 | exit() 406 | 407 | # GUI action bindings. 408 | # Update the times/action on the GUI when an edit point is selected from the "treeview". 409 | struct.bind('<>', updateDisplay) 410 | 411 | # Handle selecting EDL/video files when the place for that on the GUI is clicked. 412 | edlfilelabel.bind('', edlfileset) 413 | videofilelabel.bind('', videofileset) 414 | 415 | # Send keyboard input to the keyCommand function when the window is focused. 416 | root.bind('', keyCommand) 417 | 418 | if len(sys.argv) > 1: 419 | # If run from the cli with video and edl file arguments, go ahead and set those in the gui. 420 | videofile = sys.argv[1] 421 | edlfile = sys.argv[2] 422 | estruct = edl.EDL(edlfile) 423 | editline = 0 424 | videofilevalue.set(videofile.split('/')[-1]) 425 | edlfilevalue.set(edlfile.split('/')[-1]) 426 | player.play(videofile.split('/')[-1]) 427 | player.pause = True 428 | show_struct() 429 | 430 | # Set padding options for the GUI elements. 431 | for child in mainframe.winfo_children(): child.grid_configure(padx=5, pady=5) 432 | 433 | # Start the GUI 434 | root.mainloop() 435 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | moviepy 2 | pymediainfo 3 | 4 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | setup( 3 | name = "EDLKit", 4 | version = "1.0", 5 | description = "Python tools to create, tweak, and apply EDL file edits to video files.", 6 | url = "https://github.com/Red5d/edlkit", 7 | author = "Red5d", 8 | license = "GPLv2", 9 | keywords = "video edl ffmpeg moviepy edit editing", 10 | 11 | py_modules = ['edl', 'Stopwatch'], 12 | install_requires = ['moviepy>=0.2.3.5', 'pymediainfo>=2.3.0'], 13 | scripts = ['edledit.py', 'edlcreate.py', 'edlcreate_timer.py', 'edltweak_gui.py', 'edltweak_gui_mpv.py'], 14 | entry_points={ 15 | 'console_scripts': [ 16 | 'edledit = edledit:main', 17 | 'edlcreate = edlcreate', 18 | 'edlcreate_timer = edlcreate_timer', 19 | ], 20 | 'gui_scripts': [ 21 | 'edltweak_gui = edltweak_gui', 22 | 'edltweak_gui_mpv = edltweak_gui_mpv' 23 | ] 24 | } 25 | ) 26 | 27 | --------------------------------------------------------------------------------