Skip to content

Commit d223bef

Browse files
authored
Merge pull request #81 from GitBib/perf/optimize-mkv-loading
perf: optimize MKV file loading performance by reducing redundant mkv…
2 parents 41741ad + 7aa35af commit d223bef

File tree

9 files changed

+634
-537
lines changed

9 files changed

+634
-537
lines changed

.pre-commit-config.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ default_language_version:
66

77
repos:
88
- repo: https://github.com/pre-commit/pre-commit-hooks
9-
rev: v5.0.0
9+
rev: v6.0.0
1010
hooks:
1111
- id: trailing-whitespace
1212
- id: end-of-file-fixer
@@ -21,14 +21,14 @@ repos:
2121
- id: detect-private-key
2222

2323
- repo: https://github.com/astral-sh/uv-pre-commit
24-
rev: '0.6.10'
24+
rev: '0.8.23'
2525
hooks:
2626
- id: uv-lock
2727
- id: uv-sync
2828

2929
# Run the Ruff linter.
3030
- repo: https://github.com/astral-sh/ruff-pre-commit
31-
rev: v0.11.2
31+
rev: v0.13.3
3232
hooks:
3333
# Linter
3434
- id: ruff
@@ -37,7 +37,7 @@ repos:
3737
- id: ruff-format
3838

3939
- repo: https://github.com/pre-commit/mirrors-mypy
40-
rev: v1.15.0
40+
rev: v1.18.2
4141
hooks:
4242
- id: mypy
4343

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ test: $(TEST_FILE) $(TEST_TWO_FILE)
1515
@mkvmerge -J $(TEST_FILE)
1616
@echo "Running mkvmerge -J $(TEST_TWO_FILE)..."
1717
@mkvmerge -J $(TEST_TWO_FILE)
18-
pytest --cov=pymkv $(TEST_DIR) --cov-report=xml --junitxml=test-results/junit.xml
18+
uv run pytest --cov=pymkv $(TEST_DIR) --cov-report=xml --junitxml=test-results/junit.xml
1919

2020
$(TEST_FILE):
2121
@if [ ! -f $(TEST_FILE) ]; then \

pymkv/MKVFile.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@
5555
checking_file_path,
5656
get_file_info,
5757
verify_mkvmerge,
58-
verify_supported,
5958
)
6059

