id3 0.4.1 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +8 -0
- data/docs/index.html +13 -0
- data/lib/id3.rb +168 -173
- metadata +3 -3
data/CHANGES
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
= id3 changes
|
2
2
|
|
3
|
+
=== 0.5.0 (2008-08-18)
|
4
|
+
|
5
|
+
* Added write support
|
6
|
+
* Fixed bug in determining the audioEnd index
|
7
|
+
* reverted UTF-16 fix because it broke the writing the tags
|
8
|
+
If you really need UTF-16 encoding, please stick with version 0.4.1 for now (read-only)
|
9
|
+
|
10
|
+
|
3
11
|
=== 0.4.1 (2008-08-16)
|
4
12
|
|
5
13
|
* Included patch provided by Sergey Udaltsov for UTF-8 and UTF-16 encodings, and new parser routines
|
data/docs/index.html
CHANGED
@@ -120,6 +120,19 @@ without problems. <br>
|
|
120
120
|
<br>
|
121
121
|
<table border=1>
|
122
122
|
<tr><th>Version</th><th>gem</th><th>tar.gz</th><th>Changes</th></tr>
|
123
|
+
<tr>
|
124
|
+
<th>0.5.0</th>
|
125
|
+
<td><A HREF=../../id3-0.5.0.gem>id3-0.5.0.gem</A></td>
|
126
|
+
<td><A HREF=../../id3-0.5.0.tar.gz>id3-0.5.0.tar.gz</A></td>
|
127
|
+
<td>
|
128
|
+
<UL>
|
129
|
+
<LI>Added write support
|
130
|
+
<LI> Fixed bug in determining the audioEnd index
|
131
|
+
<LI> reverted UTF-16 fix because it broke the writing the tags
|
132
|
+
If you really need UTF-16 encoding, please stick with version 0.4.1 for now (read-only)
|
133
|
+
</UL>
|
134
|
+
</td>
|
135
|
+
</tr>
|
123
136
|
<tr>
|
124
137
|
<th>0.4.1</th>
|
125
138
|
<td><A HREF=../../id3-0.4.1.gem>id3-0.4.1.gem</A></td>
|
data/lib/id3.rb
CHANGED
@@ -2,16 +2,10 @@
|
|
2
2
|
# id3.rb Ruby Module for handling the following ID3-tag versions:
|
3
3
|
# ID3v1.0 , ID3v1.1, ID3v2.2.0, ID3v2.3.0, ID3v2.4.0
|
4
4
|
#
|
5
|
-
# Copyright (C) 2002..2008 by Tilo Sloboda <tilo@unixgods.org>
|
5
|
+
# Copyright (C) 2002 .. 2008 by Tilo Sloboda <tilo@unixgods.org>
|
6
6
|
#
|
7
7
|
# created: 12 Oct 2002
|
8
|
-
# updated: Time-stamp: <
|
9
|
-
#
|
10
|
-
# Version: 0.4.1
|
11
|
-
#
|
12
|
-
# Changes:
|
13
|
-
# 0.4.1 thanks to Sergey Udaltsov for the UTF-8 UTF-16 patch
|
14
|
-
# and parser routines!
|
8
|
+
# updated: Time-stamp: <Mon 18-Aug-2008 06:16:19 Tilo Sloboda>
|
15
9
|
#
|
16
10
|
# Docs: http://www.id3.org/id3v2-00.txt
|
17
11
|
# http://www.id3.org/id3v2.3.0.txt
|
@@ -20,8 +14,8 @@
|
|
20
14
|
# http://www.id3.org/id3v2.4.0-frames.txt
|
21
15
|
#
|
22
16
|
# different versions of ID3 tags, support different fields.
|
23
|
-
# See: http://www.unixgods.org/~tilo/ID3v2_frames_comparison.txt
|
24
|
-
# See: http://www.unixgods.org/~tilo/ID3/docs/ID3_comparison.html
|
17
|
+
# See: http://www.unixgods.org/~tilo/Ruby/ID3/docs/ID3v2_frames_comparison.txt
|
18
|
+
# See: http://www.unixgods.org/~tilo/Ruby/ID3/docs/ID3_comparison.html
|
25
19
|
#
|
26
20
|
# License:
|
27
21
|
# Freely available under the terms of the OpenSource "Artistic License"
|
@@ -42,6 +36,7 @@
|
|
42
36
|
# TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF THE COPYRIGHT HOLDERS OR OTHER PARTY HAS BEEN
|
43
37
|
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
44
38
|
#
|
39
|
+
#
|
45
40
|
# Author's Rant:
|
46
41
|
# The author of this ID3-library for Ruby is not responsible in any way for
|
47
42
|
# the definition of the ID3-standards..
|
@@ -71,7 +66,7 @@
|
|
71
66
|
# removeID3v1tag(filename)
|
72
67
|
#
|
73
68
|
# Classes:
|
74
|
-
#
|
69
|
+
# AudioFile
|
75
70
|
# Tag1
|
76
71
|
# Tag2
|
77
72
|
# Frame
|
@@ -79,13 +74,17 @@
|
|
79
74
|
################################################################################
|
80
75
|
|
81
76
|
# ==============================================================================
|
82
|
-
#
|
77
|
+
# Loading other stuff..
|
83
78
|
# ==============================================================================
|
84
79
|
|
85
|
-
require
|
86
|
-
require
|
80
|
+
require 'md5'
|
81
|
+
require 'tempfile'
|
82
|
+
require 'ftools'
|
83
|
+
|
84
|
+
# my extensions:
|
87
85
|
|
88
86
|
require 'hexdump' # load hexdump method to extend class String
|
87
|
+
|
89
88
|
require 'invert_hash' # new invert method for old Hash
|
90
89
|
|
91
90
|
|
@@ -97,6 +96,7 @@ class Hash # overwrite Hash.invert method
|
|
97
96
|
end
|
98
97
|
end
|
99
98
|
|
99
|
+
# ==============================================================================
|
100
100
|
|
101
101
|
module ID3
|
102
102
|
|
@@ -283,112 +283,28 @@ module ID3
|
|
283
283
|
VARS = 0
|
284
284
|
PACKING = 1
|
285
285
|
|
286
|
-
BE16BOM = "\xFE\xFF"
|
287
|
-
LE16BOM = "\xFF\xFE"
|
288
|
-
|
289
|
-
def ID3.parse_string_rest(encoding, string)
|
290
|
-
case encoding
|
291
|
-
when 0
|
292
|
-
# ISO8859-1
|
293
|
-
return Iconv.iconv("ISO8859-1", "UTF-8", string);
|
294
|
-
when 1
|
295
|
-
# utf-16
|
296
|
-
bom = string[0..1]
|
297
|
-
data = string[2..string.length-1]
|
298
|
-
srcenc = bom == LE16BOM ? "UTF-16LE" : bom == BE16BOM ? "UTF-16BE" : "??"
|
299
|
-
return Iconv.iconv("UTF-8", srcenc, data);
|
300
|
-
when 2
|
301
|
-
# utf-16BE
|
302
|
-
return
|
303
|
-
Iconv.iconv("UTF-16BE", "UTF-8", data);
|
304
|
-
when 3
|
305
|
-
return string
|
306
|
-
else
|
307
|
-
raise 'Unknown encoding #{encoding}'
|
308
|
-
end
|
309
|
-
end
|
310
|
-
|
311
|
-
def ID3.parse_string_zeroend(encoding, string)
|
312
|
-
strend = encoding == 0 || encoding == 3 ? "\x00" : "\x00\x00";
|
313
|
-
|
314
|
-
endidx = string.index(strend)
|
315
|
-
if endidx.nil?
|
316
|
-
return parse_string_rest(encoding, string), string.length;
|
317
|
-
elsif endidx == 0
|
318
|
-
return "", strend.length;
|
319
|
-
else
|
320
|
-
return parse_string_rest(encoding, string[0..endidx-1]), endidx + strend.length;
|
321
|
-
end
|
322
|
-
end
|
323
|
-
|
324
|
-
def ID3.parse_a(data)
|
325
|
-
return [data]
|
326
|
-
end
|
327
|
-
|
328
|
-
def ID3.parse_Z(data)
|
329
|
-
rest = parse_string_rest(data)
|
330
|
-
return first, rest
|
331
|
-
end
|
332
|
-
|
333
|
-
def ID3.parse_CZ(data)
|
334
|
-
encoding = data[0]
|
335
|
-
rest = parse_string_rest(encoding, data[1..data.length-1])
|
336
|
-
return encoding, rest
|
337
|
-
end
|
338
|
-
|
339
|
-
def ID3.parse_CZZ(data)
|
340
|
-
encoding = data[0]
|
341
|
-
second,length = parse_string_zeroend(encoding, data[1..data.length-1])
|
342
|
-
rest = parse_string_rest(encoding, data[1 + length..data.length-1])
|
343
|
-
return encoding, second, rest
|
344
|
-
end
|
345
|
-
|
346
|
-
def ID3.parse_CZCZa(data)
|
347
|
-
encoding = data[0]
|
348
|
-
second,length = parse_string_zeroend(encoding, data[1..data.length-1])
|
349
|
-
third = data[length + 1]
|
350
|
-
fourth,nexpart2 = parse_string_zeroend(encoding, data[2 + length..data.length-1])
|
351
|
-
rest = data[2 + length + length2..data.length-1]
|
352
|
-
return encoding, second, third, fourth, rest
|
353
|
-
end
|
354
|
-
|
355
|
-
def ID3.parse_Ca3ZZ(data)
|
356
|
-
encoding = data[0]
|
357
|
-
second = data[1..3]
|
358
|
-
third,length = parse_string_zeroend(encoding,data[4..data.length-1])
|
359
|
-
rest = parse_string_rest(encoding, data[4 + length..data.length-1])
|
360
|
-
return encoding, second, third, rest
|
361
|
-
end
|
362
|
-
|
363
|
-
PARSER_CZ = lambda { |data| parse_CZ(data) }
|
364
|
-
PARSER_CZZ = lambda { |data| parse_CZZ(data) }
|
365
|
-
PARSER_Z = lambda { |data| parse_Z(data) }
|
366
|
-
PARSER_a = lambda { |data| parse_a(data) }
|
367
|
-
PARSER_CZCZa = lambda { |data| parse_CZCZa(data) }
|
368
|
-
PARSER_Ca3ZZ = lambda { |data| parse_Ca3ZZ(data) }
|
369
|
-
|
370
286
|
# not sure if it's Z* or A*
|
371
287
|
# A* does not append a \0 when writing!
|
372
288
|
|
373
|
-
# STILL NEED TO
|
289
|
+
# STILL NEED TO GET MORE TEST-CASES! e.g. Japanese ID3-Tags! or other encodings..
|
374
290
|
# seems like i have no version 2.4.x ID3-tags!! If you have some, send them my way!
|
375
291
|
|
376
292
|
FRAME_PARSER = {
|
377
|
-
"TEXT" => [ %w(encoding text) ,
|
378
|
-
"USERTEXT" => [ %w(encoding description value) ,
|
293
|
+
"TEXT" => [ %w(encoding text) , 'CZ*' ] ,
|
294
|
+
"USERTEXT" => [ %w(encoding description value) , 'CZ*Z*' ] ,
|
379
295
|
|
380
|
-
"PICTURE" => [ %w(encoding mimeType pictType description picture) ,
|
296
|
+
"PICTURE" => [ %w(encoding mimeType pictType description picture) , 'CZ*CZ*a*' ] ,
|
381
297
|
|
382
|
-
"WEB" => [ "url" ,
|
383
|
-
"WWWUSER" => [ %w(encoding description url) ,
|
298
|
+
"WEB" => [ "url" , 'Z*' ] ,
|
299
|
+
"WWWUSER" => [ %w(encoding description url) , 'CZ*Z*' ] ,
|
384
300
|
|
385
|
-
"LTEXT" => [ %w(encoding language text) ,
|
386
|
-
"UNSYNCEDLYRICS" => [ %w(encoding language content text) ,
|
387
|
-
"COMMENT" => [ %w(encoding language short long) ,
|
388
|
-
"BINARY" => [ "binary" ,
|
389
|
-
"UNPARSED" => [ "raw" ,
|
301
|
+
"LTEXT" => [ %w(encoding language text) , 'CZ*Z*' ] ,
|
302
|
+
"UNSYNCEDLYRICS" => [ %w(encoding language content text) , 'Ca3Z*Z*' ] ,
|
303
|
+
"COMMENT" => [ %w(encoding language short long) , 'Ca3Z*Z*' ] ,
|
304
|
+
"BINARY" => [ "binary" , 'a*' ] ,
|
305
|
+
"UNPARSED" => [ "raw" , 'a*' ] # how would we do value checking for this?
|
390
306
|
}
|
391
|
-
|
307
|
+
|
392
308
|
# ----------------------------------------------------------------------------
|
393
309
|
# MODULE VARIABLES
|
394
310
|
# ----------------------------------------------------------------------------
|
@@ -428,9 +344,8 @@ module ID3
|
|
428
344
|
# MODULE FUNCTIONS:
|
429
345
|
# ----------------------------------------------------------------------------
|
430
346
|
# The ID3 module functions are to query or modify files directly.
|
431
|
-
# They
|
432
|
-
|
433
|
-
#
|
347
|
+
# They check directly if a file has a ID3-tag, but they don't parse the tags!
|
348
|
+
|
434
349
|
|
435
350
|
# ----------------------------------------------------------------------------
|
436
351
|
# hasID3v1tag?
|
@@ -504,7 +419,8 @@ module ID3
|
|
504
419
|
stat = File.stat(filename)
|
505
420
|
if stat.file? && stat.writable? && ID3.hasID3v1tag?(filename)
|
506
421
|
|
507
|
-
# CAREFUL: this does not check if there really is a valid tag
|
422
|
+
# CAREFUL: this does not check if there really is a valid tag,
|
423
|
+
# that's why we need to check above!!
|
508
424
|
|
509
425
|
newsize = stat.size - ID3v1tagSize
|
510
426
|
File.open(filename, "r+") { |f| f.truncate(newsize) }
|
@@ -525,8 +441,8 @@ module ID3
|
|
525
441
|
# revert feature would be nice to have..
|
526
442
|
#
|
527
443
|
# If we query and AudioFile object, we query what's currently associated with it
|
528
|
-
# e.g. we're not querying the file itself, but the perhaps modified
|
529
|
-
# To query the file itself, use the module functions
|
444
|
+
# e.g. we're not querying the file itself, but the Tag object which is perhaps modified.
|
445
|
+
# To query the file itself, use the ID3 module functions
|
530
446
|
|
531
447
|
class AudioFile
|
532
448
|
|
@@ -537,12 +453,11 @@ module ID3
|
|
537
453
|
attr_reader :dirname, :basename # absolute dirname and basename of the file (computed)
|
538
454
|
|
539
455
|
attr_accessor :tagID3v1, :tagID3v2
|
540
|
-
attr_reader :hasID3tag # either false, or a string with all version numbers found
|
541
456
|
|
542
457
|
# ----------------------------------------------------------------------------
|
543
458
|
# initialize
|
544
459
|
#
|
545
|
-
# AudioFile.new does NOT
|
460
|
+
# AudioFile.new does NOT keep the file open, but scans it and parses the info
|
546
461
|
|
547
462
|
# e.g.: ID3::AudioFile.new('mp3/a.mp3')
|
548
463
|
|
@@ -556,7 +471,7 @@ module ID3
|
|
556
471
|
@tagID3v2 = nil
|
557
472
|
|
558
473
|
audioStartX = 0
|
559
|
-
audioEndX = File.size(filename)
|
474
|
+
audioEndX = File.size(filename) - 1 # points to the last index
|
560
475
|
|
561
476
|
if ID3.hasID3v1tag?(@filename)
|
562
477
|
@tagID3v1 = Tag1.new
|
@@ -572,15 +487,102 @@ module ID3
|
|
572
487
|
end
|
573
488
|
|
574
489
|
# audioStartX audioEndX indices into the file need to be set
|
575
|
-
@audioStartX = audioStartX
|
576
|
-
@audioEndX = audioEndX
|
490
|
+
@audioStartX = audioStartX # first byte of audio data
|
491
|
+
@audioEndX = audioEndX # last byte of audio data
|
577
492
|
|
578
493
|
# user may compute the MD5sum of the audio content later..
|
579
494
|
# but we're only doing this if the user requests it..
|
495
|
+
# because MD5sum computation takes a little bit time.
|
580
496
|
|
581
497
|
@audioMD5sum = nil
|
582
498
|
end
|
583
499
|
|
500
|
+
# ----------------------------------------------------------------------------
|
501
|
+
def audioLength
|
502
|
+
@audioEndX - @audioStartX + 1
|
503
|
+
end
|
504
|
+
# ----------------------------------------------------------------------------
|
505
|
+
# write
|
506
|
+
# write the AudioFile to file, including any ID3-tags
|
507
|
+
# We keep backups if we write to a specific filename
|
508
|
+
|
509
|
+
def write(*filename)
|
510
|
+
backups = false
|
511
|
+
|
512
|
+
if filename.size == 0 # this is an Array!!
|
513
|
+
filename = @filename
|
514
|
+
backups = true # keep backups if we write to a specific filename
|
515
|
+
else
|
516
|
+
filename = filename[0]
|
517
|
+
backups = false
|
518
|
+
end
|
519
|
+
|
520
|
+
tf = Tempfile.new( @basename )
|
521
|
+
tmpname = tf.path
|
522
|
+
|
523
|
+
# write ID3v2 tag:
|
524
|
+
|
525
|
+
if @tagID3v2
|
526
|
+
tf.write( @tagID3v2.dump )
|
527
|
+
end
|
528
|
+
|
529
|
+
# write Audio Data:
|
530
|
+
|
531
|
+
File.open( @filename ) { |f|
|
532
|
+
f.seek(@audioStartX)
|
533
|
+
tf.write( f.read(@audioEndX - @audioStartX +1) )
|
534
|
+
}
|
535
|
+
|
536
|
+
# write ID3v1 tag:
|
537
|
+
|
538
|
+
if @tagID3v1
|
539
|
+
tf.write( @tagID3v1.dump )
|
540
|
+
end
|
541
|
+
|
542
|
+
tf.close
|
543
|
+
|
544
|
+
# now some logic about moving the tempfile and replacing the original
|
545
|
+
|
546
|
+
bakname = filename + '.bak'
|
547
|
+
File.move(filename, bakname) if backups && FileTest.exists?(filename) && ! FileTest.exists?(bakname)
|
548
|
+
|
549
|
+
File.move(tmpname, filename)
|
550
|
+
tf.close(true)
|
551
|
+
|
552
|
+
# write md5sum file:
|
553
|
+
|
554
|
+
writeMD5sum if @audioMD5sum
|
555
|
+
|
556
|
+
end
|
557
|
+
|
558
|
+
# ----------------------------------------------------------------------------
|
559
|
+
# writeAudio
|
560
|
+
# only for debugging, does not write any ID3-tags, but just the audio portion
|
561
|
+
|
562
|
+
def writeAudio
|
563
|
+
tf = Tempfile.new( @basename )
|
564
|
+
|
565
|
+
File.open( @filename ) { |f|
|
566
|
+
f.seek(@audioStartX)
|
567
|
+
tf.write( f.read(@audioEndX - @audioStartX + 1) )
|
568
|
+
}
|
569
|
+
tf.close
|
570
|
+
path = tf.path
|
571
|
+
|
572
|
+
tf.open
|
573
|
+
tf.close(true)
|
574
|
+
end
|
575
|
+
|
576
|
+
|
577
|
+
# ----------------------------------------------------------------------------
|
578
|
+
# NOTE on md5sum's:
|
579
|
+
# If you don't know what an md5sum is, you can think of it as a unique
|
580
|
+
# fingerprint of a file or some data. I added the md5sum computation to
|
581
|
+
# help users keep track of their converted songs - even if the ID3-tag of
|
582
|
+
# a file changes, the md5sum of the audio data does not change..
|
583
|
+
# The md5sum can help you ensure that the audio-portion of the file
|
584
|
+
# was not changed after modifying, adding or deleting ID3-tags.
|
585
|
+
|
584
586
|
# ----------------------------------------------------------------------------
|
585
587
|
# audioMD5sum
|
586
588
|
# if the user tries to access @audioMD5sum, it will be computed for him,
|
@@ -647,6 +649,11 @@ module ID3
|
|
647
649
|
|
648
650
|
end
|
649
651
|
# ----------------------------------------------------------------------------
|
652
|
+
# version aka versions
|
653
|
+
# queries the tag objects and returns the version numbers of those tags
|
654
|
+
# NOTE: this does not reflect what's currently in the file, but what's
|
655
|
+
# currently in the AudioFile object
|
656
|
+
|
650
657
|
def version
|
651
658
|
a = Array.new
|
652
659
|
a.push(@tagID3v1.version) if @tagID3v1
|
@@ -664,6 +671,8 @@ module ID3
|
|
664
671
|
|
665
672
|
# ==============================================================================
|
666
673
|
# Class RestrictedOrderedHash
|
674
|
+
# this is a helper Class for ID3::Frame
|
675
|
+
#
|
667
676
|
|
668
677
|
class RestrictedOrderedHash < Hash
|
669
678
|
|
@@ -740,15 +749,20 @@ module ID3
|
|
740
749
|
# ==============================================================================
|
741
750
|
# Class GenericTag
|
742
751
|
#
|
743
|
-
#
|
752
|
+
# Helper class for Tag1 and Tag2
|
753
|
+
#
|
754
|
+
# Checks that user uses a valid key, and adds methods for size computation
|
755
|
+
#
|
756
|
+
# as per ID3-definition, the frames are in no fixed order! that's why we can derive
|
757
|
+
# this class from Hash. But in the future we may want to write certain frames first
|
758
|
+
# into the ID3-tag and therefore may want to derive it from RestrictedOrderedHash
|
744
759
|
|
745
|
-
class GenericTag < Hash
|
760
|
+
class GenericTag < Hash
|
746
761
|
attr_reader :version, :raw
|
747
762
|
|
748
763
|
# these definitions are to prevent users from inventing their own field names..
|
749
764
|
# but on the other hand, they should be able to create a new valid field, if
|
750
765
|
# it's not yet in the current tag, but it's valid for that ID3-version...
|
751
|
-
# ... so hiding this, is not enough!
|
752
766
|
|
753
767
|
alias old_set []=
|
754
768
|
private :old_set
|
@@ -810,12 +824,6 @@ module ID3
|
|
810
824
|
# ----------------------------------------------------------------------
|
811
825
|
# read reads a version 1.x ID3tag
|
812
826
|
#
|
813
|
-
# 30 title
|
814
|
-
# 30 artist
|
815
|
-
# 30 album
|
816
|
-
# 4 year
|
817
|
-
# 30 comment
|
818
|
-
# 1 genre
|
819
827
|
|
820
828
|
def read(filename)
|
821
829
|
f = File.open(filename, 'r')
|
@@ -825,7 +833,7 @@ module ID3
|
|
825
833
|
f.seek(-ID3::ID3v1tagSize, IO::SEEK_END)
|
826
834
|
@raw = f.read(ID3::ID3v1tagSize)
|
827
835
|
|
828
|
-
# self.parse!(raw) # we should use "parse!" instead of
|
836
|
+
# self.parse!(raw) # we should use "parse!" instead of duplicating code!
|
829
837
|
|
830
838
|
if (raw[ID3v1versionbyte] == 0)
|
831
839
|
@version = "1.1"
|
@@ -860,6 +868,7 @@ module ID3
|
|
860
868
|
#
|
861
869
|
# always upgrade version 1.0 to 1.1 when writing
|
862
870
|
|
871
|
+
# not yet implemented, because AudioFile.write does the job better
|
863
872
|
|
864
873
|
# ----------------------------------------------------------------------
|
865
874
|
# this routine modifies self, e.g. the Tag1 object
|
@@ -896,7 +905,7 @@ module ID3
|
|
896
905
|
#
|
897
906
|
# although we provide this method, it's stongly discouraged to use it,
|
898
907
|
# because ID3 version 1.x tags are inferior to version 2.x tags, as entries
|
899
|
-
# are often truncated and hence often useless..
|
908
|
+
# are often truncated and hence ID3 v1 tags are often useless..
|
900
909
|
|
901
910
|
def dump
|
902
911
|
zeroes = "\0" * 32
|
@@ -1000,14 +1009,28 @@ module ID3
|
|
1000
1009
|
# writes and replaces existing ID3-v2-tag if one is present
|
1001
1010
|
# Careful, this does NOT merge or append, it overwrites!
|
1002
1011
|
|
1003
|
-
|
1012
|
+
# not yet implemented, because AudioFile.write does the job better
|
1013
|
+
|
1014
|
+
# def write(filename)
|
1004
1015
|
# check how long the old ID3-v2 tag is
|
1005
1016
|
|
1006
1017
|
# dump ID3-v2-tag
|
1007
1018
|
|
1008
1019
|
# append old audio to new tag
|
1009
1020
|
|
1010
|
-
end
|
1021
|
+
# end
|
1022
|
+
|
1023
|
+
# ----------------------------------------------------------------------------
|
1024
|
+
# writeID3v2
|
1025
|
+
# just writes the ID3v2 tag by itself into a file, no audio data is written
|
1026
|
+
#
|
1027
|
+
# for backing up ID3v2 tags and debugging only..
|
1028
|
+
#
|
1029
|
+
|
1030
|
+
# def writeID3v2
|
1031
|
+
|
1032
|
+
# end
|
1033
|
+
|
1011
1034
|
# ----------------------------------------------------------------------
|
1012
1035
|
# parse_frame_header
|
1013
1036
|
#
|
@@ -1055,7 +1078,7 @@ module ID3
|
|
1055
1078
|
framename = header[0..3]
|
1056
1079
|
size = (header[4]*256**3)+(header[5]*256**2)+(header[6]*256)+header[7]
|
1057
1080
|
flags= header[8..9]
|
1058
|
-
# printf "frame
|
1081
|
+
# printf "frame: %s , size: %d, flags: %s\n", framename , size, flags
|
1059
1082
|
|
1060
1083
|
else
|
1061
1084
|
# we can't parse higher versions
|
@@ -1074,6 +1097,12 @@ module ID3
|
|
1074
1097
|
end
|
1075
1098
|
# ----------------------------------------------------------------------
|
1076
1099
|
# dump a ID3-v2 tag into a binary array
|
1100
|
+
#
|
1101
|
+
# NOTE:
|
1102
|
+
# when "dumping" an ID3-v2 tag, I would like to have more control about
|
1103
|
+
# which frames get dumped first.. e.g. the most important frames (with the
|
1104
|
+
# most important information) should be dumped first..
|
1105
|
+
#
|
1077
1106
|
|
1078
1107
|
public
|
1079
1108
|
def dump
|
@@ -1083,7 +1112,8 @@ module ID3
|
|
1083
1112
|
self.each { |framename,framedata|
|
1084
1113
|
data << framedata.dump
|
1085
1114
|
}
|
1086
|
-
# add some padding perhaps
|
1115
|
+
# add some padding perhaps 32 bytes (should be defined by the user!)
|
1116
|
+
# NOTE: I noticed that iTunes adds excessive amounts of padding
|
1087
1117
|
data << "\0" * 32
|
1088
1118
|
|
1089
1119
|
# calculate the complete length of the data-section
|
@@ -1107,37 +1137,6 @@ module ID3
|
|
1107
1137
|
# dumps ID3v2 frames into a binary array
|
1108
1138
|
# allows to modify frame's contents if the frame was decoded..
|
1109
1139
|
#
|
1110
|
-
# NOTE: right now the class Frame is derived from Hash, which is wrong..
|
1111
|
-
# It should really be derived from something like RestrictedOrderedHash
|
1112
|
-
# ... a new class, which preserves the order of keys, and which does
|
1113
|
-
# strict checking that all keys are present and reference correct values!
|
1114
|
-
# e.g. frames["COMMENT"]
|
1115
|
-
# ==> {"encoding"=>Byte, "language"=>Chars3, "text1"=>String, "text2"=>String}
|
1116
|
-
#
|
1117
|
-
# e.g. user should be able to create a new frame , like:
|
1118
|
-
# tag2.frames["COMMENT"] = "right side"
|
1119
|
-
#
|
1120
|
-
# and the following checks should be done:
|
1121
|
-
#
|
1122
|
-
# 1) if "COMMENT" is a correct key for tag2
|
1123
|
-
# 2) if the "right side" contains the correct keys
|
1124
|
-
# 3) if the "right side" contains the correct value for each key
|
1125
|
-
#
|
1126
|
-
# In the simplest case, the "right side" might be just a string,
|
1127
|
-
# but for most FrameTypes, it's a complex datastructure.. and we need
|
1128
|
-
# to check it for correctness before doing the assignment..
|
1129
|
-
#
|
1130
|
-
# NOTE2: the class Tag2 should have hash-like accessor functions to let the user
|
1131
|
-
# easily access frames and their contents..
|
1132
|
-
#
|
1133
|
-
# e.g. tag2[framename] would really access tag2.frames[framename]
|
1134
|
-
#
|
1135
|
-
# and if that works, we can make tag2.frames private and hidden!
|
1136
|
-
#
|
1137
|
-
# This means, that when we generate the parse and dump routines dynamically,
|
1138
|
-
# we may want to create the corresponding accessor methods for Tag2 class
|
1139
|
-
# as well...? or are generic ones enough?
|
1140
|
-
#
|
1141
1140
|
|
1142
1141
|
class Frame < RestrictedOrderedHash
|
1143
1142
|
|
@@ -1181,11 +1180,7 @@ module ID3
|
|
1181
1180
|
end
|
1182
1181
|
EOB
|
1183
1182
|
|
1184
|
-
@rawflags = flags
|
1185
|
-
|
1186
|
-
if (@rawflags & (FRAME_HEADER_FLAGS["2.4.0"]["Unsynchronisation"])) != 0
|
1187
|
-
@rawdata = @rawdata.gsub("\xFF\x00","\xFF")
|
1188
|
-
end
|
1183
|
+
@rawflags = flags.to_i # preserve the raw flags (for debugging only)
|
1189
1184
|
|
1190
1185
|
if (flags.to_i & FRAME_HEADER_FLAG_MASK[@version] != 0)
|
1191
1186
|
# in this case we need to skip parsing the frame... and skip to the next one...
|
@@ -1223,7 +1218,7 @@ module ID3
|
|
1223
1218
|
vars2 = vars
|
1224
1219
|
end
|
1225
1220
|
|
1226
|
-
values =
|
1221
|
+
values = self.rawdata.unpack(packing)
|
1227
1222
|
vars.each { |key|
|
1228
1223
|
self[key] = values.shift
|
1229
1224
|
}
|
metadata
CHANGED
@@ -3,9 +3,9 @@ rubygems_version: 0.9.4
|
|
3
3
|
specification_version: 1
|
4
4
|
name: id3
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.
|
7
|
-
date: 2008-08-
|
8
|
-
summary:
|
6
|
+
version: 0.5.0
|
7
|
+
date: 2008-08-24 00:00:00 -07:00
|
8
|
+
summary: A native ID3 tag library for Ruby, which does not depend on architecture-dependent C-libraries. It supports reading and writing ID3-tag versions 1.0, 1.1, and 2.2.x, 2,3.x, 2,4.x\n http://www.unixgods.org/~tilo/Ruby/ID3
|
9
9
|
require_paths:
|
10
10
|
- lib
|
11
11
|
email: tools@unixgods.org
|