wmainfo-rb 0.3
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/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
|
+
|