ruby-mp3info 0.8.9 → 0.8.10

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ce54c521b67a367467f344b231debdbb1e36b543
4
- data.tar.gz: 160261476cab97ea32fddd4870ff67a92ffd6ac5
3
+ metadata.gz: ec6e84b6d95639a26f8ff0baf64625889bfd6b3a
4
+ data.tar.gz: 9de4508c025b0c865f0eb4b29fe9c70b75495ed2
5
5
  SHA512:
6
- metadata.gz: 0609877cdd0fba3d09a74bc613338da699eb67dfe7bedf80f872ff09bdc23689bcb3e86c97632133f4c60bbad1c4ba8ac6e188799670e73772f818803627c9a4
7
- data.tar.gz: 08999c5701caab8ba9a2a6e0d0ac0961e076d1acea747114878f1d84e2238631cf36cf472f5b2a4ad3bc880c6efa0eef2f0b558d544afb20bdfaf8c94c7e6076
6
+ metadata.gz: f619a8973e5dee36135546d5be127c9a9d105e255cd94323ce073ec58f03aa4e668dcc156398984c5ab4a9a756dd8ea9006cadab62e2824a3741c6a6f20a6d77
7
+ data.tar.gz: 171267103c889d7a3136e5f727a74db06acc664d5d7a5118e9cfb011abe0daa2165d06d1b71f2d4edd77ff6b8f4669d306c94a256ade3c6f7b4093e1eb5e9629
@@ -1,3 +1,7 @@
1
+ === 0.8.10 / 2016-10-31
2
+
3
+ * bugfix on /^(T|COM|USLT)/ tags parsing
4
+
1
5
  === 0.8.9 / 2016-02-03
2
6
 
3
7
  * bugfix on JPEG picture parsing + testsuite
@@ -18,7 +18,7 @@ end
18
18
 
19
19
  class Mp3Info
20
20
 
21
- VERSION = "0.8.9"
21
+ VERSION = "0.8.10"
22
22
 
23
23
  LAYER = [ nil, 3, 2, 1]
