id3 0.5.0 → 1.0.0.pre4
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 +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
|