ruby-audioinfo 0.2.3 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: be66808c6cbe04237d1052537ff0ad4fc2f3f614
4
+ data.tar.gz: b2843c08fe747e1c77cc94490ca266559539bc45
5
+ SHA512:
6
+ metadata.gz: 8838cdedf9de87e385559086a0dbafd9bb315b3d8254673c0441ef234d9769435c4c3ee34bc88c2325e15d7a1f7108e8efd684e051e054ccbc75441550a45003
7
+ data.tar.gz: bf2999b9756f2ec4e986e28fdce3d2d169917802b99c1b565db5ed9d60bf1ceb70e2fe21a473b04af097e400c41eb64c7c9779514ba22f2bcc8d95238b6e2f9f
data/History.txt CHANGED
@@ -1,3 +1,11 @@
1
+ === 0.3 / 2013-03-26
2
+
3
+ * correctly computes bitrate on .flac files
4
+ * string encoding fixes on .flac files
5
+ * write support on .flac with "metaflac" binary
6
+ * case insensitive wrapper to flacinfo's tags hash
7
+ * uses "ffmpeg" binary for writing tags on not natively supported format
8
+
1
9
  === 0.2.3 / 2012-05-24
2
10
 
3
11
  * fixed title method on AudioInfo::Album, now returns nil if title is not found
data/Manifest.txt CHANGED
@@ -5,4 +5,5 @@ Rakefile
5
5
  lib/audioinfo.rb
6
6
  lib/audioinfo/album.rb
7
7
  lib/audioinfo/mpcinfo.rb
8
+ lib/audioinfo/case_insensitive_hash.rb
8
9
  test/mpcinfo.rb
data/Rakefile CHANGED
@@ -17,8 +17,8 @@ Hoe.spec('ruby-audioinfo') do
17
17
  self.test_globs = ["test/test_*.rb"]
18
18
  self.rsync_args = "-rv --delete"
19
19
 
20
- extra_deps << ['ruby-mp3info', '>= 0.7.1']
21
- extra_deps << ['ruby-ogginfo', '>= 0.6.9']
20
+ extra_deps << ['ruby-mp3info', '>= 0.8']
21
+ extra_deps << ['ruby-ogginfo', '>= 0.6.11']
22
22
  extra_deps << ['mp4info', '>= 1.7.3']
23
23
  extra_deps << ['moumar-wmainfo-rb', '>= 0.7']
24
24
  extra_deps << ['flacinfo-rb', '>= 0.4']
data/lib/audioinfo.rb CHANGED
@@ -11,6 +11,7 @@ require "apetag"
11
11
  $: << File.expand_path(File.dirname(__FILE__))
12
12
 
13
13
  require "audioinfo/mpcinfo"
14
+ require "audioinfo/case_insensitive_hash"
14
15
 
15
16
  class AudioInfoError < StandardError ; end
16
17
 
@@ -35,10 +36,13 @@ class AudioInfo
35
36
 
36
37
  SUPPORTED_EXTENSIONS = %w{mp3 ogg mpc wma mp4 aac m4a flac}
37
38
 
38
- VERSION = "0.2.3"
39
+ VERSION = "0.3.1"
39
40
 
40
41
  attr_reader :path, :extension, :musicbrainz_infos, :tracknum, :bitrate, :vbr
41
42
  attr_reader :artist, :album, :title, :length, :date
43
+
44
+ # Part of testing API - you should not use this directly
45
+ attr_reader :info
42
46
 
43
47
  # "block version" of #new()
44
48
  def self.open(*args)
@@ -161,20 +165,36 @@ class AudioInfo
161
165
 
162
166
  when 'flac'
163
167
  @info = FlacInfo.new(filename)
164
- @artist = @info.tags["ARTIST"] || @info.tags["artist"]
165
- @album = @info.tags["ALBUM"] || @info.tags["album"]
166
- @title = @info.tags["TITLE"] || @info.tags["title"]
167
- @tracknum = (@info.tags["TRACKNUMBER"]||@info.tags["tracknumber"]).to_i
168
- @date = @info.tags["DATE"]||@info.tags["date"]
168
+ # Unfortunately, FlacInfo doesn't allow us to fiddle inside
169
+ # their class, so we have to brute force it. Any other
170
+ # solution (e.g. creating another abstraction or getting
171
+ # methods) lands up being more messy and brittle.
172
+ @info.instance_variable_set('@tags', CaseInsensitiveHash.new(@info.tags))
173
+
174
+ get_tag = proc do |name|
175
+ if t = @info.tags[name]
176
+ t.dup.force_encoding("utf-8")
177
+ else
178
+ nil
179
+ end
180
+ end
181
+
182
+ @artist = get_tag.call("artist")
183
+ @album = get_tag.call("album")
184
+ @title = get_tag.call("title")
185
+ @tracknum = @info.tags["tracknumber"].to_i
186
+ @date = get_tag.call("date")
187
+ @bitrate = 0
169
188
  @length = @info.streaminfo["total_samples"] / @info.streaminfo["samplerate"].to_f