24
24
  BITRATE = {
@@ -17,19 +17,7 @@ class Mp3Info
17
17
  end
18
18
  end
19
19
 
20
- class ::String
21
- if RUBY_VERSION < "1.9.0"
22
- alias getbyte []
23
- end
24
- end
25
-
26
20
  module Mp3FileMethods #:nodoc:
27
- if RUBY_VERSION < "1.9.0"
28
- def getbyte
29
- getc
30
- end
31
- end
32
-
33
21
  def get32bits
34
22
  (getbyte << 24) + (getbyte << 16) + (getbyte << 8) + getbyte
35
23
  end
@@ -41,26 +29,15 @@ class Mp3Info
41
29
 
42
30
  class EncodingHelper #:nodoc:
43
31
  def self.convert_to(value, from, to)
44
- if RUBY_1_8
45
- if to == "iso-8859-1"
46
- to = to + "//TRANSLIT"
47
- end
48
- ruby_18_encode(from, to, value)
32
+ if to == "utf-16"
33
+ ("\uFEFF" + value).encode("UTF-16LE") # Chab 01.apr.2012 : moved from big to little endian for more compatibility (Windows Media Player, older Quicktime..)
49
34
  else
50
- if to == "utf-16"
51
- ("\uFEFF" + value).encode("UTF-16LE") # Chab 01.apr.2012 : moved from big to little endian for more compatibility (Windows Media Player, older Quicktime..)
52
- else
53
- value.encode(to)
54
- end
35
+ value.encode(to)
55
36
  end
56
37
  end
57
38
 
58
39
  def self.convert_from_iso_8859_1(value)
59
- if RUBY_1_8
60
- ruby_18_encode("utf-8", "iso-8859-1", value)
61
- else
62
- value.force_encoding("iso-8859-1").encode("utf-8")
63
- end
40
+ value.force_encoding("iso-8859-1").encode("utf-8")
64
41
  end
65
42
 
66
43
  def self.ruby_18_encode(from, to, value)
@@ -68,26 +45,22 @@ class Mp3Info
68
45
  end
69
46
 
70
47
  def self.decode_utf16(out)
71
- if RUBY_1_8
72
- convert_to(out, "UTF-8", "UTF-16")
48
+ # String#bytes is not an array in Ruby 1.9
49
+ bytes = out.bytes.to_a
50
+ if out.length >= 2 and bytes[0] == 0xff and bytes[1] == 0xfe
51
+ tag_encoding = "UTF-16LE"
52
+ first_valid = 1
53
+ elsif out.length >= 2 and bytes[0] == 0xfe and bytes[1] == 0xff
54
+ tag_encoding = "UTF-16BE"
55
+ first_valid = 1
73
56
  else
74
- # String#bytes is not an array in Ruby 1.9
75
- bytes = out.bytes.to_a
76
- if out.length >= 2 and bytes[0] == 0xff and bytes[1] == 0xfe
77
- tag_encoding = "UTF-16LE"
78
- first_valid = 1
79
- elsif out.length >= 2 and bytes[0] == 0xfe and bytes[1] == 0xff
80
- tag_encoding = "UTF-16BE"
81
- first_valid = 1
82
- else
83
- # ID3v2.3.0 section 3.3 mandates a BOM but some software
84
- # erroneously omits it so we have to guess. Since most of
85
- # the world is little endian we might as well go with that.
86
- tag_encoding = "UTF-16LE"
87
- first_valid = 0
88
- end
89
- out = out.dup.force_encoding(tag_encoding)[first_valid..-1]
57
+ # ID3v2.3.0 section 3.3 mandates a BOM but some software
58
+ # erroneously omits it so we have to guess. Since most of
59
+ # the world is little endian we might as well go with that.
60
+ tag_encoding = "UTF-16LE"
61
+ first_valid = 0
90
62
  end
63
+ out = out.dup.force_encoding(tag_encoding)[first_valid..-1]
91
64
  end
92
65
  end
93
66
  end
@@ -4,13 +4,6 @@
4
4
 
5
5
  require "delegate"
6
6
 
7
- if RUBY_VERSION[0..2] == "1.8"
8
- require "iconv"
9
- RUBY_1_8 = true
10
- else
11
- RUBY_1_8 = false
12
- end
13
-
14
7
  require "mp3info/extension_modules"
15
8
 
16
9
  class ID3v2Error < StandardError ; end
@@ -19,8 +12,8 @@ class ID3v2Error < StandardError ; end
19
12
  # It works like a hash, where key represents the tag name as 3 or 4 upper case letters
20
13
  # (respectively related to 2.2 and 2.3+ tag) and value represented as array or raw value.
21
14
  # Written version is always 2.3.
22
- class ID3v2 < DelegateClass(Hash)
23
-
15
+ class ID3v2 < DelegateClass(Hash)
16
+
24
17
  TAGS = {
25
18
  "AENC" => "Audio encryption",
26
19
  "APIC" => "Attached picture",
@@ -168,17 +161,17 @@ class ID3v2 < DelegateClass(Hash)
168
161
 
169
162
  # In Ruby 2.1.0 (and possibly others), DelegateClass breaks Kernel methods.
170
163
  include Kernel
171
-
164
+
172
165
  # this is the position in the file where the tag really ends
173
166
  attr_reader :io_position
174
167
 
175
168
  # :+lang+: for writing comments
176
169
  #
177
- # [DEPRECATION] :+encoding+: one of the string of +TEXT_ENCODINGS+,
170
+ # [DEPRECATION] :+encoding+: one of the string of +TEXT_ENCODINGS+,
178
171
  # use of :encoding parameter is DEPRECATED. In ruby 1.8, use utf-8 encoded strings for tags.
179
172
  # In ruby >= 1.9, strings are automatically transcoded from their originaloriginal encoding.
180
173
  attr_reader :options
181
-
174
+
182
175
  # possible options are described above ('options' attribute)
183
176
  # you can access this object like an hash, with [] and []= methods
184
177
  # special cases are ["disc_number"] and ["disc_total"] mirroring TPOS attribute
@@ -208,7 +201,7 @@ class ID3v2 < DelegateClass(Hash)
208
201
  def changed?
209
202
  @hash_orig != @hash
210
203
  end
211
-
204
+
212
205
  # full version of this tag (like "2.3.0") or nil
213
206
  # if tag was not correctly read
214
207
  def version
@@ -237,14 +230,14 @@ class ID3v2 < DelegateClass(Hash)
237
230
  end
238
231
 
239
232
  ### ID3V2::add_picture
240
- ### Takes an image string as input and writes it with header.
233
+ ### Takes an image string as input and writes it with header.
241
234
  ### Mime type is automatically guessed by default.
242
235
  ### It is possible but not necessary to include:
243
236
  ### :pic_type => 0 - 14 (see http://id3.org/id3v2.3.0#Attached_picture)
244
- ### :mime => 'gif'
237
+ ### :mime => 'gif'
245
238
  ### :description => "Image description"
246
239
  def add_picture(data, opts = {})
247
- options = {
240
+ options = {
248
241
  :pic_type => 0,
249
242
  :mime => nil,
250
243
  :description => "image"
@@ -282,7 +275,7 @@ class ID3v2 < DelegateClass(Hash)
282
275
  apic_images.each_index do |index|
283
276
  pic = apic_images[index]
284
277
  next if !pic.is_a?(String) or pic == ""
285
- pic.force_encoding 'BINARY'
278
+ pic.force_encoding 'BINARY'
286
279
  picture = []
287
280
  jpg_regexp = Regexp.new("jpg|JPG|jpeg|JPEG".force_encoding("BINARY"),
288
281
  Regexp::FIXEDENCODING )
@@ -290,7 +283,7 @@ class ID3v2 < DelegateClass(Hash)
290
283
  Regexp::FIXEDENCODING )
291
284
  header = pic.unpack('a120').first.force_encoding "BINARY"
292
285
  mime_pos = 0
293
-
286
+
294
287
  # safest way to correctly extract jpg and png is finding mime
295
288
  if header.match jpg_regexp and not header.match png_regexp
296
289
  mime = "jpg"
@@ -300,7 +293,7 @@ class ID3v2 < DelegateClass(Hash)
300
293
  start_with_anchor = Regexp.new("^\xFF\xD8".force_encoding("BINARY"),
301
294
  Regexp::FIXEDENCODING )
302
295
  end
303
-
296
+
304
297
  if header.match png_regexp and not header.match jpg_regexp
305
298
  mime = "png"
306
299
  mime_pos = header =~ png_regexp
@@ -309,7 +302,7 @@ class ID3v2 < DelegateClass(Hash)
309
302
  start_with_anchor = Regexp.new("^\x89PNG".force_encoding("BINARY"),
310
303
  Regexp::FIXEDENCODING )
311
304
  end
312
-
305
+
313
306
  puts "analysing image: #{header.inspect}..." if $DEBUG
314
307
  _, _, desc, data = pic[mime_pos, pic.length].unpack('Z*hZ*a*')
315
308
 
@@ -318,10 +311,10 @@ class ID3v2 < DelegateClass(Hash)
318
311
  real_start = pic =~ start
319
312
  data = pic[real_start, pic.length]
320
313
  end
321
-
314
+
322
315
  if mime == "jpg"
323
316
  # inspect jpg image header (first 10 chars) for "\xFF\x00" (expect "\xFF")
324
- trailing_null_byte = Regexp.new("(\377)(\000)".force_encoding('BINARY'),
317
+ trailing_null_byte = Regexp.new("(\377)(\000)".force_encoding('BINARY'),
325
318
  Regexp::FIXEDENCODING)
326
319
  md = data =~ trailing_null_byte
327
320
  if !md.nil? and md < 10
@@ -335,7 +328,7 @@ class ID3v2 < DelegateClass(Hash)
335
328
  end
336
329
 
337
330
  filename = ("%02i_#{desc[0,25]}" % (index + 1)).gsub('/','')
338
-
331
+
339
332
  picture[0] = filename
340
333
  picture[1] = data
341
334
  result << picture
@@ -346,7 +339,7 @@ class ID3v2 < DelegateClass(Hash)
346
339
  def inspect
347
340
  self.to_inspect_hash
348
341
  end
349
-
342
+
350
343
  def remove_pictures
351
344
  self["APIC"] = ""
352
345
  self["PIC"] = ""
@@ -362,7 +355,7 @@ class ID3v2 < DelegateClass(Hash)
362
355
  raise(ID3v2Error, "can't find version_maj ('#{version_maj}')") unless [2, 3, 4].include?(version_maj)
363
356
  @version_maj, @version_min = version_maj, version_min
364
357
  @tag_length = @io.get_syncsafe
365
-
358
+
366
359
  @parsed = true
367
360
  begin
368
361
  case @version_maj
@@ -409,9 +402,7 @@ class ID3v2 < DelegateClass(Hash)
409
402
  tag << k[0,4] #4 characte max for a tag's key
410
403
  #tag << to_syncsafe(data.size) #+1 because of the language encoding byte
411
404
  size = data.size
412
- unless RUBY_1_8
413
- size = data.dup.force_encoding("binary").size
414
- end
405
+ size = data.dup.force_encoding("binary").size
415
406
  tag << [size].pack("N") #+1 because of the language encoding byte
416
407
  tag << "\x00"*2 #flags
417
408
  tag << data
@@ -442,9 +433,8 @@ class ID3v2 < DelegateClass(Hash)
442
433
  s = [ 1, @options[:lang], "\xFE\xFF\x00\x00", transcoded_value].pack("ca3a*a*")
443
434
  return s
444
435
  when /^T/
445
- unless RUBY_1_8
446
- transcoded_value.force_encoding("BINARY")
447
- end
436
+ transcoded_value.force_encoding("BINARY")
437
+
448
438
  return "\x01" + transcoded_value
449
439
  else
450
440
  return value
@@ -455,55 +445,55 @@ class ID3v2 < DelegateClass(Hash)
455
445
  def decode_tag(name, raw_value)
456
446
  puts("decode_tag(#{name.inspect}, #{raw_value.inspect})") if $DEBUG
457
447
  if name =~ /^(T|COM|USLT)/
458
- if name =~ /^(COM|USLT)/
459
- #FIXME improve this
460
- encoding_index, lang, raw_tag = raw_value.unpack("ca3a*")
461
- if encoding_index == 1
462
- =begin
463
- comment = Mp3Info::EncodingHelper.decode_utf16(raw_tag)
464
- e = comment.encoding
465
- out = comment.force_encoding("BINARY").split("\x00\x00").last.force_encoding(e)
466
- p out
467
- =end
468
- comment = Mp3Info::EncodingHelper.decode_utf16(raw_tag)
469
- split_val = RUBY_1_8 ? "\x00\x00" : "\x00".encode(comment.encoding).force_encoding('ASCII-8BIT')
470
- out = raw_tag.split(split_val).last rescue ""
448
+ begin
449
+ if name =~ /^(COM|USLT)/
450
+ encoding_index, lang, raw_tag = raw_value.unpack("ca3a*")
451
+ if raw_tag == "\x00"
452
+ return nil
453
+ else
454
+ if encoding_index == 1
455
+ split_str = /\x00\x00\x00?/
456
+ else
457
+ split_str = "\x00"
458
+ end
459
+ head, tail = raw_tag.split(split_str)
460
+ if head == "\xFF\xFE".force_encoding('binary')
461
+ rgx = Regexp.new("^\xFF\xFE\x00\x00".force_encoding("BINARY"))
462
+ out = raw_tag.sub(rgx, '')
463
+ elsif tail
464
+ out = tail
465
+ elsif head
466
+ out = head
467
+ else
468
+ warn("warning: cannot decode tag #{name} with raw value #{raw_value.inspect}")
469
+ return nil
470
+ end
471
+ end
472
+ puts "COM tag found. encoding: #{encoding_index} lang: #{lang} str: #{out.inspect}" if $DEBUG
471
473
  else
472
- comment, out = raw_tag.split("\x00", 2)
474
+ encoding_index = raw_value.getbyte(0) # language encoding (see TEXT_ENCODINGS constant)
475
+ out = raw_value[1..-1]
473
476
  end
474
- puts "COM tag found. encoding: #{encoding_index} lang: #{lang} str: #{out.inspect}" if $DEBUG
475
- else
476
- encoding_index = raw_value.getbyte(0) # language encoding (see TEXT_ENCODINGS constant)
477
- out = raw_value[1..-1]
478
- end
479
- # we need to convert the string in order to match
480
- # the requested encoding
481
- if encoding_index && TEXT_ENCODINGS[encoding_index] && out
482
- if RUBY_1_8
483
- out = Mp3Info::EncodingHelper.convert_to(out, TEXT_ENCODINGS[encoding_index], "utf-8")
484
- else
477
+ # we need to convert the string in order to match
478
+ # the requested encoding
479
+ if TEXT_ENCODINGS[encoding_index]
485
480
  if encoding_index == 1
486
481
  out = Mp3Info::EncodingHelper.decode_utf16(out)
487
482
  else
488
483
  out.force_encoding(TEXT_ENCODINGS[encoding_index])
489
484
  end
490
- if out
491
- out.encode!("utf-8")
492
- end
485
+ out.encode!("utf-8")
493
486
  end
494
- end
495
487
 
496
- if out
497
488
  # remove padding zeros for textual tags
498
- if RUBY_1_8
499
- r = /\0*$/
500
- else
501
- r = Regexp.new("\x00*$".encode(out.encoding))
502
- end
503
- out.sub!(r, '')
504
- end
489
+ r = Regexp.new("\x00*$".encode(out.encoding))
490
+ out.sub!(r, '')
505
491
 
506
- return out
492
+ return out
493
+ rescue => e
494
+ warn "warning: cannot decode tag #{name} with raw value #{raw_value.inspect}: #{e}"
495
+ return nil
496
+ end
507
497
  else
508
498
  return raw_value
509
499
  end
@@ -518,7 +508,7 @@ class ID3v2 < DelegateClass(Hash)
518
508
  @io.seek(-4, IO::SEEK_CUR) # 1. find a padding zero,
519
509
  seek_to_v2_end
520
510
  break
521
- else
511
+ else
522
512
  if @version_maj == 4
523
513
  size = @io.get_syncsafe
524
514
  else
@@ -530,7 +520,7 @@ class ID3v2 < DelegateClass(Hash)
530
520
  end
531
521
  break if @io.pos >= @tag_length # 2. reach length from header
532
522
  end
533
- end
523
+ end
534
524
 
535
525
  ### reads id3 ver 2.2.x frames and adds the contents to @tag2 hash
536
526
  ### NOTE: the id3v2 header does not take padding zero's into consideration
@@ -547,8 +537,8 @@ class ID3v2 < DelegateClass(Hash)
547
537
  break if @io.pos >= @tag_length
548
538
  end
549
539
  end
550
- end
551
-
540
+ end
541
+
552
542
  ### Add data to tag2["name"]
553
543
  ### read lang_encoding, decode data if unicode and
554
544
  ### create an array if the key already exists in the tag
@@ -558,11 +548,11 @@ class ID3v2 < DelegateClass(Hash)
558
548
  if size > 50_000_000
559
549
  raise ID3v2Error, "tag size is > 50_000_000"
560
550
  end
561
-
551
+
562
552
  data_io = @io.read(size)
563
553
  data = decode_tag(name, data_io)
564
554
  if data && !data.empty?
565
- if self.keys.include?(name)
555
+ if self.keys.include?(name)
566
556
  if self[name].is_a?(Array)
567
557
  unless self[name].include?(data)
568
558
  self[name] << data
@@ -571,7 +561,7 @@ class ID3v2 < DelegateClass(Hash)
571
561
  self[name] = [ self[name], data ]
572
562
  end
573
563
  else
574
- self[name] = data
564
+ self[name] = data
575
565
  end
576
566
 
577
567
  if name == "TPOS" && data =~ /(\d+)\s*\/\s*(\d+)/
@@ -582,7 +572,7 @@ class ID3v2 < DelegateClass(Hash)
582
572
 
583
573
  puts "self[#{name.inspect}] = #{self[name].inspect}" if $DEBUG
584
574
  end
585
-
575
+
586
576
  ### runs thru @file one char at a time looking for best guess of first MPEG
587
577
  ### frame, which should be first 0xff byte after id3v2 padding zero's
588
578
  def seek_to_v2_end
@@ -591,12 +581,12 @@ class ID3v2 < DelegateClass(Hash)
591
581
  end
592
582
  @io.seek(-1, IO::SEEK_CUR)
593
583
  end
594
-
584
+
595
585
  ### convert an 32 integer to a syncsafe string
596
586
  def to_syncsafe(num)
597
587
  ( (num<<3) & 0x7f000000 ) + ( (num<<2) & 0x7f0000 ) + ( (num<<1) & 0x7f00 ) + ( num & 0x7f )
598
588
  end
599
-
589
+
600
590
  ### this is especially useful for printing out APIC data because
601
591
  ### only the header of the APIC tag is of interest
602
592
  def pretty_header(str, chars=128)
@@ -604,4 +594,3 @@ class ID3v2 < DelegateClass(Hash)
604
594
  end
605
595
 
606
596
  end
607
-
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-mp3info
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.9
4
+ version: 0.8.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Guillaume Pierronnet
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-02-02 00:00:00.000000000 Z
11
+ date: 2016-10-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rdoc
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '3.12'
33
+ version: '3.15'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '3.12'
40
+ version: '3.15'
41
41
  description: ruby-mp3info read low-level informations and manipulate tags on mp3 files.
42
42
  email:
43
43
  - guillaume.pierronnet@gmail.com
@@ -48,7 +48,6 @@ extra_rdoc_files:
48
48
  - Manifest.txt
49
49
  - README.md
50
50
  files:
51
- - ".gemtest"
52
51
  - History.txt
53
52
  - Manifest.txt
54
53
  - README.md
@@ -80,9 +79,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
80
79
  version: '0'
81
80
  requirements: []
82
81
  rubyforge_project:
83
- rubygems_version: 2.2.2
82
+ rubygems_version: 2.5.1
84
83
  signing_key:
85
84
  specification_version: 4
86
85
  summary: ruby-mp3info read low-level informations and manipulate tags on mp3 files.
87
- test_files:
88
- - test/test_ruby-mp3info.rb
86
+ test_files: []
data/.gemtest DELETED
File without changes