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