flacinfo-rb 0.1
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/README +35 -0
- data/lib/flacinfo.rb +410 -0
- metadata +46 -0
data/README
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
:: flacinfo-rb ::
|
2
|
+
Author: Darren Kirby
|
3
|
+
mailto:bulliver@badcomputer.org
|
4
|
+
License: Ruby
|
5
|
+
|
6
|
+
= Quick API docs =
|
7
|
+
|
8
|
+
== Initializing ==
|
9
|
+
|
10
|
+
require 'flacinfo'
|
11
|
+
foo = FlacInfo.new("someSong.flac")
|
12
|
+
|
13
|
+
== Public attributes ==
|
14
|
+
|
15
|
+
streaminfo :: hash of STREAMINFO block metadata
|
16
|
+
seektable :: hash of arrays of seek points
|
17
|
+
comment :: array of VORBIS COMMENT block metadata
|
18
|
+
tags :: user-friendly hash of Vorbis comment metadata key=value pairs
|
19
|
+
application :: hash of APPLICATION block metadata
|
20
|
+
padding :: hash of PADDING block metadata (currently just ['block_size'])
|
21
|
+
cuesheet :: hash of CUESHEET block metadata (currently just ['block_size'])
|
22
|
+
flac_file :: hash of APPLICATION Id 0x41544348 (Flac File) metadata if present
|
23
|
+
|
24
|
+
== Public methods ==
|
25
|
+
|
26
|
+
print_streaminfo :: pretty-print streaminfo hash
|
27
|
+
hastag('str') :: returns true if tags['str'] exists
|
28
|
+
print_tags :: pretty-print tags hash
|
29
|
+
print_seektable :: pretty-print seektable hash
|
30
|
+
meta_flac :: prints all META BLOCKS. (Mostly) equivelant to 'metaflac --list'
|
31
|
+
raw_data_dump(?) :: if passed a filename it will dump flac_file['raw_data'] to that file,
|
32
|
+
otherwise it will dump it to the console (even if binary!)
|
33
|
+
|
34
|
+
For more/different documentation see http://badcomputer.org/unix/code/flacinfo/
|
35
|
+
|
data/lib/flacinfo.rb
ADDED
@@ -0,0 +1,410 @@
|
|
1
|
+
# = Description
|
2
|
+
#
|
3
|
+
# flacinfo-rb gives you access to low level information on Flac files.
|
4
|
+
# * It parses stream information (STREAMINFO).
|
5
|
+
# * It parses Vorbis comments (VORBIS_COMMENT).
|
6
|
+
# * It parses the seek table (SEEKTABLE).
|
7
|
+
# * It parses the 'application metadata block' (APPLICATION).
|
8
|
+
# * If (APPLICATION) is ID 0x41544348 (Flac File)
|
9
|
+
# then we can parse that too.
|
10
|
+
# * It recognizes (but does not yet parse) the cue sheet (CUESHEET TRACK).
|
11
|
+
#
|
12
|
+
# = Copyright and Disclaimer
|
13
|
+
# Copyright:: (c) 2006 Darren Kirby
|
14
|
+
# FlacInfo is free software.
|
15
|
+
# No warranty is provided and the author cannot accept responsibility
|
16
|
+
# for lost or damaged files.
|
17
|
+
# License:: Ruby
|
18
|
+
# Author:: Darren Kirby (mailto:bulliver@badcomputer.org)
|
19
|
+
# Website:: http://badcomputer.org/unix/code/flacinfo/
|
20
|
+
#
|
21
|
+
# = More information
|
22
|
+
#
|
23
|
+
# * There is an example irb session that shows typical usage at
|
24
|
+
# http://badcomputer.org/unix/code/flacinfo/
|
25
|
+
# * The Flac spec is at:
|
26
|
+
# http://flac.sourceforge.net/format.html
|
27
|
+
# * The Vorbis Comment spec is at:
|
28
|
+
# http://www.xiph.org/vorbis/doc/v-comment.html
|
29
|
+
|
30
|
+
|
31
|
+
# FlacInfoError is raised when an error occurs parsing the Flac file.
|
32
|
+
# It will print an additional error string stating where the error occured.
|
33
|
+
class FlacInfoError < StandardError
|
34
|
+
end
|
35
|
+
|
36
|
+
# Note: STREAMINFO is the only block guaranteed to be present.
|
37
|
+
# Other attributes will be present, but empty if the associated block is not present in the Flac file.
|
38
|
+
class FlacInfo
|
39
|
+
|
40
|
+
# Hash of values extracted from the STREAMINFO block. Keys are 'samplerate', 'bits_per_sample', 'total_samples'
|
41
|
+
# 'channels', 'minimum_frame', 'maximum_frame', 'minimum_block', 'maximum_block', 'md5', and 'block_size'
|
42
|
+
attr_reader :streaminfo
|
43
|
+
|
44
|
+
# Hash of values extracted from the SEEKTABLE block. Keys are 'seek_points', 'block_size' and 'points'.
|
45
|
+
# 'points' is another hash whose keys start at 0 and end at ('seek_points' - 1). Each "seektable['points'][n]" hash
|
46
|
+
# contains an array whose values are [sample number, stream offset, number of frame samples] of each seek point.
|
47
|
+
attr_reader :seektable
|
48
|
+
|
49
|
+
# Array of "name=value" strings extracted from the VORBIS_COMMENT block. This is just the contents, metadata is in 'tags'.
|
50
|
+
attr_reader :comment
|
51
|
+
|
52
|
+
# Hash of the 'comment' values separated into "key => value" pairs as well as 'vendor_tag' and 'block_size'.
|
53
|
+
attr_reader :tags
|
54
|
+
|
55
|
+
# Hash of values extracted from the APPLICATION block. Keys are 'name', 'ID', and 'block_size'.
|
56
|
+
attr_reader :application
|
57
|
+
|
58
|
+
# Hash of values extracted from the PADDING block. Just one key: 'block_size'.
|
59
|
+
attr_reader :padding
|
60
|
+
|
61
|
+
# Hash of values extracted from the CUESHEET block. Just one key: 'block_size'.
|
62
|
+
attr_reader :cuesheet
|
63
|
+
|
64
|
+
# Hash of values extracted from an APPLICATION block if it is type 0x41544348 (Flac File).
|
65
|
+
# Keys are 'description', 'mime_type', and 'raw_data'.
|
66
|
+
attr_reader :flac_file
|
67
|
+
|
68
|
+
# FlacInfo is the main class for parsing Flac files
|
69
|
+
#
|
70
|
+
# :call-seq:
|
71
|
+
# FlacInfo.new(file) -> FlacInfo instance
|
72
|
+
#
|
73
|
+
def initialize(filename)
|
74
|
+
@filename = filename
|
75
|
+
parse_flac_meta_blocks
|
76
|
+
end
|
77
|
+
|
78
|
+
# Returns true if @tags[<string>] has a value, false otherwise.
|
79
|
+
#
|
80
|
+
# :call-seq:
|
81
|
+
# FlacInfo.hastag?(tag) -> bool
|
82
|
+
#
|
83
|
+
def hastag?(tag)
|
84
|
+
@tags["#{tag}"] ? true : false
|
85
|
+
end
|
86
|
+
|
87
|
+
# Prettyprint comment hash.
|
88
|
+
#
|
89
|
+
# :call-seq:
|
90
|
+
# FlacInfo.print_tags -> nil
|
91
|
+
#
|
92
|
+
def print_tags
|
93
|
+
@tags.each_pair { |key,val| puts "#{key}: #{val}" }
|
94
|
+
nil
|
95
|
+
end
|
96
|
+
|
97
|
+
# Prettyprint streaminfo hash
|
98
|
+
#
|
99
|
+
# :call-seq:
|
100
|
+
# FlacInfo.print_streaminfo -> nil
|
101
|
+
#
|
102
|
+
def print_streaminfo
|
103
|
+
@streaminfo.each_pair { |key,val| puts "#{key}: #{val}" }
|
104
|
+
nil
|
105
|
+
end
|
106
|
+
|
107
|
+
# Prettyprint the seektable
|
108
|
+
#
|
109
|
+
# :call-seq:
|
110
|
+
# FlacInfo.print_seektable -> nil
|
111
|
+
#
|
112
|
+
def print_seektable
|
113
|
+
puts " seek points: #{@seektable['seek_points']}"
|
114
|
+
n = 0
|
115
|
+
@seektable['seek_points'].times do
|
116
|
+
print " point #{n}: sample number: #{@seektable['points'][n][0]}, "
|
117
|
+
print "stream offset: #{@seektable['points'][n][1]}, "
|
118
|
+
print "frame samples: #{@seektable['points'][n][2]}\n"
|
119
|
+
n += 1
|
120
|
+
end
|
121
|
+
nil
|
122
|
+
end
|
123
|
+
|
124
|
+
# This method produces output (mostly) identical to 'metaflac --list'
|
125
|
+
#
|
126
|
+
# :call-seq:
|
127
|
+
# FlacInfo.meta_flac -> nil
|
128
|
+
#
|
129
|
+
def meta_flac
|
130
|
+
n = 0
|
131
|
+
@metadata_blocks.each do |block|
|
132
|
+
puts "METADATA block ##{n}"
|
133
|
+
puts " type: #{block[1]} (#{block[0].upcase})"
|
134
|
+
puts " is last: #{block[2] == 0 ? "false" : "true"}"
|
135
|
+
case block[1]
|
136
|
+
when 0
|
137
|
+
meta_stream
|
138
|
+
when 1
|
139
|
+
meta_padd
|
140
|
+
when 2
|
141
|
+
meta_app
|
142
|
+
when 3
|
143
|
+
meta_seek
|
144
|
+
when 4
|
145
|
+
meta_vorb
|
146
|
+
when 5
|
147
|
+
meta_cue
|
148
|
+
end
|
149
|
+
n += 1
|
150
|
+
end
|
151
|
+
nil
|
152
|
+
end
|
153
|
+
|
154
|
+
# Dumps the contents of flac_file['raw_data']
|
155
|
+
#
|
156
|
+
# :call-seq:
|
157
|
+
# FlacInfo.raw_data_dump() -> nil
|
158
|
+
# FlacInfo.raw_data_dump(outfile) -> nil
|
159
|
+
#
|
160
|
+
# If passed with 'outfile', the data will be written to a file with that name
|
161
|
+
# otherwise it is written to the console (even if binary!).
|
162
|
+
#
|
163
|
+
def raw_data_dump(outfile = nil)
|
164
|
+
if @flac_file == {}
|
165
|
+
raise FlacInfoError, "Flac File data not present"
|
166
|
+
end
|
167
|
+
if outfile == nil
|
168
|
+
puts @flac_file['raw_data']
|
169
|
+
else
|
170
|
+
if @flac_file['mime_type'] =~ /text/
|
171
|
+
f = File.new(outfile, "w")
|
172
|
+
f.write(@flac_file['raw_data'])
|
173
|
+
f.close
|
174
|
+
else
|
175
|
+
f = File.new(outfile, "wb")
|
176
|
+
f.write(@flac_file['raw_data'])
|
177
|
+
f.close
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
|
183
|
+
private
|
184
|
+
# The following six methods are just helpers for meta_flac
|
185
|
+
def meta_stream
|
186
|
+
puts " length: #{@streaminfo['block_size']}"
|
187
|
+
puts " minumum blocksize: #{@streaminfo['minimum_block']} samples"
|
188
|
+
puts " maximum blocksize: #{@streaminfo['maximum_block']} samples"
|
189
|
+
puts " minimum framesize: #{@streaminfo['minimum_frame']} bytes"
|
190
|
+
puts " maximum framesize: #{@streaminfo['maximum_frame']} bytes"
|
191
|
+
puts " sample rate: #{@streaminfo['samplerate']} Hz"
|
192
|
+
puts " channels: #{@streaminfo['channels']}"
|
193
|
+
puts " bits-per-sample: #{@streaminfo['bits_per_sample']}"
|
194
|
+
puts " total samples: #{@streaminfo['total_samples']}"
|
195
|
+
puts " MD5 signature: #{@streaminfo['md5']}"
|
196
|
+
end
|
197
|
+
|
198
|
+
def meta_padd
|
199
|
+
puts " length: #{@padding['block_size']}"
|
200
|
+
end
|
201
|
+
|
202
|
+
def meta_app
|
203
|
+
puts " length: #{@application['block_size']}"
|
204
|
+
puts " id: #{@application['ID']}"
|
205
|
+
puts " application name: #{@application['name']}"
|
206
|
+
if @application['ID'] == "41544348"
|
207
|
+
puts " description: #{@flac_file['description']}"
|
208
|
+
puts " mime type: #{@flac_file['mime_type']}"
|
209
|
+
# Don't want to dump binary data
|
210
|
+
if @flac_file['mime_type'] =~ /text/
|
211
|
+
puts " raw data:"
|
212
|
+
puts @flac_file['raw_data']
|
213
|
+
else
|
214
|
+
puts "'Flac File' data may be binary. Use 'raw_data_dump' to see it"
|
215
|
+
end
|
216
|
+
else
|
217
|
+
puts " raw data"
|
218
|
+
puts @application['raw_data']
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
def meta_seek
|
223
|
+
puts " length: #{@seektable['block_size']}"
|
224
|
+
print_seektable
|
225
|
+
end
|
226
|
+
|
227
|
+
def meta_vorb
|
228
|
+
puts " length: #{@tags['block_size']}"
|
229
|
+
puts " length: #{@tags['vendor_tag']}"
|
230
|
+
puts " comments: #{@comment.size}"
|
231
|
+
n = 0
|
232
|
+
@comment.each do |c|
|
233
|
+
puts " comment[#{n}]: #{c}"
|
234
|
+
n += 1
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
def meta_cue
|
239
|
+
puts " length: #{@cuesheet['block_size']}"
|
240
|
+
end
|
241
|
+
|
242
|
+
# This is where the 'real' parsing starts.
|
243
|
+
def parse_flac_meta_blocks
|
244
|
+
@fp = File.new(@filename, "rb")
|
245
|
+
@streaminfo = {}
|
246
|
+
@comment = {}
|
247
|
+
@seektable = {}
|
248
|
+
@padding = {}
|
249
|
+
@application = {}
|
250
|
+
@cuesheet = {}
|
251
|
+
|
252
|
+
typetable = { 0 => "streaminfo", 1 => "padding", 2 => "application",
|
253
|
+
3 => "seektable", 4 => "vorbis_comment", 5 => "cuesheet" }
|
254
|
+
|
255
|
+
header = @fp.read(4)
|
256
|
+
# First 4 bytes must be 0x66, 0x4C, 0x61, and 0x43
|
257
|
+
if header != 'fLaC'
|
258
|
+
raise FlacInfoError, "#{@filename} does not appear to be a valid Flac file"
|
259
|
+
end
|
260
|
+
|
261
|
+
@metadata_blocks = []
|
262
|
+
lastheader = 0
|
263
|
+
n = 0
|
264
|
+
until lastheader == 1
|
265
|
+
# first bit = Last-metadata-block flag
|
266
|
+
# bits 2-7 = BLOCK_TYPE. See typetable above
|
267
|
+
block_header = @fp.read(1).unpack("B*")[0]
|
268
|
+
lastheader = block_header[0].to_i & 1
|
269
|
+
type = sprintf("%u", "0b#{block_header[1..7]}").to_i
|
270
|
+
@metadata_blocks[n] = ["#{typetable[type]}", type, lastheader]
|
271
|
+
self.send "parse_#{typetable[type]}"
|
272
|
+
n += 1
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
def parse_seektable
|
277
|
+
begin
|
278
|
+
@seektable['block_size'] = @fp.read(3).reverse.unpack("v*")[0]
|
279
|
+
@seektable['seek_points'] = @seektable['block_size'] / 18
|
280
|
+
n = 0
|
281
|
+
@seektable['points'] = {}
|
282
|
+
@seektable['seek_points'].times do
|
283
|
+
pt_arr = []
|
284
|
+
pt_arr << @fp.read(8).reverse.unpack("V*")[0]
|
285
|
+
pt_arr << @fp.read(8).reverse.unpack("V*")[0]
|
286
|
+
pt_arr << @fp.read(2).reverse.unpack("v*")[0]
|
287
|
+
@seektable['points'][n] = pt_arr
|
288
|
+
n += 1
|
289
|
+
end
|
290
|
+
rescue
|
291
|
+
raise FlacInfoError, "Could not parse seek table"
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
# Not parsed yet, I have no flacs with a cuesheet!
|
296
|
+
def parse_cuesheet
|
297
|
+
@cuesheet['block_size'] = @fp.read(3).reverse.unpack("v*")[0]
|
298
|
+
@fp.seek(@cuesheet['block_size'], IO::SEEK_CUR)
|
299
|
+
end
|
300
|
+
|
301
|
+
def parse_application
|
302
|
+
begin
|
303
|
+
@application['block_size'] = @fp.read(3).reverse.unpack("v*")[0]
|
304
|
+
@application['ID'] = @fp.read(4).unpack("H*")[0]
|
305
|
+
|
306
|
+
# See http://flac.sourceforge.net/id.html
|
307
|
+
app_id = {"41544348" => "Flac File", "43756573" => "GoldWave Cue Points",
|
308
|
+
"4D754D4C" => "MusicML", "46696361" => "CUE Splitter",
|
309
|
+
"46746F6C" => "flac-tools", "5346464C" => "Sound Font FLAC",
|
310
|
+
"7065656D" => "Parseable Embedded Extensible Metadata", "74756E65" => "TagTuner",
|
311
|
+
"786D6364" => "xmcd"}
|
312
|
+
|
313
|
+
@application['name'] = "#{app_id[@application['ID']]}"
|
314
|
+
|
315
|
+
# We only know how to parse data from 'Flac File'...
|
316
|
+
if @application['ID'] = "41544348"
|
317
|
+
parse_flac_file_contents(@application['block_size'] - 4)
|
318
|
+
else
|
319
|
+
@application['raw_data'] = @fp.read(@application['block_size'] - 4)
|
320
|
+
end
|
321
|
+
rescue
|
322
|
+
raise FlacInfoError, "Could not parse application block"
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
# Unlike most values in the Flac header
|
327
|
+
# the Vorbis comments are in LSB order
|
328
|
+
#
|
329
|
+
# @comment is an array of values according to the official spec implementation
|
330
|
+
# @tags is a more user-friendly data structure with the values
|
331
|
+
# separated into key=value pairs
|
332
|
+
def parse_vorbis_comment
|
333
|
+
begin
|
334
|
+
@tags = {}
|
335
|
+
@tags['block_size'] = @fp.read(3).reverse.unpack("v*")[0]
|
336
|
+
vendor_length = @fp.read(4).unpack("V")[0]
|
337
|
+
@tags['vendor_tag'] = @fp.read(vendor_length)
|
338
|
+
user_comment_list_length = @fp.read(4).unpack("V")[0]
|
339
|
+
@comment = []
|
340
|
+
n = 0
|
341
|
+
user_comment_list_length.times do
|
342
|
+
length = @fp.read(4).unpack("V")[0]
|
343
|
+
@comment[n] = @fp.read(length)
|
344
|
+
n += 1
|
345
|
+
end
|
346
|
+
@comment.each do |c|
|
347
|
+
k,v = c.split("=")
|
348
|
+
# Vorbis spec says we can have more than one identical comment ie:
|
349
|
+
# comment[0]="Artist=Charlie Parker"
|
350
|
+
# comment[1]="Artist=Miles Davis"
|
351
|
+
# so we just append the second and subsequent values to the first
|
352
|
+
if @tags.has_key?(k)
|
353
|
+
@tags[k] = "#{@tags[k]}, #{v}"
|
354
|
+
else
|
355
|
+
@tags[k] = v
|
356
|
+
end
|
357
|
+
end
|
358
|
+
rescue
|
359
|
+
raise FlacInfoError, "Could not parse Vorbis comments block"
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
# padding is just a bunch of '0' bytes
|
364
|
+
def parse_padding
|
365
|
+
@padding['block_size'] = @fp.read(3).reverse.unpack("v*")[0]
|
366
|
+
@fp.seek(@padding['block_size'], IO::SEEK_CUR)
|
367
|
+
end
|
368
|
+
|
369
|
+
def parse_streaminfo
|
370
|
+
begin
|
371
|
+
# Length (in bytes) of metadata to follow (not including header)
|
372
|
+
@streaminfo['block_size'] = @fp.read(3).reverse.unpack("v*")[0]
|
373
|
+
@streaminfo['minimum_block'] = @fp.read(2).reverse.unpack("v*")[0]
|
374
|
+
@streaminfo['maximum_block'] = @fp.read(2).reverse.unpack("v*")[0]
|
375
|
+
@streaminfo['minimum_frame'] = @fp.read(3).reverse.unpack("v*")[0]
|
376
|
+
@streaminfo['maximum_frame'] = @fp.read(3).reverse.unpack("v*")[0]
|
377
|
+
|
378
|
+
# 64 bits in MSB order
|
379
|
+
bitstring = @fp.read(8).unpack("B*")[0]
|
380
|
+
# 20 bits :: Sample rate in Hz.
|
381
|
+
@streaminfo['samplerate'] = sprintf("%u", "0b#{bitstring[0..19]}").to_i
|
382
|
+
# 3 bits :: (number of channels)-1
|
383
|
+
@streaminfo['channels'] = sprintf("%u", "0b#{bitstring[20..22]}").to_i + 1
|
384
|
+
# 5 bits :: (bits per sample)-1
|
385
|
+
@streaminfo['bits_per_sample'] = sprintf("%u", "0b#{bitstring[23..27]}").to_i + 1
|
386
|
+
# 36 bits :: Total samples in stream.
|
387
|
+
@streaminfo['total_samples'] = sprintf("%u", "0b#{bitstring[28..63]}").to_i
|
388
|
+
|
389
|
+
# 128 bits :: MD5 signature of the unencoded audio data.
|
390
|
+
@streaminfo['md5'] = @fp.read(16).unpack("H32")[0]
|
391
|
+
rescue
|
392
|
+
raise FlacInfoError, "Could not parse stream info block"
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
# See http://firestuff.org/flacfile/
|
397
|
+
def parse_flac_file_contents(size)
|
398
|
+
begin
|
399
|
+
@flac_file = {}
|
400
|
+
desc_length = @fp.read(1).unpack("C")[0]
|
401
|
+
@flac_file['description'] = @fp.read(desc_length)
|
402
|
+
mime_length = @fp.read(1).reverse.unpack("C")[0]
|
403
|
+
@flac_file['mime_type'] = @fp.read(mime_length)
|
404
|
+
size = size - 2 - desc_length - mime_length
|
405
|
+
@flac_file['raw_data'] = @fp.read(size)
|
406
|
+
rescue
|
407
|
+
raise FlacInfoError, "Could not parse Flac File data"
|
408
|
+
end
|
409
|
+
end
|
410
|
+
end
|
metadata
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.8.11
|
3
|
+
specification_version: 1
|
4
|
+
name: flacinfo-rb
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: "0.1"
|
7
|
+
date: 2006-09-18 00:00:00 -07:00
|
8
|
+
summary: Pure Ruby lib for accessing metadata (including Vorbis tags) from Flac files
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: bulliver@badcomputer.org
|
12
|
+
homepage: http://badcomputer.org/unix/code/flacinfo/
|
13
|
+
rubyforge_project:
|
14
|
+
description:
|
15
|
+
autorequire: flacinfo
|
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:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
authors:
|
29
|
+
- Darren Kirby
|
30
|
+
files:
|
31
|
+
- README
|
32
|
+
- lib/flacinfo.rb
|
33
|
+
test_files: []
|
34
|
+
|
35
|
+
rdoc_options: []
|
36
|
+
|
37
|
+
extra_rdoc_files:
|
38
|
+
- README
|
39
|
+
executables: []
|
40
|
+
|
41
|
+
extensions: []
|
42
|
+
|
43
|
+
requirements: []
|
44
|
+
|
45
|
+
dependencies: []
|
46
|
+
|