Example #1
0
def _tokenize_time(timestr):
    timestr = re.sub(r'\s+', '', timestr)
    SubAssert(timestr, _('Sync: time spec cannot be empty'))

    time_args = dict(sign=None, h=0, m=0, s=0, ms=0)

    if timestr[0] in '+-':
        time_args['sign'] = int('%s1' % timestr[0])
        timestr = timestr[1:]

    found_units = set()

    expr = re.compile(r'''(?P<value>\d+)(?P<unit>[a-zA-Z]+)''')
    parsed_len = 0
    for elem in expr.finditer(timestr):
        val = elem.group('value')
        unit  = elem.group('unit')
        SubAssert(unit not in found_units,
                  _('Sync: non-unique time units in time spec'))
        found_units.add(unit)
        time_args[unit] = int(val)
        parsed_len += (len(unit) + len(val))

    SubAssert(parsed_len == len(timestr),
              _('Sync: some characters not parsed'))

    try:
        return _Time(**time_args)
    except TypeError:
        raise SubException(_('Sync: incorrect time spec units'))
Example #2
0
    def __initWidgets(self):
        minimalSizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)

        # List of subtitles
        subListDelegate = SubListItemDelegate()
        self._model = QStandardItemModel(0, 3, self)
        self._model.setHorizontalHeaderLabels([_("Begin"), _("End"), _("Subtitle")])
        self._subList = QTableView(self)
        self._subList.setModel(self._model)
        self._subList.setItemDelegateForColumn(0, subListDelegate)
        self._subList.setItemDelegateForColumn(1, subListDelegate)
        self._subList.horizontalHeader().setSectionResizeMode(2, QHeaderView.Stretch)

        self._searchBar = SearchBar(self)
        self._searchBar.hide()

        # Top toolbar
        toolbar = QHBoxLayout()
        toolbar.setAlignment(Qt.AlignLeft)
        #toolbar.addWidget(someWidget....)
        toolbar.addStretch(1)

        # Main layout
        grid = QGridLayout()
        grid.setSpacing(10)
        grid.setContentsMargins(0, 3, 0, 0)
        grid.addLayout(toolbar, 0, 0, 1, 1) # stretch to the right
        grid.addWidget(self._subList, 1, 0)
        grid.addWidget(self._searchBar, 2, 0)
        self.setLayout(grid)
Example #3
0
    def saveProperties(self):
        subProperties = None

        try:
            subProperties = self._createSubtitleProperties()
        except Exception as e:
            dialog = QMessageBox(self)
            dialog.setIcon(QMessageBox.Critical)
            dialog.setWindowTitle(_("Incorrect value"))
            dialog.setText(_("Could not save SPF file because of incorrect parameters."));
            dialog.setDetailedText(str(e));
            dialog.exec()
            return

        fileDialog = FileDialog(
            parent = self,
            caption = _('Save Subtitle Properties'),
            directory = self._settings.getPropertyFilesPath()
        )
        fileDialog.setAcceptMode(QFileDialog.AcceptSave)
        fileDialog.setFileMode(QFileDialog.AnyFile)

        if fileDialog.exec():
            filename = fileDialog.selectedFiles()[0]
            if not filename.endswith(".spf"):
                filename = "%s%s" % (filename, ".spf")
            self._settings.setPropertyFilesPath(os.path.dirname(filename))
            subProperties.save(filename)
            self._settings.addPropertyFile(filename)
            self.close()
Example #4
0
    def _createEncodingBox(self):
        groupbox = QGroupBox(_("File Encoding"))
        layout = QGridLayout()


        self._autoEncoding = QCheckBox(_("Auto input encoding"), self)
        self._inputEncoding = QComboBox(self)
        self._inputEncoding.addItems(ALL_ENCODINGS)
        self._inputEncoding.setDisabled(self._autoEncoding.isChecked())
        inputLabel = QLabel(_("Input encoding"))

        self._changeEncoding = QCheckBox(_("Change encoding on save"), self)
        self._outputEncoding = QComboBox(self)
        self._outputEncoding.addItems(ALL_ENCODINGS)
        self._outputEncoding.setEnabled(self._changeEncoding.isChecked())
        outputLabel = QLabel(_("Output encoding"))

        layout.addWidget(self._autoEncoding, 0, 0)
        layout.addWidget(self._inputEncoding, 1, 0)
        layout.addWidget(inputLabel, 1, 1)
        layout.addWidget(self._changeEncoding, 2, 0)
        layout.addWidget(self._outputEncoding, 3, 0)
        layout.addWidget(outputLabel, 3, 1)
        groupbox.setLayout(layout)
        return groupbox
