def on_cut_play_clicked(self, widget, data=None): filename = self.gui.main_window.get_selected_filenames()[0] error, cutlists = cutlists_management.download_cutlists( filename, self.app.config.get('general', 'server'), self.app.config.get('general', 'choose_cutlists_by'), self.app.config.get('general', 'cutlist_mp4_as_hq')) if error: return cutlist = cutlists_management.get_best_cutlist(cutlists) cutlist.download(self.app.config.get('general', 'server'), filename) cutlist.read_cuts() # delete cutlist? if self.app.config.get('general', 'delete_cutlists'): fileoperations.remove_file(cutlist.local_filename) # make edl # https://github.com/mpv-player/mpv-player.github.io/blob/master/guides/edl-playlists.rst edlurl = "edl://" for count, (start, duration) in enumerate(cutlist.cuts_seconds): edlurl = edlurl + filename + "," + str(start) + "," + str( duration) + ";" p = subprocess.Popen([self.app.config.get_program('mpv'), edlurl]) while p.poll() == None: time.sleep(1) while Gtk.events_pending(): Gtk.main_iteration()
def get_keyframes_from_file(self, filename): """ returns keyframe list - in frame numbers""" if not os.path.isfile(filename + '.ffindex_track00.kf.txt'): try: command = [self.config.get_program('ffmsindex'), '-p', '-f', '-k', filename ] ffmsindex = subprocess.call(command) except OSError: return None, "ffmsindex konnte nicht aufgerufen werden." if os.path.isfile(filename + '.ffindex_track00.kf.txt'): filename_keyframes = filename + '.ffindex_track00.kf.txt' elif os.path.isfile(filename + '.ffindex_track01.kf.txt'): filename_keyframes = filename + '.ffindex_track01.kf.txt' elif os.path.isfile(filename + '.ffindex_track02.kf.txt'): filename_keyframes = filename + '.ffindex_track02.kf.txt' else: filename_keyframes = None try: index = open(filename_keyframes, 'r') except IOError: return None, "Keyframe File von ffmsindex konnte nicht geöffnet werden." index.readline() index.readline() try: list =[int(i) for i in index.read().splitlines()] except ValueError: return None, "Keyframes konnten nicht ermittelt werden." index.close() if os.path.isfile(filename + '.ffindex'): fileoperations.remove_file(filename +'.ffindex') return list, None
def __create_cutlist_virtualdub(self, filename, format): """ returns: cuts, error_message """ try: f = open(filename, 'r') except IOError: return None, "Die VirtualDub-Projektdatei konnte nicht gelesen werden.\nWurde das Projekt in VirtualDub nicht gespeichert?\n(Datei: %s)." % filename cuts_frames = [] # (start, duration) count = 0 for line in f.readlines(): if "VirtualDub.subset.AddRange" in line: try: start, duration = line[line.index('(') + 1: line.index(')')].split(',') except (IndexError, ValueError) as message: return None, "Konnte Schnitte nicht lesen, um Cutlist zu erstellen. (%s)" % message if format == Format.HQ or format == Format.HD: cuts_frames.append((int(start) - 2, int(duration))) else: cuts_frames.append((int(start), int(duration))) if len(cuts_frames) == 0: return None, "Konnte keine Schnitte finden!" fileoperations.remove_file(filename) return cuts_frames, None
def _on_button_show_cuts_clicked(self, widget, data=None): cutlist = cutlists_management.Cutlist() if self.builder.get_object('radio_local_cutlist').get_active(): cutlist.local_filename = self.builder.get_object( 'label_cutlist').get_text() else: cutlist = self.treeview_cutlists.get_selected() if not cutlist: self.gui.message_error_box( "Es wurde keine Cutlist ausgewählt!") return error = cutlist.download(self.app.config.get('general', 'server'), self.filename) if error: self.gui.message_error_box(error) return self.app.show_cuts(self.filename, cutlist) # delete cutlist fileoperations.remove_file(cutlist.local_filename)
def upload(): error_messages = [] count = len(cutlists) # ~ counter = 0 for cutlist in cutlists: # ~ counter += 1 error_message = cutlist.upload( self.app.config.get('general', 'server'), self.app.config.get('general', 'cutlist_hash')) if error_message: error_messages.append(error_message) else: if self.app.config.get('general', 'delete_cutlists'): fileoperations.remove_file(cutlist.local_filename) # ~ self.log.debug("Counter: {}, Count: {}".format(counter, count)) # ~ if counter < count: # ~ self.log.debug("Multiple cutlists: Next upload delayed.") # ~ time.sleep(1.1) message = "Es wurden %s/%s Cutlisten hochgeladen!" % ( str(count - len(error_messages)), str(count)) if len(error_messages) > 0: message += " (" + ", ".join(error_messages) + ")" yield message
def mux_ac3(self, filename, cut_video, ac3_file, cutlist): # cuts the ac3 and muxes it with the avi into an mkv mkvmerge = self.config.get_program('mkvmerge') root, extension = os.path.splitext(filename) mkv_file = os.path.splitext(cut_video)[0] + ".mkv" # env my_env = os.environ.copy() my_env["LANG"] = "C" # creates the timecodes string for splitting the .ac3 with mkvmerge timecodes = (','.join([self.get_timecode(start) + ',' + self.get_timecode(start+duration) for start, duration in cutlist.cuts_seconds])) # splitting .ac3. Every second fragment will be used. # return_value = subprocess.call([mkvmerge, "--split", "timecodes:" + timecodes, "-o", root + "-%03d.mka", ac3_file]) try: blocking_process = subprocess.Popen([mkvmerge, '--ui-language', 'en_US', "--split", "timecodes:" + timecodes, "-o", root + "-%03d.mka", ac3_file ], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, env=my_env) except OSError as e: return None, e.strerror + ": " + mkvmerge return_value = blocking_process.wait() # return_value=0 is OK, return_value=1 means a warning. Most probably non-ac3-data that has been omitted. # TODO: Is there some way to pass this warning to the conclusion dialog? if return_value != 0 and return_value != 1: return None, None, str(return_value) if len(cutlist.cuts_seconds) == 1: # Only the second fragment is needed. Delete the rest. fileoperations.rename_file(root + "-002.mka", root + ".mka") fileoperations.remove_file(root + "-001.mka") if os.path.isfile(root + "-003.mka"): fileoperations.remove_file(root + "-003.mka") else: # Concatenating every second fragment. command = [mkvmerge, "-o", root + ".mka", root + "-002.mka"] command[len(command):] = ["+" + root + "-%03d.mka" % (2*n) for n in range(2,len(cutlist.cuts_seconds)+1)] # return_value = subprocess.call(command) try: blocking_process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, env=my_env) except OSError as e: return None, e.strerror + ": " + mkvmerge return_value = blocking_process.wait() if return_value != 0: # There should be no warnings here return None, None, str(return_value) for n in range(1,2*len(cutlist.cuts_seconds)+2): # Delete all temporary audio fragments if os.path.isfile(root + "-%03d.mka" % n): fileoperations.remove_file(root + "-%03d.mka" % n) # Mux the cut .avi with the resulting audio-file into mkv_file # TODO: Is there some way to pass possible warnings to the conclusion dialog? # return_value = subprocess.call([mkvmerge, "-o", mkv_file, cut_video, root + ".mka"]) try: blocking_process = subprocess.Popen([mkvmerge, "-o", mkv_file, cut_video, root + ".mka"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, env=my_env) except OSError as e: return None, e.strerror + ": " + mkvmerge return_value = blocking_process.wait() if return_value != 0 and return_value != 1: return None, None, str(return_value) fileoperations.remove_file(root + ".mka") # Delete remaining temporary files fileoperations.remove_file(cut_video) return mkv_file, ac3_file, None
def do(self, filenames): if len(filenames) == 1: message = "Es ist eine Datei ausgewählt. Soll diese Datei " else: message = "Es sind %s Dateien ausgewählt. Sollen diese Dateien " % len(filenames) if self.__gui.question_box(message + "endgültig gelöscht werden?"): for f in filenames: fileoperations.remove_file(f)
def do(self, downloads): downloads_count = len(downloads) if downloads_count == 1: question = "Soll der Download wirklich entfernt werden?" else: question = "Sollen %i Downloads wirklich entfernt werden?" % downloads_count dialog = gtk.MessageDialog(self.__gui.main_window, 0, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, question) checkbutton = gtk.CheckButton('Die heruntergeladene Datei in den Müll verschieben\n(Fertige Downloads werden nicht verschoben)') checkbutton.set_active(True) checkbutton.show() dialog.vbox.pack_end(checkbutton) response = dialog.run() if response == gtk.RESPONSE_YES: model = self.__gui.main_window.treeview_download.get_model() refs = [] for row in model: if row[0] in downloads: refs.append(gtk.TreeRowReference(model, row.path)) files = [ os.path.join(row[0].information['output'], row[0].filename + '.torrent'), os.path.join(row[0].information['output'], row[0].filename + '.aria2'), os.path.join(row[0].information['output'], row[0].filename + '.cutlist'), os.path.join(self.__app.config.get('general', 'folder_trash_otrkeys'), row[0].filename + '.segments'), ] for file in files: if os.path.exists(file): fileoperations.remove_file(file, None) if checkbutton.get_active() and not row[0].information['status'] in [DownloadStatus.FINISHED, DownloadStatus.SEEDING]: otrkey = os.path.join(row[0].information['output'], row[0].filename) # move otrkey to trash if os.path.exists(otrkey): fileoperations.move_file(otrkey, self.__app.config.get('general', 'folder_trash_otrkeys'), None) # move avi file of otrdecoder to trash avi_file = os.path.splitext(otrkey)[0] if os.path.exists(avi_file): fileoperations.move_file(avi_file, self.__app.config.get('general', 'folder_trash_avis'), None) row[0].stop() for ref in refs: iter = model.get_iter(ref.get_path()) model.remove(iter) dialog.destroy()
def get_timecodes_from_file(self, filename): # TESTING """ returns frame->timecode and timecode->frame dict""" if not os.path.isfile(filename + '.ffindex_track00.tc.txt'): try: command = [ self.config.get_program('ffmsindex'), '-f', '-c', '-k', filename ] process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) self.show_indexing_progress(process) except OSError: return None, "ffmsindex konnte nicht aufgerufen werden." if os.path.isfile(filename + '.ffindex_track00.tc.txt'): filename_timecodes = filename + '.ffindex_track00.tc.txt' elif os.path.isfile(filename + '.ffindex_track01.tc.txt'): filename_timecodes = filename + '.ffindex_track01.tc.txt' elif os.path.isfile(filename + '.ffindex_track02.tc.txt'): filename_timecodes = filename + '.ffindex_track02.tc.txt' else: filename_timecodes = None try: index = open(filename_timecodes, 'r') except (IOError, TypeError) as e: return None, "Timecode Datei von ffmsindex konnte nicht geöffnet werden." index.readline() try: frame_timecode = {} for line_num, line in enumerate(index, start=0): frame_timecode[line_num] = int( round(float(line.replace('\n', '').strip()), 2) / 1000 * Gst.SECOND) except ValueError: index.close() return None, "Timecodes konnten nicht ermittelt werden." index.close() # Generate reverse dict timecode_frame = {v: k for k, v in frame_timecode.items()} if os.path.isfile(filename + '.ffindex'): fileoperations.remove_file(filename + '.ffindex') self.log.debug("Number of frames (frame_timecode dict): {}".format( list(frame_timecode.keys())[-1] + 1)) return frame_timecode, timecode_frame, None
def on_cut_play_clicked(self, widget, data=None): filename = self.gui.main_window.get_selected_filenames()[0] error, cutlists = cutlists_management.download_cutlists( filename, self.app.config.get('general', 'server'), self.app.config.get('general', 'choose_cutlists_by'), self.app.config.get('general', 'cutlist_mp4_as_hq')) if error: return cutlist = cutlists_management.get_best_cutlist(cutlists) cutlist.download(self.app.config.get('general', 'server'), filename) cutlist.read_cuts() # delete cutlist? if self.app.config.get('general', 'delete_cutlists'): fileoperations.remove_file(cutlist.local_filename) # make edl # http://www.mplayerhq.hu/DOCS/HTML/en/edl.html # [Begin Second] [End Second] [0=Skip/1=Mute] edl_filename = os.path.join( self.app.config.get('general', 'folder_uncut_avis'), ".tmp.edl") f = open(edl_filename, "w") f.write("0 %s 0\n" % (cutlist.cuts_seconds[0][0] - 1)) for count, (start, duration) in enumerate(cutlist.cuts_seconds): end = start + duration if count + 1 == len(cutlist.cuts_seconds): f.write("%s 50000 0\n" % (end)) else: f.write("%s %s 0\n" % (end, (cutlist.cuts_seconds[count + 1][0] - 1))) f.close() p = subprocess.Popen([ self.app.config.get('general', 'mplayer'), "-edl", edl_filename, filename ]) while p.poll() == None: time.sleep(1) while gtk.events_pending(): gtk.main_iteration(False) fileoperations.remove_file(edl_filename)
def upload(): error_messages = [] for cutlist in cutlists: error_message = cutlist.upload(self.app.config.get('general', 'server'), self.app.config.get('general', 'cutlist_hash')) if error_message: error_messages.append(error_message) else: if self.app.config.get('general', 'delete_cutlists'): fileoperations.remove_file(cutlist.local_filename) count = len(cutlists) message = "Es wurden %s/%s Cutlisten hochgeladen!" % (str(count - len(error_messages)), str(count)) if len(error_messages) > 0: message += " (" + ", ".join(error_messages) + ")" yield message
def upload(): error_messages = [] for cutlist in cutlists: error_message = cutlist.upload( self.app.config.get('general', 'server'), self.app.config.get('general', 'cutlist_hash')) if error_message: error_messages.append(error_message) else: if self.app.config.get('general', 'delete_cutlists'): fileoperations.remove_file(cutlist.local_filename) count = len(cutlists) message = "Es wurden %s/%s Cutlisten hochgeladen!" % ( str(count - len(error_messages)), str(count)) if len(error_messages) > 0: message += " (" + ", ".join(error_messages) + ")" yield message
def get_keyframes_from_file(self, filename): """ returns keyframe list - in frame numbers""" if not os.path.isfile(filename + '.ffindex_track00.kf.txt'): try: command = [ self.config.get_program('ffmsindex'), '-f', '-k', filename ] process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) self.show_indexing_progress(process) except OSError: return None, "ffmsindex konnte nicht aufgerufen werden." if os.path.isfile(filename + '.ffindex_track00.kf.txt'): filename_keyframes = filename + '.ffindex_track00.kf.txt' elif os.path.isfile(filename + '.ffindex_track01.kf.txt'): filename_keyframes = filename + '.ffindex_track01.kf.txt' elif os.path.isfile(filename + '.ffindex_track02.kf.txt'): filename_keyframes = filename + '.ffindex_track02.kf.txt' else: filename_keyframes = None try: index = open(filename_keyframes, 'r') except (IOError, TypeError) as e: return None, "Keyframe File von ffmsindex konnte nicht geöffnet werden." index.readline() index.readline() try: list = [int(i) for i in index.read().splitlines()] except ValueError: index.close() return None, "Keyframes konnten nicht ermittelt werden." index.close() if os.path.isfile(filename + '.ffindex'): fileoperations.remove_file(filename + '.ffindex') return list, None
def on_cut_play_clicked(self, widget, data=None): filename = self.gui.main_window.get_selected_filenames()[0] error, cutlists = cutlists_management.download_cutlists(filename, self.app.config.get('general', 'server'), self.app.config.get('general', 'choose_cutlists_by'), self.app.config.get('general', 'cutlist_mp4_as_hq')) if error: return cutlist = cutlists_management.get_best_cutlist(cutlists) cutlist.download(self.app.config.get('general', 'server'), filename) cutlist.read_cuts() # delete cutlist? if self.app.config.get('general', 'delete_cutlists'): fileoperations.remove_file(cutlist.local_filename) # make edl # http://www.mplayerhq.hu/DOCS/HTML/en/edl.html # [Begin Second] [End Second] [0=Skip/1=Mute] edl_filename = os.path.join(self.app.config.get('general', 'folder_uncut_avis'), ".tmp.edl") f = open(edl_filename, "w") f.write("0 %s 0\n" % (cutlist.cuts_seconds[0][0] - 1)) for count, (start, duration) in enumerate(cutlist.cuts_seconds): end = start + duration if count + 1 == len(cutlist.cuts_seconds): f.write("%s 50000 0\n" % (end)) else: f.write("%s %s 0\n" % (end, (cutlist.cuts_seconds[count+1][0] - 1))) f.close() p = subprocess.Popen([self.app.config.get('general', 'mplayer'), "-edl", edl_filename, filename]) while p.poll() == None: time.sleep(1) while gtk.events_pending(): gtk.main_iteration(False) fileoperations.remove_file(edl_filename)
def _on_button_show_cuts_clicked(self, widget, data=None): cutlist = cutlists_management.Cutlist() if self.builder.get_object('radio_local_cutlist').get_active(): cutlist.local_filename = self.builder.get_object('label_cutlist').get_text() else: cutlist = self.treeview_cutlists.get_selected() if not cutlist: self.gui.message_error_box("Es wurde keine Cutlist ausgewählt!") return error = cutlist.download(self.app.config.get('general', 'server'), self.filename) if error: self.gui.message_error_box(error) return self.app.show_cuts(self.filename, cutlist) # delete cutlist fileoperations.remove_file(cutlist.local_filename)
def __cut_file_virtualdub(self, filename, config_value, cuts=None, manually=False): format = self.__get_format(filename) if format == Format.HQ: aspect, error_message = self.__get_aspect_ratio(filename) if not aspect: return None, error_message if aspect == "16:9": comp_data = codec.get_comp_data_h264_169() else: comp_data = codec.get_comp_data_h264_43() elif format == Format.HD: aspect, error_message = self.__get_aspect_ratio(filename) if not aspect: return None, error_message if aspect == "16:9": comp_data = codec.get_comp_data_hd_169() else: comp_data = codec.get_comp_data_hd_43() elif format == Format.AVI: comp_data = codec.get_comp_data_dx50() else: return None, "Format nicht unterstützt (Nur Avi DX50, HQ H264 und HD sind möglich)." # make file for virtualdub scripting engine if manually: # TODO: kind of a hack curr_dir = os.getcwd() try: os.chdir(dirname(config_value)) except OSError: return None, "VirtualDub konnte nicht aufgerufen werden: " + config_value self.__gui.main_window.set_tasks_progress(50) f = open("tmp.vcf", "w") if not manually: f.write('VirtualDub.Open("%s");\n' % filename) if self.config.get('general', 'smart'): f.writelines([ 'VirtualDub.video.SetMode(1);\n', 'VirtualDub.video.SetSmartRendering(1);\n', 'VirtualDub.video.SetCompression(0x53444646,0,10000,0);\n' 'VirtualDub.video.SetCompData(%s);\n' % comp_data ]) else: f.write('VirtualDub.video.SetMode(0);\n') f.write('VirtualDub.subset.Clear();\n') if not manually: for frame_start, frames_duration in cuts: f.write("VirtualDub.subset.AddRange(%i, %i);\n" % (frame_start, frames_duration)) cut_video = self.__generate_filename(filename) f.writelines([ 'VirtualDub.SaveAVI("%s");\n' % cut_video, 'VirtualDub.Close();' ]) f.close() # start vdub if not exists(config_value): return None, "VirtualDub konnte nicht aufgerufen werden: " + config_value if manually: win_filename = "Z:" + filename.replace(r"/", r"\\") command = 'VirtualDub.exe /s tmp.vcf "%s"' % win_filename else: command = "%s /s tmp.vcf /x" % config_value command = "wineconsole " + command print command try: vdub = subprocess.Popen(command, shell=True) except OSError: return None, "VirtualDub konnte nicht aufgerufen werden: " + config_value while vdub.poll() == None: time.sleep(1) while events_pending(): main_iteration(False) fileoperations.remove_file('tmp.vcf') if manually: os.chdir(curr_dir) return None, None return cut_video, None
def do(self, downloads, cut_action=None): downloads_count = len(downloads) if downloads_count == 1: question = "Soll der Download wirklich entfernt werden?" else: question = "Sollen %i Downloads wirklich entfernt werden?" % downloads_count dialog = Gtk.MessageDialog(self.__gui.main_window, 0, Gtk.MessageType.QUESTION, Gtk.ButtonsType.YES_NO, question) checkbutton = Gtk.CheckButton( 'Die heruntergeladene Datei in den Müll verschieben\n(Fertige Downloads werden nicht verschoben)' ) checkbutton.set_active(True) checkbutton.show() dialog.vbox.pack_end(checkbutton, False, False, 0) response = dialog.run() if response == Gtk.ResponseType.OK: model = self.__gui.main_window.treeview_download.get_model() refs = [] for row in model: if row[0] in downloads: refs.append(Gtk.TreeRowReference.new(model, row.path)) files = [ os.path.join(row[0].information['output'], row[0].filename + '.torrent'), os.path.join(row[0].information['output'], row[0].filename + '.aria2'), os.path.join(row[0].information['output'], row[0].filename + '.cutlist'), os.path.join( self.__app.config.get('general', 'folder_trash_otrkeys'), row[0].filename + '.segments'), ] for file in files: if os.path.exists(file): fileoperations.remove_file(file, None) if checkbutton.get_active( ) and not row[0].information['status'] in [ DownloadStatus.FINISHED, DownloadStatus.SEEDING ]: otrkey = os.path.join(row[0].information['output'], row[0].filename) # move otrkey to trash if os.path.exists(otrkey): fileoperations.move_file( otrkey, self.__app.config.get('general', 'folder_trash_otrkeys'), None) # move avi file of otrdecoder to trash avi_file = os.path.splitext(otrkey)[0] if os.path.exists(avi_file): fileoperations.move_file( avi_file, self.__app.config.get('general', 'folder_trash_avis'), None) row[0].stop() for ref in refs: iter = model.get_iter(ref.get_path()) model.remove(iter) dialog.destroy()
def cut_file_manually(self, filename): """ Cuts a file manually with Avidemux or VirtualDub or the CutInterface and gets cuts from possibly created project files (VD) or from output (AD). returns: error_message, cutlist """ program, config_value, ac3file = self.get_program(filename, manually=True) format, ac3_file, bframe_delay = self.get_format(filename) fps, dar, sar, max_frames, ac3_stream, error = self.analyse_mediafile(filename) if error: if exists(filename+'.mkv'): fileoperations.remove_file(filename+'.mkv') return "Konnte FPS nicht bestimmen: " + error, None if program < 0: return config_value, None cutlist = cutlists_management.Cutlist() if program == Program.AVIDEMUX: cutter = CutAvidemux(self.app, self.gui) cuts_frames, cutlist_error = cutter.create_cutlist(filename, config_value) elif program == Program.VIRTUALDUB: # VIRTUALDUB cutter = CutVirtualdub(self.app, self.gui) cuts_frames, cutlist_error = cutter.create_cutlist(filename, config_value) if program == Program.CUT_INTERFACE: # looking for latest cutlist, if any p, video_file = os.path.split(filename) cutregex = re.compile("^" + video_file + "\.?(.*).cutlist$") files = os.listdir(p) number = -1 local_cutlist = None # use fallback name in conclusions if there are no local cutlists for f in files: match = cutregex.match(f) if match: # print "Found local cutlist %s" % match.group() if match.group(1) == '': res_num = 0 else: res_num = int(match.group(1)) if res_num > number: res_num = number local_cutlist = p + "/" + match.group() ci = CutinterfaceDialog.NewCutinterfaceDialog() cutlist = ci._run(filename , local_cutlist, self.app) ci.destroy() if cutlist.cuts_frames == None or len(cutlist.cuts_frames) == 0: cutlist_error = "Keine Schnitte angegeben" else: cutlist_error = None else: # complete cutlist for Avidemux & VirtualDub # create cutlist data if cutlist_error == None: cutlist.cuts_frames = cuts_frames cutlist.intended_app = basename(config_value) cutlist.usercomment = 'Mit %s geschnitten' %self.app.app_name cutlist.fps = fps # calculate seconds for start_frame, duration_frames in cuts_frames: cutlist.cuts_seconds.append((start_frame / fps, duration_frames / fps)) if cutlist_error: return cutlist_error, None else: return None, cutlist
def on_cut_play_clicked(self, widget, data=None): filename = self.gui.main_window.get_selected_filenames()[0] error, cutlists = cutlists_management.download_cutlists( filename, self.app.config.get('general', 'server'), self.app.config.get('general', 'choose_cutlists_by'), self.app.config.get('general', 'cutlist_mp4_as_hq')) if error: return cutlist = cutlists_management.get_best_cutlist(cutlists) cutlist.download(self.app.config.get('general', 'server'), filename) cutlist.read_cuts() # delete cutlist? if self.app.config.get('general', 'delete_cutlists'): fileoperations.remove_file(cutlist.local_filename) # make mplayer edl # http://www.mplayerhq.hu/DOCS/HTML/en/edl.html # [Begin Second] [End Second] [0=Skip/1=Mute] edl_filename = os.path.join( self.app.config.get('general', 'folder_uncut_avis'), ".tmp.edl") f = open(edl_filename, "w") f.write("0 %s 0\n" % (cutlist.cuts_seconds[0][0] - 1)) for count, (start, duration) in enumerate(cutlist.cuts_seconds): end = start + duration if count + 1 == len(cutlist.cuts_seconds): f.write("%s 50000 0\n" % (end)) else: f.write("%s %s 0\n" % (end, (cutlist.cuts_seconds[count + 1][0] - 1))) f.close() # make mpv edl # https://github.com/mpv-player/mpv-player.github.io/blob/master/guides/edl-playlists.rst edlurl = "edl://" for count, (start, duration) in enumerate(cutlist.cuts_seconds): edlurl = edlurl + filename + "," + str(start) + "," + str( duration) + ";" def check_prog(prog): cmdfound = False plays = False if shutil.which(prog): cmdfound = True if not subprocess.call(prog, stdin=subprocess.PIPE, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT): plays = True else: self.log.error("{} failed to start.".format(prog)) else: exist = False self.log.error("{} is not installed.".format(prog)) return cmdfound and plays def play_with(prog): if prog == 'mplayer': p = subprocess.Popen([ self.app.config.get_program('mplayer'), "-edl", edl_filename, filename ]) elif prog == 'mpv': p = subprocess.Popen( [self.app.config.get_program('mpv'), edlurl]) if self.app.config.get('general', 'prefer_mpv'): self.playprog = ['mpv', 'mplayer'] else: self.playprog = ['mplayer', 'mpv'] if check_prog(self.playprog[0]): play_with(self.playprog[0]) elif check_prog(self.playprog[1]): play_with(self.playprog[1]) else: self.gui.message_error_box("Zum Anzeigen der Schnitte sind weder mpv noch mplayer " + \ "installiert bzw. funktionieren nicht.") return while p.poll() == None: time.sleep(1) while Gtk.events_pending(): Gtk.main_iteration() fileoperations.remove_file(edl_filename)
class DecodeOrCut(BaseAction): def __init__(self, app, gui): self.update_list = True self.__app = app self.config = app.config self.__gui = gui def do(self, action, filenames, cut_action=None): self.rename_by_schema = self.__app.rename_by_schema decode, cut = False, False # prepare tasks if action == Action.DECODE: self.__gui.main_window.set_tasks_text('Dekodieren') decode = True elif action == Action.CUT: self.__gui.main_window.set_tasks_text('Schneiden') cut = True else: # decode and cut self.__gui.main_window.set_tasks_text('Dekodieren/Schneiden') decode, cut = True, True file_conclusions = [] if decode: for otrkey in filenames: file_conclusions.append(FileConclusion(action, otrkey=otrkey)) if cut and not decode: # dont add twice for uncut_video in filenames: file_conclusions.append(FileConclusion(action, uncut_video=uncut_video)) # decode files if decode: if self.decode(file_conclusions) == False: return # cut files if cut: if self.cut(file_conclusions, action, cut_action) == False: return self.__gui.main_window.block_gui(False) # no more need for tasks view self.__gui.main_window.set_tasks_visible(False) show_conclusions = False # Only cut - don't show conclusions if all were cancelled if action == Action.CUT: for conclusion in file_conclusions: if conclusion.cut.status != Status.NOT_DONE: show_conclusions = True break # Only decode - don't show if everything is OK elif action == Action.DECODE: for conclusion in file_conclusions: if conclusion.decode.status != Status.OK: show_conclusions = True if not show_conclusions: self.__app.gui.main_window.change_status(0, "%i Datei(en) erfolgreich dekodiert" % len(file_conclusions), permanent=True) # Decode and cut - always show else: show_conclusions = True if show_conclusions: self.__app.conclusions_manager.add_conclusions(*file_conclusions) def decode(self, file_conclusions): # no decoder if not "decode" in self.config.get('general', 'decoder'): # no decoder specified # dialog box: no decoder self.__gui.message_error_box("Es ist kein korrekter Dekoder angegeben!") return False # retrieve email and password email = self.config.get('general', 'email') password = base64.b64decode(self.config.get('general', 'password')) if not email or not password: self.__gui.dialog_email_password.set_email_password(email, password) # let the user type in his data through a dialog response = self.__gui.dialog_email_password.run() self.__gui.dialog_email_password.hide() if response == RESPONSE_OK: email, password = self.__gui.dialog_email_password.get_email_password() else: # user pressed cancel return False # now this method may not return "False" self.__gui.main_window.set_tasks_visible(True) self.__gui.main_window.block_gui(True) # decode each file for count, file_conclusion in enumerate(file_conclusions): # update progress self.__gui.main_window.set_tasks_text("Datei %s/%s dekodieren" % (count + 1, len(file_conclusions))) verify = True command = [self.config.get('general', 'decoder'), "-i", file_conclusion.otrkey, "-e", email, "-p", password, "-o", self.config.get('general', 'folder_uncut_avis')] if not self.config.get('general', 'verify_decoded'): verify = False command += ["-q"] try: process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) except OSError: file_conclusion.decode.status = Status.ERROR file_conclusion.decode.message = "Dekoder wurde nicht gefunden." continue while True: l = "" while True: c = process.stdout.read(1) if c == "\r" or c == "\n": break l += c if not l: break try: if verify: file_count = count + 1, len(file_conclusions) if "input" in l: self.__gui.main_window.set_tasks_text("Eingabedatei %s/%s kontrollieren" % file_count) elif "output" in l: self.__gui.main_window.set_tasks_text("Ausgabedatei %s/%s kontrollieren" % file_count) elif "Decoding" in l: self.__gui.main_window.set_tasks_text("Datei %s/%s dekodieren" % file_count) progress = int(l[10:13]) # update progress self.__gui.main_window.set_tasks_progress(progress) while events_pending(): main_iteration(False) except ValueError: pass # errors? errors = process.stderr.readlines() error_message = "" for error in errors: error_message += error.strip() if len(errors) == 0: # dekodieren erfolgreich file_conclusion.decode.status = Status.OK file_conclusion.uncut_video = join(self.config.get('general', 'folder_uncut_avis'), basename(file_conclusion.otrkey[0:len(file_conclusion.otrkey)-7])) # move otrkey to trash target = self.config.get('general', 'folder_trash_otrkeys') fileoperations.move_file(file_conclusion.otrkey, target) else: file_conclusion.decode.status = Status.ERROR try: unicode(error_message) except UnicodeDecodeError: error_message = unicode(error_message, 'iso-8859-1') file_conclusion.decode.message = error_message return True def cut(self, file_conclusions, action, default_cut_action=None): # now this method may not return "False" self.__gui.main_window.set_tasks_visible(True) self.__gui.main_window.block_gui(True) if not default_cut_action: default_cut_action = self.config.get('general', 'cut_action') for count, file_conclusion in enumerate(file_conclusions): self.__gui.main_window.set_tasks_text("Cutlist %s/%s wählen" % (count + 1, len(file_conclusions))) self.__gui.main_window.set_tasks_progress((count + 1) / float(len(file_conclusions)) * 100) # file correctly decoded? if action == Action.DECODEANDCUT: if file_conclusion.decode.status != Status.OK: file_conclusion.cut.status = Status.NOT_DONE file_conclusion.cut.message = "Datei wurde nicht dekodiert." continue file_conclusion.cut.cut_action = default_cut_action if default_cut_action in [Cut_action.ASK, Cut_action.CHOOSE_CUTLIST]: # show dialog self.__gui.dialog_cut.setup( file_conclusion.uncut_video, self.config.get('general', 'folder_cut_avis'), default_cut_action == Cut_action.ASK) cutlists = [] self.cutlists_error = False def error_cb(error): self.__gui.dialog_cut.builder.get_object('label_status').set_markup("<b>%s</b>" % error) self.cutlists_error = True def cutlist_found_cb(cutlist): self.__gui.dialog_cut.add_cutlist(cutlist) cutlists.append(cutlist) def completed(): if not self.cutlists_error: self.__gui.dialog_cut.builder.get_object('label_status').set_markup("") GeneratorTask(cutlists_management.download_cutlists, None, completed).start(file_conclusion.uncut_video, self.config.get('general', 'server'), self.config.get('general', 'choose_cutlists_by'), self.config.get('general', 'cutlist_mp4_as_hq'), error_cb, cutlist_found_cb) response = self.__gui.dialog_cut.run() self.__gui.dialog_cut.hide() if response < 0: file_conclusion.cut.status = Status.NOT_DONE file_conclusion.cut.message = "Abgebrochen." else: # change cut_action accordingly file_conclusion.cut.cut_action = response if file_conclusion.cut.cut_action == Cut_action.MANUALLY: # MANUALLY error_message, cuts, executable = self.cut_file_manually(file_conclusion.uncut_video) if not error_message: file_conclusion.cut.create_cutlist = True file_conclusion.cut.cutlist.cuts_frames = cuts file_conclusion.cut.cutlist.intended_app = basename(executable) file_conclusion.cut.cutlist.usercomment = 'Mit OTR-Verwaltung geschnitten' fps, error = self.__get_fps(file_conclusion.uncut_video) if not error: file_conclusion.cut.cutlist.fps = fps else: file_conclusion.cut.cutlist.fps = 25. print "Achtung! Möglicherweise wurde eine falsche Fps-Anzahl eingetragen! (%s)" % error # calculate seconds for start_frame, duration_frames in cuts: file_conclusion.cut.cutlist.cuts_seconds.append((start_frame / fps, duration_frames / fps)) else: file_conclusion.cut.status = Status.ERROR file_conclusion.cut.message = error_message elif file_conclusion.cut.cut_action == Cut_action.BEST_CUTLIST: error, cutlists = cutlists_management.download_cutlists(file_conclusion.uncut_video, self.config.get('general', 'server'), self.config.get('general', 'choose_cutlists_by'), self.config.get('general', 'cutlist_mp4_as_hq')) if error: file_conclusion.cut.status = Status.ERROR file_conclusion.cut.message = error continue if len(cutlists) == 0: file_conclusion.cut.status = Status.NOT_DONE file_conclusion.cut.message = "Keine Cutlist gefunden." continue file_conclusion.cut.cutlist = cutlists_management.get_best_cutlist(cutlists) elif file_conclusion.cut.cut_action == Cut_action.CHOOSE_CUTLIST: file_conclusion.cut.cutlist = self.__gui.dialog_cut.chosen_cutlist elif file_conclusion.cut.cut_action == Cut_action.LOCAL_CUTLIST: file_conclusion.cut.cutlist.local_filename = file_conclusion.uncut_video + ".cutlist" if not exists(file_conclusion.cut.cutlist.local_filename): file_conclusion.cut.status = Status.ERROR file_conclusion.cut.message = "Keine lokale Cutlist gefunden." # and finally cut the file for count, file_conclusion in enumerate(file_conclusions): if file_conclusion.cut.status in [Status.NOT_DONE, Status.ERROR]: continue print "[Decodeandcut] Datei %s wird geschnitten" % file_conclusion.uncut_video self.__gui.main_window.set_tasks_text("Datei %s/%s schneiden" % (count + 1, len(file_conclusions))) self.__gui.main_window.set_tasks_progress(0.5) # download cutlist if file_conclusion.cut.cut_action in [Cut_action.BEST_CUTLIST, Cut_action.CHOOSE_CUTLIST]: file_conclusion.cut.cutlist.download(self.config.get('general', 'server'), file_conclusion.uncut_video) cut_video, error = self.cut_file_by_cutlist(file_conclusion.uncut_video, file_conclusion.cut.cutlist) if cut_video == None: file_conclusion.cut.status = Status.ERROR file_conclusion.cut.message = error else: file_conclusion.cut.status = Status.OK file_conclusion.cut_video = cut_video if self.config.get('general', 'rename_cut'): file_conclusion.cut.rename = self.rename_by_schema(basename(file_conclusion.uncut_video)) else: file_conclusion.cut.rename = basename(cut_video) return True def __get_format(self, filename): root, extension = splitext(filename) if extension == '.avi': if splitext(root)[1] == '.HQ': return Format.HQ elif splitext(root)[1] == '.HD': return Format.HD else: return Format.AVI elif extension == '.mp4': return Format.MP4 else: return -1 def __get_program(self, filename, manually=False): if manually: programs = { Format.AVI : self.config.get('general', 'cut_avis_man_by'), Format.HQ : self.config.get('general', 'cut_hqs_man_by'), Format.HD : self.config.get('general', 'cut_hqs_man_by'), Format.MP4 : self.config.get('general', 'cut_mp4s_man_by') } else: programs = { Format.AVI : self.config.get('general', 'cut_avis_by'), Format.HQ : self.config.get('general', 'cut_hqs_by'), Format.HD : self.config.get('general', 'cut_hqs_by'), Format.MP4 : self.config.get('general', 'cut_mp4s_by') } format = self.__get_format(filename) if format < 0: return -1, "Format konnte nicht bestimmt werden/wird noch nicht unterstützt." config_value = programs[format] if 'avidemux' in config_value: return Program.AVIDEMUX, config_value elif 'vdub' in config_value or 'VirtualDub' in config_value: return Program.VIRTUALDUB, config_value else: return -2, "Programm '%s' konnte nicht bestimmt werden. Es werden VirtualDub und Avidemux unterstützt." % config_value def __generate_filename(self, filename): """ generate filename for a cut video file. """ root, extension = splitext(basename(filename)) new_name = root + "-cut" + extension cut_video = join(self.config.get('general', 'folder_cut_avis'), new_name) return cut_video def __get_fps(self, filename): """ Gets the fps of a movie using mplayer. Returns without error: fps, None with error: None, error_message """ mplayer = self.config.get('general', 'mplayer') if not mplayer: return None, "Der Mplayer ist nicht angegeben. Dieser wird zur Bestimmung der FPS benötigt." try: process = subprocess.Popen([mplayer, "-vo", "null", "-frames", "1", "-nosound", filename], stdout=subprocess.PIPE) except OSError: return None, "MPlayer wurde nicht gefunden!" stdout = process.communicate()[0] for line in stdout.split('\n'): if "VIDEO" in line: try: return float(line.split()[2]), None except: return None, "FPS konnte nicht bestimmt werden " + line return None, "FPS konnte nicht bestimmt werden." def __get_aspect_ratio(self, filename): """ Gets the aspect ratio of a movie using mplayer. Returns without error: aspect_ratio, None with error: None, error_message """ mplayer = self.config.get('general', 'mplayer') if not mplayer: return None, "Der Mplayer ist nicht angegeben. Dieser wird zur Bestimmung der Aspekt Ratio benötigt." try: process = subprocess.Popen([mplayer, "-vo", "null", "-frames", "1", "-nosound", filename], stdout=subprocess.PIPE) except OSError: return None, "MPlayer wurde nicht gefunden!" stdout = process.communicate()[0] for line in stdout.split('\n'): if "Aspe" in line: if "1.78:1" in line or "0.56:1" in line: return "16:9", None elif "1.33:1" in line or "0.75:1" in line: return "4:3", None else: return None, "Aspekt konnte nicht bestimmt werden " + line return None, "Aspekt Ratio konnte nicht bestimmt werden." def __create_cutlist_virtualdub(self, filename): """ returns: cuts, error_message """ try: f = open(filename, 'r') except IOError: return None, "Die VirtualDub-Projektdatei konnte nicht gelesen werden.\nWurde das Projekt in VirtualDub nicht gespeichert?\n(Datei: %s)." % filename cuts_frames = [] # (start, duration) count = 0 for line in f.readlines(): if "VirtualDub.subset.AddRange" in line: try: start, duration = line[line.index('(') + 1 : line.index(')')].split(',') except (IndexError, ValueError), message: return None, "Konnte Schnitte nicht lesen, um Cutlist zu erstellen. (%s)" % message cuts_frames.append((int(start), int(duration))) if len(cuts_frames) == 0: return None, "Konnte keine Schnitte finden!" fileoperations.remove_file(filename) return cuts_frames, None
def __cut_file_avidemux(self, filename, program_config_value, cuts): format, ac3_file, bframe_delay = self.get_format(filename) # make file for avidemux2.5 scripting engine f = open("tmp.js", "w") f.writelines([ '//AD\n', 'var app = new Avidemux();\n', '\n' '//** Video **\n', 'app.load("%s");\n' % filename, '\n', 'app.clearSegments();\n' ]) for frame_start, frames_duration in cuts: if format == Format.HQ or format == Format.HD: #2-Frame-Delay for HQ,HD Format f.write("app.addSegment(0, %i, %i);\n" %(frame_start+2, frames_duration)) else: f.write("app.addSegment(0, %i, %i);\n" %(frame_start, frames_duration)) cut_video = self.generate_filename(filename) f.writelines([ '//** Postproc **\n', 'app.video.setPostProc(3,3,0);\n' ]) if self.config.get('general', 'smart'): f.write('app.video.codec("Copy","CQ=4","0");\n') f.writelines([ '//** Audio **\n', 'app.audio.reset();\n', 'app.audio.codec("copy",128,0,"");\n', 'app.audio.normalizeMode=0;\n', 'app.audio.normalizeValue=0;\n', 'app.audio.delay=0;\n', 'app.audio.mixer="NONE";\n', 'app.audio.scanVBR="";\n', 'app.setContainer="AVI";\n', 'setSuccess(app.save("%s"));\n' % cut_video ]) f.close() # start avidemux: try: avidemux = subprocess.Popen([program_config_value, "--nogui", "--force-smart", "--run", "tmp.js", "--quit"], stderr=subprocess.PIPE) except OSError: return None, "Avidemux konnte nicht aufgerufen werden: " + program_config_value self.gui.main_window.set_tasks_progress(50) while avidemux.poll() == None: time.sleep(1) # TODO: make it happen # line = avidemux.stderr.readline() # # if "Done:" in line: # progress = line[line.find(":") + 1 : line.find("%")] # self.gui.main_window.set_tasks_progress(int(progress)) # while events_pending(): main_iteration(False) fileoperations.remove_file('tmp.js') return cut_video, None
def create_cutlist(self, filename, program_config_value): """ read cuts from avidemux 2 and 3 returns cut_frames und cutlist_error """ format, ac3_file = self.get_format(filename) fps, dar, sar, max_frames, ac3_stream, error = self.analyse_mediafile(filename) if fps == None: return None, error # env my_env = os.environ.copy() my_env["LANG"] = "C" my_env["LC_COLLATE"] = "C" if ".avi" in filename and "avidemux3" in program_config_value: try: mkvmerge = subprocess.Popen( [self.config.get_program("mkvmerge"), "--ui-language", "en_US", "-o", filename + ".mkv", filename], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, env=my_env, ) self.show_progress(mkvmerge) returncode = mkvmerge.wait() if returncode != 0 and returncode != 1: return None, "Fehler beim Muxen zu MKV" except: return None, "mkvmerge konnte nicht aufgerufen werden." try: if os.path.isfile(filename + ".mkv"): avidemux = subprocess.Popen( [program_config_value, filename + ".mkv"], bufsize=-1, stdout=subprocess.PIPE ) else: avidemux = subprocess.Popen([program_config_value, filename], bufsize=-1, stdout=subprocess.PIPE) except: if os.path.isfile(filename + ".mkv"): fileoperations.remove_file(filename + ".mkv") return None, "Avidemux konnte nicht aufgerufen werden: " + program_config_value seg_lines = [] pts_correction = 0 for line in avidemux.stdout.readlines(): if line.startswith(" Seg"): # delete not interesting parts line = line[: line.find("audio")] parts = line.split(",") seg_id = int(parts[0].split(":")[-1]) if format == Format.HQ or format == Format.HD: start = int(parts[1].split(":")[-1]) - 2 else: start = int(parts[1].split(":")[-1]) size = int(parts[2].split(":")[-1]) seg_lines.append((seg_id, start, size)) # Avidemux3 # The first frame has a PTS >0, adjusting to 40 ms elif "[addReferenceVideo] The first frame has a PTS >0" in line: pts_correction = float(line.split(" ")[13]) * 1000 elif line.startswith(" We have "): seg_lines = [] elif line.startswith("Segment :"): line = line[line.find(":") + 1 :] seg_id = int(line.split("/")[0]) elif "duration :" in line: line = line[line.find(":") + 1 :] size = (float(line.split(" ")[0])) * fps / 1000000 elif "refStartPts :" in line: line = line[line.find(":") + 1 :] start = (float(line.split(" ")[0]) - pts_correction) * fps / 1000000 if start > 0: seg_lines.append((seg_id, start, size)) else: # correct values for first keyframe seg_lines.append((seg_id, 0.0, size - fps * pts_correction / 1000000)) else: pass # keep only necessary items seg_lines.reverse() temp_cuts = [] for seg_id, start, duration in seg_lines: if seg_id == 0: temp_cuts.append((start, duration)) break else: temp_cuts.append((start, duration)) temp_cuts.reverse() cuts_frames = [] count = 0 for start, duration in temp_cuts: cuts_frames.append((start, duration)) count += 1 if os.path.isfile(filename + ".mkv"): fileoperations.remove_file(filename + ".mkv") if len(cuts_frames) == 0: cutlist_error = "Es wurde nicht geschnitten." else: cutlist_error = None return cuts_frames, cutlist_error
def mp4(): # env my_env = os.environ.copy() my_env["LANG"] = "C" for count, filename in enumerate(filenames): # analyse file cutter = Cut(self.app, self.gui) fps, dar, sar, max_frames, ac3_stream, error = cutter.analyse_mediafile(filename) if fps == None: self.errors[filename] = error continue # mkvmerge pass yield 0, count self.progress = 0 if os.path.splitext(filename)[1] != ".mkv": mkvpass_file = fileoperations.make_unique_filename(os.path.splitext(filename)[0] + "_remux.mkv") try: p = subprocess.Popen( [ self.app.config.get_program("mkvmerge"), "--ui-language", "en_US", "-o", mkvpass_file, filename, ], stdout=subprocess.PIPE, env=my_env, ) except OSError: self.errors[filename] = "MKVmerge wurde nicht gefunden!" continue p.stdout.readline() line = "" while p.poll() == None: # read progress from stdout char = p.stdout.read(1) line += char progress = "" if char == ":": if "Error" in line or "Warning" in line: break while char != "%": char = p.stdout.read(1) progress += char try: self.progress = int(progress.strip(" %")) yield 4, self.progress except ValueError: pass exit_code = p.poll() if exit_code == 0 or exit_code == 1: pass else: error = p.stdout.readline() if os.path.exists(mkvpass_file): fileoperations.remove_file(mkvpass_file) try: error = error.split(":")[1] except IndexError: pass if "unknown type" in error: error = "Datei konnte nicht gelesen werden." self.errors[filename] = error continue else: mkvpass_file = filename # norm volume ausrechnen yield 5, count if self.Config["NormalizeAudio"] and self.Config["EncodeAudioToAAC"]: vol, error = self.get_norm_volume(filename) else: vol = 1.0 # ffmpeg pass yield 1, count self.progress = 0 ffmpegpass_file = fileoperations.make_unique_filename(os.path.splitext(filename)[0] + "_remux.mp4") if self.Config["EncodeAudioToAAC"]: if self.Config["EncodeOnlyFirstAudioToAAC"]: aacaudiostreams = "-c:a:0" else: aacaudiostreams = "-c:a" # convert first audio stream to aac ffmpeg = self.app.config.get_program("ffmpeg") if "nonfree" in ffmpeg: # nonfree ffmpeg version with fdk support available audiocodec = [ "-c:a", "copy", aacaudiostreams, "libfdk_aac", "-flags", "+qscale", "-profile:a:0", "aac_low", "-global_quality", "5", "-afterburner", "1", ] else: # only gpl version of ffmpeg available -> use standard aac codec audiocodec = [ "-c:a", "copy", aacaudiostreams, "aac", "-strict", "-2", "-profile:a:0", "aac_low", "-ab", "192k", "-cutoff", "18000", ] else: # only copy audio ffmpeg = path.get_tools_path("intern-ffmpeg") audiocodec = ["-c:a", "copy"] if self.Config["DownMixStereo"] and self.Config["EncodeAudioToAAC"]: audiocodec.extend(["-ac:0", "2"]) if ac3_stream == None: # no ac3 stream found - all streams are muxed map = ["-map", "0"] else: if self.Config["RemoveOtherAudioStreamsThanAC3"]: # mux only video and ac3 stream map = ["-map", "0:v", "-map", ac3_stream] else: map = ["-map", "0"] args = [ ffmpeg, "-loglevel", "info", "-y", "-drc_scale", "1.0", "-i", mkvpass_file, "-vcodec", "copy", "-af", "volume=volume=" + str(vol), "-vsync", "1", "-async", "1000", "-dts_delta_threshold", "100", "-vf", "fps=" + str(fps), ffmpegpass_file, ] map.extend(audiocodec) args[8:8] = map try: p = subprocess.Popen(args, stderr=subprocess.PIPE, universal_newlines=True) except OSError: self.errors[filename] = "FFMPEG (intern) wurde nicht gefunden!" if os.path.exists(mkvpass_file) and filename != mkvpass_file: fileoperations.remove_file(mkvpass_file) continue yield 4, 0 line = "" infos_match = re.compile(r"frame=\ {0,1}(\d{1,})") while p.poll() == None: line = p.stderr.readline() m = re.search(infos_match, line) if m and max_frames != 0: next = float(float(m.group(1)) / float(max_frames)) * 100 if next > self.progress: self.progress = next yield 4, self.progress else: pass exit_code = p.poll() if os.path.exists(mkvpass_file) and filename != mkvpass_file: fileoperations.remove_file(mkvpass_file) if exit_code == 0: if self.Config["DumpAVIs"]: yield 3, self.success new_filename = os.path.join( self.app.config.get("general", "folder_trash_avis"), os.path.basename(filename) ) if os.path.exists(new_filename): fileoperations.remove_file(new_filename) fileoperations.move_file(filename, self.app.config.get("general", "folder_trash_avis")) else: self.errors[filename] = "Fehler beim Erzeugen der MP4 Datei durch FFMPEG" if os.path.exists(ffmpegpass_file): fileoperations.remove_file(ffmpegpass_file) continue # mp4box - last turn self.progress = 0 mp4boxpass_file = fileoperations.make_unique_filename(os.path.splitext(filename)[0] + ".mp4") if self.Config["DontOptimizeMP4"]: os.rename(ffmpegpass_file, mp4boxpass_file) self.success += 1 continue yield 2, count try: p = subprocess.Popen( [ self.app.config.get_program("mp4box"), "-keep-all", "-new", "-packed", "-fps", str(fps), "-add", ffmpegpass_file, mp4boxpass_file, ], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, ) except OSError: self.errors[filename] = "MP4Box (intern) wurde nicht gefunden!" if os.path.exists(ffmpegpass_file): fileoperations.remove_file(ffmpegpass_file) continue yield 4, 0 infos_match = re.compile(r".*\((\d{2,})\/\d{2,}\).*") while p.poll() == None: line = p.stdout.read(60) m = re.search(infos_match, line) if m: self.progress = int(m.group(1)) yield 4, self.progress if "Importing" in line: yield 2, count elif "Writing" in line: yield 6, count else: pass exit_code = p.poll() if os.path.exists(ffmpegpass_file): fileoperations.remove_file(ffmpegpass_file) if exit_code == 0: self.success += 1 else: self.errors[filename] = "Fehler beim Erzeugen der MP4 Datei durch MP4Box"
def create_cutlist(self, filename, program_config_value): """ read cuts from avidemux 2 and 3 returns cut_frames und cutlist_error """ format, ac3_file, bframe_delay = self.get_format(filename) fps, dar, sar, max_frames, ac3_stream, error = self.analyse_mediafile( filename) if fps == None: return None, error # env my_env = os.environ.copy() my_env["LANG"] = "C" my_env["LC_COLLATE"] = "C" if '.avi' in filename and "avidemux3" in program_config_value: try: mkvmerge = subprocess.Popen([ self.config.get_program('mkvmerge'), '--ui-language', 'en_US', '-o', filename + '.mkv', filename ], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, env=my_env) self.show_progress(mkvmerge) returncode = mkvmerge.wait() if returncode != 0 and returncode != 1: return None, 'Fehler beim Muxen zu MKV' except: return None, "mkvmerge konnte nicht aufgerufen werden." try: if os.path.isfile(filename + '.mkv'): avidemux = subprocess.Popen( [program_config_value, filename + '.mkv'], bufsize=-1, stdout=subprocess.PIPE) else: avidemux = subprocess.Popen([program_config_value, filename], bufsize=-1, stdout=subprocess.PIPE) except: if os.path.isfile(filename + '.mkv'): fileoperations.remove_file(filename + '.mkv') return None, "Avidemux konnte nicht aufgerufen werden: " + program_config_value seg_lines = [] pts_correction = 0 # Avidemux3 if "avidemux3" in program_config_value: for line in avidemux.stdout.readlines(): # [addReferenceVideo] The first frame has a PTS >0, adjusting to 120 ms m_pts = re.search( '\\[addReferenceVideo\\] The first frame has a PTS >0.*?(\\d+)', line) if m_pts: pts_correction = float(m_pts.group(1)) * 1000 continue #Segment :0/1 m_seg = re.search('Segment :(\\d+)', line) if m_seg: seg_id = int(m_seg.group(1)) if seg_id == 0: seg_lines = [] continue # duration :10671440000 02:57:51,440 m_dur = re.search(' *?duration *?:(\\d+)', line) if m_dur: size = float(m_dur.group(1)) * fps / 1000000 continue # refStartPts :15740000 00:00:15,740 m_start = re.search(' *?refStartPts *?:(\\d+)', line) if m_start: start = (float(m_start.group(1)) - pts_correction) * fps / 1000000 if start > 0: seg_lines.append((seg_id, start, size)) else: # correct values for first keyframe seg_lines.append( (seg_id, 0.0, size - fps * pts_correction / 1000000)) continue else: #avidemux2 for line in avidemux.stdout.readlines(): if line.startswith(' Seg'): # delete not interesting parts line = line[:line.find("audio")] parts = line.split(',') seg_id = int(parts[0].split(':')[-1]) if format == Format.HQ or format == Format.HD: start = int(parts[1].split(':')[-1]) - 2 else: start = int(parts[1].split(':')[-1]) size = int(parts[2].split(':')[-1]) seg_lines.append((seg_id, start, size)) else: pass # keep only necessary items seg_lines.reverse() temp_cuts = [] for seg_id, start, duration in seg_lines: if seg_id == 0: temp_cuts.append((start, duration)) break else: temp_cuts.append((start, duration)) temp_cuts.reverse() cuts_frames = [] count = 0 for start, duration in temp_cuts: cuts_frames.append((start, duration)) count += 1 if os.path.isfile(filename + '.mkv'): fileoperations.remove_file(filename + '.mkv') if len(cuts_frames) == 0: cutlist_error = "Es wurde nicht geschnitten." else: cutlist_error = None return cuts_frames, cutlist_error
def __cut_file_avidemux(self, filename, program_config_value, cuts): format, ac3_file, bframe_delay = self.get_format(filename) # make file for avidemux2.5 scripting engine f = open("tmp.js", "w") f.writelines([ '//AD\n', 'var app = new Avidemux();\n', '\n' '//** Video **\n', 'app.load("%s");\n' % filename, '\n', 'app.clearSegments();\n' ]) for frame_start, frames_duration in cuts: if format == Format.HQ or format == Format.HD: #2-Frame-Delay for HQ,HD Format f.write("app.addSegment(0, %i, %i);\n" % (frame_start + 2, frames_duration)) else: f.write("app.addSegment(0, %i, %i);\n" % (frame_start, frames_duration)) cut_video = self.generate_filename(filename) f.writelines(['//** Postproc **\n', 'app.video.setPostProc(3,3,0);\n']) if self.config.get('general', 'smart'): f.write('app.video.codec("Copy","CQ=4","0");\n') f.writelines([ '//** Audio **\n', 'app.audio.reset();\n', 'app.audio.codec("copy",128,0,"");\n', 'app.audio.normalizeMode=0;\n', 'app.audio.normalizeValue=0;\n', 'app.audio.delay=0;\n', 'app.audio.mixer="NONE";\n', 'app.audio.scanVBR="";\n', 'app.setContainer="AVI";\n', 'setSuccess(app.save("%s"));\n' % cut_video ]) f.close() # start avidemux: try: avidemux = subprocess.Popen([ program_config_value, "--nogui", "--force-smart", "--run", "tmp.js", "--quit" ], stderr=subprocess.PIPE) except OSError: return None, "Avidemux konnte nicht aufgerufen werden: " + program_config_value self.gui.main_window.set_tasks_progress(50) while avidemux.poll() == None: time.sleep(1) # TODO: make it happen # line = avidemux.stderr.readline() # # if "Done:" in line: # progress = line[line.find(":") + 1 : line.find("%")] # self.gui.main_window.set_tasks_progress(int(progress)) # while events_pending(): main_iteration(False) fileoperations.remove_file('tmp.js') return cut_video, None
def _download(self): self.log = '' self.information['message_short'] = '' self.information['status'] = DownloadStatus.RUNNING if self.information['download_type'] == DownloadTypes.TORRENT: # download torrent if necessary torrent_filename = os.path.join( self._config.get('general', 'folder_new_otrkeys'), self.filename + '.torrent') if not os.path.exists(torrent_filename): url = 'http://81.95.11.2/torrents/' + self.filename + '.torrent' try: request.urlretrieve(url, torrent_filename) # read filename f = open(torrent_filename, 'r') line = f.readlines()[0] except IOError as error: self.information['status'] = DownloadStatus.ERROR self.information[ 'message_short'] = 'Torrentdatei konnte nicht geladen werden.' yield "Torrentdatei konnte nicht heruntergeladen werden (%s)!" % error return if "Hash wrong" in line: os.remove(torrent_filename) self.information['status'] = DownloadStatus.ERROR self.information[ 'message_short'] = 'OTR-Daten nicht korrekt!' yield 'OTR-Daten nicht korrekt!' return self.information['output'] = self._config.get( 'general', 'folder_new_otrkeys') command = [self._config.get_program('aria2c')] + self._config.get( 'downloader', 'aria2c_opts_torrent') + [ "-d", self.information['output'], "-T", torrent_filename ] yield "Ausgeführt wird:\n%s\n" % " ".join(command) try: self.__process = subprocess.Popen(command, stdout=subprocess.PIPE) except OSError as error: self.information['status'] = DownloadStatus.ERROR self.information[ 'message_short'] = 'Aria2c ist nicht installiert.' yield "Ist aria2c installiert? Der Befehl konnte nicht ausgeführt werden:\nFehlermeldung: %s" % error return while self.__process.poll() == None: line = self.__process.stdout.readline().strip() if "Checksum" in line: result = re.findall('Checksum:.*\((.*%)\)', line) if result: self.information[ 'message_short'] = 'Überprüfen...%s' % result[0] elif "SEEDING" in line: self.information['message_short'] = 'Seeden...' self.information[ 'status'] = DownloadStatus.SEEDING # _NOT_ DownloadStatus.FINISHED self.information['progress'] = 100 self.information['est'] = '' self.information['speed'] = '' self.information['seeders'] = None result = re.findall('ratio:(.*)\) ', line) if result: self.information['ratio'] = result[0] result = re.findall('UP:(.*)\((.*)\)', line) if result: self.information['upspeed'] = result[0][0] self.information['uploaded'] = result[0][1] elif "%" in line: self.information['message_short'] = '' # get size if not self.information['size']: try: # aria2c gives size always in MiB (hopefully) size = re.findall('SIZE:.*/(.*)MiB\(', line)[0] size = size.replace(',', '') size = int(round(float(size))) * 1024 * 1024 self.information['size'] = size yield line except: pass # get progress result = re.findall('([0-9]{1,3})%', line) if result: self.information['progress'] = int(result[0]) # get speed, est if "UP" in line: result = re.findall( 'SPD:(.*) UP:(.*)\((.*)\) ETA:(.*)]', line) if result: self.information['speed'] = result[0][0] self.information['upspeed'] = result[0][1] self.information['uploaded'] = result[0][2] self.information['est'] = result[0][3] else: result = re.findall('SPD:(.*) .*ETA:(.*)]', line) if result: self.information['speed'] = result[0][0] self.information['est'] = result[0][1] # get seeder info result = re.findall('SEED:([0-9]*) ', line) if result: self.information['seeders'] = result[0] else: yield line self.update_view() ### Process is terminated stdout = self.__process.stdout.read().strip() yield stdout # A torrent download only stops: # a) when the user clicks 'stop' # b) when an error occured if self.information['status'] != DownloadStatus.STOPPED: self.information['status'] = DownloadStatus.ERROR elif self.information['download_type'] == DownloadTypes.BASIC: self.information['output'] = self._config.get( 'general', 'folder_new_otrkeys') if self.information['preferred_downloader'] == 'wget': command = [ self._config.get_program('wget') ] + self._config.get('downloader', 'wget') + [ "-c", "-P", self.information['output'], self.link ] yield "Ausgeführt wird:\n%s\n" % " ".join(command) try: self.__process = subprocess.Popen(command, stderr=subprocess.PIPE) except OSError as error: self.information['status'] = DownloadStatus.ERROR self.information[ 'message_short'] = 'Wget ist nicht installiert.' yield "Ist Wget installiert? Der Befehl konnte nicht ausgeführt werden:\n%s" % error return while True: exit_code = self.__process.poll() if exit_code != None: if self.information['status'] != DownloadStatus.STOPPED: if exit_code == 0: self._finished() else: self.information[ 'status'] = DownloadStatus.ERROR break line = self.__process.stderr.readline().strip() if line: if not self.information['size']: result = re.findall(': ([0-9]*) \(', line) if result: self.information['size'] = int(result[0]) if "%" in line: result = re.findall('([0-9]{1,3})% (.*)[ =](.*)', line) if result: progress = int(result[0][0]) if self.information['progress'] == progress: continue else: self.information['progress'] = progress self.information['speed'] = result[0][1] if progress == 100: self._finished() else: self.information['est'] = result[0][2] else: yield line self.update_view() ### Process is terminated yield self.__process.stderr.read().strip() else: # Download with aria2c self.information[ 'message_short'] = 'Überprüfung der bereits heruntergeladen Datei ...' self.update_view() if 'otrkey' in self.filename and os.path.exists( self.information['output'] + '/' + self.filename): torrent_check_return = self._check_file_with_torrent() if torrent_check_return == 0: self.information[ 'message_short'] = '(OK):Download vollständig.' self._finished() command = [ self._config.get_program('aria2c') ] + self._config.get('downloader', 'aria2c_opts') + [ "-d", self.information['output'], '--retry-wait=30', '--max-tries=0', '--summary-interval=5', '--log', '-', '--log-level', 'info', self.link ] yield "Ausgeführt wird:\n%s\n" % " ".join(command) if self.information['status'] == DownloadStatus.RUNNING: self.log = '' self.information[ 'message_short'] = 'Versuche Download zu starten ... eventuell erst Warteschlange durchlaufen ...' self.update_view() try: self.__process = subprocess.Popen( command, stdout=subprocess.PIPE) except OSError as error: self.information['status'] = DownloadStatus.ERROR self.information[ 'message_short'] = 'Aria2c ist nicht installiert.' yield "Ist aria2c installiert? Der Befehl konnte nicht ausgeführt werden:\n%s" % error return while self.__process.poll() == None: time.sleep(0.1) line = self.__process.stdout.readline() if 'X-OTR-Queueposition' in line or 'errorCode=29' in line: match = re.search( 'X-OTR-Queueposition:\ ([0-9]{1,})', line) if match: self.information[ 'message_short'] = 'OTR Warteschlange - %s' % match.group( 0) self.update_view() else: if not 'OTR' in self.information[ 'message_short']: self.information[ 'message_short'] = line.strip() elif 'X-OTR-Error-Message' in line: self.information['message_short'] = line.strip() self.update_view() elif 'errorCode=' in line: self.information['message_short'] = line.strip() self.update_view() elif "%" in line: if "FileAlloc" in line: result = re.findall( 'FileAlloc:.*\(([0-9]{1,3}%)', line) self.information[ 'message_short'] = 'Datei wird angelegt...%s' % result[ 0] else: self.information['message_short'] = '' if not self.information['size']: try: # aria2c gives size always in MiB (hopefully) size = re.findall('.*/(.*)\(', line.strip())[0] size = size.strip('MiB') size = size.replace(',', '') size = int(round( float(size))) * 1024 * 1024 self.information['size'] = size yield line except: pass result = re.findall( '\(([0-9]{1,3})%\).*CN:([0-9]{1,5}).*DL:(.*) ETA:(.*)]', line) if result: self.information['progress'] = int( result[0][0]) self.information[ 'message_short'] = 'Verbindungen: %s' % result[ 0][1] self.information['speed'] = result[0][2] self.information['est'] = result[0][3] self.update_view() else: yield line.strip() ### Process is terminated stdout = self.__process.stdout.read().strip() yield stdout if not self.information['status'] in [ DownloadStatus.STOPPED, DownloadStatus.ERROR ]: time.sleep(1) # wait for log being updated - very ugly if '(INPR):wird heruntergeladen.' in self.log: self.information[ 'message_short'] = '(INPR):Download unvollständig.' self.information['progress'] = 0 elif '(INPR):download in-progress.' in self.log: self.information[ 'message_short'] = '(INPR):Download unvollständig.' self.information['progress'] = 0 elif '(OK):Herunterladen abgeschlossen.' in self.log: self.information[ 'message_short'] = '(OK):Download vollständig.' self._finished() elif '(OK):download completed.' in self.log: self.information[ 'message_short'] = '(OK):Download vollständig.' self._finished() # Download Test self.information[ 'message_short'] = 'Abschliessende Überprüfung der heruntergeladen Datei ...' self.update_view() if 'otrkey' in self.filename: torrent_check_return = self._check_file_with_torrent() if torrent_check_return == 0: self.information[ 'message_short'] = '(OK):Download vollständig.' self._finished() elif torrent_check_return == 7: self.information['status'] = DownloadStatus.ERROR self.information[ 'message_short'] = '(INPR):Download unvollständig - Datei hat Torrent-Prüfung nicht bestanden. z.B. durch fehlerhaften oder nicht mehr gültiger Link. Downloadabbruch oder defekte Mirrordatei.' self.information['progress'] = 0 else: self.information['status'] = DownloadStatus.ERROR self.information[ 'message_short'] = '(EER):Fehler bei der abschliessenden Überprüfung aufgetreten. Eventuell OTR nicht erreichbar.' self.information['progress'] = 0 elif self.information['download_type'] in [ DownloadTypes.OTR_DECODE, DownloadTypes.OTR_CUT ]: decoder = self._config.get_program('decoder') email = self._config.get('general', 'email') password = self._config.get('general', 'password') cache_dir = self._config.get('general', 'folder_trash_otrkeys') command = [ decoder, "-b", "0", "-n", "-i", self.link, "-e", email, "-p", password, "-c", cache_dir ] if self.information['download_type'] == DownloadTypes.OTR_CUT: self.information['output'] = self._config.get( 'general', 'folder_cut_avis') if not self.information['cutlist']: cutlist = Cutlist() cutlist.id = self.information['cutlist_id'] error = cutlist.download( self._config.get('general', 'server'), os.path.join(self.information['output'], self.filename)) if error: self.information['status'] = DownloadStatus.ERROR self.information[ 'message_short'] = 'Cutlist konnte nicht geladen werden.' yield error return self.information['cutlist'] = cutlist command += [ "-o", self.information['output'], "-C", self.information['cutlist'].local_filename ] else: self.information['output'] = self._config.get( 'general', 'folder_uncut_avis') command += ["-o", self.information['output']] # write command to log, but strip out email and password log = list(command) log[log.index('-p') + 1] = '*******' log[log.index('-e') + 1] = '*******' yield "Ausgeführt wird:\n%s\n" % " ".join(log) try: self.__process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) except OSError as error: self.information['status'] = DownloadStatus.ERROR self.information['message_short'] = 'Dekoder nicht gefunden.' yield "Der Pfad zum Dekoder scheint nicht korrekt zu sein. Der folgende Befehl konnte nicht ausgeführt werden\nFehlermeldung: %s" % error return line = '' while self.__process.poll() == None: char = self.__process.stdout.read(1) if char == '\r' or char == '\n': line = line.strip() if not line: continue if not "%" in line: yield line result = re.findall("([0-9]{1,3})%", line) if result: self.information['progress'] = int(result[0]) result = re.findall("[0-9]{1,3}%.*: (.*)", line) if result: self.information['speed'] = result[0] self.update_view() line = '' else: line += char ### Process is terminated stderr = self.__process.stderr.read().strip() if stderr: self.information['status'] = DownloadStatus.ERROR if "invalid option" in stderr: self.information[ 'message_short'] = 'Der Dekoder ist veraltet.' yield "Es ist ein veralteter Dekoder angegeben!\n" elif "maximale Anzahl": self.information[ 'message_short'] = 'Maximale Anzahl der Dekodierungen erreicht.' yield str(stderr, 'iso-8859-1') else: self.information['message_short'] = stderr yield stderr if not self.information['status'] in [ DownloadStatus.ERROR, DownloadStatus.STOPPED ]: self._finished() # remove otrkey and .segments file otrkey = os.path.join(cache_dir, self.filename) fileoperations.remove_file(otrkey, None) fileoperations.remove_file( os.path.join(cache_dir, self.filename + '.segments'), None) if self.information['download_type'] == DownloadTypes.OTR_CUT: # rename file to "cut" filename filename = os.path.join(self.information['output'], self.filename.rstrip(".otrkey")) new_filename, extension = os.path.splitext(filename) new_filename += ".cut" + extension fileoperations.rename_file(filename, new_filename, None) conclusion = FileConclusion(Action.DECODEANDCUT, otrkey=otrkey, uncut_video=filename) conclusion.decode.status = Status.OK conclusion.cut_video = new_filename conclusion.cut.cutlist = self.information['cutlist'] conclusion.cut.cutlist.read_from_file() conclusion.cut.status = Status.OK conclusion.cut.cut_action = Cut_action.CHOOSE_CUTLIST if self._config.get('general', 'rename_cut'): conclusion.cut.rename = self._app.rename_by_schema( self.filename.rstrip(".otrkey")) else: conclusion.cut.rename = os.path.basename(new_filename) self._app.conclusions_manager.add_conclusions(conclusion) self.update_view()
def __cut_file_virtualdub(self, filename, config_value, cuts=None, manually=False): format, ac3_file, bframe_delay = self.get_format(filename) fps, dar, sar, max_frames, ac3_stream, error = self.analyse_mediafile(filename) if sar == None or dar == None: return None, error # find wine cmd_exists = lambda x: any( os.access(os.path.join(pathx, x), os.X_OK) for pathx in os.environ["PATH"].split(os.pathsep)) if cmd_exists('wineconsole'): winecommand = 'wineconsole' elif cmd_exists('wine'): winecommand = 'wine' else: return None, "Wine konnte nicht aufgerufen werden." if format == Format.HQ: if self.config.get('general', 'h264_codec') == 'ffdshow': if dar == "16:9": comp_data = codec.get_comp_data_h264_169() else: comp_data = codec.get_comp_data_h264_43() compression = 'VirtualDub.video.SetCompression(0x53444646,0,10000,0);\n' elif self.config.get('general', 'h264_codec') == 'x264vfw': comp_data = codec.get_comp_data_x264vfw_dynamic(sar, self.config.get('general', 'x264vfw_hq_string')) compression = 'VirtualDub.video.SetCompression(0x34363278,0,10000,0);\n' elif self.config.get('general', 'h264_codec') == 'komisar': comp_data = codec.get_comp_data_komisar_dynamic(sar, self.config.get('general', 'komisar_hq_string')) compression = 'VirtualDub.video.SetCompression(0x34363278,0,10000,0);\n' else: return None, "Codec nicht unterstützt. Nur ffdshow, x264vfw und komisar unterstützt." elif format == Format.HD: if self.config.get('general', 'h264_codec') == 'ffdshow': if dar == "16:9": comp_data = codec.get_comp_data_hd_169() else: comp_data = codec.get_comp_data_hd_43() compression = 'VirtualDub.video.SetCompression(0x53444646,0,10000,0);\n' elif self.config.get('general', 'h264_codec') == 'x264vfw': comp_data = codec.get_comp_data_x264vfw_dynamic(sar, self.config.get('general', 'x264vfw_hd_string')) compression = 'VirtualDub.video.SetCompression(0x34363278,0,10000,0);\n' elif self.config.get('general', 'h264_codec') == 'komisar': comp_data = codec.get_comp_data_komisar_dynamic(sar, self.config.get('general', 'komisar_hd_string')) compression = 'VirtualDub.video.SetCompression(0x34363278,0,10000,0);\n' else: return None, "Codec nicht unterstützt. Nur ffdshow, x264vfw und komisar unterstützt." elif format == Format.MP4: if self.config.get('general', 'h264_codec') == 'komisar': comp_data = codec.get_comp_data_komsiar_dynamic(sar, self.config.get('general', 'komisar_mp4_string')) compression = 'VirtualDub.video.SetCompression(0x34363278,0,10000,0);\n' else: comp_data = codec.get_comp_data_x264vfw_dynamic(sar, self.config.get('general', 'x264vfw_mp4_string')) compression = 'VirtualDub.video.SetCompression(0x34363278,0,10000,0);\n' elif format == Format.AVI: comp_data = codec.get_comp_data_dx50() compression = 'VirtualDub.video.SetCompression(0x53444646,0,10000,0);\n' else: return None, "Format nicht unterstützt (Nur Avi DX50, HQ H264 und HD sind möglich)." # make file for virtualdub scripting engine if manually: # TODO: kind of a hack curr_dir = os.getcwd() try: os.chdir(os.path.dirname(config_value)) except OSError: return None, "VirtualDub konnte nicht aufgerufen werden: " + config_value self.gui.main_window.set_tasks_progress(50) while Gtk.events_pending(): Gtk.main_iteration() f = open("/tmp/tmp.vcf", "w") if not manually: f.write('VirtualDub.Open("%s");\n' % filename) if self.config.get('general', 'smart'): f.writelines([ 'VirtualDub.video.SetMode(1);\n', 'VirtualDub.video.SetSmartRendering(1);\n', compression, 'VirtualDub.video.SetCompData(%s);\n' % comp_data ]) else: f.write('VirtualDub.video.SetMode(0);\n') f.write('VirtualDub.subset.Clear();\n') if not manually: keyframes, error = self.get_keyframes_from_file(filename) if keyframes == None: return None, "Keyframes konnten nicht ausgelesen werden." for frame_start, frames_duration in cuts: # interval does not begin with keyframe if not frame_start in keyframes and format == Format.HQ or format == Format.HD: try: # get next keyframe frame_start_keyframe = self.get_keyframe_after_frame(keyframes, frame_start) except ValueError: frame_start_keyframe = -1 if frame_start + frames_duration > frame_start_keyframe: # 'Smart Rendering Part mit anschließenden kopierten Part' if frame_start_keyframe < 0: # copy end of file f.write("VirtualDub.subset.AddRange(%i, %i);\n" % (frame_start + 2, frames_duration - 2)) else: # smart rendering part (duration -2 due to smart rendering bug) f.write("VirtualDub.subset.AddRange(%i, %i);\n" % ( frame_start + 2, frame_start_keyframe - frame_start - 2)) # vd smart rendering bug if ac3_file != None: f.write("VirtualDub.subset.AddRange(%i, %i);\n" % (frame_start_keyframe - 1, 1)) f.write("VirtualDub.subset.AddRange(%i, %i);\n" % (frame_start_keyframe - 1, 1)) # copy part f.write("VirtualDub.subset.AddRange(%i, %i);\n" % ( frame_start_keyframe, frames_duration - (frame_start_keyframe - frame_start))) else: print('reiner Smart Rendering Part') try: # get next keyframe after the interval next_keyframe = self.get_keyframe_after_frame(keyframes, frame_start + frames_duration - 2) except ValueError: next_keyframe = -1 if next_keyframe - (frame_start + frames_duration) > 2: f.write("VirtualDub.subset.AddRange(%i, %i);\n" % (frame_start + 2, frames_duration)) else: # workaround for smart rendering bug f.write("VirtualDub.subset.AddRange(%i, %i);\n" % (frame_start + 2, frames_duration - 2)) if ac3_file != None: f.write("VirtualDub.subset.AddRange(%i, %i);\n" % (next_keyframe - 1, 1)) f.write("VirtualDub.subset.AddRange(%i, %i);\n" % (next_keyframe - 1, 1)) else: if not (frame_start + frames_duration) in keyframes and format == Format.HQ or format == Format.HD: # 'Kopieren mit keinem Keyframe am Ende' f.write("VirtualDub.subset.AddRange(%i, %i);\n" % (frame_start, frames_duration - 2)) # we all love workarounds if ac3_file != None: f.write("VirtualDub.subset.AddRange(%i, %i);\n" % (frame_start + frames_duration - 1, 1)) f.write("VirtualDub.subset.AddRange(%i, %i);\n" % (frame_start + frames_duration - 1, 1)) else: print('reines Kopieren') f.write("VirtualDub.subset.AddRange(%i, %i);\n" % (frame_start, frames_duration)) cut_video = self.generate_filename(filename, 1) f.writelines([ 'VirtualDub.SaveAVI("%s");\n' % cut_video, 'VirtualDub.Close();' ]) f.close() # start vdub if not os.path.exists(config_value): return None, "VirtualDub konnte nicht aufgerufen werden: " + config_value if manually: win_filename = "Z:" + filename.replace(r"/", r"\\") command = 'VirtualDub.exe /s Z:\\\\tmp\\\\tmp.vcf "%s"' % win_filename else: command = "%s /s Z:\\\\tmp\\\\tmp.vcf /x" % config_value if 'intern-VirtualDub' in config_value: command = 'WINEPREFIX=' + os.path.dirname(config_value) + '/wine' + " " + winecommand + " " + command else: command = winecommand + " " + command try: vdub = subprocess.Popen(command, shell=True) except OSError: return None, "VirtualDub konnte nicht aufgerufen werden: " + config_value while vdub.poll() == None: time.sleep(1) while Gtk.events_pending(): Gtk.main_iteration() fileoperations.remove_file('/tmp/tmp.vcf') if manually: os.chdir(curr_dir) return None, None return cut_video, None
def mkvmerge(): # env my_env = os.environ.copy() my_env["LANG"] = "C" for count, filename in enumerate(filenames): yield 0, count yield 3, 0 self.progress = 0 #analyse file cutter = Cut(self.app, self.gui) fps, dar, sar, max_frames, ac3_stream, error = cutter.analyse_mediafile( filename) if fps == None: self.errors[filename] = error continue # encode aac with ffmpeg if self.Config['EncodeAudioToAAC']: #norm volume ausrechnen yield 5, count if self.Config['NormalizeAudio'] and self.Config[ 'EncodeAudioToAAC']: vol, error = self.get_norm_volume(filename) else: vol = 1.0 # ffmpeg pass yield 1, count self.progress = 0 ffmpegpass_file = fileoperations.make_unique_filename( os.path.splitext(filename)[0] + "_remux.mkv") # convert first audio stream to aac if self.Config['EncodeOnlyFirstAudioToAAC']: aacaudiostreams = '-c:a:0' else: aacaudiostreams = '-c:a' # convert first audio stream to aac ffmpeg = self.app.config.get_program('ffmpeg') if 'nonfree' in ffmpeg: # nonfree ffmpeg version with fdk support available audiocodec = [ '-c:a', 'copy', aacaudiostreams, 'libfdk_aac', '-flags', '+qscale', '-profile:a:0', 'aac_low', '-global_quality', '5', '-afterburner', '1' ] else: # only gpl version of ffmpeg available -> use standard aac codec audiocodec = [ '-c:a', 'copy', aacaudiostreams, 'aac', '-strict', '-2', '-profile:a:0', 'aac_low', '-ab', '192k', '-cutoff', '18000' ] if self.Config['DownMixStereo'] and self.Config[ 'EncodeAudioToAAC']: audiocodec.extend(['-ac:0', '2']) if ac3_stream == None: # no ac3 stream found - all streams are muxed map = ['-map', '0'] else: if self.Config['RemoveOtherAudioStreamsThanAC3']: # mux only video and ac3 stream map = ['-map', '0:v', '-map', ac3_stream] else: map = ['-map', '0'] args = [ ffmpeg, "-loglevel", "info", "-y", "-drc_scale", "1.0", "-i", filename, "-vn", '-af', 'volume=volume=' + str(vol), "-vsync", "1", '-async', '1000', "-dts_delta_threshold", "100", "-vf", "fps=" + str(fps), '-threads', '0', ffmpegpass_file ] map.extend(audiocodec) args[8:8] = map try: p = subprocess.Popen(args, stderr=subprocess.PIPE, universal_newlines=True) except OSError: self.errors[ filename] = "FFMPEG (intern) wurde nicht gefunden!" continue yield 4, 0 line = "" infos_match = re.compile( r"time=(\d{2,}):(\d{2,}):(\d{2,}.\d{2,})") while p.poll() == None: line = p.stderr.readline() m = re.search(infos_match, line) if m and max_frames != 0: frame = (float(m.group(1)) * 3600 + float(m.group(2)) * 60 + float(m.group(3))) * fps next = float(frame / float(max_frames)) * 100 if next > self.progress: self.progress = next yield 4, self.progress else: pass exit_code = p.poll() if exit_code == 0: pass else: self.errors[ filename] = "Fehler beim Erzeugen der MP4 Datei durch FFMPEG" if os.path.exists(ffmpegpass_file): fileoperations.remove_file(ffmpegpass_file) continue # mkvmerge pass yield 2, count self.progress = 0 mkvpass_file = fileoperations.make_unique_filename( os.path.splitext(filename)[0] + ".mkv") if self.Config['EncodeAudioToAAC']: args = [ self.app.config.get_program('mkvmerge'), '--engage', 'no_cue_duration', '--engage', 'no_cue_relative_position', '--ui-language', 'en_US', "-o", mkvpass_file, '-A', filename, '-D', ffmpegpass_file ] else: if self.Config[ 'RemoveOtherAudioStreamsThanAC3'] and ac3_stream: args = [ self.app.config.get_program('mkvmerge'), '--engage', 'no_cue_duration', '--engage', 'no_cue_relative_position', '--ui-language', 'en_US', "-o", mkvpass_file, '-a', ac3_stream[2], filename ] else: args = [ self.app.config.get_program('mkvmerge'), '--engage', 'no_cue_duration', '--engage', 'no_cue_relative_position', '--ui-language', 'en_US', "-o", mkvpass_file, filename ] p = subprocess.Popen(args, stdout=subprocess.PIPE, env=my_env) p.stdout.readline() line = "" while p.poll() == None: # read progress from stdout char = p.stdout.read(1) line += char progress = '' if char == ':': if "Error" in line or "Warning" in line: break while char != '%': char = p.stdout.read(1) progress += char try: self.progress = int(progress.strip(' %')) yield 3, self.progress except ValueError: pass exit_code = p.poll() if exit_code == 0 or exit_code == 1: self.success += 1 if self.Config['EncodeAudioToAAC']: fileoperations.remove_file(ffmpegpass_file) if self.Config['DumpAVIs']: if self.Config['DumpAVIs_delete']: fileoperations.remove_file(filename) else: new_filename = os.path.join( self.app.config.get('general', 'folder_trash_avis'), os.path.basename(filename)) if os.path.exists(new_filename): fileoperations.remove_file(new_filename) fileoperations.move_file( filename, self.app.config.get('general', 'folder_trash_avis')) else: error = p.stdout.readline() try: error = error.split(":")[1] except IndexError: pass if "unknown type" in error: error = "Datei konnte nicht gelesen werden." self.errors[filename] = error
def show_conclusions(self): conclusions = self.app.gui.dialog_conclusion._run( self.conclusions, self.app.rename_by_schema, self.app.config.get('general', 'folder_archive')) self.app.gui.main_window.builder.get_object('box_conclusion').hide() self.conclusions = [] # create cutlists cutlists = [] for conclusion in conclusions: if conclusion.action == Action.DECODE: continue print "[Conclusion] for file ", conclusion.uncut_video # rename print "[Conclusion] Rename?" if conclusion.cut.rename: print "[Conclusion] true" extension = os.path.splitext(conclusion.cut_video)[1] if not conclusion.cut.rename.endswith(extension): conclusion.cut.rename += extension new_filename = os.path.join( self.app.config.get('general', 'folder_cut_avis'), conclusion.cut.rename.replace('/', '_')) new_filename = fileoperations.make_unique_filename( new_filename) if conclusion.cut_video != new_filename: conclusion.cut_video = fileoperations.rename_file( conclusion.cut_video, new_filename) # move cut video to archive print "[Conclusion] Move to archive?" if conclusion.cut.archive_to: print "[Conclusion] true" fileoperations.move_file(conclusion.cut_video, conclusion.cut.archive_to) # move uncut video to trash if it's ok print "[Conclusion] Move to trash?" if conclusion.cut.status == Status.OK and conclusion.cut.delete_uncut: print "[Conclusion] true" if os.path.exists(conclusion.uncut_video): # move to trash target = self.app.config.get('general', 'folder_trash_avis') conclusion.uncut_video = fileoperations.move_file( conclusion.uncut_video, target) if os.path.exists(conclusion.ac3_file): target = self.app.config.get('general', 'folder_trash_avis') fileoperations.move_file(conclusion.ac3_file, target) # remove local cutlists print "[Conclusion] Remove local cutlist?" if self.app.config.get('general', 'delete_cutlists'): print "[Conclusion] true" if conclusion.cut.cutlist.local_filename: if os.path.exists( conclusion.cut.cutlist.local_filename): fileoperations.remove_file( conclusion.cut.cutlist.local_filename) print "[Conclusion] Create cutlist?" if conclusion.cut.create_cutlist: print "[Conclusion] true" if "VirtualDub" in conclusion.cut.cutlist.intended_app: intended_app_name = "VirtualDub" else: intended_app_name = "Avidemux" if not conclusion.cut.cutlist.local_filename: conclusion.cut.cutlist.local_filename = self.app.config.get( 'general', 'folder_uncut_avis') + '/' + os.path.basename( conclusion.uncut_video) + ".cutlist" conclusion.cut.cutlist.author = self.app.config.get( 'general', 'cutlist_username') conclusion.cut.cutlist.intended_version = open( path.getdatapath("VERSION"), 'r').read().strip() conclusion.cut.cutlist.smart = self.app.config.get( 'general', 'smart') conclusion.cut.cutlist.write_local_cutlist( conclusion.uncut_video, intended_app_name, conclusion.cut.my_rating) if conclusion.cut.upload_cutlist: cutlists.append(conclusion.cut.cutlist) # upload cutlists: def upload(): error_messages = [] for cutlist in cutlists: error_message = cutlist.upload( self.app.config.get('general', 'server'), self.app.config.get('general', 'cutlist_hash')) if error_message: error_messages.append(error_message) else: if self.app.config.get('general', 'delete_cutlists'): fileoperations.remove_file(cutlist.local_filename) count = len(cutlists) message = "Es wurden %s/%s Cutlisten hochgeladen!" % ( str(count - len(error_messages)), str(count)) if len(error_messages) > 0: message += " (" + ", ".join(error_messages) + ")" yield message if len(cutlists) > 0: print "[Conclusion] Upload cutlists" if self.app.gui.question_box( "Soll(en) %s Cutlist(en) hochgeladen werden?" % len(cutlists)): def change_status(message): self.app.gui.main_window.change_status(0, message) GeneratorTask(upload, change_status).start() # rate cutlists def rate(): yield 0 # fake generator messages = [] count = 0 for conclusion in conclusions: if conclusion.action == Action.DECODE: continue if conclusion.cut.my_rating > -1: print "Rate with ", conclusion.cut.my_rating success, message = conclusion.cut.cutlist.rate( conclusion.cut.my_rating, self.app.config.get('general', 'server')) if success: count += 1 else: messages += [message] if count > 0 or len(messages) > 0: if count == 0: text = "Es wurde keine Cutlist bewertet!" if count == 1: text = "Es wurde 1 Cutlist bewertet!" else: text = "Es wurden %s Cutlisten bewertet!" % count if len(messages) > 0: text += " (Fehler: %s)" % ", ".join(messages) self.app.gui.main_window.change_status(0, text) print "[Conclusion] Rate cutlists" GeneratorTask(rate).start()
def create_cutlist(self, filename, program_config_value): """ read cuts from avidemux 2 and 3 returns cut_frames und cutlist_error """ format, ac3_file, bframe_delay = self.get_format(filename) fps, dar, sar, max_frames, ac3_stream, error = self.analyse_mediafile(filename) if fps == None: return None, error # env my_env = os.environ.copy() my_env["LANG"] = "C" my_env["LC_COLLATE"] = "C" if '.avi' in filename and "avidemux3" in program_config_value: try: mkvmerge = subprocess.Popen([self.config.get_program('mkvmerge'), '--ui-language', 'en_US', '-o', filename+'.mkv', filename], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, env=my_env) self.show_progress(mkvmerge) returncode = mkvmerge.wait() if returncode != 0 and returncode != 1: return None, 'Fehler beim Muxen zu MKV' except: return None, "mkvmerge konnte nicht aufgerufen werden." try: if os.path.isfile(filename+'.mkv'): avidemux = subprocess.Popen([program_config_value, filename+'.mkv'], bufsize=-1 ,stdout=subprocess.PIPE) else: avidemux = subprocess.Popen([program_config_value, filename], bufsize=-1 ,stdout=subprocess.PIPE) except: if os.path.isfile(filename+'.mkv'): fileoperations.remove_file(filename+'.mkv') return None, "Avidemux konnte nicht aufgerufen werden: " + program_config_value seg_lines = [] pts_correction = 0 # Avidemux3 if "avidemux3" in program_config_value: for line in avidemux.stdout.readlines(): # [addReferenceVideo] The first frame has a PTS >0, adjusting to 120 ms m_pts = re.search('\\[addReferenceVideo\\] The first frame has a PTS >0.*?(\\d+)', line) if m_pts: pts_correction = float(m_pts.group(1))*1000 continue #Segment :0/1 m_seg = re.search('Segment :(\\d+)', line) if m_seg: seg_id = int(m_seg.group(1)) if seg_id == 0: seg_lines = [] continue # duration :10671440000 02:57:51,440 m_dur = re.search(' *?duration *?:(\\d+)', line) if m_dur: size = float(m_dur.group(1))*fps/1000000 continue # refStartPts :15740000 00:00:15,740 m_start = re.search(' *?refStartPts *?:(\\d+)', line) if m_start: start = (float(m_start.group(1))-pts_correction)*fps/1000000 if start > 0: seg_lines.append((seg_id, start, size)) else: # correct values for first keyframe seg_lines.append((seg_id, 0.0, size-fps*pts_correction/1000000)) continue else: #avidemux2 for line in avidemux.stdout.readlines(): if line.startswith(' Seg'): # delete not interesting parts line = line[:line.find("audio")] parts = line.split(',') seg_id = int(parts[0].split(':')[-1]) if format == Format.HQ or format == Format.HD: start = int(parts[1].split(':')[-1])-2 else: start = int(parts[1].split(':')[-1]) size = int(parts[2].split(':')[-1]) seg_lines.append((seg_id, start, size)) else: pass # keep only necessary items seg_lines.reverse() temp_cuts = [] for seg_id, start, duration in seg_lines: if seg_id == 0: temp_cuts.append((start, duration)) break else: temp_cuts.append((start, duration)) temp_cuts.reverse() cuts_frames = [] count = 0 for start, duration in temp_cuts: cuts_frames.append((start, duration)) count += 1 if os.path.isfile(filename+'.mkv'): fileoperations.remove_file(filename+'.mkv') if len(cuts_frames) == 0: cutlist_error = "Es wurde nicht geschnitten." else: cutlist_error = None return cuts_frames, cutlist_error
def mux_ac3(self, filename, cut_video, ac3_file, cutlist): # cuts the ac3 and muxes it with the avi into an mkv mkvmerge = self.config.get_program('mkvmerge') root, extension = os.path.splitext(filename) mkv_file = os.path.splitext(cut_video)[0] + ".mkv" # env my_env = os.environ.copy() my_env["LANG"] = "C" # creates the timecodes string for splitting the .ac3 with mkvmerge timecodes = (','.join([ self.get_timecode(start) + ',' + self.get_timecode(start + duration) for start, duration in cutlist.cuts_seconds ])) # splitting .ac3. Every second fragment will be used. # return_value = subprocess.call([mkvmerge, "--split", "timecodes:" + timecodes, "-o", root + "-%03d.mka", ac3_file]) try: blocking_process = subprocess.Popen([ mkvmerge, '--ui-language', 'en_US', "--split", "timecodes:" + timecodes, "-o", root + "-%03d.mka", ac3_file ], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, env=my_env) except OSError as e: return None, e.strerror + ": " + mkvmerge return_value = blocking_process.wait() # return_value=0 is OK, return_value=1 means a warning. Most probably non-ac3-data that has been omitted. # TODO: Is there some way to pass this warning to the conclusion dialog? if return_value != 0 and return_value != 1: return None, None, str(return_value) if len(cutlist.cuts_seconds ) == 1: # Only the second fragment is needed. Delete the rest. fileoperations.rename_file(root + "-002.mka", root + ".mka") fileoperations.remove_file(root + "-001.mka") if os.path.isfile(root + "-003.mka"): fileoperations.remove_file(root + "-003.mka") else: # Concatenating every second fragment. command = [mkvmerge, "-o", root + ".mka", root + "-002.mka"] command[len(command):] = [ "+" + root + "-%03d.mka" % (2 * n) for n in range(2, len(cutlist.cuts_seconds) + 1) ] # return_value = subprocess.call(command) try: blocking_process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, env=my_env) except OSError as e: return None, e.strerror + ": " + mkvmerge return_value = blocking_process.wait() if return_value != 0: # There should be no warnings here return None, None, str(return_value) for n in range(1, 2 * len(cutlist.cuts_seconds) + 2): # Delete all temporary audio fragments if os.path.isfile(root + "-%03d.mka" % n): fileoperations.remove_file(root + "-%03d.mka" % n) # Mux the cut .avi with the resulting audio-file into mkv_file # TODO: Is there some way to pass possible warnings to the conclusion dialog? # return_value = subprocess.call([mkvmerge, "-o", mkv_file, cut_video, root + ".mka"]) try: blocking_process = subprocess.Popen( [mkvmerge, "-o", mkv_file, cut_video, root + ".mka"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, env=my_env) except OSError as e: return None, e.strerror + ": " + mkvmerge return_value = blocking_process.wait() if return_value != 0 and return_value != 1: return None, None, str(return_value) fileoperations.remove_file(root + ".mka") # Delete remaining temporary files fileoperations.remove_file(cut_video) return mkv_file, ac3_file, None
def mp4(): # env my_env = os.environ.copy() my_env["LANG"] = "C" for count, filename in enumerate(filenames): # analyse file cutter = Cut(self.app, self.gui) fps, dar, sar, max_frames, ac3_stream, error = cutter.analyse_mediafile( filename) if fps == None: self.errors[filename] = error continue # mkvmerge pass yield 0, count self.progress = 0 if os.path.splitext(filename)[1] != '.mkv': mkvpass_file = fileoperations.make_unique_filename( os.path.splitext(filename)[0] + "_remux.mkv") try: p = subprocess.Popen([ self.app.config.get_program('mkvmerge'), '--ui-language', 'en_US', "-o", mkvpass_file, filename ], stdout=subprocess.PIPE, env=my_env) except OSError: self.errors[ filename] = "MKVmerge wurde nicht gefunden!" continue p.stdout.readline() line = "" while p.poll() == None: # read progress from stdout char = p.stdout.read(1) line += char.decode('utf-8') progress = '' if char == ':': if "Error" in line or "Warning" in line: break while char != '%': char = p.stdout.read(1) progress += char try: self.progress = int(progress.strip(' %')) yield 4, self.progress except ValueError: pass exit_code = p.poll() if exit_code == 0 or exit_code == 1: pass else: error = p.stdout.readline() if os.path.exists(mkvpass_file): fileoperations.remove_file(mkvpass_file) try: error = error.split(":")[1] except IndexError: pass if "unknown type" in error: error = "Datei konnte nicht gelesen werden." self.errors[filename] = error continue else: mkvpass_file = filename # norm volume ausrechnen yield 5, count if self.Config['NormalizeAudio'] and self.Config[ 'EncodeAudioToAAC']: vol, error = self.get_norm_volume(filename) else: vol = 1.0 # ffmpeg pass yield 1, count self.progress = 0 ffmpegpass_file = fileoperations.make_unique_filename( os.path.splitext(filename)[0] + "_remux.mp4") if self.Config['EncodeAudioToAAC']: if self.Config['EncodeOnlyFirstAudioToAAC']: aacaudiostreams = '-c:a:0' else: aacaudiostreams = '-c:a' # convert first audio stream to aac ffmpeg = self.app.config.get_program('ffmpeg') if 'nonfree' in ffmpeg: # nonfree ffmpeg version with fdk support available audiocodec = [ '-c:a', 'copy', aacaudiostreams, 'libfdk_aac', '-flags', '+qscale', '-profile:a:0', 'aac_low', '-global_quality', '5', '-afterburner', '1' ] else: # only gpl version of ffmpeg available -> use standard aac codec audiocodec = [ '-c:a', 'copy', aacaudiostreams, 'aac', '-strict', '-2', '-profile:a:0', 'aac_low', '-ab', '192k', '-cutoff', '18000' ] else: # only copy audio ffmpeg = path.get_tools_path('intern-ffmpeg') audiocodec = ['-c:a', 'copy'] if self.Config['DownMixStereo'] and self.Config[ 'EncodeAudioToAAC']: audiocodec.extend(['-ac:0', '2']) if ac3_stream == None: # no ac3 stream found - all streams are muxed map = ['-map', '0'] else: if self.Config['RemoveOtherAudioStreamsThanAC3']: # mux only video and ac3 stream map = ['-map', '0:v', '-map', ac3_stream] else: map = ['-map', '0'] args = [ ffmpeg, "-loglevel", "info", "-y", "-drc_scale", "1.0", "-i", mkvpass_file, "-vcodec", "copy", '-af', 'volume=volume=' + str(vol), "-vsync", "1", '-async', '1000', "-dts_delta_threshold", "100", "-vf", "fps=" + str(fps), ffmpegpass_file ] map.extend(audiocodec) args[8:8] = map try: p = subprocess.Popen(args, stderr=subprocess.PIPE, universal_newlines=True) except OSError: self.errors[ filename] = "FFMPEG (intern) wurde nicht gefunden!" if os.path.exists( mkvpass_file) and filename != mkvpass_file: fileoperations.remove_file(mkvpass_file) continue yield 4, 0 line = "" infos_match = re.compile(r"frame=\ {0,1}(\d{1,})") while p.poll() == None: line = p.stderr.readline() m = re.search(infos_match, line) if m and max_frames != 0: next = float( float(m.group(1)) / float(max_frames)) * 100 if next > self.progress: self.progress = next yield 4, self.progress else: pass exit_code = p.poll() if os.path.exists(mkvpass_file) and filename != mkvpass_file: fileoperations.remove_file(mkvpass_file) if exit_code == 0: if self.Config['DumpAVIs']: yield 3, self.success new_filename = os.path.join( self.app.config.get('general', 'folder_trash_avis'), os.path.basename(filename)) if os.path.exists(new_filename): fileoperations.remove_file(new_filename) fileoperations.move_file( filename, self.app.config.get('general', 'folder_trash_avis')) else: self.errors[ filename] = "Fehler beim Erzeugen der MP4 Datei durch FFMPEG" if os.path.exists(ffmpegpass_file): fileoperations.remove_file(ffmpegpass_file) continue # mp4box - last turn self.progress = 0 mp4boxpass_file = fileoperations.make_unique_filename( os.path.splitext(filename)[0] + ".mp4") if self.Config['DontOptimizeMP4']: os.rename(ffmpegpass_file, mp4boxpass_file) self.success += 1 continue yield 2, count try: p = subprocess.Popen([ self.app.config.get_program('mp4box'), "-keep-all", "-new", "-packed", "-fps", str(fps), "-add", ffmpegpass_file, mp4boxpass_file ], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) except OSError: self.errors[ filename] = "MP4Box (intern) wurde nicht gefunden!" if os.path.exists(ffmpegpass_file): fileoperations.remove_file(ffmpegpass_file) continue yield 4, 0 infos_match = re.compile(r".*\((\d{2,})\/\d{2,}\).*") while p.poll() == None: line = p.stdout.read(60) line = line.decode('utf-8') m = re.search(infos_match, line) if m: self.progress = int(m.group(1)) yield 4, self.progress if 'Importing' in line: yield 2, count elif 'Writing' in line: yield 6, count else: pass exit_code = p.poll() if os.path.exists(ffmpegpass_file): fileoperations.remove_file(ffmpegpass_file) if exit_code == 0: self.success += 1 else: self.errors[ filename] = "Fehler beim Erzeugen der MP4 Datei durch MP4Box"
class CutVirtualdub(Cut): def __init__(self, app, gui): self.update_list = True self.app = app self.config = app.config self.gui = gui def __del__(self): # clean up pass def cut_file_by_cutlist(self, filename, cutlist=None, program_config_value=None): return self.__cut_file_virtualdub(filename, program_config_value, cutlist.cuts_frames) def create_cutlist(self, filename, program_config_value): cut_video_is_none, error = self.__cut_file_virtualdub(filename, program_config_value, cuts=None, manually=True) if error != None: return None, error format, ac3_file, bframe_delay = self.get_format(filename) cuts_frames, cutlist_error = self.__create_cutlist_virtualdub(os.path.join(self.config.get('general', 'folder_uncut_avis'), "cutlist.vcf"), format) return cuts_frames, cutlist_error def __cut_file_virtualdub(self, filename, config_value, cuts=None, manually=False): format, ac3_file, bframe_delay = self.get_format(filename) fps, dar, sar, max_frames, ac3_stream, error = self.analyse_mediafile(filename) if sar == None or dar == None: return None, error # find wine cmd_exists = lambda x: any(os.access(os.path.join(pathx, x), os.X_OK) for pathx in os.environ["PATH"].split(os.pathsep)) if cmd_exists('wineconsole'): winecommand = 'wineconsole' elif cmd_exists('wine'): winecommand = 'wine' else: return None, "Wine konnte nicht aufgerufen werden." if format == Format.HQ: if self.config.get('general', 'h264_codec') == 'ffdshow': if dar == "16:9": comp_data = codec.get_comp_data_h264_169() else: comp_data = codec.get_comp_data_h264_43() compression = 'VirtualDub.video.SetCompression(0x53444646,0,10000,0);\n' elif self.config.get('general', 'h264_codec') == 'x264vfw': comp_data = codec.get_comp_data_x264vfw_dynamic(sar,self.config.get('general', 'x264vfw_hq_string')) compression = 'VirtualDub.video.SetCompression(0x34363278,0,10000,0);\n' elif self.config.get('general', 'h264_codec') == 'komisar': comp_data = codec.get_comp_data_komisar_dynamic(sar,self.config.get('general', 'komisar_hq_string')) compression = 'VirtualDub.video.SetCompression(0x34363278,0,10000,0);\n' else: return None, "Codec nicht unterstützt. Nur ffdshow, x264vfw und komisar unterstützt." elif format == Format.HD: if self.config.get('general', 'h264_codec') == 'ffdshow': if dar == "16:9": comp_data = codec.get_comp_data_hd_169() else: comp_data = codec.get_comp_data_hd_43() compression = 'VirtualDub.video.SetCompression(0x53444646,0,10000,0);\n' elif self.config.get('general', 'h264_codec') == 'x264vfw': comp_data = codec.get_comp_data_x264vfw_dynamic(sar,self.config.get('general', 'x264vfw_hd_string')) compression = 'VirtualDub.video.SetCompression(0x34363278,0,10000,0);\n' elif self.config.get('general', 'h264_codec') == 'komisar': comp_data = codec.get_comp_data_komisar_dynamic(sar,self.config.get('general', 'komisar_hd_string')) compression = 'VirtualDub.video.SetCompression(0x34363278,0,10000,0);\n' else: return None, "Codec nicht unterstützt. Nur ffdshow, x264vfw und komisar unterstützt." elif format == Format.MP4: if self.config.get('general', 'h264_codec') == 'komisar': comp_data = codec.get_comp_data_komsiar_dynamic(sar,self.config.get('general', 'komisar_mp4_string')) compression = 'VirtualDub.video.SetCompression(0x34363278,0,10000,0);\n' else: comp_data = codec.get_comp_data_x264vfw_dynamic(sar,self.config.get('general', 'x264vfw_mp4_string')) compression = 'VirtualDub.video.SetCompression(0x34363278,0,10000,0);\n' elif format == Format.AVI: comp_data = codec.get_comp_data_dx50() compression = 'VirtualDub.video.SetCompression(0x53444646,0,10000,0);\n' else: return None, "Format nicht unterstützt (Nur Avi DX50, HQ H264 und HD sind möglich)." # make file for virtualdub scripting engine if manually: # TODO: kind of a hack curr_dir = os.getcwd() try: os.chdir(os.path.dirname(config_value)) except OSError: return None, "VirtualDub konnte nicht aufgerufen werden: " + config_value self.gui.main_window.set_tasks_progress(50) while events_pending(): main_iteration(False) f = open("/tmp/tmp.vcf", "w") if not manually: f.write('VirtualDub.Open("%s");\n' % filename) if self.config.get('general', 'smart'): f.writelines([ 'VirtualDub.video.SetMode(1);\n', 'VirtualDub.video.SetSmartRendering(1);\n', compression, 'VirtualDub.video.SetCompData(%s);\n' % comp_data ]) else: f.write('VirtualDub.video.SetMode(0);\n') f.write('VirtualDub.subset.Clear();\n') if not manually: keyframes, error = self.get_keyframes_from_file(filename) if keyframes == None: return None, "Keyframes konnten nicht ausgelesen werden." for frame_start, frames_duration in cuts: # interval does not begin with keyframe if not frame_start in keyframes and format == Format.HQ or format == Format.HD: try: # get next keyframe frame_start_keyframe =self.get_keyframe_after_frame(keyframes, frame_start) except ValueError: frame_start_keyframe = -1 if frame_start+frames_duration > frame_start_keyframe: # 'Smart Rendering Part mit anschließenden kopierten Part' if frame_start_keyframe < 0: # copy end of file f.write("VirtualDub.subset.AddRange(%i, %i);\n" % (frame_start+2, frames_duration-2)) else: # smart rendering part (duration -2 due to smart rendering bug) f.write("VirtualDub.subset.AddRange(%i, %i);\n" % (frame_start+2, frame_start_keyframe-frame_start-2)) #vd smart rendering bug if ac3_file != None: f.write("VirtualDub.subset.AddRange(%i, %i);\n" % (frame_start_keyframe-1, 1)) f.write("VirtualDub.subset.AddRange(%i, %i);\n" % (frame_start_keyframe-1, 1)) # copy part f.write("VirtualDub.subset.AddRange(%i, %i);\n" % (frame_start_keyframe, frames_duration-(frame_start_keyframe-frame_start))) else: print 'reiner Smart Rendering Part' try: # get next keyframe after the interval next_keyframe = self.get_keyframe_after_frame(keyframes, frame_start+frames_duration-2) except ValueError: next_keyframe = -1 if next_keyframe - (frame_start+frames_duration) > 2: f.write("VirtualDub.subset.AddRange(%i, %i);\n" % (frame_start+2, frames_duration)) else: # workaround for smart rendering bug f.write("VirtualDub.subset.AddRange(%i, %i);\n" % (frame_start+2, frames_duration-2)) if ac3_file != None: f.write("VirtualDub.subset.AddRange(%i, %i);\n" % (next_keyframe-1, 1)) f.write("VirtualDub.subset.AddRange(%i, %i);\n" % (next_keyframe-1, 1)) else: if not (frame_start+frames_duration) in keyframes and format == Format.HQ or format == Format.HD: # 'Kopieren mit keinem Keyframe am Ende' f.write("VirtualDub.subset.AddRange(%i, %i);\n" % (frame_start, frames_duration-2)) # we all love workarounds if ac3_file != None: f.write("VirtualDub.subset.AddRange(%i, %i);\n" % (frame_start+frames_duration-1, 1)) f.write("VirtualDub.subset.AddRange(%i, %i);\n" % (frame_start+frames_duration-1, 1)) else: print 'reines Kopieren' f.write("VirtualDub.subset.AddRange(%i, %i);\n" % (frame_start, frames_duration)) cut_video = self.generate_filename(filename,1) f.writelines([ 'VirtualDub.SaveAVI("%s");\n' % cut_video, 'VirtualDub.Close();' ]) f.close() # start vdub if not os.path.exists(config_value): return None, "VirtualDub konnte nicht aufgerufen werden: " + config_value if manually: win_filename = "Z:" + filename.replace(r"/", r"\\") command = 'VirtualDub.exe /s Z:\\\\tmp\\\\tmp.vcf "%s"' % win_filename else: command = "%s /s Z:\\\\tmp\\\\tmp.vcf /x" % config_value if 'intern-VirtualDub' in config_value: command = 'WINEPREFIX=' + os.path.dirname(config_value) + '/wine' + " " + winecommand + " " + command else: command = winecommand + " " + command try: vdub = subprocess.Popen(command, shell=True) except OSError: return None, "VirtualDub konnte nicht aufgerufen werden: " + config_value while vdub.poll() == None: time.sleep(1) while events_pending(): main_iteration(False) fileoperations.remove_file('/tmp/tmp.vcf') if manually: os.chdir(curr_dir) return None, None return cut_video, None def __create_cutlist_virtualdub(self, filename, format): """ returns: cuts, error_message """ try: f = open(filename, 'r') except IOError: return None, "Die VirtualDub-Projektdatei konnte nicht gelesen werden.\nWurde das Projekt in VirtualDub nicht gespeichert?\n(Datei: %s)." % filename cuts_frames = [] # (start, duration) count = 0 for line in f.readlines(): if "VirtualDub.subset.AddRange" in line: try: start, duration = line[line.index('(') + 1 : line.index(')')].split(',') except (IndexError, ValueError), message: return None, "Konnte Schnitte nicht lesen, um Cutlist zu erstellen. (%s)" % message if format == Format.HQ or format == Format.HD: cuts_frames.append((int(start)-2, int(duration))) else: cuts_frames.append((int(start), int(duration))) if len(cuts_frames) == 0: return None, "Konnte keine Schnitte finden!" fileoperations.remove_file(filename) return cuts_frames, None
def __cut_file_virtualdub(self, filename, config_value, cuts=None, manually=False): format, ac3_file = self.get_format(filename) fps, dar, sar, max_frames, ac3_stream, error = self.analyse_mediafile(filename) if sar == None or dar == None: return None, error if format == Format.HQ: if self.config.get('general', 'h264_codec') == 'ffdshow': if dar == "16:9": comp_data = codec.get_comp_data_h264_169() else: comp_data = codec.get_comp_data_h264_43() compression = 'VirtualDub.video.SetCompression(0x53444646,0,10000,0);\n' elif self.config.get('general', 'h264_codec') == 'x264vfw': comp_data = codec.get_comp_data_x264vfw_dynamic(sar,self.config.get('general', 'x264vfw_hq_string')) compression = 'VirtualDub.video.SetCompression(0x34363278,0,10000,0);\n' elif self.config.get('general', 'h264_codec') == 'komisar': comp_data = codec.get_comp_data_komisar_dynamic(sar,self.config.get('general', 'komisar_hq_string')) compression = 'VirtualDub.video.SetCompression(0x34363278,0,10000,0);\n' else: return None, "Codec nicht unterstützt. Nur ffdshow, x264vfw und komisar unterstützt." elif format == Format.HD: if self.config.get('general', 'h264_codec') == 'ffdshow': if dar == "16:9": comp_data = codec.get_comp_data_hd_169() else: comp_data = codec.get_comp_data_hd_43() compression = 'VirtualDub.video.SetCompression(0x53444646,0,10000,0);\n' elif self.config.get('general', 'h264_codec') == 'x264vfw': comp_data = codec.get_comp_data_x264vfw_dynamic(sar,self.config.get('general', 'x264vfw_hd_string')) compression = 'VirtualDub.video.SetCompression(0x34363278,0,10000,0);\n' elif self.config.get('general', 'h264_codec') == 'komisar': comp_data = codec.get_comp_data_komisar_dynamic(sar,self.config.get('general', 'komisar_hd_string')) compression = 'VirtualDub.video.SetCompression(0x34363278,0,10000,0);\n' else: return None, "Codec nicht unterstützt. Nur ffdshow, x264vfw und komisar unterstützt." elif format == Format.MP4: if self.config.get('general', 'h264_codec') == 'komisar': comp_data = codec.get_comp_data_komsiar_dynamic(sar,self.config.get('general', 'komisar_mp4_string')) compression = 'VirtualDub.video.SetCompression(0x34363278,0,10000,0);\n' else: comp_data = codec.get_comp_data_x264vfw_dynamic(sar,self.config.get('general', 'x264vfw_mp4_string')) compression = 'VirtualDub.video.SetCompression(0x34363278,0,10000,0);\n' elif format == Format.AVI: comp_data = codec.get_comp_data_dx50() compression = 'VirtualDub.video.SetCompression(0x53444646,0,10000,0);\n' else: return None, "Format nicht unterstützt (Nur Avi DX50, HQ H264 und HD sind möglich)." # make file for virtualdub scripting engine if manually: # TODO: kind of a hack curr_dir = os.getcwd() try: os.chdir(os.path.dirname(config_value)) except OSError: return None, "VirtualDub konnte nicht aufgerufen werden: " + config_value self.gui.main_window.set_tasks_progress(50) while events_pending(): main_iteration(False) f = open("/tmp/tmp.vcf", "w") if not manually: f.write('VirtualDub.Open("%s");\n' % filename) if self.config.get('general', 'smart'): f.writelines([ 'VirtualDub.video.SetMode(1);\n', 'VirtualDub.video.SetSmartRendering(1);\n', compression, 'VirtualDub.video.SetCompData(%s);\n' % comp_data ]) else: f.write('VirtualDub.video.SetMode(0);\n') f.write('VirtualDub.subset.Clear();\n') if not manually: keyframes, error = self.get_keyframes_from_file(filename) if keyframes == None: return None, "Keyframes konnten nicht ausgelesen werden." for frame_start, frames_duration in cuts: # interval does not begin with keyframe if not frame_start in keyframes and format == Format.HQ or format == Format.HD: try: # get next keyframe frame_start_keyframe =self.get_keyframe_after_frame(keyframes, frame_start) except ValueError: frame_start_keyframe = -1 if frame_start+frames_duration > frame_start_keyframe: # 'Smart Rendering Part mit anschließenden kopierten Part' if frame_start_keyframe < 0: # copy end of file f.write("VirtualDub.subset.AddRange(%i, %i);\n" % (frame_start+2, frames_duration-2)) else: # smart rendering part (duration -2 due to smart rendering bug) f.write("VirtualDub.subset.AddRange(%i, %i);\n" % (frame_start+2, frame_start_keyframe-frame_start-2)) #vd smart rendering bug if ac3_file != None: f.write("VirtualDub.subset.AddRange(%i, %i);\n" % (frame_start_keyframe-1, 1)) f.write("VirtualDub.subset.AddRange(%i, %i);\n" % (frame_start_keyframe-1, 1)) # copy part f.write("VirtualDub.subset.AddRange(%i, %i);\n" % (frame_start_keyframe, frames_duration-(frame_start_keyframe-frame_start))) else: print 'reiner Smart Rendering Part' try: # get next keyframe after the interval next_keyframe = self.get_keyframe_after_frame(keyframes, frame_start+frames_duration-2) except ValueError: next_keyframe = -1 if next_keyframe - (frame_start+frames_duration) > 2: f.write("VirtualDub.subset.AddRange(%i, %i);\n" % (frame_start+2, frames_duration)) else: # workaround for smart rendering bug f.write("VirtualDub.subset.AddRange(%i, %i);\n" % (frame_start+2, frames_duration-2)) if ac3_file != None: f.write("VirtualDub.subset.AddRange(%i, %i);\n" % (next_keyframe-1, 1)) f.write("VirtualDub.subset.AddRange(%i, %i);\n" % (next_keyframe-1, 1)) else: if not (frame_start+frames_duration) in keyframes and format == Format.HQ or format == Format.HD: # 'Kopieren mit keinem Keyframe am Ende' f.write("VirtualDub.subset.AddRange(%i, %i);\n" % (frame_start, frames_duration-2)) # we all love workarounds if ac3_file != None: f.write("VirtualDub.subset.AddRange(%i, %i);\n" % (frame_start+frames_duration-1, 1)) f.write("VirtualDub.subset.AddRange(%i, %i);\n" % (frame_start+frames_duration-1, 1)) else: print 'reines Kopieren' f.write("VirtualDub.subset.AddRange(%i, %i);\n" % (frame_start, frames_duration)) cut_video = self.generate_filename(filename,1) f.writelines([ 'VirtualDub.SaveAVI("%s");\n' % cut_video, 'VirtualDub.Close();' ]) f.close() # start vdub if not os.path.exists(config_value): return None, "VirtualDub konnte nicht aufgerufen werden: " + config_value if manually: win_filename = "Z:" + filename.replace(r"/", r"\\") command = 'VirtualDub.exe /s Z:\\\\tmp\\\\tmp.vcf "%s"' % win_filename else: command = "%s /s Z:\\\\tmp\\\\tmp.vcf /x" % config_value if 'intern-VirtualDub' in config_value: command = 'WINEPREFIX=' + os.path.dirname(config_value) + '/wine' + " wineconsole " + command else: command = "wineconsole " + command try: vdub = subprocess.Popen(command, shell=True) except OSError: return None, "VirtualDub konnte nicht aufgerufen werden: " + config_value while vdub.poll() == None: time.sleep(1) while events_pending(): main_iteration(False) fileoperations.remove_file('/tmp/tmp.vcf') if manually: os.chdir(curr_dir) return None, None return cut_video, None
def mkvmerge(): # env my_env = os.environ.copy() my_env["LANG"] = "C" for count, filename in enumerate(filenames): yield 0, count yield 3, 0 self.progress = 0 #analyse file cutter = Cut(self.app, self.gui) fps, dar, sar, max_frames, ac3_stream, error = cutter.analyse_mediafile(filename) if fps == None: self.errors[filename] = error continue # encode aac with ffmpeg if self.Config['EncodeAudioToAAC']: #norm volume ausrechnen yield 5, count if self.Config['NormalizeAudio'] and self.Config['EncodeAudioToAAC']: vol, error = self.get_norm_volume(filename) else: vol = 1.0 # ffmpeg pass yield 1, count self.progress = 0 ffmpegpass_file = fileoperations.make_unique_filename(os.path.splitext(filename)[0] + "_remux.mkv") # convert first audio stream to aac if self.Config['EncodeOnlyFirstAudioToAAC']: aacaudiostreams = '-c:a:0' else: aacaudiostreams = '-c:a' # convert first audio stream to aac ffmpeg = self.app.config.get_program('ffmpeg') if 'nonfree' in ffmpeg: # nonfree ffmpeg version with fdk support available audiocodec = ['-c:a', 'copy', aacaudiostreams, 'libfdk_aac', '-flags', '+qscale', '-profile:a:0', 'aac_low', '-global_quality', '5' ,'-afterburner', '1'] else: # only gpl version of ffmpeg available -> use standard aac codec audiocodec = ['-c:a', 'copy', aacaudiostreams, 'aac', '-strict', '-2','-profile:a:0', 'aac_low', '-ab' ,'192k', '-cutoff', '18000'] if self.Config['DownMixStereo'] and self.Config['EncodeAudioToAAC']: audiocodec.extend(['-ac:0', '2']) if ac3_stream == None: # no ac3 stream found - all streams are muxed map = ['-map', '0'] else: if self.Config['RemoveOtherAudioStreamsThanAC3']: # mux only video and ac3 stream map = ['-map', '0:v', '-map', ac3_stream] else: map = ['-map' ,'0'] args = [ffmpeg, "-loglevel", "info", "-y", "-drc_scale", "1.0", "-i", filename, "-vn", '-af', 'volume=volume=' + str(vol), "-vsync", "1", '-async', '1000', "-dts_delta_threshold", "100", "-vf", "fps="+ str(fps), '-threads', '0', ffmpegpass_file] map.extend(audiocodec) args[8:8] = map try: p = subprocess.Popen(args, stderr=subprocess.PIPE, universal_newlines=True) except OSError: self.errors[filename] = "FFMPEG (intern) wurde nicht gefunden!" continue yield 4, 0 line = "" infos_match = re.compile(r"time=(\d{2,}):(\d{2,}):(\d{2,}.\d{2,})") while p.poll() == None: line = p.stderr.readline() m = re.search(infos_match,line) if m and max_frames != 0: frame = (float(m.group(1))*3600 + float(m.group(2))*60 + float(m.group(3)))*fps next = float( frame / float(max_frames) ) * 100 if next > self.progress: self.progress = next yield 4, self.progress else: pass exit_code = p.poll() if exit_code == 0: pass else: self.errors[filename] = "Fehler beim Erzeugen der MP4 Datei durch FFMPEG" if os.path.exists(ffmpegpass_file): fileoperations.remove_file(ffmpegpass_file) continue # mkvmerge pass yield 2, count self.progress = 0 mkvpass_file = fileoperations.make_unique_filename(os.path.splitext(filename)[0] + ".mkv") if self.Config['EncodeAudioToAAC']: args = [self.app.config.get_program('mkvmerge'), '--engage', 'no_cue_duration', '--engage', 'no_cue_relative_position', '--ui-language', 'en_US',"-o", mkvpass_file, '-A', filename, '-D', ffmpegpass_file] else: if self.Config['RemoveOtherAudioStreamsThanAC3'] and ac3_stream: args = [self.app.config.get_program('mkvmerge'), '--engage', 'no_cue_duration', '--engage', 'no_cue_relative_position', '--ui-language', 'en_US', "-o", mkvpass_file, '-a', ac3_stream[2], filename] else: args = [self.app.config.get_program('mkvmerge'), '--engage', 'no_cue_duration', '--engage', 'no_cue_relative_position', '--ui-language', 'en_US', "-o", mkvpass_file, filename] p = subprocess.Popen(args, stdout=subprocess.PIPE, env=my_env) p.stdout.readline() line = "" while p.poll() == None: # read progress from stdout char = p.stdout.read(1) line += char progress = '' if char == ':': if "Error" in line or "Warning" in line: break while char != '%': char = p.stdout.read(1) progress += char try: self.progress = int(progress.strip(' %')) yield 3, self.progress except ValueError: pass exit_code = p.poll() if exit_code == 0 or exit_code == 1: self.success += 1 if self.Config['EncodeAudioToAAC']: fileoperations.remove_file(ffmpegpass_file) if self.Config['DumpAVIs']: if self.Config['DumpAVIs_delete']: fileoperations.remove_file(filename) else: new_filename = os.path.join(self.app.config.get('general', 'folder_trash_avis'), os.path.basename(filename)) if os.path.exists(new_filename): fileoperations.remove_file(new_filename) fileoperations.move_file(filename, self.app.config.get('general', 'folder_trash_avis')) else: error = p.stdout.readline() try: error = error.split(":")[1] except IndexError: pass if "unknown type" in error: error = "Datei konnte nicht gelesen werden." self.errors[filename] = error
def cut_file_manually(self, filename): """ Cuts a file manually with Avidemux or VirtualDub or the CutInterface and gets cuts from possibly created project files (VD) or from output (AD). returns: error_message, cutlist """ global cutlist_error, cuts_frames program, config_value, ac3file = self.get_program(filename, manually=True) format, ac3_file, bframe_delay = self.get_format(filename) fps, dar, sar, max_frames, ac3_stream, error = self.analyse_mediafile( filename) if error: if exists(filename + '.mkv'): fileoperations.remove_file(filename + '.mkv') return "Konnte FPS nicht bestimmen: " + error, None if program < 0: return config_value, None cutlist = cutlists_management.Cutlist() if program == Program.AVIDEMUX: cutter = CutAvidemux(self.app, self.gui) cuts_frames, cutlist_error = cutter.create_cutlist( filename, config_value) elif program == Program.VIRTUALDUB: # VIRTUALDUB cutter = CutVirtualdub(self.app, self.gui) cuts_frames, cutlist_error = cutter.create_cutlist( filename, config_value) if program == Program.CUT_INTERFACE: # looking for latest cutlist, if any p, video_file = os.path.split(filename) cutregex = re.compile("^" + video_file + "\.?(.*).cutlist$") files = os.listdir(p) number = -1 local_cutlist = None # use fallback name in conclusions if there are no local cutlists for f in files: match = cutregex.match(f) if match: self.log.debug("Found local cutlist {}".format( match.group())) if match.group(1) == '' or match.group(1) == 'mkv': res_num = 0 elif "." in match.group(1): res_num = int(match.group(1).split('.')[1]) elif type(eval( match.group(1))) == type(1): # It's a number res_num = int(match.group(1)) else: res_num = 0 self.log.debug("local cutlist res_num: {}".format( match.group(1))) if res_num > number: res_num = number local_cutlist = p + "/" + match.group() ci = CutinterfaceDialog.NewCutinterfaceDialog(self.gui) ci.set_transient_for(self.gui.main_window) ci.set_modal(True) cutlist = ci._run(filename, local_cutlist, self.app) ci.destroy() if cutlist.cuts_frames is None or len(cutlist.cuts_frames) == 0: cutlist_error = "Keine Schnitte angegeben" else: cutlist_error = None else: # complete cutlist for Avidemux & VirtualDub # create cutlist data if cutlist_error is None: cutlist.cuts_frames = cuts_frames cutlist.intended_app = basename(config_value) cutlist.usercomment = 'Mit %s geschnitten' % self.app.app_name cutlist.fps = fps # calculate seconds for start_frame, duration_frames in cuts_frames: cutlist.cuts_seconds.append( (start_frame / fps, duration_frames / fps)) if cutlist_error: return cutlist_error, None else: return None, cutlist
def show_conclusions(self): conclusions = self.app.gui.dialog_conclusion._run(self.conclusions, self.app.rename_by_schema, self.app.config.get('general', 'folder_archive')) self.app.gui.main_window.builder.get_object('box_conclusion').hide() self.conclusions = [] # create cutlists cutlists = [] for conclusion in conclusions: if conclusion.action == Action.DECODE: continue print "[Conclusion] for file ", conclusion.uncut_video # rename print "[Conclusion] Rename?" if conclusion.cut.rename: print "[Conclusion] true" extension = os.path.splitext(conclusion.cut_video)[1] if not conclusion.cut.rename.endswith(extension): conclusion.cut.rename += extension new_filename = os.path.join(self.app.config.get('general', 'folder_cut_avis'), conclusion.cut.rename.replace('/', '_')) #new_filename = fileoperations.make_unique_filename(new_filename) if conclusion.cut_video != new_filename: conclusion.cut_video = fileoperations.rename_file(conclusion.cut_video, new_filename) # move cut video to archive print "[Conclusion] Move to archive?" if conclusion.cut.archive_to: print "[Conclusion] true" fileoperations.move_file(conclusion.cut_video, conclusion.cut.archive_to) # move uncut video to trash if it's ok print "[Conclusion] Move to trash?" if conclusion.cut.status == Status.OK and conclusion.cut.delete_uncut: print "[Conclusion] true" if os.path.exists(conclusion.uncut_video): # move to trash target = self.app.config.get('general', 'folder_trash_avis') conclusion.uncut_video = fileoperations.move_file(conclusion.uncut_video, target) if os.path.exists(conclusion.ac3_file): target = self.app.config.get('general', 'folder_trash_avis') fileoperations.move_file(conclusion.ac3_file, target) # remove local cutlists print "[Conclusion] Remove local cutlist?" if self.app.config.get('general', 'delete_cutlists'): print "[Conclusion] true" if conclusion.cut.cutlist.local_filename: if os.path.exists(conclusion.cut.cutlist.local_filename): fileoperations.remove_file(conclusion.cut.cutlist.local_filename) print "[Conclusion] Create cutlist?" if conclusion.cut.create_cutlist: print "[Conclusion] true" if "VirtualDub" in conclusion.cut.cutlist.intended_app: intended_app_name = "VirtualDub" else: intended_app_name = "Avidemux" if not conclusion.cut.cutlist.local_filename: conclusion.cut.cutlist.local_filename = self.app.config.get('general', 'folder_uncut_avis') + '/' + os.path.basename(conclusion.uncut_video) + ".cutlist" conclusion.cut.cutlist.author = self.app.config.get('general', 'cutlist_username') conclusion.cut.cutlist.intended_version = open(path.getdatapath("VERSION"), 'r').read().strip() conclusion.cut.cutlist.smart = self.app.config.get('general', 'smart') conclusion.cut.cutlist.write_local_cutlist(conclusion.uncut_video, intended_app_name, conclusion.cut.my_rating) if conclusion.cut.upload_cutlist: cutlists.append(conclusion.cut.cutlist) # upload cutlists: def upload(): error_messages = [] for cutlist in cutlists: error_message = cutlist.upload(self.app.config.get('general', 'server'), self.app.config.get('general', 'cutlist_hash')) if error_message: error_messages.append(error_message) else: if self.app.config.get('general', 'delete_cutlists'): fileoperations.remove_file(cutlist.local_filename) count = len(cutlists) message = "Es wurden %s/%s Cutlisten hochgeladen!" % (str(count - len(error_messages)), str(count)) if len(error_messages) > 0: message += " (" + ", ".join(error_messages) + ")" yield message if len(cutlists) > 0: print "[Conclusion] Upload cutlists" if self.app.gui.question_box("Soll(en) %s Cutlist(en) hochgeladen werden?" % len(cutlists)): def change_status(message): self.app.gui.main_window.change_status(0, message) GeneratorTask(upload, change_status).start() # rate cutlists def rate(): yield 0 # fake generator messages = [] count = 0 for conclusion in conclusions: if conclusion.action == Action.DECODE: continue if conclusion.cut.my_rating > -1: print "Rate with ", conclusion.cut.my_rating success, message = conclusion.cut.cutlist.rate(conclusion.cut.my_rating, self.app.config.get('general', 'server')) if success: count += 1 else: messages += [message] if count > 0 or len(messages) > 0: if count == 0: text = "Es wurde keine Cutlist bewertet!" if count == 1: text = "Es wurde 1 Cutlist bewertet!" else: text = "Es wurden %s Cutlisten bewertet!" % count if len(messages) > 0: text += " (Fehler: %s)" % ", ".join(messages) self.app.gui.main_window.change_status(0, text) print "[Conclusion] Rate cutlists" GeneratorTask(rate).start()