Home | Trees | Indices | Help |
---|
|
1 # -*- coding: utf-8 -*- 2 # Elisa - Home multimedia server 3 # Copyright (C) 2006-2008 Fluendo Embedded S.L. (www.fluendo.com). 4 # All rights reserved. 5 # 6 # This file is available under one of two license agreements. 7 # 8 # This file is licensed under the GPL version 3. 9 # See "LICENSE.GPL" in the root of this distribution including a special 10 # exception to use Elisa with Fluendo's plugins. 11 # 12 # The GPL part of Elisa is also available under a commercial licensing 13 # agreement from Fluendo. 14 # See "LICENSE.Elisa" in the root directory of this distribution package 15 # for details on that license. 16 17 """ 18 UPnP MediaServer 19 """ 20 21 __maintainer__ = "Philippe Normand <philippe@fluendo.com>" 22 23 from elisa.base_components import service_provider 24 from elisa.core import media_uri, log 25 from elisa.core import common, component 26 from elisa.core.observers.list import ListObserver 27 from elisa.core.utils import misc, classinit 28 from elisa.core.bus.bus_message import HttpResource, PBReferenceable, \ 29 ComponentsLoaded, LocationsList, CoherencePlugin 30 31 from twisted.internet import defer 32 from twisted.web import resource, static, server 33 from twisted.spread import pb 34 import mimetypes, threading 35 import urllib 36 5052 """ 53 DOCME 54 """ 55 56 57 __metaclass__ = classinit.ClassInitMeta 58 __classinit__ = classinit.build_properties 59 60 addSlash = True 61 65100 10967 return """ 68 <html> 69 <head> 70 <title>Elisa UPnP page</title> 71 </head> 72 <body> 73 <h1>UPnP page!</h1> 74 </body> 75 </html> 76 """7779 child = self 80 if path != '': 81 media_manager = common.application.media_manager 82 try: 83 media_id = int(path) 84 except ValueError: 85 log.debug('upnp_resource', "wrong media ID: %r", path) 86 else: 87 cm = media_manager.media_db.get_media_with_id(int(path)) 88 uri = media_uri.MediaUri(cm.uri) 89 f = uri.host + uri.path 90 log.debug('upnp_resource', 'serving %r', f) 91 child = static.File(f) 92 return child93 97111 """ 112 DOCME 113 """ 114 115 addSlash = True 116161 162 163 ALL=0 164 FOLDER=1 165118 return """ 119 <html> 120 <head> 121 <title>Elisa covers</title> 122 </head> 123 <body> 124 <h1>Get lost :)</h1> 125 </body> 126 </html> 127 """128130 child = self 131 params = request.args 132 artist = params.get('artist',[]) 133 album = params.get('album',[]) 134 135 if artist: 136 artist = artist[0] 137 138 if album: 139 album = album[0] 140 141 def got_metadata(metadata): 142 data = "" 143 if 'cover' in metadata and metadata['cover'] is not None: 144 uri = metadata['cover'] 145 f = open(uri.host + uri.path) 146 data = f.read() 147 f.close() 148 149 request.write(data) 150 request.finish()151 152 if artist and album: 153 metadata_manager = common.application.metadata_manager 154 dfr = metadata_manager.get_metadata({'artist': artist, 155 'album': album, 156 'cover': None}) 157 dfr.addCallback(got_metadata) 158 child = DeferredResource(dfr) 159 160 return child167 """ 168 Provides UPnP clients access to UPnP resources 169 """ 170 171 # TODO: should this be translated, too? 172 tree = { '0': ('Elisa', { 173 'A' : ('Music', 174 {'A': ('All', {'content': (ALL, 'audio', None)}), 175 'B': ('By artist', 176 {'content': media_uri.MediaUri('elisa://localhost/artists/')}), 177 'C': ('By album', 178 {'content': media_uri.MediaUri('elisa://localhost/albums/')}), 179 'D': ('By folder', {'content': (FOLDER, 'audio', None)}) 180 }), 181 'B': ('Videos', 182 {'A': ('All', {'content': (ALL, 'video', None)}), 183 'B': ('By folder', {'content': (FOLDER, 'video', None)}), 184 }), 185 'C': ('Pictures', 186 {'A': ('All', {'content': (ALL, 'image', None)}), 187 'B': ('By folder', {'content': (FOLDER, 'image', None)}), 188 }), 189 }) 190 } 191307193 service_provider.ServiceProvider.__init__(self) 194 self.container_id = 1 195 self.http_upnp_path = "data/upnp" 196 self._upnp_resource = UPnPResource(self) 197 self._media_locations = {'audio':[], 'video':[], 'image':[]} 198 self._lock = threading.Lock() 199 self._locations_observable = None 200 self._locations_observer = LocationsObserver(self)201203 common.application.bus.register(self._bus_message_received, 204 (ComponentsLoaded, LocationsList))205207 if isinstance(msg, ComponentsLoaded): 208 # register the cover HTTP resource to the HTTP server 209 msg = HttpResource("data/covers", CoverResource()) 210 common.application.bus.send_message(msg) 211 212 # register the UPnPResource to the HTTP server, via the Message bus 213 msg = HttpResource(self.http_upnp_path, self._upnp_resource) 214 common.application.bus.send_message(msg) 215 216 # register myself to the PB service 217 msg = PBReferenceable('get_cache_manager', self) 218 common.application.bus.send_message(msg) 219 else: 220 self._locations_observable = msg.locations 221 for location in self._locations_observable: 222 self.add_media_location(location) 223 self._locations_observable.add_observer(self._locations_observer)224226 if self._locations_observable: 227 self._locations_observable.remove_observer(self._locations_observer) 228 service_provider.ServiceProvider.clean(self)229231 # TODO: add support in the http server to serve daap and other 232 # URI schemes we correctly handle elsewhere in Elisa 233 if location.uri.scheme not in ('elisa', 'file'): 234 self.debug("Unsupported URI scheme for %r media location", 235 location.uri) 236 else: 237 self.info("Adding media location: %r with media_types %r", 238 location.uri, location.media_types) 239 for media_type in location.media_types: 240 self._media_locations[media_type].append(location.uri)241243 self.info("Removing media location %r with media_types %r", 244 location.uri, location.media_types) 245 for media_type in location.media_types: 246 if location.uri in self._media_locations[media_type]: 247 self._media_locations[media_type].remove(location.uri)248250 # add a new plugin in Coherence 251 # 'version': 2 252 args = {'name': 'Elisa medias', 253 'host': host, } 254 msg = CoherencePlugin('ElisaMediaStore', args) 255 common.application.bus.send_message(msg)256258 try: 259 self._lock.acquire() 260 self.container_id += 1 261 finally: 262 self._lock.release() 263 return self.container_id264 265 @defer.deferredGenerator267 self.debug("Got %r", children) 268 media_manager = common.application.media_manager 269 db = media_manager.media_db 270 nodes = [] 271 272 def got_result(is_directory, child_uri): 273 item = {} 274 if is_directory: 275 container_id = "%s-%s" % (parent, self._next_id()) 276 container_name = child_uri.label 277 size = 1 278 item = {'id': container_id, 279 'parent_id': parent, 280 'name': container_name, 281 'mimetype': 'directory', 282 'size': size, 283 'children': [], 284 'location': {}, 285 'content': child_uri 286 } 287 else: 288 node = db.get_media_information(child_uri, extended=True) 289 if node: 290 item = self._node_to_dict(node, parent) 291 item['content'] = child_uri 292 else: 293 self.debug("%r not found in DB. Can't serve it", child_uri) 294 return item295 296 if db: 297 for child in children: 298 d = media_manager.is_directory(child[0]) 299 d.addCallback(got_result, child[0]) 300 wfd = defer.waitForDeferred(d) 301 yield wfd 302 node = wfd.getResult() 303 if node: 304 nodes.append(node) 305 306 yield nodes309 nodes = [] 310 if kind == ALL: 311 db = common.application.media_manager.media_db 312 if db: 313 for media_node in db.get_medias(media_type=media_type): 314 node_dict = self._node_to_dict(media_node, parent) 315 node_dict['content'] = media_uri.MediaUri(media_node.uri) 316 nodes.append(node_dict) 317 elif kind == FOLDER: 318 locations = self._media_locations[media_type] 319 for location in locations: 320 container_id = "%s-%s" % (parent, self._next_id()) 321 container_name = location.label 322 size = 1 323 item = {'id': container_id, 324 'parent_id': parent, 325 'name': container_name, 326 'mimetype': 'directory', 327 'size': size, 328 'children': [], 329 'location': {}, 330 'content': (FOLDER, media_type, location) 331 } 332 nodes.append(item) 333 return nodes334336 media_manager = common.application.media_manager 337 338 def fill_cache(nodes): 339 for node in nodes: 340 uri = node['content'] 341 del node['content'] 342 path = node['id'].split('-') 343 tree[path[-1]] = (node['name'],{'content': uri}) 344 return nodes345 346 if 'content' in tree: 347 348 if isinstance(tree['content'], media_uri.MediaUri): 349 uri = tree['content'] 350 children = [] 351 self.debug("Retrieving children of %r", uri) 352 dfr = media_manager.get_direct_children(uri, 353 children) 354 dfr.addCallback(self._got_children, parent) 355 dfr.addCallback(fill_cache) 356 else: 357 kind, media_type, uri = tree['content'] 358 if uri: 359 children = [] 360 dfr = media_manager.get_direct_children(uri, 361 children) 362 dfr.addCallback(self._got_children, parent) 363 dfr.addCallback(fill_cache) 364 else: 365 nodes = self._db_filter(kind, media_type, parent) 366 nodes = fill_cache(nodes) 367 dfr = defer.Deferred() 368 dfr.callback(nodes) 369 370 else: 371 containers = [] 372 for container_id, container in tree.iteritems(): 373 sub_tree = container[1] 374 container_name = container[0] 375 size = len(sub_tree.keys()) 376 container_id = "%s-%s" % (parent, container_id) 377 node_dict = {'id': container_id, 378 'parent_id': parent, 379 'name': container_name, 380 'mimetype': 'directory', 381 'size': size, 382 'children': [], 383 'location': {} 384 } 385 containers.append(node_dict) 386 387 dfr = defer.Deferred() 388 dfr.callback(containers) 389 390 return dfr 391393 # try to guess the uri mime-type 394 node_dict = {} 395 if node: 396 mimetype, sub_type = mimetypes.guess_type(node.uri) 397 if not mimetype: 398 # fallback to incomplete mime-type based on node.format 399 mimetype = '%s/' % node.format 400 401 ip, port = self._upnp_resource.ip_port 402 external_location = 'http://%s:%s/%s/%s' % (ip, port, 403 self.http_upnp_path, 404 node.id) 405 self.debug(external_location) 406 407 cover_uri = None 408 if 'artist' in node.keys() and 'album' in node.keys(): 409 artist = media_uri.quote(node['artist']) 410 album = media_uri.quote(node['album']) 411 cover_uri = 'http://%s:%s/%s/cover.jpg?artist=%s&album=%s' 412 cover_uri = cover_uri % (ip, port, "data/covers", 413 artist, album) 414 self.debug(cover_uri) 415 416 internal_location = node.uri 417 node_dict = {'id': "%s-%s" % (parent_id,self._next_id()), 418 'parent_id': parent_id, 419 'name': node.short_name, 420 'mimetype': mimetype, 421 'size': 0, 422 'cover': cover_uri, 423 'children': [], 424 'location': {'internal': internal_location, 425 'external': external_location} 426 } 427 return node_dict428430 db = common.application.media_manager.media_db 431 if db: 432 medias = db.get_medias() 433 for media in medias: 434 if media.typ == media_type: 435 return media.source_id 436 return 0437439 """ 440 Returns a dict with following keys: 441 id = id in the media db 442 parent_id = parent_id in the media db 443 name = title, album name or basename 444 mimetype = 'directory' or real mimetype 445 size = in bytes (??) 446 children = list of objects for which this item is the parent 447 location = filesystem path if item is a file 448 """ 449 result = {} 450 451 path = container_id.split('-') 452 if len(path) > 1: 453 parent_id = '-'.join(path[:-1]) 454 else: 455 parent_id = '0' 456 457 self.debug("Looking for container %r with parent %r", container_id, 458 parent_id) 459 i = 0 460 c = None 461 tree = self.tree 462 container_name = '' 463 464 for c_id in path: 465 c = tree[c_id][1] 466 container_name = tree[c_id][0] 467 tree = c 468 469 self.debug("Looking in container: %r", tree) 470 471 def got_containers(child_list): 472 size = len(child_list) 473 container = {'id': container_id, 474 'parent_id': parent_id, 475 'name': container_name, 476 'mimetype': 'directory', 477 'size': size, 478 'children': child_list, 479 'location': {} 480 } 481 return container482 483 if c: 484 result = self._get_containers(c, container_id) 485 result.addCallback(got_containers) 486 return result 487
Home | Trees | Indices | Help |
---|
Generated by Epydoc 3.0beta1 on Wed Jan 16 19:10:21 2008 | http://epydoc.sourceforge.net |