Example #5
0
    def addPoint(self):
        rows = self._current.editor.selectedRows()
        newStart = self._videoWidget.position

        if len(rows) == 0 or newStart is None:
            self._err.showMessage(_("Select a subtitle and position in current video first."))
            return

        # row and sub reflect the same subtitle, but we need both for different things
        row = rows[0]
        sub = self._current.data.subtitles[row]

        # Don't add the same subtitle or the same sync time twice
        if any(row == point.subNo or newStart == point.start
               for point in _syncPoints(self._current.model)):
            self._err.showMessage(_("Can't repeat synchronization points"))
            return

        if sub.fps != newStart.fps:
            self._err.showMessage(_("Subtitle and video have different framerates (%(sub)s vs"
                                    "%(vid)s") % dict(sub=sub.fps, vid=newStart.fps))
            return

        delta = sub.end - sub.start
        newEnd = newStart + delta

        startItem, endItem, textItem = createRow(sub, newStart, newEnd)
        subNoItem = QStandardItem(str(row))
        subNoItem.setEditable(False)
        textItem.setEditable(False)
        rmItem = QStandardItem("")
        self._current.model.appendRow([subNoItem, startItem, endItem, textItem, rmItem])

        self._rmButton(self._table, rmItem)
Example #6
0
 def __sub__(self, other):
     """Defines FrameTime - FrameTime"""
     SubAssert(self._fps == other._fps, _("FPS values are not equal"))
     SubAssert(self._full_seconds >= other._full_seconds,
         _("Cannot substract higher time from lower"))
     result = self._full_seconds - other._full_seconds
     return FrameTime(fps = self._fps, seconds = result)
Example #7
0
    def detectFpsFromMovie(cls, movieFile, default = 23.976):
        """Fetch movie FPS from MPlayer output or return given default."""

        # initialize with a default FPS value, but not with a movieFile
        videoInfo = VideoInfo(float(default))

        command = ['mplayer',
            '-really-quiet', '-vo', 'null', '-ao', 'null', '-frames', '0', '-identify', movieFile]

        try:
            mpOut, mpErr = Popen(command, stdout=PIPE, stderr=PIPE).communicate()
            log.debug(mpOut)
            log.debug(mpErr)

            # Overwrite default (not fetched from video) values.
            # If there's any error on changing videoInfo.fps, whole videoInfo won't be changed at
            # all.
            videoInfo.fps = float(re.search(r'ID_VIDEO_FPS=([\w/.]+)\s?', str(mpOut)).group(1))
            videoInfo.videoPath = movieFile
        except OSError:
            log.warning(_("Couldn't run mplayer. It has to be installed and placed in your $PATH "
                "to detect FPS."))
        except AttributeError:
            log.warning(_("Couldn't get FPS from %(movie)s. Using default value: %(fps)s.") %
                {"movie": movieFile, "fps": videoInfo.fps})
        else:
            pass
            log.debug(P_(
                "Got %(fps)s FPS from '%(movie)s'.",
                "Got %(fps)s FPS from '%(movie)s'.",
                int(videoInfo.fps)) % {"fps": videoInfo.fps, "movie": videoInfo.videoPath})

        return videoInfo
Example #8
0
 def openHelp(self):
     url = "https://github.com/mgoral/subconvert/wiki"
     if QDesktopServices.openUrl(QUrl(url)) is False:
         dialog = QMessageBox(self)
         dialog.setIcon(QMessageBox.Critical)
         dialog.setWindowTitle(_("Couldn't open URL"))
         dialog.setText(_("""Failed to open URL: <a href="%(url)s">%(url)s</a>.""") %
             {"url": url})
         dialog.exec()
