#!/usr/bin/env python3 """ Scene Detection Service A Flask API that wraps PySceneDetect to find hard cuts in videos. Returns cut timestamps for use in clip boundary snapping. """ import os from flask import Flask, request, jsonify from scenedetect import open_video, SceneManager from scenedetect.detectors import ContentDetector, AdaptiveDetector app = Flask(__name__) # Configuration from environment CONTENT_THRESHOLD = float(os.getenv('SCENE_CONTENT_THRESHOLD', '27.0')) MIN_SCENE_LEN = int(os.getenv('SCENE_MIN_LENGTH', '15')) # minimum scene length in frames @app.route('/health', methods=['GET']) def health(): """Health check endpoint""" return jsonify({ 'status': 'ok', 'content_threshold': CONTENT_THRESHOLD, 'min_scene_len': MIN_SCENE_LEN }) @app.route('/detect', methods=['POST']) def detect(): """ Detect scene cuts in a video. Expects JSON body with: - video_path: path to video file (must be accessible from container) - threshold: optional override for content detector threshold - detector: 'content' (default) or 'adaptive' Returns JSON with: - cuts: array of cut timestamps in seconds - scene_count: number of scenes detected - duration: video duration in seconds """ data = request.get_json() if not data or 'video_path' not in data: return jsonify({'error': 'video_path is required'}), 400 video_path = data['video_path'] threshold = data.get('threshold', CONTENT_THRESHOLD) detector_type = data.get('detector', 'content') # Verify file exists if not os.path.exists(video_path): return jsonify({'error': f'Video file not found: {video_path}'}), 404 try: # Open video video = open_video(video_path) scene_manager = SceneManager() # Add detector based on type if detector_type == 'adaptive': scene_manager.add_detector(AdaptiveDetector()) else: scene_manager.add_detector( ContentDetector(threshold=threshold, min_scene_len=MIN_SCENE_LEN) ) # Detect scenes scene_manager.detect_scenes(video, show_progress=False) scene_list = scene_manager.get_scene_list() # Get video duration duration = video.duration.get_seconds() # Extract cut timestamps (scene boundaries) # Scene list contains (start_time, end_time) tuples # We want the unique timestamps where cuts occur cuts = [0.0] # Always include video start for scene in scene_list: start_sec = scene[0].get_seconds() end_sec = scene[1].get_seconds() # Add start of each scene (which is end of previous) if start_sec not in cuts and start_sec > 0: cuts.append(start_sec) # Add video end if duration not in cuts: cuts.append(duration) # Sort and deduplicate cuts = sorted(list(set(cuts))) return jsonify({ 'cuts': cuts, 'scene_count': len(scene_list), 'duration': duration, 'detector': detector_type, 'threshold': threshold }) except Exception as e: return jsonify({'error': str(e)}), 500 @app.route('/info', methods=['GET']) def info(): """Get service configuration""" return jsonify({ 'service': 'scene-detection', 'library': 'PySceneDetect', 'detectors': ['content', 'adaptive'], 'default_threshold': CONTENT_THRESHOLD, 'min_scene_len': MIN_SCENE_LEN }) if __name__ == '__main__': print(f"Scene Detection Service starting...") print(f"Content threshold: {CONTENT_THRESHOLD}") print(f"Min scene length: {MIN_SCENE_LEN} frames") app.run(host='0.0.0.0', port=5004, debug=False)