Scripts for ripping music CDs and doing metadata
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

294 lines
8.0 KiB

require "mp3tag"
require "fileutils"
require "mp3info"
require "shellwords"
class Folder
attr_reader :dirname,:tocname,:parentname
def initialize(givenpath)
Dir.chdir givenpath
@dirname = File.basename(Dir.pwd)
@parentname = File.dirname(Dir.pwd)
@tocname = find_tocname
end
def find_tocname
expr = %r{(.*)\.0}
m1 = expr.match(dirname)
if m1
toc = m1[1] + ".toc"
else
toc = dirname + ".toc"
end
toc
end
def parse_toc_v1
expr1 = %r{^DTITLE=(.*) / ([^\r]*)}
expr2 = %r{^TTITLE(\d+)=([^\r]*)}
title = []
artist = ""
album = ""
# reading lines... into line
tocfile = File.new(tocname)
tocfile.each_line {|line|
m1 = expr1.match(line)
if m1
artist = m1[1].chomp
album = m1[2].chomp
end
m2 = expr2.match(line)
if m2
# We store Track Number minus one
n = m2[1].to_i
title[n] = m2[2].chomp
#puts "title[#{n}] = {#{title[n]}}"
end
}
title != [] && artist != "" && album != "" &&
{:artist => artist,:album => album,:title => title}
end
def valid_toc_v1
File.exist?(tocname) && parse_toc_v1
end
def classify
{:toc_exists => File.exist?(tocname),
:toc_version => (if valid_toc_v1 then 1 else 0 end)}
end
def debug
puts "directory: #{dirname}"
puts "parentname: #{parentname}"
puts "tocname: #{tocname}"
classify.each_pair {|k,v| puts "#{k.to_s}: #{v.to_s}"}
puts " "
end
def songs
expr = %r{\.mp3$}
Dir.entries(".").delete_if{|file|
!(expr =~ file) ||
!FileTest.file?(file) || !FileTest.readable?(file) || FileTest.zero?(file)
}.sort
end
end
class Song
attr_reader :filename,:dirname
attr_writer :filename
def initialize(dirname,filename)
@dirname = dirname
@filename = filename
end
# An id3 tag is not "valid" unless the four tags below are not null.
def has_valid_id3v1
has_id3v1 = Mp3Tag.hastag?(filename)
if !has_id3v1 then false
else
tags = Mp3Tag.new(filename)
tags.artist.length > 0 &&
tags.album.length > 0 &&
tags.tracknum.class != nil &&
tags.songname.length > 0
end
end
def has_id3v2
Mp3Info.hastag2?(filename)
end
# This is false: nirvana-nevermind-album/01_-_smells_like_teen_spirit_192_lame_cbr.mp3
# This is true: gorillaz-first-album/gorillaz-first-album.01.mp3
def filename_is_album
expr1 = %r{^(.*)\.\d\d\.mp3}
m1 = expr1.match(filename)
(m1 && dirname == m1[1]) || false
end
def title_puts(title)
title.each_index{ |i| puts "track #{i.to_s} is: #{title[i]}"}
end
def classify
{:has_valid_id3v1 => has_valid_id3v1,
:has_id3v2 => has_id3v2,
:filename_is_album => filename_is_album}
end
def debug
puts "filename: #{filename}"
classify.each_pair {|k,v| puts "#{k.to_s}: #{v.to_s}"}
puts " "
end
end
def make_songs(folder)
folder.songs.collect{|filename| Song.new(folder.dirname,filename)}
end
class Automaton
attr_reader :action_table, :folder, :song, :songhash
def initialize(folder,song)
@action_table =
[{:conditions => {:has_valid_id3v1 => false,:has_id3v2 => false,:toc_exists => true,:toc_version => 1, :filename_is_album => true},
:actions => [:convert_toc1_to_metadata]},
{:conditions => {:has_valid_id3v1 => false,:has_id3v2 => false,:toc_exists => true,:toc_version => 1, :filename_is_album => false},
:actions => [:rename_file, :convert_toc1_to_metadata]},
{:conditions => {:has_valid_id3v1 => false,:has_id3v2 => true},
:actions => [:move_v2_to_v1,:delete_v2,:restart_script]},
{:conditions => {:has_valid_id3v1 => true,:has_id3v2 => true,:filename_is_album => true},
:actions => [:delete_v2]},
{:conditions => {:has_valid_id3v1 => true,:has_id3v2 => true,:filename_is_album => false},
:actions => [:delete_v2,:rename_file]},
{:conditions => {:toc_exists => false,:filename_is_album => false,:has_valid_id3v1 => false,:has_id3v2 => false},
:actions => [:partial_info]},
{:conditions => {:has_valid_id3v1 => true,:has_id3v2 => false,:filename_is_album => true},
:actions => [:everything_is_ok]},
{:conditions => {:has_valid_id3v1 => true,:has_id3v2 => false,:filename_is_album => false},
:actions => [:rename_file]},
{:conditions => {:has_valid_id3v1 => false,:has_id3v2 => false,:toc_exists => true,:toc_version => 0},
:actions => [:unknown_toc_format]},
{:conditions => {:has_valid_id3v1 => false, :has_id3v2 => false, :filename_is_album => true, :toc_exists => false},
:actions => [:youre_screwed]}]
@folder = folder
@song = song
@songhash = merge_hashes(folder.classify,song.classify)
end
def rename_file
expr1 = %r{^(\d\d)[\. _-](.*)\.mp3}
m1 = expr1.match(song.filename)
expr2 = %r{(.*)_-_(\d\d)_\.(.*)\.mp3}
m2 = expr2.match(song.filename)
if m1
File.rename("#{song.filename}","#{song.dirname}.#{m1[1]}.mp3")
song.filename = "#{song.dirname}.#{m1[1]}.mp3"
puts "renaming file to #{song.filename}"
else
if m2
File.rename("#{song.filename}","#{song.dirname}.#{m2[2]}.mp3")
song.filename = "#{song.dirname}.#{m2[2]}.mp3"
puts "renaming file to #{song.filename}"
else
puts "#{song.filename}: filename parse error"
end
end
end
def convert_toc1_to_metadata
toc = folder.parse_toc_v1
tags = Mp3Tag.new(song.filename)
expr = %r{^(.*)\.(\d\d)\.mp3}
m1 = expr.match(song.filename)
if m1
puts "#{song.filename}: Writing Mp3 Tag."
tags.artist = toc[:artist]
tags.album = toc[:album]
tags.tracknum = m1[2].to_i
tags.songname = toc[:title][m1[2].to_i - 1]
tags.commit
#puts "Artist: #{tags.artist}","Album: #{tags.album}","Track Number: #{tags.tracknum}","Title: #{tags.songname}"
else
puts "#{song.filename}: filename parse error"
end
end
def move_v2_to_v1
system("eyeD3 --to-v1.1 #{song.filename.shellescape}")
end
def delete_v2
system("eyeD3 --remove-v2 #{song.filename.shellescape}")
end
def restart_script
make_songs(folder).each{|song| Automaton.new(folder,song).dispatch}
end
def partial_info
# expr1 = %r{^(\d\d)(?:_| )?-(?:_| )?(.*?)(?:_192_lame_cbr)?.mp3}
# m1 = expr1.match(song.filename)
# if m1
# tracknum = m1[1].to_i
# songname = m1[2].gsub("_"," ")
# else
# puts "#{song.filename}: filename parse error"
# end
# tags = Mp3Tag.new(song.filename)
# tags.artist = artist
# tags.album = album
# tags.tracknum = tracknum
# tags.songname = songname
# tags.commit
#puts "Artist: #{tags.artist}","Album: #{tags.album}","Track Number: #{tags.tracknum}","Title: #{tags.songname}"
puts "#{folder.dirname}/#{song.filename}: partial information"
end
def everything_is_ok
puts "#{song.filename} is okay"
end
def unknown_toc_format
puts "#{folder.dirname}: Unknown toc format"
end
def youre_screwed
puts "#{song.filename}: insufficient data for tags."
end
def error_message
puts "#{song.filename}: unrecognized conditions."
end
def merge_hashes(hash1,hash2)
combined = {}
hash1.each_pair{|k,v| combined[k] = v}
hash2.each_pair{|k,v| combined[k] = v}
combined
end
def myall(hash)
bool = true
hash.each_pair{|k,v| bool = bool && yield(k,v)}
bool
end
def left_join(hash1,hash2)
myall(hash2) {|k,v| v == hash1[k]}
end
def dispatch
actions = [:error_message]
action_table.each{|entry|
if left_join(songhash,entry[:conditions])
# puts "#{song.filename}: #{entry[:conditions]} ==> #{entry[:actions]}"
actions = entry[:actions]
end}
actions.each{|v| method(v).call}
end
end
def hashdraw(title,hash)
puts title
hash.each_pair{|k,v| puts "#{k}: #{v}"}
puts ""
end
folder = Folder.new(ARGV[0])
#folder.debug
make_songs(folder).each{|song| Automaton.new(folder,song).dispatch}
#hashdraw("lets see if this works",folder.parse_toc_v1)