Example #9
0
 def linkVideo(self):
     movieExtensions = "%s%s" % ("*.", ' *.'.join(File.MOVIE_EXTENSIONS))
     fileDialog = FileDialog(
         parent = self,
         caption = _("Select a video"),
         directory = self._settings.getLatestDirectory(),
         filter = _("Video files (%s);;All files (*)") % movieExtensions)
     fileDialog.setFileMode(QFileDialog.ExistingFile)
     if fileDialog.exec():
         movieFilePath = fileDialog.selectedFiles()[0]
         self._setVideoLink(movieFilePath)
Example #10
0
 def offset(self, seconds):
     data = self.data
     fps = data.subtitles.fps
     if fps is None:
         log.error(_("No FPS for '%s' (empty subtitles)." % self.filePath))
         return
     ft = FrameTime(data.subtitles.fps, seconds=seconds)
     data.subtitles.offset(ft)
     command = ChangeData(self.filePath, data,
                          _("Offset by: %s") % ft.toStr())
     self._subtitleData.execute(command)
Example #11
0
    def _createUndoView(self, history):
        undoGroup = QGroupBox(_("History of changes"), self)
        undoGroupLayout = QVBoxLayout()
        undoGroup.setLayout(undoGroupLayout)

        undoView = QUndoView(history, self)
        undoView.setEmptyLabel(_("<Original file>"))
        undoGroupLayout.addWidget(undoView)

        mainLayout = self.layout()
        mainLayout.addWidget(undoGroup)
Example #12
0
    def _chooseSubProperties(self):
        fileDialog = FileDialog(
            parent = self,
            caption = _("Open Subtitle Properties"),
            directory = self._settings.getPropertyFilesPath(),
            filter = _("Subtitle Properties (*.spf);;All files (*)")
        )
        fileDialog.setFileMode(QFileDialog.ExistingFile)

        if fileDialog.exec():
            filename = fileDialog.selectedFiles()[0]
            self._useSubProperties(filename)
Example #13
0
    def openProperties(self):
        fileDialog = FileDialog(
            parent = self,
            caption = _("Open Subtitle Properties"),
            directory = self._settings.getPropertyFilesPath(),
            filter = _("Subtitle Properties (*.spf);;All files (*)")
        )
        fileDialog.setFileMode(QFileDialog.ExistingFile)

        if fileDialog.exec():
            filename = fileDialog.selectedFiles()[0]
            self._settings.setPropertyFilesPath(os.path.dirname(filename))
            subProperties = SubtitleProperties(list(self._formats.values()), filename)
            self.changeProperties(subProperties)
Example #14
0
    def _createFormatBox(self):
        groupbox = QGroupBox(_("Subtitle format"))
        layout = QGridLayout()

        displayedFormats = list(self._formats.keys())
        displayedFormats.sort()
        self._outputFormat = QComboBox(self)
        self._outputFormat.addItems(displayedFormats)
        formatLabel = QLabel(_("Output format"))

        layout.addWidget(self._outputFormat, 0, 0)
        layout.addWidget(formatLabel, 0, 1)
        groupbox.setLayout(layout)
        return groupbox
Example #15
0
    def _createFpsBox(self):
        groupbox = QGroupBox(_("FPS"))
        layout = QHBoxLayout()

        self._autoFps = QCheckBox(_("Auto FPS"), self)

        self._fps = QComboBox(self)
        self._fps.addItems(["23.976", "24", "25", "29.97", "30"])
        self._fps.setEditable(True)

        layout.addWidget(self._autoFps)
        layout.addWidget(self._fps)
        groupbox.setLayout(layout)
        return groupbox
Example #16
0
    def read(self, encoding = None):
        if encoding is None:
            encoding = self.detectEncoding()

        fileInput = []
        try:
            with open(self._filePath, mode='r', encoding=encoding) as file_:
                fileInput = file_.readlines()
        except LookupError as msg:
            raise SubFileError(_("Unknown encoding name: '%s'.") % encoding)
        except UnicodeDecodeError:
            vals = {"file": self._filePath, "enc": encoding}
            raise SubFileError(_("Cannot handle '%(file)s' with '%(enc)s' encoding.") % vals)
        return fileInput
Example #17
0
    def _addEncodingsBox(self, row, addAuto):
        mainLayout = self.layout()

        encodingLabel = QLabel(_("File encoding:"), self)

        self._encodingBox = QComboBox(self)
        if addAuto is True:
            self._encodingBox.addItem(AUTO_ENCODING_STR)
        self._encodingBox.addItems(ALL_ENCODINGS)
        self._encodingBox.setToolTip(_("Change file encoding"))
        self._encodingBox.setEditable(True)

        mainLayout.addWidget(encodingLabel, row, 0)
        mainLayout.addWidget(self._encodingBox, row, 1)
