diff options
author | Sascha Montellese <sascha.montellese@gmail.com> | 2013-08-06 00:30:24 -0700 |
---|---|---|
committer | Sascha Montellese <sascha.montellese@gmail.com> | 2013-08-06 00:30:24 -0700 |
commit | 3f422a00208e1338694ffedb8be1f5c68b6aa031 (patch) | |
tree | 0300d315b3823951186ee3223ff639a7233131cf | |
parent | 8b6a71faf9e0cb16db1b1cfcf7e780063597966c (diff) | |
parent | 995c5208a66580e30abbafa492262c8de04772d4 (diff) |
Merge pull request #2988 from Montellese/xsp_extensions
smartplaylists: support for virtual folders
-rw-r--r-- | language/English/strings.po | 7 | ||||
-rw-r--r-- | xbmc/dialogs/GUIDialogSmartPlaylistRule.cpp | 28 | ||||
-rw-r--r-- | xbmc/filesystem/SmartPlaylistDirectory.cpp | 25 | ||||
-rw-r--r-- | xbmc/playlists/SmartPlayList.cpp | 132 | ||||
-rw-r--r-- | xbmc/playlists/SmartPlayList.h | 21 | ||||
-rw-r--r-- | xbmc/utils/DatabaseUtils.h | 1 |
6 files changed, 175 insertions, 39 deletions
diff --git a/language/English/strings.po b/language/English/strings.po index 6b7cbc66a1..830d3d2812 100644 --- a/language/English/strings.po +++ b/language/English/strings.po @@ -2531,7 +2531,12 @@ msgctxt "#613" msgid "Sample rate" msgstr "" -#empty strings from id 614 to 619 +#: xbmc/playlists/SmartPlayList.cpp +msgctxt "#614" +msgid "Virtual folder" +msgstr "" + +#empty strings from id 615 to 619 #: system/settings/settings.xml msgctxt "#620" diff --git a/xbmc/dialogs/GUIDialogSmartPlaylistRule.cpp b/xbmc/dialogs/GUIDialogSmartPlaylistRule.cpp index aa06f377df..41be313636 100644 --- a/xbmc/dialogs/GUIDialogSmartPlaylistRule.cpp +++ b/xbmc/dialogs/GUIDialogSmartPlaylistRule.cpp @@ -103,7 +103,7 @@ void CGUIDialogSmartPlaylistRule::OnBrowse() videodatabase.Open(); std::string basePath; - if (m_type.Equals("songs") || m_type.Equals("albums") || m_type.Equals("artists") || m_type.Equals("mixed")) + if (CSmartPlaylist::IsMusicType(m_type)) basePath = "musicdb://"; else basePath = "videodb://"; @@ -153,7 +153,7 @@ void CGUIDialogSmartPlaylistRule::OnBrowse() } else if (m_rule.m_field == FieldArtist || m_rule.m_field == FieldAlbumArtist) { - if (m_type.Equals("songs") || m_type.Equals("mixed") || m_type.Equals("albums") || m_type.Equals("artists")) + if (CSmartPlaylist::IsMusicType(m_type)) database.GetArtistsNav("musicdb://artists/", items, m_rule.m_field == FieldAlbumArtist, -1); if (m_type.Equals("musicvideos") || m_type.Equals("mixed")) { @@ -165,7 +165,7 @@ void CGUIDialogSmartPlaylistRule::OnBrowse() } else if (m_rule.m_field == FieldAlbum) { - if (m_type.Equals("songs") || m_type.Equals("mixed") || m_type.Equals("albums")) + if (CSmartPlaylist::IsMusicType(m_type)) database.GetAlbumsNav("musicdb://albums/", items); if (m_type.Equals("musicvideos") || m_type.Equals("mixed")) { @@ -182,9 +182,9 @@ void CGUIDialogSmartPlaylistRule::OnBrowse() } else if (m_rule.m_field == FieldYear) { - if (m_type.Equals("songs") || m_type.Equals("mixed") || m_type.Equals("albums")) + if (CSmartPlaylist::IsMusicType(m_type)) database.GetYearsNav("musicdb://years/", items); - if (!m_type.Equals("songs") && !m_type.Equals("albums")) + if (!m_type.Equals("songs") && !m_type.Equals("albums") && !m_type.Equals("artists")) { CFileItemList items2; videodatabase.GetYearsNav(basePath + "years/", items2, type); @@ -242,7 +242,7 @@ void CGUIDialogSmartPlaylistRule::OnBrowse() else assert(false); } - else if (m_rule.m_field == FieldPlaylist) + else if (m_rule.m_field == FieldPlaylist || m_rule.m_field == FieldVirtualFolder) { // use filebrowser to grab another smart playlist @@ -257,7 +257,19 @@ void CGUIDialogSmartPlaylistRule::OnBrowse() { CFileItemPtr item = items[i]; CSmartPlaylist playlist; - if (playlist.OpenAndReadName(item->GetPath())) + // don't list unloadable smartplaylists or any referencable smartplaylists + // which do not match the type of the current smartplaylist + if (!playlist.Load(item->GetPath()) || + (m_rule.m_field == FieldPlaylist && + (!CSmartPlaylist::CheckTypeCompatibility(m_type, playlist.GetType()) || + (!playlist.GetGroup().empty() || playlist.IsGroupMixed())))) + { + items.Remove(i); + i -= 1; + continue; + } + + if (!playlist.GetName().empty()) item->SetLabel(playlist.GetName()); } iLabel = 559; @@ -316,7 +328,7 @@ void CGUIDialogSmartPlaylistRule::OnBrowse() CStdString strHeading; strHeading.Format(g_localizeStrings.Get(13401),g_localizeStrings.Get(iLabel)); pDialog->SetHeading(strHeading); - pDialog->SetMultiSelection(m_rule.m_field != FieldPlaylist); + pDialog->SetMultiSelection(m_rule.m_field != FieldPlaylist && m_rule.m_field != FieldVirtualFolder); if (!m_rule.m_parameter.empty()) pDialog->SetSelected(m_rule.m_parameter); diff --git a/xbmc/filesystem/SmartPlaylistDirectory.cpp b/xbmc/filesystem/SmartPlaylistDirectory.cpp index 2b779da4b8..553772472d 100644 --- a/xbmc/filesystem/SmartPlaylistDirectory.cpp +++ b/xbmc/filesystem/SmartPlaylistDirectory.cpp @@ -24,6 +24,7 @@ #include "FileItem.h" #include "filesystem/Directory.h" #include "filesystem/File.h" +#include "filesystem/FileDirectoryFactory.h" #include "music/MusicDatabase.h" #include "playlists/SmartPlayList.h" #include "settings/Settings.h" @@ -64,19 +65,33 @@ namespace XFILE bool CSmartPlaylistDirectory::GetDirectory(const CSmartPlaylist &playlist, CFileItemList& items, const CStdString &strBaseDir /* = "" */, bool filter /* = false */) { bool success = false, success2 = false; - std::set<CStdString> playlists; + std::vector<CStdString> virtualFolders; SortDescription sorting; sorting.limitEnd = playlist.GetLimit(); sorting.sortBy = playlist.GetOrder(); sorting.sortOrder = playlist.GetOrderAscending() ? SortOrderAscending : SortOrderDescending; + sorting.sortAttributes = playlist.GetOrderAttributes(); if (CSettings::Get().GetBool("filelists.ignorethewhensorting")) - sorting.sortAttributes = SortAttributeIgnoreArticle; + sorting.sortAttributes = (SortAttribute)(sorting.sortAttributes | SortAttributeIgnoreArticle); + items.SetSortIgnoreFolders((sorting.sortAttributes & SortAttributeIgnoreFolders) == SortAttributeIgnoreFolders); std::string option = !filter ? "xsp" : "filter"; const CStdString& group = playlist.GetGroup(); bool isGrouped = !group.empty() && !StringUtils::EqualsNoCase(group, "none") && !playlist.IsGroupMixed(); + // get all virtual folders and add them to the item list + playlist.GetVirtualFolders(virtualFolders); + for (std::vector<CStdString>::const_iterator virtualFolder = virtualFolders.begin(); virtualFolder != virtualFolders.end(); virtualFolder++) + { + CFileItemPtr pItem = CFileItemPtr(new CFileItem(*virtualFolder, true)); + if (CFileDirectoryFactory::Create(*virtualFolder, pItem.get()) != NULL) + { + pItem->SetSpecialSort(SortSpecialOnTop); + items.Add(pItem); + } + } + if (playlist.GetType().Equals("movies") || playlist.GetType().Equals("tvshows") || playlist.GetType().Equals("episodes")) @@ -145,9 +160,7 @@ namespace XFILE items.SetProperty(PROPERTY_PATH_DB, videoUrl.ToString()); } } - else if (playlist.GetType().Equals("artists") || - playlist.GetType().Equals("albums") || - playlist.GetType().Equals("songs") || playlist.GetType().Equals("mixed") || playlist.GetType().IsEmpty()) + else if (playlist.IsMusicType() || playlist.GetType().IsEmpty()) { CMusicDatabase db; if (db.Open()) @@ -314,7 +327,7 @@ namespace XFILE { CFileItemList list; bool filesExist = false; - if (playlistType == "songs" || playlistType == "albums" || playlistType == "artists") + if (CSmartPlaylist::IsMusicType(playlistType)) filesExist = CDirectory::GetDirectory("special://musicplaylists/", list, ".xsp", false); else // all others are video filesExist = CDirectory::GetDirectory("special://videoplaylists/", list, ".xsp", false); diff --git a/xbmc/playlists/SmartPlayList.cpp b/xbmc/playlists/SmartPlayList.cpp index f728b7840b..2815b036d0 100644 --- a/xbmc/playlists/SmartPlayList.cpp +++ b/xbmc/playlists/SmartPlayList.cpp @@ -101,7 +101,8 @@ static const translateField fields[] = { { "audiolanguage", FieldAudioLanguage, SortByAudioLanguage, CSmartPlaylistRule::TEXTIN_FIELD, false, 21447 }, { "subtitlelanguage", FieldSubtitleLanguage, SortBySubtitleLanguage, CSmartPlaylistRule::TEXTIN_FIELD, false, 21448 }, { "random", FieldRandom, SortByRandom, CSmartPlaylistRule::TEXT_FIELD, false, 590 }, - { "playlist", FieldPlaylist, SortByPlaylistOrder, CSmartPlaylistRule::PLAYLIST_FIELD, false, 559 }, + { "playlist", FieldPlaylist, SortByPlaylistOrder, CSmartPlaylistRule::PLAYLIST_FIELD, true, 559 }, + { "virtualfolder", FieldVirtualFolder, SortByNone, CSmartPlaylistRule::PLAYLIST_FIELD, true, 614 }, { "tag", FieldTag, SortByNone, CSmartPlaylistRule::TEXT_FIELD, true, 20459 }, { "instruments", FieldInstruments, SortByNone, CSmartPlaylistRule::TEXT_FIELD, false, 21892 }, { "biography", FieldBiography, SortByNone, CSmartPlaylistRule::TEXT_FIELD, false, 21887 }, @@ -552,6 +553,7 @@ vector<Field> CSmartPlaylistRule::GetFields(const CStdString &type) fields.push_back(FieldVideoAspectRatio); } fields.push_back(FieldPlaylist); + fields.push_back(FieldVirtualFolder); return fields; } @@ -1131,6 +1133,11 @@ CStdString CSmartPlaylistRuleCombination::GetWhereClause(const CDatabase &db, co // translate the rules into SQL for (CSmartPlaylistRules::const_iterator it = m_rules.begin(); it != m_rules.end(); ++it) { + // don't include playlists that are meant to be displayed + // as a virtual folders in the SQL WHERE clause + if (it->m_field == FieldVirtualFolder) + continue; + if (!rule.empty()) rule += m_type == CombinationAnd ? " AND " : " OR "; rule += "("; @@ -1142,20 +1149,22 @@ CStdString CSmartPlaylistRuleCombination::GetWhereClause(const CDatabase &db, co { referencedPlaylists.insert(playlistFile); CSmartPlaylist playlist; - playlist.Load(playlistFile); - CStdString playlistQuery; - // only playlists of same type will be part of the query - if (playlist.GetType().Equals(strType) || (playlist.GetType().Equals("mixed") && (strType == "songs" || strType == "musicvideos")) || playlist.GetType().IsEmpty()) - { - playlist.SetType(strType); - playlistQuery = playlist.GetWhereClause(db, referencedPlaylists); - } - if (playlist.GetType().Equals(strType)) + if (playlist.Load(playlistFile)) { - if (it->m_operator == CSmartPlaylistRule::OPERATOR_DOES_NOT_EQUAL) - currentRule.Format("NOT (%s)", playlistQuery.c_str()); - else - currentRule = playlistQuery; + CStdString playlistQuery; + // only playlists of same type will be part of the query + if (playlist.GetType().Equals(strType) || (playlist.GetType().Equals("mixed") && (strType == "songs" || strType == "musicvideos")) || playlist.GetType().IsEmpty()) + { + playlist.SetType(strType); + playlistQuery = playlist.GetWhereClause(db, referencedPlaylists); + } + if (playlist.GetType().Equals(strType)) + { + if (it->m_operator == CSmartPlaylistRule::OPERATOR_DOES_NOT_EQUAL) + currentRule.Format("NOT (%s)", playlistQuery.c_str()); + else + currentRule = playlistQuery; + } } } } @@ -1171,6 +1180,35 @@ CStdString CSmartPlaylistRuleCombination::GetWhereClause(const CDatabase &db, co return rule; } +void CSmartPlaylistRuleCombination::GetVirtualFolders(const CStdString& strType, std::vector<CStdString> &virtualFolders) const +{ + for (vector<CSmartPlaylistRuleCombination>::const_iterator it = m_combinations.begin(); it != m_combinations.end(); ++it) + it->GetVirtualFolders(strType, virtualFolders); + + for (CSmartPlaylistRules::const_iterator it = m_rules.begin(); it != m_rules.end(); ++it) + { + if ((it->m_field != FieldVirtualFolder && it->m_field != FieldPlaylist) || it->m_operator != CSmartPlaylistRule::OPERATOR_EQUALS) + continue; + + CStdString playlistFile = CSmartPlaylistDirectory::GetPlaylistByName(it->m_parameter.at(0), strType); + if (playlistFile.empty()) + continue; + + if (it->m_field == FieldVirtualFolder) + virtualFolders.push_back(playlistFile); + else + { + // look for any virtual folders in the expanded playlists + CSmartPlaylist playlist; + if (!playlist.Load(playlistFile)) + continue; + + if (CSmartPlaylist::CheckTypeCompatibility(playlist.GetType(), strType)) + playlist.GetVirtualFolders(virtualFolders); + } + } +} + bool CSmartPlaylistRuleCombination::Load(const CVariant &obj) { if (!obj.isObject() && !obj.isArray()) @@ -1402,8 +1440,12 @@ bool CSmartPlaylist::Load(const CVariant &obj) // and order if (obj.isMember("order") && obj["order"].isMember("method") && obj["order"]["method"].isString()) { - if (obj["order"].isMember("direction") && obj["order"]["direction"].isString()) - m_orderDirection = StringUtils::EqualsNoCase(obj["order"]["direction"].asString(), "ascending") ? SortOrderAscending : SortOrderDescending; + const CVariant &order = obj["order"]; + if (order.isMember("direction") && order["direction"].isString()) + m_orderDirection = StringUtils::EqualsNoCase(order["direction"].asString(), "ascending") ? SortOrderAscending : SortOrderDescending; + + if (order.isMember("ignorefolders") && obj["ignorefolders"].isBoolean()) + m_orderAttributes = obj["ignorefolders"].asBoolean() ? SortAttributeIgnoreFolders : SortAttributeNone; m_orderField = CSmartPlaylistRule::TranslateOrder(obj["order"]["method"].asString().c_str()); } @@ -1456,6 +1498,11 @@ bool CSmartPlaylist::LoadFromXML(const TiXmlNode *root, const CStdString &encodi const char *direction = order->Attribute("direction"); if (direction) m_orderDirection = StringUtils::EqualsNoCase(direction, "ascending") ? SortOrderAscending : SortOrderDescending; + + const char *ignorefolders = order->Attribute("ignorefolders"); + if (ignorefolders != NULL) + m_orderAttributes = StringUtils::EqualsNoCase(ignorefolders, "true") ? SortAttributeIgnoreFolders : SortAttributeNone; + m_orderField = CSmartPlaylistRule::TranslateOrder(order->FirstChild()->Value()); } return true; @@ -1513,6 +1560,8 @@ bool CSmartPlaylist::Save(const CStdString &path) const TiXmlText order(CSmartPlaylistRule::TranslateOrder(m_orderField).c_str()); TiXmlElement nodeOrder("order"); nodeOrder.SetAttribute("direction", m_orderDirection == SortOrderDescending ? "descending" : "ascending"); + if (m_orderAttributes & SortAttributeIgnoreFolders) + nodeOrder.SetAttribute("ignorefolders", "true"); nodeOrder.InsertEndChild(order); pRoot->InsertEndChild(nodeOrder); } @@ -1550,6 +1599,7 @@ bool CSmartPlaylist::Save(CVariant &obj, bool full /* = true */) const obj["order"] = CVariant(CVariant::VariantTypeObject); obj["order"]["method"] = CSmartPlaylistRule::TranslateOrder(m_orderField); obj["order"]["direction"] = m_orderDirection == SortOrderDescending ? "descending" : "ascending"; + obj["order"]["ignorefolders"] = (m_orderAttributes & SortAttributeIgnoreFolders); } return true; @@ -1573,6 +1623,7 @@ void CSmartPlaylist::Reset() m_limit = 0; m_orderField = SortByNone; m_orderDirection = SortOrderNone; + m_orderAttributes = SortAttributeNone; m_playlistType = "songs"; // sane default m_group.clear(); m_groupMixed = false; @@ -1588,17 +1639,44 @@ void CSmartPlaylist::SetType(const CStdString &type) m_playlistType = type; } +bool CSmartPlaylist::IsVideoType() const +{ + return IsVideoType(m_playlistType); +} + +bool CSmartPlaylist::IsMusicType() const +{ + return IsMusicType(m_playlistType); +} + +bool CSmartPlaylist::IsVideoType(const CStdString &type) +{ + return type == "movies" || type == "tvshows" || type == "episodes" || + type == "musicvideos" || type == "mixed"; +} + +bool CSmartPlaylist::IsMusicType(const CStdString &type) +{ + return type == "artists" || type == "albums" || + type == "songs" || type == "mixed"; +} + CStdString CSmartPlaylist::GetWhereClause(const CDatabase &db, set<CStdString> &referencedPlaylists) const { return m_ruleCombination.GetWhereClause(db, GetType(), referencedPlaylists); } +void CSmartPlaylist::GetVirtualFolders(std::vector<CStdString> &virtualFolders) const +{ + m_ruleCombination.GetVirtualFolders(GetType(), virtualFolders); +} + CStdString CSmartPlaylist::GetSaveLocation() const { - if (m_playlistType == "songs" || m_playlistType == "albums" || m_playlistType == "artists") - return "music"; - else if (m_playlistType == "mixed") + if (m_playlistType == "mixed") return "mixed"; + if (IsMusicType()) + return "music"; // all others are video return "video"; } @@ -1630,3 +1708,19 @@ bool CSmartPlaylist::IsEmpty(bool ignoreSortAndLimit /* = true */) const return empty; } + +bool CSmartPlaylist::CheckTypeCompatibility(const CStdString &typeLeft, const CStdString &typeRight) +{ + if (typeLeft.Equals(typeRight)) + return true; + + if (typeLeft.Equals("mixed") && + (typeRight.Equals("songs") || typeRight.Equals("musicvideos"))) + return true; + + if (typeRight.Equals("mixed") && + (typeLeft.Equals("songs") || typeLeft.Equals("musicvideos"))) + return true; + + return false; +} diff --git a/xbmc/playlists/SmartPlayList.h b/xbmc/playlists/SmartPlayList.h index 0e2da8c357..1dc337417b 100644 --- a/xbmc/playlists/SmartPlayList.h +++ b/xbmc/playlists/SmartPlayList.h @@ -79,7 +79,12 @@ public: virtual bool Save(TiXmlNode *parent) const; virtual bool Save(CVariant &obj) const; + CStdString GetParameter() const; + void SetParameter(const CStdString &value); + void SetParameter(const std::vector<CStdString> &values); + CStdString GetLocalizedRule() const; CStdString GetWhereClause(const CDatabase &db, const CStdString& strType) const; + static Field TranslateField(const char *field); static CStdString TranslateField(Field field); static SortBy TranslateOrder(const char *order); @@ -100,10 +105,6 @@ public: static FIELD_TYPE GetFieldType(Field field); static bool IsFieldBrowseable(Field field); - CStdString GetLocalizedRule() const; - CStdString GetParameter() const; - void SetParameter(const CStdString &value); - void SetParameter(const std::vector<CStdString> &values); Field m_field; SEARCH_OPERATOR m_operator; @@ -136,6 +137,7 @@ public: virtual bool Save(CVariant &obj) const; CStdString GetWhereClause(const CDatabase &db, const CStdString& strType, std::set<CStdString> &referencedPlaylists) const; + void GetVirtualFolders(const CStdString& strType, std::vector<CStdString> &virtualFolders) const; std::string TranslateCombinationType() const; Combination GetType() const { return m_type; } @@ -177,6 +179,8 @@ public: void SetType(const CStdString &type); // music, video, mixed const CStdString& GetName() const { return m_playlistName; }; const CStdString& GetType() const { return m_playlistType; }; + bool IsVideoType() const; + bool IsMusicType() const; void SetMatchAllRules(bool matchAll) { m_ruleCombination.SetType(matchAll ? CSmartPlaylistRuleCombination::CombinationAnd : CSmartPlaylistRuleCombination::CombinationOr); } bool GetMatchAllRules() const { return m_ruleCombination.GetType() == CSmartPlaylistRuleCombination::CombinationAnd; } @@ -186,10 +190,11 @@ public: void SetOrder(SortBy order) { m_orderField = order; }; SortBy GetOrder() const { return m_orderField; }; - void SetOrderAscending(bool orderAscending) { m_orderDirection = orderAscending ? SortOrderAscending : SortOrderDescending; }; bool GetOrderAscending() const { return m_orderDirection != SortOrderDescending; }; SortOrder GetOrderDirection() const { return m_orderDirection; } + void SetOrderAttributes(SortAttribute attributes) { m_orderAttributes = attributes; } + SortAttribute GetOrderAttributes() const { return m_orderAttributes; } void SetGroup(const CStdString &group) { m_group = group; } const CStdString& GetGroup() const { return m_group; } @@ -205,12 +210,17 @@ public: \param needWhere whether we need to prepend the where clause with "WHERE " */ CStdString GetWhereClause(const CDatabase &db, std::set<CStdString> &referencedPlaylists) const; + void GetVirtualFolders(std::vector<CStdString> &virtualFolders) const; CStdString GetSaveLocation() const; static void GetAvailableFields(const std::string &type, std::vector<std::string> &fieldList); static void GetAvailableOperators(std::vector<std::string> &operatorList); + static bool IsVideoType(const CStdString &type); + static bool IsMusicType(const CStdString &type); + static bool CheckTypeCompatibility(const CStdString &typeLeft, const CStdString &typeRight); + bool IsEmpty(bool ignoreSortAndLimit = true) const; private: friend class CGUIDialogSmartPlaylistEditor; @@ -229,6 +239,7 @@ private: unsigned int m_limit; SortBy m_orderField; SortOrder m_orderDirection; + SortAttribute m_orderAttributes; CStdString m_group; bool m_groupMixed; diff --git a/xbmc/utils/DatabaseUtils.h b/xbmc/utils/DatabaseUtils.h index 67e6e761dc..ed7ee757cb 100644 --- a/xbmc/utils/DatabaseUtils.h +++ b/xbmc/utils/DatabaseUtils.h @@ -54,6 +54,7 @@ typedef enum { FieldBitrate, FieldListeners, FieldPlaylist, + FieldVirtualFolder, FieldRandom, FieldDateTaken, |