fixed: curl could assert if something threw an exception inside
[xbmc:xbmc-antiquated.git] / xbmc / FileSystem / UPnPDirectory.cpp
1 /*
2 * UPnP Support for XBox Media Center
3 * Copyright (c) 2006 c0diq (Sylvain Rebaud)
4 * Portions Copyright (c) by the authors of libPlatinum
5 *
6 * http://www.plutinosoft.com/blog/category/platinum/
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23
24 #include "stdafx.h"
25 #include "../util.h"
26 #include "UPnPDirectory.h"
27 #include "../UPnP.h"
28 #include "Platinum.h"
29 #include "PltSyncMediaBrowser.h"
30
31 using namespace DIRECTORY;
32 using namespace XFILE;
33
34 namespace DIRECTORY
35 {
36 /*----------------------------------------------------------------------
37 |   CUPnPDirectory::GetFriendlyName
38 +---------------------------------------------------------------------*/
39 const char* 
40 CUPnPDirectory::GetFriendlyName(const char* url)
41 {
42     NPT_String path = url;
43     if (!path.EndsWith("/")) path += "/";
44
45     if (path.Left(7).Compare("upnp://", true) != 0) {
46         return NULL;
47     } else if (path.Compare("upnp://", true) == 0) {
48         return "UPnP Media Servers (Auto-Discover)";
49     } 
50
51     // look for nextslash 
52     int next_slash = path.Find('/', 7);
53     if (next_slash == -1) 
54         return NULL;
55
56     NPT_String uuid = path.SubString(7, next_slash-7);
57     NPT_String object_id = path.SubString(next_slash+1, path.GetLength()-next_slash-2);
58
59     // look for device 
60     PLT_DeviceDataReference* device;
61     const NPT_Lock<PLT_DeviceMap>& devices = CUPnP::GetInstance()->m_MediaBrowser->GetMediaServers();
62     if (NPT_FAILED(devices.Get(uuid, device)) || device == NULL) 
63         return NULL;
64
65     return (const char*)(*device)->GetFriendlyName();
66 }
67
68 /*----------------------------------------------------------------------
69 |   CUPnPDirectory::GetDirectory
70 +---------------------------------------------------------------------*/
71 bool 
72 CUPnPDirectory::GetDirectory(const CStdString& strPath, CFileItemList &items)
73 {
74     CFileItemList vecCacheItems;
75     CUPnP* upnp = CUPnP::GetInstance();
76
77     // start client if it hasn't been done yet
78     upnp->StartClient();
79                      
80     // We accept upnp://devuuid/[item_id/]
81     NPT_String path = strPath.c_str();
82     if (path.Left(7).Compare("upnp://", true) != 0) {
83         return false;
84     } 
85     
86     if (path.Compare("upnp://", true) == 0) {
87         // root -> get list of devices 
88         const NPT_Lock<PLT_DeviceMap>& devices = upnp->m_MediaBrowser->GetMediaServers();
89         const NPT_List<PLT_DeviceMapEntry*>& entries = devices.GetEntries();
90         NPT_List<PLT_DeviceMapEntry*>::Iterator entry = entries.GetFirstItem();
91         while (entry) {
92             PLT_DeviceDataReference device = (*entry)->GetValue();
93             NPT_String name   = device->GetFriendlyName();
94             NPT_String uuid = (*entry)->GetKey();
95
96             CFileItem *pItem = new CFileItem((const char*)name);
97             pItem->m_strPath = (const char*) path + uuid;
98             pItem->m_bIsFolder = true;
99
100             if (!CUtil::HasSlashAtEnd(pItem->m_strPath)) pItem->m_strPath += '/';
101
102             vecCacheItems.Add(pItem);
103             items.Add(new CFileItem(*pItem));
104
105             ++entry;
106         }
107     } else {
108         // look for nextslash 
109         path.TrimRight('/');
110         int next_slash = path.Find('/', 7);
111
112         NPT_String uuid = (next_slash==-1)?path.SubString(7):path.SubString(7, next_slash-7);
113         NPT_String object_id = (next_slash==-1)?"":path.SubString(next_slash+1);
114         if (object_id.GetLength()) {
115             CStdString tmp = object_id;
116             CUtil::UrlDecode(tmp);
117             object_id = tmp;
118         }
119
120         // look for device 
121         PLT_DeviceDataReference* device;
122         const NPT_Lock<PLT_DeviceMap>& devices = upnp->m_MediaBrowser->GetMediaServers();
123         if (NPT_FAILED(devices.Get(uuid, device)) || device == NULL) 
124             return false;
125
126         // issue a browse request with object_id
127         // if object_id is empty use "0" for root 
128         NPT_String root_id = object_id.IsEmpty()?"0":object_id;
129
130         // just a guess as to what types of files we want */
131         bool video = true;
132         bool audio = true;
133         bool image = true;
134         if( !m_strFileMask.IsEmpty() ) {
135           video = m_strFileMask.Find(".wmv") >= 0;
136           audio = m_strFileMask.Find(".wma") >= 0;
137           image = m_strFileMask.Find(".jpg") >= 0;
138         }
139
140         // special case for Windows Media Connect and WMP11 when looking for root
141         // We can target which root subfolder we want based on directory mask
142         if (root_id == "0" 
143          && ((*device)->GetFriendlyName().Find("Windows Media Connect", 0, true) >= 0
144            ||(*device)->m_ModelName == "Windows Media Player Sharing")) {
145
146             // look for a specific type to differentiate which folder we want
147             if (audio && !video && !image) {
148                 // music
149                 root_id = "1";
150             } else if (!audio && video && !image) {
151                 // video
152                 root_id = "2";
153             } else if (!audio && !video && image) {
154                 // pictures
155                 root_id = "3";
156             }
157         }
158
159         // same thing but special case for Xbox Media Center
160         if (root_id == "0" && ((*device)->m_ModelName.Find("Xbox Media Center", 0, true) >= 0)) {
161
162             // look for a specific type to differentiate which folder we want
163             if (audio && !video && !image) {
164                 // music
165                 root_id = "virtualpath://upnpmusic";
166             } else if (!audio && video && !image) {
167                 // video
168                 root_id = "virtualpath://upnpvideo";
169             } else if (!audio && !video && image) {
170                 // pictures
171                 root_id = "virtualpath://upnppictures";
172             }
173         }
174
175         // if error, the list could be partial and that's ok
176         // we still want to process it
177         PLT_MediaObjectListReference list;
178         upnp->m_MediaBrowser->Browse(*device, root_id, list);
179         if (list.IsNull()) return false;
180
181         PLT_MediaObjectList::Iterator entry = list->GetFirstItem();
182         while (entry) {
183             // disregard items with wrong class/type
184             if( (!video && (*entry)->m_ObjectClass.type.CompareN("object.item.videoitem", 21,true) == 0)
185              || (!audio && (*entry)->m_ObjectClass.type.CompareN("object.item.audioitem", 21,true) == 0)
186              || (!image && (*entry)->m_ObjectClass.type.CompareN("object.item.imageitem", 21,true) == 0) )
187             {
188                 ++entry;
189                 continue;
190             }
191
192             CFileItem *pItem = new CFileItem((const char*)(*entry)->m_Title);
193             pItem->m_bIsFolder = (*entry)->IsContainer();
194
195             // if it's a container, format a string as upnp://uuid/object_id/ 
196             if (pItem->m_bIsFolder) {
197                 CStdString object_id = (*entry)->m_ObjectID;
198                 CUtil::URLEncode(object_id);
199                 pItem->m_strPath = (const char*) NPT_String("upnp://") + uuid + "/" + object_id.c_str() + "/";
200             } else {
201                 if ((*entry)->m_Resources.GetItemCount()) {
202                     // if it's an item, path is the first url to the item
203                     // we hope the server made the first one reachable for us
204                     // (it could be a format we dont know how to play however)
205                     pItem->m_strPath = (const char*) (*entry)->m_Resources[0].m_Uri;
206
207                     // set metadata
208                     if ((*entry)->m_Resources[0].m_Size > 0) {
209                         pItem->m_dwSize  = (*entry)->m_Resources[0].m_Size;
210                     }
211                     pItem->GetMusicInfoTag()->SetDuration((*entry)->m_Resources[0].m_Duration);
212                     pItem->GetMusicInfoTag()->SetGenre((const char*) (*entry)->m_Affiliation.genre);
213                     pItem->GetMusicInfoTag()->SetAlbum((const char*) (*entry)->m_Affiliation.album);
214                     
215                     // some servers (like WMC) use upnp:artist instead of dc:creator
216                     if ((*entry)->m_Creator.GetLength() == 0) {
217                         pItem->GetMusicInfoTag()->SetArtist((const char*) (*entry)->m_People.artist);
218                     } else {
219                         pItem->GetMusicInfoTag()->SetArtist((const char*) (*entry)->m_Creator);
220                     }
221                     pItem->GetMusicInfoTag()->SetTitle((const char*) (*entry)->m_Title);                    
222                     pItem->GetMusicInfoTag()->SetLoaded();
223
224                     // look for content type in protocol info
225                     if ((*entry)->m_Resources[0].m_ProtocolInfo.GetLength()) {
226                         char proto[1024];
227                         char dummy1[1024];
228                         char ct[1204];
229                         char dummy2[1024];
230                         int fields = sscanf((*entry)->m_Resources[0].m_ProtocolInfo, "%[^:]:%[^:]:%[^:]:%[^:]", proto, dummy1, ct, dummy2);
231                         if (fields == 4) {
232                             pItem->SetContentType(ct);
233                         }
234                     }
235
236                     // look for date?
237                     if((*entry)->m_Description.date.GetLength()) {
238                       SYSTEMTIME time = {};
239                       int count = sscanf((*entry)->m_Description.date, "%hu-%hu-%huT%hu:%hu:%hu",
240                                           &time.wYear, &time.wMonth, &time.wDay, &time.wHour, &time.wMinute, &time.wSecond);
241                       pItem->m_dateTime = time;
242                     }
243
244                     // check if we have already cached a thumb for this
245                     CStdString thumbnail;
246                     if( (*entry)->m_ObjectClass.type.CompareN("object.item.videoitem", 21,true) == 0 )
247                       thumbnail = pItem->GetCachedVideoThumb();
248                     else if( (*entry)->m_ObjectClass.type.CompareN("object.item.audioitem", 21,true) == 0 )
249                       thumbnail = pItem->GetCachedArtistThumb();
250                     else if( (*entry)->m_ObjectClass.type.CompareN("object.item.imageitem", 21,true) == 0 )
251                       thumbnail = pItem->GetCachedPictureThumb();
252
253                     // if not, let's grab the remote one
254                     if(!CFile::Exists(thumbnail))
255                     {
256                       if((*entry)->m_ExtraInfo.album_art_uri.GetLength())
257                         pItem->SetThumbnailImage((const char*) (*entry)->m_ExtraInfo.album_art_uri);
258                       else if((*entry)->m_Description.icon_uri.GetLength())
259                         pItem->SetThumbnailImage((const char*) (*entry)->m_Description.icon_uri);
260                     }
261
262                 }
263             }
264
265             pItem->SetLabelPreformated(true);
266             vecCacheItems.Add(pItem);
267             items.Add(new CFileItem(*pItem));
268
269             ++entry;
270         }
271     }
272
273     return true;
274 }
275 }