Example #18
0
 def changeSelectedFilesInputEncoding(self, inputEncoding):
     items = self.__fileList.selectedItems()
     for item in items:
         filePath = item.text(0)
         data = self._subtitleData.data(filePath)
         if data.inputEncoding != inputEncoding:
             try:
                 data.encode(inputEncoding)
             except UnicodeDecodeError:
                 # TODO: indicate with something more than log entry
                 log.error(_("Cannot decode subtitles to '%s' encoding.") % inputEncoding)
             else:
                 command = ChangeData(filePath, data, _("Input encoding: %s") % inputEncoding)
                 self._subtitleData.execute(command)
Example #19
0
    def _createButtons(self):
        widget = QWidget(self)
        layout = QHBoxLayout()

        self._openButton = QPushButton(_("Open"))
        self._saveButton = QPushButton(_("Save"))
        self._closeButton = QPushButton(_("Close"))

        layout.addWidget(self._openButton)
        layout.addWidget(self._saveButton)
        layout.addWidget(self._closeButton)

        widget.setLayout(layout)
        return widget
Example #20
0
def loadSpf(formats, filePath):
    try:
        spf = SubtitleProperties(formats, filePath)
    except FileNotFoundError:
        log.critical(_("No such file: '%s'") % filePath)
        sys.exit(2)
    return spf
Example #21
0
    def __init__(self, filePath, oldSubtitle, newSubtitle, index, parent = None):
        super(ChangeSubtitle, self).__init__(filePath, parent)
        self.setText(_("Subtitle change (%d: %s)") % (int(index + 1), subExcerpt(newSubtitle)))

        self._oldSubtitle = oldSubtitle
        self._newSubtitle = newSubtitle
        self._subNo = index
Example #22
0
    def __initContextMenu(self):
        self._contextMenu = QMenu(self)
        self.setContextMenuPolicy(Qt.CustomContextMenu)
        af = ActionFactory(self)

        insertSub = af.create(title = _("&Insert subtitle"), icon = "list-add",
            connection = self.insertNewSubtitle)
        self._contextMenu.addAction(insertSub)

        insertSub = af.create(title = _("&Add subtitle"), icon = "list-add",
            connection = self.addNewSubtitle)
        self._contextMenu.addAction(insertSub)

        removeSub = af.create(title = _("&Remove subtitles"), icon = "list-remove",
            connection = self.removeSelectedSubtitles)
        self._contextMenu.addAction(removeSub)
Example #23
0
 def openTab(self, filePath, background=False):
     if self._subtitleData.fileExists(filePath):
         tabIndex = self.__addTab(filePath)
         if background is False:
             self.showTab(tabIndex)
     else:
         log.error(_("SubtitleEditor not created for %s!" % filePath))
Example #24
0
 def changeFps(self, fps):
     data = self.data
     if data.fps != fps:
         data.subtitles.changeFps(fps)
         data.fps = fps
         command = ChangeData(self.filePath, data, _("FPS: %s") % fps)
         self._subtitleData.execute(command)
