ruby-mp3info 0.6.14 → 0.6.15
Sign up to get free protection for your applications and to get access to all the features.
- data/.gemtest +0 -0
- data/History.txt +4 -0
- data/lib/mp3info.rb +123 -79
- data/test/test_ruby-mp3info.rb +75 -23
- metadata +13 -30
data/.gemtest
ADDED
File without changes
|
data/History.txt
CHANGED
data/lib/mp3info.rb
CHANGED
@@ -17,7 +17,7 @@ end
|
|
17
17
|
|
18
18
|
class Mp3Info
|
19
19
|
|
20
|
-
VERSION = "0.6.
|
20
|
+
VERSION = "0.6.15"
|
21
21
|
|
22
22
|
LAYER = [ nil, 3, 2, 1]
|
23
23
|
BITRATE = {
|
@@ -155,22 +155,45 @@ class Mp3Info
|
|
155
155
|
# "close" method.
|
156
156
|
attr_accessor(:tag2)
|
157
157
|
|
158
|
-
# the original filename
|
158
|
+
# the original filename unless used with a StringIO
|
159
159
|
attr_reader(:filename)
|
160
160
|
|
161
|
-
# Test the presence of an id3v1 tag in file +
|
162
|
-
def self.hastag1?(
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
161
|
+
# Test the presence of an id3v1 tag in file or StringIO +filename_or_io+
|
162
|
+
def self.hastag1?(filename_or_io)
|
163
|
+
if filename_or_io.is_a?(StringIO)
|
164
|
+
io = filename_or_io
|
165
|
+
io.rewind
|
166
|
+
else
|
167
|
+
io = File.new(filename_or_io, "rb")
|
168
|
+
end
|
169
|
+
|
170
|
+
hastag1 = false
|
171
|
+
begin
|
172
|
+
io.seek(-TAG1_SIZE, File::SEEK_END)
|
173
|
+
hastag1 = io.read(3) == "TAG"
|
174
|
+
ensure
|
175
|
+
io.close if io.is_a?(File)
|
176
|
+
end
|
177
|
+
hastag1
|
167
178
|
end
|
168
179
|
|
169
|
-
# Test the presence of an id3v2 tag in file +
|
170
|
-
def self.hastag2?(
|
171
|
-
|
172
|
-
|
173
|
-
|
180
|
+
# Test the presence of an id3v2 tag in file or StringIO +filename_or_io+
|
181
|
+
def self.hastag2?(filename_or_io)
|
182
|
+
if filename_or_io.is_a?(StringIO)
|
183
|
+
io = filename_or_io
|
184
|
+
io.rewind
|
185
|
+
else
|
186
|
+
io = File.new(filename_or_io,"rb")
|
187
|
+
end
|
188
|
+
|
189
|
+
hastag2 = false
|
190
|
+
|
191
|
+
begin
|
192
|
+
hastag2 = io.read(3) == "ID3"
|
193
|
+
ensure
|
194
|
+
io.close if io.is_a?(File)
|
195
|
+
end
|
196
|
+
hastag2
|
174
197
|
end
|
175
198
|
|
176
199
|
# Remove id3v1 tag from +filename+
|
@@ -193,24 +216,38 @@ class Mp3Info
|
|
193
216
|
# Specify :parse_tags => false to disable the processing
|
194
217
|
# of the tags (read and write).
|
195
218
|
# Specify :parse_mp3 => false to disable processing of the mp3
|
196
|
-
def initialize(
|
219
|
+
def initialize(filename_or_io, options = {})
|
197
220
|
warn("#{self.class}::new() does not take block; use #{self.class}::open() instead") if block_given?
|
198
|
-
@
|
221
|
+
@filename_or_io = filename_or_io
|
199
222
|
options = {:parse_mp3 => true, :parse_tags => true}.update(options)
|
200
223
|
@tag_parsing_enabled = options.delete(:parse_tags)
|
201
224
|
@mp3_parsing_enabled = options.delete(:parse_mp3)
|
202
225
|
@id3v2_options = options
|
203
226
|
reload
|
204
227
|
end
|
205
|
-
|
228
|
+
|
206
229
|
# reload (or load for the first time) the file from disk
|
207
230
|
def reload
|
208
|
-
raise(Mp3InfoError, "empty file") unless File.size?(@filename)
|
209
|
-
|
210
231
|
@header = {}
|
232
|
+
|
233
|
+
if @filename_or_io.is_a?(String)
|
234
|
+
@io_is_a_file = true
|
235
|
+
@io = File.new(@filename_or_io, "rb")
|
236
|
+
@io_size = @io.stat.size
|
237
|
+
@filename = @filename_or_io
|
238
|
+
elsif @filename_or_io.is_a?(StringIO)
|
239
|
+
@io_is_a_file = false
|
240
|
+
@io = @filename_or_io
|
241
|
+
@io_size = @io.size
|
242
|
+
@filename = nil
|
243
|
+
end
|
244
|
+
|
245
|
+
if @io_size == 0
|
246
|
+
raise(Mp3InfoError, "empty file or IO")
|
247
|
+
end
|
211
248
|
|
212
|
-
|
213
|
-
@
|
249
|
+
|
250
|
+
@io.extend(Mp3FileMethods)
|
214
251
|
@tag1 = @tag = @tag1_orig = @tag_orig = {}
|
215
252
|
@tag1.extend(HashKeys)
|
216
253
|
@tag2 = ID3v2.new(@id3v2_options)
|
@@ -253,7 +290,9 @@ class Mp3Info
|
|
253
290
|
end
|
254
291
|
|
255
292
|
ensure
|
256
|
-
@
|
293
|
+
if @io_is_a_file
|
294
|
+
@io.close
|
295
|
+
end
|
257
296
|
end
|
258
297
|
end
|
259
298
|
|
@@ -302,6 +341,7 @@ class Mp3Info
|
|
302
341
|
|
303
342
|
# write to another filename at close()
|
304
343
|
def rename(new_filename)
|
344
|
+
raise(Mp3InfoError, "cannot rename an IO") unless @io_is_a_file
|
305
345
|
@filename = new_filename
|
306
346
|
end
|
307
347
|
|
@@ -311,7 +351,7 @@ class Mp3Info
|
|
311
351
|
# [position_in_the_file, length_of_the_data]
|
312
352
|
def audio_content
|
313
353
|
pos = 0
|
314
|
-
length =
|
354
|
+
length = @io_size
|
315
355
|
if hastag1?
|
316
356
|
length -= TAG1_SIZE
|
317
357
|
end
|
@@ -328,8 +368,10 @@ class Mp3Info
|
|
328
368
|
end
|
329
369
|
|
330
370
|
# Flush pending modifications to tags and close the file
|
371
|
+
# not used when source IO is a StringIO
|
331
372
|
def close
|
332
373
|
puts "close" if $DEBUG
|
374
|
+
return unless @io_is_a_file
|
333
375
|
if !@tag_parsing_enabled
|
334
376
|
return
|
335
377
|
end
|
@@ -352,12 +394,12 @@ class Mp3Info
|
|
352
394
|
|
353
395
|
if @tag1 != @tag1_orig
|
354
396
|
puts "@tag1 has changed" if $DEBUG
|
355
|
-
raise(Mp3InfoError, "file is not writable") unless File.writable?(@
|
397
|
+
raise(Mp3InfoError, "file is not writable") unless File.writable?(@filename_or_io)
|
356
398
|
#@tag1_orig.update(@tag1)
|
357
399
|
@tag1_orig = @tag1.dup
|
358
|
-
File.open(@
|
400
|
+
File.open(@filename_or_io, 'rb+') do |file|
|
359
401
|
if @tag1_orig.empty?
|
360
|
-
newsize =
|
402
|
+
newsize = @io_size - TAG1_SIZE
|
361
403
|
file.truncate(newsize)
|
362
404
|
else
|
363
405
|
file.seek(-TAG1_SIZE, File::SEEK_END)
|
@@ -384,21 +426,21 @@ class Mp3Info
|
|
384
426
|
|
385
427
|
if @tag2.changed?
|
386
428
|
puts "@tag2 has changed" if $DEBUG
|
387
|
-
raise(Mp3InfoError, "file is not writable") unless File.writable?(@
|
429
|
+
raise(Mp3InfoError, "file is not writable") unless File.writable?(@filename_or_io)
|
388
430
|
tempfile_name = nil
|
389
|
-
File.open(@
|
431
|
+
File.open(@filename_or_io, 'rb+') do |file|
|
390
432
|
#if tag2 already exists, seek to end of it
|
391
433
|
if @tag2.parsed?
|
392
434
|
file.seek(@tag2.io_position)
|
393
435
|
end
|
394
|
-
# if @
|
395
|
-
# version_maj, version_min, flags = @
|
436
|
+
# if @io.read(3) == "ID3"
|
437
|
+
# version_maj, version_min, flags = @io.read(3).unpack("CCB4")
|
396
438
|
# unsync, ext_header, experimental, footer = (0..3).collect { |i| flags[i].chr == '1' }
|
397
|
-
# tag2_len = @
|
398
|
-
# @
|
399
|
-
# @
|
439
|
+
# tag2_len = @io.get_syncsafe
|
440
|
+
# @io.seek(@io.get_syncsafe - 4, IO::SEEK_CUR) if ext_header
|
441
|
+
# @io.seek(tag2_len, IO::SEEK_CUR)
|
400
442
|
# end
|
401
|
-
tempfile_name = @
|
443
|
+
tempfile_name = @filename_or_io + ".tmp"
|
402
444
|
File.open(tempfile_name, "wb") do |tempfile|
|
403
445
|
unless @tag2.empty?
|
404
446
|
tempfile.write(@tag2.to_bin)
|
@@ -410,13 +452,14 @@ class Mp3Info
|
|
410
452
|
end
|
411
453
|
end
|
412
454
|
end
|
413
|
-
File.rename(tempfile_name, @
|
455
|
+
File.rename(tempfile_name, @filename_or_io)
|
414
456
|
end
|
415
457
|
end
|
416
458
|
|
417
459
|
# close and reopen the file, i.e. commit changes to disk and
|
418
|
-
# reload it
|
460
|
+
# reload it (only works with "true" files, not StringIO ones)
|
419
461
|
def flush
|
462
|
+
return unless @io_is_a_file
|
420
463
|
close
|
421
464
|
reload
|
422
465
|
end
|
@@ -434,16 +477,14 @@ class Mp3Info
|
|
434
477
|
# counter, or whatever you like ;) +frame+ is a hash with the following keys:
|
435
478
|
# :layer, :bitrate, :samplerate, :mpeg_version, :padding and :size (in bytes)
|
436
479
|
def each_frame
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
break if file.eof?
|
446
|
-
end
|
480
|
+
@io.seek(@first_frame_pos, File::SEEK_SET)
|
481
|
+
loop do
|
482
|
+
head = @io.read(4).unpack("N").first
|
483
|
+
frame = Mp3Info.get_frames_infos(head)
|
484
|
+
@io.seek(frame[:size] -4, File::SEEK_CUR)
|
485
|
+
yield frame
|
486
|
+
#puts "frame #{frame_count} len #{frame[:length]} br #{frame[:bitrate]} @io.pos #{@io.pos}"
|
487
|
+
break if @io.eof?
|
447
488
|
end
|
448
489
|
end
|
449
490
|
|
@@ -481,49 +522,50 @@ private
|
|
481
522
|
:size => size }
|
482
523
|
end
|
483
524
|
|
484
|
-
### parses the id3 tags of the currently open @
|
525
|
+
### parses the id3 tags of the currently open @io
|
485
526
|
def parse_tags
|
486
|
-
return if @
|
527
|
+
return if @io_size < TAG1_SIZE # file is too small
|
528
|
+
|
487
529
|
@tag1_parsed = false
|
488
|
-
@
|
489
|
-
f3 = @
|
530
|
+
@io.seek(0)
|
531
|
+
f3 = @io.read(3)
|
490
532
|
# v1 tag at beginning
|
491
533
|
if f3 == "TAG"
|
492
534
|
gettag1
|
493
535
|
@tag1_parsed = true
|
494
536
|
end
|
495
537
|
|
496
|
-
@tag2.from_io(@
|
538
|
+
@tag2.from_io(@io) if f3 == "ID3" # v2 tag at beginning
|
497
539
|
|
498
540
|
unless @tag1_parsed # v1 tag at end
|
499
541
|
# this preserves the file pos if tag2 found, since gettag2 leaves
|
500
542
|
# the file at the best guess as to the first MPEG frame
|
501
543
|
pos = (@tag2.io_position || 0)
|
502
544
|
# seek to where id3v1 tag should be
|
503
|
-
@
|
504
|
-
if @
|
545
|
+
@io.seek(-TAG1_SIZE, IO::SEEK_END)
|
546
|
+
if @io.read(3) == "TAG"
|
505
547
|
gettag1
|
506
548
|
end
|
507
|
-
@
|
549
|
+
@io.seek(pos)
|
508
550
|
end
|
509
551
|
end
|
510
552
|
|
511
|
-
### gets id3v1 tag information from @
|
512
|
-
### assumes @
|
553
|
+
### gets id3v1 tag information from @io
|
554
|
+
### assumes @io is pointing to char after "TAG" id
|
513
555
|
def gettag1
|
514
556
|
@tag1_parsed = true
|
515
|
-
@tag1["title"] = @
|
516
|
-
@tag1["artist"] = @
|
517
|
-
@tag1["album"] = @
|
518
|
-
year_t = @
|
557
|
+
@tag1["title"] = @io.read(30).unpack("A*").first
|
558
|
+
@tag1["artist"] = @io.read(30).unpack("A*").first
|
559
|
+
@tag1["album"] = @io.read(30).unpack("A*").first
|
560
|
+
year_t = @io.read(4).to_i
|
519
561
|
@tag1["year"] = year_t unless year_t == 0
|
520
|
-
comments = @
|
562
|
+
comments = @io.read(30)
|
521
563
|
if comments.getbyte(-2) == 0
|
522
564
|
@tag1["tracknum"] = comments.getbyte(-1).to_i
|
523
565
|
comments.chop! #remove the last char
|
524
566
|
end
|
525
567
|
@tag1["comments"] = comments.unpack("A*").first
|
526
|
-
@tag1["genre"] = @
|
568
|
+
@tag1["genre"] = @io.getbyte
|
527
569
|
@tag1["genre_s"] = GENRES[@tag1["genre"]] || ""
|
528
570
|
|
529
571
|
# clear empty tags
|
@@ -532,29 +574,30 @@ private
|
|
532
574
|
@tag1.delete("tracknum") if @tag1["tracknum"] == 0
|
533
575
|
end
|
534
576
|
|
535
|
-
### reads through @
|
577
|
+
### reads through @io from current pos until it finds a valid MPEG header
|
536
578
|
### returns the MPEG header as FixNum
|
537
579
|
def find_next_frame
|
538
|
-
# @
|
580
|
+
# @io will now be sitting at the best guess for where the MPEG frame is.
|
539
581
|
# It should be at byte 0 when there's no id3v2 tag.
|
540
582
|
# It should be at the end of the id3v2 tag or the zero padding if there
|
541
583
|
# is a id3v2 tag.
|
542
|
-
#dummyproof = @
|
543
|
-
|
584
|
+
#dummyproof = @io.stat.size - @io.pos => WAS TOO MUCH
|
585
|
+
|
586
|
+
dummyproof = [ @io_size - @io.pos, 2000000 ].min
|
544
587
|
dummyproof.times do |i|
|
545
|
-
if @
|
546
|
-
data = @
|
547
|
-
raise(Mp3InfoError, "end of file reached") if @
|
588
|
+
if @io.getbyte == 0xff
|
589
|
+
data = @io.read(3)
|
590
|
+
raise(Mp3InfoError, "end of file reached") if @io.eof?
|
548
591
|
head = 0xff000000 + (data.getbyte(0) << 16) + (data.getbyte(1) << 8) + data.getbyte(2)
|
549
592
|
begin
|
550
593
|
Mp3Info.get_frames_infos(head)
|
551
594
|
return head
|
552
595
|
rescue Mp3InfoInternalError
|
553
|
-
@
|
596
|
+
@io.seek(-3, IO::SEEK_CUR)
|
554
597
|
end
|
555
598
|
end
|
556
599
|
end
|
557
|
-
if @
|
600
|
+
if @io.eof?
|
558
601
|
raise Mp3InfoError, "cannot find a valid frame: got EOF"
|
559
602
|
else
|
560
603
|
raise Mp3InfoError, "cannot find a valid frame after reading #{dummyproof} bytes"
|
@@ -583,7 +626,7 @@ private
|
|
583
626
|
head = nil
|
584
627
|
5.times do
|
585
628
|
head = find_next_frame()
|
586
|
-
@first_frame_pos = @
|
629
|
+
@first_frame_pos = @io.pos - 4
|
587
630
|
current_frame = Mp3Info.get_frames_infos(head)
|
588
631
|
@mpeg_version = current_frame[:mpeg_version]
|
589
632
|
@layer = current_frame[:layer]
|
@@ -608,20 +651,20 @@ private
|
|
608
651
|
(@channel_num == 3 ? 17 : 32) :
|
609
652
|
(@channel_num == 3 ? 9 : 17)
|
610
653
|
|
611
|
-
@
|
654
|
+
@io.seek(seek, IO::SEEK_CUR)
|
612
655
|
|
613
|
-
vbr_head = @
|
656
|
+
vbr_head = @io.read(4)
|
614
657
|
if vbr_head == "Xing"
|
615
658
|
puts "Xing header (VBR) detected" if $DEBUG
|
616
|
-
flags = @
|
659
|
+
flags = @io.get32bits
|
617
660
|
stream_size = frame_count = 0
|
618
|
-
flags[1] == 1 and frame_count = @
|
619
|
-
flags[2] == 1 and stream_size = @
|
661
|
+
flags[1] == 1 and frame_count = @io.get32bits
|
662
|
+
flags[2] == 1 and stream_size = @io.get32bits
|
620
663
|
puts "#{frame_count} frames" if $DEBUG
|
621
664
|
raise(Mp3InfoError, "bad VBR header") if frame_count.zero?
|
622
665
|
# currently this just skips the TOC entries if they're found
|
623
|
-
@
|
624
|
-
#@vbr_quality = @
|
666
|
+
@io.seek(100, IO::SEEK_CUR) if flags[0] == 1
|
667
|
+
#@vbr_quality = @io.get32bits if flags[3] == 1
|
625
668
|
|
626
669
|
samples_per_frame = SAMPLES_PER_FRAME[@layer][@mpeg_version]
|
627
670
|
@length = frame_count * samples_per_frame / Float(@samplerate)
|
@@ -630,7 +673,8 @@ private
|
|
630
673
|
@vbr = true
|
631
674
|
else
|
632
675
|
# for cbr, calculate duration with the given bitrate
|
633
|
-
|
676
|
+
|
677
|
+
stream_size = @io_size - (hastag1? ? TAG1_SIZE : 0) - (@tag2.io_position || 0)
|
634
678
|
@length = ((stream_size << 3)/1000.0)/@bitrate
|
635
679
|
# read the first 100 frames and decide if the mp3 is vbr and needs full scan
|
636
680
|
begin
|
data/test/test_ruby-mp3info.rb
CHANGED
@@ -10,6 +10,8 @@ require "tempfile"
|
|
10
10
|
require "zlib"
|
11
11
|
require "yaml"
|
12
12
|
|
13
|
+
system("which id3v2 > /dev/null") || raise("id3v2 not found")
|
14
|
+
|
13
15
|
class Mp3InfoTest < Test::Unit::TestCase
|
14
16
|
|
15
17
|
TEMP_FILE = File.join(File.dirname(__FILE__), "test_mp3info.mp3")
|
@@ -86,21 +88,8 @@ class Mp3InfoTest < Test::Unit::TestCase
|
|
86
88
|
end
|
87
89
|
|
88
90
|
def test_detected_info
|
89
|
-
Mp3Info.open(TEMP_FILE) do |
|
90
|
-
|
91
|
-
assert_equal(3, info.layer)
|
92
|
-
assert_equal(false, info.vbr)
|
93
|
-
assert_equal(128, info.bitrate)
|
94
|
-
assert_equal("JStereo", info.channel_mode)
|
95
|
-
assert_equal(44100, info.samplerate)
|
96
|
-
assert_equal(0.1305625, info.length)
|
97
|
-
assert_equal({:original => true,
|
98
|
-
:error_protection => false,
|
99
|
-
:padding => false,
|
100
|
-
:emphasis => 0,
|
101
|
-
:private => true,
|
102
|
-
:mode_extension => 2,
|
103
|
-
:copyright => false}, info.header)
|
91
|
+
Mp3Info.open(TEMP_FILE) do |mp3|
|
92
|
+
assert_mp3_info_are_ok(mp3)
|
104
93
|
end
|
105
94
|
end
|
106
95
|
|
@@ -167,13 +156,23 @@ class Mp3InfoTest < Test::Unit::TestCase
|
|
167
156
|
end
|
168
157
|
|
169
158
|
def test_removetag2
|
170
|
-
w =
|
159
|
+
w = write_tag2_to_temp_file({"TIT2" => "sdfqdsf"})
|
171
160
|
|
172
161
|
assert( Mp3Info.hastag2?(TEMP_FILE) )
|
173
162
|
Mp3Info.removetag2(TEMP_FILE)
|
174
163
|
assert( ! Mp3Info.hastag2?(TEMP_FILE) )
|
175
164
|
end
|
176
165
|
|
166
|
+
def test_hastags
|
167
|
+
Mp3Info.open(TEMP_FILE) do |info|
|
168
|
+
info.tag1 = @tag
|
169
|
+
end
|
170
|
+
assert(Mp3Info.hastag1?(TEMP_FILE))
|
171
|
+
|
172
|
+
written_tag = write_tag2_to_temp_file(DUMMY_TAG2)
|
173
|
+
assert(Mp3Info.hastag2?(TEMP_FILE))
|
174
|
+
end
|
175
|
+
|
177
176
|
def test_universal_tag
|
178
177
|
2.times do
|
179
178
|
tag = {"title" => "title"}
|
@@ -200,7 +199,7 @@ class Mp3InfoTest < Test::Unit::TestCase
|
|
200
199
|
end
|
201
200
|
|
202
201
|
def test_id3v2_version
|
203
|
-
written_tag =
|
202
|
+
written_tag = write_tag2_to_temp_file(DUMMY_TAG2)
|
204
203
|
assert_equal( "2.#{ID3v2::WRITE_VERSION}.0", written_tag.version )
|
205
204
|
end
|
206
205
|
|
@@ -215,7 +214,7 @@ class Mp3InfoTest < Test::Unit::TestCase
|
|
215
214
|
end
|
216
215
|
|
217
216
|
def test_id3v2_basic
|
218
|
-
w =
|
217
|
+
w = write_tag2_to_temp_file(DUMMY_TAG2)
|
219
218
|
assert_equal(DUMMY_TAG2, w)
|
220
219
|
id3v2_prog_test(DUMMY_TAG2, w)
|
221
220
|
end
|
@@ -254,13 +253,13 @@ class Mp3InfoTest < Test::Unit::TestCase
|
|
254
253
|
tag[k] = random_string(50)
|
255
254
|
end
|
256
255
|
|
257
|
-
got_tag =
|
256
|
+
got_tag = write_tag2_to_temp_file(tag)
|
258
257
|
assert_equal(tag, got_tag)
|
259
258
|
end
|
260
259
|
|
261
260
|
def test_id3v2_bigtag
|
262
261
|
tag = {"APIC" => random_string(1024) }
|
263
|
-
assert_equal(tag,
|
262
|
+
assert_equal(tag, write_tag2_to_temp_file(tag))
|
264
263
|
end
|
265
264
|
|
266
265
|
def test_infinite_loop_on_seek_to_v2_end
|
@@ -270,7 +269,7 @@ class Mp3InfoTest < Test::Unit::TestCase
|
|
270
269
|
def test_leading_char_gets_chopped
|
271
270
|
tag2 = DUMMY_TAG2.dup
|
272
271
|
tag2["WOAR"] = "http://foo.bar"
|
273
|
-
w =
|
272
|
+
w = write_tag2_to_temp_file(tag2)
|
274
273
|
assert_equal("http://foo.bar", w["WOAR"])
|
275
274
|
|
276
275
|
system(%(id3v2 --WOAR "http://foo.bar" "#{TEMP_FILE}"))
|
@@ -465,7 +464,7 @@ class Mp3InfoTest < Test::Unit::TestCase
|
|
465
464
|
end
|
466
465
|
|
467
466
|
def test_parse_tags_disabled
|
468
|
-
|
467
|
+
write_tag2_to_temp_file(DUMMY_TAG2)
|
469
468
|
Mp3Info.open(TEMP_FILE, :parse_tags => false) do |mp3|
|
470
469
|
assert mp3.tag.empty?
|
471
470
|
assert mp3.tag1.empty?
|
@@ -480,6 +479,34 @@ class Mp3InfoTest < Test::Unit::TestCase
|
|
480
479
|
end
|
481
480
|
end
|
482
481
|
|
482
|
+
def test_string_io
|
483
|
+
io = load_string_io
|
484
|
+
Mp3Info.open(io) do |mp3|
|
485
|
+
assert_mp3_info_are_ok(mp3)
|
486
|
+
end
|
487
|
+
end
|
488
|
+
|
489
|
+
def test_trying_to_rename_a_stringio_should_raise_an_error
|
490
|
+
io = load_string_io
|
491
|
+
Mp3Info.open(io) do |mp3|
|
492
|
+
assert_raises(Mp3InfoError) do
|
493
|
+
mp3.rename("whatever_filename_is_error_should_be_raised.mp3")
|
494
|
+
end
|
495
|
+
end
|
496
|
+
end
|
497
|
+
|
498
|
+
def test_hastag_class_methods_with_a_stringio
|
499
|
+
Mp3Info.open(TEMP_FILE) do |info|
|
500
|
+
info.tag1 = @tag
|
501
|
+
end
|
502
|
+
io = load_string_io
|
503
|
+
assert(Mp3Info.hastag1?(io))
|
504
|
+
|
505
|
+
written_tag = write_tag2_to_temp_file(DUMMY_TAG2)
|
506
|
+
io = load_string_io
|
507
|
+
assert(Mp3Info.hastag2?(io))
|
508
|
+
end
|
509
|
+
|
483
510
|
def compute_audio_content_mp3_digest(mp3)
|
484
511
|
pos, size = mp3.audio_content
|
485
512
|
data = File.open(mp3.filename) do |f|
|
@@ -489,7 +516,7 @@ class Mp3InfoTest < Test::Unit::TestCase
|
|
489
516
|
Digest::MD5.new.update(data).hexdigest
|
490
517
|
end
|
491
518
|
|
492
|
-
def
|
519
|
+
def write_tag2_to_temp_file(tag)
|
493
520
|
Mp3Info.open(TEMP_FILE) do |mp3|
|
494
521
|
mp3.tag2.update(tag)
|
495
522
|
end
|
@@ -503,6 +530,31 @@ class Mp3InfoTest < Test::Unit::TestCase
|
|
503
530
|
out
|
504
531
|
end
|
505
532
|
|
533
|
+
def assert_mp3_info_are_ok(mp3)
|
534
|
+
assert_equal(1, mp3.mpeg_version)
|
535
|
+
assert_equal(3, mp3.layer)
|
536
|
+
assert_equal(false, mp3.vbr)
|
537
|
+
assert_equal(128, mp3.bitrate)
|
538
|
+
assert_equal("JStereo", mp3.channel_mode)
|
539
|
+
assert_equal(44100, mp3.samplerate)
|
540
|
+
assert_equal(0.1305625, mp3.length)
|
541
|
+
assert_equal({:original => true,
|
542
|
+
:error_protection => false,
|
543
|
+
:padding => false,
|
544
|
+
:emphasis => 0,
|
545
|
+
:private => true,
|
546
|
+
:mode_extension => 2,
|
547
|
+
:copyright => false}, mp3.header)
|
548
|
+
end
|
549
|
+
|
550
|
+
def load_string_io(filename = TEMP_FILE)
|
551
|
+
io = StringIO.new
|
552
|
+
data = File.read(filename)
|
553
|
+
io.write(data)
|
554
|
+
io.rewind
|
555
|
+
io
|
556
|
+
end
|
557
|
+
|
506
558
|
=begin
|
507
559
|
|
508
560
|
def test_encoder
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-mp3info
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 25
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 6
|
9
|
-
-
|
10
|
-
version: 0.6.
|
9
|
+
- 15
|
10
|
+
version: 0.6.15
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Guillaume Pierronnet
|
@@ -15,40 +15,23 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-
|
18
|
+
date: 2011-07-18 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
|
-
name: hoe
|
21
|
+
name: hoe
|
22
22
|
prerelease: false
|
23
23
|
requirement: &id001 !ruby/object:Gem::Requirement
|
24
24
|
none: false
|
25
25
|
requirements:
|
26
|
-
- -
|
26
|
+
- - ~>
|
27
27
|
- !ruby/object:Gem::Version
|
28
|
-
hash:
|
28
|
+
hash: 23
|
29
29
|
segments:
|
30
|
-
- 0
|
31
|
-
- 1
|
32
30
|
- 2
|
33
|
-
|
31
|
+
- 10
|
32
|
+
version: "2.10"
|
34
33
|
type: :development
|
35
34
|
version_requirements: *id001
|
36
|
-
- !ruby/object:Gem::Dependency
|
37
|
-
name: hoe
|
38
|
-
prerelease: false
|
39
|
-
requirement: &id002 !ruby/object:Gem::Requirement
|
40
|
-
none: false
|
41
|
-
requirements:
|
42
|
-
- - ">="
|
43
|
-
- !ruby/object:Gem::Version
|
44
|
-
hash: 47
|
45
|
-
segments:
|
46
|
-
- 2
|
47
|
-
- 8
|
48
|
-
- 0
|
49
|
-
version: 2.8.0
|
50
|
-
type: :development
|
51
|
-
version_requirements: *id002
|
52
35
|
description: |-
|
53
36
|
* written in pure ruby
|
54
37
|
* read low-level informations like bitrate, length, samplerate, etc...
|
@@ -107,14 +90,14 @@ files:
|
|
107
90
|
- lib/mp3info/extension_modules.rb
|
108
91
|
- lib/mp3info/id3v2.rb
|
109
92
|
- test/test_ruby-mp3info.rb
|
93
|
+
- .gemtest
|
110
94
|
homepage:
|
111
95
|
licenses: []
|
112
96
|
|
113
97
|
post_install_message:
|
114
98
|
rdoc_options:
|
115
|
-
- --
|
116
|
-
-
|
117
|
-
- --quiet
|
99
|
+
- --main
|
100
|
+
- README.txt
|
118
101
|
require_paths:
|
119
102
|
- lib
|
120
103
|
required_ruby_version: !ruby/object:Gem::Requirement
|
@@ -138,7 +121,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
138
121
|
requirements: []
|
139
122
|
|
140
123
|
rubyforge_project: ruby-mp3info
|
141
|
-
rubygems_version: 1.
|
124
|
+
rubygems_version: 1.7.2
|
142
125
|
signing_key:
|
143
126
|
specification_version: 3
|
144
127
|
summary: ruby-mp3info is a pure-ruby library to retrieve low level informations on mp3 files and manipulate id3v1 and id3v2 tags
|