Create a thread for the video editing application

Hello and welcome back to this final part of the video editing project. After this chapter, you will be good enough to continue to develop this application by yourself and hopefully, you can come out with a better idea of how to further develop this video editing application.

In this chapter I have created a thread for this video editing application which will separate the application into two part, the user interface part and the thread part. The user interface part will take care of the user’s click event and the user’s input and the threaded part will process the user input.

There is nothing new in this chapter besides separate the application into two part. The first part is the user interface part.

from tkinter import *
from tkinter import filedialog
import tkinter.ttk as tk
from tkinter import messagebox
from NewVid import NewVid
import webbrowser

win = Tk() # Create tk instance
win.title("NeW Vid") # Add a title
win.resizable(0, 0) # Disable resizing the GUI
win.configure(background='white') # change background color

mainframe = Frame(win) # create a frame
mainframe.pack()

eqFrame = Frame(win) # create eq frame
eqFrame.pack(side = TOP, fill=X)

animatedFrame = Frame(win) # create animated frame
animatedFrame.pack(side = TOP, fill=X)

trimFrame = Frame(win) # create trim frame
trimFrame.pack(side = TOP, fill=X)

buttonFrame = Frame(win) # create a button frame
buttonFrame.pack(side = BOTTOM, fill=X, pady = 6)

# Create a label and scale box for eq
contrast_variable = DoubleVar()
contrast = Scale(eqFrame, from_=float(-2.00), to=float(2.00), orient=HORIZONTAL, label="CONTRAST", digits=3, resolution=0.01, variable=contrast_variable)
contrast.set(1)
contrast.pack(side = LEFT)
brightness_variable = DoubleVar()
brightness = Scale(eqFrame, from_=float(-1.00), to=float(1.00), orient=HORIZONTAL, label="BRIGHTNESS", digits=3, resolution=0.01, variable=brightness_variable)
brightness.pack(side = LEFT)
saturation_variable = DoubleVar()
saturation = Scale(eqFrame, from_=float(0.00), to=float(3.00), orient=HORIZONTAL, label="SATURATION", digits=3, resolution=0.01, variable=saturation_variable)
saturation.set(1)
saturation.pack(side = LEFT)
gamma_variable = DoubleVar()
gamma = Scale(eqFrame, from_=float(0.10), to=float(10.00), orient=HORIZONTAL, label="GAMMA", digits=4, resolution=0.01, variable=gamma_variable)
gamma.set(1)
gamma.pack(side = LEFT)
loop_variable = DoubleVar()
loop = Scale(eqFrame, from_=float(0), to=float(10), orient=HORIZONTAL, label="REPEAT", digits=2, resolution=1, variable=loop_variable)
loop.pack(side = LEFT)
fr_variable = DoubleVar()
fr = Scale(eqFrame, from_=float(9), to=float(60), orient=HORIZONTAL, label="FPS", digits=2, resolution=1, variable=fr_variable)
fr.set(24)
fr.pack(side = LEFT)

#create animated gif
anime = Label(animatedFrame, text="Create Animated Image from Video   ")
anime.pack(side = TOP)
anime.pack(side = LEFT)

from_ = Label(animatedFrame, text="Start From (hour : minute : second)  ")
from_.pack(side = BOTTOM)
from_.pack(side = LEFT)
from_t_h_varable = StringVar()
from_t_h = Entry(animatedFrame, width=3, textvariable=from_t_h_varable)
from_t_h.pack(side=BOTTOM)
from_t_h.pack(side=LEFT)
from_m = Label(animatedFrame, text=" : ")
from_m.pack(side = BOTTOM)
from_m.pack(side = LEFT)
from_t_m_varable = StringVar()
from_t_m = Entry(animatedFrame, width=3,textvariable=from_t_m_varable)
from_t_m.pack(side=BOTTOM)
from_t_m.pack(side=LEFT)
from_s = Label(animatedFrame, text=" : ")
from_s.pack(side = BOTTOM)
from_s.pack(side = LEFT)
from_t_s_varable = StringVar()
from_t_s = Entry(animatedFrame, width=3,textvariable=from_t_s_varable)
from_t_s.pack(side=BOTTOM)
from_t_s.pack(side=LEFT)

to_ = Label(animatedFrame, text="  To (in second)  ")
to_.pack(side = BOTTOM)
to_.pack(side = LEFT)
to_t_s_varable = StringVar()
to_t_s = Entry(animatedFrame, width=3,textvariable=to_t_s_varable)
to_t_s.pack(side=BOTTOM)
to_t_s.pack(side=LEFT)


#trim video
trim = Label(trimFrame, text="Trim Video   ")
trim.pack(side = TOP)
trim.pack(side = LEFT)

