Convert WAV into MP3 using FFMPG with the carrier_wave gem - ruby-on-rails

Inside my "wave_uploader.rb" script I have the following code:
class PictureUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
include CarrierWave::MimeTypes
version :wav do
process :convert_to_mp3
def convert_to_mp3
temp_path = Tempfile.new([File.basename(current_path), '.mp3']).path
`ffmpeg -t 15 -i #{current_path} -acodec libmp3lame -f mp3 #{temp_path}`
File.unlink(current_path)
FileUtils.mv(temp_path, current_path)
end
def full_filename(for_file)
super.chomp(File.extname(super)) + '.mp3'
end
end
I am trying to convert the WAV file into a 20 second MP3 file and delete the WAV file once it is converted. The code above runs but I can't find the converted MP3 file so I am guessing it did not work correctly.
At the end of wave_uploader.rb I have code that returns the unique name once it is processed but I commented out the code out thinking the code below was causing the WAV file not to be converted to an MP3.
# def filename
# "#{secure_token}.#{file.extension}" if original_filename.present?
# end
# def secure_token
# var = :"##{mounted_as}_secure_token"
# model.instance_variable_get(var) or model.instance_variable_set(var, SecureRandom.uuid)
end
Any help would be greatly appreciated on how to get this working right.

One thing I see is:
`ffmpeg -t 15 -i #{current_path} -acodec libmp3lame -f mp3 #{temp_path}`
If ffmpeg is not in your path then the OS won't be able to find it, and will return an error, however, because you're using backticks, the OS can't return a string from STDERR, which is where the error would be displayed. Backticks only return STDOUT.
To debug this try this from the command-line:
which ffmpeg
If ffmpeg is found, instead of:
`ffmpeg -t 15 -i #{current_path} -acodec libmp3lame -f mp3 #{temp_path}`
Try:
puts `which ffmpeg`
and see what is output.
I suspect it's not in your path, so you'll have to locate where it is and provide the full path to where it is on disk.
Also, it's better to move the original file, move the new file to the original file's name, then delete the original or leave it as a ".bak" file. That way the original is kept until all the code has processed:
FileUtils.mv(current_path, current_path + '.bak')
FileUtils.mv(temp_path, current_path)
File.unlink(current_path + '.bak') # <-- optional

Related

FFMPEG, how to name the output file at my convenience?

I have here a script that allows me to compress all .mp4 files of a folder.
The output file is:
original_name.mp4.webm
I would like the output file to be original_name.webm.
How to get rid of .mp4?
I think I have to learn .gsub(/ /, '\ ').
Please suggest.
I found it sorry for the buzz, here is the final code that removes ".mp4" and rename it ".webm"
Dir.glob("*.mp4") do |my_text_file|
puts ' --> converting: ' + my_text_file
puts "ffmpeg -i #{my_text_file.gsub(/ /, '\ ')} -b:v 640k #{my_text_file.gsub(/.mp4/, '')}.webm"
`ffmpeg -i #{my_text_file.gsub(/ /, '\ ')} -b:v 640k #{my_text_file.gsub(/.mp4/, '')}.webm`
end

FFMPEG with Rails 4.2 in production

I'm using FFMPEG to convert an uploaded wav file in a mp3.
My code works in development mode but not in production and I can't find where the error is. As far as I can tell, I think it's a path problem with the tmp file. FFMPEG works in production (Digital Ocean) when I run a command on the terminal.
My mp3 job:
class Mp3UploadJob < Struct.new(:audiofile_id)
def perform
path = audiofile.wav_file.url.to_s
mp3_file = File.join(Rails.root, "tmp", "uploads", "#{audiofile.filename}.mp3")
%x[ffmpeg -i "#{path}" -b:a 192k -ar 44100 -metadata title="#{audiofile.title}" -metadata artist="#{audiofile.mp3_artist_name}" -metadata album="#{audiofile.mp3_album}" -metadata date="#{audiofile.release_date}" -metadata genre="#{audiofile.genre}" "#{mp3_file}"]
audiofile.mp3_file = open(mp3_file)
audiofile.save!
File.delete(mp3_file)
end
end
Note: the wav file comes from a dropbox chooser box.
My wav job (ran before the mp3 job):
class WavUploadJob < Struct.new(:audiofile_id)
def perform
audiofile.remote_wav_file_url = audiofile.dropbox_wav
audiofile.save!
end
end

Capture output of shell command, line by line, into an array

