enigma2 20130114 (master) -> 20130216 (master)
[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                 self.__devices = []
59
60                 self.onMediaServerDetected = []
61                 self.onMediaServerRemoved  = []
62                 self.onMediaRendererDetected = []
63                 self.onMediaRendererRemoved = []
64                 self.onMediaDeviceDectected = []
65
66         def _onMediaServerDetected(self, client, udn):
67                 print "[DLNA] MediaServer Detected: %s (%s)" % (client.device.get_friendly_name(), client.device.get_friendly_device_type())
68                 self.__mediaServerClients[udn] = client
69                 for fnc in self.onMediaServerDetected:
70                         fnc(udn, client)
71
72         def _onMediaServerRemoved(self, udn):
73                 if self.__mediaServerClients.get(udn, None) != None:
74                         del self.__mediaServerClients[udn]
75                         for fnc in self.onMediaServerRemoved:
76                                 fnc(udn)
77
78         def _onMediaRendererDetected(self, client, udn):
79                 print "[DLNA] MediaRenderer detected: %s (%s, %s)" % (client.device.get_friendly_name(), client.device.get_friendly_device_type(), udn)
80                 self.__mediaRendererClients[udn] = client
81                 for fnc in self.onMediaRendererDetected:
82                         fnc(udn, client)
83
84         def _onMediaRendererRemoved(self, udn):
85                 print "[DLNA] MediaRenderer removed: %s" % (udn)
86                 if self.__mediaRendererClients.get(udn, None) != None:
87                         client = self.__mediaRendererClients[udn]
88                         del self.__mediaRendererClients[udn]
89                         for fnc in self.onMediaRendererRemoved:
90                                 fnc(udn)
91
92         def _onMediaDeviceDectected(self, device):
93                 print "[DLNA] Device found: %s (%s)" % (device.get_friendly_name(), device.get_friendly_device_type())
94                 self.__mediaDevices[device.udn] = device
95
96         def registerRenderer(self, classDef, **kwargs):
97                 renderer = MediaRenderer(self.coherence, classDef, no_thread_needed=True, **kwargs)
98                 self.__devices.append(renderer)
99                 return renderer
100
101         def registerServer(self, classDef, **kwargs):
102                 server = MediaServer(self.coherence, classDef, no_thread_needed=True, **kwargs)
103                 self.__devices.append(server)
104                 return server
105
106         def getServerList(self):
107                 return self.__mediaServerClients.values()
108
109         def getRenderingControlClientList(self):
110                 return self.__mediaRendererClients.values()
111
112         def getDeviceName(self, client):
113                 return client.device.get_friendly_name().encode( "utf-8" )
114
115         def shutdown(self):
116                 for device in self.__devices:
117                         device.unregister()
118                 self._controlPoint.shutdown()
119
120 class Item(object):
121         @staticmethod
122         def getItemType(item):
123                 if item != None:
124                         if item.__class__.__name__ == MediaServerClient.__name__:
125                                 return Statics.ITEM_TYPE_SERVER
126
127                         itemClass = item.upnp_class.encode( "utf-8" )
128                         if Item.isContainer(item):
129                                 return Statics.ITEM_TYPE_CONTAINER
130
131                         elif itemClass.startswith("object.item"):
132                                 type = item.upnp_class.split('.')[-1]
133                                 if type == "videoItem" or type == "movie":
134                                         return Statics.ITEM_TYPE_VIDEO
135                                 elif type == "musicTrack" or type == "audioItem":
136                                         return Statics.ITEM_TYPE_AUDIO
137                                 elif type == "photo":
138                                         return Statics.ITEM_TYPE_PICTURE
139
140                 return None
141
142         @staticmethod
143         def isServer(item):
144                 return Item.getItemType(item) == Statics.ITEM_TYPE_SERVER
145
146         @staticmethod
147         def getServerName(client):
148                 return client.device.get_friendly_name().encode( "utf-8" )
149
150         '''
151         Returns the title of the current item
152         '''
153         @staticmethod
154         def getItemTitle(item):
155                 if Item.isServer(item):
156                         return Item.getServerName(item)
157
158                 if item.title != None:
159                         return item.title.encode( "utf-8" )
160                 else:
161                         return "<missing title>"
162
163         '''
164         returns the number of children for container items
165         returns -1 for non-container items
166         '''
167         @staticmethod
168         def getItemChildCount(item):
169                 if Item.getItemType(item) != Statics.ITEM_TYPE_SERVER and Item.isContainer(item):
170                         return item.childCount
171
172                 return -1
173
174         '''
175         Currently always returns a dict with the first url and meta-type, which is usually the original/non-transcoded source
176         Raises an IllegalInstanceException if you pass in a container-item
177         '''
178         @staticmethod
179         def getItemUriMeta(item):
180                 assert not Item.isContainer(item)
181
182                 for res in item.res:
183                         uri = res.data.encode( "utf-8" )
184                         meta = res.protocolInfo.split(":")[2].encode( "utf-8" )
185                         print "URL: %s\nMeta:%s" %(uri, meta)
186                         if uri:
187                                 return {Statics.META_URI : uri, Statics.META_METATYPE : meta}
188
189         @staticmethod
190         def getItemId(item):
191                 if Item.isServer(item):
192                         return item.device.get_id()
193                 else:
194                         return item.id
195
196         @staticmethod
197         def getAttrOrDefault(instance, attr, default=None):
198                 val = getattr(instance, attr, default) or default
199                 try:
200                         return val.encode( "utf-8" )
201                 except:
202                         return val
203
204         @staticmethod
205         def getItemMetadata(item):
206                 type = Item.getItemType(item)
207                 meta = {}
208                 metaNA = _('n/a')
209
210                 if type == Statics.ITEM_TYPE_SERVER:
211                         meta = {
212                                         Statics.META_TYPE : type,
213                                         Statics.META_TITLE : Item.getServerName(item),
214                                 }
215
216                 elif type == Statics.ITEM_TYPE_CONTAINER:
217                                 meta = {
218                                                 Statics.META_TYPE : type,
219                                                 Statics.META_TITLE : Item.getAttrOrDefault(item, 'title', metaNA).encode( "utf-8" ),
220                                                 Statics.META_CHILD_COUNT : Item.getItemChildCount(item),
221                                         }
222                 elif type == Statics.ITEM_TYPE_PICTURE or type == Statics.ITEM_TYPE_VIDEO:
223                         for res in item.res:
224                                 meta = {
225                                                 Statics.META_TYPE : type,
226                                                 Statics.META_METATYPE : res.protocolInfo.split(":")[2].encode( "utf-8" ),
227                                                 Statics.META_TITLE : Item.getAttrOrDefault(item, 'title', metaNA).encode( "utf-8" ),
228                                                 Statics.META_DATE : Item.getAttrOrDefault(item, 'date', metaNA).encode( "utf-8" ),
229                                                 Statics.META_RESOLUTION : Item.getAttrOrDefault(item, 'resolution', metaNA).encode( "utf-8" ),
230                                                 Statics.META_SIZE : Item.getAttrOrDefault(item, 'size', -1),
231                                                 Statics.META_URI : Item.getAttrOrDefault(res, 'data'),
232                                         }
233                                 if type == Statics.ITEM_TYPE_PICTURE:
234                                         meta[Statics.META_ALBUM] = Item.getAttrOrDefault(item, 'album', metaNA).encode( "utf-8" )
235                                 elif type == Statics.ITEM_TYPE_VIDEO:
236                                         meta[Statics.META_ALBUM_ART_URI] = Item.getAttrOrDefault(item, 'albumArtURI')
237
238                                 return meta
239                 elif type == Statics.ITEM_TYPE_AUDIO:
240                         for res in item.res:
241                                 meta = {
242                                                 Statics.META_TYPE : type,
243                                                 Statics.META_METATYPE : res.protocolInfo.split(":")[2].encode( "utf-8" ),
244                                                 Statics.META_TITLE : Item.getAttrOrDefault(item, 'title', metaNA).encode( "utf-8" ),
245                                                 Statics.META_ALBUM : Item.getAttrOrDefault(item, 'album', metaNA).encode( "utf-8" ),
246                                                 Statics.META_ARTIST : Item.getAttrOrDefault(item, 'artist', metaNA).encode( "utf-8" ),
247                                                 Statics.META_GENRE : Item.getAttrOrDefault(item, 'genre', metaNA).encode( "utf-8" ),
248                                                 Statics.META_DURATION : Item.getAttrOrDefault(item, 'duration', "0"),
249                                                 Statics.META_BITRATE : Item.getAttrOrDefault(item, 'bitrate', "0"),
250                                                 Statics.META_SIZE : Item.getAttrOrDefault(item, 'size', -1),
251                                                 Statics.META_ALBUM_ART_URI : Item.getAttrOrDefault(item, 'albumArtURI'),
252                                                 Statics.META_URI : Item.getAttrOrDefault(res, 'data'),
253                                         }
254                 return meta
255
256         @staticmethod
257         def isContainer(item):
258                 print item.__class__.__name__
259                 if item.__class__.__name__ == MediaServerClient.__name__:
260                         return True
261                 return item.upnp_class.startswith("object.container")