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
2 changes: 1 addition & 1 deletion pymkv/MKVFile.py
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,7 @@ def add_attachment(self, attachment: str | MKVAttachment) -> None:
msg = "Attachment is not str of MKVAttachment"
raise TypeError(msg)

def get_track(self, track_num: int | None = None) -> MKVTrack:
def get_track(self, track_num: int | None = None) -> MKVTrack | list[MKVTrack]:
"""Get a :class:`~pymkv.MKVTrack` from the :class:`~pymkv.MKVFile` object.

Parameters
Expand Down
12 changes: 6 additions & 6 deletions pymkv/Timestamp.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from __future__ import annotations

from re import match
import re


class Timestamp:
Expand Down Expand Up @@ -175,7 +175,7 @@ def __getitem__(self, index: int) -> int:
def ts(self) -> str:
"""Generates the timestamp specified in the object."""
# parse timestamp format
format_groups = match(r"^(([Hh]{1,2}):)?([Mm]{1,2}):([Ss]{1,2})(\.([Nn]{1,9}))?$", self.form).groups()
format_groups = re.match(r"^(([Hh]{1,2}):)?([Mm]{1,2}):([Ss]{1,2})(\.([Nn]{1,9}))?$", self.form).groups()
timestamp_format = [format_groups[i] is not None for i in (1, 2, 3, 5)]

# create timestamp string
Expand Down Expand Up @@ -248,16 +248,16 @@ def form(self, form: str) -> None:
self._form = form

@staticmethod
def verify(timestamp: str | int) -> bool:
def verify(timestamp: str) -> bool:
"""Verify a timestamp has the proper form to be used in mkvmerge.

timestamp (str, int):
timestamp (str):
The timestamp to be verified.
"""
if not isinstance(timestamp, str):
msg = f'"{type(timestamp)}" is not str type'
raise TypeError(msg)
elif match(r"^[0-9]{1,2}(:[0-9]{1,2}){1,2}(\.[0-9]{1,9})?$", timestamp): # noqa: RET506
elif re.match(r"^[0-9]{1,2}(:[0-9]{1,2}){1,2}(\.[0-9]{1,9})?$", timestamp): # noqa: RET506
return True
return False

Expand Down Expand Up @@ -305,7 +305,7 @@ def splitting_timestamp(self, timestamp: str) -> None:
The seconds (self.ss) will be set to 56.
The nanoseconds (self.nn) will be set to 789012345.
"""
timestamp_groups = match(r"^(([0-9]{1,2}):)?([0-9]{1,2}):([0-9]{1,2})(\.([0-9]{1,9}))?$", timestamp).groups()
timestamp_groups = re.match(r"^(([0-9]{1,2}):)?([0-9]{1,2}):([0-9]{1,2})(\.([0-9]{1,9}))?$", timestamp).groups()

timestamp = [timestamp_groups[i] for i in (1, 2, 3, 4)]
timestamp_clean = []
Expand Down
9 changes: 8 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,14 @@ def get_path_test_file_two(get_base_path: Path) -> Path:
@pytest.fixture(autouse=True)
def cleanup_mkv_files(get_base_path: Path, get_path_test_file: Path, get_path_test_file_two: Path) -> None: # noqa: PT004
yield
for ext in ["*.mkv", "*.mp4", "*.ogg"]:
for ext in ["*.mkv", "*.mp4", "*.ogg", "*.txt"]:
for file_path in get_base_path.glob(ext):
if file_path not in (get_path_test_file, get_path_test_file_two):
file_path.unlink()


@pytest.fixture()
def temp_file(tmp_path: Path) -> str:
file = tmp_path / "test_attachment.txt"
file.write_text("Test content")
return str(file)
117 changes: 117 additions & 0 deletions tests/test_actions_track.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,120 @@ def test_move_track_front_and_mux(get_base_path: Path, get_path_test_file: Path)
assert len(mkv.tracks) == 2 # noqa: PLR2004
assert mkv.tracks[0].track_type == "audio"
assert mkv.tracks[1].track_type == "video"


def test_mux_and_test_title(get_base_path: Path, get_path_test_file: Path) -> None:
output_file = get_base_path / "file-test.mkv"

mkv = MKVFile(get_path_test_file)
mkv.title = "Test title in mkv file"
mkv.mux(output_file)

mkv = MKVFile(output_file)

assert mkv.title == "Test title in mkv file"


def test_mux_and_test_track_name(get_base_path: Path, get_path_test_file: Path) -> None:
output_file = get_base_path / "file-test.mkv"

mkv = MKVFile(get_path_test_file)
mkv.tracks[0].track_name = "Test track name"
mkv.mux(output_file)

