#!/usr/bin/env python3 """ MediaManager 测试文件 测试视频切分、场景检测、信息提取等功能 """ import os import sys import unittest import tempfile import shutil from pathlib import Path from unittest.mock import Mock, patch # 添加项目根目录到Python路径 project_root = Path(__file__).parent.parent sys.path.insert(0, str(project_root)) # 导入要测试的模块 from python_core.services.media_manager import ( MediaManager, DependencyManager, VideoProcessorFactory, FFProbeVideoInfoExtractor, OpenCVVideoInfoExtractor, PySceneDetectSceneDetector, OpenCVSceneDetector, OpenCVThumbnailGenerator, MD5FileHashCalculator, OpenCVVideoSegmentCreator ) class TestDependencyManager(unittest.TestCase): """测试依赖管理器""" def setUp(self): self.dependency_manager = DependencyManager() def test_dependency_initialization(self): """测试依赖初始化""" # 检查依赖管理器是否正确初始化 self.assertIsInstance(self.dependency_manager._dependencies, dict) # 检查是否包含预期的依赖 self.assertIn('opencv', self.dependency_manager._dependencies) self.assertIn('scenedetect', self.dependency_manager._dependencies) def test_opencv_availability(self): """测试OpenCV可用性检查""" opencv_available = self.dependency_manager.is_available('opencv') print(f"OpenCV available: {opencv_available}") if opencv_available: # 如果OpenCV可用,测试模块获取 cv2 = self.dependency_manager.get_module('opencv', 'cv2') self.assertIsNotNone(cv2) numpy = self.dependency_manager.get_module('opencv', 'numpy') self.assertIsNotNone(numpy) def test_scenedetect_availability(self): """测试PySceneDetect可用性检查""" scenedetect_available = self.dependency_manager.is_available('scenedetect') print(f"PySceneDetect available: {scenedetect_available}") if scenedetect_available: # 如果PySceneDetect可用,测试模块获取 VideoManager = self.dependency_manager.get_module('scenedetect', 'VideoManager') self.assertIsNotNone(VideoManager) def test_dependency_info(self): """测试依赖信息获取""" opencv_info = self.dependency_manager.get_dependency_info('opencv') self.assertIsInstance(opencv_info, dict) self.assertIn('available', opencv_info) all_deps = self.dependency_manager.get_all_dependencies() self.assertIsInstance(all_deps, dict) self.assertIn('opencv', all_deps) self.assertIn('scenedetect', all_deps) class TestVideoProcessorFactory(unittest.TestCase): """测试视频处理器工厂""" def setUp(self): self.dependency_manager = DependencyManager() self.factory = VideoProcessorFactory(self.dependency_manager) def test_create_video_info_extractor(self): """测试创建视频信息提取器""" try: extractor = self.factory.create_video_info_extractor() self.assertIsNotNone(extractor) print(f"Created video info extractor: {type(extractor).__name__}") except Exception as e: self.fail(f"Failed to create video info extractor: {e}") def test_create_scene_detector(self): """测试创建场景检测器""" try: detector = self.factory.create_scene_detector() self.assertIsNotNone(detector) print(f"Created scene detector: {type(detector).__name__}") except Exception as e: self.fail(f"Failed to create scene detector: {e}") def test_create_thumbnail_generator(self): """测试创建缩略图生成器""" if self.dependency_manager.is_available('opencv'): try: generator = self.factory.create_thumbnail_generator() self.assertIsNotNone(generator) print(f"Created thumbnail generator: {type(generator).__name__}") except Exception as e: self.fail(f"Failed to create thumbnail generator: {e}") else: print("OpenCV not available, skipping thumbnail generator test") def test_create_hash_calculator(self): """测试创建哈希计算器""" calculator = self.factory.create_hash_calculator() self.assertIsNotNone(calculator) self.assertIsInstance(calculator, MD5FileHashCalculator) class TestVideoInfoExtractors(unittest.TestCase): """测试视频信息提取器""" def setUp(self): # 使用assets文件夹中的测试视频 self.test_video = project_root / "assets" / "1" / "1752031789460.mp4" if not self.test_video.exists(): self.skipTest(f"Test video not found: {self.test_video}") def test_ffprobe_extractor(self): """测试FFProbe视频信息提取器""" try: extractor = FFProbeVideoInfoExtractor() info = extractor.extract_video_info(str(self.test_video)) # 验证返回的信息结构 self.assertIsInstance(info, dict) self.assertIn('duration', info) self.assertIn('width', info) self.assertIn('height', info) self.assertIn('fps', info) self.assertIn('file_size', info) print(f"FFProbe video info: {info}") # 验证数值合理性 self.assertGreater(info['duration'], 0) self.assertGreater(info['width'], 0) self.assertGreater(info['height'], 0) self.assertGreater(info['fps'], 0) self.assertGreater(info['file_size'], 0) except Exception as e: print(f"FFProbe extractor failed (expected if ffprobe not available): {e}") def test_opencv_extractor(self): """测试OpenCV视频信息提取器""" dependency_manager = DependencyManager() if not dependency_manager.is_available('opencv'): self.skipTest("OpenCV not available") try: extractor = OpenCVVideoInfoExtractor(dependency_manager) info = extractor.extract_video_info(str(self.test_video)) # 验证返回的信息结构 self.assertIsInstance(info, dict) self.assertIn('duration', info) self.assertIn('width', info) self.assertIn('height', info) self.assertIn('fps', info) self.assertIn('file_size', info) print(f"OpenCV video info: {info}") # 验证数值合理性 self.assertGreater(info['duration'], 0) self.assertGreater(info['width'], 0) self.assertGreater(info['height'], 0) self.assertGreater(info['fps'], 0) self.assertGreater(info['file_size'], 0) except Exception as e: self.fail(f"OpenCV extractor failed: {e}") class TestSceneDetectors(unittest.TestCase): """测试场景检测器""" def setUp(self): # 使用assets文件夹中的测试视频 self.test_video = project_root / "assets" / "1" / "1752031789460.mp4" if not self.test_video.exists(): self.skipTest(f"Test video not found: {self.test_video}") self.dependency_manager = DependencyManager() def test_pyscenedetect_detector(self): """测试PySceneDetect场景检测器""" if not self.dependency_manager.is_available('scenedetect'): self.skipTest("PySceneDetect not available") try: detector = PySceneDetectSceneDetector(self.dependency_manager) scene_changes = detector.detect_scenes(str(self.test_video), threshold=30.0) # 验证返回的场景变化点 self.assertIsInstance(scene_changes, list) self.assertGreater(len(scene_changes), 0) # 第一个应该是0.0 self.assertEqual(scene_changes[0], 0.0) # 场景变化点应该是递增的 for i in range(1, len(scene_changes)): self.assertGreater(scene_changes[i], scene_changes[i-1]) print(f"PySceneDetect found {len(scene_changes)} scene changes: {scene_changes}") except Exception as e: print(f"PySceneDetect detector failed: {e}") def test_opencv_detector(self): """测试OpenCV场景检测器""" if not self.dependency_manager.is_available('opencv'): self.skipTest("OpenCV not available") try: detector = OpenCVSceneDetector(self.dependency_manager) scene_changes = detector.detect_scenes(str(self.test_video), threshold=30.0) # 验证返回的场景变化点 self.assertIsInstance(scene_changes, list) self.assertGreater(len(scene_changes), 0) # 第一个应该是0.0 self.assertEqual(scene_changes[0], 0.0) # 场景变化点应该是递增的 for i in range(1, len(scene_changes)): self.assertGreater(scene_changes[i], scene_changes[i-1]) print(f"OpenCV found {len(scene_changes)} scene changes: {scene_changes}") except Exception as e: self.fail(f"OpenCV detector failed: {e}") class TestMediaManager(unittest.TestCase): """测试媒体管理器""" def setUp(self): # 创建临时目录用于测试 self.temp_dir = tempfile.mkdtemp() self.test_video = project_root / "assets" / "1" / "1752031789460.mp4" if not self.test_video.exists(): self.skipTest(f"Test video not found: {self.test_video}") # 使用临时目录创建MediaManager with patch('python_core.config.settings') as mock_settings: mock_settings.temp_dir = Path(self.temp_dir) self.media_manager = MediaManager() def tearDown(self): # 清理临时目录 shutil.rmtree(self.temp_dir, ignore_errors=True) def test_media_manager_initialization(self): """测试媒体管理器初始化""" self.assertIsNotNone(self.media_manager) self.assertIsNotNone(self.media_manager.dependency_manager) self.assertIsNotNone(self.media_manager.factory) def test_video_info_extraction(self): """测试视频信息提取""" try: video_info = self.media_manager._get_video_info(str(self.test_video)) self.assertIsInstance(video_info, dict) self.assertIn('duration', video_info) self.assertIn('width', video_info) self.assertIn('height', video_info) self.assertIn('fps', video_info) print(f"Video info extracted: {video_info}") except Exception as e: self.fail(f"Video info extraction failed: {e}") def test_scene_detection(self): """测试场景检测""" try: scene_changes = self.media_manager._detect_scene_changes(str(self.test_video)) self.assertIsInstance(scene_changes, list) self.assertGreater(len(scene_changes), 0) print(f"Scene changes detected: {scene_changes}") except Exception as e: self.fail(f"Scene detection failed: {e}") def test_video_splitting(self): """测试视频切分功能""" try: # 首先检测场景变化 scene_changes = self.media_manager._detect_scene_changes(str(self.test_video)) # 如果场景变化太少,手动添加一些切分点 if len(scene_changes) < 3: video_info = self.media_manager._get_video_info(str(self.test_video)) duration = video_info.get('duration', 10.0) scene_changes = [0.0, duration / 2, duration] print(f"Using scene changes for splitting: {scene_changes}") # 执行视频切分 segments = self.media_manager._split_video_by_scenes( str(self.test_video), scene_changes, "test_original_id", ["测试", "分镜"] ) print(f"Created {len(segments)} video segments") # 验证切分结果 self.assertIsInstance(segments, list) for i, segment in enumerate(segments): print(f"Segment {i}: {segment.filename}, duration: {segment.duration}s") # 验证片段文件是否存在(如果创建成功的话) if hasattr(segment, 'file_path') and segment.file_path: segment_path = Path(segment.file_path) if segment_path.exists(): print(f" File exists: {segment_path}") self.assertGreater(segment_path.stat().st_size, 0) except Exception as e: print(f"Video splitting failed (may be expected if dependencies missing): {e}") def run_comprehensive_test(): """运行全面的测试""" print("=" * 60) print("MediaManager 综合测试") print("=" * 60) # 检查测试视频 test_videos = list((project_root / "assets").rglob("*.mp4")) print(f"Found {len(test_videos)} test videos:") for video in test_videos[:5]: # 只显示前5个 print(f" - {video}") if not test_videos: print("❌ No test videos found in assets folder") return print("\n" + "=" * 60) print("开始测试...") print("=" * 60) # 运行测试套件 loader = unittest.TestLoader() suite = unittest.TestSuite() # 添加测试类 suite.addTests(loader.loadTestsFromTestCase(TestDependencyManager)) suite.addTests(loader.loadTestsFromTestCase(TestVideoProcessorFactory)) suite.addTests(loader.loadTestsFromTestCase(TestVideoInfoExtractors)) suite.addTests(loader.loadTestsFromTestCase(TestSceneDetectors)) suite.addTests(loader.loadTestsFromTestCase(TestMediaManager)) # 运行测试 runner = unittest.TextTestRunner(verbosity=2) result = runner.run(suite) print("\n" + "=" * 60) print("测试总结") print("=" * 60) print(f"运行测试: {result.testsRun}") print(f"失败: {len(result.failures)}") print(f"错误: {len(result.errors)}") print(f"跳过: {len(result.skipped) if hasattr(result, 'skipped') else 0}") if result.failures: print("\n失败的测试:") for test, traceback in result.failures: print(f" - {test}: {traceback}") if result.errors: print("\n错误的测试:") for test, traceback in result.errors: print(f" - {test}: {traceback}") return result.wasSuccessful() if __name__ == "__main__": success = run_comprehensive_test() sys.exit(0 if success else 1)