Skip to content

Commit

Permalink
smartplaylists: add Save() and Load() based on CVariant and SaveAsJso…
Browse files Browse the repository at this point in the history
…n() and LoadFromJson()
  • Loading branch information
Montellese committed Aug 5, 2012
1 parent 52934da commit 9670b3f
Show file tree
Hide file tree
Showing 2 changed files with 205 additions and 14 deletions.
201 changes: 190 additions & 11 deletions xbmc/playlists/SmartPlayList.cpp
Expand Up @@ -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"
Expand Down Expand Up @@ -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())
Expand All @@ -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<CStdString>::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++)
Expand Down Expand Up @@ -575,7 +622,7 @@ CStdString CSmartPlaylistRule::GetVideoResolutionQuery(const CStdString &paramet
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)
Expand Down Expand Up @@ -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());
Expand All @@ -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
Expand All @@ -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;

Expand Down Expand Up @@ -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");
Expand All @@ -955,7 +1091,7 @@ bool CSmartPlaylist::Save(const CStdString &path)
pRoot->InsertEndChild(nodeMatch);

// add <rule> tags
for (vector<CSmartPlaylistRule>::iterator it = m_playlistRules.begin(); it != m_playlistRules.end(); ++it)
for (vector<CSmartPlaylistRule>::const_iterator it = m_playlistRules.begin(); it != m_playlistRules.end(); ++it)
it->Save(pRoot);

// add <limit> tag
Expand All @@ -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<CSmartPlaylistRule>::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;
Expand All @@ -996,7 +1175,7 @@ void CSmartPlaylist::AddRule(const CSmartPlaylistRule &rule)
m_playlistRules.push_back(rule);
}

CStdString CSmartPlaylist::GetWhereClause(CDatabase &db, set<CStdString> &referencedPlaylists) const
CStdString CSmartPlaylist::GetWhereClause(const CDatabase &db, set<CStdString> &referencedPlaylists) const
{
CStdString rule, currentRule;
for (vector<CSmartPlaylistRule>::const_iterator it = m_playlistRules.begin(); it != m_playlistRules.end(); ++it)
Expand Down
18 changes: 15 additions & 3 deletions xbmc/playlists/SmartPlayList.h
Expand Up @@ -28,6 +28,7 @@
#include "utils/XBMCTinyXML.h"

class CDatabase;
class CVariant;

class CSmartPlaylistRule
{
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -130,14 +138,18 @@ 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<CStdString> &referencedPlaylists) const;
CStdString GetWhereClause(const CDatabase &db, std::set<CStdString> &referencedPlaylists) const;

const std::vector<CSmartPlaylistRule> &GetRules() const;

CStdString GetSaveLocation() const;
private:
friend class CGUIDialogSmartPlaylistEditor;

TiXmlElement* readName();
TiXmlElement *readNameFromXml(const CStdString &xml);
bool load(TiXmlElement *root);

std::vector<CSmartPlaylistRule> m_playlistRules;
CStdString m_playlistName;
CStdString m_playlistType;
Expand Down

0 comments on commit 9670b3f

Please sign in to comment.