trim_from_ = Label(trimFrame, text="Start From (hour : minute : second)  ")
trim_from_.pack(side = BOTTOM)
trim_from_.pack(side = LEFT)
trim_from_t_h_varable = StringVar()
trim_from_t_h = Entry(trimFrame, width=3, textvariable=trim_from_t_h_varable)
trim_from_t_h.pack(side=BOTTOM)
trim_from_t_h.pack(side=LEFT)
trim_from_m = Label(trimFrame, text=" : ")
trim_from_m.pack(side = BOTTOM)
trim_from_m.pack(side = LEFT)
trim_from_t_m_varable = StringVar()
trim_from_t_m = Entry(trimFrame, width=3,textvariable=trim_from_t_m_varable)
trim_from_t_m.pack(side=BOTTOM)
trim_from_t_m.pack(side=LEFT)
trim_from_s = Label(trimFrame, text=" : ")
trim_from_s.pack(side = BOTTOM)
trim_from_s.pack(side = LEFT)
trim_from_t_s_varable = StringVar()
trim_from_t_s = Entry(trimFrame, width=3,textvariable=trim_from_t_s_varable)
trim_from_t_s.pack(side=BOTTOM)
trim_from_t_s.pack(side=LEFT)

trim_to_ = Label(trimFrame, text="  To (in second)  ")
trim_to_.pack(side = BOTTOM)
trim_to_.pack(side = LEFT)
trim_to_t_h_varable = StringVar()
trim_to_t_h = Entry(trimFrame, width=3,textvariable=trim_to_t_h_varable)
trim_to_t_h.pack(side=BOTTOM)
trim_to_t_h.pack(side=LEFT)
trim_to_m = Label(trimFrame, text=" : ")
trim_to_m.pack(side = BOTTOM)
trim_to_m.pack(side = LEFT)
trim_to_t_m_varable = StringVar()
trim_to_t_m = Entry(trimFrame, width=3,textvariable=trim_to_t_m_varable)
trim_to_t_m.pack(side=BOTTOM)
trim_to_t_m.pack(side=LEFT)
trim_to_s = Label(trimFrame, text=" : ")
trim_to_s.pack(side = BOTTOM)
trim_to_s.pack(side = LEFT)
trim_to_t_s_varable = StringVar()
trim_to_t_s = Entry(trimFrame, width=3,textvariable=trim_to_t_s_varable)
trim_to_t_s.pack(side=BOTTOM)
trim_to_t_s.pack(side=LEFT)

# Create a combo box
vid_size = StringVar() # create a string variable
preferSize = tk.Combobox(mainframe, textvariable=vid_size) 
preferSize['values'] = (1920, 1280, 854, 640) # video width in pixels
preferSize.current(0) # select item one 
preferSize.pack(side = LEFT)

# Create a combo box
vid_format = StringVar() # create a string variable
preferFormat = tk.Combobox(mainframe, textvariable=vid_format) 
preferFormat['values'] = ('.mp4', '.webm', '.avi', '.wmv', '.mpg', '.ogv') # video format
preferFormat.current(0) # select item one 
preferFormat.pack(side = LEFT)

removeAudioVal = IntVar()
removeAudio = tk.Checkbutton(mainframe, text="Remove Audio", variable=removeAudioVal)
removeAudio.pack(side = LEFT, padx=3)

newAudio = IntVar()
aNewAudio = tk.Checkbutton(mainframe, text="New Audio", variable=newAudio)
aNewAudio.pack(side = LEFT, padx=2)

count = 0 # counter uses to create multiple videos

btn_text = StringVar() # button text