6160
T = TypeVar("T")
@@ -123,10 +122,6 @@ def __init__(
123122
raise FileNotFoundError(msg)
124123

125124
if file_path is not None:
126-
if not verify_supported(file_path, mkvmerge_path=self.mkvmerge_path):
127-
msg = f"The file '{file_path}' is not a valid Matroska file or is not supported."
128-
raise ValueError(msg)
129-
# add file title
130125
file_path = checking_file_path(file_path)
131126
try:
132127
info_json = get_file_info(
@@ -142,6 +137,11 @@ def __init__(
142137
e.cmd,
143138
output=error_output,
144139
) from e
140+
141+
if not info_json["container"]["supported"]:
142+
msg = f"The file '{file_path}' is not a valid Matroska file or is not supported."
143+
raise ValueError(msg)
144+
145145
if self.title is None and "title" in info_json["container"]["properties"]:
146146
self.title = info_json["container"]["properties"]["title"]
147147

pymkv/MKVTrack.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ def __init__( # noqa: PLR0913
142142
tag_entries: int = 0,
143143
compression: bool | None = None,
144144
) -> None:
145-
from pymkv.TypeTrack import get_track_extension
145+
from pymkv.TypeTrack import get_track_extension # noqa: PLC0415
146146

147147
# track info
148148
self._track_codec: str | None = None
@@ -215,6 +215,8 @@ def file_path(self, file_path: str) -> None:
215215
216216
This method checks if the provided file path is valid and supported by mkvmerge.
217217
If the file is valid, it sets the file path and resets the track_id to 0.
218+
If existing_info is already set, skip the verify_supported check since the file
219+
was already verified.
218220
219221
Args:
220222
file_path (str): The path to the file containing the track.
@@ -223,7 +225,7 @@ def file_path(self, file_path: str) -> None:
223225
ValueError: If the file is not a valid Matroska file or is not supported.
224226
"""
225227
fp = checking_file_path(file_path)
226-
if not verify_supported(fp, mkvmerge_path=self.mkvmerge_path):
228+
if not self._info_json and not verify_supported(fp, mkvmerge_path=self.mkvmerge_path):
227229
msg = f"The file '{file_path}' is not a valid Matroska file or is not supported."
228230
raise ValueError(msg)
229231
self._file_path = fp

pymkv/Verifications.py

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import os
4646
import subprocess as sp
4747
from collections.abc import Iterable, Sequence
48+
from functools import cache
4849
from pathlib import Path
4950
from re import match
5051
from typing import Any
@@ -122,6 +123,18 @@ def get_file_info(
122123
return json.loads(sp.check_output(cmds).decode()) # noqa: S603
123124

124125

126+
@cache
127+
def _verify_mkvmerge_cached(mkvmerge_path_tuple: tuple[str, ...]) -> bool:
128+
"""Internal cached function to verify mkvmerge availability."""
129+
try:
130+
mkvmerge_command = list(mkvmerge_path_tuple)
131+
mkvmerge_command.append("-V")
132+
output = sp.check_output(mkvmerge_command).decode() # noqa: S603
133+
except (sp.CalledProcessError, FileNotFoundError):
134+
return False
135+
return bool(match("mkvmerge.*", output))
136+
137+
125138
def verify_mkvmerge(
126139
mkvmerge_path: str | os.PathLike | Iterable[str] = "mkvmerge",
127140
) -> bool:
@@ -137,13 +150,10 @@ def verify_mkvmerge(
137150
bool
138151
True if mkvmerge is available at the specified path, False otherwise.
139152
"""
140-
try:
141-
mkvmerge_command = list(prepare_mkvtoolnix_path(mkvmerge_path))
142-
mkvmerge_command.append("-V")
143-
output = sp.check_output(mkvmerge_command).decode() # noqa: S603
144-
except (sp.CalledProcessError, FileNotFoundError):
145-
return False
146-
return bool(match("mkvmerge.*", output))
153+
mkvmerge_command = prepare_mkvtoolnix_path(mkvmerge_path)
154+
if isinstance(mkvmerge_command, list):
155+
mkvmerge_command = tuple(mkvmerge_command)
156+
return _verify_mkvmerge_cached(mkvmerge_command)
147157

148158

149159
def verify_matroska(

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ dev = [
4343
"mypy<2.0.0,>=1.14.1",
4444
"ruff<1.0.0,>=0.9.0",
4545
"sphinx-immaterial<0.14.0,>=0.12.4; python_version >= \"3.10\"",
46+
"pyinstrument>=5.1.1",
4647
]
4748

4849
[project.urls]

tests/conftest.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
from pathlib import Path
44

55
import pytest
6+
from pyinstrument import Profiler
7+
8+
TESTS_ROOT = Path.cwd()
69

710

811
@pytest.fixture
@@ -67,3 +70,18 @@ def get_path_test_srt(get_base_path: Path) -> Path:
6770
srt_path = get_base_path / "test_file.srt"
6871
create_srt_file_with_random_text(srt_path)
6972
return srt_path
73+
74+
75+
@pytest.fixture(autouse=True)
76+
def auto_profile(request: pytest.FixtureRequest) -> Generator[None, None, None]:
77+
profile_root = TESTS_ROOT / ".profiles"
78+
profiler = Profiler()
79+
profiler.start()
80+
81+
yield
82+
83+
profiler.stop()
84+
profile_root.mkdir(exist_ok=True)
85+
test_name = request.node.name
86+
results_file = profile_root / f"{test_name}.html"
87+
profiler.write_html(results_file)

tests/test_open_files.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,15 @@ def test_file_not_found() -> None:
3131
def test_file_not_support() -> None:
3232
with pytest.raises(
3333
ValueError,
34-
match="The file 'tests/conftest.py' is not a valid Matroska file or is not supported.",
34+
match=r"The file 'tests[/\\]conftest\.py' is not a valid Matroska file or is not supported\.",
3535
):
3636
MKVFile("tests/conftest.py")
3737

3838

3939
def test_track_not_support() -> None:
4040
with pytest.raises(
4141
ValueError,
42-
match="The file 'tests/conftest.py' is not a valid Matroska file or is not supported.",
42+
match=r"The file 'tests[/\\]conftest\.py' is not a valid Matroska file or is not supported\.",
4343
):
4444
MKVTrack("tests/conftest.py")
4545

0 commit comments

Comments
 (0)