Merge pull request #4314 from MartijnKaijser/beta1
[vuplus_xbmc] / xbmc / filesystem / RTVDirectory.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 // RTVDirectory.cpp: implementation of the CRTVDirectory class.
22 //
23 //////////////////////////////////////////////////////////////////////
24
25 #include "RTVDirectory.h"
26 #include "utils/URIUtils.h"
27 #include "URL.h"
28 #include "utils/XBMCTinyXML.h"
29 #include "utils/StringUtils.h"
30 #include "FileItem.h"
31
32 using namespace XFILE;
33
34 extern "C"
35 {
36 #include "lib/libRTV/interface.h"
37 }
38
39 //////////////////////////////////////////////////////////////////////
40 // Construction/Destruction
41 //////////////////////////////////////////////////////////////////////
42
43 CRTVDirectory::CRTVDirectory(void)
44 {
45 }
46
47 CRTVDirectory::~CRTVDirectory(void)
48 {
49 }
50
51 //*********************************************************************************************
52 bool CRTVDirectory::GetDirectory(const CStdString& strPath, CFileItemList &items)
53 {
54   CURL url(strPath);
55
56   CStdString strRoot = strPath;
57   URIUtils::AddSlashAtEnd(strRoot);
58
59   // Host name is "*" so we try to discover all ReplayTVs.  This requires some trickery but works.
60   if (url.GetHostName() == "*")
61   {
62     // Check to see whether the URL's path is blank or "Video"
63     if (url.GetFileName() == "" || url.GetFileName() == "Video")
64     {
65       int iOldSize=items.Size();
66       struct RTV * rtv = NULL;
67       int numRTV;
68
69       // Request that all ReplayTVs on the LAN identify themselves.  Each ReplayTV
70       // is given 3000ms to respond to the request.  rtv_discovery returns an array
71       // of structs containing the IP and friendly name of all ReplayTVs found on the LAN.
72       // For some reason, DVArchive doesn't respond to this request (probably only responds
73       // to requests from an IP address of a real ReplayTV).
74       numRTV = rtv_discovery(&rtv, 3000);
75
76       // Run through the array and add the ReplayTVs found as folders in XBMC.
77       // We must add the IP of each ReplayTV as if it is a file name at the end of a the
78       // auto-discover URL--e.g. rtv://*/192.168.1.100--because XBMC does not permit
79       // dyamically added shares and will not play from them.  This little trickery is
80       // the best workaround I could come up with.
81       for (int i = 0; i < numRTV; i++)
82       {
83         CFileItemPtr pItem(new CFileItem(rtv[i].friendlyName));
84         // This will keep the /Video or / and allow one to set up an auto ReplayTV
85         // share of either type--simple file listing or ReplayGuide listing.
86         pItem->SetPath(strRoot + rtv[i].hostname);
87         pItem->m_bIsFolder = true;
88         pItem->SetLabelPreformated(true);
89         items.Add(pItem);
90       }
91       free(rtv);
92       return (items.Size()>iOldSize);
93       // Else the URL's path should be an IP address of the ReplayTV
94     }
95     else
96     {
97       CStdString strURL, strRTV;
98       size_t pos;
99
100       // Isolate the IP from the URL and replace the "*" with the real IP
101       // of the ReplayTV.  E.g., rtv://*/Video/192.168.1.100/ becomes
102       // rtv://192.168.1.100/Video/ .  This trickery makes things work.
103       strURL = StringUtils::TrimRight(strRoot, "/");
104       pos = strURL.rfind('/');
105       strRTV = strURL.substr(0, pos + 1);
106       StringUtils::Replace(strRTV, "*", strURL.substr(pos + 1));
107       CURL tmpURL(strRTV);
108
109       // Force the newly constructed share into the right variables to
110       // be further processed by the remainder of GetDirectory.
111       url = tmpURL;
112       strRoot = strRTV;
113     }
114   }
115
116   // Allow for ReplayTVs on ports other than 80
117   CStdString strHostAndPort;
118   strHostAndPort = url.GetHostName();
119   if (url.HasPort())
120   {
121     char buffer[10];
122     sprintf(buffer,"%i",url.GetPort());
123     strHostAndPort += ':';
124     strHostAndPort += buffer;
125   }
126
127   // No path given, list shows from ReplayGuide
128   if (url.GetFileName() == "")
129   {
130     unsigned char * data = NULL;
131
132     // Get the RTV guide data in XML format
133     rtv_get_guide_xml(&data, strHostAndPort.c_str());
134
135     // Begin parsing the XML data
136     CXBMCTinyXML xmlDoc;
137     xmlDoc.Parse( (const char *) data );
138     if ( xmlDoc.Error() )
139     {
140       free(data);
141       return false;
142     }
143     TiXmlElement* pRootElement = xmlDoc.RootElement();
144     if (!pRootElement)
145     {
146       free(data);
147       return false;
148     }
149
150     const TiXmlNode *pChild = pRootElement->FirstChild();
151     while (pChild > 0)
152     {
153       CStdString strTagName = pChild->Value();
154
155       if ( !strcmpi(strTagName.c_str(), "ITEM") )
156       {
157         const TiXmlNode *nameNode = pChild->FirstChild("DISPLAYNAME");
158 //        const TiXmlNode *qualityNode = pChild->FirstChild("QUALITY");
159         const TiXmlNode *recordedNode = pChild->FirstChild("RECORDED");
160         const TiXmlNode *pathNode = pChild->FirstChild("PATH");
161 //        const TiXmlNode *durationNode = pChild->FirstChild("DURATION");
162         const TiXmlNode *sizeNode = pChild->FirstChild("SIZE");
163         const TiXmlNode *atrbNode = pChild->FirstChild("ATTRIB");
164
165         SYSTEMTIME dtDateTime;
166         DWORD dwFileSize = 0;
167         memset(&dtDateTime, 0, sizeof(dtDateTime));
168
169         // DISPLAYNAME
170         const char* szName = NULL;
171         if (nameNode)
172         {
173           szName = nameNode->FirstChild()->Value() ;
174         }
175         else
176         {
177           // Something went wrong, the recording has no name
178           free(data);
179           return false;
180         }
181
182         // QUALITY
183 //        const char* szQuality = NULL;
184 //        if (qualityNode)
185 //        {
186 //          szQuality = qualityNode->FirstChild()->Value() ;
187 //        }
188
189         // RECORDED
190         if (recordedNode)
191         {
192           CStdString strRecorded = recordedNode->FirstChild()->Value();
193
194           if (strRecorded.size() >= 19)
195           {
196             /* TODO:STRING_CLEANUP */
197             int iYear, iMonth, iDay;
198             iYear = atoi(strRecorded.substr(0, 4).c_str());
199             iMonth = atoi(strRecorded.substr(5, 2).c_str());
200             iDay = atoi(strRecorded.substr(8, 2).c_str());
201             dtDateTime.wYear = iYear;
202             dtDateTime.wMonth = iMonth;
203             dtDateTime.wDay = iDay;
204
205             int iHour, iMin, iSec;
206             iHour = atoi(strRecorded.substr(11, 2).c_str());
207             iMin = atoi(strRecorded.substr(14, 2).c_str());
208             iSec = atoi(strRecorded.substr(17, 2).c_str());
209             dtDateTime.wHour = iHour;
210             dtDateTime.wMinute = iMin;
211             dtDateTime.wSecond = iSec;
212           }
213         }
214
215         // PATH
216         const char* szPath = NULL;
217         if (pathNode)
218         {
219           szPath = pathNode->FirstChild()->Value() ;
220         }
221         else
222         {
223           // Something went wrong, the recording has no filename
224           free(data);
225           return false;
226         }
227
228         // DURATION
229 //        const char* szDuration = NULL;
230 //        if (durationNode)
231 //        {
232 //          szDuration = durationNode->FirstChild()->Value() ;
233 //        }
234
235         // SIZE
236         // NOTE: Size here is actually just duration in minutes because
237         // filesize is not reported by the stripped down GuideParser I use
238         if (sizeNode)
239         {
240           dwFileSize = atol( sizeNode->FirstChild()->Value() );
241         }
242
243         // ATTRIB
244         // NOTE: Not currently reported in the XML guide data, nor is it particularly
245         // needed unless someone wants to add the ability to sub-divide the recordings
246         // into categories, as on a real RTV.
247         int attrib = 0;
248         if (atrbNode)
249         {
250           attrib = atoi( atrbNode->FirstChild()->Value() );
251         }
252
253         bool bIsFolder(false);
254         if (attrib & FILE_ATTRIBUTE_DIRECTORY)
255           bIsFolder = true;
256
257         CFileItemPtr pItem(new CFileItem(szName));
258         pItem->m_dateTime=dtDateTime;
259         pItem->SetPath(strRoot + szPath);
260         // Hack to show duration of show in minutes as KB in XBMC because
261         // it doesn't currently permit showing duration in minutes.
262         // E.g., a 30 minute show will show as 29.3 KB in XBMC.
263         pItem->m_dwSize = dwFileSize * 1000;
264         pItem->m_bIsFolder = bIsFolder;
265         pItem->SetLabelPreformated(true);
266         items.Add(pItem);
267       }
268
269       pChild = pChild->NextSibling();
270     }
271
272     free(data);
273
274     // Path given (usually Video), list filenames only
275   }
276   else
277   {
278
279     unsigned char * data;
280     char * p, * q;
281     unsigned long status;
282
283     // Return a listing of all files in the given path
284     status = rtv_list_files(&data, strHostAndPort.c_str(), url.GetFileName().c_str());
285     if (status == 0)
286     {
287       return false;
288     }
289
290     // Loop through the file list using pointers p and q, where p will point to the current
291     // filename and q will point to the next filename
292     p = (char *) data;
293     while (p)
294     {
295       // Look for the end of the current line of the file listing
296       q = strchr(p, '\n');
297       // If found, replace the newline character with the NULL terminator
298       if (q)
299       {
300         *q = '\0';
301         // Increment q so that it points to the next filename
302         q++;
303         // *p should be the current null-terminated filename in the list
304         if (*p)
305         {
306           // Only display MPEG files in XBMC (but not circular.mpg, as that is the RTV
307           // video buffer and XBMC may cause problems if it tries to play it)
308           if (strstr(p, ".mpg") && !strstr(p, "circular"))
309           {
310             CFileItemPtr pItem(new CFileItem(p));
311             pItem->SetPath(strRoot + p);
312             pItem->m_bIsFolder = false;
313             // The list returned by the RTV doesn't include file sizes, unfortunately
314             //pItem->m_dwSize = atol(szSize);
315             pItem->SetLabelPreformated(true);
316             items.Add(pItem);
317           }
318         }
319       }
320       // Point p to the next filename in the list and loop
321       p = q;
322     }
323
324     free(data);
325   }
326
327   return true;
328 }