170
- @bitrate = File.size(filename).to_f*8/@length/1024
189
+ if @length > 0
190
+ @bitrate = File.size(filename).to_f*8/@length/1024
191
+ end
171
192
  @info.tags.each do |tagname, tagvalue|
172
193
  next unless tagname =~ /^musicbrainz_(.+)$/
173
- @musicbrainz_infos[$1] = @info.tags[tagname]
194
+ @musicbrainz_infos[$1] = get_tag.call(tagname)
174
195
  end
175
196
  @musicbrainz_infos["trmid"] = @info.tags["musicip_puid"]
176
197
  #default_fill_musicbrainz_fields
177
-
178
198
  else
179
199
  raise(AudioInfoError, "unsupported extension '.#{@extension}'")
180
200
  end
@@ -274,7 +294,27 @@ class AudioInfo
274
294
  fields["Track"] = @tracknum.to_s
275
295
  end
276
296
  else
277
- raise(AudioInfoError, "implement me")
297
+ have_metaflac = system("which metaflac > /dev/null")
298
+ have_ffmpeg = system("which ffmpeg > /dev/null")
299
+ if have_metaflac and @info.is_a?(FlacInfo)
300
+ tags = {"ARTIST" => @artist,
301
+ "ALBUM" => @album,
302
+ "TITLE" => @title,
303
+ "TRACKNUMBER" => @tracknum}.inject([]) do |tags, (key, value)|
304
+ tags + ["--set-tag", "#{key}=#{value.to_s}"]
305
+ end
306
+ tag_with_shell_command("metaflac", "--remove-all", :src)
307
+ tag_with_shell_command("metaflac", tags, :src)
308
+ elsif have_ffmpeg
309
+ tags = {"artist" => @artist,
310
+ "album" => @album,
311
+ "title" => @title}.inject([]) do |tags, (key, value)|
312
+ tags + ["-metadata", "#{key}=#{value.to_s}"]
313
+ end
314
+ tag_with_shell_command("ffmpeg", "-y", "-i", :src, "-loglevel", "quiet", tags, :dst)
315
+ else
316
+ raise(AudioInfoError, "implement me")
317
+ end
278
318
  end
279
319
 
280
320
  end
@@ -362,4 +402,32 @@ class AudioInfo
362
402
  # Return the stderr because faad prints info on that fd...
363
403
  status.exitstatus.zero? ? err : ''
364
404
  end