# Open a video file
def openVideo():
        
        audiofilename = ''
        fullfilename = filedialog.askopenfilename(initialdir="/", title="Select a file", filetypes=[("Video file", "*.mp4; *.avi ")]) # select a video file from the hard drive
        if(newAudio.get() == 1):
                audiofilename = filedialog.askopenfilename(initialdir="/", title="Select a file", filetypes=[("Audio file", "*.wav; *.ogg ")]) # select a new audio file from the hard drive
        global count # access the global count variable
         
        if(fullfilename != ''): 

                scale_vid = preferSize.get() # retrieve value from the comno box
                new_size = str(scale_vid)
                file_extension = fullfilename.split('.')[-1] # extract the video format from the original video
                f = '_new_vid_' + new_size  + '.' + file_extension # the new output file name
                f2 = str(count)+f # second video
                f_gif = str(count) + f + '.gif' # create animated gif
                count += 1 # increase video counter for new video

                # create animated image from video
                animi_from_hour = from_t_h_varable.get()
                animi_from_minute = from_t_m_varable.get()
                animi_from_second = from_t_s_varable.get()
                animi_to_second = to_t_s_varable.get()

                # video editing part start here
                noAudio = removeAudioVal.get() # get the checkbox state for audio 

                # trim video starting point and end point
                trim_from_hour = trim_from_t_h_varable.get()
                trim_from_minute = trim_from_t_m_varable.get()
                trim_from_second = trim_from_t_s_varable.get()

                trim_to_hour = trim_to_t_h_varable.get()
                trim_to_minute = trim_to_t_m_varable.get()
                trim_to_second = trim_to_t_s_varable.get()

                f3 = f + vid_format.get() # The final video format

                contrast_value = str(contrast_variable.get()) 
                brightness_value = str(brightness_variable.get()) 
                saturation_value = str(saturation_variable.get())
                gamma_value =  str(gamma_variable.get())
                loop_value = str(loop_variable.get())
                frame_rate_value = str(fr_variable.get())

                try:
                        new_vide_thread = NewVid(contrast_value, brightness_value, saturation_value, gamma_value, loop_value, frame_rate_value, new_size, noAudio, file_extension, f, f2, f3, f_gif, audiofilename, fullfilename, animi_from_hour, animi_from_minute, animi_from_second, animi_to_second, trim_from_hour, trim_from_minute, trim_from_second, trim_to_hour, trim_to_minute, trim_to_second)
                        new_vide_thread.start()
                        new_vide_thread.join()
                except:
                        messagebox.showinfo("Error", "You need to install FFmpeg before using this program!")
                        webbrowser.open('https://www.ffmpeg.org/')

        else:
                messagebox.showinfo("Error", "You need to select a video file!")

action_vid = tk.Button(buttonFrame, command=openVideo, text="Select Video")
action_vid.pack(fill=X)

win.mainloop()

The second part is the thread part,

import os
import subprocess
import threading

