ruby-mp3info 0.5.1 → 0.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,4 @@
1
- # $Id: mp3info.rb 51 2007-09-10 11:53:52Z moumar $
1
+ # $Id: mp3info.rb 64 2008-02-24 02:11:57Z moumar $
2
2
  # License:: Ruby
3
3
  # Author:: Guillaume Pierronnet (mailto:moumar_AT__rubyforge_DOT_org)
4
4
  # Website:: http://ruby-mp3info.rubyforge.org/
@@ -18,7 +18,7 @@ end
18
18
 
19
19
  class Mp3Info
20
20
 
21
- VERSION = "0.5.1"
21
+ VERSION = "0.6"
22
22
 
23
23
  LAYER = [ nil, 3, 2, 1]
24
24
  BITRATE = [
@@ -161,14 +161,16 @@ class Mp3Info
161
161
  end
162
162
  end
163
163
 
164
- # Instantiate a new Mp3Info object with name +filename+
165
- def initialize(filename)
164
+ # Instantiate Mp3Info object with name +filename+ and an
165
+ # options hash for ID3v2#new underlying object
166
+ def initialize(filename, id3v2_options = {})
166
167
  $stderr.puts("#{self.class}::new() does not take block; use #{self.class}::open() instead") if block_given?
167
168
  @filename = filename
169
+ @id3v2_options = id3v2_options
168
170
  reload
169
171
  end
170
172
 
171
- # reload the file from disk
173
+ # reload (or load for the first time) the file from disk
172
174
  def reload
173
175
  raise(Mp3InfoError, "empty file") unless File.size?(@filename)
174
176
  @hastag1 = false
@@ -176,7 +178,7 @@ class Mp3Info
176
178
  @tag1 = {}
177
179
  @tag1.extend(HashKeys)
178
180
 
179
- @tag2 = ID3v2.new
181
+ @tag2 = ID3v2.new(@id3v2_options)
180
182
 
181
183
  @file = File.new(filename, "rb")
182
184
  @file.extend(Mp3FileMethods)
@@ -283,8 +285,8 @@ class Mp3Info
283
285
  end
284
286
 
285
287
  # "block version" of Mp3Info::new()
286
- def self.open(filename)
287
- m = self.new(filename)
288
+ def self.open(*params)
289
+ m = self.new(*params)
288
290
  ret = nil
289
291
  if block_given?
290
292
  begin
@@ -307,6 +309,7 @@ class Mp3Info
307
309
  self
308
310
  end
309
311
 
312
+ # Remove id3v2 from mp3
310
313
  def removetag2
311
314
  @tag2.clear
312
315
  self
@@ -397,7 +400,6 @@ class Mp3Info
397
400
  tempfile_name = @filename + ".tmp"
398
401
  File.open(tempfile_name, "wb") do |tempfile|
399
402
  unless @tag2.empty?
400
- tempfile.write("ID3")
401
403
  tempfile.write(@tag2.to_bin)
402
404
  end
403
405
 
@@ -1,8 +1,11 @@
1
1
  require "mp3info/extension_modules"
2
+ require "iconv"
2
3
 
3
4
  # This class is not intended to be used directly
4
5
  class ID3v2 < DelegateClass(Hash)
5
- VERSION_MAJ = 3
6
+
7
+ # Major version used when writing tags
8
+ WRITE_VERSION = 3
6
9
 
7
10
  TAGS = {
8
11
  "AENC" => "Audio encryption",
@@ -81,22 +84,33 @@ class ID3v2 < DelegateClass(Hash)
81
84
  "WXXX" => "User defined URL link frame"
82
85
  }
83
86
 
87
+ # See id3v2.4.0-structure document, at section 4.
88
+ TEXT_ENCODINGS = ["iso-8859-1", "utf-16", "utf-16be", "utf-8"]
89
+
84
90
  include Mp3Info::HashKeys
85
91
 
86
92
  attr_reader :io_position
87
93
 
88
- # possibles keys:
89
- # :+lang+ for writing comments
90
- # :+encoding+: :+iso+ or :+unicode+
94
+ # :+lang+: for writing comments
95
+ #
96
+ # :+encoding+: one of the string of +TEXT_ENCODINGS+,
97
+ # used as a source and destination encoding respectively
98
+ # for read and write tag2 values.
91
99
  attr_reader :options
92
100
 
93
101
  def initialize(options = {})
94
102
  @options = {
95
- :lang => "ENG",
96
- :encoding => :iso #language encoding bit 0 for iso_8859_1, 1 for unicode
103
+ :lang => "ENG",
104
+ :encoding => "iso-8859-1"
97
105
  }
106
+
98
107
  @options.update(options)
108
+ @text_encoding_index = TEXT_ENCODINGS.index(@options[:encoding])
99
109
 
110
+ unless @text_encoding_index
111
+ raise(ArgumentError, "bad id3v2 text encoding specified")
112
+ end
113
+
100
114
  @hash = {}
101
115
  #TAGS.keys.each { |k| @hash[k] = nil }
102
116
  @hash_orig = {}
@@ -105,19 +119,22 @@ class ID3v2 < DelegateClass(Hash)
105
119
  @version_maj = @version_min = nil
106
120
  end
107
121
 
122
+ # does this tag correctly read ?
108
123
  def valid?
109
124
  @valid
110
125
  end
111
126
 
127
+ # does this tag has been changed ?
112
128
  def changed?
113
129
  @hash_orig != @hash
114
130
  end
115
131
 
132
+ # full version of this tag (like 2.3.0)
116
133
  def version
117
134
  "2.#{@version_maj}.#{@version_min}"
118
135
  end
119
136
 
120
- ### gets id3v2 tag information from io
137
+ ### gets id3v2 tag information from io object (must support #seek() method)
121
138
  def from_io(io)
122
139
  @io = io
123
140
  version_maj, version_min, flags = @io.read(3).unpack("CCB4")
@@ -142,6 +159,7 @@ class ID3v2 < DelegateClass(Hash)
142
159
  # we should now have io sitting at the first MPEG frame
143
160
  end
144
161
 
162
+ # dump tag for writing. Version is always 2.#{WRITE_VERSION}.0.
145
163
  def to_bin
146
164
  #TODO handle of @tag2[TLEN"]
147
165
  #TODO add of crc
@@ -151,7 +169,7 @@ class ID3v2 < DelegateClass(Hash)
151
169
  @hash.each do |k, v|
152
170
  next unless v
153
171
  next if v.respond_to?("empty?") and v.empty?
154
- data = encode_tag(k, v.to_s)
172
+ data = encode_tag(k, v.to_s, WRITE_VERSION)
155
173
  #data << "\x00"*2 #End of tag
156
174
 
157
175
  tag << k[0,4] #4 characte max for a tag's key
@@ -161,9 +179,9 @@ class ID3v2 < DelegateClass(Hash)
161
179
  tag << data
162
180
  end
163
181
 
164
- tag_str = ""
182
+ tag_str = "ID3"
165
183
  #version_maj, version_min, unsync, ext_header, experimental, footer
166
- tag_str << [ VERSION_MAJ, 0, "0000" ].pack("CCB4")
184
+ tag_str << [ WRITE_VERSION, 0, "0000" ].pack("CCB4")
167
185
  tag_str << to_syncsafe(tag.size)
168
186
  tag_str << tag
169
187
  p tag_str if $DEBUG
@@ -172,21 +190,26 @@ class ID3v2 < DelegateClass(Hash)
172
190
 
173
191
  private
174
192
 
193
+ def encode_tag(name, value, version)
194
+ puts "encode_tag(#{name.inspect}, #{value.inspect}, #{version})" if $DEBUG
195
+
196
+ text_encoding_index = @text_encoding_index
197
+ if version == 3
198
+ # in id3v2.3 tags, there is only 2 encodings possible
199
+ transcoded_value = value
200
+ if text_encoding_index >= 2
201
+ transcoded_value = Iconv.iconv(TEXT_ENCODINGS[1], TEXT_ENCODINGS[text_encoding_index], value)[0]
202
+ text_encoding_index = 1
203
+ end
204
+ end
175
205
 
176
- def encode_tag(name, value)
177
- puts "encode_tag(#{name.inspect}, #{value.inspect})" if $DEBUG
178
206
  case name
179
207
  when "COMM"
180
- [ @options[:encoding] == :iso ? 0 : 1, @options[:lang], 0, value ].pack("ca3ca*")
181
- when /^W/ # URL link frames
208
+ [ text_encoding_index , @options[:lang], 0, transcoded_value ].pack("ca3ca*")
209
+ when "UFID", /^W/ # URL link frames
182
210
  value
183
211
  else
184
- if @options[:encoding] == :iso
185
- "\x00"+value
186
- else
187
- "\x01"+value #Iconv.iconv("UNICODE", "ISO-8859-1", value)[0]
188
- end
189
- #data << "\x00"
212
+ text_encoding_index.chr + transcoded_value
190
213
  end
191
214
  end
192
215
 
@@ -197,22 +220,18 @@ class ID3v2 < DelegateClass(Hash)
197
220
  #FIXME improve this
198
221
  encoding, lang, str = value.unpack("ca3a*")
199
222
  out = value.split(0.chr).last
200
- when /^W/ # URL link frames
223
+ when "UFID", /^W/ # URL link frames
201
224
  out = value
202
225
  else
203
- encoding = value[0] # language encoding bit 0 for iso_8859_1, 1 for unicode
226
+ encoding = value[0] # language encoding (see TEXT_ENCODINGS constant)
204
227
  out = value[1..-1]
205
228
  end
206
-
207
- if encoding == 1 #and name[0] == ?T
208
- require "iconv"
209
-
210
- #strip byte-order bytes at the beginning of the unicode string if they exists
211
- out[0..3] =~ /^[\xff\xfe]+$/ and out = out[2..-1]
212
- begin
213
- out = Iconv.iconv("ISO-8859-1", "UTF-16", out)[0]
214
- rescue Iconv::IllegalSequence, Iconv::InvalidCharacter
215
- end
229
+
230
+ printf("encoding: %d, value: %s\n", encoding, value.inspect) if $DEBUG
231
+ # we need to convert the string in order to match
232
+ # the requested encoding
233
+ if encoding && encoding != @text_encoding_index
234
+ out = Iconv.iconv(@options[:encoding], TEXT_ENCODINGS[encoding], out)[0]
216
235
  end
217
236
  out
218
237
  end
@@ -21,10 +21,6 @@ class Mp3InfoTest < Test::Unit::TestCase
21
21
  "TRCK" => "tracknum"
22
22
  }
