id3 0.4.1 → 0.5.0
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.
- 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
|