ruby-mp3info 0.5.1 → 0.6
Sign up to get free protection for your applications and to get access to all the features.
- data/{CHANGELOG → History.txt} +11 -15
- data/Manifest.txt +9 -0
- data/README.txt +78 -0
- data/Rakefile +18 -0
- data/install.rb +1022 -0
- data/lib/mp3info.rb +11 -9
- data/lib/mp3info/id3v2.rb +51 -32
- data/{test.rb → test/test_ruby-mp3info.rb} +61 -32
- metadata +59 -41
- data/EXAMPLES +0 -43
- data/README +0 -28
data/lib/mp3info.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# $Id: mp3info.rb
|
1
|
+
# $Id: mp3info.rb 64 2008-02-24 02:11:57Z moumar $
|
2
2
|
# License:: Ruby
|
3
3
|
# Author:: Guillaume Pierronnet (mailto:moumar_AT__rubyforge_DOT_org)
|
4
4
|
# Website:: http://ruby-mp3info.rubyforge.org/
|
@@ -18,7 +18,7 @@ end
|
|
18
18
|
|
19
19
|
class Mp3Info
|
20
20
|
|
21
|
-
VERSION = "0.
|
21
|
+
VERSION = "0.6"
|
22
22
|
|
23
23
|
LAYER = [ nil, 3, 2, 1]
|
24
24
|
BITRATE = [
|
@@ -161,14 +161,16 @@ class Mp3Info
|
|
161
161
|
end
|
162
162
|
end
|
163
163
|
|
164
|
-
# Instantiate
|
165
|
-
|
164
|
+
# Instantiate Mp3Info object with name +filename+ and an
|
165
|
+
# options hash for ID3v2#new underlying object
|
166
|
+
def initialize(filename, id3v2_options = {})
|
166
167
|
$stderr.puts("#{self.class}::new() does not take block; use #{self.class}::open() instead") if block_given?
|
167
168
|
@filename = filename
|
169
|
+
@id3v2_options = id3v2_options
|
168
170
|
reload
|
169
171
|
end
|
170
172
|
|
171
|
-
# reload the file from disk
|
173
|
+
# reload (or load for the first time) the file from disk
|
172
174
|
def reload
|
173
175
|
raise(Mp3InfoError, "empty file") unless File.size?(@filename)
|
174
176
|
@hastag1 = false
|
@@ -176,7 +178,7 @@ class Mp3Info
|
|
176
178
|
@tag1 = {}
|
177
179
|
@tag1.extend(HashKeys)
|
178
180
|
|
179
|
-
@tag2 = ID3v2.new
|
181
|
+
@tag2 = ID3v2.new(@id3v2_options)
|
180
182
|
|
181
183
|
@file = File.new(filename, "rb")
|
182
184
|
@file.extend(Mp3FileMethods)
|
@@ -283,8 +285,8 @@ class Mp3Info
|
|
283
285
|
end
|
284
286
|
|
285
287
|
# "block version" of Mp3Info::new()
|
286
|
-
def self.open(
|
287
|
-
m = self.new(
|
288
|
+
def self.open(*params)
|
289
|
+
m = self.new(*params)
|
288
290
|
ret = nil
|
289
291
|
if block_given?
|
290
292
|
begin
|
@@ -307,6 +309,7 @@ class Mp3Info
|
|
307
309
|
self
|
308
310
|
end
|
309
311
|
|
312
|
+
# Remove id3v2 from mp3
|
310
313
|
def removetag2
|
311
314
|
@tag2.clear
|
312
315
|
self
|
@@ -397,7 +400,6 @@ class Mp3Info
|
|
397
400
|
tempfile_name = @filename + ".tmp"
|
398
401
|
File.open(tempfile_name, "wb") do |tempfile|
|
399
402
|
unless @tag2.empty?
|
400
|
-
tempfile.write("ID3")
|
401
403
|
tempfile.write(@tag2.to_bin)
|
402
404
|
end
|
403
405
|
|
data/lib/mp3info/id3v2.rb
CHANGED
@@ -1,8 +1,11 @@
|
|
1
1
|
require "mp3info/extension_modules"
|
2
|
+
require "iconv"
|
2
3
|
|
3
4
|
# This class is not intended to be used directly
|
4
5
|
class ID3v2 < DelegateClass(Hash)
|
5
|
-
|
6
|
+
|
7
|
+
# Major version used when writing tags
|
8
|
+
WRITE_VERSION = 3
|
6
9
|
|
7
10
|
TAGS = {
|
8
11
|
"AENC" => "Audio encryption",
|
@@ -81,22 +84,33 @@ class ID3v2 < DelegateClass(Hash)
|
|
81
84
|
"WXXX" => "User defined URL link frame"
|
82
85
|
}
|
83
86
|
|
87
|
+
# See id3v2.4.0-structure document, at section 4.
|
88
|
+
TEXT_ENCODINGS = ["iso-8859-1", "utf-16", "utf-16be", "utf-8"]
|
89
|
+
|
84
90
|
include Mp3Info::HashKeys
|
85
91
|
|
86
92
|
attr_reader :io_position
|
87
93
|
|
88
|
-
#
|
89
|
-
#
|
90
|
-
# :+encoding+:
|
94
|
+
# :+lang+: for writing comments
|
95
|
+
#
|
96
|
+
# :+encoding+: one of the string of +TEXT_ENCODINGS+,
|
97
|
+
# used as a source and destination encoding respectively
|
98
|
+
# for read and write tag2 values.
|
91
99
|
attr_reader :options
|
92
100
|
|
93
101
|
def initialize(options = {})
|
94
102
|
@options = {
|
95
|
-
:lang => "ENG",
|
96
|
-
:encoding =>
|
103
|
+
:lang => "ENG",
|
104
|
+
:encoding => "iso-8859-1"
|
97
105
|
}
|
106
|
+
|
98
107
|
@options.update(options)
|
108
|
+
@text_encoding_index = TEXT_ENCODINGS.index(@options[:encoding])
|
99
109
|
|
110
|
+
unless @text_encoding_index
|
111
|
+
raise(ArgumentError, "bad id3v2 text encoding specified")
|
112
|
+
end
|
113
|
+
|
100
114
|
@hash = {}
|
101
115
|
#TAGS.keys.each { |k| @hash[k] = nil }
|
102
116
|
@hash_orig = {}
|
@@ -105,19 +119,22 @@ class ID3v2 < DelegateClass(Hash)
|
|
105
119
|
@version_maj = @version_min = nil
|
106
120
|
end
|
107
121
|
|
122
|
+
# does this tag correctly read ?
|
108
123
|
def valid?
|
109
124
|
@valid
|
110
125
|
end
|
111
126
|
|
127
|
+
# does this tag has been changed ?
|
112
128
|
def changed?
|
113
129
|
@hash_orig != @hash
|
114
130
|
end
|
115
131
|
|
132
|
+
# full version of this tag (like 2.3.0)
|
116
133
|
def version
|
117
134
|
"2.#{@version_maj}.#{@version_min}"
|
118
135
|
end
|
119
136
|
|
120
|
-
### gets id3v2 tag information from io
|
137
|
+
### gets id3v2 tag information from io object (must support #seek() method)
|
121
138
|
def from_io(io)
|
122
139
|
@io = io
|
123
140
|
version_maj, version_min, flags = @io.read(3).unpack("CCB4")
|
@@ -142,6 +159,7 @@ class ID3v2 < DelegateClass(Hash)
|
|
142
159
|
# we should now have io sitting at the first MPEG frame
|
143
160
|
end
|
144
161
|
|
162
|
+
# dump tag for writing. Version is always 2.#{WRITE_VERSION}.0.
|
145
163
|
def to_bin
|
146
164
|
#TODO handle of @tag2[TLEN"]
|
147
165
|
#TODO add of crc
|
@@ -151,7 +169,7 @@ class ID3v2 < DelegateClass(Hash)
|
|
151
169
|
@hash.each do |k, v|
|
152
170
|
next unless v
|
153
171
|
next if v.respond_to?("empty?") and v.empty?
|
154
|
-
data = encode_tag(k, v.to_s)
|
172
|
+
data = encode_tag(k, v.to_s, WRITE_VERSION)
|
155
173
|
#data << "\x00"*2 #End of tag
|
156
174
|
|
157
175
|
tag << k[0,4] #4 characte max for a tag's key
|
@@ -161,9 +179,9 @@ class ID3v2 < DelegateClass(Hash)
|
|
161
179
|
tag << data
|
162
180
|
end
|
163
181
|
|
164
|
-
tag_str = ""
|
182
|
+
tag_str = "ID3"
|
165
183
|
#version_maj, version_min, unsync, ext_header, experimental, footer
|
166
|
-
tag_str << [
|
184
|
+
tag_str << [ WRITE_VERSION, 0, "0000" ].pack("CCB4")
|
167
185
|
tag_str << to_syncsafe(tag.size)
|
168
186
|
tag_str << tag
|
169
187
|
p tag_str if $DEBUG
|
@@ -172,21 +190,26 @@ class ID3v2 < DelegateClass(Hash)
|
|
172
190
|
|
173
191
|
private
|
174
192
|
|
193
|
+
def encode_tag(name, value, version)
|
194
|
+
puts "encode_tag(#{name.inspect}, #{value.inspect}, #{version})" if $DEBUG
|
195
|
+
|
196
|
+
text_encoding_index = @text_encoding_index
|
197
|
+
if version == 3
|
198
|
+
# in id3v2.3 tags, there is only 2 encodings possible
|
199
|
+
transcoded_value = value
|
200
|
+
if text_encoding_index >= 2
|
201
|
+
transcoded_value = Iconv.iconv(TEXT_ENCODINGS[1], TEXT_ENCODINGS[text_encoding_index], value)[0]
|
202
|
+
text_encoding_index = 1
|
203
|
+
end
|
204
|
+
end
|
175
205
|
|
176
|
-
def encode_tag(name, value)
|
177
|
-
puts "encode_tag(#{name.inspect}, #{value.inspect})" if $DEBUG
|
178
206
|
case name
|
179
207
|
when "COMM"
|
180
|
-
[
|
181
|
-
when /^W/ # URL link frames
|
208
|
+
[ text_encoding_index , @options[:lang], 0, transcoded_value ].pack("ca3ca*")
|
209
|
+
when "UFID", /^W/ # URL link frames
|
182
210
|
value
|
183
211
|
else
|
184
|
-
|
185
|
-
"\x00"+value
|
186
|
-
else
|
187
|
-
"\x01"+value #Iconv.iconv("UNICODE", "ISO-8859-1", value)[0]
|
188
|
-
end
|
189
|
-
#data << "\x00"
|
212
|
+
text_encoding_index.chr + transcoded_value
|
190
213
|
end
|
191
214
|
end
|
192
215
|
|
@@ -197,22 +220,18 @@ class ID3v2 < DelegateClass(Hash)
|
|
197
220
|
#FIXME improve this
|
198
221
|
encoding, lang, str = value.unpack("ca3a*")
|
199
222
|
out = value.split(0.chr).last
|
200
|
-
when /^W/ # URL link frames
|
223
|
+
when "UFID", /^W/ # URL link frames
|
201
224
|
out = value
|
202
225
|
else
|
203
|
-
encoding = value[0] # language encoding
|
226
|
+
encoding = value[0] # language encoding (see TEXT_ENCODINGS constant)
|
204
227
|
out = value[1..-1]
|
205
228
|
end
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
out[
|
212
|
-
begin
|
213
|
-
out = Iconv.iconv("ISO-8859-1", "UTF-16", out)[0]
|
214
|
-
rescue Iconv::IllegalSequence, Iconv::InvalidCharacter
|
215
|
-
end
|
229
|
+
|
230
|
+
printf("encoding: %d, value: %s\n", encoding, value.inspect) if $DEBUG
|
231
|
+
# we need to convert the string in order to match
|
232
|
+
# the requested encoding
|
233
|
+
if encoding && encoding != @text_encoding_index
|
234
|
+
out = Iconv.iconv(@options[:encoding], TEXT_ENCODINGS[encoding], out)[0]
|
216
235
|
end
|
217
236
|
out
|
218
237
|
end
|
@@ -21,10 +21,6 @@ class Mp3InfoTest < Test::Unit::TestCase
|
|
21
21
|
"TRCK" => "tracknum"
|
22
22
|
}
|
23
23
|
|
24
|
-
# aliasing to allow testing with old versions of Test::Unit
|
25
|
-
alias set_up setup
|
26
|
-
alias tear_down teardown
|
27
|
-
|
28
24
|
def setup
|
29
25
|
# Command to create a dummy MP3
|
30
26
|
# dd if=/dev/zero bs=1024 count=15 | lame --preset cbr 128 -r -s 44.1 --bitwidth 16 - - | ruby -rbase64 -e 'print Base64.encode64($stdin.read)'
|
@@ -94,7 +90,6 @@ EOF
|
|
94
90
|
FileUtils.rm_f(TEMP_FILE)
|
95
91
|
end
|
96
92
|
|
97
|
-
|
98
93
|
def test_to_s
|
99
94
|
Mp3Info.open(TEMP_FILE) { |info| assert(info.to_s.is_a?(String)) }
|
100
95
|
end
|
@@ -104,15 +99,15 @@ EOF
|
|
104
99
|
str = "0"*1024*1024
|
105
100
|
f.write(str)
|
106
101
|
end
|
107
|
-
assert_raises(Mp3InfoError)
|
102
|
+
assert_raises(Mp3InfoError) do
|
108
103
|
mp3 = Mp3Info.new(TEMP_FILE)
|
109
|
-
|
104
|
+
end
|
110
105
|
end
|
111
106
|
|
112
107
|
def test_is_an_mp3
|
113
|
-
assert_nothing_raised
|
108
|
+
assert_nothing_raised do
|
114
109
|
Mp3Info.new(TEMP_FILE).close
|
115
|
-
|
110
|
+
end
|
116
111
|
end
|
117
112
|
|
118
113
|
def test_detected_info
|
@@ -129,7 +124,7 @@ EOF
|
|
129
124
|
end
|
130
125
|
|
131
126
|
def test_vbr_mp3_length
|
132
|
-
temp_file_vbr = File.join(File.dirname($0), "
|
127
|
+
temp_file_vbr = File.join(File.dirname($0), "test_vbr_length.mp3")
|
133
128
|
|
134
129
|
unless File.exists?(temp_file_vbr)
|
135
130
|
tempfile = Tempfile.new("ruby-mp3info_test")
|
@@ -235,7 +230,7 @@ EOF
|
|
235
230
|
|
236
231
|
def test_id3v2_version
|
237
232
|
written_tag = write_temp_file(BASIC_TAG2)
|
238
|
-
assert_equal( "2.
|
233
|
+
assert_equal( "2.#{ID3v2::WRITE_VERSION}.0", written_tag.version )
|
239
234
|
end
|
240
235
|
|
241
236
|
def test_id3v2_methods
|
@@ -254,6 +249,30 @@ EOF
|
|
254
249
|
id3v2_prog_test(BASIC_TAG2, w)
|
255
250
|
end
|
256
251
|
|
252
|
+
#test the tag with the "id3v2" program
|
253
|
+
def id3v2_prog_test(tag, written_tag)
|
254
|
+
return if RUBY_PLATFORM =~ /win32/
|
255
|
+
return if `which id3v2`.empty?
|
256
|
+
start = false
|
257
|
+
id3v2_output = {}
|
258
|
+
`id3v2 -l #{TEMP_FILE}`.each do |line|
|
259
|
+
if line =~ /^id3v2 tag info/
|
260
|
+
start = true
|
261
|
+
next
|
262
|
+
end
|
263
|
+
next unless start
|
264
|
+
k, v = /^(.{4}) \(.+\): (.+)$/.match(line)[1,2]
|
265
|
+
case k
|
266
|
+
#COMM (Comments): ()[spa]: fmg
|
267
|
+
when "COMM"
|
268
|
+
v.sub!(/\(\)\[.{3}\]: (.+)/, '\1')
|
269
|
+
end
|
270
|
+
id3v2_output[k] = v
|
271
|
+
end
|
272
|
+
|
273
|
+
assert_equal( id3v2_output, written_tag, "id3v2 program output doesn't match")
|
274
|
+
end
|
275
|
+
|
257
276
|
def test_id3v2_trash
|
258
277
|
end
|
259
278
|
|
@@ -319,7 +338,7 @@ EOF
|
|
319
338
|
# end
|
320
339
|
|
321
340
|
def test_good_parsing_of_a_pathname
|
322
|
-
fn = "Freak On `(Stone
|
341
|
+
fn = "Freak On `(Stone´s Club Mix).mp3"
|
323
342
|
File.rename(TEMP_FILE, fn)
|
324
343
|
begin
|
325
344
|
mp3 = Mp3Info.new(fn)
|
@@ -333,28 +352,38 @@ EOF
|
|
333
352
|
end
|
334
353
|
end
|
335
354
|
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
if line =~ /^id3v2 tag info/
|
344
|
-
start = true
|
345
|
-
next
|
346
|
-
end
|
347
|
-
next unless start
|
348
|
-
k, v = /^(.{4}) \(.+\): (.+)$/.match(line)[1,2]
|
349
|
-
case k
|
350
|
-
#COMM (Comments): ()[spa]: fmg
|
351
|
-
when "COMM"
|
352
|
-
v.sub!(/\(\)\[.{3}\]: (.+)/, '\1')
|
353
|
-
end
|
354
|
-
id3v2_output[k] = v
|
355
|
+
def test_validity_of_id3v2_options
|
356
|
+
info = Mp3Info.new(TEMP_FILE)
|
357
|
+
expected_hash = { :lang => "ENG", :encoding => "iso-8859-1" }
|
358
|
+
assert_equal( expected_hash, info.tag2.options)
|
359
|
+
|
360
|
+
assert_raises(ArgumentError) do
|
361
|
+
Mp3Info.new(TEMP_FILE, :encoding => "bad encoding")
|
355
362
|
end
|
363
|
+
end
|
356
364
|
|
357
|
-
|
365
|
+
def test_encoding_read
|
366
|
+
Mp3Info.open(TEMP_FILE) do |mp3|
|
367
|
+
mp3.tag2['TEST'] = "all\xe9"
|
368
|
+
end
|
369
|
+
|
370
|
+
Mp3Info.open(TEMP_FILE, :encoding => "utf-8") do |mp3|
|
371
|
+
assert_equal "all\xc3\xa9", mp3.tag2['TEST']
|
372
|
+
end
|
373
|
+
|
374
|
+
Mp3Info.open(TEMP_FILE, :encoding => "iso-8859-1") do |mp3|
|
375
|
+
assert_equal "all\xe9", mp3.tag2['TEST']
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
379
|
+
def test_encoding_write
|
380
|
+
Mp3Info.open(TEMP_FILE, :encoding => 'utf-8') do |mp3|
|
381
|
+
mp3.tag2['TEST'] = "all\xc3\xa9"
|
382
|
+
end
|
383
|
+
|
384
|
+
Mp3Info.open(TEMP_FILE, :encoding => "iso-8859-1") do |mp3|
|
385
|
+
assert_equal "all\xe9", mp3.tag2['TEST']
|
386
|
+
end
|
358
387
|
end
|
359
388
|
|
360
389
|
def write_temp_file(tag)
|
metadata
CHANGED
@@ -1,54 +1,72 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
|
-
rubygems_version: 0.9.2
|
3
|
-
specification_version: 1
|
4
2
|
name: ruby-mp3info
|
5
3
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.
|
7
|
-
date: 2007-09-10 00:00:00 +02:00
|
8
|
-
summary: ruby-mp3info is a pure-ruby library to retrieve low level informations on mp3 files and manipulate id3v1 and id3v2 tags
|
9
|
-
require_paths:
|
10
|
-
- lib
|
11
|
-
email: moumar@rubyforge.org
|
12
|
-
homepage: http://ruby-mp3info.rubyforge.org
|
13
|
-
rubyforge_project: ruby-mp3info
|
14
|
-
description:
|
15
|
-
autorequire: mp3info
|
16
|
-
default_executable:
|
17
|
-
bindir: bin
|
18
|
-
has_rdoc: true
|
19
|
-
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
-
requirements:
|
21
|
-
- - ">"
|
22
|
-
- !ruby/object:Gem::Version
|
23
|
-
version: 0.0.0
|
24
|
-
version:
|
4
|
+
version: "0.6"
|
25
5
|
platform: ruby
|
26
|
-
signing_key:
|
27
|
-
cert_chain:
|
28
|
-
post_install_message:
|
29
6
|
authors:
|
30
7
|
- Guillaume Pierronnet
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
- EXAMPLES
|
35
|
-
- test.rb
|
36
|
-
- lib/mp3info.rb
|
37
|
-
- lib/mp3info/extension_modules.rb
|
38
|
-
- lib/mp3info/id3v2.rb
|
39
|
-
test_files: []
|
40
|
-
|
41
|
-
rdoc_options: []
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
42
11
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
-
|
12
|
+
date: 2008-02-24 00:00:00 +01:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: hoe
|
17
|
+
version_requirement:
|
18
|
+
version_requirements: !ruby/object:Gem::Requirement
|
19
|
+
requirements:
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 1.5.0
|
23
|
+
version:
|
24
|
+
description: "* written in pure ruby * read low-level informations like bitrate, length, samplerate, etc... * read, write, remove id3v1 and id3v2 tags * only 2.3 version is supported for writings id3v2 tags == SYNOPSIS: a good exercise is to read the test.rb to understand how the library works deeper require \"mp3info\" # read and display infos & tags Mp3Info.open(\"myfile.mp3\") do |mp3info| puts mp3info end # read/write tag1 and tag2 with Mp3Info#tag attribute # when reading tag2 have priority over tag1 # when writing, each tag is written. Mp3Info.open(\"myfile.mp3\") do |mp3| puts mp3.tag.title puts mp3.tag.artist puts mp3.tag.album puts mp3.tag.tracknum mp3.tag.title = \"track title\" mp3.tag.artist = \"artist name\" end Mp3Info.open(\"myfile.mp3\") do |mp3| # you can access four letter v2 tags like this puts mp3.tag2.TIT2 mp3.tag2.TIT2 = \"new TIT2\" # or like that mp3.tag2[\"TIT2\"] # at this time, only COMM tag is processed after reading and before writing # according to ID3v2#options hash mp3.tag2.options[:lang] = \"FRE\" mp3.tag2.COMM = \"my comment in french, correctly handled when reading and writing\" end"
|
25
|
+
email: moumar@rubyforge.org
|
47
26
|
executables: []
|
48
27
|
|
49
28
|
extensions: []
|
50
29
|
|
30
|
+
extra_rdoc_files:
|
31
|
+
- History.txt
|
32
|
+
- Manifest.txt
|
33
|
+
- README.txt
|
34
|
+
files:
|
35
|
+
- History.txt
|
36
|
+
- Manifest.txt
|
37
|
+
- README.txt
|
38
|
+
- Rakefile
|
39
|
+
- install.rb
|
40
|
+
- lib/mp3info.rb
|
41
|
+
- lib/mp3info/extension_modules.rb
|
42
|
+
- lib/mp3info/id3v2.rb
|
43
|
+
- test/test_ruby-mp3info.rb
|
44
|
+
has_rdoc: true
|
45
|
+
homepage:
|
46
|
+
post_install_message:
|
47
|
+
rdoc_options:
|
48
|
+
- --main
|
49
|
+
- README.txt
|
50
|
+
require_paths:
|
51
|
+
- lib
|
52
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: "0"
|
57
|
+
version:
|
58
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: "0"
|
63
|
+
version:
|
51
64
|
requirements: []
|
52
65
|
|
53
|
-
|
54
|
-
|
66
|
+
rubyforge_project: ruby-mp3info
|
67
|
+
rubygems_version: 1.0.1
|
68
|
+
signing_key:
|
69
|
+
specification_version: 2
|
70
|
+
summary: ruby-mp3info is a pure-ruby library to retrieve low level informations on mp3 files and manipulate id3v1 and id3v2 tags
|
71
|
+
test_files:
|
72
|
+
- test/test_ruby-mp3info.rb
|