77
loading...
This website collects cookies to deliver better user experience
ffmpeg -i <input> -acodec copy -bsf:a aac_adtstoasc -vcodec copy <output>
<input>
can actually be a fully qualified https://provider/video.m3u8
url so the HLS manifest doesn't have to be available locally.system("ffmpeg", "-i", hls_url, "-acodec", "copy", "-bsf:a", "aac_adtstoasc", "-vcodec", "copy", path)
migrate_to_mp4
. This method will also exist on the Video
model and the Video
will have one attached mp4 video like so:class Video < ApplicationRecord
has_one_attached :mp4_video
def migrate_to_mp4
system("ffmpeg", "-i", hls_url, "-acodec", "copy", "-bsf:a", "aac_adtstoasc", "-vcodec", "copy", path)
end
end
def migrate_to_mp4
tempfile = ::Tempfile.new(["video", ".mp4"])
path = tempfile.path
tempfile.close
tempfile.unlink
# hls_url is a db column on the video record.
system("ffmpeg", "-i", hls_url, "-acodec", "copy", "-bsf:a", "aac_adtstoasc", "-vcodec", "copy", path)
end
Tempfile
which has a number of semantics that make it great for creating...you guessed it, temporary files.::Tempfile.new
an array we say: "Generate a temporary file with a random name prefixed with 'video' and ending with '.mp4'"#attach
method on mp4_video
.mp4_video.attach(io: File.open(tempfile), filename: "video-#{id}.mp4")
begin/ensure
clause to ensure we delete the file regardless of whether or not it succeeds.def migrate_to_mp4
return if hls_url.blank?
tempfile = ::Tempfile.new(["video", ".mp4"])
path = tempfile.path
# We dont actually want the tempfile, just its path.
tempfile.close
tempfile.unlink
begin
system("ffmpeg", "-i", hls_url, "-acodec", "copy", "-bsf:a", "aac_adtstoasc", "-vcodec", "copy", path)
mp4_video.attach(io: File.open(tempfile), filename: "video-#{id}.mp4")
ensure
# always cleanup our mess.
File.delete(path)
end
end
scope :not_migrated,
-> {
left_joins(
:mp4_video_attachment,
).finished.where(active_storage_attachments: {id: nil})
}
ActiveJob
. We also use Sidekiq so its worth noting to make sure to use JSON serializable parameters with Sidekiq.class MigrateVideoStorageJob < ApplicationJob
queue_as :default
def perform(video_id=nil)
if video_id.blank?
ids = Video.not_migrated.ids
ids.each { |id| MigrateVideoStorageJob.perform_later(id) }
return
end
video = Video.find(video_id)
return if video.mp4_video.attached?
video.migrate_to_mp4
end
end
bundle exec rails console
MigrateVideoStorageJob.perform_later
77