2 * Copyright (C) 2005-2013 Team XBMC
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)
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.
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/>.
21 #include "threads/SystemClock.h"
22 #include "MultiPathDirectory.h"
23 #include "Directory.h"
26 #include "guilib/GUIWindowManager.h"
27 #include "dialogs/GUIDialogProgress.h"
29 #include "utils/StringUtils.h"
30 #include "utils/log.h"
31 #include "utils/TimeUtils.h"
32 #include "utils/URIUtils.h"
35 using namespace XFILE;
38 // multipath://{path1}/{path2}/{path3}/.../{path-N}
40 // unlike the older virtualpath:// protocol, sub-folders are combined together into a new
41 // multipath:// style url.
44 CMultiPathDirectory::CMultiPathDirectory()
47 CMultiPathDirectory::~CMultiPathDirectory()
50 bool CMultiPathDirectory::GetDirectory(const CStdString& strPath, CFileItemList &items)
52 CLog::Log(LOGDEBUG,"CMultiPathDirectory::GetDirectory(%s)", strPath.c_str());
54 vector<CStdString> vecPaths;
55 if (!GetPaths(strPath, vecPaths))
58 XbmcThreads::EndTime progressTime(3000); // 3 seconds before showing progress bar
59 CGUIDialogProgress* dlgProgress = NULL;
61 unsigned int iFailures = 0;
62 for (unsigned int i = 0; i < vecPaths.size(); ++i)
64 // show the progress dialog if we have passed our time limit
65 if (progressTime.IsTimePast() && !dlgProgress)
67 dlgProgress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
70 dlgProgress->SetHeading(15310);
71 dlgProgress->SetLine(0, 15311);
72 dlgProgress->SetLine(1, "");
73 dlgProgress->SetLine(2, "");
74 dlgProgress->StartModal();
75 dlgProgress->ShowProgressBar(true);
76 dlgProgress->SetProgressMax((int)vecPaths.size()*2);
77 dlgProgress->Progress();
82 CURL url(vecPaths[i]);
83 dlgProgress->SetLine(1, url.GetWithoutUserDetails());
84 dlgProgress->SetProgressAdvance();
85 dlgProgress->Progress();
88 CFileItemList tempItems;
89 CLog::Log(LOGDEBUG,"Getting Directory (%s)", vecPaths[i].c_str());
90 if (CDirectory::GetDirectory(vecPaths[i], tempItems, m_strFileMask, m_flags))
91 items.Append(tempItems);
94 CLog::Log(LOGERROR,"Error Getting Directory (%s)", vecPaths[i].c_str());
100 dlgProgress->SetProgressAdvance();
101 dlgProgress->Progress();
106 dlgProgress->Close();
108 if (iFailures == vecPaths.size())
111 // merge like-named folders into a sub multipath:// style url
117 bool CMultiPathDirectory::Exists(const char* strPath)
119 CLog::Log(LOGDEBUG,"Testing Existence (%s)", strPath);
121 vector<CStdString> vecPaths;
122 if (!GetPaths(strPath, vecPaths))
125 for (unsigned int i = 0; i < vecPaths.size(); ++i)
127 CLog::Log(LOGDEBUG,"Testing Existence (%s)", vecPaths[i].c_str());
128 if (CDirectory::Exists(vecPaths[i]))
134 bool CMultiPathDirectory::Remove(const char* strPath)
136 vector<CStdString> vecPaths;
137 if (!GetPaths(strPath, vecPaths))
140 bool success = false;
141 for (unsigned int i = 0; i < vecPaths.size(); ++i)
143 if (CDirectory::Remove(vecPaths[i]))
149 CStdString CMultiPathDirectory::GetFirstPath(const CStdString &strPath)
151 size_t pos = strPath.find("/", 12);
152 if (pos != std::string::npos)
154 CStdString firstPath = strPath.substr(12, pos - 12);
155 CURL::Decode(firstPath);
161 bool CMultiPathDirectory::GetPaths(const CStdString& strPath, vector<CStdString>& vecPaths)
164 CStdString strPath1 = strPath;
166 // remove multipath:// from path and any trailing / (so that the last path doesn't get any more than it originally had)
167 strPath1 = strPath1.substr(12);
168 URIUtils::RemoveSlashAtEnd(strPath1);
171 vector<CStdString> vecTemp;
172 StringUtils::SplitString(strPath1, "/", vecTemp);
173 if (vecTemp.size() == 0)
177 for (unsigned int i = 0; i < vecTemp.size(); i++)
179 CStdString tempPath = vecTemp[i];
180 CURL::Decode(tempPath);
181 vecPaths.push_back(tempPath);
186 bool CMultiPathDirectory::HasPath(const CStdString& strPath, const CStdString& strPathToFind)
188 // remove multipath:// from path and any trailing / (so that the last path doesn't get any more than it originally had)
189 CStdString strPath1 = strPath.substr(12);
190 URIUtils::RemoveSlashAtEnd(strPath1);
193 vector<CStdString> vecTemp;
194 StringUtils::SplitString(strPath1, "/", vecTemp);
195 if (vecTemp.size() == 0)
199 for (unsigned int i = 0; i < vecTemp.size(); i++)
201 CStdString tempPath = vecTemp[i];
202 CURL::Decode(tempPath);
203 if(tempPath == strPathToFind)
209 CStdString CMultiPathDirectory::ConstructMultiPath(const CFileItemList& items, const vector<int> &stack)
211 // we replace all instances of comma's with double comma's, then separate
212 // the paths using " , "
213 //CLog::Log(LOGDEBUG, "Building multipath");
214 CStdString newPath = "multipath://";
215 //CLog::Log(LOGDEBUG, "-- adding path: %s", strPath.c_str());
216 for (unsigned int i = 0; i < stack.size(); ++i)
217 AddToMultiPath(newPath, items[stack[i]]->GetPath());
219 //CLog::Log(LOGDEBUG, "Final path: %s", newPath.c_str());
223 void CMultiPathDirectory::AddToMultiPath(CStdString& strMultiPath, const CStdString& strPath)
225 CStdString strPath1 = strPath;
226 URIUtils::AddSlashAtEnd(strMultiPath);
227 //CLog::Log(LOGDEBUG, "-- adding path: %s", strPath.c_str());
228 strPath1 = CURL::Encode(strPath1);
229 strMultiPath += strPath1;
233 CStdString CMultiPathDirectory::ConstructMultiPath(const vector<CStdString> &vecPaths)
235 // we replace all instances of comma's with double comma's, then separate
236 // the paths using " , "
237 //CLog::Log(LOGDEBUG, "Building multipath");
238 CStdString newPath = "multipath://";
239 //CLog::Log(LOGDEBUG, "-- adding path: %s", strPath.c_str());
240 for (unsigned int i = 0; i < vecPaths.size(); ++i)
241 AddToMultiPath(newPath, vecPaths[i]);
242 //CLog::Log(LOGDEBUG, "Final path: %s", newPath.c_str());
246 CStdString CMultiPathDirectory::ConstructMultiPath(const std::set<CStdString> &setPaths)
248 CStdString newPath = "multipath://";
249 for (std::set<CStdString>::const_iterator path = setPaths.begin(); path != setPaths.end(); ++path)
250 AddToMultiPath(newPath, *path);
255 void CMultiPathDirectory::MergeItems(CFileItemList &items)
257 CLog::Log(LOGDEBUG, "CMultiPathDirectory::MergeItems, items = %i", (int)items.Size());
258 unsigned int time = XbmcThreads::SystemClockMillis();
259 if (items.Size() == 0)
261 // sort items by label
262 // folders are before files in this sort method
263 items.Sort(SortByLabel, SortOrderAscending);
266 // if first item in the sorted list is a file, just abort
267 if (!items.Get(i)->m_bIsFolder)
270 while (i + 1 < items.Size())
272 // there are no more folders left, so exit the loop
273 CFileItemPtr pItem1 = items.Get(i);
274 if (!pItem1->m_bIsFolder)
279 CLog::Log(LOGDEBUG,"Testing path: [%03i] %s", i, pItem1->GetPath().c_str());
284 CFileItemPtr pItem2 = items.Get(j);
285 if (!pItem2->GetLabel().Equals(pItem1->GetLabel()))
288 // ignore any filefolders which may coincidently have
289 // the same label as a true folder
290 if (!pItem2->IsFileFolder())
293 CLog::Log(LOGDEBUG," Adding path: [%03i] %s", j, pItem2->GetPath().c_str());
297 while (j < items.Size());
299 // do we have anything to combine?
300 if (stack.size() > 1)
302 // we have a multipath so remove the items and add the new item
303 CStdString newPath = ConstructMultiPath(items, stack);
304 for (unsigned int k = stack.size() - 1; k > 0; --k)
305 items.Remove(stack[k]);
306 pItem1->SetPath(newPath);
307 CLog::Log(LOGDEBUG," New path: %s", pItem1->GetPath().c_str());
314 "CMultiPathDirectory::MergeItems, items = %i, took %d ms",
315 items.Size(), XbmcThreads::SystemClockMillis() - time);
318 bool CMultiPathDirectory::SupportsWriteFileOperations(const CStdString &strPath)
320 vector<CStdString> paths;
321 GetPaths(strPath, paths);
322 for (unsigned int i = 0; i < paths.size(); ++i)
323 if (CUtil::SupportsWriteFileOperations(paths[i]))