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()