process mp4 file thumbnailing

This commit is contained in:
Dylan Knutson
2025-08-14 18:16:14 +00:00
parent 981bea5016
commit ca937eb2bc
5 changed files with 67 additions and 39 deletions

View File

@@ -23,8 +23,8 @@ class LoadedMedia
)
when %r{image/jpeg}, %r{image/jpg}, %r{image/png}, %r{image/bmp}
LoadedMedia::StaticImage.new(media_path)
when %r{video/webm}
LoadedMedia::Webm.new(media_path)
when %r{video/webm}, %r{video/mp4}
LoadedMedia::WebmOrMp4.new(media_path)
else
return nil
end

View File

@@ -1,7 +1,7 @@
# typed: strict
# frozen_string_literal: true
class LoadedMedia::Webm < LoadedMedia
class LoadedMedia::WebmOrMp4 < LoadedMedia
include HasColorLogger
::FFMPEG.logger = Logger.new(nil)
@@ -74,42 +74,33 @@ class LoadedMedia::Webm < LoadedMedia
File.join(BlobFile::TMP_DIR, "webm-#{frame}-#{SecureRandom.uuid}.png")
FileUtils.mkdir_p(File.dirname(tmp_path))
# @media.screenshot(tmp_path, { seek_time: frame_time })
# Always seek from the beginning for simplicity and reliability
# Add a small safety margin for the last frame to avoid seeking beyond video duration
safe_seek_time = [frame_time, @duration - 0.01].min
# Determine if we should seek from start or end based on where we are in the file
past_halfway = frame_time / @duration > 0.5
cmd = [FFMPEG_BIN, "-y", "-xerror", "-abort_on", "empty_output"] # Overwrite output files
if past_halfway
# For frames in the second half of the file, seek from the end
# Convert to a negative offset from the end
end_offset = frame_time - @duration
cmd.concat(["-sseof", end_offset.round(2).to_s])
else
# For frames in the first half, seek from the beginning
cmd.concat(["-ss", frame_time.round(2).to_s])
end
# Add input file and frame extraction options
cmd.concat(
[
"-i",
@media_path, # Input file
"-vframes",
"1", # Extract one frame
"-f",
"image2", # Force format to image2
"-update",
"1", # Update existing file
tmp_path,
],
)
cmd = [
FFMPEG_BIN,
"-y", # Overwrite output files
"-xerror", # Exit on error
"-abort_on",
"empty_output", # Abort if output is empty
"-ss",
safe_seek_time.round(2).to_s,
"-i",
@media_path, # Input file
"-vframes",
"1", # Extract one frame
"-f",
"image2", # Force format to image2
"-update",
"1", # Update existing file
tmp_path,
]
_output, error, status = Open3.capture3(*cmd)
unless status.exitstatus == 0
$stderr.print error
raise "Failed to extract frame with ffmpeg: #{error}"
raise "Failed to extract frame with ffmpeg: #{cmd.join(" ")}: #{error}"
end
# Use the original frame number in the error message