From 9670b3f4cefb231b6a8c277599be4dfd705ca55a Mon Sep 17 00:00:00 2001 From: montellese Date: Wed, 11 Jul 2012 18:00:41 +0200 Subject: [PATCH] smartplaylists: add Save() and Load() based on CVariant and SaveAsJson() and LoadFromJson() --- xbmc/playlists/SmartPlayList.cpp | 201 +++++++++++++++++++++++++++++-- xbmc/playlists/SmartPlayList.h | 18 ++- 2 files changed, 205 insertions(+), 14 deletions(-) diff --git a/xbmc/playlists/SmartPlayList.cpp b/xbmc/playlists/SmartPlayList.cpp index d063f2089c1d0..76d34eaee8f17 100644 --- a/xbmc/playlists/SmartPlayList.cpp +++ b/xbmc/playlists/SmartPlayList.cpp @@ -26,7 +26,10 @@ #include "filesystem/File.h" #include "utils/CharsetConverter.h" #include "utils/DatabaseUtils.h" +#include "utils/JSONVariantParser.h" +#include "utils/JSONVariantWriter.h" #include "utils/URIUtils.h" +#include "utils/Variant.h" #include "utils/XMLUtils.h" #include "video/VideoDatabase.h" #include "Util.h" @@ -191,6 +194,33 @@ bool CSmartPlaylistRule::Load(TiXmlElement *element, const CStdString &encoding return true; } +bool CSmartPlaylistRule::Load(const CVariant &obj) +{ + if (!obj.isObject() || + !obj.isMember("field") || !obj["field"].isString() || + !obj.isMember("operator") || !obj["operator"].isString() || + !obj.isMember("value") || (!obj["value"].isString() && !obj["value"].isArray())) + return false; + + const CVariant &value = obj["value"]; + if (value.isString() && !value.asString().empty()) + m_parameter.push_back(value.asString()); + else if (value.isArray()) + { + for (CVariant::const_iterator_array val = value.begin_array(); val != value.end_array(); val++) + { + if (val->isString() && !val->asString().empty()) + m_parameter.push_back(val->asString()); + } + } + else + return false; + + m_field = TranslateField(obj["field"].asString().c_str()); + m_operator = TranslateOperator(obj["operator"].asString().c_str()); + return true; +} + bool CSmartPlaylistRule::Save(TiXmlNode *parent) const { if (parent == NULL || m_parameter.empty()) @@ -213,6 +243,23 @@ bool CSmartPlaylistRule::Save(TiXmlNode *parent) const return true; } +bool CSmartPlaylistRule::Save(CVariant &obj) const +{ + if (obj.isNull() || m_parameter.empty()) + return false; + + CVariant rule(CVariant::VariantTypeObject); + rule["field"] = TranslateField(m_field); + rule["operator"] = TranslateOperator(m_operator); + + rule["value"] = CVariant(CVariant::VariantTypeArray); + for (vector::const_iterator it = m_parameter.begin(); it != m_parameter.end(); it++) + rule["value"].push_back(*it); + + obj.push_back(rule); + return true; +} + Field CSmartPlaylistRule::TranslateField(const char *field) { for (unsigned int i = 0; i < NUM_FIELDS; i++) @@ -575,7 +622,7 @@ CStdString CSmartPlaylistRule::GetVideoResolutionQuery(const CStdString ¶met return retVal; } -CStdString CSmartPlaylistRule::GetWhereClause(CDatabase &db, const CStdString& strType) const +CStdString CSmartPlaylistRule::GetWhereClause(const CDatabase &db, const CStdString& strType) const { SEARCH_OPERATOR op = m_operator; if ((strType == "tvshows" || strType == "episodes") && m_field == FieldYear) @@ -842,6 +889,18 @@ TiXmlElement *CSmartPlaylist::OpenAndReadName(const CStdString &path) m_xmlDoc.Clear(); file >> m_xmlDoc; + TiXmlElement *root = readName(); + if (m_playlistName.empty()) + { + m_playlistName = CUtil::GetTitleFromPath(path); + if (URIUtils::GetExtension(m_playlistName) == ".xsp") + URIUtils::RemoveExtension(m_playlistName); + } + return root; +} + +TiXmlElement* CSmartPlaylist::readName() +{ if (m_xmlDoc.Error()) { CLog::Log(LOGERROR, "Error loading Smart playlist (failed to parse xml: %s)", m_xmlDoc.ErrorDesc()); @@ -851,7 +910,7 @@ TiXmlElement *CSmartPlaylist::OpenAndReadName(const CStdString &path) TiXmlElement *root = m_xmlDoc.RootElement(); if (!root || strcmpi(root->Value(),"smartplaylist") != 0) { - CLog::Log(LOGERROR, "Error loading Smart playlist %s", path.c_str()); + CLog::Log(LOGERROR, "Error loading Smart playlist"); return NULL; } // load the playlist type @@ -868,18 +927,86 @@ TiXmlElement *CSmartPlaylist::OpenAndReadName(const CStdString &path) TiXmlHandle name = ((TiXmlHandle)root->FirstChild("name")).FirstChild(); if (name.Node()) m_playlistName = name.Node()->Value(); - else + + return root; +} + +TiXmlElement *CSmartPlaylist::readNameFromXml(const CStdString &xml) +{ + if (xml.empty()) { - m_playlistName = CUtil::GetTitleFromPath(path); - if (URIUtils::GetExtension(m_playlistName) == ".xsp") - URIUtils::RemoveExtension(m_playlistName); + CLog::Log(LOGERROR, "Error loading empty Smart playlist"); + return NULL; } - return root; + + m_xmlDoc.Clear(); + if (!m_xmlDoc.Parse(xml)) + { + CLog::Log(LOGERROR, "Error loading Smart playlist (failed to parse xml: %s)", m_xmlDoc.ErrorDesc()); + return NULL; + } + + return readName(); } bool CSmartPlaylist::Load(const CStdString &path) { - TiXmlElement *root = OpenAndReadName(path); + return load(OpenAndReadName(path)); +} + +bool CSmartPlaylist::Load(const CVariant &obj) +{ + if (!obj.isObject() || + !obj.isMember("match") || !obj["match"].isString() || + !obj.isMember("rules") || !obj["rules"].isArray()) + return false; + + // load the playlist type + if (obj.isMember("type") && obj["type"].isString()) + m_playlistType = obj["type"].asString(); + // backward compatibility: + if (m_playlistType == "music") + m_playlistType = "songs"; + if (m_playlistType == "video") + m_playlistType = "musicvideos"; + + // load the playlist name + if (obj.isMember("name") && obj["name"].isString()) + m_playlistName = obj["name"].asString(); + + m_matchAllRules = strcmpi(obj["match"].asString().c_str(), "all") == 0; + + // now the rules + for (CVariant::const_iterator_array it = obj["rules"].begin_array(); it != obj["rules"].end_array(); it++) + { + CSmartPlaylistRule rule; + if (rule.Load(*it)) + m_playlistRules.push_back(rule); + } + + // now any limits + if (obj.isMember("limit") && (obj["limit"].isInteger() || obj["limit"].isUnsignedInteger()) && obj["limit"].asUnsignedInteger() > 0) + m_limit = (unsigned int)obj["limit"].asUnsignedInteger(); + + // and order + if (obj.isMember("order") && obj["order"].isMember("method") && obj["order"]["method"].isString()) + { + if (obj["order"].isMember("direction") && obj["order"]["direction"].isString()) + m_orderAscending = strcmpi(obj["order"]["direction"].asString().c_str(), "ascending") == 0; + + m_orderField = CSmartPlaylistRule::TranslateOrder(obj["order"]["method"].asString().c_str()); + } + + return true; +} + +bool CSmartPlaylist::LoadFromXml(const CStdString &xml) +{ + return load(readNameFromXml(xml)); +} + +bool CSmartPlaylist::load(TiXmlElement *root) +{ if (!root) return false; @@ -930,7 +1057,16 @@ bool CSmartPlaylist::LoadFromXML(TiXmlElement *root, const CStdString &encoding) return true; } -bool CSmartPlaylist::Save(const CStdString &path) +bool CSmartPlaylist::LoadFromJson(const CStdString &json) +{ + if (json.empty()) + return false; + + CVariant obj = CJSONVariantParser::Parse((const unsigned char *)json.c_str(), json.size()); + return Load(obj); +} + +bool CSmartPlaylist::Save(const CStdString &path) const { CXBMCTinyXML doc; TiXmlDeclaration decl("1.0", "UTF-8", "yes"); @@ -955,7 +1091,7 @@ bool CSmartPlaylist::Save(const CStdString &path) pRoot->InsertEndChild(nodeMatch); // add tags - for (vector::iterator it = m_playlistRules.begin(); it != m_playlistRules.end(); ++it) + for (vector::const_iterator it = m_playlistRules.begin(); it != m_playlistRules.end(); ++it) it->Save(pRoot); // add tag @@ -981,6 +1117,49 @@ bool CSmartPlaylist::Save(const CStdString &path) return doc.SaveFile(path); } +bool CSmartPlaylist::Save(CVariant &obj, bool full /* = true */) const +{ + if (obj.type() == CVariant::VariantTypeConstNull) + return false; + + obj.clear(); + // add "type", "name" and "match" + obj["type"] = m_playlistType; + obj["match"] = m_matchAllRules ? "all" : "one"; + + if (full) + obj["name"] = m_playlistName; + + // add "rules" + obj["rules"] = CVariant(CVariant::VariantTypeArray); + for (vector::const_iterator it = m_playlistRules.begin(); it != m_playlistRules.end(); ++it) + it->Save(obj["rules"]); + + // add "limit" + if (full && m_limit) + obj["limit"] = m_limit; + + // add "order" + if (full && m_orderField != SortByNone) + { + obj["order"] = CVariant(CVariant::VariantTypeObject); + obj["order"]["method"] = CSmartPlaylistRule::TranslateOrder(m_orderField); + obj["order"]["direction"] = m_orderAscending ? "ascending" : "descending"; + } + + return true; +} + +bool CSmartPlaylist::SaveAsJson(CStdString &json, bool full /* = true */) const +{ + CVariant xsp(CVariant::VariantTypeObject); + if (!Save(xsp, full)) + return false; + + json = CJSONVariantWriter::Write(xsp, true); + return json.size() > 0; +} + void CSmartPlaylist::SetName(const CStdString &name) { m_playlistName = name; @@ -996,7 +1175,7 @@ void CSmartPlaylist::AddRule(const CSmartPlaylistRule &rule) m_playlistRules.push_back(rule); } -CStdString CSmartPlaylist::GetWhereClause(CDatabase &db, set &referencedPlaylists) const +CStdString CSmartPlaylist::GetWhereClause(const CDatabase &db, set &referencedPlaylists) const { CStdString rule, currentRule; for (vector::const_iterator it = m_playlistRules.begin(); it != m_playlistRules.end(); ++it) diff --git a/xbmc/playlists/SmartPlayList.h b/xbmc/playlists/SmartPlayList.h index 95a09b2abc979..2f46ccac0f84d 100644 --- a/xbmc/playlists/SmartPlayList.h +++ b/xbmc/playlists/SmartPlayList.h @@ -28,6 +28,7 @@ #include "utils/XBMCTinyXML.h" class CDatabase; +class CVariant; class CSmartPlaylistRule { @@ -63,9 +64,11 @@ class CSmartPlaylistRule }; bool Load(TiXmlElement *element, const CStdString &encoding = "UTF-8"); + bool Load(const CVariant &obj); bool Save(TiXmlNode *parent) const; + bool Save(CVariant &obj) const; - CStdString GetWhereClause(CDatabase &db, const CStdString& strType) 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); @@ -98,7 +101,12 @@ class CSmartPlaylist CSmartPlaylist(); bool Load(const CStdString &path); - bool Save(const CStdString &path); + bool Load(const CVariant &obj); + bool LoadFromXml(const CStdString &xml); + bool LoadFromJson(const CStdString &json); + bool Save(const CStdString &path) const; + bool Save(CVariant &obj, bool full = true) const; + bool SaveAsJson(CStdString &json, bool full = true) const; TiXmlElement *OpenAndReadName(const CStdString &path); bool LoadFromXML(TiXmlElement *root, const CStdString &encoding = "UTF-8"); @@ -130,7 +138,7 @@ class CSmartPlaylist \param referencedPlaylists a set of playlists to know when we reach a cycle \param needWhere whether we need to prepend the where clause with "WHERE " */ - CStdString GetWhereClause(CDatabase &db, std::set &referencedPlaylists) const; + CStdString GetWhereClause(const CDatabase &db, std::set &referencedPlaylists) const; const std::vector &GetRules() const; @@ -138,6 +146,10 @@ class CSmartPlaylist private: friend class CGUIDialogSmartPlaylistEditor; + TiXmlElement* readName(); + TiXmlElement *readNameFromXml(const CStdString &xml); + bool load(TiXmlElement *root); + std::vector m_playlistRules; CStdString m_playlistName; CStdString m_playlistType;