def start(self, force=False): def loop(*args): self.log += "%s\n" % args[0] if force or not self.information['status'] in [ DownloadStatus.RUNNING, DownloadStatus.SEEDING ]: self.__task = GeneratorTask(self._download, loop) self.__task.start()
def on_button_search_clicked(self, widget, data=None): text = self.builder.get_object('entry_search').get_text() if len(text) < 3: self.builder.get_object('label_status').set_markup( "<b>Die Suche muss mindesten 3 Zeichen haben!</b>") return else: for char in text: if not char.lower( ) in 'abcdefghijklmnopqrstuvwxyz0123456789.-_ *': self.builder.get_object('label_status').set_markup( "<b>Erlaubt sind nur Groß- und Kleinbuchstaben, die Ziffern 0 bis 9, der Punkt, das Minus, der Unterstrich und der Stern/Leerzeichen als Platzhalter!</b>" ) return self.builder.get_object('label_status').set_markup("") model = self.builder.get_object('liststore_programs') model.clear() self.builder.get_object('scrolledwindow_programs').hide() self.builder.get_object('vbox_searching').show() self.builder.get_object('button_search').set_sensitive(False) self.error = "" GeneratorTask(self.search, self.search_callback, self.search_stop).start(text)
def on_entry_link_changed(self, widget, data=None): download_link = widget.get_text() result = re.findall("([a-z._\-0-9]*_TVOON_DE[a-z0-9.]*\.otrkey)", download_link, re.IGNORECASE) if result: self.filename = result[0] GeneratorTask(self.gather_information, self.gather_information_callback, self.gather_information_stop).start() else: pass
def download_generator(self, get_all_qualities): # start looking for downloadable cutlists self.treeview_download_cutlists.get_model().clear() self.builder.get_object('label_status').set_markup( "<b>Cutlisten werden heruntergeladen...</b>") self.download_error = False # Empty the list for reuse self.cutlists_list = [] GeneratorTask(cutlists_management.download_cutlists, None, self._completed).\ start(self.filename, self.app.config.get('general', 'server'), self.app.config.get('general', 'choose_cutlists_by'), self.app.config.get('general', 'cutlist_mp4_as_hq'), self._error_cb, self._cutlist_found_cb, get_all_qualities)
def setup(self, video_file): self.filename = video_file self.builder.get_object('label_file').set_markup( "<b>%s</b>" % os.path.basename(video_file)) # looking for local cutlists p, filename = os.path.split(video_file) cutregex = re.compile("^" + filename + "\.?(.*).cutlist$") files = os.listdir(p) local_cutlists = [] for f in files: match = cutregex.match(f) if match: # print "Found local cutlist" local_cutlists.append(p + '/' + match.group()) else: # print f + " is no cutlist" pass # print "%d cutlists found" % len(local_cutlists) if len(local_cutlists) > 0: self.treeview_local_cutlists.get_model().clear() self.builder.get_object('scrolledwindow_local').set_sensitive(True) self.builder.get_object('button_local').set_sensitive(True) for c in local_cutlists: cutlist = cutlists_management.Cutlist() cutlist.local_filename = c cutlist.read_from_file() self.treeview_local_cutlists.add_cutlist(cutlist) else: self.builder.get_object('scrolledwindow_local').set_sensitive( False) self.builder.get_object('button_local').set_active(False) self.builder.get_object('button_local').set_sensitive(False) # start looking for downloadable cutlists self.treeview_download_cutlists.get_model().clear() self.builder.get_object('label_status').set_markup( "<b>Cutlisten werden heruntergeladen...</b>") self.download_error = False GeneratorTask(cutlists_management.download_cutlists, None, self._completed).start( video_file, self.app.config.get('general', 'server'), self.app.config.get('general', 'choose_cutlists_by'), self.app.config.get('general', 'cutlist_mp4_as_hq'), self._error_cb, self._cutlist_found_cb)
def change_status(self, message_type, message, permanent=False): """ Zeigt ein Bild und einen Text in der Statusleiste an. message_type 0 = Information-Icon, -1 = kein Icon message Anzuzeigender Text permanent: wenn \e False, verschwindet die Nachricht nach 10s wieder.""" self.builder.get_object('label_statusbar').set_text(message) if message_type == 0: self.builder.get_object('image_status').set_from_file(path.get_image_path("information.png")) if not permanent: def wait(): yield 0 # fake generator time.sleep(10) def completed(): self.builder.get_object('label_statusbar').set_text("") self.builder.get_object('image_status').clear() GeneratorTask(wait, None, completed).start()
def forward(self, iter=None, link=None): """ iter==None --> programs search was skipped iter!=None --> iter is the selected program link!=None --> executable was called with 'link' argument """ self.mode = 1 # download self.builder.get_object('vbox_search').hide() self.builder.get_object('vbox_download').show() self.builder.get_object('button_ok').set_label("_Download") self.builder.get_object('button_ok').set_sensitive(True) if iter: self.filename, mirrors = self.builder.get_object( 'liststore_programs').get(iter, 0, 7) if mirrors == 1: self.builder.get_object('button_mirror_search').set_label( "Auf einem Mirror suchen") else: self.builder.get_object('button_mirror_search').set_label( "Auf %i Mirrors suchen" % mirrors) GeneratorTask(self.gather_information, self.gather_information_callback, self.gather_information_stop).start() else: self.builder.get_object('image_spinner_download').hide() self.builder.get_object('button_mirror_search').hide() self.builder.get_object('label_torrent').set_markup( "Download via Torrent") self.builder.get_object('label_error').set_markup('') if link: self.builder.get_object('entry_link').set_text(link) else: self.builder.get_object('label_download_status').set_markup( "Füge einen Downloadlink in das Feld ein!")
def on_mp4_clicked(self, widget, data=None): filenames = self.gui.main_window.get_selected_filenames() if len(filenames) == 0: self.gui.message_error_box("Es muss eine Datei markiert sein.") return self.toolbutton.set_sensitive(False) self.gui.main_window.set_tasks_visible(True) self.success = 0 self.errors = {} 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" def loop(state, argument): if state == 0: self.gui.main_window.set_tasks_text( "Extrahiere Streams aus Inputdatei ... %s/%s" % (str(argument + 1), str(len(filenames)))) elif state == 1: self.gui.main_window.set_tasks_text( "MP4 erzeugen ... %s/%s" % (str(argument + 1), str(len(filenames)))) elif state == 2: self.gui.main_window.set_tasks_text( "MP4 optimieren (importiere Stream) ... %s/%s" % (str(argument + 1), str(len(filenames)))) elif state == 3: self.gui.main_window.set_tasks_text( "Originaldatei in Mülleimer verschieben ... %s/%s" % (str(argument + 1), str(len(filenames)))) elif state == 5: self.gui.main_window.set_tasks_text( "Normalisierungswert berechnen ... %s/%s" % (str(argument + 1), str(len(filenames)))) elif state == 6: self.gui.main_window.set_tasks_text( "MP4 optimieren (schreibe MP4) ... %s/%s" % (str(argument + 1), str(len(filenames)))) else: self.gui.main_window.set_tasks_progress(argument) def complete(): if len(self.errors) == 0: self.gui.main_window.change_status( 0, "Erfolgreich %s/%s Dateien umgewandelt." % (str(self.success), str(len(filenames)))) else: self.gui.main_window.change_status( 0, "Erfolgreich %s/%s Dateien umgewandelt. (Fehler: %s)" % (str(self.success), str(len(filenames)), " ".join( self.errors.values()))) self.gui.main_window.set_tasks_visible(False) if self.success > 0: self.app.show_section(self.app.section) self.toolbutton.set_sensitive(True) GeneratorTask(mp4, loop, complete).start()
def on_mkv_clicked(self, widget, data=None): filenames = self.gui.main_window.get_selected_filenames() if len(filenames) == 0: self.gui.message_error_box("Es muss eine Datei markiert sein.") return self.toolbutton.set_sensitive(False) self.gui.main_window.set_tasks_visible(True) self.success = 0 self.errors = {} 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 loop(state, argument): if state == 0: self.gui.main_window.set_tasks_text( "Analysiere Datei ... %s/%s" % (str(argument + 1), str(len(filenames)))) elif state == 1: self.gui.main_window.set_tasks_text( "Audiospur in AAC wandeln ... %s/%s" % (str(argument + 1), str(len(filenames)))) elif state == 2: self.gui.main_window.set_tasks_text( "MKV erstellen ... %s/%s" % (str(argument + 1), str(len(filenames)))) elif state == 5: self.gui.main_window.set_tasks_text( "Normalisierungswert berechnen ... %s/%s" % (str(argument + 1), str(len(filenames)))) else: self.gui.main_window.set_tasks_progress(argument) def complete(): if len(self.errors) == 0: self.gui.main_window.change_status( 0, "Erfolgreich %s/%s Dateien umgewandelt." % (str(self.success), str(len(filenames)))) else: self.gui.main_window.change_status( 0, "Erfolgreich %s/%s Dateien umgewandelt. (Fehler: %s)" % (str(self.success), str(len(filenames)), " ".join( self.errors.values()))) self.gui.main_window.set_tasks_visible(False) if self.success > 0: self.app.show_section(self.app.section) self.toolbutton.set_sensitive(True) GeneratorTask(mkvmerge, loop, complete).start()
class Details(Plugin): Name = "Details" Desc = "Zeigt zu einer Video-Datei Details wie den verwendeten Codec und die Dauer an." Author = "Benjamin Elbers" Configurable = False def enable(self): treeselection = self.gui.main_window.builder.get_object('treeview_files').get_selection() self.on_treeselection_changed_id = treeselection.connect('changed', lambda callback: self.update_details()) # Typ # Aspect-Ratio ... # Video-Format ... # Dauer ... padding = 3,3 table = gtk.Table(4, 2) self.label_filetype = gtk.Label('') self.label_aspect = gtk.Label('...') self.label_video_format = gtk.Label('...') self.label_length = gtk.Label('...') table.attach(self.label_filetype, 0, 2, 0, 1, gtk.FILL, gtk.FILL, *padding) table.attach(gtk.Label("Aspect-Ratio"), 0, 1, 1, 2, gtk.FILL, gtk.FILL, *padding) table.attach(self.label_aspect, 1, 2, 1, 2, gtk.FILL, gtk.FILL, *padding) table.attach(gtk.Label("Video-Format"), 0, 1, 2, 3, gtk.FILL, gtk.FILL, *padding) table.attach(self.label_video_format, 1, 2, 2, 3, gtk.FILL, gtk.FILL, *padding) table.attach(gtk.Label("Dauer"), 0, 1, 3, 4, gtk.FILL, gtk.FILL, *padding) table.attach(self.label_length, 1, 2, 3, 4, gtk.FILL, gtk.FILL, *padding) table.show_all() # add to bottom bar self.page_index = self.gui.main_window.builder.get_object('notebook_bottom').append_page(table, gtk.Label("Details")) def disable(self): treeselection = self.gui.main_window.builder.get_object('treeview_files').get_selection() treeselection.disconnect(self.on_treeselection_changed_id) self.gui.main_window.builder.get_object('notebook_bottom').remove_page(self.page_index) def update_details(self): try: self.task.stop() except: pass if self.app.section == Section.PLANNING: self.reset_details("") return mplayer = self.app.config.get('general', 'mplayer') if not mplayer: self.reset_details("Der MPlayer ist nicht installiert!") return filenames = self.gui.main_window.get_selected_filenames() if len(filenames) == 0: self.reset_details("<b>Keine Datei markiert.</b>") elif len(filenames) > 1: self.reset_details("<b>%s Dateien markiert.</b>" % len(filenames)) else: filename = filenames[0] extension = os.path.splitext(filename)[1] self.label_filetype.set_markup("<b>%s-Datei</b>" % extension[1:]) if extension != ".otrkey": def get_information(): yield # prettify the output! def prettify_aspect(aspect): if aspect == "1.7778": return "16:9" elif aspect == "1.3333": return "4:3" else: return aspect def prettify_length(seconds): hrs = float(seconds) / 3600 leftover = float(seconds) % 3600 mins = leftover / 60 secs = leftover % 60 return "%02d:%02d:%02d" % (hrs, mins, secs) values = ( ("ID_VIDEO_ASPECT", self.label_aspect, prettify_aspect), ("ID_VIDEO_FORMAT", self.label_video_format, None), ("ID_LENGTH", self.label_length, prettify_length) ) process = subprocess.Popen([mplayer, "-identify", "-vo", "null", "-frames", "1", "-nosound", filename], stdout=subprocess.PIPE) while process.poll() == None: line = process.stdout.readline().strip() for value, widget, callback in values: if line.startswith(value): # mplayer gives an output like this: ID_VIDEO_ASPECT=1.3333 value = line.split("=")[1] if callback: value = callback(value) widget.set_text(value) self.task = GeneratorTask(get_information) self.task.start() def reset_details(self, filetype=""): self.label_filetype.set_markup(filetype) self.label_aspect.set_text("...") self.label_video_format.set_text("...") self.label_length.set_text("...")
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 update_details(self): try: self.task.stop() except: pass if self.app.section == Section.PLANNING: self.reset_details("") return mplayer = self.app.config.get_program('mplayer') if not mplayer: self.reset_details("Der MPlayer ist nicht installiert!") return filenames = self.gui.main_window.get_selected_filenames() if len(filenames) == 0: self.reset_details("<b>Keine Datei markiert.</b>") elif len(filenames) > 1: self.reset_details("<b>%s Dateien markiert.</b>" % len(filenames)) else: filename = filenames[0] extension = os.path.splitext(filename)[1] self.label_filetype.set_markup("<b>%s-Datei</b>" % extension[1:]) if extension != ".otrkey": def get_information(): yield # prettify the output! def prettify_aspect(aspect): if aspect == "1.7778": return "16:9" elif aspect == "1.3333": return "4:3" else: return aspect def prettify_length(seconds): hrs = float(seconds) / 3600 leftover = float(seconds) % 3600 mins = leftover / 60 secs = leftover % 60 return "%02d:%02d:%02d" % (hrs, mins, secs) values = (("ID_VIDEO_ASPECT", self.label_aspect, prettify_aspect), ("ID_VIDEO_FORMAT", self.label_video_format, None), ("ID_LENGTH", self.label_length, prettify_length)) process = subprocess.Popen([ mplayer, "-identify", "-vo", "null", "-frames", "1", "-nosound", filename ], stdout=subprocess.PIPE) while process.poll() == None: line = process.stdout.readline().strip() for value, widget, callback in values: if line.startswith(value): # mplayer gives an output like this: ID_VIDEO_ASPECT=1.3333 value = line.split("=")[1] if callback: value = callback(value) widget.set_text(value) self.task = GeneratorTask(get_information) self.task.start()
class Details(Plugin): Name = "Details" Desc = "Zeigt zu einer Video-Datei Details wie den verwendeten Codec und die Dauer an." Author = "Benjamin Elbers" Configurable = False def enable(self): treeselection = self.gui.main_window.builder.get_object( 'treeview_files').get_selection() self.on_treeselection_changed_id = treeselection.connect( 'changed', lambda callback: self.update_details()) # Typ # Aspect-Ratio ... # Video-Format ... # Dauer ... padding = 3, 3 table = Gtk.Table(4, 2) self.label_filetype = Gtk.Label('') self.label_aspect = Gtk.Label('...') self.label_video_format = Gtk.Label('...') self.label_length = Gtk.Label('...') table.attach(self.label_filetype, 0, 2, 0, 1, Gtk.Align.FILL, Gtk.Align.FILL, *padding) table.attach(Gtk.Label("Aspect-Ratio"), 0, 1, 1, 2, Gtk.Align.FILL, Gtk.Align.FILL, *padding) table.attach(self.label_aspect, 1, 2, 1, 2, Gtk.Align.FILL, Gtk.Align.FILL, *padding) table.attach(Gtk.Label("Video-Format"), 0, 1, 2, 3, Gtk.Align.FILL, Gtk.Align.FILL, *padding) table.attach(self.label_video_format, 1, 2, 2, 3, Gtk.Align.FILL, Gtk.Align.FILL, *padding) table.attach(Gtk.Label("Dauer"), 0, 1, 3, 4, Gtk.Align.FILL, Gtk.Align.FILL, *padding) table.attach(self.label_length, 1, 2, 3, 4, Gtk.Align.FILL, Gtk.Align.FILL, *padding) table.show_all() # add to bottom bar self.page_index = self.gui.main_window.builder.get_object( 'notebook_bottom').append_page(table, Gtk.Label("Details")) def disable(self): treeselection = self.gui.main_window.builder.get_object( 'treeview_files').get_selection() treeselection.disconnect(self.on_treeselection_changed_id) self.gui.main_window.builder.get_object('notebook_bottom').remove_page( self.page_index) def update_details(self): try: self.task.stop() except: pass if self.app.section == Section.PLANNING: self.reset_details("") return mplayer = self.app.config.get_program('mplayer') if not mplayer: self.reset_details("Der MPlayer ist nicht installiert!") return filenames = self.gui.main_window.get_selected_filenames() if len(filenames) == 0: self.reset_details("<b>Keine Datei markiert.</b>") elif len(filenames) > 1: self.reset_details("<b>%s Dateien markiert.</b>" % len(filenames)) else: filename = filenames[0] extension = os.path.splitext(filename)[1] self.label_filetype.set_markup("<b>%s-Datei</b>" % extension[1:]) if extension != ".otrkey": def get_information(): yield # prettify the output! def prettify_aspect(aspect): if aspect == "1.7778": return "16:9" elif aspect == "1.3333": return "4:3" else: return aspect def prettify_length(seconds): hrs = float(seconds) / 3600 leftover = float(seconds) % 3600 mins = leftover / 60 secs = leftover % 60 return "%02d:%02d:%02d" % (hrs, mins, secs) values = (("ID_VIDEO_ASPECT", self.label_aspect, prettify_aspect), ("ID_VIDEO_FORMAT", self.label_video_format, None), ("ID_LENGTH", self.label_length, prettify_length)) process = subprocess.Popen([ mplayer, "-identify", "-vo", "null", "-frames", "1", "-nosound", filename ], stdout=subprocess.PIPE) while process.poll() == None: line = process.stdout.readline().strip() for value, widget, callback in values: if line.startswith(value): # mplayer gives an output like this: ID_VIDEO_ASPECT=1.3333 value = line.split("=")[1] if callback: value = callback(value) widget.set_text(value) self.task = GeneratorTask(get_information) self.task.start() def reset_details(self, filetype=""): self.label_filetype.set_markup(filetype) self.label_aspect.set_text("...") self.label_video_format.set_text("...") self.label_length.set_text("...")
class Download: def __init__(self, app, config, filename=None, link=None): """ Torrent: link=None """ self._app = app self._config = config self.filename = filename self.link = link self.log = "" self.information = { 'output': '', 'status': -1, 'size': None, 'progress': 0, 'speed': '', 'est': '', 'message_short': '', # Torrent 'seeders': None, 'upspeed': None, 'uploaded': None, 'ratio': None } self.__task = None self.__process = None # # Storage # def to_json(self): information = self.information.copy() if 'cutlist' in information.keys(): information['cutlist'] = None return { 'information': information, 'filename': self.filename, 'link': self.link } def from_json(self, json): self.information = json['information'] self.filename = json['filename'] self.link = json['link'] # # Init methods for action # def download_torrent(self): self.information['download_type'] = DownloadTypes.TORRENT def download_basic(self, preferred_downloader): self.information['download_type'] = DownloadTypes.BASIC self.information['preferred_downloader'] = preferred_downloader def download_decode(self, cutlist_id=None): if cutlist_id: self.information['download_type'] = DownloadTypes.OTR_CUT self.information['cutlist_id'] = cutlist_id self.information['cutlist'] = None else: self.information['download_type'] = DownloadTypes.OTR_DECODE # # Convenience methods used only by this class # def _finished(self): self.information['status'] = DownloadStatus.FINISHED self.information['progress'] = 100 self.information['est'] = "" # unused by now def _parse_time(time): """ Takes a string '5m' or '6h2m59s' and calculates seconds. """ m = re.match( '((?P<h>[0-9]*)h)?((?P<m>[0-9]{1,2})m)?((?P<s>[0-9]{1,2})s)?', time) if m: d = m.groupdict() time = 60 * 60 * int(m.group('h')) if m.group('h') else 0 time = (time + 60 * int(m.group('m'))) if m.group('m') else time time = (time + int(m.group('s'))) if m.group('s') else time return time else: return 0 def _check_file_with_torrent(self): """ checks file with torrent """ torrent_url = 'http://81.95.11.2/torrents/' + self.filename + '.torrent' torrent_command = [ self._config.get_program('aria2c') ] + self._config.get('downloader', 'aria2c_opts_torrent') + [ "-d", self.information['output'], '--check-integrity=true', '--continue', '--bt-enable-lpd=false', '--bt-exclude-tracker="*"', '--enable-dht=false', '--enable-dht6=false', '--enable-peer-exchange=false', '--bt-hash-check-seed=false', '--bt-stop-timeout=1', '--seed-time=0', '--follow-torrent=mem', torrent_url ] # Checking try: returncode = subprocess.call(torrent_command) except OSError: return -1 return returncode # # Download # 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 start(self, force=False): def loop(*args): self.log += "%s\n" % args[0] if force or not self.information['status'] in [ DownloadStatus.RUNNING, DownloadStatus.SEEDING ]: self.__task = GeneratorTask(self._download, loop) self.__task.start() def stop(self): if self.information['status'] in [ DownloadStatus.RUNNING, DownloadStatus.SEEDING ]: self.information['status'] = DownloadStatus.STOPPED self.information['message_short'] = "" self.information['est'] = "" self.information['speed'] = "" self.update_view() if self.__process: try: self.__process.kill() except OSError: pass
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 on_mkv_clicked(self, widget, data=None): self.toolbutton.set_sensitive(False) filenames = self.gui.main_window.get_selected_filenames() self.gui.main_window.set_tasks_visible(True) self.success = 0 self.errors ={} def mkvmerge(): for count, filename in enumerate(filenames): yield 0, count self.progress = 0 p = subprocess.Popen([self.Config['mkvmerge'], "-o", os.path.splitext(filename)[0] + ".mkv", filename], stdout=subprocess.PIPE) 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 1, self.progress except ValueError: pass exit_code = p.poll() if exit_code == 0: self.success += 1 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 loop(state, argument): if state == 0: self.gui.main_window.set_tasks_text("In Mkv Umwandeln %s/%s" % (str(argument + 1), str(len(filenames)))) else: self.gui.main_window.set_tasks_progress(argument) def complete(): if len(self.errors) == 0: self.gui.main_window.change_status(0, "Erfolgreich %s/%s Dateien umgewandelt." % (str(self.success), str(len(filenames)))) else: self.gui.main_window.change_status(0, "Erfolgreich %s/%s Dateien umgewandelt. (Fehler: %s)" % (str(self.success), str(len(filenames)), " ".join(self.errors.values()))) self.gui.main_window.set_tasks_visible(False) if self.success > 0: self.app.show_section(self.app.section) self.toolbutton.set_sensitive(True) GeneratorTask(mkvmerge, loop, complete).start()
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, cutlist = self.cut_file_manually( file_conclusion.uncut_video) if not error_message: file_conclusion.cut.create_cutlist = True file_conclusion.cut.upload_cutlist = True file_conclusion.cut.cutlist = cutlist 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: if self.gui.dialog_cut.chosen_cutlist is not None: file_conclusion.cut.cutlist = self.gui.dialog_cut.chosen_cutlist else: file_conclusion.cut.status = Status.NOT_DONE file_conclusion.cut.message = "Keine Cutlist gefunden." 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." elif file_conclusion.cut.cut_action == Cut_action.ASK: file_conclusion.cut.status = Status.NOT_DONE file_conclusion.cut.message = "Keine 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 self.log.info("[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) while Gtk.events_pending(): Gtk.main_iteration() # 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, ac3_file, error = self.cut_file_by_cutlist( file_conclusion.uncut_video, file_conclusion.cut.cutlist, None) if cut_video is None: file_conclusion.cut.status = Status.ERROR file_conclusion.cut.message = error file_conclusion.cut.upload_cutlist = False else: file_conclusion.cut.status = Status.OK file_conclusion.cut_video = cut_video file_conclusion.ac3_file = ac3_file if self.config.get('general', 'rename_cut'): file_conclusion.cut.rename = self.rename_by_schema( basename(file_conclusion.cut_video) ) # rename after cut video, extension could have changed else: file_conclusion.cut.rename = basename(cut_video) if os.path.isfile(file_conclusion.uncut_video + '.ffindex_track00.kf.txt'): os.remove(file_conclusion.uncut_video + '.ffindex_track00.kf.txt') if os.path.isfile(file_conclusion.uncut_video + '.ffindex_track00.tc.txt'): os.remove(file_conclusion.uncut_video + '.ffindex_track00.tc.txt') return True
def update_details(self): try: self.task.stop() except: pass if self.app.section == Section.PLANNING: self.reset_details("") return mplayer = self.app.config.get('general', 'mplayer') if not mplayer: self.reset_details("Der MPlayer ist nicht installiert!") return filenames = self.gui.main_window.get_selected_filenames() if len(filenames) == 0: self.reset_details("<b>Keine Datei markiert.</b>") elif len(filenames) > 1: self.reset_details("<b>%s Dateien markiert.</b>" % len(filenames)) else: filename = filenames[0] extension = os.path.splitext(filename)[1] self.label_filetype.set_markup("<b>%s-Datei</b>" % extension[1:]) if extension != ".otrkey": def get_information(): yield # prettify the output! def prettify_aspect(aspect): if aspect == "1.7778": return "16:9" elif aspect == "1.3333": return "4:3" else: return aspect def prettify_length(seconds): hrs = float(seconds) / 3600 leftover = float(seconds) % 3600 mins = leftover / 60 secs = leftover % 60 return "%02d:%02d:%02d" % (hrs, mins, secs) values = ( ("ID_VIDEO_ASPECT", self.label_aspect, prettify_aspect), ("ID_VIDEO_FORMAT", self.label_video_format, None), ("ID_LENGTH", self.label_length, prettify_length) ) process = subprocess.Popen([mplayer, "-identify", "-vo", "null", "-frames", "1", "-nosound", filename], stdout=subprocess.PIPE) while process.poll() == None: line = process.stdout.readline().strip() for value, widget, callback in values: if line.startswith(value): # mplayer gives an output like this: ID_VIDEO_ASPECT=1.3333 value = line.split("=")[1] if callback: value = callback(value) widget.set_text(value) self.task = GeneratorTask(get_information) self.task.start()