rendered paste bodydiff --git a/xbmc/FileItem.cpp b/xbmc/FileItem.cppindex 5ad317d..1b3e6d0 100644--- a/xbmc/FileItem.cpp+++ b/xbmc/FileItem.cpp@@ -2215,7 +2215,16 @@ void CFileItemList::Stack() if (Get(stack[0])->IsRAR()) stackPath = Get(stack[0])->m_strPath; else- stackPath = dir.ConstructStackPath(*this, stack);+ {+ CStdString stackTitlePath, commonDir, commonDir2;+ CUtil::GetDirectory(Get(stack[0])->m_strPath, commonDir);+ CUtil::GetDirectory(Get(stack[1])->m_strPath, commonDir2);+ commonDir = CUtil::GetParentPath(commonDir);+ commonDir2 = CUtil::GetParentPath(commonDir2);+ CUtil::GetCommonPath(commonDir, commonDir2);+ CUtil::AddFileToFolder(commonDir, stackName, stackTitlePath);+ stackPath = dir.ConstructStackPath(*this, stack, stackTitlePath);+ } item1->m_strPath = stackPath; // clean up list for (unsigned k = 1; k < stack.size(); k++)diff --git a/xbmc/FileSystem/StackDirectory.cpp b/xbmc/FileSystem/StackDirectory.cppindex f62c5c7..3056713 100644--- a/xbmc/FileSystem/StackDirectory.cpp+++ b/xbmc/FileSystem/StackDirectory.cpp@@ -25,9 +25,8 @@ #include "FileItem.h" #include "StringUtils.h" -#define PRE_2_1_STACK_COMPATIBILITY- using namespace std;+ namespace DIRECTORY { CStackDirectory::CStackDirectory()@@ -38,6 +37,20 @@ namespace DIRECTORY { } + bool CStackDirectory::GetDirectory(const CStdString& strPath, CFileItemList& items, CStdString& strStackTitlePath)+ {+ CStdStringArray files, tokens;+ StringUtils::SplitString(strPath, " ; ", tokens);+ if (tokens.size() > 1) // 4 capture algo+ {+ strStackTitlePath = tokens[1];+ CUtil::UrlDecode(strStackTitlePath);+ }+ else+ strStackTitlePath = GetStackedTitlePath(tokens[0]);+ return GetDirectory(strPath, items);+ }+ bool CStackDirectory::GetDirectory(const CStdString& strPath, CFileItemList& items) { items.Clear();@@ -47,116 +60,129 @@ namespace DIRECTORY //CStdString folder, file; //CUtil::Split(strPath, folder, file); // split files on the single comma- CStdStringArray files;- StringUtils::SplitString(strPath, " , ", files);- if (files.empty())- return false; // error in path- // remove "stack://" from the folder- for (unsigned int i = 0; i < files.size(); i++)+ CStdStringArray files, tokens;+ StringUtils::SplitString(strPath, " ; ", tokens);+ if (!tokens.empty()) {- CStdString file = files[i];- if (i == 0)- file = file.Mid(8);-#ifdef PRE_2_1_STACK_COMPATIBILITY- if (i > 0 && file.Find("\\") == -1 && file.Find('/') == -1)+ StringUtils::SplitString(tokens[0], " , ", files);+ if (files.empty())+ return false; // error in path+ // remove "stack://" from the folder+ for (unsigned int i = 0; i < files.size(); i++) {- CStdString strPath;- CUtil::GetDirectory(items[0]->m_strPath,strPath);- CStdString strFile = file;- CUtil::AddFileToFolder(strPath,strFile,file);+ CStdString file = files[i];+ if (i == 0)+ file = file.Mid(8);+ if (tokens.size() > 1) // 4 capture algo+ CUtil::UrlDecode(file);+ else // replace double comma's with single ones.+ file.Replace(",,", ",");+ CFileItemPtr item(new CFileItem(file));+ item->m_strPath = file;+ item->m_bIsFolder = false;+ items.Add(item); }-#endif- // replace double comma's with single ones.- file.Replace(",,", ",");- CFileItemPtr item(new CFileItem(file));- //CUtil::AddFileToFolder(folder, file, item->m_strPath);- item->m_strPath = file;- item->m_bIsFolder = false;- items.Add(item);+ return true; }- return true;+ return false; } CStdString CStackDirectory::GetStackedTitlePath(const CStdString &strPath) { CStdString path, file, folder;- int pos = strPath.Find(" , ");- // remove "stack://" from the folder- if (pos > 0)+ CStdStringArray tokens;+ StringUtils::SplitString(strPath, " ; ", tokens);+ if (tokens.size() > 1) // 4 capture stack algo appends the stackedTitlePath and url encodes everything {- CUtil::Split(strPath.Left(pos), folder, file);- file.Replace(",,", ",");- folder = folder.Mid(8);- CStdString title, volume;- CUtil::GetVolumeFromFileName(file, title, volume);- CUtil::AddFileToFolder(folder, title, path);+ path = tokens[1];+ CUtil::UrlDecode(path);+ }+ else+ {+ int pos = strPath.Find(" , ");+ // remove "stack://" from the folder+ if (pos > 0)+ {+ CUtil::Split(strPath.Left(pos), folder, file);+ file.Replace(",,", ",");+ folder = folder.Mid(8);+ CStdString title, volume;+ CUtil::GetVolumeFromFileName(file, title, volume);+ CUtil::AddFileToFolder(folder, title, path);+ } } return path; } CStdString CStackDirectory::GetFirstStackedFile(const CStdString &strPath) {+ CStdString strBikeShed;+ CStdStringArray tokens;+ StringUtils::SplitString(strPath, " ; ", tokens);++ if (tokens.size() > 1) // 4 capture stack algo+ strBikeShed = tokens[0];+ else+ strBikeShed = strPath;+ // the stacked files are always in volume order, so just get up to the first filename // occurence of " , " CStdString path, file, folder;- int pos = strPath.Find(" , ");+ int pos = strBikeShed.Find(" , "); if (pos > 0)- CUtil::Split(strPath.Left(pos), folder, file);+ CUtil::Split(strBikeShed.Left(pos), folder, file); else- CUtil::Split(strPath, folder, file); // single filed stacks - should really not happen+ CUtil::Split(strBikeShed, folder, file); // single filed stacks - should really not happen // remove "stack://" from the folder folder = folder.Mid(8);- file.Replace(",,", ",");+ if (tokens.size() <= 1) // Not 4 capture stack algo+ file.Replace(",,", ",");+ CUtil::AddFileToFolder(folder, file, path);-+ if (tokens.size() > 1)+ CUtil::UrlDecode(path); return path; } - CStdString CStackDirectory::ConstructStackPath(const CFileItemList &items, const vector<int> &stack)+ CStdString CStackDirectory::ConstructStackPath(const CFileItemList &items, const vector<int> &stack, const CStdString& strStackedFilename) {- // no checks on the range of stack here.- // we replace all instances of comma's with double comma's, then separate- // the files using " , ".- CStdString stackedPath = "stack://";- CStdString folder, file;- CUtil::Split(items[stack[0]]->m_strPath, folder, file);- stackedPath += folder;- // double escape any occurence of commas- file.Replace(",", ",,");+ CStdString stackedPath("stack://");+ CStdString file(items[stack[0]]->m_strPath);+ CUtil::URLEncode(file); stackedPath += file; for (unsigned int i = 1; i < stack.size(); ++i) { stackedPath += " , "; file = items[stack[i]]->m_strPath;-- // double escape any occurence of commas- file.Replace(",", ",,");+ CUtil::URLEncode(file); stackedPath += file; }++ file = strStackedFilename;+ CUtil::URLEncode(file);+ stackedPath += " ; " + file; return stackedPath; } - bool CStackDirectory::ConstructStackPath(const vector<CStdString> &paths, CStdString& stackedPath)+ bool CStackDirectory::ConstructStackPath(const vector<CStdString> &paths, CStdString& stackedPath, const CStdString& strStackedFilename) { if (paths.size() < 2) return false; stackedPath = "stack://";- CStdString folder, file;- CUtil::Split(paths[0], folder, file);- stackedPath += folder;- // double escape any occurence of commas- file.Replace(",", ",,");+ CStdString file = paths[0];+ CUtil::URLEncode(file); stackedPath += file; for (unsigned int i = 1; i < paths.size(); ++i) { stackedPath += " , "; file = paths[i];-- // double escape any occurence of commas- file.Replace(",", ",,");+ CUtil::URLEncode(file); stackedPath += file; }+ file = strStackedFilename;+ CUtil::URLEncode(file);+ stackedPath += " ; " + file; return true; } }diff --git a/xbmc/FileSystem/StackDirectory.h b/xbmc/FileSystem/StackDirectory.hindex 63eefc9..8be9d15 100644--- a/xbmc/FileSystem/StackDirectory.h+++ b/xbmc/FileSystem/StackDirectory.h@@ -30,11 +30,12 @@ namespace DIRECTORY public: CStackDirectory(); ~CStackDirectory();+ virtual bool GetDirectory(const CStdString& strPath, CFileItemList& items, CStdString& strStackTitlePath); virtual bool GetDirectory(const CStdString& strPath, CFileItemList& items); virtual bool IsAllowed(const CStdString &strFile) const { return true; }; static CStdString GetStackedTitlePath(const CStdString &strPath); static CStdString GetFirstStackedFile(const CStdString &strPath);- CStdString ConstructStackPath(const CFileItemList& items, const std::vector<int> &stack);- bool ConstructStackPath(const std::vector<CStdString> &paths, CStdString &stackedPath);+ static CStdString ConstructStackPath(const CFileItemList& items, const std::vector<int> &stack, const CStdString& strStackTitlePath);+ static bool ConstructStackPath(const std::vector<CStdString> &paths, CStdString &stackedPath, const CStdString& strStackTitlePath); }; }diff --git a/xbmc/URL.cpp b/xbmc/URL.cppindex 765edf2..6847c81 100644--- a/xbmc/URL.cpp+++ b/xbmc/URL.cpp@@ -64,6 +64,7 @@ void CURL::Reset() m_strFileName.clear(); m_strProtocol.clear(); m_strFileType.clear();+ m_strStackPath.clear(); m_iPort = 0; } @@ -162,8 +163,14 @@ void CURL::Parse(const CStdString& strURL1) // ones that come to mind are iso9660, cdda, musicdb, etc. // they are all local protocols and have no server part, port number, special options, etc. // this removes the need for special handling below.- if (- m_strProtocol.Equals("stack") ||+ if (m_strProtocol.Equals("stack"))+ {+ CStdString FirstInStack(DIRECTORY::CStackDirectory::GetFirstStackedFile(strURL));+ m_strStackPath = strURL.Mid(iPos);+ SetFileName(FirstInStack);+ return;+ }+ else if ( m_strProtocol.Equals("virtualpath") || m_strProtocol.Equals("multipath") || m_strProtocol.Equals("filereader") ||@@ -500,6 +507,7 @@ CStdString CURL::Get() const + m_strFileName.length() + m_strOptions.length() + m_strProtocolOptions.length()+ + m_strStackPath.length() + 10; if (m_strProtocol == "")@@ -509,7 +517,11 @@ CStdString CURL::Get() const strURL.reserve(sizeneed); strURL = GetWithoutFilename();- strURL += m_strFileName;++ if (m_strProtocol.Equals("stack"))+ strURL += m_strStackPath;+ else+ strURL += m_strFileName; if( m_strOptions.length() > 0 ) strURL += m_strOptions;@@ -526,10 +538,11 @@ CStdString CURL::GetWithoutUserDetails() const if (m_strProtocol.Equals("stack")) { CFileItemList items;- CStdString strURL2;+ CStdString strURL2,+ stackedTitleName; strURL2 = Get(); DIRECTORY::CStackDirectory dir;- dir.GetDirectory(strURL2,items);+ dir.GetDirectory(strURL2,items, stackedTitleName); vector<CStdString> newItems; for (int i=0;i<items.Size();++i) {@@ -537,7 +550,7 @@ CStdString CURL::GetWithoutUserDetails() const items[i]->m_strPath = url.GetWithoutUserDetails(); newItems.push_back(items[i]->m_strPath); }- dir.ConstructStackPath(newItems,strURL);+ dir.ConstructStackPath(newItems,strURL,stackedTitleName); return strURL; } diff --git a/xbmc/URL.h b/xbmc/URL.hindex bbf6268..b89646b 100644--- a/xbmc/URL.h+++ b/xbmc/URL.h@@ -80,4 +80,5 @@ protected: CStdString m_strFileType; CStdString m_strOptions; CStdString m_strProtocolOptions;+ CStdString m_strStackPath; };diff --git a/xbmc/Util.cpp b/xbmc/Util.cppindex 29b9c6a..3e557b5 100644--- a/xbmc/Util.cpp+++ b/xbmc/Util.cpp@@ -470,6 +470,13 @@ void CUtil::GetCommonPath(CStdString& strParent, const CStdString& strPath) } } +CStdString CUtil::GetParentPath(const CStdString& strPath)+{+ CStdString strReturn;+ GetParentPath(strPath, strReturn);+ return strReturn;+}+ bool CUtil::GetParentPath(const CStdString& strPath, CStdString& strParent) { strParent = "";diff --git a/xbmc/Util.h b/xbmc/Util.hindex 2a34380..7355310 100644--- a/xbmc/Util.h+++ b/xbmc/Util.h@@ -75,6 +75,7 @@ public: static void GetCommonPath(CStdString& strPath, const CStdString& strPath2); static bool IsDOSPath(const CStdString &path); static bool IsHD(const CStdString& strFileName);+ static CStdString GetParentPath(const CStdString& strPath); static bool GetParentPath(const CStdString& strPath, CStdString& strParent); static void GetQualifiedFilename(const CStdString &strBasePath, CStdString &strFilename); static void RunShortcut(const char* szPath);diff --git a/xbmc/VideoDatabase.cpp b/xbmc/VideoDatabase.cppindex 1ab228d..8a25c0b 100644--- a/xbmc/VideoDatabase.cpp+++ b/xbmc/VideoDatabase.cpp@@ -49,7 +49,7 @@ using namespace XFILE; using namespace DIRECTORY; using namespace VIDEO; -#define VIDEO_DATABASE_VERSION 34+#define VIDEO_DATABASE_VERSION 35 #define VIDEO_DATABASE_OLD_VERSION 3.f #define VIDEO_DATABASE_NAME "MyVideos34.db" @@ -3729,6 +3729,126 @@ bool CVideoDatabase::UpdateOldVersion(int iVersion) strSQL = FormatSQL("update episode set c%02d = replace(c%02d,'images.thetvdb.com','thetvdb.com')",VIDEODB_ID_EPISODE_THUMBURL,VIDEODB_ID_EPISODE_THUMBURL); m_pDS->exec(strSQL.c_str()); }+ if (iVersion < 35)+ {+ // Load up our REs+ VECCREGEXP RegExps;+ CRegExp tempRE(true);+ const CStdStringArray& strRegExps = g_advancedSettings.m_videoStackRegExps;+ CStdStringArray::const_iterator itRegExp = strRegExps.begin();+ vector<pair<int, CStdString> > badStacks;+ while (itRegExp != strRegExps.end())+ {+ tempRE.RegComp(*itRegExp);+ if (tempRE.GetCaptureTotal() == 4)+ RegExps.push_back(tempRE);+ else+ CLog::Log(LOGERROR, "Invalid video stack RE (%s). Must have exactly 4 captures.", itRegExp->c_str());+ itRegExp++;+ }++ // Grab all stack paths and their ID+ m_pDS->query("select idFile,strFilename from files where strFilename like 'stack://%'");+ while (!m_pDS->eof())+ {+ CStackDirectory stack;+ CFileItemList files;+ CStdString path(m_pDS->fv(1).get_asString());+ stack.GetDirectory(m_pDS->fv(1).get_asString(), files);++ if (files.Size() > 1)+ {+ CStdString Dir1, File1,+ Dir2, File2,+ CommonDir,+ StackFilename;+ std::pair<std::string::iterator, std::string::iterator> diffs;++ CUtil::Split(files[0]->GetAsUrl().GetWithoutUserDetails(), Dir1, File1);+ CUtil::Split(files[1]->GetAsUrl().GetWithoutUserDetails(), Dir2, File2);+ CommonDir = CUtil::GetParentPath(m_pDS->fv(1).get_asString());++ std::vector<CRegExp>::iterator itRegExp = RegExps.begin();+ int offset = 0;++ while (itRegExp != RegExps.end())+ {+ if (itRegExp->RegFind(File1, offset) != -1)+ {+ CStdString Title1 = itRegExp->GetMatch(1),+ Volume1 = itRegExp->GetMatch(2),+ Ignore1 = itRegExp->GetMatch(3),+ Extension1 = itRegExp->GetMatch(4);+ if (offset)+ Title1 = File1.substr(0, itRegExp->GetSubStart(2));+ if (itRegExp->RegFind(File2, offset) != -1)+ {+ CStdString Title2 = itRegExp->GetMatch(1),+ Volume2 = itRegExp->GetMatch(2),+ Ignore2 = itRegExp->GetMatch(3),+ Extension2 = itRegExp->GetMatch(4);+ if (offset)+ Title2 = File2.substr(0, itRegExp->GetSubStart(2));+ if (Title1.Equals(Title2))+ {+ if (!Volume1.Equals(Volume2))+ {+ if (Ignore1.Equals(Ignore2) && Extension1.Equals(Extension2))+ {+ // got it+ StackFilename = Title1 + Extension1;+ itRegExp = RegExps.end();+ break;+ }+ else // Invalid stack+ break;+ }+ else // Early match, retry with offset+ {+ offset = itRegExp->GetSubStart(3);+ continue;+ }+ }+ }+ }+ offset = 0;+ itRegExp++;+ }++ if (!StackFilename.empty() && !CommonDir.empty())+ {+ std::vector<int> stackIndicies;+ CStdString StackTitleName = CommonDir + StackFilename,+ StackPath,+ PathHash;++ for (int i = 0; i < files.Size(); i++)+ stackIndicies.push_back(i);++ StackPath = CStackDirectory::ConstructStackPath(files, stackIndicies, StackTitleName);+ CStdString sqlCmd = FormatSQL("update files set strFilename='%s' where idFile=%i",+ StackPath.c_str(), m_pDS->fv(0).get_asInt());+ m_pDS2->exec(sqlCmd.c_str());+ CVideoInfoScanner::GetPathHash(files, PathHash);+ sqlCmd = FormatSQL("update path set strHash='%s' where strPath='%s'", CommonDir.c_str(), PathHash.c_str());+ m_pDS2->exec(sqlCmd.c_str());+ }+ else+ badStacks.push_back(make_pair(m_pDS->fv(0).get_asInt(), m_pDS->fv(1).get_asString()));+ }+ m_pDS->next();+ }++ vector<pair<int, CStdString> >::iterator badStack = badStacks.begin();+ while (badStack != badStacks.end())+ {+ CStdString sql = FormatSQL("delete from files where idFile=%i", badStack->first);+ CLog::Log(LOGNOTICE, "UpdateVideoDatabase: Removing %s from library as it no longer stacks", badStack->second.c_str());+ DeleteMovie(badStack->second);+ m_pDS->exec(sql.c_str());+ badStack++;+ }+ } } catch (...) {diff --git a/xbmc/VideoInfoScanner.cpp b/xbmc/VideoInfoScanner.cppindex c51aa0a..19a8fda 100644--- a/xbmc/VideoInfoScanner.cpp+++ b/xbmc/VideoInfoScanner.cpp@@ -1252,6 +1252,8 @@ namespace VIDEO nfoFile = CUtil::AddFileToFolder(strPath,"mymovies.xml"); if (CFile::Exists(nfoFile)) return nfoFile;+ else+ nfoFile.clear(); } } if (item->m_bIsFolder || (bGrabAny && nfoFile.IsEmpty()))diff --git a/xbmc/VideoInfoScanner.h b/xbmc/VideoInfoScanner.hindex a4184a5..5ca3d1c 100644--- a/xbmc/VideoInfoScanner.h+++ b/xbmc/VideoInfoScanner.h@@ -83,6 +83,7 @@ namespace VIDEO bool RetrieveVideoInfo(CFileItemList& items, bool bDirNames, const SScraperInfo& info, bool bRefresh=false, CScraperUrl *pURL=NULL, CGUIDialogProgress* pDlgProgress = NULL); static void ApplyIMDBThumbToFolder(const CStdString &folder, const CStdString &imdbThumb); CNfoFile::NFOResult CheckForNFOFile(CFileItem* pItem, bool bGrabAny, SScraperInfo& info, CScraperUrl& scrUrl);+ static int GetPathHash(const CFileItemList &items, CStdString &hash); CIMDB m_IMDB; protected: virtual void Process();@@ -92,7 +93,6 @@ namespace VIDEO int CountFiles(const CStdString& strPath); void FetchSeasonThumbs(int idTvShow); void FetchActorThumbs(const std::vector<SActorInfo>& actors, const CStdString& strPath);- static int GetPathHash(const CFileItemList &items, CStdString &hash); protected: IVideoInfoScannerObserver* m_pObserver;