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