fix: 重构

This commit is contained in:
root
2025-07-11 18:59:29 +08:00
parent 5dad5cfae7
commit 82c1c71771
6 changed files with 842 additions and 176 deletions

View File

@@ -0,0 +1,204 @@
# PySceneDetect 依赖安装指南
## 📦 依赖添加
### 已添加到 requirements.txt
```
scenedetect[opencv]
```
这个版本包含了OpenCV支持提供最佳的场景检测性能。
## 🚀 安装方法
### 方法1: 使用pip直接安装 (推荐)
```bash
pip install scenedetect[opencv]
```
### 方法2: 使用requirements.txt安装
```bash
pip install -r python_core/requirements.txt
```
### 方法3: 使用提供的脚本
#### Windows:
```cmd
scripts\install_scenedetect.bat
```
#### Linux/macOS:
```bash
chmod +x scripts/install_scenedetect_simple.sh
./scripts/install_scenedetect_simple.sh
```
#### Python脚本:
```bash
python scripts/install_dependencies.py
```
## 🧪 验证安装
### 1. 检查导入
```python
import scenedetect
print(f"PySceneDetect版本: {scenedetect.__version__}")
```
### 2. 测试基本功能
```python
from scenedetect import VideoManager, SceneManager
from scenedetect.detectors import ContentDetector
# 创建管理器
video_manager = VideoManager([])
scene_manager = SceneManager()
scene_manager.add_detector(ContentDetector())
print("✅ PySceneDetect功能正常")
```
## 🔧 集成状态
### media_manager.py 中的集成
-**主要方法**: `_detect_scene_changes()` 优先使用PySceneDetect
-**备用方案**: 如果PySceneDetect不可用自动回退到OpenCV方法
-**错误处理**: 完善的异常处理和日志记录
### 使用流程
```python
# 在media_manager.py中
def _detect_scene_changes(self, file_path: str, threshold: float = 30.0) -> List[float]:
if SCENEDETECT_AVAILABLE:
try:
# 使用PySceneDetect进行高精度检测
return self._detect_with_pyscenedetect(file_path, threshold)
except Exception as e:
logger.error(f"PySceneDetect failed: {e}")
# 回退到OpenCV方法
return self._detect_scene_changes_opencv(file_path, threshold)
else:
# 直接使用OpenCV方法
return self._detect_scene_changes_opencv(file_path, threshold)
```
## 📊 性能对比
| 方法 | 准确性 | 速度 | 资源占用 | 推荐场景 |
|------|--------|------|----------|----------|
| PySceneDetect | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐ | 高质量分镜 |
| OpenCV | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | 快速处理 |
## 🎯 配置参数
### PySceneDetect 参数
```python
# 在_detect_scene_changes中
scene_manager.add_detector(ContentDetector(threshold=threshold))
```
**threshold 参数说明**:
- **10-20**: 高敏感度,检测更多场景变化
- **25-35**: 中等敏感度,适合大多数情况 (默认30.0)
- **40-50**: 低敏感度,只检测明显的场景变化
### 使用建议
```python
# 高质量视频,需要精确分镜
scene_changes = media_manager._detect_scene_changes(video_path, threshold=25.0)
# 快速预览,粗略分镜
scene_changes = media_manager._detect_scene_changes(video_path, threshold=40.0)
```
## 🔍 故障排除
### 常见问题
#### 1. 安装失败
```
ERROR: Could not find a version that satisfies the requirement scenedetect
```
**解决方案**:
```bash
# 更新pip
pip install --upgrade pip
# 重新安装
pip install scenedetect[opencv]
```
#### 2. 导入失败
```
ImportError: No module named 'scenedetect'
```
**解决方案**:
```bash
# 检查Python环境
which python
pip list | grep scenedetect
# 重新安装
pip install --force-reinstall scenedetect[opencv]
```
#### 3. OpenCV依赖问题
```
ImportError: No module named 'cv2'
```
**解决方案**:
```bash
# 安装OpenCV
pip install opencv-python
# 或者重新安装完整版本
pip install scenedetect[opencv]
```
### 检查安装状态
```python
# 检查PySceneDetect
try:
import scenedetect
print(f"✅ PySceneDetect {scenedetect.__version__}")
except ImportError:
print("❌ PySceneDetect 未安装")
# 检查OpenCV
try:
import cv2
print(f"✅ OpenCV {cv2.__version__}")
except ImportError:
print("❌ OpenCV 未安装")
```
## 📚 相关文档
- [PySceneDetect 官方文档](https://pyscenedetect.readthedocs.io/)
- [PySceneDetect GitHub](https://github.com/Breakthrough/PySceneDetect)
- [场景检测测试脚本](../scripts/test_scene_detection.py)
## 🎉 安装完成后
安装成功后,系统将:
1. **自动使用PySceneDetect**: 新导入的视频将使用更准确的场景检测
2. **保持兼容性**: 如果PySceneDetect不可用自动回退到OpenCV
3. **提升分镜质量**: 更准确的场景边界检测
4. **支持多种视频格式**: 更好的格式兼容性
### 验证效果
重新导入视频文件,观察日志输出:
```
INFO: PySceneDetect found 5 scene changes in video
DEBUG: Scene change timestamps: [0.0, 15.2, 32.8, 48.1, 65.3, 78.9]
```
---
*PySceneDetect 依赖安装完成,享受更准确的视频场景检测!*

View File

@@ -28,7 +28,7 @@ class Settings(BaseSettings):
project_root: Path = project_root
temp_dir: Path = Field(default_factory=lambda: project_root / ".mixvideo" / "temp")
cache_dir: Path = Field(default_factory=lambda: project_root / ".mixvideo" / "cache")
projects_dir: Path = Field(default_factory=lambda: project_root / "MixVideoProjects")
projects_dir: Path = Field(default_factory=lambda: project_root / ".mixvideo"/"MixVideoProjects")
# Video Processing
max_video_resolution: str = "1920x1080"

View File

@@ -17,24 +17,369 @@ from python_core.config import settings
from python_core.utils.logger import logger
from python_core.utils.jsonrpc import create_response_handler
# 视频处理库
try:
import cv2
import numpy as np
VIDEO_LIBS_AVAILABLE = True
except ImportError:
logger.warning("Video processing libraries not available. Install opencv-python for full functionality.")
VIDEO_LIBS_AVAILABLE = False
# 依赖管理器 - 遵循SOLID原则
class DependencyManager:
"""依赖管理器 - 单一职责原则(SRP): 只负责管理依赖"""
# PySceneDetect库
try:
from scenedetect import VideoManager, SceneManager
from scenedetect.detectors import ContentDetector
SCENEDETECT_AVAILABLE = True
logger.info("PySceneDetect is available for advanced scene detection")
except ImportError:
logger.warning("PySceneDetect not available. Install scenedetect for better scene detection.")
SCENEDETECT_AVAILABLE = False
def __init__(self):
self._dependencies = {}
self._initialize_dependencies()
def _initialize_dependencies(self):
"""初始化所有依赖"""
self._check_opencv_dependency()
self._check_scenedetect_dependency()
def _check_opencv_dependency(self):
"""检查OpenCV依赖"""
try:
import cv2
import numpy as np
self._dependencies['opencv'] = {
'available': True,
'modules': {'cv2': cv2, 'numpy': np},
'version': cv2.__version__
}
logger.info(f"OpenCV {cv2.__version__} is available")
except ImportError as e:
self._dependencies['opencv'] = {
'available': False,
'error': str(e),
'modules': {}
}
logger.warning("OpenCV not available. Install opencv-python for full functionality.")
def _check_scenedetect_dependency(self):
"""检查PySceneDetect依赖"""
try:
from scenedetect import VideoManager, SceneManager
from scenedetect.detectors import ContentDetector
import scenedetect
self._dependencies['scenedetect'] = {
'available': True,
'modules': {
'VideoManager': VideoManager,
'SceneManager': SceneManager,
'ContentDetector': ContentDetector
},
'version': scenedetect.__version__
}
logger.info(f"PySceneDetect {scenedetect.__version__} is available for advanced scene detection")
except ImportError as e:
self._dependencies['scenedetect'] = {
'available': False,
'error': str(e),
'modules': {}
}
logger.warning("PySceneDetect not available. Install scenedetect for better scene detection.")
def is_available(self, dependency_name: str) -> bool:
"""检查依赖是否可用 - 开闭原则(OCP): 对扩展开放"""
return self._dependencies.get(dependency_name, {}).get('available', False)
def get_module(self, dependency_name: str, module_name: str):
"""获取依赖模块"""
if not self.is_available(dependency_name):
raise ImportError(f"Dependency '{dependency_name}' is not available")
modules = self._dependencies[dependency_name]['modules']
if module_name not in modules:
raise ImportError(f"Module '{module_name}' not found in dependency '{dependency_name}'")
return modules[module_name]
def get_dependency_info(self, dependency_name: str) -> dict:
"""获取依赖信息"""
return self._dependencies.get(dependency_name, {})
def get_all_dependencies(self) -> dict:
"""获取所有依赖信息"""
return self._dependencies.copy()
# 抽象接口 - 接口隔离原则(ISP): 定义专门的接口
from abc import ABC, abstractmethod
class VideoInfoExtractor(ABC):
"""视频信息提取器接口"""
@abstractmethod
def extract_video_info(self, file_path: str) -> Dict:
"""提取视频信息"""
pass
class SceneDetector(ABC):
"""场景检测器接口"""
@abstractmethod
def detect_scenes(self, file_path: str, threshold: float = 30.0) -> List[float]:
"""检测场景变化点"""
pass
class VideoProcessor(ABC):
"""视频处理器接口"""
@abstractmethod
def split_video(self, input_path: str, output_dir: str, scene_times: List[float]) -> List[str]:
"""分割视频"""
pass
# 具体实现类 - 单一职责原则(SRP): 每个类只负责一个功能
class FFProbeVideoInfoExtractor(VideoInfoExtractor):
"""使用FFProbe提取视频信息"""
def extract_video_info(self, file_path: str) -> Dict:
"""使用ffprobe获取准确的视频信息"""
file_size = os.path.getsize(file_path)
try:
import subprocess
import json
cmd = [
'ffprobe',
'-v', 'quiet',
'-print_format', 'json',
'-show_format',
'-show_streams',
file_path
]
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
if result.returncode == 0:
probe_data = json.loads(result.stdout)
video_stream = None
for stream in probe_data.get('streams', []):
if stream.get('codec_type') == 'video':
video_stream = stream
break
if video_stream:
duration = float(probe_data.get('format', {}).get('duration', 0))
width = int(video_stream.get('width', 0))
height = int(video_stream.get('height', 0))
fps_str = video_stream.get('r_frame_rate', '0/1')
if '/' in fps_str:
num, den = fps_str.split('/')
fps = float(num) / float(den) if float(den) != 0 else 0
else:
fps = float(fps_str)
frame_count = int(duration * fps) if fps > 0 else 0
logger.info(f"ffprobe video info: duration={duration:.2f}s, fps={fps:.2f}, resolution={width}x{height}")
return {
'duration': duration,
'width': width,
'height': height,
'fps': fps,
'frame_count': frame_count,
'file_size': file_size,
'codec': video_stream.get('codec_name', 'unknown')
}
except Exception as e:
logger.warning(f"ffprobe failed: {e}")
raise
raise Exception("Failed to extract video info with ffprobe")
class OpenCVVideoInfoExtractor(VideoInfoExtractor):
"""使用OpenCV提取视频信息"""
def __init__(self, dependency_manager: DependencyManager):
self.dependency_manager = dependency_manager
def extract_video_info(self, file_path: str) -> Dict:
"""使用OpenCV获取视频信息"""
if not self.dependency_manager.is_available('opencv'):
raise Exception("OpenCV not available")
cv2 = self.dependency_manager.get_module('opencv', 'cv2')
file_size = os.path.getsize(file_path)
try:
cap = cv2.VideoCapture(file_path)
fps = cap.get(cv2.CAP_PROP_FPS)
frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
duration = frame_count / fps if fps > 0 else 0
cap.release()
logger.info(f"OpenCV video info: duration={duration:.2f}s, fps={fps:.2f}, resolution={width}x{height}")
return {
'duration': duration,
'width': width,
'height': height,
'fps': fps,
'frame_count': frame_count,
'file_size': file_size,
'codec': 'unknown'
}
except Exception as e:
logger.error(f"Failed to get video info with OpenCV: {e}")
raise
class PySceneDetectSceneDetector(SceneDetector):
"""使用PySceneDetect进行场景检测"""
def __init__(self, dependency_manager: DependencyManager):
self.dependency_manager = dependency_manager
def detect_scenes(self, file_path: str, threshold: float = 30.0) -> List[float]:
"""使用PySceneDetect检测场景变化点"""
if not self.dependency_manager.is_available('scenedetect'):
raise Exception("PySceneDetect not available")
VideoManager = self.dependency_manager.get_module('scenedetect', 'VideoManager')
SceneManager = self.dependency_manager.get_module('scenedetect', 'SceneManager')
ContentDetector = self.dependency_manager.get_module('scenedetect', 'ContentDetector')
try:
video_manager = VideoManager([file_path])
scene_manager = SceneManager()
scene_manager.add_detector(ContentDetector(threshold=threshold))
video_manager.start()
scene_manager.detect_scenes(frame_source=video_manager)
scene_list = scene_manager.get_scene_list()
scene_changes = [0.0]
for scene in scene_list:
start_time = scene[0].get_seconds()
end_time = scene[1].get_seconds()
if start_time > 0 and start_time not in scene_changes:
scene_changes.append(start_time)
if end_time not in scene_changes:
scene_changes.append(end_time)
scene_changes = sorted(list(set(scene_changes)))
video_manager.release()
logger.info(f"PySceneDetect found {len(scene_changes)-1} scene changes in video")
logger.debug(f"Scene change timestamps: {scene_changes}")
return scene_changes
except Exception as e:
logger.error(f"PySceneDetect failed: {e}")
raise
class OpenCVSceneDetector(SceneDetector):
"""使用OpenCV进行场景检测"""
def __init__(self, dependency_manager: DependencyManager):
self.dependency_manager = dependency_manager
def detect_scenes(self, file_path: str, threshold: float = 30.0) -> List[float]:
"""使用OpenCV检测场景变化点"""
if not self.dependency_manager.is_available('opencv'):
raise Exception("OpenCV not available")
cv2 = self.dependency_manager.get_module('opencv', 'cv2')
np = self.dependency_manager.get_module('opencv', 'numpy')
try:
cap = cv2.VideoCapture(file_path)
fps = cap.get(cv2.CAP_PROP_FPS)
if fps <= 0:
cap.release()
logger.warning(f"Invalid fps ({fps}) for video {file_path}")
return [0.0]
scene_changes = [0.0]
prev_frame = None
frame_count = 0
frame_skip = max(1, int(fps / 2))
while True:
ret, frame = cap.read()
if not ret:
break
if frame_count % frame_skip == 0:
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
gray = cv2.resize(gray, (320, 240))
if prev_frame is not None:
diff = cv2.absdiff(prev_frame, gray)
mean_diff = np.mean(diff)
if mean_diff > threshold:
timestamp = frame_count / fps
if not scene_changes or timestamp - scene_changes[-1] > 1.0:
scene_changes.append(timestamp)
logger.debug(f"Scene change detected at {timestamp:.2f}s (diff: {mean_diff:.2f})")
prev_frame = gray
frame_count += 1
cap.release()
duration = frame_count / fps if fps > 0 else 0
if duration > 0 and (not scene_changes or duration - scene_changes[-1] > 0.5):
scene_changes.append(duration)
logger.info(f"OpenCV detected {len(scene_changes)-1} scene changes in video")
return scene_changes
except Exception as e:
logger.error(f"Failed to detect scene changes with OpenCV: {e}")
raise
# 工厂类 - 依赖倒置原则(DIP): 依赖抽象而不是具体实现
class VideoProcessorFactory:
"""视频处理器工厂 - 单一职责原则(SRP): 只负责创建对象"""
def __init__(self, dependency_manager: DependencyManager):
self.dependency_manager = dependency_manager
def create_video_info_extractor(self) -> VideoInfoExtractor:
"""创建视频信息提取器 - 开闭原则(OCP): 对扩展开放"""
# 优先使用FFProbe回退到OpenCV
try:
return FFProbeVideoInfoExtractor()
except Exception:
if self.dependency_manager.is_available('opencv'):
return OpenCVVideoInfoExtractor(self.dependency_manager)
else:
raise Exception("No video info extractor available")
def create_scene_detector(self) -> SceneDetector:
"""创建场景检测器"""
# 优先使用PySceneDetect回退到OpenCV
if self.dependency_manager.is_available('scenedetect'):
try:
return PySceneDetectSceneDetector(self.dependency_manager)
except Exception:
pass
if self.dependency_manager.is_available('opencv'):
return OpenCVSceneDetector(self.dependency_manager)
else:
raise Exception("No scene detector available")
# 全局依赖管理器实例
dependency_manager = DependencyManager()
video_processor_factory = VideoProcessorFactory(dependency_manager)
# 向后兼容的全局变量 - 里氏替换原则(LSP): 保持接口兼容
VIDEO_LIBS_AVAILABLE = dependency_manager.is_available('opencv')
SCENEDETECT_AVAILABLE = dependency_manager.is_available('scenedetect')
@dataclass
@@ -87,27 +432,71 @@ class OriginalVideo:
class MediaManager:
"""媒体库管理器"""
def __init__(self):
"""媒体库管理器
遵循SOLID原则:
- SRP: 只负责媒体文件的管理和协调
- OCP: 通过依赖注入支持扩展
- LSP: 可以替换不同的处理器实现
- ISP: 使用专门的接口
- DIP: 依赖抽象接口而不是具体实现
"""
def __init__(self,
dependency_manager: DependencyManager = None,
video_info_extractor: VideoInfoExtractor = None,
scene_detector: SceneDetector = None):
"""
初始化媒体管理器
Args:
dependency_manager: 依赖管理器
video_info_extractor: 视频信息提取器
scene_detector: 场景检测器
"""
self.cache_dir = settings.temp_dir / "cache"
self.cache_dir.mkdir(parents=True, exist_ok=True)
# 数据文件
self.segments_file = self.cache_dir / "video_segments.json"
self.original_videos_file = self.cache_dir / "original_videos.json"
# 依赖注入 - 依赖倒置原则(DIP)
self.dependency_manager = dependency_manager or globals()['dependency_manager']
self.factory = VideoProcessorFactory(self.dependency_manager)
# 延迟初始化处理器 - 单一职责原则(SRP)
self._video_info_extractor = video_info_extractor
self._scene_detector = scene_detector
# 加载数据
self.video_segments = self._load_video_segments()
self.original_videos = self._load_original_videos()
# 存储目录
self.video_storage_dir = settings.temp_dir / "video_storage"
@property
def video_info_extractor(self) -> VideoInfoExtractor:
"""获取视频信息提取器 - 懒加载"""
if self._video_info_extractor is None:
self._video_info_extractor = self.factory.create_video_info_extractor()
return self._video_info_extractor
@property
def scene_detector(self) -> SceneDetector:
"""获取场景检测器 - 懒加载"""
if self._scene_detector is None:
self._scene_detector = self.factory.create_scene_detector()
return self._scene_detector
def _initialize_directories(self):
"""初始化目录结构 - 单一职责原则(SRP)"""
self.video_storage_dir.mkdir(parents=True, exist_ok=True)
self.segments_dir = settings.temp_dir / "video_segments"
self.segments_dir.mkdir(parents=True, exist_ok=True)
self.thumbnails_dir = settings.temp_dir / "video_thumbnails"
self.thumbnails_dir.mkdir(parents=True, exist_ok=True)
@@ -174,176 +563,38 @@ class MediaManager:
return hash_md5.hexdigest()
def _get_video_info(self, file_path: str) -> Dict:
"""获取视频基本信息"""
if not VIDEO_LIBS_AVAILABLE:
# 如果没有视频处理库,返回基本信息
file_size = os.path.getsize(file_path)
return {
'duration': 0.0,
'width': 0,
'height': 0,
'fps': 0.0,
'frame_count': 0,
'file_size': file_size
}
"""获取视频基本信息 - 使用依赖注入的提取器"""
try:
cap = cv2.VideoCapture(file_path)
# 获取视频属性
fps = cap.get(cv2.CAP_PROP_FPS)
frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
duration = frame_count / fps if fps > 0 else 0
cap.release()
return {
'duration': duration,
'width': width,
'height': height,
'fps': fps,
'frame_count': frame_count,
'file_size': os.path.getsize(file_path)
}
return self.video_info_extractor.extract_video_info(file_path)
except Exception as e:
logger.error(f"Failed to get video info: {e}")
file_size = os.path.getsize(file_path)
# 返回基本信息作为后备
file_size = os.path.getsize(file_path) if os.path.exists(file_path) else 0
return {
'duration': 0.0,
'width': 0,
'height': 0,
'fps': 0.0,
'frame_count': 0,
'file_size': file_size
'file_size': file_size,
'codec': 'unknown'
}
def _detect_scene_changes(self, file_path: str, threshold: float = 30.0) -> List[float]:
"""使用PySceneDetect检测场景变化点转场镜头"""
if SCENEDETECT_AVAILABLE:
try:
# 创建视频管理器
video_manager = VideoManager([file_path])
scene_manager = SceneManager()
# 添加内容检测器threshold参数控制敏感度
scene_manager.add_detector(ContentDetector(threshold=threshold))
# 开始检测
video_manager.start()
scene_manager.detect_scenes(frame_source=video_manager)
# 获取场景列表
scene_list = scene_manager.get_scene_list()
# 转换为时间戳列表
scene_changes = [0.0] # 开始时间
for scene in scene_list:
start_time = scene[0].get_seconds()
end_time = scene[1].get_seconds()
# 添加场景开始时间跳过第一个因为已经有0.0了)
if start_time > 0 and start_time not in scene_changes:
scene_changes.append(start_time)
# 添加场景结束时间
if end_time not in scene_changes:
scene_changes.append(end_time)
# 确保时间戳排序
scene_changes = sorted(list(set(scene_changes)))
video_manager.release()
logger.info(f"PySceneDetect found {len(scene_changes)-1} scene changes in video")
logger.debug(f"Scene change timestamps: {scene_changes}")
return scene_changes
except Exception as e:
logger.error(f"PySceneDetect failed: {e}, falling back to OpenCV method")
return self._detect_scene_changes_opencv(file_path, threshold)
else:
logger.warning("PySceneDetect not available, using OpenCV method")
return self._detect_scene_changes_opencv(file_path, threshold)
def _detect_scene_changes_opencv(self, file_path: str, threshold: float = 30.0) -> List[float]:
"""使用OpenCV检测场景变化点备用方案"""
if not VIDEO_LIBS_AVAILABLE:
logger.warning("Video processing not available, returning basic scene changes")
# 获取视频时长,返回开始和结束时间
try:
video_info = self._get_video_info(file_path)
duration = video_info.get('duration', 0)
return [0.0, duration] if duration > 0 else [0.0]
except:
return [0.0]
"""检测场景变化点 - 使用依赖注入的检测器"""
try:
cap = cv2.VideoCapture(file_path)
fps = cap.get(cv2.CAP_PROP_FPS)
if fps <= 0:
cap.release()
logger.warning(f"Invalid fps ({fps}) for video {file_path}")
return [0.0]
scene_changes = [0.0] # 开始时间
prev_frame = None
frame_count = 0
# 每隔几帧检测一次,提高性能
frame_skip = max(1, int(fps / 2)) # 每秒检测2次
while True:
ret, frame = cap.read()
if not ret:
break
# 跳帧处理
if frame_count % frame_skip == 0:
# 转换为灰度图并缩小尺寸以提高性能
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
gray = cv2.resize(gray, (320, 240))
if prev_frame is not None:
# 计算帧间差异
diff = cv2.absdiff(prev_frame, gray)
mean_diff = np.mean(diff)
# 如果差异超过阈值,认为是场景变化
if mean_diff > threshold:
timestamp = frame_count / fps
# 避免过于接近的场景变化点
if not scene_changes or timestamp - scene_changes[-1] > 1.0:
scene_changes.append(timestamp)
logger.debug(f"Scene change detected at {timestamp:.2f}s (diff: {mean_diff:.2f})")
prev_frame = gray
frame_count += 1
cap.release()
# 添加结束时间
duration = frame_count / fps if fps > 0 else 0
if duration > 0 and (not scene_changes or duration - scene_changes[-1] > 0.5):
scene_changes.append(duration)
logger.info(f"OpenCV detected {len(scene_changes)-1} scene changes in video")
return scene_changes
return self.scene_detector.detect_scenes(file_path, threshold)
except Exception as e:
logger.error(f"Failed to detect scene changes with OpenCV: {e}")
# 返回基本的开始和结束时间
logger.error(f"Scene detection failed: {e}")
# 返回基本的开始和结束时间作为后备
try:
video_info = self._get_video_info(file_path)
duration = video_info.get('duration', 0)
return [0.0, duration] if duration > 0 else [0.0]
except:
return [0.0]
def _generate_thumbnail(self, video_path: str, timestamp: float, output_path: str) -> bool:
"""生成视频缩略图"""

View File

@@ -0,0 +1,150 @@
#!/usr/bin/env python3
"""
安装Python依赖的脚本
包括新添加的PySceneDetect库
"""
import subprocess
import sys
import os
from pathlib import Path
def install_requirements():
"""安装requirements.txt中的依赖"""
print("🔧 安装Python依赖...")
# 获取项目根目录
project_root = Path(__file__).parent.parent
requirements_file = project_root / "python_core" / "requirements.txt"
if not requirements_file.exists():
print(f"❌ requirements.txt 文件不存在: {requirements_file}")
return False
try:
# 安装依赖
print(f"📦 从 {requirements_file} 安装依赖...")
subprocess.check_call([
sys.executable, '-m', 'pip', 'install', '-r', str(requirements_file)
])
print("✅ 依赖安装成功!")
return True
except subprocess.CalledProcessError as e:
print(f"❌ 依赖安装失败: {e}")
return False
except Exception as e:
print(f"❌ 安装过程中出现错误: {e}")
return False
def verify_scenedetect():
"""验证PySceneDetect是否正确安装"""
print("\n🧪 验证PySceneDetect安装...")
try:
# 尝试导入PySceneDetect
import scenedetect
from scenedetect import VideoManager, SceneManager
from scenedetect.detectors import ContentDetector
print(f"✅ PySceneDetect 导入成功")
print(f" 版本: {scenedetect.__version__}")
# 测试基本功能
print("🔍 测试基本功能...")
# 创建一个简单的测试
video_manager = VideoManager([])
scene_manager = SceneManager()
scene_manager.add_detector(ContentDetector())
print("✅ PySceneDetect 功能测试通过")
return True
except ImportError as e:
print(f"❌ PySceneDetect 导入失败: {e}")
return False
except Exception as e:
print(f"❌ PySceneDetect 功能测试失败: {e}")
return False
def verify_other_dependencies():
"""验证其他关键依赖"""
print("\n🔍 验证其他关键依赖...")
dependencies = [
('opencv-python', 'cv2'),
('numpy', 'numpy'),
('moviepy', 'moviepy'),
('ffmpeg-python', 'ffmpeg'),
('loguru', 'loguru'),
('pydantic', 'pydantic')
]
failed_deps = []
for package_name, import_name in dependencies:
try:
__import__(import_name)
print(f"{package_name}")
except ImportError:
print(f"{package_name}")
failed_deps.append(package_name)
if failed_deps:
print(f"\n⚠️ 以下依赖导入失败: {', '.join(failed_deps)}")
return False
else:
print("\n✅ 所有关键依赖验证通过")
return True
def show_usage_info():
"""显示使用信息"""
print("\n📖 PySceneDetect 使用信息:")
print("1. PySceneDetect 已添加到 requirements.txt")
print("2. 使用 scenedetect[opencv] 版本包含OpenCV支持")
print("3. 在 media_manager.py 中已集成PySceneDetect场景检测")
print("4. 如果PySceneDetect不可用会自动回退到OpenCV方法")
print("\n🔧 手动安装命令:")
print("pip install scenedetect[opencv]")
print("\n📚 相关文档:")
print("- PySceneDetect: https://pyscenedetect.readthedocs.io/")
print("- 项目文档: docs/tauri-security-config-guide.md")
def main():
"""主函数"""
print("🚀 Python依赖安装和验证")
print("=" * 50)
# 1. 安装依赖
if not install_requirements():
print("❌ 依赖安装失败,退出")
return 1
# 2. 验证PySceneDetect
scenedetect_ok = verify_scenedetect()
# 3. 验证其他依赖
other_deps_ok = verify_other_dependencies()
# 4. 显示使用信息
show_usage_info()
# 5. 总结
print("\n" + "=" * 50)
if scenedetect_ok and other_deps_ok:
print("✅ 所有依赖安装和验证成功!")
print("\n🎉 现在可以使用改进的场景检测功能了:")
print("- 重新导入视频将使用PySceneDetect进行更准确的分镜")
print("- 如果PySceneDetect不可用系统会自动回退到OpenCV方法")
return 0
else:
print("⚠️ 部分依赖存在问题,请检查上述错误信息")
return 1
if __name__ == "__main__":
exit_code = main()
sys.exit(exit_code)

View File

@@ -0,0 +1,31 @@
@echo off
echo 🎬 安装PySceneDetect...
REM 安装PySceneDetect
pip install scenedetect[opencv]
if %errorlevel% equ 0 (
echo ✅ PySceneDetect安装成功
REM 验证安装
echo 🧪 验证安装...
python -c "import scenedetect; print(f'PySceneDetect版本: {scenedetect.__version__}')"
if %errorlevel% equ 0 (
echo ✅ PySceneDetect验证成功
echo.
echo 🚀 现在可以使用改进的场景检测功能了:
echo - 重新导入视频将使用PySceneDetect进行更准确的分镜
echo - 如果PySceneDetect不可用系统会自动回退到OpenCV方法
) else (
echo ❌ PySceneDetect验证失败
pause
exit /b 1
)
) else (
echo ❌ PySceneDetect安装失败
pause
exit /b 1
)
pause

View File

@@ -0,0 +1,30 @@
#!/bin/bash
# 简单的PySceneDetect安装脚本
echo "🎬 安装PySceneDetect..."
# 安装PySceneDetect
pip install scenedetect[opencv]
if [ $? -eq 0 ]; then
echo "✅ PySceneDetect安装成功"
# 验证安装
echo "🧪 验证安装..."
python -c "import scenedetect; print(f'PySceneDetect版本: {scenedetect.__version__}')"
if [ $? -eq 0 ]; then
echo "✅ PySceneDetect验证成功"
echo ""
echo "🚀 现在可以使用改进的场景检测功能了:"
echo "- 重新导入视频将使用PySceneDetect进行更准确的分镜"
echo "- 如果PySceneDetect不可用系统会自动回退到OpenCV方法"
else
echo "❌ PySceneDetect验证失败"
exit 1
fi
else
echo "❌ PySceneDetect安装失败"
exit 1
fi