moumar-wmainfo-rb 0.7
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 +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: []
|