def __init__(self, filename): self.edl = Edl() # nahraj třídu Edl self.api = Api() # nahraj třídu Api # path to ffmpeg executable for all platforms if sys.platform == 'linux' or sys.platform == 'darwin': self.ffmpeg_exec = 'ffmpeg' elif sys.platform == 'win32': self.ffmpeg_exec = 'ffmpeg.exe' self.tmp_prefix = '_nocut' # přidá se do <filename>_prefix.mkv if os.path.exists(filename): self.filename = filename else: print(_('Requested file: ') + filename + _(' not found!')) sys.exit() self.api.get_info(filename) # get videofile info self.duration = self.api.get_duration() # get duration self.edlname = os.path.splitext(self.filename)[0] + '.edl' self.orig_filename = os.path.splitext(self.filename)[0] +\ self.tmp_prefix + os.path.splitext(self.filename)[1] try: self.edl.import_edl(self.edlname, self.duration) self.edllist = self.edl.imported_edl except Exception as er: print(_('Failed to import an EDL file - error : ') + str(er)) sys.exit() try: proc = subprocess.Popen(self.ffmpeg_exec) proc.communicate() except Exception as er: print(_('Not found ffmpeg program! ') + er.args[1]) sys.exit()
def __init__(self): # třída logiky ovládáni Mplayer api self.api = Api() # třída pro práci s EDL self.edl = Edl() # hl.okno titulek a rozmery self.gui = Tk() self.gui.title(_('Commercial cutter')) # při zavření okna self.gui.protocol("WM_DELETE_WINDOW", self.close) self.gui.bind('<Control-KeyPress-q>', self.close) # styl if sys.platform == 'linux': self.style = ttk.Style() # fix ugly appearance self.style.theme_use('clam') # prvky okna od shora dolů # video plátno self.video = Canvas(self.gui, width=800, height=450) self.video.grid(row=0, column=0, columnspan=9, sticky='wens') self.video.bind('<Double-Button-1>', self.fullscreen) # fullscreen # plátno střihu self.play_before_cut = 3 # počet sekund ukázky přehrání přesk. reklamy self.blink_time = 200 # interval blikání editovaného cutu v ms self.cutting = Canvas(self.gui, width=800, height=22, bg='#D9D9D9', cursor='hand1', highlightthickness=0) self.cutting.grid(row=1, column=0, columnspan=9, sticky='we') self.cutting.bind('<Button-1>', self.edit_cut_changer) # přepinani self.gui.bind('<Shift_R>', self.edit_cut_changer) # přepinani shiftem self.cutting.bind('<Button-3>', self.edl_insert_new) # nový střih self.gui.bind('<Return>', self.edl_insert_new) # --//-- self.gui.bind('<Escape>', self.deselect_cut) # nulovani vyberu self.gui.bind('<Delete>', self.cut_delete) # vymazani cutu v edit mode self.edl_cuts = [] # analogie k self.edl.edl pro ID cisla střihu obd. self.editmode = False # stav edit mod ano/ne při vybrani cutu # plátno progress baru self.pbar = Canvas(self.gui, width=800, height=16, bg='#D9D9D9', cursor='xterm', highlightthickness=0) self.pbar.grid(row=2, column=0, columnspan=9, sticky='we') for event in ['<Configure>', '<Button-1>', '<ButtonRelease-1>', '<B1-Motion>', '<Button-3>', '<ButtonRelease-3>', '<B3-Motion>']: self.pbar.bind(event, self.mouse_set_pos) self.pbar.bind('<Configure>', self.redraw_canvas) self.progress = '' # progressbar - modry obdelnik self.mouse_soft_pos = False # proměnná pro jemný posuv True nebo False self.click_canvas_pos = 0 # posl. souř. kliku pro e.type 6 (tažení) # Message info - stavový řádek self.info = Label( self.gui, height=0, wraplength=600, bg='#FFFCDD', font="Arial 12") self.info.grid(row=5, column=0, columnspan=9, sticky='we') # regulace velikost posuvu vpřed/vzad self.frame = '' # hodnota shiftu o 1 frame self.shift_values = ['0.1 sec. => 0.1', '1 sec. => 1', '5 sec. => 5', '10 sec. => 10', '30 sec. => 30', '1 min. => 60', '5 min. => 300', '10 min. => 600', '30 min. => 1800'] # aktuální hodnota posunu v sekundách self.shift_actual_value = 30 # spinbox v Gui self.shift = Spinbox( values=self.shift_values, cursor='sb_v_double_arrow', readonlybackground='#FFFCDD', command=self.set_shift, state='readonly', font="Arial 12", justify='center', width=16) self.shift.grid(row=3, column=3, columnspan=3, sticky='we') for event in '<Up>', '<Down>': self.gui.bind(event, self.set_shift) for event in '<Button-4>', '<Button-5>': # kolečko myši up/down self.shift.bind(event, self.set_shift) # spid a spis seconds/pixels pro duration seconds/pixels pro shift self.spid = 0 # poměr pix/sec pro api.duration self.spis = self.sec_to_pix_ratio(self.shift_actual_value) for event in '<Enter>', '<Leave>': self.pbar.bind(event, lambda e, help=_('Rewind LM + drag - soft RM + drag '): self.tooltip(e, help)) self.cutting.bind(event, lambda e, help=_('Cutting canvas ,') + _(' next cut mark [Ctrl] + <- ->, insert new [Enter]/RM') + _(' Back Esc/LM/P-Shift'): self.tooltip(e, help)) self.shift.bind(event, lambda e, help=_('Skip shift: more [Up] less [Down] '): self.tooltip(e, help)) # lcdl - levý lcd + lcdr - pravý lcd for lcd in 'lcd_l', 'lcd_r': setattr(self, lcd, Label(self.gui, wraplength=600, bg='#FFFCDD', font='Arial 12', relief='sunken', justify='center')) self.lcd_l.grid(row=3, column=0, columnspan=3, sticky='we') self.lcd_l['text'] = _('Cut not selected') self.lcd_r.grid(row=3, column=6, columnspan=3, sticky='we') self.lcd_r['text'] = _('Video file not open') # ikony pro buttony self.icon = [] # zásobník na ikony for name in ['open.gif', 'import.gif', 'back_cut.gif', 'back.gif', 'play.gif', 'forw.gif', 'forw_cut.gif', 'remix.gif', 'save.gif']: self.icon.append(PhotoImage(file=os.path.join('icons', name))) # tlacitka [<text>, <funkce>, <kl.zkratka>, <text nápovědy(tooltip)>] self.buttons = [ ('open_button', ['open_video', '<Control-KeyPress-o>', _('Open video file [Ctrl+o]')]), ('open_edl_button', ['edl_import', '<Control-KeyPress-i>', _('Import cuts from EDL file [Ctrl+i]')]), ('prev_cut_button', ['prew_cut', '<Control-KeyPress-Left>', _('Previous cut [Ctrl+Left]')]), ('rewind_button', ['rewind', '<Left>', _('Fast rewind [Left]')]), ('play_button', ['play', '<p>', _('Play / pauze or a show of the selected cut [p]')]), ('refind_button', ['refind', '<Right>', _('Fast forward [Right]')]), ('next_cut_button', ['next_cut', '<Control-KeyPress-Right>', _('Next cut [Ctrl+Left]')]), ('remixtep_button', ['remix', '<Control-KeyPress-r>', _('Re multiplex to new file [Ctrl+r]')]), ('save_edl_bbutton', ['save_edl', '<Control-KeyPress-s>', _('Save to EDL file [Ctrl+s]')]) ] # buttony for button in self.buttons: index = self.buttons.index(button) #predava se tlacitko, (text, funkce) setattr(self, button[0], ttk.Button(self.gui, image=self.icon[self.buttons.index(button)], command=getattr(self, button[1][0]))) cudl = getattr(self, button[0]) # cast self.<open_button> #Tool tipy v Labelu info help = button[1][2] for event in '<Enter>', '<Leave>': cudl.bind(event, lambda e, help=help: self.tooltip(e, help)) # + grid cudl.grid(row=4, column=index, sticky='we') #nast. roztahovat druhý řádek self.gui.grid_rowconfigure(0, weight=1) #nast. roztahovat tlačítka 4. řádek/ vsechny sloupce self.gui.grid_columnconfigure(index, weight=1) #Kl. zkratky self.gui.bind_all(button[1][1], getattr(self, button[1][0])) # start Mplayeru v iddle režimu v okně canvasu při otevření aplikace self.wid = str(hex(self.video.winfo_id())) self.api.start(wid=self.wid)
class Gui: '''Gui pro Mcutter''' def __init__(self): # třída logiky ovládáni Mplayer api self.api = Api() # třída pro práci s EDL self.edl = Edl() # hl.okno titulek a rozmery self.gui = Tk() self.gui.title(_('Commercial cutter')) # při zavření okna self.gui.protocol("WM_DELETE_WINDOW", self.close) self.gui.bind('<Control-KeyPress-q>', self.close) # styl if sys.platform == 'linux': self.style = ttk.Style() # fix ugly appearance self.style.theme_use('clam') # prvky okna od shora dolů # video plátno self.video = Canvas(self.gui, width=800, height=450) self.video.grid(row=0, column=0, columnspan=9, sticky='wens') self.video.bind('<Double-Button-1>', self.fullscreen) # fullscreen # plátno střihu self.play_before_cut = 3 # počet sekund ukázky přehrání přesk. reklamy self.blink_time = 200 # interval blikání editovaného cutu v ms self.cutting = Canvas(self.gui, width=800, height=22, bg='#D9D9D9', cursor='hand1', highlightthickness=0) self.cutting.grid(row=1, column=0, columnspan=9, sticky='we') self.cutting.bind('<Button-1>', self.edit_cut_changer) # přepinani self.gui.bind('<Shift_R>', self.edit_cut_changer) # přepinani shiftem self.cutting.bind('<Button-3>', self.edl_insert_new) # nový střih self.gui.bind('<Return>', self.edl_insert_new) # --//-- self.gui.bind('<Escape>', self.deselect_cut) # nulovani vyberu self.gui.bind('<Delete>', self.cut_delete) # vymazani cutu v edit mode self.edl_cuts = [] # analogie k self.edl.edl pro ID cisla střihu obd. self.editmode = False # stav edit mod ano/ne při vybrani cutu # plátno progress baru self.pbar = Canvas(self.gui, width=800, height=16, bg='#D9D9D9', cursor='xterm', highlightthickness=0) self.pbar.grid(row=2, column=0, columnspan=9, sticky='we') for event in ['<Configure>', '<Button-1>', '<ButtonRelease-1>', '<B1-Motion>', '<Button-3>', '<ButtonRelease-3>', '<B3-Motion>']: self.pbar.bind(event, self.mouse_set_pos) self.pbar.bind('<Configure>', self.redraw_canvas) self.progress = '' # progressbar - modry obdelnik self.mouse_soft_pos = False # proměnná pro jemný posuv True nebo False self.click_canvas_pos = 0 # posl. souř. kliku pro e.type 6 (tažení) # Message info - stavový řádek self.info = Label( self.gui, height=0, wraplength=600, bg='#FFFCDD', font="Arial 12") self.info.grid(row=5, column=0, columnspan=9, sticky='we') # regulace velikost posuvu vpřed/vzad self.frame = '' # hodnota shiftu o 1 frame self.shift_values = ['0.1 sec. => 0.1', '1 sec. => 1', '5 sec. => 5', '10 sec. => 10', '30 sec. => 30', '1 min. => 60', '5 min. => 300', '10 min. => 600', '30 min. => 1800'] # aktuální hodnota posunu v sekundách self.shift_actual_value = 30 # spinbox v Gui self.shift = Spinbox( values=self.shift_values, cursor='sb_v_double_arrow', readonlybackground='#FFFCDD', command=self.set_shift, state='readonly', font="Arial 12", justify='center', width=16) self.shift.grid(row=3, column=3, columnspan=3, sticky='we') for event in '<Up>', '<Down>': self.gui.bind(event, self.set_shift) for event in '<Button-4>', '<Button-5>': # kolečko myši up/down self.shift.bind(event, self.set_shift) # spid a spis seconds/pixels pro duration seconds/pixels pro shift self.spid = 0 # poměr pix/sec pro api.duration self.spis = self.sec_to_pix_ratio(self.shift_actual_value) for event in '<Enter>', '<Leave>': self.pbar.bind(event, lambda e, help=_('Rewind LM + drag - soft RM + drag '): self.tooltip(e, help)) self.cutting.bind(event, lambda e, help=_('Cutting canvas ,') + _(' next cut mark [Ctrl] + <- ->, insert new [Enter]/RM') + _(' Back Esc/LM/P-Shift'): self.tooltip(e, help)) self.shift.bind(event, lambda e, help=_('Skip shift: more [Up] less [Down] '): self.tooltip(e, help)) # lcdl - levý lcd + lcdr - pravý lcd for lcd in 'lcd_l', 'lcd_r': setattr(self, lcd, Label(self.gui, wraplength=600, bg='#FFFCDD', font='Arial 12', relief='sunken', justify='center')) self.lcd_l.grid(row=3, column=0, columnspan=3, sticky='we') self.lcd_l['text'] = _('Cut not selected') self.lcd_r.grid(row=3, column=6, columnspan=3, sticky='we') self.lcd_r['text'] = _('Video file not open') # ikony pro buttony self.icon = [] # zásobník na ikony for name in ['open.gif', 'import.gif', 'back_cut.gif', 'back.gif', 'play.gif', 'forw.gif', 'forw_cut.gif', 'remix.gif', 'save.gif']: self.icon.append(PhotoImage(file=os.path.join('icons', name))) # tlacitka [<text>, <funkce>, <kl.zkratka>, <text nápovědy(tooltip)>] self.buttons = [ ('open_button', ['open_video', '<Control-KeyPress-o>', _('Open video file [Ctrl+o]')]), ('open_edl_button', ['edl_import', '<Control-KeyPress-i>', _('Import cuts from EDL file [Ctrl+i]')]), ('prev_cut_button', ['prew_cut', '<Control-KeyPress-Left>', _('Previous cut [Ctrl+Left]')]), ('rewind_button', ['rewind', '<Left>', _('Fast rewind [Left]')]), ('play_button', ['play', '<p>', _('Play / pauze or a show of the selected cut [p]')]), ('refind_button', ['refind', '<Right>', _('Fast forward [Right]')]), ('next_cut_button', ['next_cut', '<Control-KeyPress-Right>', _('Next cut [Ctrl+Left]')]), ('remixtep_button', ['remix', '<Control-KeyPress-r>', _('Re multiplex to new file [Ctrl+r]')]), ('save_edl_bbutton', ['save_edl', '<Control-KeyPress-s>', _('Save to EDL file [Ctrl+s]')]) ] # buttony for button in self.buttons: index = self.buttons.index(button) #predava se tlacitko, (text, funkce) setattr(self, button[0], ttk.Button(self.gui, image=self.icon[self.buttons.index(button)], command=getattr(self, button[1][0]))) cudl = getattr(self, button[0]) # cast self.<open_button> #Tool tipy v Labelu info help = button[1][2] for event in '<Enter>', '<Leave>': cudl.bind(event, lambda e, help=help: self.tooltip(e, help)) # + grid cudl.grid(row=4, column=index, sticky='we') #nast. roztahovat druhý řádek self.gui.grid_rowconfigure(0, weight=1) #nast. roztahovat tlačítka 4. řádek/ vsechny sloupce self.gui.grid_columnconfigure(index, weight=1) #Kl. zkratky self.gui.bind_all(button[1][1], getattr(self, button[1][0])) # start Mplayeru v iddle režimu v okně canvasu při otevření aplikace self.wid = str(hex(self.video.winfo_id())) self.api.start(wid=self.wid) #***FUNKCE OVLADAČŮ UDÁLOSTÍ TLAČÍTKA, KLÁVESY***# def tooltip(self, e, help): '''Zobrazovač tooltipů v labelu info''' if e.type == '7': # najede mys self.info['text'] = help # tooltip zobraz elif e.type == '8': # mys odjela self.info['text'] = '' # smazat tooltip def close(self, e=''): '''zavření okna jakymkoliv způsobem''' if self.edl.imported_edl != self.edl.edl: # byly učiněny změny save = messagebox.askyesno(_('Save changes ?'), _('In the list of cuts made changes,') + _(' you want to save them?')) if save: self.save_edl() if self.api.player: self.api.close() self.gui.destroy() print('the END :)') # grafika už je mrtvá def fullscreen(self, e=''): '''Prepne aplikaci do fullscreen modu''' if self.gui.attributes('-fullscreen') == 0: self.gui.attributes('-fullscreen', 1) else: self.gui.attributes('-fullscreen', 0) def open_video(self, e='', filename=''): '''Otevření videa''' if filename: video = filename else: video = filedialog.askopenfilename(title=_('Select video file'), filetypes=[('MKV', '*.mkv'), ('MPEG', '*.mpg'), ('AVI', '*.avi')]) if video: try: self.edl.edl = [] # reset stareho EDL self.edl.imported_edl = [] # reset stareho importovaneho EDL self.edl_render(self.edl.edl, method='redraw') # re-draw self.api.open_video(video) # jaky soubor otevrit self.frame = '~1 frame => ' + str(1 / self.api.fps) if self.shift_values[0] != self.frame: # jen kdyz tam neni self.shift_values.insert(0, self.frame) # vloz FPS shift self.shift_spinbox_value = StringVar() # tk proměnná self.shift_spinbox_value.set('30 sec. => 30') # nastav vých. self.shift_actual_value = 30 self.shift['textvariable'] = self.shift_spinbox_value # init self.pbar.delete(self.progress) # vymazat starý progressbar self.edl.edl_name(video) # název edl dle videa self.cutting['bg'] = '#008A00' # barvy self.redraw_canvas() # překreslit a získat spid a spis self.editmode = False # vypnutí edit mode self.edl.sel_cut_index = [None, None] # reset selected cut self.lcd_l['text'] = _('Cut unselected') # reset cut lcd # vytvoření prvku progressbar pozice videa self.progress = self.pbar.create_rectangle(0, 0, 0, 16, fill='blue', width=0) self.actual_position() # sledovani pozice kazdou vterinu # jméno a délka videa titulek a info self.gui.title(os.path.split(self.api.videofilename)[1] + ' - ' + _('Commercial cutter')) self.gprint('Video: ' + os.path.split( self.api.videofilename)[1] + ' délka: ' + str(self.api.duration)) # kdyz existuje stejnojmenne edl tak imporovat if os.path.exists(self.edl.edlname): self.edl_import(quiet=True) except Exception as er: messagebox.showerror(_('Error while opening video '), er.args[1]) self.close() else: self.gprint(_('The video file was not selected!')) def edl_import(self, e='', quiet=False): '''otevře EDL pro uživatelský import - quiet je pro import bez dialogu na vybrani souboru''' if self.api.duration: if not quiet: # quiet True tichy rezim bez dialogu edl = filedialog.askopenfilename( title=_('Select the EDL file'), filetypes=[('EDL', '*.edl')]) elif quiet: edl = self.edl.edlname # když exis. edl ktery ma stejne jmeno if edl: try: self.edl.import_edl(edl, self.api.duration) # importovat self.edl.edl = list(self.edl.imported_edl) # vytv. kopie self.edl_render(self.edl.edl, method='redraw') # render self.gprint(_('Importing EDL file ') + edl + _(' was successful')) except Exception as er: print(self.edl.edl) self.edl.imported_edl = [] # nulování import EDL listu self.edl_render(self.edl.edl, method='redraw') # render self.gprint(_('EDL file: ') + edl + _(' failed to import - Error: ') + str(er)) else: self.gprint(_('Selecting an EDL file has been canceled')) else: self.gprint(_('Please first open any video!')) def rewind(self, e=''): '''přetáčení vzad''' self.rewinder('back') def refind(self, e=''): '''přetáčení vpřed''' self.rewinder('forw') def prew_cut(self, e=''): self.mark_rewinder('back') def next_cut(self, e=''): self.mark_rewinder('next') def play(self, e=''): '''pause/play''' if self.api.paused == True: self.api.paused = False else: self.api.paused = True if self.api.duration: try: cut_index = self.edl.sel_cut_index[0] if cut_index == None: # normalne play/pause strih neni vybran self.api.command('pause') self.api.paused = False else: # kdyz je vybran cut prehraje se ukazka cutu cut_start = self.edl.edl[cut_index][0] # zacatek preskoku if cut_start >= self.play_before_cut: # sekund hrani pred pos_before = cut_start - self.play_before_cut # < 2s self.gui.after(round(self.play_before_cut) * 1000, self.play_after_cut) else: pos_before = 0 # hrej a preskoc $cut_start sec self.gui.after(round(cut_start * 1000), self.play_after_cut) self.api.seek(pos_before) # pretoc na start ukazky self.pos_progress(pos_before) # pretoc progressbar -//- self.api.command('pause') # zacni hrat self.api.paused = False except Exception as er: self.gprint(er.args[1]) else: self.gprint(_('You do not have any open video!')) def play_after_cut(self): '''Prehraje usek ukazky po reklamě ...''' try: cut_index = self.edl.sel_cut_index[0] # zjisti aktualni sel. index cut_end = self.edl.edl[cut_index][1] # konec reklamy self.api.seek(cut_end) # skok za reklamu self.pos_progress(cut_end) # pretoc progressbar self.deselect_cut() # deselectuj cut pro normal play if self.api.position < self.api.duration: # kdyz neni konec videa self.play() # prehravej normalne dal pomoci self.play except Exception as er: self.gprint(er.args[1]) def remix(self, e=''): '''Remultiplex new copy without commercials''' self.gprint(_('Future implemented..')) def save_edl(self, e=''): '''uloží EDL do souboru na žádost uživatele''' if self.edl.edlname and self.edl.edl: try: self.edl.save_edl(self.edl.edlname) self.edl.imported_edl = self.edl.edl # aktualizuj stav importu self.gprint(_('EDL file was saved to: ') + self.edl.edlname) except Exception as er: self.gprint(_('EDL could not be saved to: ') + self.edl.edlname + ' Error: ' + er) else: self.gprint( _('It is necessary to open a video and perform video cut')) def set_shift(self, e=''): '''Interval skoku posunu vpřed/vzad pomocí Up/Down''' if self.api.duration: # načte aktuální hodnotu widgetu shift (spinbox) shift_index = self.shift_values.index( self.shift_spinbox_value.get()) if e: # ovládáno myší nebo Up/Down klávesou if e.keysym == 'Up' or e.num == 4: # nahoru na doraz if shift_index < len(self.shift_values) - 1: shift_index += 1 self.shift_spinbox_value.set( self.shift_values[shift_index]) elif e and e.keysym == 'Down' or e.num == 5: # dolu k 0 if shift_index > 0: shift_index -= 1 self.shift_spinbox_value.set( self.shift_values[shift_index]) # nastaví hodnotu posuvu přetáčení dle indexu zvolene hodnoty self.shift_actual_value = float( self.shift_values[shift_index].split('=>')[1].strip()) # rekonfiguruje self.spis sec/shift_actual_value self.spis = self.sec_to_pix_ratio(self.shift_actual_value) def mouse_set_pos(self, e): '''Pomocí stisknutí Button-1 myši a tažení přetáčí na lib. pozici''' if self.api.duration: # video musí být open try: # reakce na událost dle typu if e.num == 3 and e.type == '4': self.mouse_soft_pos = True # přepnutí na jemný - PM self.click_canvas_pos = e.x # +/- pix od kliku, pozici self.click_time_pos = float(self.api.position) # ulozit time_pos = self.click_time_pos elif e.num == 1 and e.type == '4': self.mouse_soft_pos == False # vypne se jemny time_pos = self.spid * e.x # vypočet time pos self.pos_progress(time_pos, color='orange') # progr. elif e.num == '??' and e.type == '6': # jemné Myš 3 if self.mouse_soft_pos == False: time_pos = self.spid * e.x self.pos_progress(time_pos, color='orange') elif self.mouse_soft_pos == True: click_pix_diff = e.x - self.click_canvas_pos # myš +/- rel_time = click_pix_diff * self.spis time_pos = rel_time + self.click_time_pos self.pos_progress(time_pos, color='orange') elif e.type == '5': # uvolněno tl. myši resety time_pos = self.api.position self.pos_progress(self.api.position, color='blue') self.mouse_soft_pos = False # reset jemného posuvu self.api.seek(time_pos) # přetočit na if self.editmode == True: self.edl_cutter(self.api.position) # cut value except Exception as er: self.gprint(er.args[1]) else: self.gprint(_('First you need to open a video file!')) def edl_insert_new(self, e): '''Insert new cut start and end''' position = self.api.position self.edl_cutter(position, type='new') def edl_cutter(self, position, type='edit'): '''Dle hodnoty position při posuvu překresluje i obdelník nového cutu při tvorbě, nebo editaci nového cutu před vložením do EDL. Funkce je volána klikem do zelene pro vytvoření nového cutu a dále se edituje posuvem modreho posuvniku''' try: position = round(position, 2) # zaookr na 2 mista if type == 'new': new_cut = [position, position] # novy cut na pretoc. pozici cut_index = len(self.edl.edl) edit_to = [] # edit_to nezadano # úprava existujiciho nějaký cut je vybrán a edit mod je spuštěn elif self.edl.sel_cut_index != [None, None] and self.editmode == True: cut_index = self.edl.sel_cut_index[0] # index_cut vybr. cutu mark_index = self.edl.sel_cut_index[1] # index_mark vybr. marku new_cut = list(self.edl.edl[cut_index]) # editovany cut new_cut[mark_index] = position # vloz novy mark_time edit_to = [cut_index] # index cutu v EDL jenz se edituje v [] if new_cut[0] >= new_cut[1]: if mark_index == 1: # prohodi start end pri pretazeni self.cut_activate(cut_index, 0) # konce else: self.cut_activate(cut_index, 1) # nebo zacatku self.edl.edl_build_validate(new_cut, self.edl.edl, self.api.duration, edit_to=edit_to) # validace self.edl.edl_build(new_cut, self.edl.edl, self.api.duration, edit_to=edit_to) # vložení do EDL self.edl_render(self.edl.edl, method='redraw') self.lcd_l['text'] = _('Editing ') + str(cut_index + 1) +\ _('. cut ') + str(self.edl.edl[cut_index]) # auto označeni do edit modu a vybrani end marku pro posun if type == 'new': cut_index, mark_index = self.edl.find_pos_nearest_mark( position, self.edl.edl, self.api.duration, direction='abs') self.cut_activate(cut_index, 1) # aktivuj cut a levy mark self.edit_cut_changer() # spust editaci na posunuti konce except Exception as er: self.gprint(er.args[1]) def edit_cut_changer(self, e=''): '''Ovladač pro prepinani cut modů select pro ukázku a mazání a edit modu pro úpravu (resizing)''' try: if e and e.type == '4': # kliknutím position = e.x * self.spid elif e and e.type == '2' and e.keysym == 'Shift_R' or not e: # nebo dle pozice se vybere nejblizsi cut/mark position = self.api.position # x sour. na cas a nejblizsi mark cut_index, mark_index = self.edl.find_pos_nearest_mark( position, self.edl.edl, self.api.duration) # není aktivovaný if self.edl.sel_cut_index == [None, None]: self.cut_activate(cut_index, mark_index) # aktivace cut_marku self.lcd_l['bg'] = '#DDFFDE' # zelene lcd # uz aktivovany - vstup do edit mode elif self.editmode is not True: self.gprint( _('Editing cut by position.') + _(' Ends with Esc, deleting Delete LM,') + _('Ctrl + <- -> next cut mark.')) self.editmode = True # aktivace edit modu self.cut_blink() # blikat self.lcd_l['bg'] = '#FBC3C5' # cervene lcd elif self.editmode == True: self.editmode = False self.deselect_cut() self.lcd_l['bg'] = '#FFFCDD' # zpet na vychozi barvu except Exception as er: self.gprint(er.args[1]) def cut_delete(self, e=''): '''Odstranění prave vybraneho cutu v select mode z EDL a prekresleni platna strihu''' if self.edl.edl and self.edl.sel_cut_index != [None, None]: if self.editmode == True: # deletovat se bude v edit modu cut_index = self.edl.sel_cut_index[0] # index selection cutu if e: confirm = messagebox.askyesno(_('Deleting cut!'), _('Really delete ') + str(cut_index + 1) + _('. cut ?')) else: confirm = True if confirm == True: del(self.edl.edl[cut_index]) # vymaz self.deselect_cut() # deselect cut self.edl_render(self.edl.edl, method='redraw') # render print('DEBUG EDL CHANGE to: ' + str(self.edl.edl)) self.gprint('Střih ' + str(cut_index) + _(' was deleted from the EDL')) def cut_activate(self, cut_index, mark_index): '''aktivuje vybraný cut (zcervená) rusi vyber stareho pretoci na novou pozici vybraneho marku''' try: old_sel_cut_index = self.edl.sel_cut_index[0] if old_sel_cut_index != None: # existuje starý výběr self.cutting.itemconfig(self.edl_cuts[old_sel_cut_index], fill='#AB0000') # zrusit zcervenani position = self.edl.edl[cut_index][mark_index] # nova pozice self.api.seek(position) # přetočit na novou pozici self.edl.sel_cut_index = [cut_index, mark_index] # novy sel. index self.pos_progress(position) # posunout progressbar + lcd_l mark_text = _('Start ') if mark_index == 0 else _('End ') self.lcd_l['text'] = mark_text + str(cut_index + 1) +\ _('. cut ') + str(self.edl.edl[cut_index]) self.cutting.itemconfig(self.edl_cuts[cut_index], fill='red') self.gprint( str(cut_index + 1) + _('. cut selected. ') + _(' Back Esc, next cut mark Ctrl + <- ->, editing R-Shift/LM')) except Exception as er: self.gprint(er.args[1]) def cut_blink(self): '''vizualni blikani editovaneho cutu pri editovani''' cut_index = self.edl.sel_cut_index[0] normal_colors = ['#AB0000', '#FFFCDD'] # cut, lcd_l glow_colors = ['red', '#FBC3C5'] # glow color for blink mode if self.editmode == True: # blink only edit mode cut_item_color = self.cutting.itemcget(self.edl_cuts[cut_index], 'fill') # get actual color for cut lcd_item_color = self.lcd_l['bg'] # get actual color for lcd_l if cut_item_color == normal_colors[0]\ and lcd_item_color == normal_colors[1]: self.cutting.itemconfig(self.edl_cuts[cut_index], fill=glow_colors[0]) self.lcd_l['bg'] = glow_colors[1] else: self.cutting.itemconfig(self.edl_cuts[cut_index], fill=normal_colors[0]) self.lcd_l['bg'] = normal_colors[1] self.blinkator = self.gui.after(self.blink_time, self.cut_blink) def deselect_cut(self, e=''): '''Resetuje vyběr editovaneho cutu při kliku do zelene oblasti nebo stisku kl. Esc''' cut_index, mark_index = self.edl.sel_cut_index if cut_index is not None: if self.editmode == True: self.gui.after_cancel(self.blinkator) # zrusit blikani self.lcd_l['text'] = _('Not selected cut') self.cutting.itemconfig(self.edl_cuts[cut_index], fill='#AB0000') self.edl.sel_cut_index = [None, None] self.editmode = False self.lcd_l['bg'] = '#FFFCDD' # zpet na vychozi barvu self.gprint(_('Selecting cut deactivated')) def edl_render(self, edl_list, method='resize'): '''překreslí interacktivní čtverce střihů (self.edl_cuts) při editaci, naimportování EDL souboru, nebo změně velikosti okna dle zadaných souřadnic a přidá do self.edl_cuts''' if method == 'redraw': for cut_rect in self.edl_cuts: # smazani starych obdelniku self.cutting.delete(cut_rect) # v platne strihu self.edl_cuts = [] # vynulovani seznamu ID cutů v gui for cut in edl_list: cutx_start, cutx_end = cut[0] / self.spid, \ cut[1] / self.spid # ziskej casy index_cut = edl_list.index(cut) # index strihu v self.edl.edl if method == 'redraw': # kompletni překresleni střihu odznova self.edl_cuts.append(self.cutting.create_rectangle( cutx_start, 0, cutx_end, 22, fill='#AB0000', width=0)) # jen změny velikostí cutu při změně vel. okna elif method == 'resize': self.cutting.coords( self.edl_cuts[index_cut], cutx_start, 0, cutx_end, 22) self.edl.sorter(edl_list) # serareni def gprint(self, message): self.info['text'] = message print('GUI>>' + str(message)) def sec_to_pix_ratio(self, duration_time): '''Vrátí koeficient poměr seconds/pixels při zadané časové délce delka celeho videa''' return duration_time / self.pbar.winfo_width() # vynucená délka def rewinder(self, direction): '''Přetáčení vpřed nebo vzad''' if self.api.duration: try: if direction == 'back': position = self.api.position - self.shift_actual_value elif direction == 'forw': position = self.api.position + self.shift_actual_value self.api.seek(position) self.pos_progress(position) if self.editmode == True: try: self.edl_cutter(self.api.position) except Exception as er: self.gprint(er) except Exception as er: self.gprint(er.args[1]) else: self.gprint(_('You do not have any open video!')) def mark_rewinder(self, direction): '''Přetočí na předchozi, nebo dalsi cutmark od toho nejblizsiho, ktery najde edl.find_pos_nearest_mark''' if self.edl.edl: try: # pozice nejb. marku cut_index, mark_index = self.edl.find_pos_nearest_mark( self.api.position, self.edl.edl, self.api.duration, direction=direction) self.cut_activate(cut_index, mark_index) except Exception as er: self.gprint(er.args[1]) def pos_progress(self, time, color=''): '''Překresluje progressbar videa (velikost obdélníku) v plátně střihu na časovou hodnotu v time - absolutně zadanou''' self.pbar.update_idletasks() if self.api.duration: # video je otevřeno a délka je známá self.lcd_r['text'] = str(round(self.api.position, 2)) + ' s / ' +\ str(self.api.duration) + ' s' pixels = time / self.spid self.pbar.coords(self.progress, 0, 0, pixels, 16) if color: self.pbar.itemconfig(self.progress, fill=color) def redraw_canvas(self, e=''): '''Přepočítat a překreslit prvky v kanvasech a jeho prvky při změně velikosti okna aplikace ve správném poměru velikostí''' self.pbar.update_idletasks() # spid a spis seconds/pixels pro duration seconds/pixels pro shift self.spid = self.sec_to_pix_ratio(self.api.duration) # sec/pix delku self.spis = self.sec_to_pix_ratio(self.shift_actual_value) # // shift self.pos_progress(self.api.position) # překr progressbaru self.edl_render(self.edl.edl, method='resize') print('DEBUG : ' + _('Rewind ') + str(self.spid) + ' sec/pix - ' + _('soft ') + str(self.spis) + ' sec/pix') def actual_position(self): '''Pravidelné zjišťování pozice každou sekundu''' if self.api.paused == False: # neni pause - hraje se try: self.api.position = self.api.get_position() self.pos_progress(self.api.position) # pred koncem videa zastav aby neskoncilo if self.api.position > self.api.duration - ( self.api.safe_end_time * 2): self.api.seek(self.api.duration) # skok na konec except Exception as er: self.gprint(er.args[1]) self.timer = self.gui.after(100, self.actual_position)
class Postprocess: '''Zpracování střihu z ED listu, vykopírování vystříhaného do nového mkv''' def __init__(self, filename): self.edl = Edl() # nahraj třídu Edl self.api = Api() # nahraj třídu Api # path to ffmpeg executable for all platforms if sys.platform == 'linux' or sys.platform == 'darwin': self.ffmpeg_exec = 'ffmpeg' elif sys.platform == 'win32': self.ffmpeg_exec = 'ffmpeg.exe' self.tmp_prefix = '_nocut' # přidá se do <filename>_prefix.mkv if os.path.exists(filename): self.filename = filename else: print(_('Requested file: ') + filename + _(' not found!')) sys.exit() self.api.get_info(filename) # get videofile info self.duration = self.api.get_duration() # get duration self.edlname = os.path.splitext(self.filename)[0] + '.edl' self.orig_filename = os.path.splitext(self.filename)[0] +\ self.tmp_prefix + os.path.splitext(self.filename)[1] try: self.edl.import_edl(self.edlname, self.duration) self.edllist = self.edl.imported_edl except Exception as er: print(_('Failed to import an EDL file - error : ') + str(er)) sys.exit() try: proc = subprocess.Popen(self.ffmpeg_exec) proc.communicate() except Exception as er: print(_('Not found ffmpeg program! ') + er.args[1]) sys.exit() # Pomocné funkce def hour_min_sec(self, seconds): '''Konvertuje sekundy do h:m:s''' m, s = divmod(seconds, 60) h, m = divmod(m, 60) return str(int(h)) + ':' + str(int(m)) + ':' + str(round(s, 2)) def remux(self): '''Remux video with ffmpeg''' if self.duration: # delku videa je potreba znat green_areas = [] # vyhledej green areas max_index = len(self.edllist) - 1 for cut in self.edllist: if self.edllist.index(cut) == 0 and cut[0] > 0: # začína zel. green_areas.append([0, cut[0]]) # 0 az cut[0] if self.edllist.index(cut) < max_index: next_cut_index = self.edllist.index(cut) + 1 # cut[1]+next green_areas.append([cut[1], self.edllist[next_cut_index][0]]) if self.edllist.index(cut) == max_index\ and cut[1] < self.duration: green_areas.append([cut[1], self.duration]) # last -> end print(_('DEBUG : green areas : ') + str(green_areas)) if green_areas: parts_list = [] for green in green_areas: time = self.hour_min_sec(green[0]) duration = self.hour_min_sec(green[1] - green[0]) parts_list.append([time, duration]) # prejmenuj na *-orig.mkv os.rename(self.filename, self.orig_filename) # části pro spojení cut_parts = [] # rozstřihat jednotlivé části od part[0], délka part[1] for part in parts_list: index = str(parts_list.index(part)) out_part = self.filename + '.part' + index + '.ts' cut_parts.append(out_part) # bsf pro h264 avc streamy je nutný jinak ffmpeg řve if self.api.video_info['ID_VIDEO_FORMAT'] == 'avc1': bsf = ['-vbsf', 'h264_mp4toannexb'] else: bsf = list() ffmpeg_cmd = [self.ffmpeg_exec, '-i', self.orig_filename, '-ss', part[0], '-t', part[1], '-f', 'mpegts', '-c', 'copy'] + bsf + [out_part] print('DEBUG:') print(ffmpeg_cmd) proc = subprocess.Popen(ffmpeg_cmd) proc.communicate() concat_str = 'concat:' + '|'.join(cut_parts) # spojit out_parts soubory do výsledného souboru self.filename ffmpeg_cmd2 = [self.ffmpeg_exec, '-isync', '-i', concat_str, '-f', 'matroska', '-c', 'copy', self.filename] proc = subprocess.Popen(ffmpeg_cmd2) proc.communicate() else: print(_('Not found the green area to leave !'))