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