MP3 ファイルのタグ付けを一気にやるrubyスクリプト

自分用のちょこっとスクリプトです。動作は無保証です。

(2009/07/01 追記: ちょこっと改良版書きました)

インディーズというか個人製作のmp3を落としていたりするとタグが適切についていない場合があるので作りました。

対象はアルバム名とアーティスト名。個人的にアーティストというのは嫌いなんですが(ぉ

利用方法

下記のようなフォルダ構成で mp3 ファイルが配置されている事を想定しています

音楽ファイル用のディレクトリ/アーティスト名/アルバム名/mp3ファイル

動作

  • カレントディレクトリ配下一階層目のディレクトリ名をアーティスト名として認識します。アーティスト名が設定されていないファイルに対してアーティスト名を設定します
  • mp3ファイルが入っているディレクトリの名前をアルバム名として認識します。アルバム名が設定されていないファイルにアルバム名を(ry
  • これらの動作はカレントディレクトリ以下全ての mp3 ファイルに対して行われます

制限

  • 適当に作っているので、currentdirectory/artist_name/hoge.mp3 とかいう構成だと上手くいきません。カレントディレクトリから2階層以上深いフォルダ構成である必要があります。最低限 currentdirectory/artist_name/alubm_name/hoge.mp3 のような構成である必要があります。
  • Settings をいじればこの辺の挙動は変えられます
  • 判らなくて使いたい人は適当に質問くださ(ぉ

依存ライブラリ

Mp3Info

gem install ruby-mp3info

Code

require 'rubygems'
require 'mp3info'
require 'find'
require 'jcode'

module Settings
  NameDirectoryFromCurrent= {
    :artist => {
      :_default_ => 1,
    },
    :album => {
      :_default_ => -2,
    }
  }
end

class NameAnswerer
  include Settings

  def initialize(path)
    @path = path
  end

  def artist
    get_name :artist
  end

  def album
    get_name :album
  end

  private
    def get_name(type)
      NameDirectoryFromCurrent[type].has_key?(get_first_directory_sym) ?
        get_value_from_index(type, NameDirectoryFromCurrent[type][get_first_directory_sym]) :
        get_value_from_index(type)
    end

    def get_first_directory_sym
      @path.split('/')[1].to_sym
    end

    def get_value_from_index(type, index=nil)
      f = @path.split('/')
      index ?
        f[index] :
        f[NameDirectoryFromCurrent[type][:_default_]]
    end
end

errors = []
Find.find('./') do |path|
  next unless path =~ /\.mp3\z/
  begin
    ns = NameAnswerer.new(path)
    Mp3Info.open(path) do |mp3|
      puts mp3.filename
      if !mp3.tag.artist
        mp3.tag.artist = ns.artist
        puts "\t ARTIST:" << ns.artist
      end
      if !mp3.tag.album
        mp3.tag.album = ns.album
        puts "\t ALBUM:" << ns.album
      end
    end
  rescue Mp3InfoError => e
    errors << [e, path]
  end
end

if errors.size > 0
  puts 'errors:'
  errors.each do |e|
    puts "\t" << e[1]
    puts "\t\t" << e[0].message
  end
end