lib/audioinfo.rb in ruby-audioinfo-0.4 vs lib/audioinfo.rb in ruby-audioinfo-0.5.0

- old
+ new

@@ -5,10 +5,11 @@ require "ogginfo" require "wmainfo" require "mp4info" require "flacinfo" require "apetag" +require "wavefile" $: << File.expand_path(File.dirname(__FILE__)) require "audioinfo/mpcinfo" require "audioinfo/case_insensitive_hash" @@ -32,20 +33,20 @@ "albumartistid" => "Album Artist Id", "sortname" => "Sort Name", "trackid" => "Track Id" } - SUPPORTED_EXTENSIONS = %w{mp3 ogg opus spx mpc wma mp4 aac m4a flac} + SUPPORTED_EXTENSIONS = %w{mp3 ogg opus spx mpc wma mp4 aac m4a flac wav} - VERSION = "0.4" + VERSION = "0.5.0" attr_reader :path, :extension, :musicbrainz_infos, :tracknum, :bitrate, :vbr attr_reader :artist, :album, :title, :length, :date # Part of testing API - you should not use this directly attr_reader :info - + # "block version" of #new() def self.open(*args) audio_info = self.new(*args) ret = nil if block_given? @@ -79,148 +80,152 @@ @extension = ext[1..-1].downcase @musicbrainz_infos = {} begin case @extension - when 'mp3' - @info = Mp3Info.new(filename) - default_tag_fill - #"TXXX"=> - #["MusicBrainz TRM Id\000", - #"MusicBrainz Artist Id\000aba64937-3334-4c65-90a1-4e6b9d4d7ada", - #"MusicBrainz Album Id\000e1a223c1-cbc2-427f-a192-5d22fefd7c4c", - #"MusicBrainz Album Type\000album", - #"MusicBrainz Album Status\000official", - #"MusicBrainz Album Artist Id\000"] - - if (arr = @info.tag2["TXXX"]).is_a?(Array) - fields = MUSICBRAINZ_FIELDS.invert - arr.each do |val| - if val =~ /^MusicBrainz (.+)\000(.*)$/ - short_name = fields[$1] - @musicbrainz_infos[short_name] = $2.gsub("\xEF\xBB\xBF".force_encoding("UTF-8"), '') - end - end - end - @bitrate = @info.bitrate - i = @info.tag.tracknum - @tracknum = (i.is_a?(Array) ? i.last : i).to_i - @length = @info.length.to_i - @date = @info.tag["date"] - @vbr = @info.vbr - @info.close + when 'mp3' + @info = Mp3Info.new(filename) + default_tag_fill + #"TXXX"=> + #["MusicBrainz TRM Id\000", + #"MusicBrainz Artist Id\000aba64937-3334-4c65-90a1-4e6b9d4d7ada", + #"MusicBrainz Album Id\000e1a223c1-cbc2-427f-a192-5d22fefd7c4c", + #"MusicBrainz Album Type\000album", + #"MusicBrainz Album Status\000official", + #"MusicBrainz Album Artist Id\000"] - when 'ogg', 'opus', 'spx' - @info = OggInfo.new(filename) - default_fill_musicbrainz_fields - default_tag_fill - @bitrate = @info.bitrate/1000 - @tracknum = @info.tag.tracknumber.to_i - @length = @info.length.to_i - @date = @info.tag["date"] - @vbr = true - @info.close - - when 'mpc' - fill_ape_tag(filename) + if (arr = @info.tag2["TXXX"]).is_a?(Array) + fields = MUSICBRAINZ_FIELDS.invert + arr.each do |val| + if val =~ /^MusicBrainz (.+)\000(.*)$/ + short_name = fields[$1] + @musicbrainz_infos[short_name] = $2.gsub("\xEF\xBB\xBF".force_encoding("UTF-8"), '') + end + end + end + @bitrate = @info.bitrate + i = @info.tag.tracknum + @tracknum = (i.is_a?(Array) ? i.last : i).to_i + @length = @info.length.to_i + @date = @info.tag["date"] + @vbr = @info.vbr + @info.close - mpc_info = MpcInfo.new(filename) - @bitrate = mpc_info.infos['bitrate']/1000 - @length = mpc_info.infos['length'] + when 'ogg', 'opus', 'spx' + @info = OggInfo.new(filename) + default_fill_musicbrainz_fields + default_tag_fill + @bitrate = @info.bitrate/1000 + @tracknum = @info.tag.tracknumber.to_i + @length = @info.length.to_i + @date = @info.tag["date"] + @vbr = true + @info.close - when 'ape' - fill_ape_tag(filename) + when 'mpc' + fill_ape_tag(filename) + mpc_info = MpcInfo.new(filename) + @bitrate = mpc_info.infos['bitrate']/1000 + @length = mpc_info.infos['length'] - when 'wma' - @info = WmaInfo.new(filename, :encoding => 'utf-8') - @artist = @info.tags["Author"] - @album = @info.tags["AlbumTitle"] - @title = @info.tags["Title"] - @tracknum = @info.tags["TrackNumber"].to_i - @date = @info.tags["Year"] - @bitrate = @info.info["bitrate"] - @length = @info.info["playtime_seconds"] - MUSICBRAINZ_FIELDS.each do |key, original_key| - @musicbrainz_infos[key] = - @info.info["MusicBrainz/" + original_key.tr(" ", "")] || - @info.info["MusicBrainz/" + original_key] - end - - when 'mp4', 'aac', 'm4a' - @extension = 'mp4' - @info = MP4Info.open(filename) - @artist = @info.ART - @album = @info.ALB - @title = @info.NAM - @tracknum = ( t = @info.TRKN ) ? t.first : 0 - @date = @info.DAY - @bitrate = @info.BITRATE - @length = @info.SECS - mapping = MUSICBRAINZ_FIELDS.invert + when 'ape' + fill_ape_tag(filename) - faad_info(filename).match(/^MusicBrainz (.+)$/) do - name, value = $1.split(/: /, 2) - key = mapping[name] - @musicbrainz_infos[key] = value - end - - when 'flac' - @info = FlacInfo.new(filename) - # Unfortunately, FlacInfo doesn't allow us to fiddle inside - # their class, so we have to brute force it. Any other - # solution (e.g. creating another abstraction or getting - # methods) lands up being more messy and brittle. - @info.instance_variable_set('@tags', CaseInsensitiveHash.new(@info.tags)) + when 'wma' + @info = WmaInfo.new(filename, :encoding => 'utf-8') + @artist = @info.tags["Author"] + @album = @info.tags["AlbumTitle"] + @title = @info.tags["Title"] + @tracknum = @info.tags["TrackNumber"].to_i + @date = @info.tags["Year"] + @bitrate = @info.info["bitrate"] + @length = @info.info["playtime_seconds"] + MUSICBRAINZ_FIELDS.each do |key, original_key| + @musicbrainz_infos[key] = + @info.info["MusicBrainz/" + original_key.tr(" ", "")] || + @info.info["MusicBrainz/" + original_key] + end - get_tag = proc do |name| - if t = @info.tags[name] - t.dup.force_encoding("utf-8") - else - nil - end + when 'mp4', 'aac', 'm4a' + @extension = 'mp4' + @info = MP4Info.open(filename) + @artist = @info.ART + @album = @info.ALB + @title = @info.NAM + @tracknum = ( t = @info.TRKN ) ? t.first : 0 + @date = @info.DAY + @bitrate = @info.BITRATE + @length = @info.SECS + mapping = MUSICBRAINZ_FIELDS.invert + + faad_info(filename).match(/^MusicBrainz (.+)$/) do + name, value = $1.split(/: /, 2) + key = mapping[name] + @musicbrainz_infos[key] = value + end + + when 'flac' + @info = FlacInfo.new(filename) + # Unfortunately, FlacInfo doesn't allow us to fiddle inside + # their class, so we have to brute force it. Any other + # solution (e.g. creating another abstraction or getting + # methods) lands up being more messy and brittle. + @info.instance_variable_set('@tags', CaseInsensitiveHash.new(@info.tags)) + + get_tag = proc do |name| + if t = @info.tags[name] + t.dup.force_encoding("utf-8") + else + nil end - - @artist = get_tag.call("artist") - @album = get_tag.call("album") - @title = get_tag.call("title") - @tracknum = @info.tags["tracknumber"].to_i - @date = get_tag.call("date") - @bitrate = 0 - @length = @info.streaminfo["total_samples"] / @info.streaminfo["samplerate"].to_f - if @length > 0 - @bitrate = File.size(filename).to_f*8/@length/1024 - end - @info.tags.each do |tagname, tagvalue| - next unless tagname =~ /^musicbrainz_(.+)$/ - @musicbrainz_infos[$1] = get_tag.call(tagname) - end - @musicbrainz_infos["trmid"] = @info.tags["musicip_puid"] - #default_fill_musicbrainz_fields - else - raise(AudioInfoError, "unsupported extension '.#{@extension}'") + end + + @artist = get_tag.call("artist") + @album = get_tag.call("album") + @title = get_tag.call("title") + @tracknum = @info.tags["tracknumber"].to_i + @date = get_tag.call("date") + @bitrate = 0 + @length = @info.streaminfo["total_samples"] / @info.streaminfo["samplerate"].to_f + if @length > 0 + @bitrate = File.size(filename).to_f*8/@length/1024 + end + @info.tags.each do |tagname, tagvalue| + next unless tagname =~ /^musicbrainz_(.+)$/ + @musicbrainz_infos[$1] = get_tag.call(tagname) + end + @musicbrainz_infos["trmid"] = @info.tags["musicip_puid"] + #default_fill_musicbrainz_fields + + when 'wav' + @info = WaveFile::Reader.info(filename) + @length = @info.duration.seconds + @bitrate = File.size(filename) * 8 / @length / 1024 + + else + raise(AudioInfoError, "unsupported extension '.#{@extension}'") end if @tracknum == 0 @tracknum = nil end @musicbrainz_infos.delete_if { |k, v| v.nil? } @hash = { "artist" => @artist, - "album" => @album, - "title" => @title, - "tracknum" => @tracknum, - "date" => @date, - "length" => @length, - "bitrate" => @bitrate, + "album" => @album, + "title" => @title, + "tracknum" => @tracknum, + "date" => @date, + "length" => @length, + "bitrate" => @bitrate, } rescue Exception, Mp3InfoError, OggInfoError, ApeTagError => e raise AudioInfoError, e.to_s, e.backtrace end @needs_commit = false - end # set the title of the file def title=(v) if @title != v @@ -273,66 +278,65 @@ # close the file and commits changes to disk def close if @needs_commit case @info - when Mp3Info - Mp3Info.open(@path) do |info| - info.tag.artist = @artist - info.tag.title = @title - info.tag.album = @album - info.tag.tracknum = @tracknum - if @picture - info.tag2.remove_pictures - info.tag2.add_picture(File.binread(@picture)) - end - end - when OggInfo - OggInfo.open(@path) do |ogg| - { "artist" => @artist, - "album" => @album, - "title" => @title, - "tracknumber" => @tracknum}.each do |k,v| - ogg.tag[k] = v.to_s - end - if @picture - ogg.picture = @picture - end - end + when Mp3Info + Mp3Info.open(@path) do |info| + info.tag.artist = @artist + info.tag.title = @title + info.tag.album = @album + info.tag.tracknum = @tracknum + if @picture + info.tag2.remove_pictures + info.tag2.add_picture(File.binread(@picture)) + end + end + when OggInfo + OggInfo.open(@path) do |ogg| + { "artist" => @artist, + "album" => @album, + "title" => @title, + "tracknumber" => @tracknum}.each do |k,v| + ogg.tag[k] = v.to_s + end + if @picture + ogg.picture = @picture + end + end - when ApeTag - ape = ApeTag.new(@path) - ape.update do |fields| - fields["Artist"] = @artist - fields["Album"] = @album - fields["Title"] = @title - fields["Track"] = @tracknum.to_s + when ApeTag + ape = ApeTag.new(@path) + ape.update do |fields| + fields["Artist"] = @artist + fields["Album"] = @album + fields["Title"] = @title + fields["Track"] = @tracknum.to_s + end + else + have_metaflac = system("which metaflac > /dev/null") + have_ffmpeg = system("which ffmpeg > /dev/null") + if have_metaflac and @info.is_a?(FlacInfo) + tags = {"ARTIST" => @artist, + "ALBUM" => @album, + "TITLE" => @title, + "TRACKNUMBER" => @tracknum}.inject([]) do |tags, (key, value)| + tags + ["--set-tag", "#{key}=#{value.to_s}"] end - else - have_metaflac = system("which metaflac > /dev/null") - have_ffmpeg = system("which ffmpeg > /dev/null") - if have_metaflac and @info.is_a?(FlacInfo) - tags = {"ARTIST" => @artist, - "ALBUM" => @album, - "TITLE" => @title, - "TRACKNUMBER" => @tracknum}.inject([]) do |tags, (key, value)| - tags + ["--set-tag", "#{key}=#{value.to_s}"] - end - tag_with_shell_command("metaflac", "--remove-all", :src) - tag_with_shell_command("metaflac", tags, :src) - elsif have_ffmpeg - tags = {"artist" => @artist, - "album" => @album, - "title" => @title}.inject([]) do |tags, (key, value)| - tags + ["-metadata", "#{key}=#{value.to_s}"] - end - tag_with_shell_command("ffmpeg", "-y", "-i", :src, "-loglevel", "quiet", tags, :dst) - else - raise(AudioInfoError, "implement me") + tag_with_shell_command("metaflac", "--remove-all", :src) + tag_with_shell_command("metaflac", tags, :src) + elsif have_ffmpeg + tags = {"artist" => @artist, + "album" => @album, + "title" => @title}.inject([]) do |tags, (key, value)| + tags + ["-metadata", "#{key}=#{value.to_s}"] end + tag_with_shell_command("ffmpeg", "-y", "-i", :src, "-loglevel", "quiet", tags, :dst) + else + raise(AudioInfoError, "implement me") + end end - end @needs_commit end =begin {"musicbrainz_albumstatus"=>"official", @@ -354,10 +358,10 @@ # check if the file is correctly tagged by MusicBrainz def mb_tagged? ! @musicbrainz_infos.empty? end - private + private def sanitize(input) s = input.is_a?(Array) ? input.first : input s.gsub("\000", "") end