405
+
406
+ def shell_escape(s)
407
+ "'" + s.gsub(/'/) { "'\\''" } + "'"
408
+ end
409
+
410
+ def tag_with_shell_command(*command_arr)
411
+ expand_command = proc do |hash|
412
+ command_arr.collect do |token|
413
+ token.is_a?(Symbol) ? hash[token] : token
414
+ end.flatten
415
+ end
416
+
417
+ hash = {:src => @path}
418
+ if command_arr.include?(:dst)
419
+ Tempfile.open(["ruby-audioinfo", "."+@extension]) do |tf|
420
+ cmd = expand_command.call(hash.merge(:dst => tf.path))
421
+ tf.close
422
+ if system(*cmd)
423
+ FileUtils.mv(tf.path, @path)
424
+ else
425
+ raise(AudioInfoError, "error while running #{command_arr[0]}")
426
+ end
427
+ end
428
+ else
429
+ cmd = expand_command.call(hash)
430
+ system(*cmd) || raise(AudioInfoError, "error while running #{command_arr[0]}")
431
+ end
432
+ end
365
433
  end
@@ -0,0 +1,16 @@
1
+ class CaseInsensitiveHash < Hash
2
+ def initialize(hash = {})
3
+ super
4
+ hash.each do |key, value|
5
+ self[key.downcase] = value
6
+ end
7
+ end
8
+
9
+ def [](key)
10
+ super(key.downcase)
11
+ end
12
+
13
+ def []=(key, value)
14
+ super(key.downcase, value)
15
+ end
16
+ end
@@ -0,0 +1,35 @@
1
+ require_relative "../lib/audioinfo"
2
+ require "minitest/autorun"
3
+
4
+ require "fileutils"
5
+ require "tmpdir"
6
+
7
+ require_relative "test_helper"
8
+
9
+ class TestAudioinfo < MiniTest::Unit::TestCase
10
+
11
+ FLAC_FILE = "#{Dir.tmpdir}/ruby-audioinfo-test.flac"
12
+
13
+ def setup
14
+ FileUtils.cp(File.join(SUPPORT_DIR, "440Hz-5sec.flac"), FLAC_FILE)
15
+ end
16
+
17
+ def test_flac_whitelist
18
+ i = AudioInfo.new(FLAC_FILE)
19
+ assert_kind_of FlacInfo, i.info
20
+ end
21
+
22
+ def test_flac_tags_wrapper
23
+ i = AudioInfo.new(FLAC_FILE)
24
+ assert_kind_of CaseInsensitiveHash, i.info.tags
25
+ end
26
+
27
+ def test_flac_writing
28
+ title = "test with utf8éblèàqsf"
29
+ ai = AudioInfo.new(FLAC_FILE)
30
+ ai.title = title
31
+ ai.close
32
+ ai = AudioInfo.new(FLAC_FILE)
33
+ assert_equal title, ai.title
34
+ end
35
+ end
@@ -0,0 +1,28 @@
1
+ require "audioinfo"
2
+ require "minitest/autorun"
3
+
4
+ class TestCaseInsensitiveHash < MiniTest::Unit::TestCase
5
+
6
+ def test_string_access
7
+ h = CaseInsensitiveHash.new
8
+ h["foo"] = "bar"
9
+ assert_equal "bar", h["foo"]
10
+ end
11
+
12
+ def test_symbol_access
13
+ h = CaseInsensitiveHash.new
14
+ h[:foo] = "bar"
15
+ assert_equal "bar", h[:foo]
16
+ end
17
+
18
+ def test_case_insensitive_access
19
+ h = CaseInsensitiveHash.new
20
+ h["FOO"] = "bar"
21
+ assert_equal "bar", h["foo"]
22
+ end
23
+
24
+ def test_copy_constructor
25
+ h = CaseInsensitiveHash.new({"FOO" => "bar"})
26
+ assert_equal "bar", h["foo"]
27
+ end
28
+ end
@@ -0,0 +1 @@
1
+ SUPPORT_DIR = File.expand_path("support", File.dirname(__FILE__))
metadata CHANGED
@@ -1,8 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-audioinfo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
5
- prerelease:
4
+ version: 0.3.1
6
5
  platform: ruby
7
6
  authors:
8
7
  - Guillaume Pierronnet
@@ -10,102 +9,124 @@ authors:
10
9
  autorequire:
11
10
  bindir: bin
12
11
  cert_chain: []
13
- date: 2012-05-24 00:00:00.000000000Z
12
+ date: 2013-03-26 00:00:00.000000000 Z
14
13
  dependencies:
15
14
  - !ruby/object:Gem::Dependency
16
15
  name: ruby-mp3info
17
- requirement: &70504490 !ruby/object:Gem::Requirement
18
- none: false
16
+ requirement: !ruby/object:Gem::Requirement
19
17
  requirements:
20
- - - ! '>='
18
+ - - '>='
21
19
  - !ruby/object:Gem::Version
22
- version: 0.7.1
20
+ version: '0.8'
23
21
  type: :runtime
24
22
  prerelease: false
25
- version_requirements: *70504490
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - '>='
26
+ - !ruby/object:Gem::Version
27
+ version: '0.8'
26
28
  - !ruby/object:Gem::Dependency
27
29
  name: ruby-ogginfo
28
- requirement: &70504040 !ruby/object:Gem::Requirement
29
- none: false
30
+ requirement: !ruby/object:Gem::Requirement
30
31
  requirements:
31
- - - ! '>='
32
+ - - '>='
32
33
  - !ruby/object:Gem::Version
33
- version: 0.6.9
34
+ version: 0.6.11
34
35
  type: :runtime
35
36
  prerelease: false
36
- version_requirements: *70504040
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - '>='
40
+ - !ruby/object:Gem::Version
41
+ version: 0.6.11
37
42
  - !ruby/object:Gem::Dependency
38
43
  name: mp4info
39
- requirement: &70503440 !ruby/object:Gem::Requirement
40
- none: false
44
+ requirement: !ruby/object:Gem::Requirement
41
45
  requirements:
42
- - - ! '>='
46
+ - - '>='
43
47
  - !ruby/object:Gem::Version
44
48
  version: 1.7.3
45
49
  type: :runtime
46
50
  prerelease: false
47
- version_requirements: *70503440
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - '>='
54
+ - !ruby/object:Gem::Version
55
+ version: 1.7.3
48
56
  - !ruby/object:Gem::Dependency
49
57
  name: moumar-wmainfo-rb
50
- requirement: &70502870 !ruby/object:Gem::Requirement
51
- none: false
58
+ requirement: !ruby/object:Gem::Requirement
52
59
  requirements:
53
- - - ! '>='
60
+ - - '>='
54
61
  - !ruby/object:Gem::Version
55
62
  version: '0.7'
56
63
  type: :runtime
57
64
  prerelease: false
58
- version_requirements: *70502870
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0.7'
59
70
  - !ruby/object:Gem::Dependency
60
71
  name: flacinfo-rb
61
- requirement: &70501980 !ruby/object:Gem::Requirement
62
- none: false
72
+ requirement: !ruby/object:Gem::Requirement
63
73
  requirements:
64
- - - ! '>='
74
+ - - '>='
65
75
  - !ruby/object:Gem::Version
66
76
  version: '0.4'
67
77
  type: :runtime
68
78
  prerelease: false
69
- version_requirements: *70501980
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - '>='
82
+ - !ruby/object:Gem::Version
83
+ version: '0.4'
70
84
  - !ruby/object:Gem::Dependency
71
85
  name: apetag
72
- requirement: &70501120 !ruby/object:Gem::Requirement
73
- none: false
86
+ requirement: !ruby/object:Gem::Requirement
74
87
  requirements:
75
- - - ! '>='
88
+ - - '>='
76
89
  - !ruby/object:Gem::Version
77
90
  version: 1.1.4
78
91
  type: :runtime
79
92
  prerelease: false
80
- version_requirements: *70501120
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - '>='
96
+ - !ruby/object:Gem::Version
97
+ version: 1.1.4
81
98
  - !ruby/object:Gem::Dependency
82
99
  name: rdoc
83
- requirement: &70500210 !ruby/object:Gem::Requirement
84
- none: false
100
+ requirement: !ruby/object:Gem::Requirement
85
101
  requirements:
86
102
  - - ~>
87
103
  - !ruby/object:Gem::Version
88
104
  version: '3.10'
89
105
  type: :development
90
106
  prerelease: false
91
- version_requirements: *70500210
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ~>
110
+ - !ruby/object:Gem::Version
111
+ version: '3.10'
92
112
  - !ruby/object:Gem::Dependency
93
113
  name: hoe
94
- requirement: &70499640 !ruby/object:Gem::Requirement
95
- none: false
114
+ requirement: !ruby/object:Gem::Requirement
96
115
  requirements:
97
116
  - - ~>
98
117
  - !ruby/object:Gem::Version
99
- version: '3.0'
118
+ version: '3.5'
100
119
  type: :development
101
120
  prerelease: false
102
- version_requirements: *70499640
103
- description: ! 'ruby-audioinfo glue together various audio ruby libraries and presents
104
- a unified
105
-
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ~>
124
+ - !ruby/object:Gem::Version
125
+ version: '3.5'
126
+ description: |-
127
+ ruby-audioinfo glue together various audio ruby libraries and presents a unified
106
128
  API to the developper. Currently, supported formats are: mp3, ogg, mpc, ape,
107
-
108
- wma, flac, aac, mp4, m4a.'
129
+ wma, flac, aac, mp4, m4a.
109
130
  email:
110
131
  - guillaume.pierronnet@gmail.com
111
132
  - unknown
@@ -123,10 +144,15 @@ files:
123
144
  - lib/audioinfo.rb
124
145
  - lib/audioinfo/album.rb
125
146
  - lib/audioinfo/mpcinfo.rb
147
+ - lib/audioinfo/case_insensitive_hash.rb
126
148
  - test/mpcinfo.rb
149
+ - test/test_helper.rb
150
+ - test/test_case_insensitive_hash.rb
151
+ - test/test_audioinfo.rb
127
152
  - .gemtest
128
153
  homepage: http://ruby-audioinfo.rubyforge.org
129
154
  licenses: []
155
+ metadata: {}
130
156
  post_install_message:
131
157
  rdoc_options:
132
158
  - --main
@@ -134,22 +160,23 @@ rdoc_options:
134
160
  require_paths:
135
161
  - lib
136
162
  required_ruby_version: !ruby/object:Gem::Requirement
137
- none: false
138
163
  requirements:
139
- - - ! '>='
164
+ - - '>='
140
165
  - !ruby/object:Gem::Version
141
166
  version: '0'
142
167
  required_rubygems_version: !ruby/object:Gem::Requirement
143
- none: false
144
168
  requirements:
145
- - - ! '>='
169
+ - - '>='
146
170
  - !ruby/object:Gem::Version
147
171
  version: '0'
148
172
  requirements: []
149
173
  rubyforge_project: ruby-audioinfo
150
- rubygems_version: 1.8.17
174
+ rubygems_version: 2.0.0
151
175
  signing_key:
152
- specification_version: 3
176
+ specification_version: 4
153
177
  summary: ruby-audioinfo glue together various audio ruby libraries and presents a
154
178
  unified API to the developper
155
- test_files: []
179
+ test_files:
180
+ - test/test_helper.rb
181
+ - test/test_case_insensitive_hash.rb
182
+ - test/test_audioinfo.rb