Example #25
0
    def sync(self, syncPointList):
        """Synchronise subtitles using a given list of SyncPoints."""

        if len(syncPointList) == 0:
            return

        subsCopy = self._subs.clone()

        syncPointList.sort()

        SubAssert(syncPointList[0].subNo >= 0)
        SubAssert(syncPointList[0].subNo < subsCopy.size())
        SubAssert(syncPointList[-1].subNo < subsCopy.size())

        # Always start from the first subtitle.
        firstSyncPoint = self._getLowestSyncPoint(syncPointList, subsCopy)
        if firstSyncPoint != syncPointList[0]:
            syncPointList.insert(0, firstSyncPoint)

        for i, syncPoint in enumerate(syncPointList):

            # Algorithm:
            # 1. Calculate time deltas between sync points and between subs:
            #        DE_OLD = subTime[secondSyncSubNo] - subTime[firstSyncSubNo]
            #        DE_NEW = secondSyncTime - firstSyncTime
            # 2. Calculate proportional sub position within DE_OLD:
            #        d = (subTime - subTime[firstSubNo]) / DE_OLD
            # 3. "d" is constant within deltas, so we can now calculate newSubTime:
            #        newSubTime = DE_NEW * d + firstSyncTime

            firstSyncPoint = syncPointList[i]
            secondSyncPoint = self._getSyncPointOrEnd(i + 1, syncPointList, subsCopy)

            log.debug(_("Syncing times for sync points:"))
            log.debug("  %s" % firstSyncPoint)
            log.debug("  %s" % secondSyncPoint)

            # A case for the last one syncPoint
            if firstSyncPoint == secondSyncPoint:
                continue

            secondSubNo = secondSyncPoint.subNo
            firstSubNo = firstSyncPoint.subNo

            firstOldSub = subsCopy[firstSubNo]
            secondOldSub = subsCopy[secondSubNo]

            oldStartDelta, oldEndDelta = self._getDeltas(firstOldSub, secondOldSub)
            newStartDelta, newEndDelta = self._getDeltas(firstSyncPoint, secondSyncPoint)

            for subNo in range(firstSubNo, secondSubNo + 1):
                sub = subsCopy[subNo]
                newStartTime = self._calculateTime(sub.start, firstOldSub.start,
                    firstSyncPoint.start, oldStartDelta, newStartDelta)
                newEndTime = self._calculateTime(sub.end, firstOldSub.end,
                    firstSyncPoint.end, oldEndDelta, newEndDelta)

                self._subs.changeSubStart(subNo, newStartTime)
                self._subs.changeSubEnd(subNo, newEndTime)
Example #26
0
    def offsetSelectedFiles(self, seconds):
        if seconds == 0:
            return

        items = self.__fileList.selectedItems()
        for item in items:
            filePath = item.text(0)
            data = self._subtitleData.data(filePath)
            fps = data.subtitles.fps
            if fps is None:
                log.error(_("No FPS for '%s' (empty subtitles)." % filePath))
                continue
            ft = FrameTime(fps, seconds=seconds)
            data.subtitles.offset(ft)
            command = ChangeData(filePath, data,
                                _("Offset by: %s") % ft.toStr())
            self._subtitleData.execute(command)
Example #27
0
    def parseFile(self, subFile, inputEncoding , fps):
        content = subFile.read(inputEncoding)
        try:
            subtitles = self._parser.parse(content, fps)
        except SubParsingError as msg:
            log.error(msg)
            raise SubException(_("Couldn't parse file '%s'" % subFile.path))

        return subtitles
Example #28
0
 def changeSelectedFilesOutputEncoding(self, outputEncoding):
     items = self.__fileList.selectedItems()
     for item in items:
         filePath = item.text(0)
         data = self._subtitleData.data(filePath)
         if data.outputEncoding != outputEncoding:
             data.outputEncoding = outputEncoding
             command = ChangeData(filePath, data, _("Output encoding: %s") % outputEncoding)
             self._subtitleData.execute(command)
Example #29
0
    def openFile(self):
        sub_extensions = self.__getAllSubExtensions()
        str_sub_exts = ' '.join(['*.%s' % ext for ext in sub_extensions[1:]])

        fileDialog = FileDialog(
            parent = self,
            caption = _("Open file"),
            directory = self._settings.getLatestDirectory(),
            filter = _("Subtitles (%s);;All files (*)") % str_sub_exts
        )
        fileDialog.addEncodings(True)
        fileDialog.setFileMode(QFileDialog.ExistingFiles)

        if fileDialog.exec():
            filenames = fileDialog.selectedFiles()
            encoding = fileDialog.getEncoding()
            self._settings.setLatestDirectory(os.path.dirname(filenames[0]))
            self._openFiles(filenames, encoding)
Example #30
0
    def __init__(self, args, parser):
        super(MainWindow, self).__init__()
        log.debug(_("Theme search paths: %s") % QIcon.themeSearchPaths())
        log.debug(_("Used theme name: '%s'") % QIcon.themeName())

        self.setObjectName("main_window")

        self._subtitleData = DataController(parser, self)

        self.__initGui()
        self.__initActions()
        self.__initMenuBar()
        self.__initShortcuts()
        self.__updateMenuItemsState()
        self.__connectSignals()
        self.restoreWidgetState()

        self.handleArgs(args)