Class: SmartVideoPosterExtractionService
- Inherits:
-
Object
- Object
- SmartVideoPosterExtractionService
- Defined in:
- app/services/smart_video_poster_extraction_service.rb
Overview
Heuristic ("smart") poster extraction using FFmpeg scene detection.
Strategy
- Use FFmpeg to analyze the video input and find the first significant scene change
after a minimum start time (to avoid intros/slates). - For Cloudflare videos, we detect the best timestamp using FFmpeg over the HLS
manifest URL, then delegate the actual poster creation to
VideoPosterExtractionService with that timestamp (which uses Cloudflare's
thumbnail endpoint at ?time=s). - For self-hosted videos, we likewise detect the timestamp and delegate to
VideoPosterExtractionService which extracts with FFmpeg at that timestamp.
Options
- min_start_seconds: skip the first N seconds before detecting scenes (default: 5.0)
- scene_threshold: FFmpeg scene threshold for cuts (default: 0.40)
- fallback_seconds: if detection fails, use this timestamp (default: 10.0)
Instance Method Summary collapse
-
#extract_poster ⇒ Object
Raises VideoPosterExtractionService::ExtractionError on failure so callers see the real root cause.
-
#initialize(video, min_start_seconds: 5.0, scene_threshold: 0.40, fallback_seconds: 10.0) ⇒ SmartVideoPosterExtractionService
constructor
A new instance of SmartVideoPosterExtractionService.
Constructor Details
#initialize(video, min_start_seconds: 5.0, scene_threshold: 0.40, fallback_seconds: 10.0) ⇒ SmartVideoPosterExtractionService
Returns a new instance of SmartVideoPosterExtractionService.
21 22 23 24 25 26 |
# File 'app/services/smart_video_poster_extraction_service.rb', line 21 def initialize(video, min_start_seconds: 5.0, scene_threshold: 0.40, fallback_seconds: 10.0) @video = video @min_start_seconds = min_start_seconds @scene_threshold = scene_threshold @fallback_seconds = fallback_seconds end |
Instance Method Details
#extract_poster ⇒ Object
Raises VideoPosterExtractionService::ExtractionError on failure so callers
see the real root cause.
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
# File 'app/services/smart_video_poster_extraction_service.rb', line 30 def extract_poster input = input_source unless input raise VideoPosterExtractionService::ExtractionError, "Video #{@video.id} has no valid input source (no Cloudflare UID or attachment)" end Rails.logger.info "[SmartPoster] Detecting scene timestamp for video #{@video.id} (min_start=#{@min_start_seconds}s, threshold=#{@scene_threshold})" = (input) if .nil? [0.35, 0.30, 0.25, 0.20, 0.15, 0.10].each do |th| next if th >= @scene_threshold Rails.logger.info "[SmartPoster] Retrying scene detection with threshold=#{th}" = (input, threshold_override: th) break if end end if .nil? Rails.logger.info '[SmartPoster] Attempting thumbnail heuristic' = (input) end ||= @fallback_seconds Rails.logger.info "[SmartPoster] Using timestamp #{.round(3)}s for video #{@video.id}" ms = (.to_f * 1000).to_i @video.update_column(:poster_offset, ms) VideoPosterExtractionService.new(@video, ).extract_poster end |