From 3bfdda1e38f525d0a21f121a6fefdba17ad97e84 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Fri, 9 Jan 2026 20:54:05 +0100 Subject: [PATCH 1/2] Update pyproject --- .gitignore | 4 ++++ pyproject.toml | 19 +++++++------------ 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index a0975ab..baa91da 100644 --- a/.gitignore +++ b/.gitignore @@ -2,9 +2,13 @@ .venv/ .ruff_cache/ *.pyc +uv.lock # Tests .pytest_cache .coverage coverage.* htmlcov/ + +# Package +*.egg-info diff --git a/pyproject.toml b/pyproject.toml index 7b133b2..9d1d1e3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,12 +4,10 @@ build-backend = "setuptools.build_meta" [project] name = "subtle" -authors = [ - {name = "Veronica Berglyd Olsen", email = "code@vkbo.net"}, -] +authors = [{ name = "Veronica Berglyd Olsen", email = "code@vkbo.net" }] description = "A simple subtitle editor and converter" -readme = {file = "README.md", content-type = "text/markdown"} -license = {text = "GNU General Public License v3"} +readme = { file = "README.md", content-type = "text/markdown" } +license = { text = "GNU General Public License v3" } classifiers = [ "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.11", @@ -23,10 +21,7 @@ classifiers = [ "Topic :: Multimedia", ] requires-python = ">=3.11" -dependencies = [ - "pyqt6>=6.4", - "pyenchant>=3.0.0", -] +dependencies = ["pyqt6>=6.4", "pyenchant>=3.0.0"] dynamic = ["version"] [project.urls] @@ -38,13 +33,13 @@ Issues = "https://github.com/vkbo/subtle/issues" subtle = "subtle:main" [tool.setuptools.dynamic] -version = {attr = "subtle.__version__"} +version = { attr = "subtle.__init__.__version__" } [tool.setuptools.packages.find] include = ["subtle*"] [tool.isort] -py_version="311" +py_version = "311" line_length = 99 wrap_length = 79 multi_line_output = 5 @@ -56,7 +51,7 @@ forced_separate = ["tests.*", "PyQt6.*"] line-length = 99 [tool.ruff.lint] -preview = true +preview = false # Rules: https://docs.astral.sh/ruff/rules select = [ From 9dec291457456afff6f5812845488c23ba80f3ea Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Fri, 9 Jan 2026 20:54:10 +0100 Subject: [PATCH 2/2] Allow adding time offset to SRT files --- subtle/formats/srtsubs.py | 12 +++++++----- subtle/gui/subsview.py | 6 +++--- subtle/gui/toolspanel.py | 16 ++++++++++++---- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/subtle/formats/srtsubs.py b/subtle/formats/srtsubs.py index f00246d..cff6869 100644 --- a/subtle/formats/srtsubs.py +++ b/subtle/formats/srtsubs.py @@ -50,13 +50,13 @@ def read(self, path: Path) -> None: logger.error("Could not read SRT file: %s", self._path, exc_info=exc) return - def write(self, path: Path | None = None) -> None: + def write(self, path: Path | None = None, offset: float = 0.0) -> None: """Writer SRT data to file.""" try: if path is None: path = self._path if path: - self._writeData(path) + self._writeData(path, round(offset*1000.0)) except Exception as exc: logger.error("Could not write SRT file: %s", self._path, exc_info=exc) return @@ -83,7 +83,7 @@ def _readData(self, path: Path) -> None: self._parseFrame(block) return - def _writeData(self, path: Path) -> None: + def _writeData(self, path: Path, offset: int = 0) -> None: """Write SRT text to file.""" with open(path, mode="w", encoding="utf-8") as fo: prev = -1.0 @@ -92,7 +92,9 @@ def _writeData(self, path: Path) -> None: for frame in self._frames: if frame.start > prev and frame.text: index += 1 - fo.write(f"{index}\n{formatTS(frame.start)} --> {formatTS(frame.end)}\n") + start = frame.start + offset + end = frame.end + offset + fo.write(f"{index}\n{formatTS(start)} --> {formatTS(end)}\n") fo.write("\n".join(frame.text)) fo.write("\n\n") prev = frame.start @@ -102,7 +104,7 @@ def _writeData(self, path: Path) -> None: skipped += 1 if skipped: logger.warning("Skipping %d entries with no text", skipped) - logger.info("Saved %d subtitle entries to SRT file.", index) + logger.info("Saved %d subtitle entries to SRT file with %d ms offset.", index, offset) return def _parseFrame(self, block: list[str]) -> None: diff --git a/subtle/gui/subsview.py b/subtle/gui/subsview.py index a021ef7..05e5d74 100644 --- a/subtle/gui/subsview.py +++ b/subtle/gui/subsview.py @@ -124,13 +124,13 @@ def updateText(self, frame: FrameBase) -> None: self._updateItemText(item, frame.text) return - @pyqtSlot(Path) - def writeSrtFile(self, path: Path) -> None: + @pyqtSlot(Path, float) + def writeSrtFile(self, path: Path, offset: float = 0.0) -> None: """Save the processed subtitles to an SRT file.""" if SHARED.media.currentTrack: writer = SRTSubs() SHARED.media.currentTrack.copyFrames(writer) - writer.write(path) + writer.write(path, offset) return @pyqtSlot(Path) diff --git a/subtle/gui/toolspanel.py b/subtle/gui/toolspanel.py index 4deb4ca..28bf875 100644 --- a/subtle/gui/toolspanel.py +++ b/subtle/gui/toolspanel.py @@ -29,8 +29,8 @@ from PyQt6.QtCore import Qt, pyqtSignal, pyqtSlot from PyQt6.QtWidgets import ( - QCheckBox, QFormLayout, QGroupBox, QHBoxLayout, QLineEdit, QListWidget, - QListWidgetItem, QPushButton, QVBoxLayout, QWidget + QCheckBox, QDoubleSpinBox, QFormLayout, QGroupBox, QHBoxLayout, QLineEdit, + QListWidget, QListWidgetItem, QPushButton, QVBoxLayout, QWidget ) logger = logging.getLogger(__name__) @@ -40,7 +40,7 @@ class GuiToolsPanel(QWidget): D_SUBS_PATH = Qt.ItemDataRole.UserRole - requestSrtSave = pyqtSignal(Path) + requestSrtSave = pyqtSignal(Path, float) requestSubsLoad = pyqtSignal(Path) def __init__(self, parent: QWidget) -> None: @@ -92,6 +92,14 @@ def __init__(self, parent: QWidget) -> None: self.srtFileName = QLineEdit(self) self.srtForm.addRow(self.tr("File Name"), self.srtFileName) + self.srtOffset = QDoubleSpinBox(self) + self.srtOffset.setDecimals(3) + self.srtOffset.setSingleStep(0.1) + self.srtOffset.setValue(0.0) + self.srtOffset.setMinimum(-499999.0) + self.srtOffset.setMaximum(499999.0) + self.srtForm.addRow(self.tr("Time Offset"), self.srtOffset) + self.srtSubsDir = QCheckBox(self.tr("Use 'Subs' folder"), self) self.srtSubsDir.clicked.connect(self._updateTrackInfo) @@ -176,7 +184,7 @@ def _clickedSaveSrt(self) -> None: folder = Path(self.srtSaveDir.text()) folder.mkdir(exist_ok=True) if name := self.srtFileName.text().strip(): - self.requestSrtSave.emit(folder / name) + self.requestSrtSave.emit(folder / name, self.srtOffset.value()) except Exception as exc: logger.error("Failed to prepare subtitles folder", exc_info=exc) return