23
23
 
24
- # aliasing to allow testing with old versions of Test::Unit
25
- alias set_up setup
26
- alias tear_down teardown
27
-
28
24
  def setup
29
25
  # Command to create a dummy MP3
30
26
  # dd if=/dev/zero bs=1024 count=15 | lame --preset cbr 128 -r -s 44.1 --bitwidth 16 - - | ruby -rbase64 -e 'print Base64.encode64($stdin.read)'
@@ -94,7 +90,6 @@ EOF
94
90
  FileUtils.rm_f(TEMP_FILE)
95
91
  end
96
92
 
97
-
98
93
  def test_to_s
99
94
  Mp3Info.open(TEMP_FILE) { |info| assert(info.to_s.is_a?(String)) }
100
95
  end
@@ -104,15 +99,15 @@ EOF
104
99
  str = "0"*1024*1024
105
100
  f.write(str)
106
101
  end
107
- assert_raises(Mp3InfoError) {
102
+ assert_raises(Mp3InfoError) do
108
103
  mp3 = Mp3Info.new(TEMP_FILE)
109
- }
104
+ end
110
105
  end
111
106
 
112
107
  def test_is_an_mp3
113
- assert_nothing_raised {
108
+ assert_nothing_raised do
114
109
  Mp3Info.new(TEMP_FILE).close
115
- }
110
+ end
116
111
  end
