Files
MatchLiveTv/backend/app/services/recordings/generate_thumbnail.rb
Emiliano Frascaro 1f273f849d Aggiunge modulo Replay, Garage dev e YouTube a livello società.
Pipeline registrazione/upload con storage S3, archivio web e app, proxy replay
per il browser, OAuth YouTube sulla pagina club (Premium Full) e footer legale.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 07:53:11 +02:00

56 lines
1.7 KiB
Ruby

module Recordings
class GenerateThumbnail
class Error < StandardError; end
def initialize(recording, video_path: nil)
@recording = recording
@video_path = video_path
end
def call
return @recording if @recording.thumbnail_storage_key.present?
path = @video_path || local_video_path
return @recording unless path && File.exist?(path)
thumb_path = extract_frame(path)
key = thumbnail_key
Recordings::Storage.new.upload(local_path: thumb_path, key: key, content_type: "image/jpeg")
@recording.update!(thumbnail_storage_key: key)
FileUtils.rm_f(thumb_path)
@recording
rescue Recordings::Storage::Error => e
Rails.logger.warn("[Recordings::GenerateThumbnail] #{@recording.id}: #{e.message}")
@recording
ensure
FileUtils.rm_f(@thumb_temp) if @thumb_temp && File.exist?(@thumb_temp)
end
private
def local_video_path
return nil if @recording.storage_key.blank?
Recordings::Storage.new.local_path_for_key(@recording.storage_key)
end
def extract_frame(video_path)
@thumb_temp = File.join(Dir.tmpdir, "thumb-#{@recording.id}-#{SecureRandom.hex(4)}.jpg")
dur = @recording.duration_secs.to_i
offset = dur.positive? ? [[dur / 4, 5].max, dur - 1].min : 1
success = system(
"ffmpeg", "-y", "-ss", offset.to_s, "-i", video_path,
"-vframes", "1", "-q:v", "2", @thumb_temp,
out: File::NULL, err: File::NULL
)
raise Error, "ffmpeg thumbnail failed" unless success && File.exist?(@thumb_temp)
@thumb_temp
end
def thumbnail_key
"teams/#{@recording.team_id}/sessions/#{@recording.stream_session_id}/thumbnail.jpg"
end
end
end