Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -55,28 +55,35 @@ subtle_gui = ["assets/*"]
line-length = 120

[tool.ruff.lint]
preview = false

# Rules: https://docs.astral.sh/ruff/rules
select = [
"A", # flake8-builtins (A)
"ANN", # flake8-annotations (ANN)
"B", # flake8-bugbear (B)
"C4", # flake8-comprehensions (C4)
"D", # pydocstyle (D)
"E", # pycodestyle (E)
"F", # Pyflakes (F)
"FA", # flake8-future-annotations (FA)
"G", # flake8-logging-format (G)
"I", # isort (I)
"ISC", # flake8-implicit-str-concat (ISC)
"LOG", # flake8-logging (LOG)
"PERF", # Perflint (PERF)
"PIE", # flake8-pie (PIE)
"PLC", # Pylint Convention (PLC)
"PLE", # Pylint Error (PLE)
"PLR17", # Refactor (PLR) - Only PLR17xx
"PLW", # Pylint Warning (PLW)
"PT", # flake8-pytest-style (PT)
"Q", # flake8-quotes (Q)
"RET", # flake8-return (RET)
"RUF", # Ruff-specific rules (RUF)
"SIM", # flake8-simplify (SIM)
"SLF", # flake8-self (SLF)
"SLOT", # flake8-slots (SLOT)
"TC", # flake8-type-checking (TC)
"TID", # flake8-tidy-imports (TID)
"UP", # pyupgrade (UP)
"W", # pycodestyle (W)
]
Expand All @@ -95,14 +102,18 @@ ignore = [
"RUF001", # ambiguous-unicode-character-string
"RUF015", # unnecessary-iterable-allocation-for-first-element
"RUF067", # non-empty-init-module
"RUF076", # pytest-fixture-autouse
"SIM108", # if-else-block-instead-of-if-exp
"UP015", # redundant-open-modes
"UP030", # format-literals
]

[tool.ruff.lint.per-file-ignores]
"tests/*" = ["ANN", "D101", "D102", "D105", "PLC2701", "RUF069", "SLF", "TC"]

[tool.ruff.lint.isort]
lines-between-types = 1
forced-separate = ["tests"]

[tool.ruff.format]
quote-style = "double"

Expand Down
20 changes: 10 additions & 10 deletions subtle_gui/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,26 +28,26 @@

from typing import TYPE_CHECKING

from PyQt6.QtWidgets import QApplication

from subtle_gui.config import Config
from subtle_gui.shared import SharedData

from PyQt6.QtWidgets import QApplication

if TYPE_CHECKING: # pragma: no cover
from subtle_gui.guimain import GuiMain

# Package Meta
# ============

# fmt: off
__package__ = "subtle_gui"
__copyright__ = "Copyright (C) Veronica Berglyd Olsen"
__license__ = "GPLv3"
__author__ = "Veronica Berglyd Olsen"
__package__ = "subtle_gui"
__copyright__ = "Copyright (C) Veronica Berglyd Olsen"
__license__ = "GPLv3"
__author__ = "Veronica Berglyd Olsen"
__maintainer__ = "Veronica Berglyd Olsen"
__email__ = "code@vkbo.net"
__version__ = "26.1.1"
__date__ = "2026-06-24"
__email__ = "code@vkbo.net"
__version__ = "26.1.1"
__date__ = "2026-06-24"
# fmt: on

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -114,7 +114,7 @@ def main(sysArgs: list | None = None) -> GuiMain | None:
)

# Defaults
logLevel = logging.WARN
logLevel = logging.WARNING
fmtColor = FORCE_COLOR
fmtLong = False

Expand Down
11 changes: 5 additions & 6 deletions subtle_gui/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,10 +161,9 @@ def decodeTS(value: str | None, default: int = 0, fmt: T_Subs = "SRT") -> int:
return 3600000 * int(value[0:2]) + 60000 * int(value[3:5]) + int(value[6:8] + value[9:12])
except Exception:
pass
elif fmt == "SSA" and len(value) == 10:
if value[1] == ":" and value[4] == ":" and value[7] in ":.,":
try:
return 3600000 * int(value[0]) + 60000 * int(value[2:4]) + 10 * int(value[5:7] + value[8:10])
except Exception:
pass
elif fmt == "SSA" and len(value) == 10 and value[1] == ":" and value[4] == ":" and value[7] in ":.,":
try:
return 3600000 * int(value[0]) + 60000 * int(value[2:4]) + 10 * int(value[5:7] + value[8:10])
except Exception:
pass
return default
4 changes: 2 additions & 2 deletions subtle_gui/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@
from pathlib import Path
from typing import Literal

from subtle_gui.common import jsonEncode

from PyQt6.QtCore import PYQT_VERSION, PYQT_VERSION_STR, QT_VERSION, QT_VERSION_STR, QSize, QStandardPaths, QSysInfo
from PyQt6.QtGui import QFont
from PyQt6.QtWidgets import QApplication

