def on_dvd_sel_changed(self, curr, prev): if not curr.isValid(): self.textTrackInfo.clear() return node = curr.internalPointer() self.textTrackInfo.clear() if node.typeinfo == 'Track': self.DVDTree.resizeColumnToContents(1) elif node.typeinfo == 'File': avprobe = Commands().make_command('avprobe') avprobe.add_options('-show_streams', '-of', 'json') avprobe_command = avprobe.gen_command() avprobe_command.append(node.path) try: streams_json = subp.check_output(avprobe_command, stderr=subp.DEVNULL, universal_newlines=True) except subp.CalledProcessError: self.enable_tabs(False) return node.streams = json.loads(streams_json) else: self.enable_tabs(False) return self.source = self.make_source(node) alist = self.source.audio_streams if alist: self.textTrackInfo.append('<b>Audio:</b>') for a in alist: self.textTrackInfo.append( '{0}: Language: <i>{1}</i>, Format: <i>{2}</i>, Channels: <i>{3}</i>' .format(a.index, a.language, a.format, a.channels)) self.textTrackInfo.append('<p></p>') slist = self.source.subtitle_streams if slist: self.textTrackInfo.append('<b>Subtitles:</b>') for s in slist: self.textTrackInfo.append('{0}: Language: <i>{1}</i>'.format( s.index, s.language)) tuple(map(lambda obj: obj.deleteLater(), self.gen_codec_groups())) self.populate_subtitles() self.populate_audio() self.populate_video() self.enable_tabs(True)
def run(self): """ Start the lsdvd thread. Emits datasig on completion. """ lsdvd = Commands().make_command('lsdvd', '-x', '-Oy') lsdvd.add_options(self.device.device_node) try: contents = subp.check_output(lsdvd.gen_command(), stderr=subp.DEVNULL) except (subp.CalledProcessError): return # lsdvd data is returned as python code so execute it glbs = {} exec(str(contents, errors='ignore'), glbs) self.datasig.emit(glbs['lsdvd'])
def populate_formats(self): avconv = Commands().make_command('avconv', '-formats') formats = subp.check_output(avconv.gen_command(), stderr=subp.DEVNULL, universal_newlines=True) refmt = re.compile(r'^\W+[^.]D?E{1}\W+(?P<ext>\w+)\W+(?P<desc>\w.*$)') with StringIO(formats) as formats: for format in formats: match = refmt.search(format) if match: self.formats[match.group('desc')] = match.group('ext') widget = QWidget() layout = QVBoxLayout() flist = list(self.formats.keys()) flist.sort(key=str.lower) for fmt in flist: radio = QRadioButton(fmt) layout.addWidget(radio) widget.setLayout(layout) self.FscrollArea.setWidget(widget)
def __init__(self, parent=None): super().__init__(parent) self.setupUi(self) try: self.cmds = Commands('lsdvd', 'mplayer', 'avconv', 'avprobe') except CommandError as err: errmsg = """The following commands are not available (Ravenc will not work properly)\n{}""".format( '\n'.join(c for c in err.command)) msg = QMessageBox(QMessageBox.Warning, 'Unmet Dependencies', errmsg) msg.setStandardButtons(QMessageBox.Ok) msg.setDefaultButton(QMessageBox.Ok) msg.exec_() else: self.cmds.register_command_class('avconv', Avconv) self.cmds.register_command_class('mplayer', Mplayer) self.populate_dvd_tree() self.btnExit.clicked.connect(self.close) self.dvdtreemodel.rowsInserted.connect(self.on_rows_inserted) self.DVDTree.expanded.connect(self.on_dvdtree_expanded) self.DVDTree.currentChanged = self.on_dvd_sel_changed self.DVDTree.rowsAboutToBeRemoved = self.rowsAboutToBeRemoved self.tabWidget.currentChanged.connect(self.on_tab_changed) self.btnSelDir.clicked.connect(self.on_seldir_clicked) self.btnStart.clicked.connect(self.on_start_clicked) self.enable_tabs(False) self.jobs = {} self.formats = {} sblabel = QLabel( 'Ravenc version: {}, \u00A9 2014, Geoff Clements'.format(_version)) self.statusbar.addWidget(sblabel)
def run(self): recrop = re.compile( r'\(-vf crop=(?P<width>\d+):(?P<height>\d+):(?P<horiz>\d+):(?P<vert>\d+)\)' ) widfreq = Counter() hgtfreq = Counter() horfreq = Counter() verfreq = Counter() length = self.stream.length.total_seconds() sample_points = (p * length / self.samples for p in range(1, self.samples)) for point in sample_points: mplayer = Commands().make_command('mplayer') mplayer.add_options('--vo=null', '--ss={}'.format(point), '--frames=100', '--nosound', '--benchmark', '--vf=cropdetect=round,format=yv12') try: mplayer.add_options( 'dvd://{}'.format(self.stream.ix), '--dvd-device={}'.format(self.stream.device)) except: mplayer.outfile = self.stream.path mplayer_proc = subp.Popen(mplayer.gen_command(), stdout=subp.PIPE, stderr=subp.DEVNULL, universal_newlines=True) for mpop in mplayer_proc.stdout: match = recrop.search(mpop) if match: widfreq[match.group('width')] += 1 hgtfreq[match.group('height')] += 1 horfreq[match.group('horiz')] += 1 verfreq[match.group('vert')] += 1 try: self.datasig.emit( (widfreq.most_common(1)[0][0], hgtfreq.most_common(1)[0][0], horfreq.most_common(1)[0][0], verfreq.most_common(1)[0][0])) except: self.datasig.emit(['0'] * 4)
def stream_data(self): mplayer = Commands().make_command('mplayer') mplayer.add_options('dvd://{}'.format(self.track['ix'])) mplayer.add_options('--dvd-device={}'.format(self.parent.node)) mplayer.add_options('--really-quiet', '--dumpstream') mplayer.add_options('--dumpfile=/dev/fd/1') return subp.Popen(mplayer.gen_command(), stdout=subp.PIPE, stderr=subp.DEVNULL)
def on_start_clicked(self): def mangle_name(name): name = re.sub('%T', self.source.title, name) name = re.sub('%N', str(self.source.track_number), name) name = re.sub('%L', str(self.source.length), name) name = re.sub('%f', self.source.title[0], name) return name job_id = self.source.unique_id if job_id in self.jobs: return avconv = Commands().make_command('avconv') avconv.global_opts.append('-y') avconv.infile_opts.append(Options('-fflags', '+genpts')) vcount = acount = scount = 0 stream_id = 0 passes = 1 codec_groups = self.gen_codec_groups() for codec_group in codec_groups: if codec_group.isChecked(): if codec_group.codec_type == 'video': avconv.video_opts.append( codec_group.make_video_opts(stream_id)) passes = max(passes, codec_group.pass_spin.value()) vcount += 1 elif codec_group.codec_type == 'audio': avconv.audio_opts.append( codec_group.make_audio_opts(stream_id)) acount += 1 elif codec_group.codec_type == 'subtitle': avconv.subt_opts.append( codec_group.make_subt_opts(stream_id)) scount += 1 if codec_group.codec_type in ('video', 'audio', 'subtitle'): avconv.map_opts.append(codec_group.make_map_opt()) stream_id += 1 if vcount == 0: avconv.map_opts.append('-vn') if acount == 0: avconv.map_opts.append('-an') if scount == 0: avconv.map_opts.append('-sn') else: avconv.infile_opts.insert( 0, Options('-analyzeduration', '200M', '-probesize', '400M')) fbuttons = self.FscrollArea.findChildren(QRadioButton) for button in fbuttons: if button.isChecked(): format = self.formats[button.text()] avconv.format_opts.append(Options('-f', format)) break opdir = self.op_dir_edit.text() or os.path.join( os.environ['HOME'], '%T', 'Title_%N') opdir = mangle_name(opdir) if not os.path.isdir(opdir): try: os.makedirs(opdir) except: return fname = self.op_file_edit.text() or '%T_Title_%N' fname = mangle_name(fname) avconv.outfile = os.path.join(opdir, fname) jobwin = BR_JobDock(avconv, passes, job_id, self.encodeclose, self.source, self) self.jobs[job_id] = jobwin jobwin.show() jobwin.start(opdir)
class BR_MainWindow(QMainWindow, Ui_BR_MainWindow): """ ravenc main window. """ encodeclose = pyqtSignal(str) def __init__(self, parent=None): super().__init__(parent) self.setupUi(self) try: self.cmds = Commands('lsdvd', 'mplayer', 'avconv', 'avprobe') except CommandError as err: errmsg = """The following commands are not available (Ravenc will not work properly)\n{}""".format( '\n'.join(c for c in err.command)) msg = QMessageBox(QMessageBox.Warning, 'Unmet Dependencies', errmsg) msg.setStandardButtons(QMessageBox.Ok) msg.setDefaultButton(QMessageBox.Ok) msg.exec_() else: self.cmds.register_command_class('avconv', Avconv) self.cmds.register_command_class('mplayer', Mplayer) self.populate_dvd_tree() self.btnExit.clicked.connect(self.close) self.dvdtreemodel.rowsInserted.connect(self.on_rows_inserted) self.DVDTree.expanded.connect(self.on_dvdtree_expanded) self.DVDTree.currentChanged = self.on_dvd_sel_changed self.DVDTree.rowsAboutToBeRemoved = self.rowsAboutToBeRemoved self.tabWidget.currentChanged.connect(self.on_tab_changed) self.btnSelDir.clicked.connect(self.on_seldir_clicked) self.btnStart.clicked.connect(self.on_start_clicked) self.enable_tabs(False) self.jobs = {} self.formats = {} sblabel = QLabel( 'Ravenc version: {}, \u00A9 2014, Geoff Clements'.format(_version)) self.statusbar.addWidget(sblabel) def populate_dvd_tree(self): self.dvdtreemodel = DVDTreeModel(self) self.DVDTree.setModel(self.dvdtreemodel) self.DVDTree.resizeColumnToContents(0) def enable_tabs(self, enable): for tab in range(1, self.tabWidget.count()): self.tabWidget.setTabEnabled(tab, enable) if not enable: self.tabWidget.setCurrentIndex(0) def populate_formats(self): avconv = Commands().make_command('avconv', '-formats') formats = subp.check_output(avconv.gen_command(), stderr=subp.DEVNULL, universal_newlines=True) refmt = re.compile(r'^\W+[^.]D?E{1}\W+(?P<ext>\w+)\W+(?P<desc>\w.*$)') with StringIO(formats) as formats: for format in formats: match = refmt.search(format) if match: self.formats[match.group('desc')] = match.group('ext') widget = QWidget() layout = QVBoxLayout() flist = list(self.formats.keys()) flist.sort(key=str.lower) for fmt in flist: radio = QRadioButton(fmt) layout.addWidget(radio) widget.setLayout(layout) self.FscrollArea.setWidget(widget) def populate_subtitles(self): widget = QWidget() layout = QVBoxLayout() for subt in self.source.subtitle_streams: groupbox = SubtitleGroup('Subtitle Stream {}'.format(subt.index), subt, self.SscrollArea) layout.addWidget(groupbox) widget.setLayout(layout) self.SscrollArea.setWidget(widget) def populate_audio(self): widget = QWidget() layout = QVBoxLayout() acount = 0 for audio in self.source.audio_streams: groupbox = AudioGroup('Audio Stream {}'.format(audio.index), audio, self.AscrollArea, acount == 0) layout.addWidget(groupbox) acount += 1 widget.setLayout(layout) self.AscrollArea.setWidget(widget) def populate_video(self): widget = QWidget() layout = QVBoxLayout() vcount = 0 for video in self.source.video_streams: groupbox = VideoGroup( 'Video Stream for Title {}'.format(video.index), video, self.VscrollArea, vcount == 0) layout.addWidget(groupbox) vcount += 1 widget.setLayout(layout) self.VscrollArea.setWidget(widget) if not self.op_dir_edit.text(): self.op_dir_edit.setText( os.path.join(os.environ['HOME'], '%T', 'Title_%N')) if not self.op_file_edit.text(): self.op_file_edit.setText('%T_Title_%N') def gen_codec_groups(self): group_parents = (self.VscrollArea, self.AscrollArea, self.SscrollArea) for parent in group_parents: groups = parent.findChildren(CodecGroup) for group in groups: yield group @pyqtSlot(int) def on_tab_changed(self, page): if page == 4: if not self.formats: self.populate_formats() @pyqtSlot() def on_rows_inserted(self): self.DVDTree.resizeColumnToContents(0) @pyqtSlot(QModelIndex) def on_dvdtree_expanded(self, index): self.DVDTree.resizeColumnToContents(0) def make_source(self, node): if node.typeinfo == 'Track': return TrackSource(node) elif node.typeinfo == 'File': return FileSource(node) else: raise NotImplementedError def on_dvd_sel_changed(self, curr, prev): if not curr.isValid(): self.textTrackInfo.clear() return node = curr.internalPointer() self.textTrackInfo.clear() if node.typeinfo == 'Track': self.DVDTree.resizeColumnToContents(1) elif node.typeinfo == 'File': avprobe = Commands().make_command('avprobe') avprobe.add_options('-show_streams', '-of', 'json') avprobe_command = avprobe.gen_command() avprobe_command.append(node.path) try: streams_json = subp.check_output(avprobe_command, stderr=subp.DEVNULL, universal_newlines=True) except subp.CalledProcessError: self.enable_tabs(False) return node.streams = json.loads(streams_json) else: self.enable_tabs(False) return self.source = self.make_source(node) alist = self.source.audio_streams if alist: self.textTrackInfo.append('<b>Audio:</b>') for a in alist: self.textTrackInfo.append( '{0}: Language: <i>{1}</i>, Format: <i>{2}</i>, Channels: <i>{3}</i>' .format(a.index, a.language, a.format, a.channels)) self.textTrackInfo.append('<p></p>') slist = self.source.subtitle_streams if slist: self.textTrackInfo.append('<b>Subtitles:</b>') for s in slist: self.textTrackInfo.append('{0}: Language: <i>{1}</i>'.format( s.index, s.language)) tuple(map(lambda obj: obj.deleteLater(), self.gen_codec_groups())) self.populate_subtitles() self.populate_audio() self.populate_video() self.enable_tabs(True) def rowsAboutToBeRemoved(self, parent, start, end): sel_list = self.DVDTree.selectedIndexes() if sel_list: pos = sel_list[0].parent().row() if pos is not None: if start <= pos <= end: self.textTrackInfo.clear() self.enable_tabs(False) @pyqtSlot() def on_seldir_clicked(self): dir = QFileDialog.getExistingDirectory( self, 'Select Output Directory', self.op_dir_edit.text(), QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks) if dir: self.op_dir_edit.setText(dir) @pyqtSlot() def on_start_clicked(self): def mangle_name(name): name = re.sub('%T', self.source.title, name) name = re.sub('%N', str(self.source.track_number), name) name = re.sub('%L', str(self.source.length), name) name = re.sub('%f', self.source.title[0], name) return name job_id = self.source.unique_id if job_id in self.jobs: return avconv = Commands().make_command('avconv') avconv.global_opts.append('-y') avconv.infile_opts.append(Options('-fflags', '+genpts')) vcount = acount = scount = 0 stream_id = 0 passes = 1 codec_groups = self.gen_codec_groups() for codec_group in codec_groups: if codec_group.isChecked(): if codec_group.codec_type == 'video': avconv.video_opts.append( codec_group.make_video_opts(stream_id)) passes = max(passes, codec_group.pass_spin.value()) vcount += 1 elif codec_group.codec_type == 'audio': avconv.audio_opts.append( codec_group.make_audio_opts(stream_id)) acount += 1 elif codec_group.codec_type == 'subtitle': avconv.subt_opts.append( codec_group.make_subt_opts(stream_id)) scount += 1 if codec_group.codec_type in ('video', 'audio', 'subtitle'): avconv.map_opts.append(codec_group.make_map_opt()) stream_id += 1 if vcount == 0: avconv.map_opts.append('-vn') if acount == 0: avconv.map_opts.append('-an') if scount == 0: avconv.map_opts.append('-sn') else: avconv.infile_opts.insert( 0, Options('-analyzeduration', '200M', '-probesize', '400M')) fbuttons = self.FscrollArea.findChildren(QRadioButton) for button in fbuttons: if button.isChecked(): format = self.formats[button.text()] avconv.format_opts.append(Options('-f', format)) break opdir = self.op_dir_edit.text() or os.path.join( os.environ['HOME'], '%T', 'Title_%N') opdir = mangle_name(opdir) if not os.path.isdir(opdir): try: os.makedirs(opdir) except: return fname = self.op_file_edit.text() or '%T_Title_%N' fname = mangle_name(fname) avconv.outfile = os.path.join(opdir, fname) jobwin = BR_JobDock(avconv, passes, job_id, self.encodeclose, self.source, self) self.jobs[job_id] = jobwin jobwin.show() jobwin.start(opdir) @pyqtSlot(str) def on_jobclose(self, job_id): try: del self.jobs[job_id] except: pass def closeEvent(self, event): if self.jobs: msg = QMessageBox( QMessageBox.Question, 'Exiting Baserip', 'There are running jobs!\nAre you sure you want to exit Baserip?' ) msg.setStandardButtons(QMessageBox.Yes | QMessageBox.No) msg.setDefaultButton(QMessageBox.No) ans = msg.exec_() if ans == QMessageBox.Yes: for job in self.jobs: self.jobs[job].cancel() event.accept() else: event.ignore() else: event.accept()