123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115 |
- #!/usr/bin/ruby
- # Daniel "Trizen" Șuteu
- # Date: 21 March 2020
- # Edit: 24 October 2023
- # https://github.com/trizen
- # Convert any media file with ffmpeg, using `n` parallel processes.
- func ffmpeg_convert(input, output, bitrate) {
- Sys.run("ffmpeg", "-loglevel", "warning", "-i", input, "-vn", "-sn", "-dn", "-b:a", "#{bitrate}K", output)
- }
- func prepare_file(file, outdir, ext) {
- var out = ""
- if (file =~ /^(.*)\.\w+\z/s) {|m|
- out = (m[0] + "." + ext)
- }
- else {
- out = (file + "." + ext)
- }
- out = File(outdir, File(out).basename)
- if (File(out).rel2abs == File(file).rel2abs) {
- return nil
- }
- return out
- }
- var threads = 2
- var format = 'opus'
- var outdir = Dir.cwd
- var bitrate = 96
- var delete_original = false
- func print_help(exit_code=0) {
- var name = File(__FILE__).basename
- print <<"EOT"
- usage: #{name} [options] [files]
- options:
- -b --bitrate=i : output bitrate in kbps (default: #{bitrate})
- -f --format=s : output format (default: #{format})
- -d --delete! : delete the original files (default: #{delete_original})
- -o --outdir=s : output directory (default: .)
- -t --threads=i : number of threads to use (default: #{threads})
- example:
- #{name} -b 40 -f opus -t 4 *.mp3 # convert MP3 to OPUS at 40kbps
- EOT
- Sys.exit(exit_code)
- }
- ARGV.getopt!(
- 'f|format=s' => \format,
- 'b|bitrate=i' => \bitrate,
- 't|threads=i' => \threads,
- 'o|outdir=s' => \outdir,
- 'd|delete!' => \delete_original,
- 'h|help' => print_help,
- )
- outdir.exists || outdir.make_path || die "Can't create directory <<#{outdir}>>: #{$!}"
- ARGV || print_help(1)
- ARGV.each_slice(threads, {|*slice|
- var files = slice.grep { File(_).exists }
- var conversions = files.map {|file|
- [File(file), prepare_file(file, outdir, format)]
- }.grep {
- defined(.tail)
- }.grep {
- !.tail.exists
- }
- conversions || next
- conversions.each_2d {|from,to|
- say ":: Converting: '#{from}' -> '#{to}'"
- }
- var forks = conversions.map_2d {|input, output|
- ffmpeg_convert.fork(input, output, bitrate)
- }
- forks.each {|t|
- say ":: Waiting..."
- t.wait
- }
- conversions.each_2d {|input, output|
- output.is_file || next
- var output_size = output.size
- if (delete_original && (output_size > 0)) {
- say "Removing: #{input}"
- input.delete || warn "failed to remove: #{$!}"
- }
- elsif (output_size == 0) { # ffmpeg failed
- output.delete # remove empty file
- }
- }
- })
|