Class: UPnP::Service::ContentDirectory

Inherits:
UPnP::Service
  • Object
show all
Defined in:
lib/UPnP/service/content_directory.rb

Overview

A UPnP ContentDirectory service. See upnp.org for specifications.

Constant Summary collapse

VERSION =
'1.0'

Instance Method Summary collapse

Constructor Details

#initialize(*args) ⇒ ContentDirectory

Returns a new instance of ContentDirectory.



124
125
126
127
128
# File 'lib/UPnP/service/content_directory.rb', line 124

def initialize(*args)
  @directories = []

  super
end

Instance Method Details

#add_directory(directory) ⇒ Object

Adds directory as a path searched by the content server



198
199
200
201
202
203
204
205
# File 'lib/UPnP/service/content_directory.rb', line 198

def add_directory(directory)
  return self if @directories.include? directory

  add_object directory, 0
  @directories << directory

  self
end

#add_object(name, parent) ⇒ Object

Adds object name to the directory tree under parent



183
184
185
186
187
188
189
190
191
192
193
# File 'lib/UPnP/service/content_directory.rb', line 183

def add_object(name, parent)
  object_id = @object_count
  @object_count += 1

  @objects[object_id] = name
  @objects[name] = object_id

  @parents[object_id] = parent

  object_id
end

#Browse(object_id, browse_flag, filter, starting_index, requested_count, sort_criteria) ⇒ Object

Allows the caller to incrementally browse the hierarchy of the content directory, including information listing the classes of objects available in any container.



148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/UPnP/service/content_directory.rb', line 148

def Browse(object_id, browse_flag, filter, starting_index, requested_count,
           sort_criteria)
  filter = filter.split ','
  object_id = object_id.to_i
  update_id = 0

  case browse_flag
  when 'BrowseMetadata' then
    number_returned = 1
    total_matches = 1

    result =  object_id
  when 'BrowseDirectChildren' then
    number_returned, total_matches, result = children_result object_id
  else
    raise "unknown BrowseFlag #{browse_flag}"
  end

  [nil, result, number_returned, total_matches, update_id]
end

#children_result(object_id) ⇒ Object



207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
# File 'lib/UPnP/service/content_directory.rb', line 207

def children_result(object_id)
  object = get_object object_id

  children = if object_id == 0 then
               @directories
             else
               Dir[File.join(object, '*')]
             end

  result = make_result do |xml|
    children.each do |child|
      child_id = get_object child, object_id

      result_object xml, child, child_id, File.basename(child)
    end
  end

  [children.length, children.length, result]
end

#get_object(name, parent_id = nil) ⇒ Object



227
228
229
230
231
232
233
234
235
# File 'lib/UPnP/service/content_directory.rb', line 227

def get_object(name, parent_id = nil)
  if @objects.key? name then
    @objects[name]
  elsif parent_id.nil? then
    raise Error, "object #{name} does not exist"
  else
    add_object name, parent_id
  end
end

#get_parent(object_id) ⇒ Object



237
238
239
240
241
242
243
# File 'lib/UPnP/service/content_directory.rb', line 237

def get_parent(object_id)
  if @parents.key? object_id then
    @parents[object_id]
  else
    raise Error, "invalid object id #{object_id}"
  end
end

#GetSearchCapabilitiesObject

Returns the searching capabilities supported by the device



17
18
# File 'lib/UPnP/service/content_directory.rb', line 17

add_action 'GetSearchCapabilities',
[OUT, 'SearchCaps', 'SearchCapabilities']

#GetSortCapabilitiesObject

Returns the CSV list of metadata tags that can be used in sortCriteria



23
24
# File 'lib/UPnP/service/content_directory.rb', line 23

add_action 'GetSortCapabilities',
[OUT, 'SortCaps', 'SortCapabilities']

#GetSystemUpdateIDObject

Returns the current value of the SystemUpdateID state variable. For use by clients that want to poll for any changes in the content directory instead of subscribing to events.



174
175
176
# File 'lib/UPnP/service/content_directory.rb', line 174

def GetSystemUpdateID
  [nil, @SystemUpdateID]
end

#item_class(mime_type) ⇒ Object



245
246
247
248
249
250
251
252
253
254
255
# File 'lib/UPnP/service/content_directory.rb', line 245

def item_class(mime_type)
  case mime_type
  when /^image/ then
    'object.item.imageItem'
  when /^audio/ then
    'object.item.audioItem'
  else
    puts "unhandled mime type #{mime_type}"
    'object.item'
  end
end

#make_resultObject



257
258
259
260
261
262
263
264
265
266
267
268
269
# File 'lib/UPnP/service/content_directory.rb', line 257