mkv = MKVFile(output_file)

assert mkv.tracks[0].track_name == "Test track name"


def test_move_track_end_raises(get_path_test_file: Path) -> None:
mkv = MKVFile(get_path_test_file)

with pytest.raises(IndexError):
mkv.move_track_end(-1)

with pytest.raises(IndexError):
mkv.move_track_end(2)


def test_move_track_end_and_mux(get_base_path: Path, get_path_test_file: Path) -> None:
output_file = get_base_path / "file-test.mkv"

mkv = MKVFile(get_path_test_file)
mkv.move_track_end(0)
mkv.mux(output_file)

mkv = MKVFile(output_file)

assert len(mkv.tracks) == 2 # noqa: PLR2004
assert mkv.tracks[0].track_type == "audio"
assert mkv.tracks[1].track_type == "video"


def test_move_track_forward_raises(get_path_test_file: Path) -> None:
mkv = MKVFile(get_path_test_file)

with pytest.raises(IndexError):
mkv.move_track_forward(-1)

with pytest.raises(IndexError):
mkv.move_track_forward(2)

with pytest.raises(IndexError):
mkv.move_track_forward(1)


def test_move_track_forward_and_mux(get_base_path: Path, get_path_test_file: Path) -> None:
output_file = get_base_path / "file-test.mkv"

mkv = MKVFile(get_path_test_file)
mkv.move_track_forward(0)
mkv.mux(output_file)

mkv = MKVFile(output_file)

assert len(mkv.tracks) == 2 # noqa: PLR2004
assert mkv.tracks[0].track_type == "audio"
assert mkv.tracks[1].track_type == "video"


def test_move_track_backward_raises(get_path_test_file: Path) -> None:
mkv = MKVFile(get_path_test_file)

with pytest.raises(IndexError):
mkv.move_track_backward(-1)

with pytest.raises(IndexError):
mkv.move_track_backward(2)


def test_move_track_backward_and_mux(get_base_path: Path, get_path_test_file: Path) -> None:
output_file = get_base_path / "file-test.mkv"

mkv = MKVFile(get_path_test_file)
mkv.move_track_backward(1)
mkv.mux(output_file)

mkv = MKVFile(output_file)

assert len(mkv.tracks) == 2 # noqa: PLR2004
assert mkv.tracks[0].track_type == "audio"
assert mkv.tracks[1].track_type == "video"


def test_get_track(get_path_test_file: Path) -> None:
mkv = MKVFile(get_path_test_file)
track = mkv.get_track(1)

assert track.track_type == "audio"

tracks = mkv.get_track()

assert isinstance(tracks, list)
assert len(tracks) == 2 # noqa: PLR2004