117
112
 
118
113
  def test_detected_info
@@ -129,7 +124,7 @@ EOF
129
124
  end
130
125
 
131
126
  def test_vbr_mp3_length
132
- temp_file_vbr = File.join(File.dirname($0), "vbr.mp3")
127
+ temp_file_vbr = File.join(File.dirname($0), "test_vbr_length.mp3")
133
128
 
134
129
  unless File.exists?(temp_file_vbr)
135
130
  tempfile = Tempfile.new("ruby-mp3info_test")
@@ -235,7 +230,7 @@ EOF
235
230
 
236
231
  def test_id3v2_version
237
232
  written_tag = write_temp_file(BASIC_TAG2)
238
- assert_equal( "2.3.0", written_tag.version )
233
+ assert_equal( "2.#{ID3v2::WRITE_VERSION}.0", written_tag.version )
239
234
  end
240
235
 
241
236
  def test_id3v2_methods
@@ -254,6 +249,30 @@ EOF
254
249
  id3v2_prog_test(BASIC_TAG2, w)
255
250
  end
256
251
 
252
+ #test the tag with the "id3v2" program
253
+ def id3v2_prog_test(tag, written_tag)
254
+ return if RUBY_PLATFORM =~ /win32/
255
+ return if `which id3v2`.empty?
256
+ start = false
257
+ id3v2_output = {}
258
+ `id3v2 -l #{TEMP_FILE}`.each do |line|
259
+ if line =~ /^id3v2 tag info/
260
+ start = true
261
+ next
262
+ end
263
+ next unless start
264
+ k, v = /^(.{4}) \(.+\): (.+)$/.match(line)[1,2]
265
+ case k
266
+ #COMM (Comments): ()[spa]: fmg
267
+ when "COMM"
268
+ v.sub!(/\(\)\[.{3}\]: (.+)/, '\1')
269
+ end
270
+ id3v2_output[k] = v
271
+ end
272
+
273
+ assert_equal( id3v2_output, written_tag, "id3v2 program output doesn't match")
274
+ end
275
+
257
276
  def test_id3v2_trash
