id3 0.5.0 → 1.0.0.pre4
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +14 -0
- data/LICENSE.html +8 -1
- data/README.md +39 -0
- data/docs/ID3_comparison.html +10 -2
- data/docs/ID3_comparison2.html +29 -21
- data/docs/ID3v2_frames_overview.txt +172 -35
- data/{lib → docs}/hexdump.rb +0 -0
- data/docs/index.html +29 -9
- data/lib/helpers/hash_extensions.rb +20 -0
- data/lib/helpers/hexdump.rb +136 -0
- data/lib/helpers/invert_hash.rb +128 -0
- data/lib/helpers/recursive_helper.rb +39 -0
- data/lib/helpers/restricted_ordered_hash.rb +88 -0
- data/lib/helpers/ruby_1.8_1.9_compatibility.rb +62 -0
- data/lib/id3.rb +23 -1252
- data/lib/id3/audiofile.rb +261 -0
- data/lib/id3/constants.rb +292 -0
- data/lib/id3/frame.rb +178 -0
- data/lib/id3/frame_array.rb +19 -0
- data/lib/id3/generic_tag.rb +73 -0
- data/lib/id3/id3.rb +159 -0
- data/lib/id3/io_extensions.rb +44 -0
- data/lib/id3/module_methods.rb +127 -0
- data/lib/id3/string_extensions.rb +40 -0
- data/lib/id3/tag1.rb +131 -0
- data/lib/id3/tag2.rb +261 -0
- metadata +87 -58
- data/README +0 -18
- data/docs/ID3v2_frames_comparison.txt +0 -197
- data/lib/invert_hash.rb +0 -105
data/lib/id3/frame.rb
ADDED
@@ -0,0 +1,178 @@
|
|
1
|
+
module ID3
|
2
|
+
# ==============================================================================
|
3
|
+
# Class Frame ID3 Version 2.x.y Frame
|
4
|
+
#
|
5
|
+
# parses ID3v2 frames from a binary array
|
6
|
+
# dumps ID3v2 frames into a binary array
|
7
|
+
# allows to modify frame's contents if the frame was decoded..
|
8
|
+
#
|
9
|
+
# NOTE: right now the class Frame is derived from Hash, which is wrong..
|
10
|
+
# It should really be derived from something like RestrictedOrderedHash
|
11
|
+
# ... a new class, which preserves the order of keys, and which does
|
12
|
+
# strict checking that all keys are present and reference correct values!
|
13
|
+
# e.g. frames["COMMENT"]
|
14
|
+
# ==> {"encoding"=>Byte, "language"=>Chars3, "text1"=>String, "text2"=>String}
|
15
|
+
#
|
16
|
+
# e.g. user should be able to create a new frame , like:
|
17
|
+
# tag2.frames["COMMENT"] = "right side"
|
18
|
+
#
|
19
|
+
# and the following checks should be done:
|
20
|
+
#
|
21
|
+
# 1) if "COMMENT" is a correct key for tag2
|
22
|
+
# 2) if the "right side" contains the correct keys
|
23
|
+
# 3) if the "right side" contains the correct value for each key
|
24
|
+
#
|
25
|
+
# In the simplest case, the "right side" might be just a string,
|
26
|
+
# but for most FrameTypes, it's a complex datastructure.. and we need
|
27
|
+
# to check it for correctness before doing the assignment..
|
28
|
+
#
|
29
|
+
# NOTE2: the class Tag2 should have hash-like accessor functions to let the user
|
30
|
+
# easily access frames and their contents..
|
31
|
+
#
|
32
|
+
# e.g. tag2[framename] would really access tag2.frames[framename]
|
33
|
+
#
|
34
|
+
# and if that works, we can make tag2.frames private and hidden!
|
35
|
+
#
|
36
|
+
# This means, that when we generate the parse and dump routines dynamically,
|
37
|
+
# we may want to create the corresponding accessor methods for Tag2 class
|
38
|
+
# as well...? or are generic ones enough?
|
39
|
+
#
|
40
|
+
#
|
41
|
+
# NOTE3:
|
42
|
+
#
|
43
|
+
# The old way to pack / unpack frames to encode / decode them, is working,
|
44
|
+
# but has the disadvantage that it's a little bit too close to the metal.
|
45
|
+
# e.g. encoding and textcontent are both accessible, but ideally only
|
46
|
+
# the textvalue should be accessible and settable, and the encoding should
|
47
|
+
# automatically be set correctly / accordingly...
|
48
|
+
#
|
49
|
+
# NOTE4:
|
50
|
+
# for frames like TXXX , WXXX , which can occur multiple times in a ID3v2 frame,
|
51
|
+
# we should manage those tags as an Array...
|
52
|
+
#
|
53
|
+
|
54
|
+
class Frame < RestrictedOrderedHash
|
55
|
+
|
56
|
+
attr_reader :name, :version
|
57
|
+
attr_reader :headerStartX, :dataStartX, :dataEndX, :rawdata, :rawheader # debugging only
|
58
|
+
|
59
|
+
# ----------------------------------------------------------------------
|
60
|
+
# return the complete raw frame
|
61
|
+
|
62
|
+
def raw
|
63
|
+
return @rawheader + @rawdata
|
64
|
+
end
|
65
|
+
# ----------------------------------------------------------------------
|
66
|
+
|
67
|
+
def initialize(name, version = '2.3.0', flags = 0, tag, headerStartX, dataStartX, dataEndX )
|
68
|
+
super
|
69
|
+
|
70
|
+
@name = name
|
71
|
+
@headerStartX = headerStartX if headerStartX
|
72
|
+
@dataStartX = dataStartX if dataStartX
|
73
|
+
@dataEndX = dataEndX if dataEndX
|
74
|
+
|
75
|
+
if tag
|
76
|
+
@rawdata = tag.raw[dataStartX..dataEndX]
|
77
|
+
@rawheader = tag.raw[headerStartX..dataStartX-1]
|
78
|
+
# parse the darn flags, if there are any..
|
79
|
+
@version = tag.version # caching..
|
80
|
+
else
|
81
|
+
@rawdata = ''
|
82
|
+
@rawheader= ''
|
83
|
+
@version = version
|
84
|
+
end
|
85
|
+
|
86
|
+
case @version
|
87
|
+
when /2\.2\.[0-9]/
|
88
|
+
# no flags, no extra attributes necessary
|
89
|
+
|
90
|
+
when /2\.[34]\.0/
|
91
|
+
|
92
|
+
# dynamically create attributes and reader functions for flags in ID3-frames:
|
93
|
+
# (not defined in earlier ID3 versions)
|
94
|
+
instance_eval <<-EOB
|
95
|
+
class << self
|
96
|
+
attr_reader :rawflags, :flags
|
97
|
+
end
|
98
|
+
EOB
|
99
|
+
|
100
|
+
@rawflags = flags.to_i # preserve the raw flags (for debugging only)
|
101
|
+
|
102
|
+
if (flags.to_i & FRAME_HEADER_FLAG_MASK[@version] != 0)
|
103
|
+
# in this case we need to skip parsing the frame... and skip to the next one...
|
104
|
+
wrong = flags.to_i & FRAME_HEADER_FLAG_MASK[@version]
|
105
|
+
error = printf "ID3 version %s frame header flags 0x%X contain invalid flags 0x%X !\n", @version, flags, wrong
|
106
|
+
raise ArgumentError, error
|
107
|
+
end
|
108
|
+
|
109
|
+
@flags = Hash.new
|
110
|
+
|
111
|
+
FRAME_HEADER_FLAGS[@version].each do |key,val|
|
112
|
+
# only define the flags which are set..
|
113
|
+
@flags[key] = true if (flags.to_i & val == 1)
|
114
|
+
end
|
115
|
+
|
116
|
+
else
|
117
|
+
raise ArgumentError, "ID3 version #{@version} not recognized when parsing frame header flags\n"
|
118
|
+
end # parsing flags
|
119
|
+
|
120
|
+
# generate methods for parsing data (low-level read support) and for dumping data out (low-level write-support)
|
121
|
+
#
|
122
|
+
# based on the particular ID3-version and the ID3-frame name, we basically obtain a string saying how to pack/unpack the data for that frame
|
123
|
+
# then we use that packing-string to define a parser and dump method for this particular frame
|
124
|
+
|
125
|
+
instance_eval <<-EOB
|
126
|
+
class << self
|
127
|
+
|
128
|
+
def parse
|
129
|
+
# here we GENERATE the code to parse, dump and verify methods
|
130
|
+
|
131
|
+
vars,packing = ID3::FRAME_PARSER[ ID3::FrameName2FrameType[ ID3::Framename2symbol[self.version][self.name]] ]
|
132
|
+
|
133
|
+
values = self.rawdata.unpack(packing)
|
134
|
+
|
135
|
+
vars.each do |key|
|
136
|
+
self[key] = values.shift
|
137
|
+
end
|
138
|
+
self.lock # lock the OrderedHash
|
139
|
+
end
|
140
|
+
|
141
|
+
|
142
|
+
def dump
|
143
|
+
vars,packing = ID3::FRAME_PARSER[ ID3::FrameName2FrameType[ ID3::Framename2symbol[self.version][self.name]] ]
|
144
|
+
|
145
|
+
data = self.values.pack(packing) # we depend on an OrderedHash, so the values are in the correct order!!!
|
146
|
+
header = self.name.dup # we want the value! not the reference!!
|
147
|
+
len = data.length
|
148
|
+
if self.version =~ /^2\.2\./
|
149
|
+
byte2,rest = len.divmod(256**2)
|
150
|
+
byte1,byte0 = rest.divmod(256)
|
151
|
+
|
152
|
+
header << byte2 << byte1 << byte0
|
153
|
+
|
154
|
+
elsif self.version =~ /^2\.[34]\./ # 10-byte header
|
155
|
+
byte3,rest = len.divmod(256**3)
|
156
|
+
byte2,rest = rest.divmod(256**2)
|
157
|
+
byte1,byte0 = rest.divmod(256)
|
158
|
+
|
159
|
+
flags1,flags0 = self.rawflags.divmod(256)
|
160
|
+
|
161
|
+
header << byte3 << byte2 << byte1 << byte0 << flags1 << flags0
|
162
|
+
end
|
163
|
+
header << data
|
164
|
+
end
|
165
|
+
|
166
|
+
end
|
167
|
+
EOB
|
168
|
+
|
169
|
+
self.parse # now we're using the just defined parsing routine
|
170
|
+
|
171
|
+
return self
|
172
|
+
end
|
173
|
+
# ----------------------------------------------------------------------
|
174
|
+
|
175
|
+
end # of class Frame
|
176
|
+
# ==============================================================================
|
177
|
+
|
178
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module ID3
|
2
|
+
# ==============================================================================
|
3
|
+
# Class FrameArray
|
4
|
+
#
|
5
|
+
# basically nothing more than an Array, but it knows how to dump it's contents as ID3v2 frames
|
6
|
+
#
|
7
|
+
# this solves in part the problem of having multiple ID3v2 frames in one tag, e.g. TXXX , WXXX, APIC
|
8
|
+
|
9
|
+
class FrameArray < Array
|
10
|
+
def dump
|
11
|
+
result = ''
|
12
|
+
self.each do |element|
|
13
|
+
result << element.dump
|
14
|
+
end
|
15
|
+
return result
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module ID3
|
2
|
+
|
3
|
+
# ==============================================================================
|
4
|
+
# Class GenericTag
|
5
|
+
#
|
6
|
+
# Helper class for Tag1 and Tag2
|
7
|
+
#
|
8
|
+
# Checks that user uses a valid key, and adds methods for size computation
|
9
|
+
#
|
10
|
+
# as per ID3-definition, the frames are in no fixed order! that's why we can derive
|
11
|
+
# this class from Hash. But in the future we may want to write certain frames first
|
12
|
+
# into the ID3-tag and therefore may want to derive it from RestrictedOrderedHash
|
13
|
+
|
14
|
+
# BUG (4) : When an ID3frame is assigned a value, e.g. a String, then the Hash just stores the value right now.
|
15
|
+
# Whereas when you read the ID3v2 tag, the object for the frame is ID3::Frame
|
16
|
+
|
17
|
+
class GenericTag < RestrictedOrderedHash
|
18
|
+
attr_accessor :version
|
19
|
+
attr_reader :raw
|
20
|
+
|
21
|
+
# these definitions are to prevent users from inventing their own field names..
|
22
|
+
# but on the other hand, they should be able to create a new valid field, if
|
23
|
+
# it's not yet in the current tag, but it's valid for that ID3-version...
|
24
|
+
|
25
|
+
alias old_set []=
|
26
|
+
private :old_set
|
27
|
+
|
28
|
+
# ----------------------------------------------------------------------
|
29
|
+
def []=(key,val)
|
30
|
+
if @version == ""
|
31
|
+
raise ArgumentError, "undefined version of ID3-tag! - set version before accessing components!\n"
|
32
|
+
else
|
33
|
+
if ID3::SUPPORTED_SYMBOLS[@version].keys.include?(key)
|
34
|
+
old_set(key,val)
|
35
|
+
else
|
36
|
+
# exception
|
37
|
+
raise ArgumentError, "Incorrect ID3-field \"#{key}\" for ID3 version #{@version}\n" +
|
38
|
+
" valid ID3-fields are: " + SUPPORTED_SYMBOLS[@version].keys.join(",") +"\n"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
# ----------------------------------------------------------------------
|
43
|
+
# convert the 4 bytes found in the id3v2 header and return the size
|
44
|
+
private
|
45
|
+
def unmungeSize(bytes)
|
46
|
+
size = 0
|
47
|
+
j = 0; i = 3
|
48
|
+
while i >= 0
|
49
|
+
size += 128**i * (bytes.getbyte(j) & 0x7f)
|
50
|
+
j += 1
|
51
|
+
i -= 1
|
52
|
+
end
|
53
|
+
return size
|
54
|
+
end
|
55
|
+
# ----------------------------------------------------------------------
|
56
|
+
# convert the size into 4 bytes to be written into an id3v2 header
|
57
|
+
private
|
58
|
+
def mungeSize(size)
|
59
|
+
bytes = Array.new(4,0)
|
60
|
+
j = 0; i = 3
|
61
|
+
while i >= 0
|
62
|
+
bytes[j],size = size.divmod(128**i)
|
63
|
+
j += 1
|
64
|
+
i -= 1
|
65
|
+
end
|
66
|
+
|
67
|
+
return bytes
|
68
|
+
end
|
69
|
+
# ----------------------------------------------------------------------------
|
70
|
+
|
71
|
+
end # of class GenericTag
|
72
|
+
|
73
|
+
end # of module ID3
|
data/lib/id3/id3.rb
ADDED
@@ -0,0 +1,159 @@
|
|
1
|
+
################################################################################
|
2
|
+
# id3.rb Ruby Module for handling the following ID3-tag versions:
|
3
|
+
# ID3v1.0 , ID3v1.1, ID3v2.2.0, ID3v2.3.0, ID3v2.4.0
|
4
|
+
#
|
5
|
+
# Copyright (C) 2002 .. 2011 by Tilo Sloboda <firstname.lastname@google_email>
|
6
|
+
#
|
7
|
+
# created: 12 Oct 2002
|
8
|
+
# updated: Time-stamp: <Fri, 21 Oct 2011, 11:54:26 PDT tilo>
|
9
|
+
#
|
10
|
+
# Docs: http://www.id3.org/id3v2-00.txt
|
11
|
+
# http://www.id3.org/id3v2.3.0.txt
|
12
|
+
# http://www.id3.org/id3v2.4.0-changes.txt
|
13
|
+
# http://www.id3.org/id3v2.4.0-structure.txt
|
14
|
+
# http://www.id3.org/id3v2.4.0-frames.txt
|
15
|
+
#
|
16
|
+
# different versions of ID3 tags, support different fields.
|
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_comparison2.html
|
19
|
+
#
|
20
|
+
# PLEASE HELP:
|
21
|
+
#
|
22
|
+
# >>> Please contact me and email me the extracted ID3v2 tags, if you:
|
23
|
+
# >>> - if you have tags with exotic character encodings (exotic for me, not for you, obviously ;-) )
|
24
|
+
# >>> - if you find need support for any ID3v2 tags which are not yet supported by this library
|
25
|
+
# >>> (e.g. they are currently just parsed 'raw' and you need them fully parsed)
|
26
|
+
# >>> - if something terribly breaks
|
27
|
+
# >>>
|
28
|
+
# >>> You can find a small helper program in the examples folder, which extracts a ID3v2 tag from a file,
|
29
|
+
# >>> and saves it separately, so you can email it to me without emailing the whole audio file.
|
30
|
+
# >>>
|
31
|
+
# >>> THANK YOU FOR YOUR HELP!
|
32
|
+
#
|
33
|
+
# Non-ASCII encoded Strings:
|
34
|
+
#
|
35
|
+
# This library's main purpose is to unify access across different ID3-tag versions. So you don't have to worry
|
36
|
+
# about the changing names of the frames in different ID3-versions, and e.g. just access "AUTHOR" symbolically
|
37
|
+
# no matter if it's ID3 v1.0 v2.1.0 or v2.4.0. Think of this as a low-level library in that sense.
|
38
|
+
#
|
39
|
+
# Non-ASCII encodings are currently not really dealt with. For Strings which can be encoded differntly,
|
40
|
+
# you will see attributes like 'encoding' and 'text', where 'encoding' is a number representing the encoding,
|
41
|
+
# and the other attribute, e.g. 'text' or 'description', is the raw uninterpreted String.
|
42
|
+
#
|
43
|
+
# If your code requires to assign values to a ID3v2-frame which are foreign encoded Strings, you will need to make
|
44
|
+
# a small wrapper class on top of ID3::Frame which detects the encoding and properly saves it as a number.
|
45
|
+
# I'd love to add this -- but I don't have enough examples of ID3-tags in foreign languages. See: PLEASE HELP
|
46
|
+
#
|
47
|
+
# Limitations:
|
48
|
+
#
|
49
|
+
# - this library currently does not support the ID3v2.4 feature of having ID3v2 tags at the end of the file
|
50
|
+
# IMHO this doesn't make much sense in the age of streaming, and I haven't found examples for ths in any MP3-files.
|
51
|
+
# I think this is just one of the many unused "features" in the ID3v2 specifications ;-)
|
52
|
+
#
|
53
|
+
# - ID3v2 Chapters are not supported (see: Wikipedia)
|
54
|
+
#
|
55
|
+
# - ID3v1 extended tags are currently not supported (see: Wikipedia)
|
56
|
+
#
|
57
|
+
# License:
|
58
|
+
# Freely available under the terms of the OpenSource "Artistic License"
|
59
|
+
# in combination with the Addendum A (below)
|
60
|
+
#
|
61
|
+
# In case you did not get a copy of the license along with the software,
|
62
|
+
# it is also available at: http://www.unixgods.org/~tilo/artistic-license.html
|
63
|
+
#
|
64
|
+
# Addendum A:
|
65
|
+
# THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU!
|
66
|
+
# SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
67
|
+
# REPAIR OR CORRECTION.
|
68
|
+
#
|
69
|
+
# IN NO EVENT WILL THE COPYRIGHT HOLDERS BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL,
|
70
|
+
# SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY
|
71
|
+
# TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED
|
72
|
+
# INACCURATE OR USELESS OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM
|
73
|
+
# TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF THE COPYRIGHT HOLDERS OR OTHER PARTY HAS BEEN
|
74
|
+
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
75
|
+
#
|
76
|
+
#
|
77
|
+
# Author's Rant:
|
78
|
+
# The author of this ID3-library for Ruby is not responsible in any way for
|
79
|
+
# the awkward definition of the ID3-standards..
|
80
|
+
#
|
81
|
+
# You're lucky though that you can use this little library, rather than having
|
82
|
+
# to parse ID3v2 tags yourself! Trust me! At the first glance it doesn't seem
|
83
|
+
# to be so complicated, but the ID3v2 definitions are so convoluted and
|
84
|
+
# unnecessarily complicated, with so many useless frame-types, it's a pain to
|
85
|
+
# read the documents describing the ID3 V2.x standards.. with tiny bits of information
|
86
|
+
# strewn all accross the documents here and there.. and even worse to implement them.
|
87
|
+
#
|
88
|
+
# I don't know what these people were thinking... can we make it any more
|
89
|
+
# complicated than that?? ID3 version 2.4.0 tops everything! If this flag
|
90
|
+
# is set and it's a full moon, and an even weekday number, then do this..
|
91
|
+
# Outch!!! I assume that's why I don't find any 2.4.0 tags in any of my
|
92
|
+
# MP3-files... seems like noone is writing 2.4.0 tags... iTunes writes 2.3.0
|
93
|
+
#
|
94
|
+
################################################################################
|
95
|
+
# How does it work?
|
96
|
+
#
|
97
|
+
# Main concepts used:
|
98
|
+
#
|
99
|
+
# - Unification of ID3 Frames according to this nomenclature, using "pretty" names for frames:
|
100
|
+
# http://www.unixgods.org/~tilo/Ruby/ID3/docs/ID3_comparison2.html
|
101
|
+
#
|
102
|
+
# - String pack/unpack to parse and dump contents of ID3v2 frames;
|
103
|
+
# For each ID3v2 frame type, there is a specific list of attributes for that frame
|
104
|
+
# and a pack/unpack recipe associated with that frame type (see: FRAME_PARSER Hash)
|
105
|
+
#
|
106
|
+
# - if there is a ID3v2 frame that's not parsed yet, it's easy to add:
|
107
|
+
# - create a new entry for that frame's symbolic (pretty) name in FRAMETYPE2FRAMENAME Hash
|
108
|
+
# - make sure to delete that name from the "UNPARSED" category
|
109
|
+
# - add an entry to FRAME_PARSER Hash
|
110
|
+
# - note how these pre-defined Hashes are used in ID3::Frame class during parse() and dump()
|
111
|
+
#
|
112
|
+
# - Metaprogramming: when ID3v2 frames are instanciated when they are read,
|
113
|
+
# we define parse and dump methods individually, using above pack/unpack recipes
|
114
|
+
# (check the two lines which use ID3::FRAME_PARSER to better understand the internal mechanics)
|
115
|
+
#
|
116
|
+
# - After the ID3v2 frames are parsed, they are Hashes; the keys are the attributes defined in FRAME_PARSER,
|
117
|
+
# the values are the extracted data from the ID3v2 tag.
|
118
|
+
#
|
119
|
+
################################################################################
|
120
|
+
#--
|
121
|
+
# TO DO:
|
122
|
+
#
|
123
|
+
# - haven't touched the code in a very long time..
|
124
|
+
# - I should write a general write-up and explanation on how to use the classes
|
125
|
+
# - I should write a general write-up to explain the metaprogramming ;)
|
126
|
+
#
|
127
|
+
# - they really changed all the IO calls in Ruby 1.9 -- how painful!!
|
128
|
+
# I need to make some wrappers, to handle this nicely in both Ruby 1.9 and 1.8
|
129
|
+
#
|
130
|
+
# - should probably use IO#sysopen , IO#sysseek , IO#sysread , IO#syswrite for low-level i/o
|
131
|
+
# - files should be opened with the 'b' option - to tell Ruby 1.9 to open them in binary mode
|
132
|
+
#
|
133
|
+
# - Note: the external representation for non-printable characters in strings is now hex, not octal
|
134
|
+
# - should use sha1 instead of md5
|
135
|
+
# - some functionality , like has_id3...tag? and is_mp3_file? should extend class File instead of being an ID3 module method
|
136
|
+
# - class AudioFile could extend class IO or File - hmm, not sure
|
137
|
+
# - class RestrictedOrderedHash vs OrderedHash vs Hash ...?? can we just do this with the ordered hash in 1.9?
|
138
|
+
# should probably at least inherit RestrictedOrderedHash < ActiveSupport::OrderedHash
|
139
|
+
#
|
140
|
+
# - tripple-check if the semantics of pack/unpack has changed between 1.8 and 1.9
|
141
|
+
# - hexdump definitely barfs in Ruby 1.9 -- needs fixing
|
142
|
+
#
|
143
|
+
# - check out ruby-uuid on how he manipulates raw bytes.. looks like it is Ruby 1.9 compatible.. .ord .char .bytes
|
144
|
+
#
|
145
|
+
# - this needs some serious refactoring..
|
146
|
+
#++
|
147
|
+
|
148
|
+
|
149
|
+
# ==============================================================================
|
150
|
+
|
151
|
+
|
152
|
+
|
153
|
+
# ==============================================================================
|
154
|
+
|
155
|
+
module ID3
|
156
|
+
|
157
|
+
# ... moved everything into separate files
|
158
|
+
|
159
|
+
end # of module ID3
|
@@ -0,0 +1,44 @@
|
|
1
|
+
#
|
2
|
+
# EXTENSIONS to Class IO (included in File)
|
3
|
+
#
|
4
|
+
|
5
|
+
# if you have a (partial) MP3-file stored in a File or IO object, you can check if it contains ID3 tags
|
6
|
+
# NOTE: file needs to be opened in binary mode! 'rb:binary'
|
7
|
+
|
8
|
+
class IO
|
9
|
+
def id3_versions
|
10
|
+
[ hasID3v1tag? ,hasID3v2tag? ].compact # returns an Array of version numbers
|
11
|
+
end
|
12
|
+
|
13
|
+
def hasID3tag?
|
14
|
+
hasID3v2tag? || hasID3v1tag? ? true : false # returns true or false
|
15
|
+
end
|
16
|
+
|
17
|
+
def hasID3v1tag?
|
18
|
+
seek(-ID3::ID3v1tagSize, IO::SEEK_END)
|
19
|
+
if (read(3) == 'TAG')
|
20
|
+
seek(-ID3::ID3v1tagSize + ID3::ID3v1versionbyte, IO::SEEK_END)
|
21
|
+
return get_byte == 0 ? "1.0" : "1.1"
|
22
|
+
else
|
23
|
+
return nil
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def ID3v2_tag_size
|
28
|
+
rewind
|
29
|
+
return 0 if (read(3) != 'ID3')
|
30
|
+
read_bytes(3) # skip version and flags
|
31
|
+
return ID3::ID3v2headerSize + ID3.unmungeSize( read_bytes(4) )
|
32
|
+
end
|
33
|
+
|
34
|
+
def hasID3v2tag?
|
35
|
+
rewind
|
36
|
+
if (read(3) == "ID3")
|
37
|
+
major = get_byte
|
38
|
+
minor = get_byte
|
39
|
+
return version = "2." + major.to_s + '.' + minor.to_s
|
40
|
+
else
|
41
|
+
return nil
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|