def make_result
  result = []

  xml = Builder::XmlMarkup.new :indent => 2, :target => result
  xml.tag! 'DIDL-Lite',
           'xmlns' => 'urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/',
           'xmlns:dc' => 'http://purl.org/dc/elements/1.1/',
           'xmlns:upnp' => 'urn:schemas-upnp-org:metadata-1-0/upnp/' do
    yield xml
  end

  result.join
end

#metadata_result(object_id) ⇒ Object



271
272
273
274
275
276
277
278
279
280
# File 'lib/UPnP/service/content_directory.rb', line 271

def (object_id)
  object = get_object object_id
  parent = get_parent object_id

  title = File.basename object

  make_result do |xml|
    result_object xml, object, object_id, title
  end
end

#mount_extra(http_server) ⇒ Object



282
283
284
285
286
287
288
289
290
291
# File 'lib/UPnP/service/content_directory.rb', line 282

def mount_extra(http_server)
  super

  @directories.each do |root|
    root_id = get_object root
    path = File.join service_path, root_id.to_s

    http_server.mount path, WEBrick::HTTPServlet::FileHandler, root
  end
end

#on_initObject



130
131
132
133
134
135
136
137
138
139
# File 'lib/UPnP/service/content_directory.rb', line 130

def on_init
  @SystemUpdateID = 0

  @object_count = 0
  @objects = {}
  @parents = {}

  add_object 'root', -1
  WEBrick::HTTPUtils::DefaultMimeTypes['mp3'] = 'audio/mpeg'
end

#resource(xml, object, mime_type, stat) ⇒ Object



293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
# File 'lib/UPnP/service/content_directory.rb', line 293

def resource(xml, object, mime_type, stat)
  info = nil
  url = nil

  case mime_type
  when /^audio\/(.*)/ then
    pn = case $1
         when 'mpeg' then
           'MP3'
         end

    pn = "DLNA.ORG_PN=#{pn}"

    additional = [pn, 'DLNA.ORG_OP=01', 'DLNA.ORG_CI=0'].compact.join ';'

    info = ['http-get', '*', mime_type, additional]
  when /^image\/(.*)/ then
    pn = case $1
         when 'jpeg' then
           'JPEG_LRG'
         end

    pn = "DLNA.ORG_PN=#{pn}"

    additional = [pn, 'DLNA.ORG_OP=01', 'DLNA.ORG_CI=0'].compact.join ';'

    info = ['http-get', '*', mime_type, additional]
  end

  if info then
    url = resource_url object

    attributes = {
      :protocolInfo => info.join(':'),
      :size => stat.size,
    }

    xml.res attributes, URI.escape(url)
  end
end

#resource_url(object) ⇒ Object

A URL to this object on this server. Correctly handles multi-homed servers.



338
339
340
341
342
343
344
345
346
347
# File 'lib/UPnP/service/content_directory.rb', line 338

def resource_url(object)
  _, port, host, addr = Thread.current[:WEBrickSocket].addr

  root = root_for object
  root_id = get_object root

  object = object.sub root, ''

  File.join "http://#{addr}:#{port}", service_path, root_id.to_s, object
end

#result_container(xml, object, object_id, children, title) ⇒ Object



361
362
363
364
365
366
367
# File 'lib/UPnP/service/content_directory.rb', line 361

def result_container(xml, object, object_id, children, title)
  xml.tag! 'container', :id => object_id, :parentID => get_parent(object_id),
                 :restricted => true, :childCount => children do
    xml.dc :title, title
    xml.upnp :class, 'object.container'
  end
end

#result_item(xml, object, object_id, title) ⇒ Object



369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
# File 'lib/UPnP/service/content_directory.rb', line 369

def result_item(xml, object, object_id, title)
  inn, out, = Open3.popen3('file', '-bI', object)
  inn.close

  mime_type = out.read.strip

  stat = File.stat object

  xml.tag! 'item', :id => object_id, :parentID => get_parent(object_id),
           :restricted => true, :childCount => 0 do
    xml.dc :title, title
    xml.dc :date, stat.ctime.iso8601
    xml.upnp :class, item_class(mime_type)

    resource xml, object, mime_type, stat
  end
end

#result_object(xml, object, object_id, title) ⇒ Object



349
350
351
352
353
354
355
356
357
358
359
# File 'lib/UPnP/service/content_directory.rb', line 349

def result_object(xml, object, object_id, title)
  if object_id == 0 then
    result_container xml, object, object_id, @directories.length, title
  elsif File.directory? object then
    children = Dir[File.join(object, '*/')].length

    result_container xml, object, object_id, children, title
  else
    result_item xml, object, object_id, title
  end
end

#root_for(object_id) ⇒ Object

Returns the root for object_id



390
391
392
393
394
395
396
397
398
# File 'lib/UPnP/service/content_directory.rb', line 390

def root_for(object_id)
  object_id = get_object object_id unless Integer === object_id

  while (parent_id = get_parent(object_id)) != 0 do
    object_id = parent_id
  end

  get_object object_id
end