Merge pull request #25808 from CastagnaIT/fix_url_parse
[xbmc.git] / xbmc / filesystem / StackDirectory.cpp
blob9b6aae53b0c1c9154d2ea81de0377e43101a67c3
1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 #include "StackDirectory.h"
11 #include "FileItem.h"
12 #include "FileItemList.h"
13 #include "ServiceBroker.h"
14 #include "URL.h"
15 #include "settings/AdvancedSettings.h"
16 #include "settings/SettingsComponent.h"
17 #include "utils/StringUtils.h"
18 #include "utils/URIUtils.h"
19 #include "utils/log.h"
21 #include <stdlib.h>
23 namespace XFILE
25 CStackDirectory::CStackDirectory() = default;
27 CStackDirectory::~CStackDirectory() = default;
29 bool CStackDirectory::GetDirectory(const CURL& url, CFileItemList& items)
31 items.Clear();
32 std::vector<std::string> files;
33 const std::string pathToUrl(url.Get());
34 if (!GetPaths(pathToUrl, files))
35 return false; // error in path
37 for (const std::string& i : files)
39 CFileItemPtr item(new CFileItem(i));
40 item->SetPath(i);
41 item->m_bIsFolder = false;
42 items.Add(item);
44 return true;
47 std::string CStackDirectory::GetStackedTitlePath(const std::string &strPath)
49 // Load up our REs
50 VECCREGEXP RegExps;
51 CRegExp tempRE(true, CRegExp::autoUtf8);
52 const std::vector<std::string>& strRegExps = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoStackRegExps;
53 std::vector<std::string>::const_iterator itRegExp = strRegExps.begin();
54 while (itRegExp != strRegExps.end())
56 (void)tempRE.RegComp(*itRegExp);
57 if (tempRE.GetCaptureTotal() == 4)
58 RegExps.push_back(tempRE);
59 else
60 CLog::Log(LOGERROR, "Invalid video stack RE ({}). Must have exactly 4 captures.",
61 *itRegExp);
62 ++itRegExp;
64 return GetStackedTitlePath(strPath, RegExps);
67 std::string CStackDirectory::GetStackedTitlePath(const std::string &strPath, VECCREGEXP& RegExps)
69 CStackDirectory stack;
70 CFileItemList files;
71 std::string strStackTitlePath,
72 strCommonDir = URIUtils::GetParentPath(strPath);
74 const CURL pathToUrl(strPath);
75 stack.GetDirectory(pathToUrl, files);
77 if (files.Size() > 1)
79 std::string strStackTitle;
81 std::string File1 = URIUtils::GetFileName(files[0]->GetPath());
82 std::string File2 = URIUtils::GetFileName(files[1]->GetPath());
83 // Check if source path uses URL encoding
84 if (URIUtils::HasEncodedFilename(CURL(strCommonDir)))
86 File1 = CURL::Decode(File1);
87 File2 = CURL::Decode(File2);
90 std::vector<CRegExp>::iterator itRegExp = RegExps.begin();
91 int offset = 0;
93 while (itRegExp != RegExps.end())
95 if (itRegExp->RegFind(File1, offset) != -1)
97 std::string Title1 = itRegExp->GetMatch(1),
98 Volume1 = itRegExp->GetMatch(2),
99 Ignore1 = itRegExp->GetMatch(3),
100 Extension1 = itRegExp->GetMatch(4);
101 if (offset)
102 Title1 = File1.substr(0, itRegExp->GetSubStart(2));
103 if (itRegExp->RegFind(File2, offset) != -1)
105 std::string Title2 = itRegExp->GetMatch(1),
106 Volume2 = itRegExp->GetMatch(2),
107 Ignore2 = itRegExp->GetMatch(3),
108 Extension2 = itRegExp->GetMatch(4);
109 if (offset)
110 Title2 = File2.substr(0, itRegExp->GetSubStart(2));
111 if (StringUtils::EqualsNoCase(Title1, Title2))
113 if (!StringUtils::EqualsNoCase(Volume1, Volume2))
115 if (StringUtils::EqualsNoCase(Ignore1, Ignore2) &&
116 StringUtils::EqualsNoCase(Extension1, Extension2))
118 // got it
119 strStackTitle = Title1 + Ignore1 + Extension1;
120 // Check if source path uses URL encoding
121 if (URIUtils::HasEncodedFilename(CURL(strCommonDir)))
122 strStackTitle = CURL::Encode(strStackTitle);
124 itRegExp = RegExps.end();
125 break;
127 else // Invalid stack
128 break;
130 else // Early match, retry with offset
132 offset = itRegExp->GetSubStart(3);
133 continue;
138 offset = 0;
139 ++itRegExp;
141 if (!strCommonDir.empty() && !strStackTitle.empty())
142 strStackTitlePath = strCommonDir + strStackTitle;
145 return strStackTitlePath;
148 std::string CStackDirectory::GetFirstStackedFile(const std::string &strPath)
150 // the stacked files are always in volume order, so just get up to the first filename
151 // occurrence of " , "
152 std::string file, folder;
153 size_t pos = strPath.find(" , ");
154 if (pos != std::string::npos)
155 URIUtils::Split(strPath.substr(0, pos), folder, file);
156 else
157 URIUtils::Split(strPath, folder, file); // single filed stacks - should really not happen
159 // remove "stack://" from the folder
160 folder = folder.substr(8);
161 StringUtils::Replace(file, ",,", ",");
163 return URIUtils::AddFileToFolder(folder, file);
166 bool CStackDirectory::GetPaths(const std::string& strPath, std::vector<std::string>& vecPaths)
168 // format is:
169 // stack://file1 , file2 , file3 , file4
170 // filenames with commas are double escaped (ie replaced with ,,), thus the " , " separator used.
171 std::string path = strPath;
172 // remove stack:// from the beginning
173 path = path.substr(8);
175 vecPaths = StringUtils::Split(path, " , ");
176 if (vecPaths.empty())
177 return false;
179 // because " , " is used as a separator any "," in the real paths are double escaped
180 for (std::string& itPath : vecPaths)
181 StringUtils::Replace(itPath, ",,", ",");
183 return true;
186 std::string CStackDirectory::ConstructStackPath(const CFileItemList &items, const std::vector<int> &stack)
188 // no checks on the range of stack here.
189 // we replace all instances of comma's with double comma's, then separate
190 // the files using " , ".
191 std::string stackedPath = "stack://";
192 std::string folder, file;
193 URIUtils::Split(items[stack[0]]->GetPath(), folder, file);
194 stackedPath += folder;
195 // double escape any occurrence of commas
196 StringUtils::Replace(file, ",", ",,");
197 stackedPath += file;
198 for (unsigned int i = 1; i < stack.size(); ++i)
200 stackedPath += " , ";
201 file = items[stack[i]]->GetPath();
203 // double escape any occurrence of commas
204 StringUtils::Replace(file, ",", ",,");
205 stackedPath += file;
207 return stackedPath;
210 bool CStackDirectory::ConstructStackPath(const std::vector<std::string> &paths, std::string& stackedPath)
212 if (paths.size() < 2)
213 return false;
214 stackedPath = "stack://";
215 std::string folder, file;
216 URIUtils::Split(paths[0], folder, file);
217 stackedPath += folder;
218 // double escape any occurrence of commas
219 StringUtils::Replace(file, ",", ",,");
220 stackedPath += file;
221 for (unsigned int i = 1; i < paths.size(); ++i)
223 stackedPath += " , ";
224 file = paths[i];
226 // double escape any occurrence of commas
227 StringUtils::Replace(file, ",", ",,");
228 stackedPath += file;
230 return true;