from subtle_gui.common import jsonEncode

logger = logging.getLogger(__name__)


Expand Down
4 changes: 2 additions & 2 deletions subtle_gui/core/media.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@

from typing import TYPE_CHECKING

from PyQt6.QtCore import QObject, pyqtSignal

from subtle_gui import SHARED
from subtle_gui.common import decodeTS
from subtle_gui.constants import MediaType
Expand All @@ -33,8 +35,6 @@
from subtle_gui.formats.srtsubs import SRTSubs
from subtle_gui.formats.ssasubs import SSASubs

from PyQt6.QtCore import QObject, pyqtSignal

if TYPE_CHECKING:
from collections.abc import Iterable
from pathlib import Path
Expand Down
4 changes: 2 additions & 2 deletions subtle_gui/core/mkvextract.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@

from typing import TYPE_CHECKING

from subtle_gui.common import checkInt

from PyQt6.QtCore import QObject, QProcess, pyqtSignal, pyqtSlot

from subtle_gui.common import checkInt

if TYPE_CHECKING:
from pathlib import Path

Expand Down
4 changes: 2 additions & 2 deletions subtle_gui/core/spellcheck.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@
from pathlib import Path
from typing import TYPE_CHECKING

from subtle_gui import CONFIG

from PyQt6.QtCore import QLocale

from subtle_gui import CONFIG

if TYPE_CHECKING:
from collections.abc import Iterator

Expand Down
6 changes: 3 additions & 3 deletions subtle_gui/formats/pgssubs.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING

from subtle_gui.common import formatTS
from subtle_gui.formats.base import FrameBase, SubtitlesBase

from PyQt6.QtCore import QMargins, QPoint, QRect, QSize
from PyQt6.QtGui import QColor, QImage, QPainter, qRgba

from subtle_gui.common import formatTS
from subtle_gui.formats.base import FrameBase, SubtitlesBase

if TYPE_CHECKING:
from collections.abc import Iterable
from pathlib import Path
Expand Down
11 changes: 5 additions & 6 deletions subtle_gui/gui/filetree.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@

from pathlib import Path

from subtle_gui import CONFIG, SHARED

from PyQt6.QtCore import QModelIndex, pyqtSlot
from PyQt6.QtGui import QFileSystemModel
from PyQt6.QtWidgets import QTreeView, QWidget

from subtle_gui import CONFIG, SHARED

logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -71,10 +71,9 @@ def saveSettings(self) -> None:
@pyqtSlot(QModelIndex)
def _itemDoubleClicked(self, index: QModelIndex) -> None:
"""Process item double click in the file tree."""
if (path := Path(self._model.filePath(index))).is_file():
if path != self._current:
SHARED.media.loadMediaFile(path)
self._current = path
if (path := Path(self._model.filePath(index))).is_file() and path != self._current:
SHARED.media.loadMediaFile(path)
self._current = path

@pyqtSlot(str)
def _directoryLoaded(self, path: str) -> None:
Expand Down
4 changes: 2 additions & 2 deletions subtle_gui/gui/highlighter.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@
import logging
import re

from subtle_gui import SHARED

from PyQt6.QtGui import QColor, QSyntaxHighlighter, QTextBlockUserData, QTextCharFormat, QTextDocument
from PyQt6.QtWidgets import QApplication

from subtle_gui import SHARED

logger = logging.getLogger(__name__)

SPELL_RX = re.compile(r"\b[^\s\-–—\/<>]+\b", re.UNICODE)
Expand Down
4 changes: 2 additions & 2 deletions subtle_gui/gui/imageviewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@

import logging

from subtle_gui.formats.base import FrameBase

from PyQt6.QtCore import QRect, QRectF, Qt, pyqtSlot
from PyQt6.QtGui import QPixmap, QResizeEvent
from PyQt6.QtWidgets import QGraphicsScene, QGraphicsView, QHBoxLayout, QWidget

from subtle_gui.formats.base import FrameBase

logger = logging.getLogger(__name__)


Expand Down
27 changes: 15 additions & 12 deletions subtle_gui/gui/mediaview.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,6 @@

from typing import TYPE_CHECKING

from subtle_gui import CONFIG, SHARED
from subtle_gui.common import formatTS
from subtle_gui.constants import GuiLabels, MediaType, trConst
from subtle_gui.core.mediafile import EXTRACTABLE, SUBTITLE_FILE
from subtle_gui.core.mkvextract import MkvExtract

from PyQt6.QtCore import QModelIndex, pyqtSlot
from PyQt6.QtWidgets import (
QHBoxLayout,
Expand All @@ -44,6 +38,12 @@
QWidget,
)

from subtle_gui import CONFIG, SHARED
from subtle_gui.common import formatTS
from subtle_gui.constants import GuiLabels, MediaType, trConst
from subtle_gui.core.mediafile import EXTRACTABLE, SUBTITLE_FILE
from subtle_gui.core.mkvextract import MkvExtract

if TYPE_CHECKING:
from pathlib import Path

