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
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)
|
|
|