I want to capture the total number of rubocop offenses to determine whether my codebase is getting better or worse. I have almost no experience with ruby scripting.
This is my script so far:
#!/usr/bin/env ruby
#script/code_coverage
var = `rubocop ../ -f fuubar -o ../coverage/rubocop_results.txt -f offenses`
puts var
So I ./rails-app/script/code_coverage and my terminal displays
...
--
6844 Total
When I var.inspect I get a long string. I want to either read the output of rubocop ../ -f ... line by line (preferred) or capture each line of output into an array.
I would like to be able to do something like this:
var.each |line| do
if line.contains('total')
...
total = ...
end
Is this possible? I guess this would be similar to writing the output to a file and and then reading the file in line by line.
If you want to keep it simple, you can simply split your var string.
var = `rubocop ../ -f fuubar -o ../coverage/rubocop_results.txt -f offenses`.split("\n")
Then you can iterate on var like you wanted to.
use open3 library of ruby. Open3 grants you access to stdin, stdout, stderr and a thread to wait the child process when running another program. You can specify various attributes, redirections, current directory, etc., of the program as Process.spawn.
http://blog.bigbinary.com/2012/10/18/backtick-system-exec-in-ruby.html
require 'open3'
# Run lynx on this file.
cmd = "lynx -crawl -dump /data/feed/#{file_name}.html > /data/feed/#{file_name}"
Open3.popen3(cmd) do |stdin, stdout, stderr, wait_thr|
cmdout = stdout.read
$logger.debug "stdout is:" + stdout.read
$logger.debug "stderr is:" + stderr.read
end

cron job is not completing the process?

I have a ruby program to convert video to MP4 format using ffmpeg. And I'm using the crontab to run the ruby program every 15 minutes. crontab actually runs the ruby program, but the conversion of the file is not complete. The process is stopped before completing the conversion. My sample code for testin is below.
def convert(y)
system "ffmpeg -i #{SOURCE_FOLDER + LOCATION_SOURCE}/#{y} -acodec libfaac -ar 44100 -ab 96k -vcodec libx264 #{DEST_FOLDER + LOCATION_DEST}/#{y}"
end
SOURCE_FOLDER = "/home/someone/work/videoapp/public/"
DEST_FOLDER = "/home/someone/work/videoapp/public/"
LOCATION_SOURCE = "source"
LOCATION_DEST = "dest"
files = Dir.new(SOURCE_FOLDER + LOCATION_SOURCE)
files.each do |x|
convert(x)
end
This code works fine, if i run it manually in the console.
My first guess is that it's dying on "dot" directories. In Unix there are two directories in every directory/folder: "." and "..". You'll either need to specifically skip those in your script:
next if File.directory?(x) # OR
next file x.match(/^\.+$/)
-- OR --
Look specifically for whatever filetypes you are wanting
Dir[SOURCE_FOLDER + LOCATION_SOURCE + "*.wav"].each do |file|
convert(file)
end
Update: 20110401
Add Unix redirects to the crontab entry to see what the output is
* * * * * /your/program/location/file.rb 1> /some/output/file.txt 2>&1

Prevent ffmpeg from taking over stdout

When I do system "ffmpeg -i just-do-it.mp4 -ab 96k -ar 22050 -qscale 6 output.flv" ffmpeg takes over the ruby process till the job is done, which sometimes take a long time. I've tried using threads amd fork in Ruby to no avail, also system equivalent commands like exec %x[] I also tried the latest Fibers in ruby 1.9.2, but I don't think I'm using it properly.
My question is how to run two ffmpeg processes from ruby concurrently?
EDIT:
fork do
fork do
system "ffmpeg -i you-know.mp4 -ab 96k -ar 22050 -qscale 6 #{Time.now.sec}.flv"
end
fork do
system "ffmpeg -i bangbang.mp4 -ab 96k -ar 22050 -qscale 6 #{Time.now.sec}.flv"
end
end
fork/exec is the right solution. Since forked processes inherit the parent processes fopen file handles/etc., you'll have to close (or redirect) the file handles you don't want children processes to use.
For example:
# this will print nothing, but yes is running as a forked process
# you'll want to `killall yes` after running this script.
fork do
[$stdout, $stderr].each { |fh| fh.reopen File.open("/dev/null", "w") }
exec "yes"
end
Ok, some comments on the code you posted. The outer fork is pointless. Just fork the two ffmpeg process from the main process. Maybe write a helper function like:
def ffmpeg(mp4)
fork do
[$stdout, $stderr].each { ... }
exec "ffmpeg -i #{mp4} ..."
end
end
ffmpeg("you-know.mp4")
ffmpeg("bangbang.mp4")
Try the subprocess gem - that's what I'm using now for dealing with process forking and finding it much easier to use.
E.g.
work_list.each do |cmd|
process = Subprocess::Popen.new(cmd)
process.run
process.wait
#puts process.stdout
#puts process.stderr
puts process.status
end

Resources