class NewVid(threading.Thread):
                
    def __init__(self, contrast_value, brightness_value, saturation_value, gamma_value, loop_value, frame_rate_value, new_size, noAudio, file_extension, f, f2, f3, f_gif, audiofilename, fullfilename, animi_from_hour, animi_from_minute, animi_from_second, animi_to_second, trim_from_hour, trim_from_minute, trim_from_second, trim_to_hour, trim_to_minute, trim_to_second):
        threading.Thread.__init__(self)
        self.contrast_value = contrast_value
        self.brightness_value = brightness_value
        self.saturation_value = saturation_value
        self.gamma_value = gamma_value
        self.loop_value = loop_value
        self.frame_rate_value = frame_rate_value
        self.new_size =new_size
        self.noAudio = noAudio
        self.file_extension = file_extension
        self.f = f
        self.f2 = f2
        self.f3 = f3
        self.f_gif = f_gif
        self.audiofilename = audiofilename
        self.fullfilename = fullfilename
        self.animi_from_hour = animi_from_hour
        self.animi_from_minute = animi_from_hour
        self.animi_from_second = animi_from_second
        self.animi_to_second = animi_to_second
        self.trim_from_hour = trim_from_hour
        self.trim_from_minute = trim_from_minute
        self.trim_from_second = trim_from_second
        self.trim_to_hour = trim_to_hour
        self.trim_to_minute = trim_to_minute
        self.trim_to_second = trim_to_second

    # Processing video file
    def run(self):

        dir_path = os.path.dirname(os.path.realpath(self.fullfilename))

        self.trim_video = False # set the trim video flag to false
        os.chdir(dir_path) # change the directory to the original file's directory

        # if the time areas are not empty and they have a digit then only the animated gif will be created 
        if((self.animi_from_hour != '' and self.animi_from_hour.isdigit()) and (self.animi_from_minute != '' and self.animi_from_minute.isdigit()) and (self.animi_from_second != '' and self.animi_from_second.isdigit()) and (self.animi_to_second != '' and self.animi_to_second.isdigit())):
            subprocess.call(['ffmpeg', '-i', self.fullfilename, '-vf', 'scale=' + self.new_size + ':-1', '-y', self.f]) # resize video
            subprocess.call(['ffmpeg', '-i', self.f, '-vf', 'eq=contrast=' + self.contrast_value +':brightness='+ self.brightness_value +':saturation=' + self.saturation_value +':gamma=' + self.gamma_value, '-y', self.f2]) # adjust the saturation, gamma, contrast and brightness of video
            subprocess.call(['ffmpeg', '-i', self.f2, '-ss', self.animi_from_hour + ':' + self.animi_from_minute + ':' + self.animi_from_second, '-t',  self.animi_to_second, '-y', self.f_gif]) # creating animated gif from starting point to end point
            os.remove(self.f)
            os.remove(self.f2)
            return 0
    
        # video editing part start here
        subprocess.call(['ffmpeg', '-stream_loop', (self.loop_value), '-i', self.fullfilename, '-vf', 'scale=' + self.new_size + ':-1', '-y', '-r', self.frame_rate_value, self.f]) # resize, speedup and loop the video with ffmpeg
        subprocess.call(['ffmpeg', '-i', self.f, '-vf', 'eq=contrast=' + self.contrast_value +':brightness='+ self.brightness_value +':saturation=' + self.saturation_value +':gamma=' + self.gamma_value, '-y', self.f2]) # adjust the saturation, gamma, contrast and brightness of video
       
        # if the time areas are not empty and they have a digit then trim the video 
        if((self.trim_from_hour != '' and self.trim_from_hour.isdigit()) and (self.trim_from_minute != '' and self.trim_from_minute.isdigit()) and (self.trim_from_second != '' and self.trim_from_second.isdigit()) and (self.trim_to_second != '' and self.trim_to_second.isdigit()) and (self.trim_to_minute != '' and self.trim_to_minute.isdigit()) and (self.trim_to_hour != '' and self.trim_to_hour.isdigit())):
            subprocess.call(['ffmpeg', '-i', self.f2, '-ss', self.trim_from_hour + ':' + self.trim_from_minute + ':' + self.trim_from_second, '-t',  self.trim_to_hour + ':' + self.trim_to_minute + ':' + self.trim_to_second, '-y', '-c:v', 'copy', '-c:a', 'copy', self.f]) # trim the video from start to end point
            self.trim_video = True

        if(self.noAudio == 1 and self.trim_video == True):
            subprocess.call(['ffmpeg', '-i', self.f, '-c', 'copy', '-y', '-an', self.f2]) # remove audio from the original video
                        
        elif(self.noAudio == 1 and self.trim_video == False):
            subprocess.call(['ffmpeg', '-i', self.f2, '-c', 'copy', '-y', '-an', self.f]) # remove audio from the original video
               
        if(self.audiofilename != '' and self.noAudio == 1 and self.trim_video == False):
            subprocess.call(['ffmpeg', '-i', self.f, '-i', self.audiofilename, '-shortest', '-c:v', 'copy', '-b:a', '256k', '-y', self.f2]) # add audio to the original video, trim either the audio or video depends on which one is longer
        elif(self.audiofilename != '' and self.noAudio == 1 and self.trim_video == True):
            subprocess.call(['ffmpeg', '-i', self.f2, '-i', self.audiofilename, '-shortest', '-c:v', 'copy',  '-b:a', '256k', '-y', self.f]) # add audio to the original video, trim either the audio or video depends on which one is longer

        if(self.f3.split('.')[-1] != self.f2.split('.')[-1] and self.trim_video == True and self.noAudio == 1 and self.audiofilename != ''):
            subprocess.call(['ffmpeg', '-i', self.f, '-y', self.f3]) # converting the video with ffmpeg
            os.remove(self.f2) # remove two videos
            os.remove(self.f)
        elif(self.f3.split('.')[-1] != self.f2.split('.')[-1] and self.trim_video == False and self.noAudio == 1 and self.audiofilename != ''):
            subprocess.call(['ffmpeg', '-i', self.f2, '-y', self.f3]) # converting the video with ffmpeg
            os.remove(self.f2) # remove two videos
            os.remove(self.f)
        elif(self.f3.split('.')[-1] != self.f2.split('.')[-1] and self.trim_video == False and self.noAudio != 1 and self.audiofilename == ''):
            subprocess.call(['ffmpeg', '-i', self.f2, '-y', self.f3]) # converting the video with ffmpeg
            os.remove(self.f2) # remove two videos
            os.remove(self.f)
        elif(self.f3.split('.')[-1] == self.f2.split('.')[-1] and self.trim_video == True and self.noAudio == 1 and self.audiofilename != ''):
            os.remove(self.f2) # remove one video
        elif(self.f3.split('.')[-1] == self.f2.split('.')[-1] and self.trim_video == True and self.noAudio != 1):
            os.remove(self.f2) # remove one video
        elif(self.f3.split('.')[-1] == self.f2.split('.')[-1] and self.trim_video == False and self.noAudio != 1):
            os.remove(self.f) # remove one video
        elif(self.f3.split('.')[-1] == self.f2.split('.')[-1] and self.trim_video == False and self.noAudio == 1):
            os.remove(self.f2) # remove one video
        else:
            os.remove(self.f) # remove one video

        self.trim_video = False # reset the trim video flag to false

Well, now is your turn to edit this application then use it to edit video. How about creating a progress bar which tells the user how much time he still needs to wait for the software to finish editing the video?

Leave a Reply

avatar
  Subscribe  
Notify of