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