changed: Add logic to properly handle subtitles for stacked files
[vuplus_xbmc] / xbmc / filesystem / StackDirectory.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 #include "StackDirectory.h"
22 #include "utils/log.h"
23 #include "utils/URIUtils.h"
24 #include "FileItem.h"
25 #include "utils/StringUtils.h"
26 #include "settings/AdvancedSettings.h"
27 #include "URL.h"
28
29 using namespace std;
30 namespace XFILE
31 {
32   CStackDirectory::CStackDirectory()
33   {
34   }
35
36   CStackDirectory::~CStackDirectory()
37   {
38   }
39
40   bool CStackDirectory::GetDirectory(const CStdString& strPath, CFileItemList& items)
41   {
42     items.Clear();
43     CStdStringArray files;
44     if (!GetPaths(strPath, files))
45       return false;   // error in path
46
47     for (unsigned int i = 0; i < files.size(); i++)
48     {
49       CStdString file = files[i];
50       CFileItemPtr item(new CFileItem(file));
51       item->SetPath(file);
52       item->m_bIsFolder = false;
53       items.Add(item);
54     }
55     return true;
56   }
57
58   CStdString CStackDirectory::GetStackedTitlePath(const CStdString &strPath)
59   {
60     // Load up our REs
61     VECCREGEXP  RegExps;
62     CRegExp     tempRE(true, true);
63     const CStdStringArray& strRegExps = g_advancedSettings.m_videoStackRegExps;
64     CStdStringArray::const_iterator itRegExp = strRegExps.begin();
65     vector<pair<int, CStdString> > badStacks;
66     while (itRegExp != strRegExps.end())
67     {
68       tempRE.RegComp(*itRegExp);
69       if (tempRE.GetCaptureTotal() == 4)
70         RegExps.push_back(tempRE);
71       else
72         CLog::Log(LOGERROR, "Invalid video stack RE (%s). Must have exactly 4 captures.", itRegExp->c_str());
73       itRegExp++;
74     }
75     return GetStackedTitlePath(strPath, RegExps);
76   }
77
78   CStdString CStackDirectory::GetStackedTitlePath(const CStdString &strPath, VECCREGEXP& RegExps)
79   {
80     CStackDirectory stack;
81     CFileItemList   files;
82     CStdString      strStackTitlePath,
83                     strCommonDir        = URIUtils::GetParentPath(strPath);
84
85     stack.GetDirectory(strPath, files);
86
87     if (files.Size() > 1)
88     {
89       CStdString strStackTitle;
90
91       CStdString File1 = URIUtils::GetFileName(files[0]->GetPath());
92       CStdString File2 = URIUtils::GetFileName(files[1]->GetPath());
93       // Check if source path uses URL encoding
94       if (URIUtils::ProtocolHasEncodedFilename(CURL(strCommonDir).GetProtocol()))
95       {
96         CURL::Decode(File1);
97         CURL::Decode(File2);
98       }
99
100       std::vector<CRegExp>::iterator itRegExp = RegExps.begin();
101       int offset = 0;
102
103       while (itRegExp != RegExps.end())
104       {
105         if (itRegExp->RegFind(File1, offset) != -1)
106         {
107           CStdString Title1     = itRegExp->GetMatch(1),
108                      Volume1    = itRegExp->GetMatch(2),
109                      Ignore1    = itRegExp->GetMatch(3),
110                      Extension1 = itRegExp->GetMatch(4);
111           if (offset)
112             Title1 = File1.substr(0, itRegExp->GetSubStart(2));
113           if (itRegExp->RegFind(File2, offset) != -1)
114           {
115             CStdString Title2     = itRegExp->GetMatch(1),
116                        Volume2    = itRegExp->GetMatch(2),
117                        Ignore2    = itRegExp->GetMatch(3),
118                        Extension2 = itRegExp->GetMatch(4);
119             if (offset)
120               Title2 = File2.substr(0, itRegExp->GetSubStart(2));
121             if (Title1.Equals(Title2))
122             {
123               if (!Volume1.Equals(Volume2))
124               {
125                 if (Ignore1.Equals(Ignore2) && Extension1.Equals(Extension2))
126                 {
127                   // got it
128                   strStackTitle = Title1 + Ignore1 + Extension1;
129                   // Check if source path uses URL encoding
130                   if (URIUtils::ProtocolHasEncodedFilename(CURL(strCommonDir).GetProtocol()))
131                     CURL::Encode(strStackTitle);
132
133                   itRegExp = RegExps.end();
134                   break;
135                 }
136                 else // Invalid stack
137                   break;
138               }
139               else // Early match, retry with offset
140               {
141                 offset = itRegExp->GetSubStart(3);
142                 continue;
143               }
144             }
145           }
146         }
147         offset = 0;
148         itRegExp++;
149       }
150       if (!strCommonDir.empty() && !strStackTitle.empty())
151         strStackTitlePath = strCommonDir + strStackTitle;
152     }
153
154     return strStackTitlePath;
155   }
156
157   CStdString CStackDirectory::GetFirstStackedFile(const CStdString &strPath)
158   {
159     // the stacked files are always in volume order, so just get up to the first filename
160     // occurence of " , "
161     CStdString file, folder;
162     size_t pos = strPath.find(" , ");
163     if (pos != std::string::npos)
164       URIUtils::Split((CStdString)strPath.substr(0, pos), folder, file);
165     else
166       URIUtils::Split(strPath, folder, file); // single filed stacks - should really not happen
167
168     // remove "stack://" from the folder
169     folder = folder.substr(8);
170     StringUtils::Replace(file, ",,", ",");
171
172     return URIUtils::AddFileToFolder(folder, file);
173   }
174
175   bool CStackDirectory::GetPaths(const CStdString& strPath, vector<CStdString>& vecPaths)
176   {
177     // format is:
178     // stack://file1 , file2 , file3 , file4
179     // filenames with commas are double escaped (ie replaced with ,,), thus the " , " separator used.
180     CStdString path = strPath;
181     // remove stack:// from the beginning
182     path = path.substr(8);
183     
184     vecPaths.clear();
185     StringUtils::SplitString(path, " , ", vecPaths);
186     if (vecPaths.empty())
187       return false;
188
189     // because " , " is used as a seperator any "," in the real paths are double escaped
190     for (vector<CStdString>::iterator itPath = vecPaths.begin(); itPath != vecPaths.end(); itPath++)
191       StringUtils::Replace(*itPath, ",,", ",");
192
193     return true;
194   }
195
196   CStdString CStackDirectory::ConstructStackPath(const CFileItemList &items, const vector<int> &stack)
197   {
198     // no checks on the range of stack here.
199     // we replace all instances of comma's with double comma's, then separate
200     // the files using " , ".
201     CStdString stackedPath = "stack://";
202     CStdString folder, file;
203     URIUtils::Split(items[stack[0]]->GetPath(), folder, file);
204     stackedPath += folder;
205     // double escape any occurence of commas
206     StringUtils::Replace(file, ",", ",,");
207     stackedPath += file;
208     for (unsigned int i = 1; i < stack.size(); ++i)
209     {
210       stackedPath += " , ";
211       file = items[stack[i]]->GetPath();
212
213       // double escape any occurence of commas
214       StringUtils::Replace(file, ",", ",,");
215       stackedPath += file;
216     }
217     return stackedPath;
218   }
219
220   bool CStackDirectory::ConstructStackPath(const vector<CStdString> &paths, CStdString& stackedPath)
221   {
222     vector<string> pathsT;
223     pathsT.reserve(paths.size());
224     for (vector<CStdString>::const_iterator path = paths.begin();
225          path != paths.end(); ++path)
226     {
227       pathsT.push_back(*path);
228     }
229     std::string stackedPathT = stackedPath;
230     bool retVal = ConstructStackPath(pathsT, stackedPathT);
231     stackedPath = stackedPathT;
232     return retVal;
233   }
234
235   bool CStackDirectory::ConstructStackPath(const vector<std::string> &paths, std::string& stackedPath)
236   {
237     if (paths.size() < 2)
238       return false;
239     stackedPath = "stack://";
240     std::string folder, file;
241     URIUtils::Split(paths[0], folder, file);
242     stackedPath += folder;
243     // double escape any occurence of commas
244     StringUtils::Replace(file, ",", ",,");
245     stackedPath += file;
246     for (unsigned int i = 1; i < paths.size(); ++i)
247     {
248       stackedPath += " , ";
249       file = paths[i];
250
251       // double escape any occurence of commas
252       StringUtils::Replace(file, ",", ",,");
253       stackedPath += file;
254     }
255     return true;
256   }
257 }
258