I need to convert uploaded video files with carrierwave. Uploader:
class MediaItemUploader < CarrierWave::Uploader::Base
version :video_for_device, if: :video? do
process :encode_video_for_device
end
storage :file
private
def video? file
if file.path.ends_with?('avi') || ...
true
else
false
end
end
def encode_video_for_device
input_file = file.path
output_file = # How to get the output file path?
system("ffmpeg -i #{input_file} -vcodec libx264 -acodec aac -strict -2 #{output_file}")
end
end
But how do I get the output file path and tell carrierwave to attach this file?
If I hardcode output_file then ffmpeg works fine, but carrierwave puts same file named 'video_for_device_#{original_filename}' along with original file. But I need to process this new file.
Here is a solution:
def encode_video_for_device
tmp_path = File.join File.dirname(current_path), "#{SecureRandom.hex}.mp4"
system("ffmpeg -i #{current_path} -vcodec libx264 -acodec aac -strict -2 #{tmp_path}")
File.rename tmp_path, current_path
end
current_path is a path of the current file which has to be modified before attaching.
You gotta return the processed file in the end of the method, so this should do it:
def encode_video_for_device
input_file = file.path
output_file = "/whatever/temp/filename"
system("ffmpeg -i #{input_file} -vcodec libx264 -acodec aac -strict -2 #{output_file}")
File.new(output_file)
end
Related
So I'm trying to convert mp3 files into .flac with Paperclip custom processors and ffmpeg. The following code runs the ffmpeg command and creates a temporary flac file. However, it is not saved? Currently only the original file is saved. What am I missing here?
class AudioFile < ApplicationRecord
has_attached_file :raw_audio, processors: [:custom], styles: { original: {}}
the custom processor
module Paperclip
class Custom < Processor
def initialize(file, options = {}, attachment = nil)
super
#file = file
#basename = File.basename(#file.path)
#format = options[:format] || 'flac'
#params = options[:params] || '-y -i'
end
def make
source = #file
output = Tempfile.new([#basename, ".#{#format}"])
begin
parameters = [#params, ':source',':dest'].join(' ')
Paperclip.run('ffmpeg', parameters, :source => File.expand_path(source.path), :dest => File.expand_path(output.path), :sample_rate => #sample_rate, :bit_rate => #bit_rate)
end
output
end
end
end
Ran into exactly the same problem.
You can use ffmpeg directly (note the backticks)
`ffmpeg -i <original_file_path.mp3> <new_file_path.flac>`
There are various other things you can do (and need to in the case of Google Speech API).
e.g. If you have an mp3 file in stereo, you're going to need to either
compress to mono:
`ffmpeg -i file.mp3 -ac 1 file_mono.flac`
or split the channels:
`ffmpeg -i file.mp3 -map_channel 0.0.0 left_file.flac -map_channel 0.0.1 right_file.flac`
otherwise the API won't accept it.
Setup:
ruby 1.8.6
rails 2.2.2
attachment_fu - not sure (it's vendorized), but last entry in CHANGELOG is "Apr 17 2008"
aws-s3 (0.6.3)
aws-sdk (2.1.13)
aws-sdk-core (2.1.13)
aws-sdk-resources (2.1.13)
I have a model which uses attachment_fu like so:
has_attachment :storage => :s3,
:path_prefix => "vip_uploads#{("_"+RAILS_ENV) if RAILS_ENV != "production"}",
:max_size => 100.megabytes,
:processor => :mp3
The s3 stuff is all set up fine - if i remove the processor option then the upload to s3 works.
My mp3 processor, which converts wav files to mp3s, looks like this:
module Technoweenie # :nodoc:
module AttachmentFu # :nodoc:
module Processors
module Mp3Processor
def self.included(base)
base.send :extend, ClassMethods
base.alias_method_chain :process_attachment, :processing
end
module ClassMethods
end
protected
def process_attachment_with_processing
self.convert_to_mp3
end
# Convert to mp3 and set some metadata
def convert_to_mp3(options={})
#do the conversion with ffmpeg
mp3_temp_path = "#{self.temp_path}.mp3"
cmd = "ffmpeg -i #{self.temp_path} -metadata title=\"#{self.filename.gsub("\.wav","")}\" -metadata artist=\"Vip Studio Sessions\" -metadata album=\"Uploads\" -vn -ar 44100 -ac 2 -ab 320k -f mp3 #{mp3_temp_path}"
`#{cmd}`
#copy this file back into temp_data
self.copy_to_temp_file(mp3_temp_path)
#update attributes
self.filename = self.filename.gsub(/\.wav$/,".mp3")
self.content_type = "audio/mpeg"
self.set_size_from_temp_path
end
end
end
end
end
All of the conversion stuff seems to be working, in that it makes a new mp3 file in the tmp folder, with the filename saved in mp3_temp_path, and it makes a record in the database. But for some reason, the resulting file isn't then pushed up to s3. I've got a feeling i just need to set some accessor to do with temp_data or temp_file or something. I've tried this
self.temp_path = mp3_temp_path
and
self.temp_data = File.read(mp3_temp_path)
and
self.temp_path = write_to_temp_file(File.read(mp3_temp_path))
Currently, as you can see in my code, i'm trying this:
self.copy_to_temp_file(mp3_temp_path)
but none of them work. These attempts were based on looking in the pre-existing processors, eg for rmagick, and seeing what they do. They seem to do the same thing as me, but since all of them are about thumbnailing it's easy to lose something in the translation.
Can anyone see what i'm missing? thanks, Max
Ok, answering my own question - it's amazing what a new day can bring. It suddenly occurred to me to look more closely at the attachment_fu source code, in which i noticed this...
def after_process_attachment
if #saved_attachment
if respond_to?(:process_attachment_with_processing) && thumbnailable? && !attachment_options[:thumbnails].blank? && parent_id.nil?
temp_file = temp_path || create_temp_file
attachment_options[:thumbnails].each { |suffix, size| create_or_update_thumbnail(temp_file, suffix, *size) }
end
save_to_storage
#temp_paths.clear
#saved_attachment = nil
callback :after_attachment_saved
end
end
I hadn't set this instance variable #saved_attachment to true (or truthy), so it was not going ahead with save_to_storage. It kind of fails silently - the record is still happily saved to the database , it just doesn't store the file.
in summary, the answer was that, if i'm happy with the resulting mp3 (ie it was an mp3 in the first place, or I successfully convert it to an mp3), then i need to set #saved_attachment = true in my processor.
I received files in mp3 (2 minutes/files) I want to concatenate together and create a bigger file. So I created my model a function to do this using ffmpeg and IO.popen
FileUtils.mkdir_p "#{Rails.root}/tmp/files"
imported_dir = "#{Rails.root}/tmp/files/#{SecureRandom.uuid}"
links.each_with_index do |link, index|
file_path = "#{imported_dir}_#{index}#{File.extname(link)}"
File.open(file_path, 'wb') do |file|
file.write open(link).read
end
concat_list << "file '#{file_path}'\n"
end
File.open("#{imported_dir}.txt", 'w'){ |f| f.write(concat_list)}
io = IO.popen("#{Rails.root}/lib/ffmpeg/ffmpeg -f concat -i #{imported_dir}.txt -c copy #{imported_dir}.mp3").readlines
if sound = Sound.create(user_id: user.id, file: File.open("#{imported_dir}.mp3"), lang: lang, title: title)
audio = FFMPEG::Movie.new("#{imported_dir}.mp3")
if !audio.valid?
puts "//_!_\\\\ Failed reading with ffmpeg (#{sound.id})#{sound.title} //_!_\\\\"
return false
end
end
the problem is that my .txt file containing the file path
file '/home/test/apps/example/releases/20150305224026/tmp/files/4dbe9707-cfef-467b-ab2c-a5e1e1165953_0.mp3'
created files as well but the final file is not created and i got the error message :
No such file or directory # rb_sysopen - /home/test/apps/example/releases/20150305224026/tmp/files/4dbe9707-cfef-467b-ab2c-a5e1e1165953.mp3
If anyone could help me
Make sure the target directory exists and is writable by the user running your app.
I use carrierwave for upload sound in my app.
I use large file so I configure my carrierwave for use move_to_cache and move_to_store but the problem is when I upload file now , in my cache folder carrierwave create 2 folder with two cache.id and 1 folder in my store.
I need when I upload file , just have 1 folder in the cache folder and 1 folder in my store folder. And In my cache folder I want my file was delete , but actually my file stay in my cache folder.
I hope i was clear.
I give you my sound_uploader.rb
class SoundUploader < CarrierWave::Uploader::Base
before :store , :print
def print(new_file)
puts ("PRINT CAAAAACHE")
puts (cache_id)
end
# Choose what kind of storage to use for this uploader:
storage :file
def store_dir
"tmp/#{model.class.to_s.underscore}/store/#{model.id}"
end
def cache_dir
"tmp/#{model.class.to_s.underscore}/cache/#{model.id}"
end
def move_to_cache
puts("MOVE TO CACHE ")
false
end
def move_to_store
puts("MOVE TO STORE ")
true
end
def extension_white_list
%w(3ga 3gp 3g2 3gpp 3gp2 m4a m4b m4p m4v m4r mp4 aac flac flv avi asf wma wmv dpx mkv mka mks bwf mpg mpeg mp1 mp2 mp3
m1v m1a m2a mpa mpv rm mov ogm ogg ogv oga ogx nut riff webm weba wav mxf asx ts aiff aif aifc au snd caf)
end
def filename
model.title = original_filename if model.title.to_s == ''
"#{secure_token}.#{file.extension}" if original_filename.present?
end
protected
def secure_token
var = :"##{mounted_as}_secure_token"
model.instance_variable_get(var) or model.instance_variable_set(var, SecureRandom.uuid)
end
end
thank's for your help.
The likely reason for this is that you're using cancan's load_and_authorize_resource or load_resource, and you forgot creating the model object in the controller action (#song = Song.new(params[:song]), so the resource is being created twice, which causes carrierwave to move the file to cache twice.
I am uploading an audiofile and make a mp3 version, which works. Additionally I am generating a waveform out of the mp3 as a "png". Wich also works well.
The image was generated and saved but with "mp3" as suffix, which should be "png".
The view has rendered the image correctly with the "mp3" fileextension name.
Now I get a 404 error when the view tries to get the image. The filename was not assumed correctly:
https://mybucket.amazonaws.com/uploads/sound/soundfile/142/waveform_Sky_02.wav
which should be
https://mybucket.amazonaws.com/uploads/sound/soundfile/142/waveform.png
here is my :version code:
version :waveform do
def filename
"watermark.png" if original_filename.present?
end
def convert_to_waveform
cache_stored_file! if !cached?
Dir::Tmpname.create(File.basename(current_path)) do |tempname|
begin
puts system %Q{ffmpeg -y -i "#{current_path}" -f wav "#{tempname}" > /dev/null 2>&1}
FileUtils.rm current_path
Waveform.generate(tempname, current_path, method: :rms, background_color: :transparent)
ensure
FileUtils.rm tempname
end
end
end
process :convert_to_waveform
end
The database saves "waveform_Sky_02.wav"
How can I get this working?
this has been working for me:
def full_filename(for_file=file)
super.chomp('wav') + '.png'
end
or for all filetypes:
def full_filename(for_file=file)
super.chomp(File.extname(super)) + '.png'
end