258
277
  end
259
278
 
@@ -319,7 +338,7 @@ EOF
319
338
  # end
320
339
 
321
340
  def test_good_parsing_of_a_pathname
322
- fn = "Freak On `(Stones Club Mix).mp3"
341
+ fn = "Freak On `(Stone´s Club Mix).mp3"
323
342
  File.rename(TEMP_FILE, fn)
324
343
  begin
325
344
  mp3 = Mp3Info.new(fn)
@@ -333,28 +352,38 @@ EOF
333
352
  end
334
353
  end
335
354
 
336
- #test the tag with the "id3v2" program
337
- def id3v2_prog_test(tag, written_tag)
338
- return if PLATFORM =~ /win32/
339
- return if `which id3v2`.empty?
340
- start = false
341
- id3v2_output = {}
342
- `id3v2 -l #{TEMP_FILE}`.each do |line|
343
- if line =~ /^id3v2 tag info/
344
- start = true
345
- next
346
- end
347
- next unless start
348
- k, v = /^(.{4}) \(.+\): (.+)$/.match(line)[1,2]
349
- case k
350
- #COMM (Comments): ()[spa]: fmg
351
- when "COMM"
352
- v.sub!(/\(\)\[.{3}\]: (.+)/, '\1')
353
- end
354
- id3v2_output[k] = v
355
+ def test_validity_of_id3v2_options
356
+ info = Mp3Info.new(TEMP_FILE)
357
+ expected_hash = { :lang => "ENG", :encoding => "iso-8859-1" }
358
+ assert_equal( expected_hash, info.tag2.options)
359
+
360
+ assert_raises(ArgumentError) do
361
+ Mp3Info.new(TEMP_FILE, :encoding => "bad encoding")
355
362
  end
363
+ end
356
364
 
357
- assert_equal( id3v2_output, written_tag, "id3v2 program output doesn't match")
365
+ def test_encoding_read
366
+ Mp3Info.open(TEMP_FILE) do |mp3|
367
+ mp3.tag2['TEST'] = "all\xe9"
368
+ end
369
+
370
+ Mp3Info.open(TEMP_FILE, :encoding => "utf-8") do |mp3|
371
+ assert_equal "all\xc3\xa9", mp3.tag2['TEST']
372
+ end
373
+
374
+ Mp3Info.open(TEMP_FILE, :encoding => "iso-8859-1") do |mp3|
375
+ assert_equal "all\xe9", mp3.tag2['TEST']
376
+ end
377
+ end
378
+
379
+ def test_encoding_write
380
+ Mp3Info.open(TEMP_FILE, :encoding => 'utf-8') do |mp3|
381
+ mp3.tag2['TEST'] = "all\xc3\xa9"
382
+ end
383
+
384
+ Mp3Info.open(TEMP_FILE, :encoding => "iso-8859-1") do |mp3|
385
+ assert_equal "all\xe9", mp3.tag2['TEST']
386
+ end
358
387
  end
359
388
 
360
389
  def write_temp_file(tag)
metadata CHANGED
@@ -1,54 +1,72 @@
1
1
  --- !ruby/object:Gem::Specification
