Esempio n. 1
0
    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)
Esempio n. 2
0
    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'])
Esempio n. 3
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)
Esempio n. 4
0
    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)
Esempio n. 5
0
 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)
Esempio n. 6
0
 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)
Esempio n. 7
0
    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)
Esempio n. 8
0
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()