Expand Down Expand Up @@ -183,12 +183,15 @@ def _itemDoubleClicked(self, index: QModelIndex) -> None:
"""Process track double click in the media view."""
if (file := SHARED.media.mediaFile) and (item := self.tracksView.itemFromIndex(index)):
idx = item.text(self.C_TRACK)
if idx not in self._extracted:
if (track := SHARED.media.getTrack(idx)) and track.trackType == MediaType.SUBS:
path = file.dumpFile(idx)
track.setTrackFile(path)
self._runTrackExtraction(file.filePath, [(idx, path)])
self._emitTrack = idx
if (
idx not in self._extracted
and (track := SHARED.media.getTrack(idx))
and track.trackType == MediaType.SUBS
):
path = file.dumpFile(idx)
track.setTrackFile(path)
self._runTrackExtraction(file.filePath, [(idx, path)])
self._emitTrack = idx
if idx in self._extracted and not self._emitTrack:
SHARED.media.setCurrentTrack(idx)

Expand Down
27 changes: 15 additions & 12 deletions subtle_gui/gui/subsview.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,15 @@

from pathlib import Path

from PyQt6.QtCore import QModelIndex, Qt, pyqtSignal, pyqtSlot
from PyQt6.QtWidgets import QAbstractItemView, QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget

from subtle_gui import CONFIG, SHARED
from subtle_gui.common import formatTS
from subtle_gui.constants import MediaType
from subtle_gui.formats.base import FrameBase
from subtle_gui.formats.srtsubs import SRTSubs

from PyQt6.QtCore import QModelIndex, Qt, pyqtSignal, pyqtSlot
from PyQt6.QtWidgets import QAbstractItemView, QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget

logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -157,15 +157,18 @@ def selectNearby(self, step: int) -> None:
@pyqtSlot(QModelIndex)
def _itemClicked(self, index: QModelIndex) -> None:
"""Process item click in the subtitles list."""
if (track := SHARED.media.currentTrack) and (item := self.subEntries.itemFromIndex(index)):
if frame := SHARED.media.currentTrack.getFrame(item.data(self.C_DATA, self.D_INDEX)):
if frame.imageBased:
image = frame.getImage()
if (ocrTool := SHARED.ocr) and not (text := frame.text):
text = ocrTool.processImage(frame.index, image, [track.language])
frame.setText(text)
self.updateText(frame)
self.subsFrameUpdated.emit(frame)
if (
(track := SHARED.media.currentTrack)
and (item := self.subEntries.itemFromIndex(index))
and (frame := SHARED.media.currentTrack.getFrame(item.data(self.C_DATA, self.D_INDEX)))
):
if frame.imageBased:
image = frame.getImage()
if (ocrTool := SHARED.ocr) and not (text := frame.text):
text = ocrTool.processImage(frame.index, image, [track.language])
frame.setText(text)
self.updateText(frame)
self.subsFrameUpdated.emit(frame)

##
# Internal Functions
Expand Down
8 changes: 4 additions & 4 deletions subtle_gui/gui/texteditor.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@

import logging

from subtle_gui import CONFIG, SHARED
from subtle_gui.formats.base import FrameBase
from subtle_gui.gui.highlighter import GuiDocHighlighter, TextBlockData

from PyQt6.QtCore import QPoint, Qt, pyqtSignal, pyqtSlot
from PyQt6.QtGui import QShortcut, QTextBlock, QTextBlockFormat, QTextCharFormat, QTextCursor
from PyQt6.QtWidgets import QComboBox, QMenu, QTextEdit, QToolBar, QToolButton, QVBoxLayout, QWidget

from subtle_gui import CONFIG, SHARED
from subtle_gui.formats.base import FrameBase
from subtle_gui.gui.highlighter import GuiDocHighlighter, TextBlockData

logger = logging.getLogger(__name__)


Expand Down
11 changes: 5 additions & 6 deletions subtle_gui/gui/toolspanel.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,6 @@

from pathlib import Path

from subtle_gui import SHARED
from subtle_gui.constants import MediaType

from PyQt6.QtCore import Qt, pyqtSignal, pyqtSlot
from PyQt6.QtWidgets import (
QCheckBox,
Expand All @@ -43,6 +40,9 @@
QWidget,
)

from subtle_gui import SHARED
from subtle_gui.constants import MediaType

logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -198,9 +198,8 @@ def _clickedSaveSrt(self) -> None:
@pyqtSlot()
def _clickedLoadSubs(self) -> None:
"""Process load subs button click."""
if items := self.subsList.selectedItems():
if (path := Path(items[0].data(self.D_SUBS_PATH))).is_file():
self.requestSubsLoad.emit(path)
if (items := self.subsList.selectedItems()) and (path := Path(items[0].data(self.D_SUBS_PATH))).is_file():
self.requestSubsLoad.emit(path)

@pyqtSlot()
def _updateTrackInfo(self) -> None:
Expand Down
Loading