Replace the prior release tree with the current plugin, frontend, tests, and documentation. Document retired node IDs and the public Gitea update source.
99 lines
3.8 KiB
Python
99 lines
3.8 KiB
Python
from __future__ import annotations
|
|
|
|
import importlib.util
|
|
import tempfile
|
|
import unittest
|
|
from pathlib import Path
|
|
from types import SimpleNamespace
|
|
from unittest import mock
|
|
|
|
|
|
PLUGIN_ROOT = Path(__file__).resolve().parents[1]
|
|
MODULE_PATH = PLUGIN_ROOT / "nodes" / "video_trim.py"
|
|
|
|
|
|
def load_module():
|
|
spec = importlib.util.spec_from_file_location("o1key_video_trim_test", MODULE_PATH)
|
|
module = importlib.util.module_from_spec(spec)
|
|
assert spec.loader is not None
|
|
spec.loader.exec_module(module)
|
|
return module
|
|
|
|
|
|
class VideoTrimTests(unittest.TestCase):
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
cls.module = load_module()
|
|
|
|
def test_schema_keeps_released_widget_order_and_internal_path_value(self):
|
|
required = self.module.O1keyVideoTrim.INPUT_TYPES()["required"]
|
|
self.assertEqual(
|
|
list(required),
|
|
["视频路径", "开始时间", "结束时间", "固定时长"],
|
|
)
|
|
self.assertEqual(required["视频路径"][0], "STRING")
|
|
self.assertIn("粘贴", required["视频路径"][1]["placeholder"])
|
|
|
|
def test_fixed_duration_is_clamped_to_the_end_of_the_source(self):
|
|
module = self.module
|
|
recorded_command = []
|
|
|
|
def fake_run(command, **_kwargs):
|
|
recorded_command[:] = command
|
|
Path(command[-1]).write_bytes(b"trimmed-video")
|
|
return SimpleNamespace(returncode=0, stderr="")
|
|
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
source = Path(temp_dir) / "source.mp4"
|
|
source.write_bytes(b"source-video")
|
|
video_factory = mock.Mock(side_effect=lambda path: SimpleNamespace(path=path))
|
|
fake_input_impl = SimpleNamespace(VideoFromFile=video_factory)
|
|
|
|
with (
|
|
mock.patch.object(module, "InputImpl", fake_input_impl),
|
|
mock.patch.object(module, "_probe_duration", return_value=10.0),
|
|
mock.patch.object(module, "_resolve_ffmpeg", return_value="ffmpeg"),
|
|
mock.patch.object(module.subprocess, "run", side_effect=fake_run),
|
|
):
|
|
video, duration = module.O1keyVideoTrim().trim(
|
|
视频路径=f' "{source}" ',
|
|
开始时间=9.0,
|
|
结束时间=0.0,
|
|
固定时长=4.0,
|
|
)
|
|
|
|
try:
|
|
self.assertEqual(duration, 4.0)
|
|
self.assertEqual(recorded_command[recorded_command.index("-ss") + 1], "6.000")
|
|
self.assertEqual(recorded_command[recorded_command.index("-t") + 1], "4.000")
|
|
self.assertEqual(video.path, recorded_command[-1])
|
|
finally:
|
|
Path(video.path).unlink(missing_ok=True)
|
|
|
|
def test_fixed_duration_longer_than_source_returns_the_full_video(self):
|
|
module = self.module
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
source = Path(temp_dir) / "short.mp4"
|
|
source.write_bytes(b"source-video")
|
|
video_factory = mock.Mock(side_effect=lambda path: SimpleNamespace(path=path))
|
|
fake_input_impl = SimpleNamespace(VideoFromFile=video_factory)
|
|
|
|
with (
|
|
mock.patch.object(module, "InputImpl", fake_input_impl),
|
|
mock.patch.object(module, "_probe_duration", return_value=3.0),
|
|
mock.patch.object(module, "_resolve_ffmpeg") as resolve_ffmpeg,
|
|
):
|
|
video, duration = module.O1keyVideoTrim().trim(
|
|
视频路径=str(source),
|
|
开始时间=2.0,
|
|
固定时长=5.0,
|
|
)
|
|
|
|
self.assertEqual(video.path, str(source))
|
|
self.assertEqual(duration, 3.0)
|
|
resolve_ffmpeg.assert_not_called()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|