ruby-mp3info 0.5.1 → 0.6

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.
@@ -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