wmainfo-rb 0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/README +35 -0
- data/lib/wmainfo.rb +407 -0
- metadata +46 -0
data/README
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
:: wmainfo-rb ::
|
2
|
+
Author: Darren Kirby
|
3
|
+
mailto:bulliver@badcomputer.org
|
4
|
+
License: Ruby
|
5
|
+
|
6
|
+
= Quick API docs =
|
7
|
+
|
8
|
+
== Initializing ==
|
9
|
+
|
10
|
+
require 'wmainfo'
|
11
|
+
foo = WmaInfo.new("someSong.wma")
|
12
|
+
... or ...
|
13
|
+
foo = WmaInfo.new("someVideo.wmv", debug=1)
|
14
|
+
|
15
|
+
== Public attributes ==
|
16
|
+
|
17
|
+
@drm :: '1' if DRM present else 'None'
|
18
|
+
@tags :: dict of strings (id3 like data)
|
19
|
+
@info :: dict of variable types (non-id3 like data)
|
20
|
+
@headerObject :: dict of arrays (name, GUID, size and offset of ASF objects)
|
21
|
+
|
22
|
+
== Public methods ==
|
23
|
+
|
24
|
+
print_objects :: pretty-print header objects
|
25
|
+
hasdrm :: returns True if file has DRM
|
26
|
+
hastag('str') :: returns True if @tags['str'] exists
|
27
|
+
print_tags :: pretty-print @tags dict
|
28
|
+
hasinfo('str') :: returns True if @info['str'] exists
|
29
|
+
print_info :: pretty-print @info dict
|
30
|
+
parse_stream :: parse Asf_Stream_Property_Object
|
31
|
+
... which will create another public attribute:
|
32
|
+
@stream :: dict of variable types (stream properties data)
|
33
|
+
|
34
|
+
For more/different documentation see http://badcomputer.org/unix/code/wmainfo/
|
35
|
+
|
data/lib/wmainfo.rb
ADDED
@@ -0,0 +1,407 @@
|
|
1
|
+
# = Description
|
2
|
+
#
|
3
|
+
# wmainfo-ruby gives you access to low level information on wma/wmv files.
|
4
|
+
# * It identifies all "ASF_..." objects and shows each objects size
|
5
|
+
# * It returns info such as bitrate, size, length, creation date etc
|
6
|
+
# * It returns meta-tags from ASF_Content_Description_Object
|
7
|
+
#
|
8
|
+
# = Note:
|
9
|
+
#
|
10
|
+
# I wrestled with the ASF spec (150 page .doc format!) with no joy for
|
11
|
+
# a while, then found Dan Sully's Audio-WMA Perl module:
|
12
|
+
# (http://cpants.perl.org/dist/Audio-WMA :: http://www.slimdevices.com/)
|
13
|
+
# This entire library is essentially a translation of WMA.pm
|
14
|
+
# to Ruby. All credit for the hard work is owed to Dan...
|
15
|
+
#
|
16
|
+
# License:: Ruby
|
17
|
+
# Author:: Darren Kirby (mailto:bulliver@badcomputer.org)
|
18
|
+
# Website:: http://badcomputer.org/unix/code/wmainfo/
|
19
|
+
|
20
|
+
|
21
|
+
# raised when errors occur parsing wma header
|
22
|
+
class WmaInfoError < StandardError
|
23
|
+
end
|
24
|
+
|
25
|
+
class WmaInfo
|
26
|
+
# WmaInfo.tags and WmaInfo.info are hashes of NAME=VALUE pairs
|
27
|
+
# WmaInfo.headerObject is a hash of arrays
|
28
|
+
attr_reader :tags, :headerObject, :info, :stream
|
29
|
+
def initialize(file, debug=nil)
|
30
|
+
@drm = nil
|
31
|
+
@tags = {}
|
32
|
+
@headerObject = {}
|
33
|
+
@info = {}
|
34
|
+
@file = file
|
35
|
+
@debug = debug
|
36
|
+
parseWmaHeader
|
37
|
+
end
|
38
|
+
|
39
|
+
# for ASF_Header_Object prints: "name: GUID size num_objects"
|
40
|
+
# for others, prints: "name: GUID size offset"
|
41
|
+
def print_objects
|
42
|
+
@headerObject.each_pair do |key,val|
|
43
|
+
puts "#{key}: #{val[0]} #{val[1]} #{val[2]}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# returns true if the file has DRM
|
48
|
+
# ie: if a "*Content_Encryption_Object" is found
|
49
|
+
def hasdrm?
|
50
|
+
@drm ? true : false
|
51
|
+
end
|
52
|
+
|
53
|
+
# returns true if tags["tag"] has a value
|
54
|
+
def hastag?(tag)
|
55
|
+
@tags["#{tag}"] ? true : false
|
56
|
+
end
|
57
|
+
|
58
|
+
# prettyprint WmaInfo.tags hash
|
59
|
+
def print_tags
|
60
|
+
@tags.each_pair { |key,val| puts "#{key}: #{val}" }
|
61
|
+
end
|
62
|
+
|
63
|
+
# returns true if info["field"] has a value
|
64
|
+
def hasinfo?(field)
|
65
|
+
@info["#{field}"] ? true : false
|
66
|
+
end
|
67
|
+
|
68
|
+
# prettyprint WmaInfo.info hash
|
69
|
+
def print_info
|
70
|
+
@info.each_pair { |key,val| puts "#{key}: #{val}" }
|
71
|
+
end
|
72
|
+
|
73
|
+
# I don't think most people will want/need this info
|
74
|
+
# so it is not parsed automatically
|
75
|
+
def parse_stream
|
76
|
+
begin
|
77
|
+
@stream = {}
|
78
|
+
offset = @headerObject['ASF_Stream_Properties_Object'][2]
|
79
|
+
parseASFStreamPropertiesObject(offset)
|
80
|
+
rescue
|
81
|
+
raise WmaInfoError, "Cannot grok ASF_Stream_Properties_Object", caller
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# returns: "filename.wma :: Size: N bytes :: Bitrate: N kbps :: Duration: N seconds"
|
86
|
+
# this is useless
|
87
|
+
def to_s
|
88
|
+
"#{File.basename(@file)} :: Size: #{@size} bytes :: Bitrate: #{@info['bitrate']} kbps :: Duration: #{@info['playtime_seconds']} seconds"
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
def parseWmaHeader
|
93
|
+
@size = File.size(@file)
|
94
|
+
@fh = File.new(@file, "rb")
|
95
|
+
@offset = 0
|
96
|
+
@fileOffset = 30
|
97
|
+
@guidMapping = knownGUIDs
|
98
|
+
@reverseGuidMapping = @guidMapping.invert
|
99
|
+
require 'iconv'
|
100
|
+
@ic = Iconv.new("ASCII//IGNORE", "UTF-16LE")
|
101
|
+
|
102
|
+
# read first 30 bytes and parse ASF_Header_Object
|
103
|
+
begin
|
104
|
+
objectId = byteStringToGUID(@fh.read(16))
|
105
|
+
objectSize = @fh.read(8).unpack("V")[0]
|
106
|
+
headerObjects = @fh.read(4).unpack("V")[0]
|
107
|
+
reserved1 = @fh.read(1).unpack("b*")[0]
|
108
|
+
reserved2 = @fh.read(1).unpack("b*")[0]
|
109
|
+
objectIdName = @reverseGuidMapping[objectId]
|
110
|
+
rescue
|
111
|
+
# not getting raised when fed a non-wma file
|
112
|
+
# objectSize must be getting value because
|
113
|
+
# "Header size reported larger than file size"
|
114
|
+
# gets raised instead?
|
115
|
+
raise WmaInfoError, "Not a wma header", caller
|
116
|
+
end
|
117
|
+
|
118
|
+
if objectSize > @size
|
119
|
+
raise WmaInfoError, "Header size reported larger than file size", caller
|
120
|
+
end
|
121
|
+
|
122
|
+
@headerObject[objectIdName] = [objectId, objectSize, headerObjects, reserved1, reserved2]
|
123
|
+
|
124
|
+
if @debug
|
125
|
+
puts "objectId: #{objectId}"
|
126
|
+
puts "objectIdName: #{@reverseGuidMapping[objectId]}"
|
127
|
+
puts "objectSize: #{objectSize}"
|
128
|
+
puts "headerObjects: #{headerObjects}"
|
129
|
+
puts "reserved1: #{reserved1}"
|
130
|
+
puts "reserved2: #{reserved2}"
|
131
|
+
end
|
132
|
+
|
133
|
+
@headerData = @fh.read(objectSize - 30)
|
134
|
+
@fh.close
|
135
|
+
headerObjects.times do
|
136
|
+
nextObject = readAndIncrementOffset(16)
|
137
|
+
nextObjectText = byteStringToGUID(nextObject)
|
138
|
+
nextObjectSize = parse64BitString(readAndIncrementOffset(8))
|
139
|
+
nextObjectName = @reverseGuidMapping[nextObjectText];
|
140
|
+
|
141
|
+
@headerObject[nextObjectName] = [nextObjectText, nextObjectSize, @fileOffset]
|
142
|
+
@fileOffset += nextObjectSize
|
143
|
+
|
144
|
+
if @debug
|
145
|
+
puts "nextObjectGUID: #{nextObjectText}"
|
146
|
+
puts "nextObjectName: #{nextObjectName}"
|
147
|
+
puts "nextObjectSize: #{nextObjectSize}"
|
148
|
+
end
|
149
|
+
|
150
|
+
# start looking at object contents
|
151
|
+
if nextObjectName == 'ASF_File_Properties_Object'
|
152
|
+
parseASFFilePropertiesObject
|
153
|
+
next
|
154
|
+
elsif nextObjectName == 'ASF_Content_Description_Object'
|
155
|
+
parseASFContentDescriptionObject
|
156
|
+
next
|
157
|
+
elsif nextObjectName == 'ASF_Extended_Content_Description_Object'
|
158
|
+
parseASFExtendedContentDescriptionObject
|
159
|
+
next
|
160
|
+
elsif nextObjectName == 'ASF_Content_Encryption_Object' || nextObjectName == 'ASF_Extended_Content_Encryption_Object'
|
161
|
+
parseASFContentEncryptionObject
|
162
|
+
end
|
163
|
+
|
164
|
+
# set our next object size
|
165
|
+
@offset += nextObjectSize - 24
|
166
|
+
end
|
167
|
+
|
168
|
+
# meta-tag like values go to 'tags' all others to 'info'
|
169
|
+
@ext_info.each do |k,v|
|
170
|
+
if k =~ /WM\/(TrackNumber|AlbumTitle|AlbumArtist|Genre|Year|Composer|Mood|Lyrics|BeatsPerMinute|Publisher)/
|
171
|
+
@tags[k.gsub(/WM\//, "")] = v # dump "WM/"
|
172
|
+
else
|
173
|
+
@info[k] = v
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
# dump empty tags
|
178
|
+
@tags.delete_if { |k,v| v == "" || v == nil }
|
179
|
+
end
|
180
|
+
|
181
|
+
def parseASFContentEncryptionObject
|
182
|
+
@drm = 1
|
183
|
+
end
|
184
|
+
|
185
|
+
def parseASFFilePropertiesObject
|
186
|
+
fileid = readAndIncrementOffset(16)
|
187
|
+
@info['fileid_guid'] = byteStringToGUID(fileid)
|
188
|
+
@info['filesize'] = parse64BitString(readAndIncrementOffset(8))
|
189
|
+
@info['creation_date'] = readAndIncrementOffset(8).unpack("Q")[0]
|
190
|
+
@info['creation_date_unix'] = fileTimeToUnixTime(@info['creation_date'])
|
191
|
+
@info['creation_string'] = Time.at(@info['creation_date_unix'].to_i)
|
192
|
+
@info['data_packets'] = readAndIncrementOffset(8).unpack("V")[0]
|
193
|
+
@info['play_duration'] = parse64BitString(readAndIncrementOffset(8))
|
194
|
+
@info['send_duration'] = parse64BitString(readAndIncrementOffset(8))
|
195
|
+
@info['preroll'] = readAndIncrementOffset(8).unpack("V")[0]
|
196
|
+
@info['playtime_seconds'] = (@info['play_duration'] / 10000000) - (@info['preroll'] / 1000)
|
197
|
+
flags_raw = readAndIncrementOffset(4).unpack("V")[0]
|
198
|
+
if flags_raw & 0x0001 == 0
|
199
|
+
@info['broadcast'] = 0
|
200
|
+
else
|
201
|
+
@info['broadcast'] = 1
|
202
|
+
end
|
203
|
+
if flags_raw & 0x0002 == 0
|
204
|
+
@info['seekable'] = 0
|
205
|
+
else
|
206
|
+
@info['seekable'] = 1
|
207
|
+
end
|
208
|
+
@info['min_packet_size'] = readAndIncrementOffset(4).unpack("V")[0]
|
209
|
+
@info['max_packet_size'] = readAndIncrementOffset(4).unpack("V")[0]
|
210
|
+
@info['max_bitrate'] = readAndIncrementOffset(4).unpack("V")[0]
|
211
|
+
@info['bitrate'] = @info['max_bitrate'] / 1000
|
212
|
+
|
213
|
+
if @debug
|
214
|
+
@info.each_pair { |key,val| puts "#{key}: #{val}" }
|
215
|
+
end
|
216
|
+
|
217
|
+
end
|
218
|
+
|
219
|
+
def parseASFContentDescriptionObject
|
220
|
+
lengths = {}
|
221
|
+
keys = %w/Title Author Copyright Description Rating/
|
222
|
+
keys.each do |key| # read the lengths of each key
|
223
|
+
lengths[key] = readAndIncrementOffset(2).unpack("v")[0]
|
224
|
+
end
|
225
|
+
keys.each do |key| # now pull the data based on length
|
226
|
+
@tags[key] = decodeBinaryString(readAndIncrementOffset(lengths[key]))
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
def parseASFExtendedContentDescriptionObject
|
231
|
+
@ext_info = {}
|
232
|
+
@ext_info['content_count'] = readAndIncrementOffset(2).unpack("v")[0]
|
233
|
+
@ext_info['content_count'].times do |n|
|
234
|
+
ext = {}
|
235
|
+
ext['base_offset'] = @offset + 30
|
236
|
+
ext['name_length'] = readAndIncrementOffset(2).unpack("v")[0]
|
237
|
+
ext['name'] = decodeBinaryString(readAndIncrementOffset(ext['name_length']))
|
238
|
+
ext['value_type'] = readAndIncrementOffset(2).unpack("v")[0]
|
239
|
+
ext['value_length'] = readAndIncrementOffset(2).unpack("v")[0]
|
240
|
+
|
241
|
+
value = readAndIncrementOffset(ext['value_length'])
|
242
|
+
if ext['value_type'] <= 1
|
243
|
+
ext['value'] = decodeBinaryString(value)
|
244
|
+
elsif ext['value_type'] == 4
|
245
|
+
ext['value'] = parse64BitString(value)
|
246
|
+
else
|
247
|
+
valTypeTemplates = ["", "", "V", "V", "", "v"]
|
248
|
+
ext['value'] = value.unpack(valTypeTemplates[ext['value_type']])[0]
|
249
|
+
end
|
250
|
+
|
251
|
+
if @debug
|
252
|
+
puts "base_offset: #{ext['base_offset']}"
|
253
|
+
puts "name length: #{ext['name_length']}"
|
254
|
+
puts "name: #{ext['name']}"
|
255
|
+
puts "value type: #{ext['value_type']}"
|
256
|
+
puts "value length: #{ext['value_length']}"
|
257
|
+
puts "value: #{ext['value']}"
|
258
|
+
end
|
259
|
+
|
260
|
+
@ext_info["#{ext['name']}"] = ext['value']
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
def parseASFStreamPropertiesObject(offset)
|
265
|
+
@offset = offset - 6 # gained an extra 6 bytes somewhere?!
|
266
|
+
|
267
|
+
streamType = readAndIncrementOffset(16)
|
268
|
+
@stream['stream_type_guid'] = byteStringToGUID(streamType)
|
269
|
+
@stream['stream_type_name'] = @reverseGuidMapping[@stream['stream_type_guid']]
|
270
|
+
errorType = readAndIncrementOffset(16)
|
271
|
+
@stream['error_correct_guid'] = byteStringToGUID(errorType)
|
272
|
+
@stream['error_correct_name'] = @reverseGuidMapping[@stream['error_correct_guid']]
|
273
|
+
|
274
|
+
@stream['time_offset'] = readAndIncrementOffset(8).unpack("4v")[0]
|
275
|
+
@stream['type_data_length'] = readAndIncrementOffset(4).unpack("2v")[0]
|
276
|
+
@stream['error_data_length'] = readAndIncrementOffset(4).unpack("2v")[0]
|
277
|
+
flags_raw = readAndIncrementOffset(2).unpack("v")[0]
|
278
|
+
@stream['stream_number'] = flags_raw & 0x007F
|
279
|
+
@stream['encrypted'] = flags_raw & 0x8000
|
280
|
+
|
281
|
+
# reserved - set to zero
|
282
|
+
readAndIncrementOffset(4)
|
283
|
+
|
284
|
+
@stream['type_specific_data'] = readAndIncrementOffset(@stream['type_data_length'])
|
285
|
+
@stream['error_correct_data'] = readAndIncrementOffset(@stream['error_data_length'])
|
286
|
+
|
287
|
+
if @stream['stream_type_name'] == 'ASF_Audio_Media'
|
288
|
+
parseASFAudioMediaObject
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
def parseASFAudioMediaObject
|
293
|
+
data = @stream['type_specific_data'][0...16]
|
294
|
+
@stream['audio_channels'] = data[2...4].unpack("v")[0]
|
295
|
+
@stream['audio_sample_rate'] = data[4...8].unpack("2v")[0]
|
296
|
+
@stream['audio_bitrate'] = data[8...12].unpack("2v")[0] * 8
|
297
|
+
@stream['audio_bits_per_sample'] = data[14...16].unpack("v")[0]
|
298
|
+
end
|
299
|
+
|
300
|
+
# UTF16LE -> ASCII ... am still not happy with this
|
301
|
+
def decodeBinaryString(data)
|
302
|
+
textString = @ic.iconv(data[0, data.length / 2 * 2] + "\000\000")[0..-2]
|
303
|
+
textString.sub!(/\x00$/, '')
|
304
|
+
end
|
305
|
+
|
306
|
+
def readAndIncrementOffset(size)
|
307
|
+
value = @headerData[@offset..(@offset + size)]
|
308
|
+
@offset += size
|
309
|
+
return value
|
310
|
+
end
|
311
|
+
|
312
|
+
def byteStringToGUID(byteString)
|
313
|
+
guidString = sprintf("%02X", byteString[3])
|
314
|
+
guidString += sprintf("%02X", byteString[2])
|
315
|
+
guidString += sprintf("%02X", byteString[1])
|
316
|
+
guidString += sprintf("%02X", byteString[0])
|
317
|
+
guidString += '-'
|
318
|
+
guidString += sprintf("%02X", byteString[5])
|
319
|
+
guidString += sprintf("%02X", byteString[4])
|
320
|
+
guidString += '-'
|
321
|
+
guidString += sprintf("%02X", byteString[7])
|
322
|
+
guidString += sprintf("%02X", byteString[6])
|
323
|
+
guidString += '-'
|
324
|
+
guidString += sprintf("%02X", byteString[8])
|
325
|
+
guidString += sprintf("%02X", byteString[9])
|
326
|
+
guidString += '-'
|
327
|
+
guidString += sprintf("%02X", byteString[10])
|
328
|
+
guidString += sprintf("%02X", byteString[11])
|
329
|
+
guidString += sprintf("%02X", byteString[12])
|
330
|
+
guidString += sprintf("%02X", byteString[13])
|
331
|
+
guidString += sprintf("%02X", byteString[14])
|
332
|
+
guidString += sprintf("%02X", byteString[15])
|
333
|
+
end
|
334
|
+
|
335
|
+
def parse64BitString(data)
|
336
|
+
d = data.unpack('VV')
|
337
|
+
d[1] * 2 ** 32 + d[0]
|
338
|
+
end
|
339
|
+
|
340
|
+
def fileTimeToUnixTime(time)
|
341
|
+
(time - 116444736000000000) / 10000000
|
342
|
+
end
|
343
|
+
|
344
|
+
def knownGUIDs
|
345
|
+
guidMapping = {
|
346
|
+
'ASF_Extended_Stream_Properties_Object' => '14E6A5CB-C672-4332-8399-A96952065B5A',
|
347
|
+
'ASF_Padding_Object' => '1806D474-CADF-4509-A4BA-9AABCB96AAE8',
|
348
|
+
'ASF_Payload_Ext_Syst_Pixel_Aspect_Ratio' => '1B1EE554-F9EA-4BC8-821A-376B74E4C4B8',
|
349
|
+
'ASF_Script_Command_Object' => '1EFB1A30-0B62-11D0-A39B-00A0C90348F6',
|
350
|
+
'ASF_No_Error_Correction' => '20FB5700-5B55-11CF-A8FD-00805F5C442B',
|
351
|
+
'ASF_Content_Branding_Object' => '2211B3FA-BD23-11D2-B4B7-00A0C955FC6E',
|
352
|
+
'ASF_Content_Encryption_Object' => '2211B3FB-BD23-11D2-B4B7-00A0C955FC6E',
|
353
|
+
'ASF_Digital_Signature_Object' => '2211B3FC-BD23-11D2-B4B7-00A0C955FC6E',
|
354
|
+
'ASF_Extended_Content_Encryption_Object' => '298AE614-2622-4C17-B935-DAE07EE9289C',
|
355
|
+
'ASF_Simple_Index_Object' => '33000890-E5B1-11CF-89F4-00A0C90349CB',
|
356
|
+
'ASF_Degradable_JPEG_Media' => '35907DE0-E415-11CF-A917-00805F5C442B',
|
357
|
+
'ASF_Payload_Extension_System_Timecode' => '399595EC-8667-4E2D-8FDB-98814CE76C1E',
|
358
|
+
'ASF_Binary_Media' => '3AFB65E2-47EF-40F2-AC2C-70A90D71D343',
|
359
|
+
'ASF_Timecode_Index_Object' => '3CB73FD0-0C4A-4803-953D-EDF7B6228F0C',
|
360
|
+
'ASF_Metadata_Library_Object' => '44231C94-9498-49D1-A141-1D134E457054',
|
361
|
+
'ASF_Reserved_3' => '4B1ACBE3-100B-11D0-A39B-00A0C90348F6',
|
362
|
+
'ASF_Reserved_4' => '4CFEDB20-75F6-11CF-9C0F-00A0C90349CB',
|
363
|
+
'ASF_Command_Media' => '59DACFC0-59E6-11D0-A3AC-00A0C90348F6',
|
364
|
+
'ASF_Header_Extension_Object' => '5FBF03B5-A92E-11CF-8EE3-00C00C205365',
|
365
|
+
'ASF_Media_Object_Index_Parameters_Obj' => '6B203BAD-3F11-4E84-ACA8-D7613DE2CFA7',
|
366
|
+
'ASF_Header_Object' => '75B22630-668E-11CF-A6D9-00AA0062CE6C',
|
367
|
+
'ASF_Content_Description_Object' => '75B22633-668E-11CF-A6D9-00AA0062CE6C',
|
368
|
+
'ASF_Error_Correction_Object' => '75B22635-668E-11CF-A6D9-00AA0062CE6C',
|
369
|
+
'ASF_Data_Object' => '75B22636-668E-11CF-A6D9-00AA0062CE6C',
|
370
|
+
'ASF_Web_Stream_Media_Subtype' => '776257D4-C627-41CB-8F81-7AC7FF1C40CC',
|
371
|
+
'ASF_Stream_Bitrate_Properties_Object' => '7BF875CE-468D-11D1-8D82-006097C9A2B2',
|
372
|
+
'ASF_Language_List_Object' => '7C4346A9-EFE0-4BFC-B229-393EDE415C85',
|
373
|
+
'ASF_Codec_List_Object' => '86D15240-311D-11D0-A3A4-00A0C90348F6',
|
374
|
+
'ASF_Reserved_2' => '86D15241-311D-11D0-A3A4-00A0C90348F6',
|
375
|
+
'ASF_File_Properties_Object' => '8CABDCA1-A947-11CF-8EE4-00C00C205365',
|
376
|
+
'ASF_File_Transfer_Media' => '91BD222C-F21C-497A-8B6D-5AA86BFC0185',
|
377
|
+
'ASF_Advanced_Mutual_Exclusion_Object' => 'A08649CF-4775-4670-8A16-6E35357566CD',
|
378
|
+
'ASF_Bandwidth_Sharing_Object' => 'A69609E6-517B-11D2-B6AF-00C04FD908E9',
|
379
|
+
'ASF_Reserved_1' => 'ABD3D211-A9BA-11cf-8EE6-00C00C205365',
|
380
|
+
'ASF_Bandwidth_Sharing_Exclusive' => 'AF6060AA-5197-11D2-B6AF-00C04FD908E9',
|
381
|
+
'ASF_Bandwidth_Sharing_Partial' => 'AF6060AB-5197-11D2-B6AF-00C04FD908E9',
|
382
|
+
'ASF_JFIF_Media' => 'B61BE100-5B4E-11CF-A8FD-00805F5C442B',
|
383
|
+
'ASF_Stream_Properties_Object' => 'B7DC0791-A9B7-11CF-8EE6-00C00C205365',
|
384
|
+
'ASF_Video_Media' => 'BC19EFC0-5B4D-11CF-A8FD-00805F5C442B',
|
385
|
+
'ASF_Audio_Spread' => 'BFC3CD50-618F-11CF-8BB2-00AA00B4E220',
|
386
|
+
'ASF_Metadata_Object' => 'C5F8CBEA-5BAF-4877-8467-AA8C44FA4CCA',
|
387
|
+
'ASF_Payload_Ext_Syst_Sample_Duration' => 'C6BD9450-867F-4907-83A3-C77921B733AD',
|
388
|
+
'ASF_Group_Mutual_Exclusion_Object' => 'D1465A40-5A79-4338-B71B-E36B8FD6C249',
|
389
|
+
'ASF_Extended_Content_Description_Object' => 'D2D0A440-E307-11D2-97F0-00A0C95EA850',
|
390
|
+
'ASF_Stream_Prioritization_Object' => 'D4FED15B-88D3-454F-81F0-ED5C45999E24',
|
391
|
+
'ASF_Payload_Ext_System_Content_Type' => 'D590DC20-07BC-436C-9CF7-F3BBFBF1A4DC',
|
392
|
+
'ASF_Index_Object' => 'D6E229D3-35DA-11D1-9034-00A0C90349BE',
|
393
|
+
'ASF_Bitrate_Mutual_Exclusion_Object' => 'D6E229DC-35DA-11D1-9034-00A0C90349BE',
|
394
|
+
'ASF_Index_Parameters_Object' => 'D6E229DF-35DA-11D1-9034-00A0C90349BE',
|
395
|
+
'ASF_Mutex_Language' => 'D6E22A00-35DA-11D1-9034-00A0C90349BE',
|
396
|
+
'ASF_Mutex_Bitrate' => 'D6E22A01-35DA-11D1-9034-00A0C90349BE',
|
397
|
+
'ASF_Mutex_Unknown' => 'D6E22A02-35DA-11D1-9034-00A0C90349BE',
|
398
|
+
'ASF_Web_Stream_Format' => 'DA1E6B13-8359-4050-B398-388E965BF00C',
|
399
|
+
'ASF_Payload_Ext_System_File_Name' => 'E165EC0E-19ED-45D7-B4A7-25CBD1E28E9B',
|
400
|
+
'ASF_Marker_Object' => 'F487CD01-A951-11CF-8EE6-00C00C205365',
|
401
|
+
'ASF_Timecode_Index_Parameters_Object' => 'F55E496D-9797-4B5D-8C8B-604DFE9BFB24',
|
402
|
+
'ASF_Audio_Media' => 'F8699E40-5B4D-11CF-A8FD-00805F5C442B',
|
403
|
+
'ASF_Media_Object_Index_Object' => 'FEB103F8-12AD-4C64-840F-2A1D2F7AD48C',
|
404
|
+
'ASF_Alt_Extended_Content_Encryption_Obj' => 'FF889EF1-ADEE-40DA-9E71-98704BB928CE',
|
405
|
+
}
|
406
|
+
end
|
407
|
+
end
|
metadata
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.8.11
|
3
|
+
specification_version: 1
|
4
|
+
name: wmainfo-rb
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: "0.3"
|
7
|
+
date: 2006-08-05 00:00:00 -07:00
|
8
|
+
summary: Pure Ruby lib for accessing info/tags from wma/wmv files
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: bulliver@badcomputer.org
|
12
|
+
homepage: http://badcomputer.org/unix/code/wmainfo/
|
13
|
+
rubyforge_project:
|
14
|
+
description:
|
15
|
+
autorequire: wmainfo
|
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/wmainfo.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
|
+
|