changed: avoid opening music/video db if info for an item already is known
[xbmc:xbmc-antiquated.git] / xbmc / UPnP.cpp
1 /*\r
2 * UPnP Support for XBox Media Center\r
3 * Copyright (c) 2006 c0diq (Sylvain Rebaud)\r
4 * Portions Copyright (c) by the authors of libPlatinum\r
5 *\r
6 * http://www.plutinosoft.com/blog/category/platinum/\r
7 *\r
8 * This program is free software; you can redistribute it and/or modify\r
9 * it under the terms of the GNU General Public License as published by\r
10 * the Free Software Foundation; either version 2 of the License, or\r
11 * (at your option) any later version.\r
12 *\r
13 * This program is distributed in the hope that it will be useful,\r
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
16 * GNU General Public License for more details.\r
17 *\r
18 * You should have received a copy of the GNU General Public License\r
19 * along with this program; if not, write to the Free Software\r
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\r
21 */\r
22 \r
23 \r
24 #include "stdafx.h"\r
25 #include "util.h"\r
26 #include "Application.h"\r
27 \r
28 #include "xbox/Network.h"\r
29 #include "UPnP.h"\r
30 #include "FileSystem/UPnPVirtualPathDirectory.h"\r
31 #include "FileSystem/MusicDatabaseDirectory.h"\r
32 #include "FileSystem/VideoDatabaseDirectory.h"\r
33 #include "MusicDatabase.h"\r
34 #include "VideoDatabase.h"\r
35 #include "FileSystem/VideoDatabaseDirectory/DirectoryNode.h"\r
36 #include "FileSystem/VideoDatabaseDirectory/QueryParams.h"\r
37 #include "Platinum.h"\r
38 #include "PltFileMediaServer.h"\r
39 #include "PltMediaServer.h"\r
40 #include "PltMediaBrowser.h"\r
41 #include "../MediaRenderer/PltMediaRenderer.h"\r
42 #include "PltSyncMediaBrowser.h"\r
43 #include "PltDidl.h"\r
44 #include "NptNetwork.h"\r
45 #include "NptConsole.h"\r
46 \r
47 NPT_SET_LOCAL_LOGGER("xbmc.upnp")\r
48 \r
49 /*----------------------------------------------------------------------\r
50 |   static\r
51 +---------------------------------------------------------------------*/\r
52 CUPnP* CUPnP::upnp = NULL;\r
53 // change to false for XBMC_PC if you want real UPnP functionality\r
54 // otherwise keep to true for xbmc as it doesn't support multicast\r
55 // don't change unless you know what you're doing!\r
56 bool CUPnP::broadcast = true; \r
57 \r
58 #ifdef HAS_XBOX_NETWORK\r
59 #include <xtl.h>\r
60 #include <winsockx.h>\r
61 #include "NptXboxNetwork.h"\r
62 \r
63 /*----------------------------------------------------------------------\r
64 |   static initializer\r
65 +---------------------------------------------------------------------*/\r
66 NPT_WinsockSystem::NPT_WinsockSystem() \r
67 {\r
68 }\r
69 \r
70 NPT_WinsockSystem::~NPT_WinsockSystem() \r
71 {\r
72 }\r
73 \r
74 NPT_WinsockSystem NPT_WinsockSystem::Initializer;\r
75 \r
76 /*----------------------------------------------------------------------\r
77 |       NPT_NetworkInterface::GetNetworkInterfaces\r
78 +---------------------------------------------------------------------*/\r
79 NPT_Result\r
80 NPT_NetworkInterface::GetNetworkInterfaces(NPT_List<NPT_NetworkInterface*>& interfaces)\r
81 {\r
82     XNADDR xna;\r
83     DWORD  state;\r
84     do {\r
85         state = XNetGetTitleXnAddr(&xna);\r
86         Sleep(100);\r
87     } while (state == XNET_GET_XNADDR_PENDING);\r
88 \r
89     if (state & XNET_GET_XNADDR_STATIC || state & XNET_GET_XNADDR_DHCP) {\r
90         NPT_IpAddress primary_address;\r
91         primary_address.ResolveName(g_network.m_networkinfo.ip);\r
92 \r
93         NPT_IpAddress netmask;\r
94         netmask.ResolveName(g_network.m_networkinfo.subnet);\r
95 \r
96         NPT_IpAddress broadcast_address;        \r
97         broadcast_address.ResolveName("255.255.255.255");\r
98 \r
99 //        {\r
100 //            // broadcast address is incorrect\r
101 //            unsigned char addr[4];\r
102 //            for(int i=0; i<4; i++) {\r
103 //                addr[i] = (primary_address.AsBytes()[i] & netmask.AsBytes()[i]) | \r
104 //                    ~netmask.AsBytes()[i];\r
105 //            }\r
106 //            broadcast_address.Set(addr);\r
107 //        }\r
108 \r
109 \r
110         NPT_Flags     flags = NPT_NETWORK_INTERFACE_FLAG_BROADCAST | NPT_NETWORK_INTERFACE_FLAG_MULTICAST;\r
111 \r
112         NPT_MacAddress mac;\r
113         if (state & XNET_GET_XNADDR_ETHERNET) {\r
114             mac.SetAddress(NPT_MacAddress::TYPE_ETHERNET, xna.abEnet, 6);\r
115         }\r
116 \r
117         // create an interface object\r
118         char iface_name[5];\r
119         iface_name[0] = 'i';\r
120         iface_name[1] = 'f';\r
121         iface_name[2] = '0';\r
122         iface_name[3] = '0';\r
123         iface_name[4] = '\0';\r
124         NPT_NetworkInterface* iface = new NPT_NetworkInterface(iface_name, mac, flags);\r
125 \r
126         // set the interface address\r
127         NPT_NetworkInterfaceAddress iface_address(\r
128             primary_address,\r
129             broadcast_address,\r
130             NPT_IpAddress::Any,\r
131             netmask);\r
132         iface->AddAddress(iface_address);  \r
133 \r
134         // add the interface to the list\r
135         interfaces.Add(iface);  \r
136     }\r
137 \r
138     return NPT_SUCCESS;\r
139 }\r
140 #endif\r
141 \r
142 \r
143 /*----------------------------------------------------------------------\r
144 |   NPT_Console::Output and NPT_GetEnvironment\r
145 +---------------------------------------------------------------------*/\r
146 \r
147 void NPT_Console::Output(const char* message)\r
148 {\r
149     CLog::Log(LOGDEBUG, message);\r
150 }\r
151 \r
152 NPT_Result NPT_GetEnvironment(const char* name, NPT_String& value)\r
153 {\r
154     return NPT_FAILURE;\r
155 }\r
156 \r
157 /*----------------------------------------------------------------------\r
158 |   CDeviceHostReferenceHolder class\r
159 +---------------------------------------------------------------------*/\r
160 class CDeviceHostReferenceHolder\r
161 {\r
162 public:\r
163     PLT_DeviceHostReference m_Device;\r
164 };\r
165 \r
166 /*----------------------------------------------------------------------\r
167 |   CCtrlPointReferenceHolder class\r
168 +---------------------------------------------------------------------*/\r
169 class CCtrlPointReferenceHolder\r
170 {\r
171 public:\r
172     PLT_CtrlPointReference m_CtrlPoint;\r
173 };\r
174 \r
175 /*----------------------------------------------------------------------\r
176 |   CUPnPCleaner class\r
177 +---------------------------------------------------------------------*/\r
178 class CUPnPCleaner : public NPT_Thread\r
179 {\r
180 public:\r
181     CUPnPCleaner(CUPnP* upnp) : NPT_Thread(true), m_UPnP(upnp) {}\r
182     void Run() {\r
183         delete m_UPnP;\r
184     }\r
185 \r
186     CUPnP* m_UPnP;\r
187 };\r
188 \r
189 /*----------------------------------------------------------------------\r
190 |   CUPnP::CUPnP\r
191 +---------------------------------------------------------------------*/\r
192 class CUPnPServer : public PLT_FileMediaServer\r
193 {\r
194 public:\r
195     CUPnPServer(const char* friendly_name, const char* uuid = NULL) : \r
196       PLT_FileMediaServer("", friendly_name, true, uuid) {\r
197           // hack: override path to make sure it's empty\r
198           // urls will contain full paths to local files\r
199           m_Path = "";\r
200           m_DirDelimiter = NPT_WIN32_DIR_DELIMITER_STR;\r
201       }\r
202 \r
203     virtual NPT_Result OnBrowseMetadata(\r
204         PLT_ActionReference& action, \r
205         const char*          object_id, \r
206         NPT_SocketInfo*      info = NULL);\r
207     virtual NPT_Result OnBrowseDirectChildren(\r
208         PLT_ActionReference& action, \r
209         const char*          object_id, \r
210         NPT_SocketInfo*      info = NULL);\r
211 \r
212 private:\r
213     PLT_MediaObject* BuildObject(\r
214         CFileItem*      item,\r
215         NPT_String&     file_path,\r
216         bool            with_count = false,\r
217         NPT_SocketInfo* info = NULL);\r
218 \r
219     PLT_MediaObject* Build(\r
220         CFileItem*      item, \r
221         bool            with_count = false, \r
222         NPT_SocketInfo* info = NULL,\r
223         const char*     parent_id = NULL);\r
224 \r
225     static NPT_String GetParentFolder(NPT_String file_path) {       \r
226         int index = file_path.ReverseFind("\\");\r
227         if (index == -1) return "";\r
228 \r
229         return file_path.Left(index);\r
230     }\r
231 };\r
232 \r
233 /*----------------------------------------------------------------------\r
234 |   PLT_FileMediaServer::BuildObject\r
235 +---------------------------------------------------------------------*/\r
236 PLT_MediaObject*\r
237 CUPnPServer::BuildObject(CFileItem*      item,\r
238                          NPT_String&     file_path,\r
239                          bool            with_count /* = false */,\r
240                          NPT_SocketInfo* info /* = NULL */)\r
241 {\r
242     PLT_MediaItemResource resource;\r
243     PLT_MediaObject*      object = NULL;\r
244 \r
245     // get list of ip addresses\r
246     NPT_List<NPT_String> ips;\r
247     NPT_CHECK_LABEL(PLT_UPnPMessageHelper::GetIPAddresses(ips), failure);\r
248 \r
249     // if we're passed an interface where we received the request from\r
250     // move the ip to the top\r
251     if (info && info->local_address.GetIpAddress().ToString() != "0.0.0.0") {\r
252         ips.Remove(info->local_address.GetIpAddress().ToString());\r
253         ips.Insert(ips.GetFirstItem(), info->local_address.GetIpAddress().ToString());\r
254     }\r
255 \r
256     if (!item->m_bIsFolder) {\r
257         object = new PLT_MediaItem();\r
258 \r
259         object->m_ObjectID = item->m_strPath;\r
260 \r
261         /* Setup object type */\r
262         if( item->IsMusicDb() ) {\r
263             object->m_ObjectClass.type = "object.item.audioitem";\r
264           \r
265             if( item->HasMusicInfoTag() ) {\r
266                 CMusicInfoTag *tag = item->GetMusicInfoTag();\r
267                 if( !tag->GetURL().IsEmpty() )\r
268                   file_path = tag->GetURL();\r
269                 \r
270                 object->m_Title = tag->GetTitle();\r
271                 object->m_Affiliation.genre = tag->GetGenre();\r
272                 object->m_Affiliation.album = tag->GetAlbum();\r
273                 object->m_People.artist = tag->GetArtist();\r
274                 object->m_Creator = tag->GetArtist();\r
275                 //object->m_ExtraInfo.album_art_uri = \r
276                 resource.m_Duration = tag->GetDuration();\r
277                 \r
278             }\r
279 \r
280         } else if( item->IsVideoDb() ) {\r
281             object->m_ObjectClass.type = "object.item.videoitem";\r
282 \r
283             if( item->HasVideoInfoTag() ) {\r
284                 CVideoInfoTag *tag = item->GetVideoInfoTag();\r
285                 if( !tag->m_strFileNameAndPath.IsEmpty() )\r
286                   file_path = tag->m_strFileNameAndPath;\r
287 \r
288                 object->m_Affiliation.genre = tag->m_strGenre;\r
289                 object->m_People.director = tag->m_strDirector;\r
290                 object->m_Description.description = tag->m_strTagLine;\r
291                 object->m_Description.long_description = tag->m_strPlot;\r
292                 resource.m_Duration = atoi(tag->m_strRuntime.c_str());\r
293             }\r
294 \r
295         } else if( item->IsAudio() ) {\r
296             object->m_ObjectClass.type = "object.item.audioitem";\r
297         } else if( item->IsVideo() ) {\r
298             object->m_ObjectClass.type = "object.item.videoitem";\r
299         } else if( item->IsPicture() ) {\r
300             object->m_ObjectClass.type = "object.item.imageitem";\r
301         }\r
302 \r
303         /* we need a valid extension to retrieve the mimetype for the protocol info */\r
304         CStdString ext = CUtil::GetExtension((const char*)file_path);\r
305 \r
306         /* if we still miss a object class, try set it now from extension */\r
307         if( object->m_ObjectClass.type == "object.item" || object->m_ObjectClass.type == "" ) {\r
308             object->m_ObjectClass.type = PLT_MediaItem::GetUPnPClassFromExt(ext);\r
309         }\r
310 \r
311         /* Set the protocol Info from the extension */\r
312         resource.m_ProtocolInfo = PLT_MediaItem::GetProtInfoFromExt(ext);\r
313         if (resource.m_ProtocolInfo.GetLength() == 0)  goto failure;\r
314 \r
315         /* Set the resource file size */\r
316         resource.m_Size = (NPT_Integer)item->m_dwSize;\r
317 \r
318         // if the item is remote, add a direct link to the item\r
319         if( CUtil::IsRemote ( (const char*)file_path ) ) {\r
320             resource.m_Uri = file_path;\r
321             object->m_Resources.Add(resource);\r
322         } else {\r
323 \r
324             // iterate through list and build list of resources\r
325             NPT_List<NPT_String>::Iterator ip = ips.GetFirstItem();\r
326             while (ip) {\r
327                 NPT_HttpUrl uri = m_FileBaseUri;\r
328                 NPT_HttpUrlQuery query;\r
329                 query.AddField("path", file_path);\r
330                 uri.SetHost(*ip);\r
331                 uri.SetQuery(query.ToString());\r
332                 resource.m_Uri = uri.ToString();\r
333                 \r
334                 object->m_Resources.Add(resource);\r
335 \r
336                 ++ip;\r
337             }\r
338         }        \r
339     } else {\r
340         object = new PLT_MediaContainer;\r
341 \r
342         /* Assign a title and id for this container */\r
343         object->m_ObjectID = item->m_strPath;\r
344         object->m_ObjectClass.type = "object.container";\r
345         ((PLT_MediaContainer*)object)->m_ChildrenCount = -1;\r
346 \r
347         /* Get the number of children for this container */\r
348         if (with_count) {\r
349             if( object->m_ObjectID.StartsWith("virtualpath://") ) {\r
350                 NPT_Cardinal count = 0;\r
351                 NPT_CHECK_LABEL(GetEntryCount(file_path, count), failure);\r
352                 ((PLT_MediaContainer*)object)->m_ChildrenCount = count;\r
353             } else {\r
354                 /* this should be a standard path */\r
355                 // TODO - get file count of this directory\r
356             }\r
357         }\r
358         \r
359     }\r
360     \r
361     // set a title for the object\r
362     if( !item->GetLabel().IsEmpty() /* item->IsLabelPreformated() */ ) {\r
363         object->m_Title = item->GetLabel();\r
364     } else {\r
365         object->m_Title = CUtil::GetTitleFromPath(item->m_strPath, item->m_bIsFolder);\r
366     }\r
367 \r
368     // set a thumbnail if we have one\r
369     if( item->HasThumbnail() ) {\r
370         NPT_HttpUrl uri = m_FileBaseUri;\r
371         NPT_HttpUrlQuery query;\r
372         query.AddField("path", item->GetThumbnailImage() );\r
373         uri.SetHost(*ips.GetFirstItem());\r
374         uri.SetQuery(query.ToString());\r
375         object->m_ExtraInfo.album_art_uri = uri.ToString();\r
376     }\r
377 \r
378     return object;\r
379 \r
380 failure:\r
381     if(object)\r
382       delete object;\r
383     return NULL;\r
384 }\r
385 \r
386 /*----------------------------------------------------------------------\r
387 |   CUPnPServer::Build\r
388 +---------------------------------------------------------------------*/\r
389 PLT_MediaObject* \r
390 CUPnPServer::Build(CFileItem*        item, \r
391                    bool              with_count /* = true */, \r
392                    NPT_SocketInfo*   info /* = NULL */,\r
393                    const char*       parent_id /* = NULL */)\r
394 {\r
395     PLT_MediaObject* object = NULL;\r
396     NPT_String       path = item->m_strPath;\r
397     NPT_String       share_name;\r
398     NPT_String       file_path;\r
399 \r
400     //HACK: temporary disabling count as it thrashes HDD\r
401     with_count = false;\r
402 \r
403     if(!CUPnPVirtualPathDirectory::SplitPath(path, share_name, file_path))\r
404     {\r
405       file_path = item->m_strPath;\r
406       share_name = "";\r
407 \r
408       if (path.StartsWith("musicdb://")) {\r
409           CStdString label;\r
410           if( path == "musicdb://" ) {              \r
411               item->SetLabel("Music Library");\r
412               item->SetLabelPreformated(true);\r
413           } else {\r
414 \r
415               if( !item->HasMusicInfoTag() || !item->GetMusicInfoTag()->Loaded() )\r
416                   item->LoadMusicTag();\r
417 \r
418               if( !item->HasThumbnail() )\r
419                   item->SetCachedMusicThumb();\r
420 \r
421               if( item->GetLabel().IsEmpty() ) {\r
422                   /* if no label try to grab it from node type */\r
423                   if( CMusicDatabaseDirectory::GetLabel((const char*)path, label) ) {\r
424                       item->SetLabel(label);\r
425                       item->SetLabelPreformated(true);\r
426                   }\r
427               }\r
428           }\r
429       } else if (file_path.StartsWith("videodb://")) {\r
430           CStdString label;\r
431           if( path == "videodb://" ) {\r
432               item->SetLabel("Video Library");\r
433               item->SetLabelPreformated(true);\r
434           } else {\r
435 \r
436 \r
437               if( !item->HasVideoInfoTag() ) {\r
438                   DIRECTORY::VIDEODATABASEDIRECTORY::CQueryParams params;\r
439                   DIRECTORY::VIDEODATABASEDIRECTORY::CDirectoryNode::GetDatabaseInfo((const char*)path, params);\r
440 \r
441                   CVideoDatabase db;\r
442                   if( !db.Open() )\r
443                       return NULL;\r
444 \r
445                   if( params.GetMovieId() >= 0 )\r
446                       db.GetMovieInfo((const char*)path, *item->GetVideoInfoTag(), params.GetMovieId());\r
447                   else if( params.GetEpisodeId() >= 0 )\r
448                       db.GetEpisodeInfo((const char*)path, *item->GetVideoInfoTag(), params.GetEpisodeId());\r
449                   else if( params.GetTvShowId() >= 0 )\r
450                       db.GetTvShowInfo((const char*)path, *item->GetVideoInfoTag(), params.GetTvShowId());\r
451               }\r
452 \r
453               // try to grab title from tag\r
454               if( item->HasVideoInfoTag() && !item->GetVideoInfoTag()->m_strTitle.IsEmpty() ) {\r
455                   item->SetLabel( item->GetVideoInfoTag()->m_strTitle );\r
456                   item->SetLabelPreformated(true);\r
457               }\r
458 \r
459               // try to grab it from the folder\r
460               if( item->GetLabel().IsEmpty() ) {\r
461                   if( CVideoDatabaseDirectory::GetLabel((const char*)path, label) ) {\r
462                       item->SetLabel(label);\r
463                       item->SetLabelPreformated(true);\r
464                   }\r
465               }\r
466 \r
467               if( !item->HasThumbnail() )\r
468                   item->SetCachedVideoThumb();\r
469           }\r
470           \r
471       }\r
472 \r
473       //not a virtual path directory, new system\r
474       object = BuildObject(item, file_path, with_count, info);\r
475       if(!object)\r
476         return NULL;\r
477 \r
478       if(parent_id)\r
479         object->m_ParentID = parent_id;\r
480 \r
481       return object;\r
482     }\r
483 \r
484     path.TrimRight("/");\r
485     if (file_path.GetLength()) {\r
486         // make sure the path starts with something that is shared given the share\r
487         if (!CUPnPVirtualPathDirectory::FindSharePath(share_name, file_path, true)) goto failure;\r
488         \r
489         // this is not a virtual directory\r
490         object = BuildObject(item, file_path, with_count, info);\r
491         if (!object) goto failure;\r
492 \r
493         // override object id & change the class if it's an item\r
494         // and it's not been set previously\r
495         if (object->m_ObjectClass.type == "object.item") {\r
496             if (share_name == "virtualpath://upnpmusic")\r
497                 object->m_ObjectClass.type = "object.item.audioitem";\r
498             else if (share_name == "virtualpath://upnpvideo")\r
499                 object->m_ObjectClass.type = "object.item.videoitem";\r
500             else if (share_name == "virtualpath://upnppictures")\r
501                 object->m_ObjectClass.type = "object.item.imageitem";\r
502         }\r
503 \r
504         if (parent_id) {\r
505             object->m_ParentID = parent_id;\r
506         } else {\r
507             // populate parentid manually\r
508             if (CUPnPVirtualPathDirectory::FindSharePath(share_name, file_path)) {\r
509                 // found the file_path as one of the path of the share\r
510                 // this means the parent id is the share\r
511                 object->m_ParentID = share_name;\r
512             } else {\r
513                 // we didn't find the path, find the parent path\r
514                 NPT_String parent_path = GetParentFolder(file_path);\r
515                 if (parent_path.IsEmpty()) goto failure;\r
516 \r
517                 // try again with parent\r
518                 if (CUPnPVirtualPathDirectory::FindSharePath(share_name, parent_path)) {\r
519                     // found the file_path parent folder as one of the path of the share\r
520                     // this means the parent id is the share\r
521                     object->m_ParentID = share_name;\r
522                 } else {\r
523                     object->m_ParentID = share_name + "/" + parent_path;\r
524                 }\r
525             }\r
526         }\r
527 \r
528         // old style, needs virtual path prefix\r
529         if( !object->m_ObjectID.StartsWith("virtualpath://") )\r
530             object->m_ObjectID = share_name + "/" + object->m_ObjectID;\r
531 \r
532     } else {\r
533         object = new PLT_MediaContainer;\r
534         object->m_Title = item->GetLabel();\r
535         object->m_ObjectClass.type = "object.container";\r
536         object->m_ObjectID = path;\r
537 \r
538         if (path == "virtualpath://upnproot") {\r
539             // root\r
540             object->m_ObjectID = "0";\r
541             object->m_ParentID = "-1";\r
542             // root has 5 children\r
543             if (with_count) ((PLT_MediaContainer*)object)->m_ChildrenCount = 5;\r
544         } else if (share_name.GetLength() == 0) {\r
545             // no share_name means it's virtualpath://X where X=music, video or pictures\r
546             object->m_ParentID = "0";\r
547             if (with_count || true) { // we can always count these, it's quick\r
548                 ((PLT_MediaContainer*)object)->m_ChildrenCount = 0;\r
549 \r
550                 // look up number of shares\r
551                 VECSHARES *shares = NULL;\r
552                 if (path == "virtualpath://upnpmusic") {\r
553                     shares = g_settings.GetSharesFromType("upnpmusic");\r
554                 } else if (path == "virtualpath://upnpvideo") {\r
555                     shares = g_settings.GetSharesFromType("upnpvideo");\r
556                 } else if (path == "virtualpath://upnppictures") {\r
557                     shares = g_settings.GetSharesFromType("upnppictures");\r
558                 }\r
559 \r
560                 // use only shares that would some path with local files\r
561                 if (shares) {\r
562                     CUPnPVirtualPathDirectory dir;\r
563                     for (unsigned int i = 0; i < shares->size(); i++) {\r
564                         // Does this share contains any local paths?\r
565                         CShare &share = shares->at(i);\r
566                         vector<CStdString> paths;\r
567 \r
568                         // reconstruct share name as it could have been replaced by\r
569                         // a path if there was just one entry\r
570                         NPT_String share_name = path + "/";\r
571                         share_name += share.strName;\r
572                         if (dir.GetMatchingShare((const char*)share_name, share, paths) && paths.size()) {\r
573                             ((PLT_MediaContainer*)object)->m_ChildrenCount++;\r
574                         }\r
575                     }\r
576                 }\r
577             }\r
578         } else {\r
579             CStdString mask;\r
580             // this is a share name\r
581             if (share_name.StartsWith("virtualpath://upnpmusic")) {\r
582                 object->m_ParentID = "virtualpath://upnpmusic";\r
583                 mask = g_stSettings.m_musicExtensions;\r
584             } else if (share_name.StartsWith("virtualpath://upnpvideo")) {\r
585                 object->m_ParentID = "virtualpath://upnpvideo";\r
586                 mask = g_stSettings.m_videoExtensions;\r
587             } else if (share_name.StartsWith("virtualpath://upnppictures")) {\r
588                 object->m_ParentID = "virtualpath://upnppictures";\r
589                 mask = g_stSettings.m_pictureExtensions;\r
590             } else {\r
591                 // weird!\r
592                 goto failure;\r
593             }\r
594 \r
595             if (with_count) {\r
596                 ((PLT_MediaContainer*)object)->m_ChildrenCount = 0;\r
597 \r
598                 // get all the paths for a given share\r
599                 CShare share;\r
600                 CUPnPVirtualPathDirectory dir;\r
601                 vector<CStdString> paths;\r
602                 if (!dir.GetMatchingShare((const char*)share_name, share, paths)) goto failure;\r
603                 for (unsigned int i=0; i<paths.size(); i++) {\r
604                     // FIXME: this is not efficient, we only need the number of items given a mask\r
605                     // and not the list of items\r
606 \r
607                     // retrieve all the files for a given path\r
608                    CFileItemList items;\r
609                    if (CDirectory::GetDirectory(paths[i], items, mask)) {\r
610                        // update childcount\r
611                        ((PLT_MediaContainer*)object)->m_ChildrenCount += items.Size();\r
612                    }\r
613                 }\r
614             }\r
615         }\r
616     }\r
617 \r
618     return object;\r
619 \r
620 failure:\r
621     if(object)\r
622       delete object;\r
623     return NULL;\r
624 }\r
625 \r
626 /*----------------------------------------------------------------------\r
627 |   CUPnPServer::OnBrowseMetadata\r
628 +---------------------------------------------------------------------*/\r
629 NPT_Result\r
630 CUPnPServer::OnBrowseMetadata(PLT_ActionReference& action, \r
631                               const char*          object_id, \r
632                               NPT_SocketInfo*      info /* = NULL */)\r
633 {\r
634     NPT_String didl;\r
635     NPT_Reference<PLT_MediaObject> object;\r
636     NPT_String id = object_id;\r
637     CShare share;\r
638     CUPnPVirtualPathDirectory dir;\r
639     vector<CStdString> paths;\r
640 \r
641     CFileItem* item = NULL;\r
642 \r
643     if (id == "0") {\r
644         id = "virtualpath://upnproot/";\r
645     }\r
646 \r
647     if (id.StartsWith("virtualpath://")) {\r
648 \r
649         id.TrimRight("/");\r
650         if (id == "virtualpath://upnproot") {\r
651             id += "/";\r
652             item = new CFileItem((const char*)id, true);\r
653             item->SetLabel("Root");\r
654             item->SetLabelPreformated(true);\r
655             object = Build(item, true, info);\r
656         } else if (id == "virtualpath://upnpmusic") {\r
657             id += "/";\r
658             item = new CFileItem((const char*)id, true);\r
659             item->SetLabel("Music Files");\r
660             item->SetLabelPreformated(true);\r
661             object = Build(item, true, info);\r
662         } else if (id == "virtualpath://upnpvideo") {\r
663             id += "/";\r
664             item = new CFileItem((const char*)id, true);\r
665             item->SetLabel("Video Files");\r
666             item->SetLabelPreformated(true);\r
667             object = Build(item, true, info);\r
668         } else if (id == "virtualpath://upnppictures") {\r
669             id += "/";\r
670             item = new CFileItem((const char*)id, true);\r
671             item->SetLabel("Picture Files");\r
672             item->SetLabelPreformated(true);\r
673             object = Build(item, true, info);\r
674         } else if (dir.GetMatchingShare((const char*)id, share, paths)) {\r
675             id += "/";\r
676             item = new CFileItem((const char*)id, true);\r
677             item->SetLabel(share.strName);\r
678             item->SetLabelPreformated(true);\r
679             object = Build(item, true, info);\r
680         } else {\r
681             NPT_String share_name, file_path;\r
682             if (!CUPnPVirtualPathDirectory::SplitPath(id, share_name, file_path)) \r
683                 return NPT_FAILURE;\r
684 \r
685             NPT_String parent_path = GetParentFolder(file_path);\r
686             if (parent_path.IsEmpty()) return NPT_FAILURE;\r
687 \r
688             NPT_DirectoryEntryInfo entry_info;\r
689             NPT_CHECK(NPT_DirectoryEntry::GetInfo(file_path, entry_info));\r
690 \r
691             item = new CFileItem((const char*)id, (entry_info.type==NPT_DIRECTORY_TYPE)?true:false);\r
692             item->SetLabel((const char*)file_path.SubString(parent_path.GetLength()+1));\r
693             item->SetLabelPreformated(true);\r
694 \r
695             // get file size\r
696             if (entry_info.type == NPT_FILE_TYPE) {\r
697                 item->m_dwSize = entry_info.size;\r
698             }\r
699 \r
700             object = Build(item, true, info);\r
701             if (!object.IsNull()) object->m_ObjectID = id;\r
702         }\r
703     } else {\r
704 \r
705         if( CDirectory::Exists((const char*)id) ) {\r
706             item = new CFileItem((const char*)id, true);\r
707         } else {\r
708             item = new CFileItem((const char*)id, false);            \r
709         }\r
710         CStdString parent;\r
711         if(!CUtil::GetParentPath((const char*)id, parent))\r
712           parent = "0";\r
713 \r
714         object = Build(item, true, info, parent.c_str());\r
715     }\r
716 \r
717     delete item;\r
718     if (object.IsNull()) return NPT_FAILURE;\r
719 \r
720     NPT_String filter;\r
721     NPT_CHECK(action->GetArgumentValue("Filter", filter));\r
722 \r
723     NPT_String tmp;    \r
724     NPT_CHECK(PLT_Didl::ToDidl(*object.AsPointer(), filter, tmp));\r
725 \r
726     /* add didl header and footer */\r
727     didl = didl_header + tmp + didl_footer;\r
728 \r
729     NPT_CHECK(action->SetArgumentValue("Result", didl));\r
730     NPT_CHECK(action->SetArgumentValue("NumberReturned", "1"));\r
731     NPT_CHECK(action->SetArgumentValue("TotalMatches", "1"));\r
732 \r
733     // update ID may be wrong here, it should be the one of the container?\r
734     NPT_CHECK(action->SetArgumentValue("UpdateId", "1"));\r
735     // TODO: We need to keep track of the overall updateID of the CDS\r
736 \r
737     return NPT_SUCCESS;\r
738 }\r
739 /*----------------------------------------------------------------------\r
740 |   CUPnPServer::OnBrowseDirectChildren\r
741 +---------------------------------------------------------------------*/\r
742 NPT_Result\r
743 CUPnPServer::OnBrowseDirectChildren(PLT_ActionReference& action, \r
744                                     const char*          object_id, \r
745                                     NPT_SocketInfo*      info /* = NULL */)\r
746 {\r
747     NPT_String id = object_id;    \r
748     CFileItemList items;\r
749 \r
750     if (id == "0") {\r
751         id = "virtualpath://upnproot/";\r
752     }\r
753 \r
754     if (id.StartsWith("virtualpath://")) {\r
755         CUPnPVirtualPathDirectory dir;\r
756         if (!dir.GetDirectory((const char*)id, items)) {\r
757             /* error */\r
758             NPT_LOG_FINE("CUPnPServer::OnBrowseDirectChildren - ObjectID not found.")\r
759             action->SetError(701, "No Such Object.");\r
760             return NPT_SUCCESS;\r
761         }\r
762     } else {\r
763         items.m_strPath = id;\r
764         if (!items.Load()) {\r
765             // cache anything that takes more than a second to retreive\r
766             DWORD time = GetTickCount() + 1000;\r
767 \r
768             if (!CDirectory::GetDirectory((const char*)id, items)) {\r
769                 /* error */\r
770                 NPT_LOG_FINE("CUPnPServer::OnBrowseDirectChildren - ObjectID not found.")\r
771                 action->SetError(701, "No Such Object.");\r
772                 return NPT_SUCCESS;\r
773             }\r
774             if(items.GetCacheToDisc() || time < GetTickCount())\r
775               items.Save();\r
776         }\r
777     }\r
778 \r
779     NPT_String filter;\r
780     NPT_String startingInd;\r
781     NPT_String reqCount;\r
782 \r
783     NPT_CHECK_SEVERE(action->GetArgumentValue("Filter", filter));\r
784     NPT_CHECK_SEVERE(action->GetArgumentValue("StartingIndex", startingInd));\r
785     NPT_CHECK_SEVERE(action->GetArgumentValue("RequestedCount", reqCount));   \r
786 \r
787     unsigned long start_index, req_count;\r
788     if (NPT_FAILED(startingInd.ToInteger(start_index)) ||\r
789         NPT_FAILED(reqCount.ToInteger(req_count))) {\r
790         return NPT_FAILURE;\r
791     }\r
792         \r
793     unsigned long cur_index = 0;\r
794     unsigned long num_returned = 0;\r
795     unsigned long total_matches = 0;\r
796     //unsigned long update_id = 0;\r
797     NPT_String didl = didl_header;\r
798     PLT_MediaObjectReference item;\r
799     for (int i=0; i < (int) items.Size(); ++i) {\r
800         item = Build(items[i], true, info, object_id);\r
801         if (!item.IsNull()) {\r
802             if ((cur_index >= start_index) && ((num_returned < req_count) || (req_count == 0))) {\r
803                 NPT_String tmp;\r
804                 NPT_CHECK(PLT_Didl::ToDidl(*item.AsPointer(), filter, tmp));\r
805 \r
806                 didl += tmp;\r
807                 num_returned++;\r
808             }\r
809             cur_index++;\r
810             total_matches++;        \r
811         }\r
812     }\r
813 \r
814     didl += didl_footer;\r
815 \r
816     NPT_CHECK(action->SetArgumentValue("Result", didl));\r
817     NPT_CHECK(action->SetArgumentValue("NumberReturned", NPT_String::FromInteger(num_returned)));\r
818     NPT_CHECK(action->SetArgumentValue("TotalMatches", NPT_String::FromInteger(total_matches)));\r
819     NPT_CHECK(action->SetArgumentValue("UpdateId", "1"));\r
820     return NPT_SUCCESS;\r
821 }\r
822 \r
823 /*----------------------------------------------------------------------\r
824 |   CUPnP::CMediaRenderer\r
825 +---------------------------------------------------------------------*/\r
826 class CUPnPRenderer : \r
827     public PLT_MediaRenderer\r
828 {\r
829 public:\r
830     CUPnPRenderer(const char*          friendly_name,\r
831                   bool                 show_ip = false,\r
832                   const char*          uuid = NULL,\r
833                   unsigned int         port = 0) :\r
834         PLT_MediaRenderer(NULL, friendly_name, show_ip, uuid, port)\r
835     {\r
836     }\r
837 \r
838     void UpdateState()\r
839     {\r
840         PLT_Service* avt;\r
841         if(NPT_FAILED(FindServiceByType("urn:schemas-upnp-org:service:AVTransport:1", avt)))\r
842           return;\r
843 \r
844         bool publish = true;\r
845         CStdString buffer;\r
846 \r
847         StringUtils::SecondsToTimeString((long)g_application.GetTime(), buffer, TIME_FORMAT_HH_MM_SS);\r
848         avt->SetStateVariable("RelativeTimePosition", buffer.c_str(), publish);\r
849 \r
850         StringUtils::SecondsToTimeString((long)g_application.GetTotalTime(), buffer, TIME_FORMAT_HH_MM_SS);\r
851         avt->SetStateVariable("CurrentTrackDuration", buffer.c_str(), publish);\r
852 \r
853         // TODO - these states don't generate events, LastChange state needs to be fixed\r
854         if (g_application.IsPlaying()) {\r
855             avt->SetStateVariable("TransportState", "PLAYING", publish);\r
856             avt->SetStateVariable("TransportStatus", "OK", publish);\r
857             avt->SetStateVariable("TransportPlaySpeed", "1", publish);\r
858             avt->SetStateVariable("NumberOfTracks", "1", publish);\r
859             avt->SetStateVariable("CurrentTrack", "1", publish);\r
860         } else {\r
861             avt->SetStateVariable("TransportState", "STOPPED", publish);\r
862             avt->SetStateVariable("TransportStatus", "OK", publish);\r
863             avt->SetStateVariable("TransportPlaySpeed", "1", publish);\r
864             avt->SetStateVariable("NumberOfTracks", "0", publish);\r
865             avt->SetStateVariable("CurrentTrack", "0", publish);\r
866         }\r
867     }\r
868 \r
869     // AVTransport\r
870     virtual NPT_Result OnNext(PLT_ActionReference& action)\r
871     {\r
872         g_applicationMessenger.PlayListPlayerNext();\r
873         return NPT_SUCCESS;\r
874     }\r
875     virtual NPT_Result OnPause(PLT_ActionReference& action)\r
876     {\r
877         if(!g_application.IsPaused())\r
878           g_applicationMessenger.MediaPause();\r
879         return NPT_SUCCESS;\r
880     }\r
881     virtual NPT_Result OnPlay(PLT_ActionReference& action)\r
882     {\r
883         if(g_application.IsPaused())\r
884             g_applicationMessenger.MediaPause();\r
885         return NPT_SUCCESS;\r
886     }\r
887     virtual NPT_Result OnPrevious(PLT_ActionReference& action)\r
888     {\r
889         g_applicationMessenger.PlayListPlayerPrevious();\r
890         return NPT_SUCCESS;\r
891     }\r
892     virtual NPT_Result OnStop(PLT_ActionReference& action)\r
893     {\r
894         g_applicationMessenger.MediaStop();\r
895         return NPT_SUCCESS;\r
896     }\r
897     virtual NPT_Result OnSetAVTransportURI(PLT_ActionReference& action)\r
898     {\r
899         NPT_String uri, meta;\r
900         PLT_Service* service;\r
901 \r
902         NPT_CHECK_SEVERE(action->GetArgumentValue("CurrentURI",uri));\r
903         NPT_CHECK_SEVERE(action->GetArgumentValue("CurrentURIMetaData",meta));\r
904 \r
905         NPT_CHECK_SEVERE(FindServiceByType("urn:schemas-upnp-org:service:AVTransport:1", service));\r
906 \r
907         service->SetStateVariable("TransportState", "TRANSITIONING", false);\r
908         service->SetStateVariable("TransportStatus", "OK", false);\r
909         service->SetStateVariable("TransportPlaySpeed", "1", false);\r
910 \r
911         service->SetStateVariable("AVTransportURI", uri, false);\r
912         service->SetStateVariable("AVTransportURIMetaData", meta, false);\r
913         NPT_CHECK_SEVERE(action->SetArgumentsOutFromStateVariable());\r
914 \r
915         g_applicationMessenger.MediaPlay((const char*)uri);\r
916         \r
917         return NPT_SUCCESS;\r
918     }\r
919 \r
920 };\r
921 \r
922 /*----------------------------------------------------------------------\r
923 |   CCtrlPointReferenceHolder class\r
924 +---------------------------------------------------------------------*/\r
925 class CRendererReferenceHolder\r
926 {\r
927 public:\r
928     PLT_DeviceHostReference m_Device;\r
929 };\r
930 \r
931 \r
932 /*----------------------------------------------------------------------\r
933 |   CUPnP::CUPnP\r
934 +---------------------------------------------------------------------*/\r
935 CUPnP::CUPnP() :\r
936     m_ServerHolder(new CDeviceHostReferenceHolder()),\r
937     m_CtrlPointHolder(new CCtrlPointReferenceHolder()),\r
938     m_RendererHolder(new CRendererReferenceHolder())\r
939 {\r
940 //#ifdef HAS_XBOX_HARDWARE\r
941 //    broadcast = true;\r
942 //#else\r
943 //    broadcast = false;\r
944 //#endif\r
945     // xbox can't receive multicast, but it can send it\r
946     broadcast = false;\r
947     \r
948     // initialize upnp in broadcast listening mode for xbmc\r
949     m_UPnP = new PLT_UPnP(1900, !broadcast);\r
950 \r
951     // start upnp monitoring\r
952     m_UPnP->Start();\r
953 }\r
954 \r
955 /*----------------------------------------------------------------------\r
956 |   CUPnP::~CUPnP\r
957 +---------------------------------------------------------------------*/\r
958 CUPnP::~CUPnP()\r
959 {\r
960     m_UPnP->Stop();\r
961     StopClient();\r
962     StopServer();\r
963 \r
964     delete m_UPnP;\r
965     delete m_ServerHolder;\r
966     delete m_RendererHolder;\r
967     delete m_CtrlPointHolder;\r
968 }\r
969 \r
970 /*----------------------------------------------------------------------\r
971 |   CUPnP::GetInstance\r
972 +---------------------------------------------------------------------*/\r
973 CUPnP*\r
974 CUPnP::GetInstance()\r
975 {\r
976     if (!upnp) {\r
977         upnp = new CUPnP();\r
978     }\r
979 \r
980     return upnp;\r
981 }\r
982 \r
983 /*----------------------------------------------------------------------\r
984 |   CUPnP::ReleaseInstance\r
985 +---------------------------------------------------------------------*/\r
986 void\r
987 CUPnP::ReleaseInstance()\r
988 {\r
989     if (upnp) {\r
990         // since it takes a while to clean up\r
991         // starts a detached thread to do this\r
992         CUPnPCleaner* cleaner = new CUPnPCleaner(upnp);\r
993         cleaner->Start();\r
994         upnp = NULL;\r
995     }\r
996 }\r
997 \r
998 /*----------------------------------------------------------------------\r
999 |   CUPnP::StartClient\r
1000 +---------------------------------------------------------------------*/\r
1001 void\r
1002 CUPnP::StartClient()\r
1003 {\r
1004     if (!m_CtrlPointHolder->m_CtrlPoint.IsNull()) return;\r
1005 \r
1006     // create controlpoint, pass NULL to avoid sending a multicast search\r
1007     m_CtrlPointHolder->m_CtrlPoint = new PLT_CtrlPoint(broadcast?NULL:"upnp:rootdevice");\r
1008 \r
1009     // ignore our own server\r
1010     if (!m_ServerHolder->m_Device.IsNull()) {\r
1011         m_CtrlPointHolder->m_CtrlPoint->IgnoreUUID(m_ServerHolder->m_Device->GetUUID());\r
1012     }\r
1013 \r
1014     // start it\r
1015     m_UPnP->AddCtrlPoint(m_CtrlPointHolder->m_CtrlPoint);\r
1016 \r
1017     // start browser\r
1018     m_MediaBrowser = new PLT_SyncMediaBrowser(m_CtrlPointHolder->m_CtrlPoint, true);\r
1019 \r
1020 #ifdef _XBOX\r
1021     // Issue a search request on the every 6 seconds, both on broadcast and multicast\r
1022     // xbox can't receive multicast, but it can send it so upnp clients know we are here\r
1023     m_CtrlPointHolder->m_CtrlPoint->Discover(NPT_HttpUrl("255.255.255.255", 1900, "*"), "upnp:rootdevice", 1, 6000);\r
1024     m_CtrlPointHolder->m_CtrlPoint->Discover(NPT_HttpUrl("239.255.255.250", 1900, "*"), "upnp:rootdevice", 1, 6000);\r
1025 #endif\r
1026 }\r
1027 \r
1028 /*----------------------------------------------------------------------\r
1029 |   CUPnP::StopClient\r
1030 +---------------------------------------------------------------------*/\r
1031 void\r
1032 CUPnP::StopClient()\r
1033 {\r
1034     if (m_CtrlPointHolder->m_CtrlPoint.IsNull()) return;\r
1035 \r
1036     m_UPnP->RemoveCtrlPoint(m_CtrlPointHolder->m_CtrlPoint);\r
1037     m_CtrlPointHolder->m_CtrlPoint = NULL;\r
1038     delete m_MediaBrowser;\r
1039     m_MediaBrowser = NULL;\r
1040 }\r
1041 \r
1042 /*----------------------------------------------------------------------\r
1043 |   CUPnP::StartServer\r
1044 +---------------------------------------------------------------------*/\r
1045 void\r
1046 CUPnP::StartServer()\r
1047 {\r
1048     if (!m_ServerHolder->m_Device.IsNull()) return;\r
1049 \r
1050     // load upnpserver.xml so that g_settings.m_vecUPnPMusicShares, etc.. are loaded\r
1051     CStdString filename;\r
1052     CUtil::AddFileToFolder(g_settings.GetUserDataFolder(), "upnpserver.xml", filename);\r
1053     g_settings.LoadUPnPXml(filename);\r
1054 \r
1055     // create the server with the friendlyname and UUID from upnpserver.xml if found\r
1056     m_ServerHolder->m_Device = new CUPnPServer("XBMC - Media Server",\r
1057         g_settings.m_UPnPUUID.length()?g_settings.m_UPnPUUID.c_str():NULL);\r
1058 \r
1059     // trying to set optional upnp values for XP UPnP UI Icons to detect us\r
1060     // but it doesn't work anyways as it requires multicast for XP to detect us\r
1061     NPT_String ip = g_network.m_networkinfo.ip;\r
1062 #ifndef HAS_XBOX_NETWORK\r
1063     NPT_List<NPT_String> list;\r
1064     if (NPT_SUCCEEDED(PLT_UPnPMessageHelper::GetIPAddresses(list))) {\r
1065         ip = *(list.GetFirstItem());\r
1066     }\r
1067 #endif\r
1068     m_ServerHolder->m_Device->m_PresentationURL = NPT_HttpUrl(ip, atoi(g_guiSettings.GetString("servers.webserverport")), "/").ToString();\r
1069     m_ServerHolder->m_Device->m_ModelName = "Xbox Media Center";\r
1070     m_ServerHolder->m_Device->m_ModelDescription = "Xbox Media Center - Media Server";\r
1071     m_ServerHolder->m_Device->m_ModelURL = "http://www.xboxmediacenter.com/";\r
1072     m_ServerHolder->m_Device->m_ModelNumber = "2.0";\r
1073     m_ServerHolder->m_Device->m_ModelName = "XBMC";\r
1074     m_ServerHolder->m_Device->m_Manufacturer = "Xbox Team";\r
1075     m_ServerHolder->m_Device->m_ManufacturerURL = "http://www.xboxmediacenter.com/";\r
1076 \r
1077     // since the xbox doesn't support multicast\r
1078     // we use broadcast but we advertise more often\r
1079     m_ServerHolder->m_Device->SetBroadcast(broadcast);\r
1080 \r
1081     // tell controller to ignore ourselves from list of upnp servers\r
1082     if (!m_CtrlPointHolder->m_CtrlPoint.IsNull()) {\r
1083         m_CtrlPointHolder->m_CtrlPoint->IgnoreUUID(m_ServerHolder->m_Device->GetUUID());\r
1084     }\r
1085 \r
1086     // start server\r
1087     m_UPnP->AddDevice(m_ServerHolder->m_Device);\r
1088 \r
1089     // save UUID\r
1090     g_settings.m_UPnPUUID = m_ServerHolder->m_Device->GetUUID();\r
1091     g_settings.SaveUPnPXml(filename);\r
1092 }\r
1093 \r
1094 /*----------------------------------------------------------------------\r
1095 |   CUPnP::StopServer\r
1096 +---------------------------------------------------------------------*/\r
1097 void\r
1098 CUPnP::StopServer()\r
1099 {\r
1100     if (m_ServerHolder->m_Device.IsNull()) return;\r
1101 \r
1102     m_UPnP->RemoveDevice(m_ServerHolder->m_Device);\r
1103     m_ServerHolder->m_Device = NULL;\r
1104 }\r
1105 \r
1106 void CUPnP::StartRenderer()\r
1107 {\r
1108     if (!m_RendererHolder->m_Device.IsNull()) return;\r
1109 \r
1110     CStdString filename;\r
1111     CUtil::AddFileToFolder(g_settings.GetUserDataFolder(), "upnpserver.xml", filename);\r
1112     g_settings.LoadUPnPXml(filename);\r
1113 \r
1114     NPT_String ip = g_network.m_networkinfo.ip;\r
1115 #ifndef HAS_XBOX_NETWORK\r
1116     NPT_List<NPT_String> list;\r
1117     if (NPT_SUCCEEDED(PLT_UPnPMessageHelper::GetIPAddresses(list))) {\r
1118         ip = *(list.GetFirstItem());\r
1119     }\r
1120 #endif\r
1121 \r
1122     m_RendererHolder->m_Device = new CUPnPRenderer("XBMC - Media Renderer", true, \r
1123           (g_settings.m_UPnPUUIDRenderer.length() ? g_settings.m_UPnPUUIDRenderer.c_str() : NULL) );\r
1124 \r
1125     m_RendererHolder->m_Device->m_PresentationURL = NPT_HttpUrl(ip, atoi(g_guiSettings.GetString("servers.webserverport")), "/").ToString();\r
1126     m_RendererHolder->m_Device->m_ModelName = "Xbox Media Center";\r
1127     m_RendererHolder->m_Device->m_ModelDescription = "Xbox Media Center - Media Renderer";\r
1128     m_RendererHolder->m_Device->m_ModelURL = "http://www.xboxmediacenter.com/";\r
1129     m_RendererHolder->m_Device->m_ModelNumber = "2.0";\r
1130     m_RendererHolder->m_Device->m_Manufacturer = "Xbox Team";\r
1131     m_RendererHolder->m_Device->m_ManufacturerURL = "http://www.xboxmediacenter.com/";\r
1132 \r
1133     m_RendererHolder->m_Device->SetBroadcast(broadcast);\r
1134 \r
1135     m_UPnP->AddDevice(m_RendererHolder->m_Device);\r
1136 \r
1137     // save UUID\r
1138     g_settings.m_UPnPUUIDRenderer = m_RendererHolder->m_Device->GetUUID();\r
1139     g_settings.SaveUPnPXml(filename);\r
1140 }\r
1141 \r
1142 void CUPnP::StopRenderer()\r
1143 {\r
1144     if (m_RendererHolder->m_Device.IsNull()) return;\r
1145 \r
1146     m_UPnP->RemoveDevice(m_RendererHolder->m_Device);\r
1147     m_RendererHolder->m_Device = NULL;\r
1148 }\r
1149 \r
1150 void CUPnP::UpdateState()\r
1151 {\r
1152   if (!m_RendererHolder->m_Device.IsNull())\r
1153       ((CUPnPRenderer*)m_RendererHolder->m_Device.AsPointer())->UpdateState();  \r
1154 }