summaryrefslogtreecommitdiffstats
path: root/xbmc/utils/FileOperationJob.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/utils/FileOperationJob.cpp')
-rw-r--r--xbmc/utils/FileOperationJob.cpp353
1 files changed, 353 insertions, 0 deletions
diff --git a/xbmc/utils/FileOperationJob.cpp b/xbmc/utils/FileOperationJob.cpp
new file mode 100644
index 0000000..67a5536
--- /dev/null
+++ b/xbmc/utils/FileOperationJob.cpp
@@ -0,0 +1,353 @@
1/*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
8
9#include "FileOperationJob.h"
10
11#include "ServiceBroker.h"
12#include "URL.h"
13#include "Util.h"
14#include "dialogs/GUIDialogExtendedProgressBar.h"
15#include "filesystem/Directory.h"
16#include "filesystem/File.h"
17#include "filesystem/FileDirectoryFactory.h"
18#include "guilib/GUIComponent.h"
19#include "guilib/GUIWindowManager.h"
20#include "guilib/LocalizeStrings.h"
21#include "utils/StringUtils.h"
22#include "utils/URIUtils.h"
23#include "utils/log.h"
24
25using namespace XFILE;
26
27CFileOperationJob::CFileOperationJob()
28 : m_items(),
29 m_strDestFile(),
30 m_avgSpeed(),
31 m_currentOperation(),
32 m_currentFile()
33{ }
34
35CFileOperationJob::CFileOperationJob(FileAction action, CFileItemList & items,
36 const std::string& strDestFile,
37 bool displayProgress /* = false */,
38 int heading /* = 0 */, int line /* = 0 */)
39 : m_action(action),
40 m_items(),
41 m_strDestFile(strDestFile),
42 m_avgSpeed(),
43 m_currentOperation(),
44 m_currentFile(),
45 m_displayProgress(displayProgress),
46 m_heading(heading),
47 m_line(line)
48{
49 SetFileOperation(action, items, strDestFile);
50}
51
52void CFileOperationJob::SetFileOperation(FileAction action, CFileItemList &items, const std::string &strDestFile)
53{
54 m_action = action;
55 m_strDestFile = strDestFile;
56
57 m_items.Clear();
58 for (int i = 0; i < items.Size(); i++)
59 m_items.Add(CFileItemPtr(new CFileItem(*items[i])));
60}
61
62bool CFileOperationJob::DoWork()
63{
64 FileOperationList ops;
65 double totalTime = 0.0;
66
67 if (m_displayProgress && GetProgressDialog() == NULL)
68 {
69 CGUIDialogExtendedProgressBar* dialog =
70 CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogExtendedProgressBar>(WINDOW_DIALOG_EXT_PROGRESS);
71 SetProgressBar(dialog->GetHandle(GetActionString(m_action)));
72 }
73
74 bool success = DoProcess(m_action, m_items, m_strDestFile, ops, totalTime);
75
76 unsigned int size = ops.size();
77
78 double opWeight = 100.0 / totalTime;
79 double current = 0.0;
80
81 for (unsigned int i = 0; i < size && success; i++)
82 success &= ops[i].ExecuteOperation(this, current, opWeight);
83
84 MarkFinished();
85
86 return success;
87}
88
89bool CFileOperationJob::DoProcessFile(FileAction action, const std::string& strFileA, const std::string& strFileB, FileOperationList &fileOperations, double &totalTime)
90{
91 int64_t time = 1;
92
93 if (action == ActionCopy || action == ActionReplace || (action == ActionMove && !CanBeRenamed(strFileA, strFileB)))
94 {
95 struct __stat64 data;
96 if (CFile::Stat(strFileA, &data) == 0)
97 time += data.st_size;
98 }
99
100 fileOperations.push_back(CFileOperation(action, strFileA, strFileB, time));
101
102 totalTime += time;
103
104 return true;
105}
106
107bool CFileOperationJob::DoProcessFolder(FileAction action, const std::string& strPath, const std::string& strDestFile, FileOperationList &fileOperations, double &totalTime)
108{
109 // check whether this folder is a filedirectory - if so, we don't process it's contents
110 CFileItem item(strPath, false);
111 IFileDirectory *file = CFileDirectoryFactory::Create(item.GetURL(), &item);
112 if (file)
113 {
114 delete file;
115 return true;
116 }
117
118 CFileItemList items;
119 CDirectory::GetDirectory(strPath, items, "", DIR_FLAG_NO_FILE_DIRS | DIR_FLAG_GET_HIDDEN);
120 for (int i = 0; i < items.Size(); i++)
121 {
122 CFileItemPtr pItem = items[i];
123 pItem->Select(true);
124 }
125
126 if (!DoProcess(action, items, strDestFile, fileOperations, totalTime))
127 {
128 CLog::Log(LOGERROR,"FileManager: error while processing folder: %s", strPath.c_str());
129 return false;
130 }
131
132 if (action == ActionMove)
133 {
134 fileOperations.push_back(CFileOperation(ActionDeleteFolder, strPath, "", 1));
135 totalTime += 1.0;
136 }
137
138 return true;
139}
140
141bool CFileOperationJob::DoProcess(FileAction action, CFileItemList & items, const std::string& strDestFile, FileOperationList &fileOperations, double &totalTime)
142{
143 for (int iItem = 0; iItem < items.Size(); ++iItem)
144 {
145 CFileItemPtr pItem = items[iItem];
146 if (pItem->IsSelected())
147 {
148 std::string strNoSlash = pItem->GetPath();
149 URIUtils::RemoveSlashAtEnd(strNoSlash);
150 std::string strFileName = URIUtils::GetFileName(strNoSlash);
151
152 // special case for upnp
153 if (URIUtils::IsUPnP(items.GetPath()) || URIUtils::IsUPnP(pItem->GetPath()))
154 {
155 // get filename from label instead of path
156 strFileName = pItem->GetLabel();
157
158 if (!pItem->m_bIsFolder && !URIUtils::HasExtension(strFileName))
159 {
160 // FIXME: for now we only work well if the url has the extension
161 // we should map the content type to the extension otherwise
162 strFileName += URIUtils::GetExtension(pItem->GetPath());
163 }
164
165 strFileName = CUtil::MakeLegalFileName(strFileName);
166 }
167
168 std::string strnewDestFile;
169 if (!strDestFile.empty()) // only do this if we have a destination
170 strnewDestFile = URIUtils::ChangeBasePath(pItem->GetPath(), strFileName, strDestFile); // Convert (URL) encoding + slashes (if source / target differ)
171
172 if (pItem->m_bIsFolder)
173 {
174 // in ActionReplace mode all subdirectories will be removed by the below
175 // DoProcessFolder(ActionDelete) call as well, so ActionCopy is enough when
176 // processing those
177 FileAction subdirAction = (action == ActionReplace) ? ActionCopy : action;
178 // create folder on dest. drive
179 if (action != ActionDelete && action != ActionDeleteFolder)
180 DoProcessFile(ActionCreateFolder, strnewDestFile, "", fileOperations, totalTime);
181
182 if (action == ActionReplace && CDirectory::Exists(strnewDestFile))
183 DoProcessFolder(ActionDelete, strnewDestFile, "", fileOperations, totalTime);
184
185 if (!DoProcessFolder(subdirAction, pItem->GetPath(), strnewDestFile, fileOperations, totalTime))
186 return false;
187
188 if (action == ActionDelete || action == ActionDeleteFolder)
189 DoProcessFile(ActionDeleteFolder, pItem->GetPath(), "", fileOperations, totalTime);
190 }
191 else
192 DoProcessFile(action, pItem->GetPath(), strnewDestFile, fileOperations, totalTime);
193 }
194 }
195
196 return true;
197}
198
199CFileOperationJob::CFileOperation::CFileOperation(FileAction action, const std::string &strFileA, const std::string &strFileB, int64_t time)
200 : m_action(action),
201 m_strFileA(strFileA),
202 m_strFileB(strFileB),
203 m_time(time)
204{ }
205
206struct DataHolder
207{
208 CFileOperationJob *base;
209 double current;
210 double opWeight;
211};
212
213std::string CFileOperationJob::GetActionString(FileAction action)
214{
215 std::string result;
216 switch (action)
217 {
218 case ActionCopy:
219 case ActionReplace:
220 result = g_localizeStrings.Get(115);
221 break;
222
223 case ActionMove:
224 result = g_localizeStrings.Get(116);
225 break;
226
227 case ActionDelete:
228 case ActionDeleteFolder:
229 result = g_localizeStrings.Get(117);
230 break;
231
232 case ActionCreateFolder:
233 result = g_localizeStrings.Get(119);
234 break;
235
236 default:
237 break;
238 }
239
240 return result;
241}
242
243bool CFileOperationJob::CFileOperation::ExecuteOperation(CFileOperationJob *base, double &current, double opWeight)
244{
245 bool bResult = true;
246
247 base->m_currentFile = CURL(m_strFileA).GetFileNameWithoutPath();
248 base->m_currentOperation = GetActionString(m_action);
249
250 if (base->ShouldCancel((unsigned int)current, 100))
251 return false;
252
253 base->SetText(base->GetCurrentFile());
254
255 DataHolder data = {base, current, opWeight};
256
257 switch (m_action)
258 {
259 case ActionCopy:
260 case ActionReplace:
261 bResult = CFile::Copy(m_strFileA, m_strFileB, this, &data);
262 break;
263
264 case ActionMove:
265 if (CanBeRenamed(m_strFileA, m_strFileB))
266 bResult = CFile::Rename(m_strFileA, m_strFileB);
267 else if (CFile::Copy(m_strFileA, m_strFileB, this, &data))
268 bResult = CFile::Delete(m_strFileA);
269 else
270 bResult = false;
271 break;
272
273 case ActionDelete:
274 bResult = CFile::Delete(m_strFileA);
275 break;
276
277 case ActionDeleteFolder:
278 bResult = CDirectory::Remove(m_strFileA);
279 break;
280
281 case ActionCreateFolder:
282 bResult = CDirectory::Create(m_strFileA);
283 break;
284
285 default:
286 CLog::Log(LOGERROR, "FileManager: unknown operation");
287 bResult = false;
288 break;
289 }
290
291 current += (double)m_time * opWeight;
292
293 return bResult;
294}
295
296inline bool CFileOperationJob::CanBeRenamed(const std::string &strFileA, const std::string &strFileB)
297{
298#ifndef TARGET_POSIX
299 if (strFileA[1] == ':' && strFileA[0] == strFileB[0])
300 return true;
301#else
302 if (URIUtils::IsHD(strFileA) && URIUtils::IsHD(strFileB))
303 return true;
304 else if (URIUtils::IsSmb(strFileA) && URIUtils::IsSmb(strFileB)) {
305 CURL smbFileA(strFileA), smbFileB(strFileB);
306 return smbFileA.GetHostName() == smbFileB.GetHostName() &&
307 smbFileA.GetShareName() == smbFileB.GetShareName();
308 }
309#endif
310 return false;
311}
312
313bool CFileOperationJob::CFileOperation::OnFileCallback(void* pContext, int ipercent, float avgSpeed)
314{
315 DataHolder *data = static_cast<DataHolder*>(pContext);
316 double current = data->current + ((double)ipercent * data->opWeight * (double)m_time)/ 100.0;
317
318 if (avgSpeed > 1000000.0f)
319 data->base->m_avgSpeed = StringUtils::Format("%.1f MB/s", avgSpeed / 1000000.0f);
320 else
321 data->base->m_avgSpeed = StringUtils::Format("%.1f KB/s", avgSpeed / 1000.0f);
322
323 std::string line;
324 line = StringUtils::Format("%s (%s)",
325 data->base->GetCurrentFile().c_str(),
326 data->base->GetAverageSpeed().c_str());
327 data->base->SetText(line);
328 return !data->base->ShouldCancel((unsigned)current, 100);
329}
330
331bool CFileOperationJob::operator==(const CJob* job) const
332{
333 if (strcmp(job->GetType(), GetType()) != 0)
334 return false;
335
336 const CFileOperationJob* rjob = dynamic_cast<const CFileOperationJob*>(job);
337 if (rjob == NULL)
338 return false;
339
340 if (GetAction() != rjob->GetAction() ||
341 m_strDestFile != rjob->m_strDestFile ||
342 m_items.Size() != rjob->m_items.Size())
343 return false;
344
345 for (int i = 0; i < m_items.Size(); i++)
346 {
347 if (m_items[i]->GetPath() != rjob->m_items[i]->GetPath() ||
348 m_items[i]->IsSelected() != rjob->m_items[i]->IsSelected())
349 return false;
350 }
351
352 return true;
353}