2
- rubygems_version: 0.9.2
3
- specification_version: 1
4
2
  name: ruby-mp3info
5
3
  version: !ruby/object:Gem::Version
6
- version: 0.5.1
7
- date: 2007-09-10 00:00:00 +02:00
8
- summary: ruby-mp3info is a pure-ruby library to retrieve low level informations on mp3 files and manipulate id3v1 and id3v2 tags
9
- require_paths:
10
- - lib
11
- email: moumar@rubyforge.org
12
- homepage: http://ruby-mp3info.rubyforge.org
13
- rubyforge_project: ruby-mp3info
14
- description:
15
- autorequire: mp3info
16
- default_executable:
17
- bindir: bin
18
- has_rdoc: true
19
- required_ruby_version: !ruby/object:Gem::Version::Requirement
20
- requirements:
21
- - - ">"
22
- - !ruby/object:Gem::Version
23
- version: 0.0.0
24
- version:
4
+ version: "0.6"
25
5
  platform: ruby
26
- signing_key:
27
- cert_chain:
28
- post_install_message:
29
6
  authors:
30
7
  - Guillaume Pierronnet
31
- files:
32
- - README
33
- - CHANGELOG
34
- - EXAMPLES
35
- - test.rb
36
- - lib/mp3info.rb
37
- - lib/mp3info/extension_modules.rb
38
- - lib/mp3info/id3v2.rb
39
- test_files: []
40
-
41
- rdoc_options: []
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
42
11
 
43
- extra_rdoc_files:
44
- - README
45
- - CHANGELOG
46
- - EXAMPLES
12
+ date: 2008-02-24 00:00:00 +01:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: hoe
17
+ version_requirement:
18
+ version_requirements: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 1.5.0
23
+ version:
24
+ description: "* written in pure ruby * read low-level informations like bitrate, length, samplerate, etc... * read, write, remove id3v1 and id3v2 tags * only 2.3 version is supported for writings id3v2 tags == SYNOPSIS: a good exercise is to read the test.rb to understand how the library works deeper require \"mp3info\" # read and display infos & tags Mp3Info.open(\"myfile.mp3\") do |mp3info| puts mp3info end # read/write tag1 and tag2 with Mp3Info#tag attribute # when reading tag2 have priority over tag1 # when writing, each tag is written. Mp3Info.open(\"myfile.mp3\") do |mp3| puts mp3.tag.title puts mp3.tag.artist puts mp3.tag.album puts mp3.tag.tracknum mp3.tag.title = \"track title\" mp3.tag.artist = \"artist name\" end Mp3Info.open(\"myfile.mp3\") do |mp3| # you can access four letter v2 tags like this puts mp3.tag2.TIT2 mp3.tag2.TIT2 = \"new TIT2\" # or like that mp3.tag2[\"TIT2\"] # at this time, only COMM tag is processed after reading and before writing # according to ID3v2#options hash mp3.tag2.options[:lang] = \"FRE\" mp3.tag2.COMM = \"my comment in french, correctly handled when reading and writing\" end"
25
+ email: moumar@rubyforge.org
47
26
  executables: []
48
27
 
49
28
  extensions: []
50
29
 
30
+ extra_rdoc_files:
31
+ - History.txt
32
+ - Manifest.txt
33
+ - README.txt
34
+ files:
35
+ - History.txt
36
+ - Manifest.txt
37
+ - README.txt
38
+ - Rakefile
39
+ - install.rb
40
+ - lib/mp3info.rb
41
+ - lib/mp3info/extension_modules.rb
42
+ - lib/mp3info/id3v2.rb
43
+ - test/test_ruby-mp3info.rb
44
+ has_rdoc: true
45
+ homepage:
46
+ post_install_message:
47
+ rdoc_options:
48
+ - --main
49
+ - README.txt
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: "0"
57
+ version:
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: "0"
63
+ version:
51
64
  requirements: []
52
65
 
53
- dependencies: []
54
-
66
+ rubyforge_project: ruby-mp3info
67
+ rubygems_version: 1.0.1
68
+ signing_key:
69
+ specification_version: 2
70
+ summary: ruby-mp3info is a pure-ruby library to retrieve low level informations on mp3 files and manipulate id3v1 and id3v2 tags
71
+ test_files:
72
+ - test/test_ruby-mp3info.rb