enigma2 20121210 master -> 20121219
[enigma2.git] / usr / lib / enigma2 / python / Plugins / SystemPlugins / UPnP / UPnPCore.py
1 # -*- coding: UTF-8 -*-
2
3 from coherence.base import Coherence
4
5 from coherence.upnp.devices.control_point import ControlPoint
6 from coherence.upnp.devices.media_renderer import MediaRenderer
7 from coherence.upnp.devices.media_server import MediaServer
8 from coherence.upnp.devices.media_server_client import MediaServerClient
9
10 class Statics:
11         CONTAINER_ID_ROOT = 0
12         CONTAINER_ID_SERVERLIST = -1
13
14         ITEM_TYPE_AUDIO = "audio"
15         ITEM_TYPE_CONTAINER = "container"
16         ITEM_TYPE_PICTURE = "picture"
17         ITEM_TYPE_SERVER = "server"
18         ITEM_TYPE_FILE = "file"
19         ITEM_TYPE_VIDEO = "video"
20
21         META_ALBUM = 'album'
22         META_ALBUM_ART_URI = 'album_art_uri'
23         META_ARTIST = 'artist'
24         META_BITRATE = 'bitrate'
25         META_CHILD_COUNT = 'child_count'
26         META_DATE = 'date'
27         META_DURATION = 'duration'
28         META_GENRE = 'genre'
29         META_METATYPE = 'metatype'
30         META_RESOLUTION = 'resolution'
31         META_SIZE = 'size'
32         META_TITLE = 'title'
33         META_TYPE = 'type'
34         META_URI = 'uri'
35
36         SORT_TITLE_ASC = "+dc:title"
37         SORT_TITLE_DSC = "+dc:title"
38
39 '''
40 This is a "managed" UPnP A/V Controlpoint which eases the use of UPnP, for Browsing media or adding a Renderer
41 please see the helper classes (UPnPBrowser and AbstractUPnPRenderer) for more
42 '''
43 class ManagedControlPoint(object):
44         def __init__(self):
45                 self.coherence = Coherence({'logmode':'warning'})
46                 self._controlPoint = ControlPoint(self.coherence, auto_client=['MediaServer','MediaRenderer'])
47                 self._controlPoint.connect(self._onMediaServerDetected, 'Coherence.UPnP.ControlPoint.MediaServer.detected')
48                 self._controlPoint.connect(self._onMediaServerRemoved, 'Coherence.UPnP.ControlPoint.MediaServer.removed')
49                 self._controlPoint.connect(self._onMediaRendererDetected, 'Coherence.UPnP.ControlPoint.MediaRenderer.detected')
50                 self._controlPoint.connect(self._onMediaRendererRemoved, 'Coherence.UPnP.ControlPoint.MediaRenderer.removed')
51                 self._controlPoint.connect(self._onMediaDeviceDectected, 'Coherence.UPnP.Device.detection_completed')
52
53                 self.__mediaServerClients = {}
54                 self.__mediaRendererClients = {}
55                 self.__mediaDevices = {}
56
57                 self.__browser = []
58
59                 self.onMediaServerDetected = []
60                 self.onMediaServerRemoved  = []
61                 self.onMediaRendererDetected = []
62                 self.onMediaRendererRemoved = []
63                 self.onMediaDeviceDectected = []
64
65         def _onMediaServerDetected(self, client, udn):
66                 print "[DLNA] MediaServer Detected: %s (%s)" % (client.device.get_friendly_name(), client.device.get_friendly_device_type())
67                 self.__mediaServerClients[udn] = client
68                 for fnc in self.onMediaServerDetected:
69                         fnc(udn, client)
70
71         def _onMediaServerRemoved(self, udn):
72                 if self.__mediaServerClients.get(udn, None) != None:
73                         del self.__mediaServerClients[udn]
74                         for fnc in self.onMediaServerRemoved:
75                                 fnc(udn)
76
77         def _onMediaRendererDetected(self, client, udn):
78                 print "[DLNA] MediaRenderer detected: %s (%s, %s)" % (client.device.get_friendly_name(), client.device.get_friendly_device_type(), udn)
79                 self.__mediaRendererClients[udn] = client
80                 for fnc in self.onMediaRendererDetected:
81                         fnc(udn, client)
82
83         def _onMediaRendererRemoved(self, udn):
84                 print "[DLNA] MediaRenderer removed: %s" % (udn)
85                 if self.__mediaRendererClients.get(udn, None) != None:
86                         client = self.__mediaRendererClients[udn]
87                         del self.__mediaRendererClients[udn]
88                         for fnc in self.onMediaRendererRemoved:
89                                 fnc(udn)
90
91         def _onMediaDeviceDectected(self, device):
92                 print "[DLNA] Device found: %s (%s)" % (device.get_friendly_name(), device.get_friendly_device_type())
93                 self.__mediaDevices[device.udn] = device
94
95         def registerRenderer(self, classDef, **kwargs):
96                 return MediaRenderer(self.coherence, classDef, no_thread_needed=True, **kwargs)
97
98         def registerServer(self, classDef, **kwargs):
99                 return MediaServer(self.coherence, classDef, no_thread_needed=True, **kwargs)
100
101         def getServerList(self):
102                 return self.__mediaServerClients.values()
103
104         def getRenderingControlClientList(self):
105                 return self.__mediaRendererClients.values()
106
107         def getDeviceName(self, client):
108                 return client.device.get_friendly_name().encode( "utf-8" )
109
110 class Item(object):
111         @staticmethod
112         def getItemType(item):
113                 if item != None:
114                         if item.__class__.__name__ == MediaServerClient.__name__:
115                                 return Statics.ITEM_TYPE_SERVER
116
117                         itemClass = item.upnp_class.encode( "utf-8" )
118                         if Item.isContainer(item):
119                                 return Statics.ITEM_TYPE_CONTAINER
120
121                         elif itemClass.startswith("object.item"):
122                                 type = item.upnp_class.split('.')[-1]
123                                 if type == "videoItem" or type == "movie":
124                                         return Statics.ITEM_TYPE_VIDEO
125                                 elif type == "musicTrack" or type == "audioItem":
126                                         return Statics.ITEM_TYPE_AUDIO
127                                 elif type == "photo":
128                                         return Statics.ITEM_TYPE_PICTURE
129
130                 return None
131
132         @staticmethod
133         def isServer(item):
134                 return Item.getItemType(item) == Statics.ITEM_TYPE_SERVER
135
136         @staticmethod
137         def getServerName(client):
138                 return client.device.get_friendly_name().encode( "utf-8" )
139
140         '''
141         Returns the title of the current item
142         '''
143         @staticmethod
144         def getItemTitle(item):
145                 if Item.isServer(item):
146                         return Item.getServerName(item)
147
148                 if item.title != None:
149                         return item.title.encode( "utf-8" )
150                 else:
151                         return "<missing title>"
152
153         '''
154         returns the number of children for container items
155         returns -1 for non-container items
156         '''
157         @staticmethod
158         def getItemChildCount(item):
159                 if Item.getItemType(item) != Statics.ITEM_TYPE_SERVER and Item.isContainer(item):
160                         return item.childCount
161
162                 return -1
163
164         '''
165         Currently always returns a dict with the first url and meta-type, which is usually the original/non-transcoded source
166         Raises an IllegalInstanceException if you pass in a container-item
167         '''
168         @staticmethod
169         def getItemUriMeta(item):
170                 assert not Item.isContainer(item)
171
172                 for res in item.res:
173                         uri = res.data.encode( "utf-8" )
174                         meta = res.protocolInfo.split(":")[2].encode( "utf-8" )
175                         print "URL: %s\nMeta:%s" %(uri, meta)
176                         if uri:
177                                 return {Statics.META_URI : uri, Statics.META_METATYPE : meta}
178
179         @staticmethod
180         def getItemId(item):
181                 if Item.isServer(item):
182                         return item.device.get_id()
183                 else:
184                         return item.id
185
186         @staticmethod
187         def getAttrOrDefault(instance, attr, default=None):
188                 val = getattr(instance, attr, default) or default
189                 try:
190                         return val.encode( "utf-8" )
191                 except:
192                         return val
193
194         @staticmethod
195         def getItemMetadata(item):
196                 type = Item.getItemType(item)
197                 meta = {}
198                 metaNA = _('n/a')
199
200                 if type == Statics.ITEM_TYPE_SERVER:
201                         meta = {
202                                         Statics.META_TYPE : type,
203                                         Statics.META_TITLE : Item.getServerName(item),
204                                 }
205
206                 elif type == Statics.ITEM_TYPE_CONTAINER:
207                                 meta = {
208                                                 Statics.META_TYPE : type,
209                                                 Statics.META_TITLE : Item.getAttrOrDefault(item, 'title', metaNA).encode( "utf-8" ),
210                                                 Statics.META_CHILD_COUNT : Item.getItemChildCount(item),
211                                         }
212                 elif type == Statics.ITEM_TYPE_PICTURE or type == Statics.ITEM_TYPE_VIDEO:
213                         for res in item.res:
214                                 meta = {
215                                                 Statics.META_TYPE : type,
216                                                 Statics.META_METATYPE : res.protocolInfo.split(":")[2].encode( "utf-8" ),
217                                                 Statics.META_TITLE : Item.getAttrOrDefault(item, 'title', metaNA).encode( "utf-8" ),
218                                                 Statics.META_DATE : Item.getAttrOrDefault(item, 'date', metaNA).encode( "utf-8" ),
219                                                 Statics.META_RESOLUTION : Item.getAttrOrDefault(item, 'resolution', metaNA).encode( "utf-8" ),
220                                                 Statics.META_SIZE : Item.getAttrOrDefault(item, 'size', -1),
221                                                 Statics.META_URI : Item.getAttrOrDefault(res, 'data'),
222                                         }
223                                 if type == Statics.ITEM_TYPE_PICTURE:
224                                         meta[Statics.META_ALBUM] = Item.getAttrOrDefault(item, 'album', metaNA).encode( "utf-8" )
225                                 elif type == Statics.ITEM_TYPE_VIDEO:
226                                         meta[Statics.META_ALBUM_ART_URI] = Item.getAttrOrDefault(item, 'albumArtURI')
227
228                                 return meta
229                 elif type == Statics.ITEM_TYPE_AUDIO:
230                         for res in item.res:
231                                 meta = {
232                                                 Statics.META_TYPE : type,
233                                                 Statics.META_METATYPE : res.protocolInfo.split(":")[2].encode( "utf-8" ),
234                                                 Statics.META_TITLE : Item.getAttrOrDefault(item, 'title', metaNA).encode( "utf-8" ),
235                                                 Statics.META_ALBUM : Item.getAttrOrDefault(item, 'album', metaNA).encode( "utf-8" ),
236                                                 Statics.META_ARTIST : Item.getAttrOrDefault(item, 'artist', metaNA).encode( "utf-8" ),
237                                                 Statics.META_GENRE : Item.getAttrOrDefault(item, 'genre', metaNA).encode( "utf-8" ),
238                                                 Statics.META_DURATION : Item.getAttrOrDefault(item, 'duration', "0"),
239                                                 Statics.META_BITRATE : Item.getAttrOrDefault(item, 'bitrate', "0"),
240                                                 Statics.META_SIZE : Item.getAttrOrDefault(item, 'size', -1),
241                                                 Statics.META_ALBUM_ART_URI : Item.getAttrOrDefault(item, 'albumArtURI'),
242                                                 Statics.META_URI : Item.getAttrOrDefault(res, 'data'),
243                                         }
244                 return meta
245
246         @staticmethod
247         def isContainer(item):
248                 print item.__class__.__name__
249                 if item.__class__.__name__ == MediaServerClient.__name__:
250                         return True
251                 return item.upnp_class.startswith("object.container")