Last active
June 9, 2016 13:45
-
-
Save kreeger/9511399 to your computer and use it in GitHub Desktop.
Converts a directory full of MP3s and M4As into uniform, -V5 MP3 files, filed away into directories, tagged with ID3 tags, ready to burn onto an MP3 CD.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
require 'fileutils' | |
require 'pp' | |
directory = File.expand_path ARGV[0] | |
args = ARGV[1,ARGV.length-1].join(' ') | |
class Track | |
attr_accessor :source, :destination, :base_dir | |
attr_accessor :artist, :album, :song, :track_number, :year | |
def initialize(source, base_dir) | |
self.source = source | |
self.base_dir = base_dir | |
self.destination = File.basename(source).gsub(/\.\w+$/, '.mp3') | |
if File.extname(self.source) == '.mp3' | |
self.read_tags_from_mp3 | |
else | |
self.read_tags_from_mp4 | |
end | |
end | |
def directory_name | |
[self.artist, self.album].join(' - ') | |
end | |
def read_tags_from_mp3 | |
id3 = `id3v2 -l \"#{self.source}\"`.unpack('C*').pack('U*') | |
self.song = find_matches(id3, /^TT2 .+: (.*)$/, /^TIT2 .+: (.*)$/) | |
self.artist = find_matches(id3, /^TP1 .+: (.*)$/, /^TPE1 .+: (.*)$/, /^TPE2 .+: (.*)$/) | |
self.album = find_matches(id3, /^TAL .+: (.*)$/, /^TALB .+: (.*)$/) | |
self.year = find_matches(id3, /^TYE .+: (.*)$/, /^TYER .+: (.*)$/) | |
self.track_number = find_matches(id3, /^TRK .+: (.*)$/, /^TRCK .+: (.*)$/).split("/").first | |
end | |
def read_tags_from_mp4 | |
parsley = `atomicparsley \"#{self.source}\" -t` | |
self.song = find_matches(parsley, /©nam" contains: (.*)/) | |
self.artist = find_matches(parsley, /©ART" contains: (.*)/) | |
self.album = find_matches(parsley, /©alb" contains: (.*)/) | |
self.track_number = find_matches(parsley, /trkn" contains: (.*)/).strip.split(' of ').join('/') | |
self.year = find_matches(parsley, /©day" contains: (.*)/) | |
end | |
def find_matches(data, *patterns) | |
match = nil | |
while match == nil do | |
break if patterns.count == 0 | |
pattern = patterns.pop | |
match = pattern && pattern.match(data) | |
end | |
match && match[1] && match[1].strip | |
end | |
def full_destination | |
File.join(self.base_dir, self.directory_name, self.destination) | |
end | |
def wave_path | |
self.full_destination.gsub(/\.\w+$/, '.wav') | |
end | |
def convert! | |
FileUtils.mkdir_p(File.join(self.base_dir, self.directory_name)) | |
result = self.to_wave | |
if result == 0 | |
result2 = self.to_mp3 | |
if !result2 | |
puts "ERROR! Tried to encode #{File.basename(self.wave_path)} to MP3 and failed with exit status #{result2}." | |
else | |
self.write_tags! | |
end | |
else | |
puts "ERROR! Tried to encode #{File.basename(self.source)} to WAV and failed with exit status #{result}." | |
end | |
self.cleanup | |
end | |
def write_tags! | |
`id3v2 -a \"#{self.artist}\" -A \"#{self.album}\" -t \"#{self.song}\" -y #{self.year} -T #{self.track_number} \"#{self.full_destination}\"` | |
end | |
def to_wave | |
`afconvert -f 'WAVE' -d I16@44100 "#{self.source}" "#{self.wave_path}"` | |
$?.exitstatus | |
end | |
def to_mp3 | |
`lame -V5 \"#{self.wave_path}\" \"#{self.full_destination}\"` | |
$?.exitstatus | |
end | |
def cleanup | |
File.delete(self.wave_path) rescue nil | |
end | |
end | |
def traverse(base_dir, current_dir, env) | |
Dir.entries(current_dir).each do |file| | |
next if %r(^\.).match(file) | |
basename = File.basename(file) | |
file = File.join(current_dir, file) | |
if File.directory?(file) | |
traverse(base_dir, file, env) | |
next | |
end | |
env[:tracks] << Track.new(file, base_dir) | |
end | |
end | |
env = {tracks: []} | |
traverse(directory, directory, env) | |
env[:tracks].each do |track| | |
track.convert! | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment