UPnP-ContentDirectory 1.0

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.
@@ -0,0 +1,4 @@
1
+ �D>�5������~�T�S�|�D�'��)�����B�f��b�ʕ�~�bt�������9B�x#d��-�{#9�T�~�3�C���r�"Dϔ��*��
2
+ MŰld�Y�q)��g�L��
3
+ �~1����ʒ=B�Ca&O���V^,'����!�3���h'�}�j��\��0�;���M±��>���!!����s0Lۚ���j�ۤ�D��!٥-)@�zor��7��g��_F�w�x�B�t��
4
+ �w�oJ�Fl�*N
@@ -0,0 +1,5 @@
1
+ === 1.0.0 / 2008-07-23
2
+
3
+ * 1 major enhancement
4
+ * Birthday!
5
+
@@ -0,0 +1,5 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ lib/UPnP/service/content_directory.rb
@@ -0,0 +1,83 @@
1
+ = UPnP-ContentDirectory
2
+
3
+ * http://seattlerb.org/UPnP-ContentDirectory
4
+ * http://upnp.org
5
+ * Bugs: http://rubyforge.org/tracker/?func=add&group_id=1513&atid=5921
6
+
7
+ == DESCRIPTION:
8
+
9
+ A UPnP ContentDirectory service with some DLNA extensions. Currently this is
10
+ a work in progress, and is only adequate for viewing images on a PlayStation
11
+ 3.
12
+
13
+ == FEATURES/PROBLEMS:
14
+
15
+ * Only direct filesystem representations currently supported
16
+ * Largely undocumented
17
+ * Only tested on PlayStation 3
18
+ * Only images known to work
19
+ * Partially implemented:
20
+ * Browse
21
+ * GetSystemUpdateID
22
+ * Not implemented:
23
+ * CreateObject
24
+ * CreateReference
25
+ * DeleteResource
26
+ * DestroyObject
27
+ * ExportResource
28
+ * GetSearchCapabilities
29
+ * GetSortCapabilities
30
+ * GetTransferProgress
31
+ * ImportResource
32
+ * Search
33
+ * StopTransferResource
34
+ * UpdateObject
35
+
36
+ == SYNOPSIS:
37
+
38
+ In a UPnP::Device::create block:
39
+
40
+ your_device.add_service 'ContentDirectory' do |cd|
41
+ cd.add_directory Dir.pwd
42
+ end
43
+
44
+ See UPnP::Device::MediaServer for an example of usage.
45
+
46
+ == REQUIREMENTS:
47
+
48
+ * A filesystem with files on it
49
+ * A UPnP control point
50
+
51
+ == INSTALL:
52
+
53
+ sudo gem install UPnP-ContentDirectory
54
+
55
+ == LICENSE:
56
+
57
+ Copyright 2008 Eric Hodel. All rights reserved.
58
+
59
+ Redistribution and use in source and binary forms, with or without
60
+ modification, are permitted provided that the following conditions
61
+ are met:
62
+
63
+ 1. Redistributions of source code must retain the above copyright
64
+ notice, this list of conditions and the following disclaimer.
65
+ 2. Redistributions in binary form must reproduce the above copyright
66
+ notice, this list of conditions and the following disclaimer in the
67
+ documentation and/or other materials provided with the distribution.
68
+ 3. Neither the names of the authors nor the names of their contributors
69
+ may be used to endorse or promote products derived from this software
70
+ without specific prior written permission.
71
+
72
+ THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
73
+ OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
74
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
75
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
76
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
77
+ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
78
+ OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
79
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
80
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
81
+ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
82
+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
83
+
@@ -0,0 +1,14 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+ require './lib/UPnP/service/content_directory'
6
+
7
+ Hoe.new 'UPnP-ContentDirectory', UPnP::Service::ContentDirectory::VERSION do |p|
8
+ p.rubyforge_name = 'seattlerb'
9
+ p.developer 'Eric Hodel', 'drbrain@segment7.net'
10
+
11
+ p.extra_deps << ['UPnP', '>= 1.1.0']
12
+ end
13
+
14
+ # vim: syntax=Ruby
@@ -0,0 +1,401 @@
1
+ require 'open3'
2
+ require 'uri'
3
+
4
+ require 'rubygems'
5
+ require 'UPnP/service'
6
+
7
+ ##
8
+ # A UPnP ContentDirectory service. See upnp.org for specifications.
9
+
10
+ class UPnP::Service::ContentDirectory < UPnP::Service
11
+
12
+ VERSION = '1.0'
13
+
14
+ ##
15
+ # Returns the searching capabilities supported by the device
16
+
17
+ add_action 'GetSearchCapabilities',
18
+ [OUT, 'SearchCaps', 'SearchCapabilities']
19
+
20
+ ##
21
+ # Returns the CSV list of metadata tags that can be used in sortCriteria
22
+
23
+ add_action 'GetSortCapabilities',
24
+ [OUT, 'SortCaps', 'SortCapabilities']
25
+
26
+ add_action 'GetSystemUpdateID',
27
+ [OUT, 'Id', 'SystemUpdateID']
28
+
29
+ add_action 'Browse',
30
+ [IN, 'ObjectID', 'A_ARG_TYPE_ObjectID'],
31
+ [IN, 'BrowseFlag', 'A_ARG_TYPE_BrowseFlag'],
32
+ [IN, 'Filter', 'A_ARG_TYPE_Filter'],
33
+ [IN, 'StartingIndex', 'A_ARG_TYPE_Index'],
34
+ [IN, 'RequestedCount', 'A_ARG_TYPE_Count'],
35
+ [IN, 'SortCriteria', 'A_ARG_TYPE_SortCriteria'],
36
+
37
+ [OUT, 'Result', 'A_ARG_TYPE_Result'],
38
+ [OUT, 'NumberReturned', 'A_ARG_TYPE_Count'],
39
+ [OUT, 'TotalMatches', 'A_ARG_TYPE_Count'],
40
+ [OUT, 'UpdateID', 'A_ARG_TYPE_UpdateID']
41
+
42
+ # optional actions
43
+
44
+ add_action 'Search',
45
+ [IN, 'ContainerID', 'A_ARG_TYPE_ObjectID'],
46
+ [IN, 'SearchCriteria', 'A_ARG_TYPE_SearchCriteria'],
47
+ [IN, 'Filter', 'A_ARG_TYPE_Filter'],
48
+ [IN, 'StartingIndex', 'A_ARG_TYPE_Index'],
49
+ [IN, 'RequestedCount', 'A_ARG_TYPE_Count'],
50
+ [IN, 'SortCriteria', 'A_ARG_TYPE_SortCriteria'],
51
+
52
+ [OUT, 'Result', 'A_ARG_TYPE_Result'],
53
+ [OUT, 'NumberReturned', 'A_ARG_TYPE_Count'],
54
+ [OUT, 'TotalMatches', 'A_ARG_TYPE_Count'],
55
+ [OUT, 'UpdateID', 'A_ARG_TYPE_UpdateID']
56
+
57
+ add_action 'CreateObject',
58
+ [IN, 'ContainerID', 'A_ARG_TYPE_ObjectID'],
59
+ [IN, 'Elements', 'A_ARG_TYPE_Result'],
60
+
61
+ [OUT, 'ObjectID', 'A_ARG_TYPE_ObjectID'],
62
+ [OUT, 'Result', 'A_ARG_TYPE_Result']
63
+
64
+ add_action 'DestroyObject',
65
+ [IN, 'ObjectID', 'A_ARG_TYPE_ObjectID']
66
+
67
+ add_action 'UpdateObject',
68
+ [IN, 'ObjectID', 'A_ARG_TYPE_ObjectID'],
69
+ [IN, 'CurrentTagValue', 'A_ARG_TYPE_TagValueList'],
70
+ [IN, 'NewTagValue', 'A_ARG_TYPE_TagValueList']
71
+
72
+ add_action 'ImportResource',
73
+ [IN, 'SourceURI', 'A_ARG_TYPE_URI'],
74
+ [IN, 'DestinationURI', 'A_ARG_TYPE_URI'],
75
+
76
+ [OUT, 'TransferID', 'A_ARG_TYPE_TransferID']
77
+
78
+ add_action 'ExportResource',
79
+ [IN, 'SourceURI', 'A_ARG_TYPE_URI'],
80
+ [IN, 'DestinationURI', 'A_ARG_TYPE_URI'],
81
+
82
+ [OUT, 'TransferID', 'A_ARG_TYPE_TransferID']
83
+
84
+ add_action 'StopTransferResource',
85
+ [IN, 'TransferID', 'A_ARG_TYPE_TransferID']
86
+
87
+ add_action 'GetTransferProgress',
88
+ [IN, 'TransferID', 'A_ARG_TYPE_TransferID'],
89
+
90
+ [OUT, 'TransferStatus', 'A_ARG_TYPE_TransferStatus'],
91
+ [OUT, 'TransferLength', 'A_ARG_TYPE_TransferLength'],
92
+ [OUT, 'TransferTotal', 'A_ARG_TYPE_TransferTotal']
93
+
94
+ add_action 'DeleteResource',
95
+ [IN, 'ResourceURI', 'A_ARG_TYPE_URI']
96
+
97
+ add_action 'CreateReference',
98
+ [IN, 'ContainerID', 'A_ARG_TYPE_ObjectID'],
99
+ [IN, 'ObjectID', 'A_ARG_TYPE_ObjectID'],
100
+ [OUT, 'NewID', 'A_ARG_TYPE_ObjectID']
101
+
102
+ add_variable 'TransferIDs', 'string', nil, nil, true
103
+ add_variable 'A_ARG_TYPE_ObjectID', 'string'
104
+ add_variable 'A_ARG_TYPE_Result', 'string' # 2.5.4 - DIDL-Lite
105
+ add_variable 'A_ARG_TYPE_SearchCriteria', 'string' # 2.5.5
106
+ add_variable 'A_ARG_TYPE_BrowseFlag', 'string',
107
+ %w[BrowseMetadata BrowseDirectChildren]
108
+ add_variable 'A_ARG_TYPE_Filter', 'string' # 2.5.7
109
+ add_variable 'A_ARG_TYPE_SortCriteria', 'string' # 2.5.8
110
+ add_variable 'A_ARG_TYPE_Index', 'ui4' # 2.5.9
111
+ add_variable 'A_ARG_TYPE_Count', 'ui4' # 2.5.10
112
+ add_variable 'A_ARG_TYPE_UpdateID', 'ui4' # 2.5.11
113
+ add_variable 'A_ARG_Type_TransferID', 'ui4' # 2.5.12
114
+ add_variable 'A_ARG_Type_TransferStatus', 'string' # 2.5.13
115
+ add_variable 'A_ARG_Type_TransferLength', 'string' # 2.5.14
116
+ add_variable 'A_ARG_Type_TransferTotal', 'string' # 2.5.15
117
+ add_variable 'A_ARG_TYPE_TagValueList', 'string' # 2.5.16
118
+ add_variable 'A_ARG_TYPE_URI', 'uri' # 2.5.17
119
+ add_variable 'SearchCapabilities', 'string' # 2.5.18
120
+ add_variable 'SortCapabilities', 'string' # 2.5.19
121
+ add_variable 'SystemUpdateID', 'ui4', nil, nil, true # 2.5.20
122
+ add_variable 'ContainerUpdateIDs', 'string', nil, nil, true # 2.5.21
123
+
124
+ def initialize(*args)
125
+ @directories = []
126
+
127
+ super
128
+ end
129
+
130
+ def on_init
131
+ @SystemUpdateID = 0
132
+
133
+ @object_count = 0
134
+ @objects = {}
135
+ @parents = {}
136
+
137
+ add_object 'root', -1
138
+ WEBrick::HTTPUtils::DefaultMimeTypes['mp3'] = 'audio/mpeg'
139
+ end
140
+
141
+ # :section: ContentServer implementation
142
+
143
+ ##
144
+ # Allows the caller to incrementally browse the hierarchy of the content
145
+ # directory, including information listing the classes of objects available
146
+ # in any container.
147
+
148
+ def Browse(object_id, browse_flag, filter, starting_index, requested_count,
149
+ sort_criteria)
150
+ filter = filter.split ','
151
+ object_id = object_id.to_i
152
+ update_id = 0
153
+
154
+ case browse_flag
155
+ when 'BrowseMetadata' then
156
+ number_returned = 1
157
+ total_matches = 1
158
+
159
+ result = metadata_result object_id
160
+ when 'BrowseDirectChildren' then
161
+ number_returned, total_matches, result = children_result object_id
162
+ else
163
+ raise "unknown BrowseFlag #{browse_flag}"
164
+ end
165
+
166
+ [nil, result, number_returned, total_matches, update_id]
167
+ end
168
+
169
+ ##
170
+ # Returns the current value of the SystemUpdateID state variable. For use
171
+ # by clients that want to poll for any changes in the content directory
172
+ # instead of subscribing to events.
173
+
174
+ def GetSystemUpdateID
175
+ [nil, @SystemUpdateID]
176
+ end
177
+
178
+ # :section: Support implementation
179
+
180
+ ##
181
+ # Adds object +name+ to the directory tree under +parent+
182
+
183
+ def add_object(name, parent)
184
+ object_id = @object_count
185
+ @object_count += 1
186
+
187
+ @objects[object_id] = name
188
+ @objects[name] = object_id
189
+
190
+ @parents[object_id] = parent
191
+
192
+ object_id
193
+ end
194
+
195
+ ##
196
+ # Adds +directory+ as a path searched by the content server
197
+
198
+ def add_directory(directory)
199
+ return self if @directories.include? directory
200
+
201
+ add_object directory, 0
202
+ @directories << directory
203
+
204
+ self
205
+ end
206
+
207
+ def children_result(object_id)
208
+ object = get_object object_id
209
+
210
+ children = if object_id == 0 then
211
+ @directories
212
+ else
213
+ Dir[File.join(object, '*')]
214
+ end
215
+
216
+ result = make_result do |xml|
217
+ children.each do |child|
218
+ child_id = get_object child, object_id
219
+
220
+ result_object xml, child, child_id, File.basename(child)
221
+ end
222
+ end
223
+
224
+ [children.length, children.length, result]
225
+ end
226
+
227
+ def get_object(name, parent_id = nil)
228
+ if @objects.key? name then
229
+ @objects[name]
230
+ elsif parent_id.nil? then
231
+ raise Error, "object #{name} does not exist"
232
+ else
233
+ add_object name, parent_id
234
+ end
235
+ end
236
+
237
+ def get_parent(object_id)
238
+ if @parents.key? object_id then
239
+ @parents[object_id]
240
+ else
241
+ raise Error, "invalid object id #{object_id}"
242
+ end
243
+ end
244
+
245
+ def item_class(mime_type)
246
+ case mime_type
247
+ when /^image/ then
248
+ 'object.item.imageItem'
249
+ when /^audio/ then
250
+ 'object.item.audioItem'
251
+ else
252
+ puts "unhandled mime type #{mime_type}"
253
+ 'object.item'
254
+ end
255
+ end
256
+
257
+ def make_result
258
+ result = []
259
+
260
+ xml = Builder::XmlMarkup.new :indent => 2, :target => result
261
+ xml.tag! 'DIDL-Lite',
262
+ 'xmlns' => 'urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/',
263
+ 'xmlns:dc' => 'http://purl.org/dc/elements/1.1/',
264
+ 'xmlns:upnp' => 'urn:schemas-upnp-org:metadata-1-0/upnp/' do
265
+ yield xml
266
+ end
267
+
268
+ result.join
269
+ end
270
+
271
+ def metadata_result(object_id)
272
+ object = get_object object_id
273
+ parent = get_parent object_id
274
+
275
+ title = File.basename object
276
+
277
+ make_result do |xml|
278
+ result_object xml, object, object_id, title
279
+ end
280
+ end
281
+
282
+ def mount_extra(http_server)
283
+ super
284
+
285
+ @directories.each do |root|
286
+ root_id = get_object root
287
+ path = File.join service_path, root_id.to_s
288
+
289
+ http_server.mount path, WEBrick::HTTPServlet::FileHandler, root
290
+ end
291
+ end
292
+
293
+ def resource(xml, object, mime_type, stat)
294
+ info = nil
295
+ url = nil
296
+
297
+ case mime_type
298
+ when /^audio\/(.*)/ then
299
+ pn = case $1
300
+ when 'mpeg' then
301
+ 'MP3'
302
+ end
303
+
304
+ pn = "DLNA.ORG_PN=#{pn}"
305
+
306
+ additional = [pn, 'DLNA.ORG_OP=01', 'DLNA.ORG_CI=0'].compact.join ';'
307
+
308
+ info = ['http-get', '*', mime_type, additional]
309
+ when /^image\/(.*)/ then
310
+ pn = case $1
311
+ when 'jpeg' then
312
+ 'JPEG_LRG'
313
+ end
314
+
315
+ pn = "DLNA.ORG_PN=#{pn}"
316
+
317
+ additional = [pn, 'DLNA.ORG_OP=01', 'DLNA.ORG_CI=0'].compact.join ';'
318
+
319
+ info = ['http-get', '*', mime_type, additional]
320
+ end
321
+
322
+ if info then
323
+ url = resource_url object
324
+
325
+ attributes = {
326
+ :protocolInfo => info.join(':'),
327
+ :size => stat.size,
328
+ }
329
+
330
+ xml.res attributes, URI.escape(url)
331
+ end
332
+ end
333
+
334
+ ##
335
+ # A URL to this object on this server. Correctly handles multi-homed
336
+ # servers.
337
+
338
+ def resource_url(object)
339
+ _, port, host, addr = Thread.current[:WEBrickSocket].addr
340
+
341
+ root = root_for object
342
+ root_id = get_object root
343
+
344
+ object = object.sub root, ''
345
+
346
+ File.join "http://#{addr}:#{port}", service_path, root_id.to_s, object
347
+ end
348
+
349
+ def result_object(xml, object, object_id, title)
350
+ if object_id == 0 then
351
+ result_container xml, object, object_id, @directories.length, title
352
+ elsif File.directory? object then
353
+ children = Dir[File.join(object, '*/')].length
354
+
355
+ result_container xml, object, object_id, children, title
356
+ else
357
+ result_item xml, object, object_id, title
358
+ end
359
+ end
360
+
361
+ def result_container(xml, object, object_id, children, title)
362
+ xml.tag! 'container', :id => object_id, :parentID => get_parent(object_id),
363
+ :restricted => true, :childCount => children do
364
+ xml.dc :title, title
365
+ xml.upnp :class, 'object.container'
366
+ end
367
+ end
368
+
369
+ def result_item(xml, object, object_id, title)
370
+ inn, out, = Open3.popen3('file', '-bI', object)
371
+ inn.close
372
+
373
+ mime_type = out.read.strip
374
+
375
+ stat = File.stat object
376
+
377
+ xml.tag! 'item', :id => object_id, :parentID => get_parent(object_id),
378
+ :restricted => true, :childCount => 0 do
379
+ xml.dc :title, title
380
+ xml.dc :date, stat.ctime.iso8601
381
+ xml.upnp :class, item_class(mime_type)
382
+
383
+ resource xml, object, mime_type, stat
384
+ end
385
+ end
386
+
387
+ ##
388
+ # Returns the root for +object_id+
389
+
390
+ def root_for(object_id)
391
+ object_id = get_object object_id unless Integer === object_id
392
+
393
+ while (parent_id = get_parent(object_id)) != 0 do
394
+ object_id = parent_id
395
+ end
396
+
397
+ get_object object_id
398
+ end
399
+
400
+ end
401
+
metadata ADDED
@@ -0,0 +1,101 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: UPnP-ContentDirectory
3
+ version: !ruby/object:Gem::Version
4
+ version: "1.0"
5
+ platform: ruby
6
+ authors:
7
+ - Eric Hodel
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain:
11
+ - |
12
+ -----BEGIN CERTIFICATE-----
13
+ MIIDNjCCAh6gAwIBAgIBADANBgkqhkiG9w0BAQUFADBBMRAwDgYDVQQDDAdkcmJy
14
+ YWluMRgwFgYKCZImiZPyLGQBGRYIc2VnbWVudDcxEzARBgoJkiaJk/IsZAEZFgNu
15
+ ZXQwHhcNMDcxMjIxMDIwNDE0WhcNMDgxMjIwMDIwNDE0WjBBMRAwDgYDVQQDDAdk
16
+ cmJyYWluMRgwFgYKCZImiZPyLGQBGRYIc2VnbWVudDcxEzARBgoJkiaJk/IsZAEZ
17
+ FgNuZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCbbgLrGLGIDE76
18
+ LV/cvxdEzCuYuS3oG9PrSZnuDweySUfdp/so0cDq+j8bqy6OzZSw07gdjwFMSd6J
19
+ U5ddZCVywn5nnAQ+Ui7jMW54CYt5/H6f2US6U0hQOjJR6cpfiymgxGdfyTiVcvTm
20
+ Gj/okWrQl0NjYOYBpDi+9PPmaH2RmLJu0dB/NylsDnW5j6yN1BEI8MfJRR+HRKZY
21
+ mUtgzBwF1V4KIZQ8EuL6I/nHVu07i6IkrpAgxpXUfdJQJi0oZAqXurAV3yTxkFwd
22
+ g62YrrW26mDe+pZBzR6bpLE+PmXCzz7UxUq3AE0gPHbiMXie3EFE0oxnsU3lIduh
23
+ sCANiQ8BAgMBAAGjOTA3MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQW
24
+ BBS5k4Z75VSpdM0AclG2UvzFA/VW5DANBgkqhkiG9w0BAQUFAAOCAQEAHagT4lfX
25
+ kP/hDaiwGct7XPuVGbrOsKRVD59FF5kETBxEc9UQ1clKWngf8JoVuEoKD774dW19
26
+ bU0GOVWO+J6FMmT/Cp7nuFJ79egMf/gy4gfUfQMuvfcr6DvZUPIs9P/TlK59iMYF
27
+ DIOQ3DxdF3rMzztNUCizN4taVscEsjCcgW6WkUJnGdqlu3OHWpQxZBJkBTjPCoc6
28
+ UW6on70SFPmAy/5Cq0OJNGEWBfgD9q7rrs/X8GGwUWqXb85RXnUVi/P8Up75E0ag
29
+ 14jEc90kN+C7oI/AGCBN0j6JnEtYIEJZibjjDJTSMWlUKKkj30kq7hlUC2CepJ4v
30
+ x52qPcexcYZR7w==
31
+ -----END CERTIFICATE-----
32
+
33
+ date: 2008-07-23 00:00:00 -07:00
34
+ default_executable:
35
+ dependencies:
36
+ - !ruby/object:Gem::Dependency
37
+ name: UPnP
38
+ type: :runtime
39
+ version_requirement:
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: 1.1.0
45
+ version:
46
+ - !ruby/object:Gem::Dependency
47
+ name: hoe
48
+ type: :development
49
+ version_requirement:
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 1.7.0
55
+ version:
56
+ description: A UPnP ContentDirectory service with some DLNA extensions. Currently this is a work in progress, and is only adequate for viewing images on a PlayStation 3.
57
+ email:
58
+ - drbrain@segment7.net
59
+ executables: []
60
+
61
+ extensions: []
62
+
63
+ extra_rdoc_files:
64
+ - History.txt
65
+ - Manifest.txt
66
+ - README.txt
67
+ files:
68
+ - History.txt
69
+ - Manifest.txt
70
+ - README.txt
71
+ - Rakefile
72
+ - lib/UPnP/service/content_directory.rb
73
+ has_rdoc: true
74
+ homepage: http://seattlerb.org/UPnP-ContentDirectory
75
+ post_install_message:
76
+ rdoc_options:
77
+ - --main
78
+ - README.txt
79
+ require_paths:
80
+ - lib
81
+ required_ruby_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: "0"
86
+ version:
87
+ required_rubygems_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: "0"
92
+ version:
93
+ requirements: []
94
+
95
+ rubyforge_project: seattlerb
96
+ rubygems_version: 1.2.0
97
+ signing_key:
98
+ specification_version: 2
99
+ summary: A UPnP ContentDirectory service with some DLNA extensions
100
+ test_files: []
101
+
@@ -0,0 +1,4 @@
1
+ Κ�����h.�H?ؠ+�Q�3 �x��!��he�.�рNY@@3��~ؑ�ł����d"6̈�*o�
2
+ ��j��+��@G�4�a4"���K�ŽVц�yQ�N�P C�Ľ*�i,w��.~�mѡȡ�4��@*�����a�Z$�y̱���� �x��ʼn5c�;�����;X����&�E��V�~T����6^u����{֢�����
3
+
4
+ 淅w:N��q��w��X|b�}���3ya�n�K�N�AH��L�kN�d