ruby-audioinfo 0.2.3 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
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