lib/audioinfo.rb in ruby-audioinfo-0.5.2 vs lib/audioinfo.rb in ruby-audioinfo-0.5.4
- old
+ new
@@ -1,55 +1,47 @@
-# encoding: utf-8
-require "stringio"
+# frozen_string_literal: true
-require "mp3info"
-require "ogginfo"
-require "wmainfo"
-require "mp4info"
-require "flacinfo"
-require "apetag"
-require "wavefile"
+require 'stringio'
-$: << File.expand_path(File.dirname(__FILE__))
+require 'mp3info'
+require 'ogginfo'
+require 'wmainfo'
+require 'mp4info'
+require 'flacinfo'
+require 'apetag'
+require 'wavefile'
-require "audioinfo/mpcinfo"
-require "audioinfo/case_insensitive_hash"
+$LOAD_PATH << __dir__
-class AudioInfoError < StandardError ; end
+require 'audioinfo/mpcinfo'
+require 'audioinfo/case_insensitive_hash'
+require 'audioinfo/version'
+class AudioInfoError < StandardError; end
+
class AudioInfo
- if RUBY_VERSION[0..2] == "1.8"
- RUBY_1_8 = true
- require "iconv"
- else
- RUBY_1_8 = false
- end
+ MUSICBRAINZ_FIELDS = {
+ 'trmid' => 'TRM Id',
+ 'artistid' => 'Artist Id',
+ 'albumid' => 'Album Id',
+ 'albumtype' => 'Album Type',
+ 'albumstatus' => 'Album Status',
+ 'albumartistid' => 'Album Artist Id',
+ 'sortname' => 'Sort Name',
+ 'trackid' => 'Track Id'
+ }.freeze
- MUSICBRAINZ_FIELDS = {
- "trmid" => "TRM Id",
- "artistid" => "Artist Id",
- "albumid" => "Album Id",
- "albumtype" => "Album Type",
- "albumstatus" => "Album Status",
- "albumartistid" => "Album Artist Id",
- "sortname" => "Sort Name",
- "trackid" => "Track Id"
- }
+ SUPPORTED_EXTENSIONS = %w[mp3 ogg opus spx mpc wma mp4 aac m4a flac wav].freeze
- SUPPORTED_EXTENSIONS = %w{mp3 ogg opus spx mpc wma mp4 aac m4a flac wav}
+ attr_reader :path, :extension, :musicbrainz_infos, :tracknum, :bitrate, :vbr, :artist, :album, :title, :length, :date
- VERSION = "0.5.2"
-
- 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)
+ audio_info = new(*args)
ret = nil
if block_given?
begin
ret = yield(audio_info)
ensure
@@ -61,167 +53,161 @@
ret
end
# test whether +path+ is a valid and supported audiofile
def self.is_audio_file?(path)
- begin
- AudioInfo.new(path)
- return true
- rescue AudioInfoError
- return false
- end
+ AudioInfo.new(path)
+ true
+ rescue AudioInfoError
+ false
end
# open the file with path +fn+
- def initialize(filename)
- raise(AudioInfoError, "path is nil") if filename.nil?
+ def initialize(filename, extension = nil)
+ raise(AudioInfoError, 'path is nil') if filename.nil?
+
@path = filename
ext = File.extname(@path)
- raise(AudioInfoError, "cannot find extension") if ext.empty?
- @extension = ext[1..-1].downcase
+ @extension = extension || (ext && ext[1..-1].downcase)
+ raise(AudioInfoError, 'cannot find extension') if @extension.empty?
+
@musicbrainz_infos = {}
begin
case @extension
- when 'mp3'
+ 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"]
+ # "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)
+ 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
+ short_name = fields[Regexp.last_match(1)]
+ @musicbrainz_infos[short_name] = Regexp.last_match(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"]
+ @date = @info.tag['date']
@vbr = @info.vbr
@info.close
when 'ogg', 'opus', 'spx'
@info = OggInfo.new(filename)
default_fill_musicbrainz_fields
default_tag_fill
- @bitrate = @info.bitrate/1000
+ @bitrate = @info.bitrate / 1000
@tracknum = @info.tag.tracknumber.to_i
@length = @info.length.to_i
- @date = @info.tag["date"]
+ @date = @info.tag['date']
@vbr = true
@info.close
- when 'mpc'
+ when 'mpc'
fill_ape_tag(filename)
- mpc_info = MpcInfo.new(filename)
- @bitrate = mpc_info.infos['bitrate']/1000
- @length = mpc_info.infos['length']
+ mpc_info = MpcInfo.new(filename)
+ @bitrate = mpc_info.infos['bitrate'] / 1000
+ @length = mpc_info.infos['length']
when 'ape'
- fill_ape_tag(filename)
+ fill_ape_tag(filename)
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"]
+ @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
+ @info.info["MusicBrainz/#{original_key.tr(' ', '')}"] ||
+ @info.info["MusicBrainz/#{original_key}"]
+ end
- when 'mp4', 'aac', 'm4a'
+ 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
+ @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)
+ name, value = Regexp.last_match(1).split(/: /, 2)
key = mapping[name]
@musicbrainz_infos[key] = value
end
- when 'flac'
+ 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
+ t.dup.force_encoding('utf-8')
end
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")
+ @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|
+ @length = @info.streaminfo['total_samples'] / @info.streaminfo['samplerate'].to_f
+ @bitrate = File.size(filename).to_f * 8 / @length / 1024 if @length.positive?
+ @info.tags.each do |tagname, _tagvalue|
next unless tagname =~ /^musicbrainz_(.+)$/
- @musicbrainz_infos[$1] = get_tag.call(tagname)
+
+ @musicbrainz_infos[Regexp.last_match(1)] = get_tag.call(tagname)
end
- @musicbrainz_infos["trmid"] = @info.tags["musicip_puid"]
- #default_fill_musicbrainz_fields
+ @musicbrainz_infos['trmid'] = @info.tags['musicip_puid']
+ # default_fill_musicbrainz_fields
when 'wav'
@info = WaveFile::Reader.info(filename)
- @length = @info.duration.hours * 3600 + @info.duration.minutes * 60 + @info.duration.seconds + @info.duration.milliseconds * 0.001
+ @length = @info.duration.hours * 3600 + @info.duration.minutes * 60 + @info.duration.seconds +
+ @info.duration.milliseconds * 0.001
@bitrate = File.size(filename) * 8 / @length / 1024
else
raise(AudioInfoError, "unsupported extension '.#{@extension}'")
end
- if @tracknum == 0
- @tracknum = nil
- end
+ @tracknum = nil if @tracknum&.zero?
- @musicbrainz_infos.delete_if { |k, v| v.nil? }
- @hash = { "artist" => @artist,
- "album" => @album,
- "title" => @title,
- "tracknum" => @tracknum,
- "date" => @date,
- "length" => @length,
- "bitrate" => @bitrate,
- }
-
- rescue Exception, Mp3InfoError, OggInfoError, ApeTagError => e
+ @musicbrainz_infos.delete_if { |_k, v| v.nil? }
+ @hash = { 'artist' => @artist,
+ 'album' => @album,
+ 'title' => @title,
+ 'tracknum' => @tracknum,
+ 'date' => @date,
+ 'length' => @length,
+ 'bitrate' => @bitrate }
+ rescue StandardError, Mp3InfoError, OggInfoError, ApeTagError => e
raise AudioInfoError, e.to_s, e.backtrace
end
@needs_commit = false
end
@@ -285,128 +271,121 @@
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))
+ 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
+ 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
+ ogg.picture = @picture if @picture
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
+ 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}"]
+ else
+ have_metaflac = system('which metaflac > /dev/null')
+ have_ffmpeg = system('which ffmpeg > /dev/null')
+ if have_metaflac && @info.is_a?(FlacInfo)
+ tags = { 'ARTIST' => @artist,
+ 'ALBUM' => @album,
+ 'TITLE' => @title,
+ 'TRACKNUMBER' => @tracknum }.inject([]) do |tags, (key, value)|
+ tags + ['--set-tag', "#{key}=#{value}"]
end
- tag_with_shell_command("metaflac", "--remove-all", :src)
- tag_with_shell_command("metaflac", tags, :src)
+ 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}"]
+ tags = { 'artist' => @artist,
+ 'album' => @album,
+ 'title' => @title }.inject([]) do |tags, (key, value)|
+ tags + ['-metadata', "#{key}=#{value}"]
end
- tag_with_shell_command("ffmpeg", "-y", "-i", :src, "-loglevel", "quiet", tags, :dst)
+ tag_with_shell_command('ffmpeg', '-y', '-i', :src, '-loglevel', 'quiet', tags, :dst)
else
- raise(AudioInfoError, "implement me")
+ raise(AudioInfoError, 'implement me')
end
end
end
@needs_commit
end
-=begin
- {"musicbrainz_albumstatus"=>"official",
- "artist"=>"Jill Scott",
- "replaygain_track_gain"=>"-3.29 dB",
- "tracknumber"=>"1",
- "title"=>"A long walk (A touch of Jazz Mix)..Jazzanova Love Beats...",
- "musicbrainz_sortname"=>"Scott, Jill",
- "musicbrainz_artistid"=>"b1fb6a18-1626-4011-80fb-eaf83dfebcb6",
- "musicbrainz_albumid"=>"cb2ad8c7-4a02-4e46-ae9a-c7c2463c7235",
- "replaygain_track_peak"=>"0.82040048",
- "musicbrainz_albumtype"=>"compilation",
- "album"=>"...Mixing (Jazzanova)",
- "musicbrainz_trmid"=>"1ecec0a6-c7c3-4179-abea-ef12dabc7cbd",
- "musicbrainz_trackid"=>"0a368e63-dddf-441f-849c-ca23f9cb2d49",
- "musicbrainz_albumartistid"=>"89ad4ac3-39f7-470e-963a-56509c546377"}>
-=end
+ # {"musicbrainz_albumstatus"=>"official",
+ # "artist"=>"Jill Scott",
+ # "replaygain_track_gain"=>"-3.29 dB",
+ # "tracknumber"=>"1",
+ # "title"=>"A long walk (A touch of Jazz Mix)..Jazzanova Love Beats...",
+ # "musicbrainz_sortname"=>"Scott, Jill",
+ # "musicbrainz_artistid"=>"b1fb6a18-1626-4011-80fb-eaf83dfebcb6",
+ # "musicbrainz_albumid"=>"cb2ad8c7-4a02-4e46-ae9a-c7c2463c7235",
+ # "replaygain_track_peak"=>"0.82040048",
+ # "musicbrainz_albumtype"=>"compilation",
+ # "album"=>"...Mixing (Jazzanova)",
+ # "musicbrainz_trmid"=>"1ecec0a6-c7c3-4179-abea-ef12dabc7cbd",
+ # "musicbrainz_trackid"=>"0a368e63-dddf-441f-849c-ca23f9cb2d49",
+ # "musicbrainz_albumartistid"=>"89ad4ac3-39f7-470e-963a-56509c546377"}>
# check if the file is correctly tagged by MusicBrainz
def mb_tagged?
- ! @musicbrainz_infos.empty?
+ !@musicbrainz_infos.empty?
end
private
def sanitize(input)
s = input.is_a?(Array) ? input.first : input
- s.gsub("\000", "")
+ s.delete("\000")
end
def default_fill_musicbrainz_fields(tags = @info.tag)
- MUSICBRAINZ_FIELDS.keys.each do |field|
+ MUSICBRAINZ_FIELDS.each_key do |field|
val = tags["musicbrainz_#{field}"]
@musicbrainz_infos[field] = val if val
end
end
def default_tag_fill(tags = @info.tag)
- %w{artist album title}.each do |v|
- instance_variable_set( "@#{v}".to_sym, sanitize(tags[v]||"") )
+ %w[artist album title].each do |v|
+ instance_variable_set("@#{v}".to_sym, sanitize(tags[v] || ''))
end
end
def fill_ape_tag(filename)
- begin
- @info = ApeTag.new(filename)
- tags = @info.fields.inject({}) do |hash, (k, v)|
- hash[k.downcase] = v ? v.first : nil
- hash
- end
- default_fill_musicbrainz_fields(tags)
- default_tag_fill(tags)
-
- @date = tags["year"]
- @tracknum = tags['track'].to_i
- rescue ApeTagError
+ @info = ApeTag.new(filename)
+ tags = @info.fields.each_with_object({}) do |(k, v), hash|
+ hash[k.downcase] = v ? v.first : nil
end
+ default_fill_musicbrainz_fields(tags)
+ default_tag_fill(tags)
+
+ @date = tags['year']
+ @tracknum = tags['track'].to_i
+ rescue ApeTagError
end
def faad_info(file)
stdout, stdout_w = IO.pipe
stderr, stderr_w = IO.pipe
fork do
stdout.close
stderr.close
- STDOUT.reopen(stdout_w)
- STDERR.reopen(stderr_w)
+ $stdout.reopen(stdout_w)
+ $stderr.reopen(stderr_w)
exec 'faad', '-i', file
end
stdout_w.close
stderr_w.close
@@ -420,23 +399,23 @@
# Return the stderr because faad prints info on that fd...
status.exitstatus.zero? ? err : ''
end
def shell_escape(s)
- "'" + s.gsub(/'/) { "'\\''" } + "'"
+ "'#{s.gsub(/'/) { "'\\''" }}'"
end
def tag_with_shell_command(*command_arr)
expand_command = proc do |hash|
command_arr.collect do |token|
token.is_a?(Symbol) ? hash[token] : token
end.flatten
end
- hash = {:src => @path}
+ hash = { src: @path }
if command_arr.include?(:dst)
- Tempfile.open(["ruby-audioinfo", "."+@extension]) do |tf|
- cmd = expand_command.call(hash.merge(:dst => tf.path))
+ Tempfile.open(['ruby-audioinfo', ".#{@extension}"]) do |tf|
+ cmd = expand_command.call(hash.merge(dst: tf.path))
tf.close
if system(*cmd)
FileUtils.mv(tf.path, @path)
else
raise(AudioInfoError, "error while running #{command_arr[0]}")