def test_get_track_error(get_path_test_file: Path) -> None:
mkv = MKVFile(get_path_test_file)
with pytest.raises(IndexError):
mkv.get_track(2)
6 changes: 6 additions & 0 deletions tests/test_add_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ def test_add_file(get_base_path: Path, get_path_test_file: Path, get_path_test_f
mkv_two = MKVFile(get_path_test_file_two)
mkv.add_file(mkv_two)

for i, track in enumerate(mkv.tracks):
expected_file_id = i // 2 # the file_id should change every two tracks
expected_track_id = i % 2 # the track_id should alternate between 0 and 1
assert track.file_id == expected_file_id
assert track.track_id == expected_track_id

assert len(mkv.tracks) == 4 # noqa: PLR2004
mkv.mux(output_file)

Expand Down
74 changes: 74 additions & 0 deletions tests/test_mkv_attachment.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
from pathlib import Path

import pytest

from pymkv.MKVAttachment import MKVAttachment


def test_init(temp_file: str) -> None:
attachment = MKVAttachment(temp_file)
assert attachment.file_path == temp_file
assert attachment.mime_type == "text/plain"
assert attachment.name is None
assert attachment.description is None
assert attachment.attach_once is False


def test_init_with_options(temp_file: str) -> None:
attachment = MKVAttachment(temp_file, name="Test", description="Test Description", attach_once=True)
assert attachment.file_path == temp_file
assert attachment.name == "Test"
assert attachment.description == "Test Description"
assert attachment.attach_once is True


def test_file_path_setter_valid(temp_file: str) -> None:
attachment = MKVAttachment(temp_file)
new_file = Path(temp_file).parent / "new_file.txt"
new_file.write_text("New content")
attachment.file_path = str(new_file)
assert attachment.file_path == str(new_file)
assert attachment.mime_type == "text/plain"
assert attachment.name is None


def test_file_path_setter_invalid() -> None:
with pytest.raises(FileNotFoundError):
MKVAttachment("non_existent_file.txt")


def test_repr(temp_file: str) -> None:
attachment = MKVAttachment(temp_file, name="Test", description="Test Description")
repr_str = repr(attachment)
assert "file_path" in repr_str
assert "name" in repr_str
assert "description" in repr_str
assert "mime_type" in repr_str


def test_mime_type_guess(tmp_path: Path) -> None:
# Test different file types
file_types = {
"test.txt": "text/plain",
"test.jpg": "image/jpeg",
"test.png": "image/png",
"test.mp3": "audio/mpeg",
}

for file_name, expected_mime in file_types.items():
file_path = tmp_path / file_name
file_path.write_text("Test content")
attachment = MKVAttachment(str(file_path))
assert attachment.mime_type == expected_mime


def test_file_path_expansion(tmp_path: Path, monkeypatch) -> None: # noqa: ANN001
fake_home = tmp_path / "fake_home"
fake_home.mkdir()
monkeypatch.setenv("HOME", str(fake_home))

test_file = fake_home / "test_file.txt"
test_file.write_text("Test content")

attachment = MKVAttachment("~/test_file.txt")
assert attachment.file_path == str(test_file)
9 changes: 9 additions & 0 deletions tests/test_open_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
def test_open_file(get_path_test_file: Path) -> None:
mkv = MKVFile(get_path_test_file)

assert mkv.title is None
assert len(mkv.tracks) == 2 # noqa: PLR2004


Expand Down Expand Up @@ -45,3 +46,11 @@ def test_empty_mkv_file() -> None:

assert mkv.title == "test"
assert len(mkv.tracks) == 0


def test_verify_mkvmerge_in_mkv_file() -> None:
with pytest.raises(
FileNotFoundError,
match="mkvmerge is not at the specified path, add it there or changed mkvmerge_path property",
):
MKVFile(title="test", mkvmerge_path="mkvmerge_test")
91 changes: 91 additions & 0 deletions tests/test_timestamp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import pytest

from pymkv.Timestamp import Timestamp


def test_init() -> None:
ts1 = Timestamp("01:23:45.678")
assert str(ts1) == "01:23:45.678"

ts2 = Timestamp(3661) # 1 hour, 1 minute, 1 second
assert str(ts2) == "01:01:01"

ts3 = Timestamp(hh=2, mm=30, ss=15, nn=500000000)
assert str(ts3) == "02:30:15.5"


def test_comparison() -> None:
ts1 = Timestamp("01:00:00")
ts2 = Timestamp("02:00:00")
ts3 = Timestamp("01:00:00")

assert ts1 < ts2
assert ts2 > ts1
assert ts1 <= ts3
assert ts1 >= ts3
assert ts1 == ts3
assert ts1 != ts2


def test_properties() -> None:
ts = Timestamp("12:34:56.789")
assert ts.hh == 12 # noqa: PLR2004
assert ts.mm == 34 # noqa: PLR2004
assert ts.ss == 56 # noqa: PLR2004
assert ts.nn == 789000000 # noqa: PLR2004


def test_setters() -> None:
ts = Timestamp()
ts.hh = 10
ts.mm = 20
ts.ss = 30
ts.nn = 400000000
assert str(ts) == "10:20:30.4"

# Test overflow handling
ts.mm = 70
assert ts.mm == 0


def test_verify() -> None:
assert Timestamp.verify("01:23:45")
assert Timestamp.verify("01:23:45.678")
assert not Timestamp.verify("25.00:00")


def test_extract() -> None:
ts = Timestamp()
ts.extract(3661) # 1 hour, 1 minute, 1 second

assert ts.hh == 1
assert ts.mm == 1
assert ts.ss == 1
assert ts.nn == 0


def test_getitem() -> None:
ts = Timestamp("01:23:45.678")
assert ts[0] == 1 # hours
assert ts[1] == 23 # minutes # noqa: PLR2004
assert ts[2] == 45 # seconds # noqa: PLR2004
assert ts[3] == 45 # seconds (again, as per the implementation) # noqa: PLR2004


def test_invalid_input() -> None:
with pytest.raises(TypeError):
Timestamp([]) # Invalid type

with pytest.raises(ValueError): # noqa: PT011
Timestamp("invalid_timestamp")


def test_ts_property() -> None:
ts = Timestamp("01:23:45.678")
assert ts.ts == "01:23:45.678"

ts.ts = "02:30:00"
assert ts.ts == "02:30:00"

with pytest.raises(TypeError):
ts.ts = [] # Invalid type