summaryrefslogtreecommitdiffstats
path: root/lib/libUPnP/Platinum/Source/Devices/MediaServer/PltDidl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libUPnP/Platinum/Source/Devices/MediaServer/PltDidl.cpp')
-rw-r--r--lib/libUPnP/Platinum/Source/Devices/MediaServer/PltDidl.cpp406
1 files changed, 406 insertions, 0 deletions
diff --git a/lib/libUPnP/Platinum/Source/Devices/MediaServer/PltDidl.cpp b/lib/libUPnP/Platinum/Source/Devices/MediaServer/PltDidl.cpp
new file mode 100644
index 0000000..37d36dd
--- /dev/null
+++ b/lib/libUPnP/Platinum/Source/Devices/MediaServer/PltDidl.cpp
@@ -0,0 +1,406 @@
+/*****************************************************************
+|
+| Platinum - DIDL
+|
+| Copyright (c) 2004-2010, Plutinosoft, LLC.
+| All rights reserved.
+| http://www.plutinosoft.com
+|
+| This program is free software; you can redistribute it and/or
+| modify it under the terms of the GNU General Public License
+| as published by the Free Software Foundation; either version 2
+| of the License, or (at your option) any later version.
+|
+| OEMs, ISVs, VARs and other distributors that combine and
+| distribute commercially licensed software with Platinum software
+| and do not wish to distribute the source code for the commercially
+| licensed software under version 2, or (at your option) any later
+| version, of the GNU General Public License (the "GPL") must enter
+| into a commercial license agreement with Plutinosoft, LLC.
+| licensing@plutinosoft.com
+|
+| This program is distributed in the hope that it will be useful,
+| but WITHOUT ANY WARRANTY; without even the implied warranty of
+| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+| GNU General Public License for more details.
+|
+| You should have received a copy of the GNU General Public License
+| along with this program; see the file LICENSE.txt. If not, write to
+| the Free Software Foundation, Inc.,
+| 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+| http://www.gnu.org/licenses/gpl-2.0.html
+|
+****************************************************************/
+
+/*----------------------------------------------------------------------
+| includes
++---------------------------------------------------------------------*/
+#include "PltDidl.h"
+#include "PltUtilities.h"
+#include "PltService.h"
+
+NPT_SET_LOCAL_LOGGER("platinum.media.server.didl")
+
+/*----------------------------------------------------------------------
+| globals
++---------------------------------------------------------------------*/
+const char* didl_header = "<DIDL-Lite xmlns=\"urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/\""
+ " xmlns:dc=\"http://purl.org/dc/elements/1.1/\""
+ " xmlns:upnp=\"urn:schemas-upnp-org:metadata-1-0/upnp/\""
+ " xmlns:dlna=\"urn:schemas-dlna-org:metadata-1-0/\""
+ " xmlns:sec=\"http://www.sec.co.kr/\""
+ " xmlns:xbmc=\"urn:schemas-xbmc-org:metadata-1-0/\">";
+const char* didl_footer = "</DIDL-Lite>";
+const char* didl_namespace_dc = "http://purl.org/dc/elements/1.1/";
+const char* didl_namespace_upnp = "urn:schemas-upnp-org:metadata-1-0/upnp/";
+const char* didl_namespace_dlna = "urn:schemas-dlna-org:metadata-1-0/";
+const char* didl_namespace_xbmc = "urn:schemas-xbmc-org:metadata-1-0/";
+
+/*----------------------------------------------------------------------
+| PLT_Didl::ConvertFilterToMask
++---------------------------------------------------------------------*/
+NPT_UInt64
+PLT_Didl::ConvertFilterToMask(const NPT_String& filter)
+{
+ // easy out
+ if (filter.GetLength() == 0) return PLT_FILTER_MASK_ALL;
+
+ // a filter string is a comma delimited set of fields identifying
+ // a given DIDL property (or set of properties).
+ // These fields are or start with: upnp:, @, res@, res, dc:, container@
+
+ NPT_UInt64 mask = 0;
+ const char* s = filter;
+ int i = 0;
+
+ while (s[i] != '\0') {
+ int next_comma = filter.Find(',', i);
+ int len = ((next_comma < 0)?(int)filter.GetLength():next_comma)-i;
+
+ if (NPT_String::CompareN(s+i, "*", 1) == 0) {
+ // return now, there's no point in parsing the rest
+ return PLT_FILTER_MASK_ALL;
+ }
+
+ // title is required, so we return a non empty mask
+ mask |= PLT_FILTER_MASK_TITLE;
+
+ if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_TITLE, len, true) == 0) {
+ mask |= PLT_FILTER_MASK_TITLE;
+ } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_REFID, len, true) == 0) {
+ mask |= PLT_FILTER_MASK_REFID;
+ } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_CREATOR, len, true) == 0) {
+ mask |= PLT_FILTER_MASK_CREATOR;
+ } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_ARTIST, len, true) == 0) {
+ mask |= PLT_FILTER_MASK_ARTIST;
+ } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_ACTOR, len, true) == 0) {
+ mask |= PLT_FILTER_MASK_ACTOR;
+ } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_DIRECTOR, len, true) == 0) {
+ mask |= PLT_FILTER_MASK_DIRECTOR;
+ } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_AUTHOR, len, true) == 0) {
+ mask |= PLT_FILTER_MASK_AUTHOR;
+ } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_DATE, len, true) == 0) {
+ mask |= PLT_FILTER_MASK_DATE;
+ } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_ALBUM, len, true) == 0) {
+ mask |= PLT_FILTER_MASK_ALBUM;
+ } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_GENRE, len, true) == 0) {
+ mask |= PLT_FILTER_MASK_GENRE;
+ } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_ALBUMARTURI, len, true) == 0 ||
+ NPT_String::CompareN(s+i, PLT_FILTER_FIELD_ALBUMARTURI_DLNAPROFILEID, len, true) == 0) {
+ mask |= PLT_FILTER_MASK_ALBUMARTURI;
+ } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_DESCRIPTION, len, true) == 0) {
+ mask |= PLT_FILTER_MASK_DESCRIPTION;
+ } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_LONGDESCRIPTION, len, true) == 0) {
+ mask |= PLT_FILTER_MASK_LONGDESCRIPTION;
+ } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_ORIGINALTRACK, len, true) == 0) {
+ mask |= PLT_FILTER_MASK_ORIGINALTRACK;
+ } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_LASTPOSITION, len, true) == 0) {
+ mask |= PLT_FILTER_MASK_LASTPOSITION;
+ } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_LASTPLAYBACK, len, true) == 0) {
+ mask |= PLT_FILTER_MASK_LASTPLAYBACK;
+ } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_PLAYCOUNT, len, true) == 0) {
+ mask |= PLT_FILTER_MASK_PLAYCOUNT;
+ } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_SEARCHABLE, len, true) == 0) {
+ mask |= PLT_FILTER_MASK_SEARCHABLE;
+ } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_SEARCHCLASS, len, true) == 0) {
+ mask |= PLT_FILTER_MASK_SEARCHCLASS;
+ } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_CONTAINER_SEARCHABLE, len, true) == 0) {
+ mask |= PLT_FILTER_MASK_SEARCHABLE;
+ } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_CHILDCOUNT, len, true) == 0) {
+ mask |= PLT_FILTER_MASK_CHILDCOUNT;
+ } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_CONTAINER_CHILDCOUNT, len, true) == 0) {
+ mask |= PLT_FILTER_MASK_CHILDCOUNT;
+ } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_PROGRAMTITLE, len, true) == 0) {
+ mask |= PLT_FILTER_MASK_PROGRAMTITLE;
+ } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_SERIESTITLE, len, true) == 0) {
+ mask |= PLT_FILTER_MASK_SERIESTITLE;
+ } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_EPISODE, len, true) == 0) {
+ mask |= PLT_FILTER_MASK_EPISODE;
+ } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_RATING, len, true) == 0) {
+ mask |= PLT_FILTER_MASK_RATING;
+ } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_PUBLISHER, len, true) == 0) {
+ mask |= PLT_FILTER_MASK_PUBLISHER;
+ } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_RES, len, true) == 0) {
+ mask |= PLT_FILTER_MASK_RES;
+ } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_RES_DURATION, len, true) == 0 ||
+ NPT_String::CompareN(s+i, PLT_FILTER_FIELD_RES_DURATION_SHORT, len, true) == 0) {
+ mask |= PLT_FILTER_MASK_RES | PLT_FILTER_MASK_RES_DURATION;
+ } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_RES_SIZE, len, true) == 0) {
+ mask |= PLT_FILTER_MASK_RES | PLT_FILTER_MASK_RES_SIZE;
+ } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_RES_PROTECTION, len, true) == 0) {
+ mask |= PLT_FILTER_MASK_RES | PLT_FILTER_MASK_RES_PROTECTION;
+ } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_RES_RESOLUTION, len, true) == 0) {
+ mask |= PLT_FILTER_MASK_RES | PLT_FILTER_MASK_RES_RESOLUTION;
+ } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_RES_BITRATE, len, true) == 0) {
+ mask |= PLT_FILTER_MASK_RES | PLT_FILTER_MASK_RES_BITRATE;
+ } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_RES_BITSPERSAMPLE, len, true) == 0) {
+ mask |= PLT_FILTER_MASK_RES | PLT_FILTER_MASK_RES_BITSPERSAMPLE;
+ } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_RES_NRAUDIOCHANNELS, len, true) == 0) {
+ mask |= PLT_FILTER_MASK_RES | PLT_FILTER_MASK_RES_NRAUDIOCHANNELS;
+ } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_RES_SAMPLEFREQUENCY, len, true) == 0) {
+ mask |= PLT_FILTER_MASK_RES | PLT_FILTER_MASK_RES_SAMPLEFREQUENCY;
+ } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_EPISODE_COUNT, len, true) == 0) {
+ mask |= PLT_FILTER_MASK_EPISODE_COUNT;
+ } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_EPISODE_SEASON, len, true) == 0) {
+ mask |= PLT_FILTER_MASK_EPISODE_SEASON;
+ } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_XBMC_LASTPLAYERSTATE, len, true) == 0) {
+ mask |= PLT_FILTER_MASK_XBMC_LASTPLAYERSTATE;
+ } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_XBMC_DATEADDED, len, true) == 0) {
+ mask |= PLT_FILTER_MASK_XBMC_DATEADDED;
+ } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_XBMC_RATING, len, true) == 0) {
+ mask |= PLT_FILTER_MASK_XBMC_RATING;
+ } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_XBMC_VOTES, len, true) == 0) {
+ mask |= PLT_FILTER_MASK_XBMC_VOTES;
+ } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_XBMC_ARTWORK, len, true) == 0) {
+ mask |= PLT_FILTER_MASK_XBMC_ARTWORK;
+ } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_XBMC_UNIQUE_IDENTIFIER, len, true) == 0) {
+ mask |= PLT_FILTER_MASK_XBMC_UNIQUE_IDENTIFIER;
+ } else if (NPT_String::CompareN(s + i, PLT_FILTER_FIELD_XBMC_COUNTRY, len, true) == 0) {
+ mask |= PLT_FILTER_MASK_XBMC_COUNTRY;
+ } else if (NPT_String::CompareN(s + i, PLT_FILTER_FIELD_XBMC_USERRATING, len, true) == 0) {
+ mask |= PLT_FILTER_MASK_XBMC_USERRATING;
+ }
+
+ if (next_comma < 0) {
+ return mask;
+ }
+
+ i = next_comma + 1;
+ }
+
+ return mask;
+}
+
+/*----------------------------------------------------------------------
+| PLT_Didl::AppendXmlUnEscape
++---------------------------------------------------------------------*/
+void
+PLT_Didl::AppendXmlUnEscape(NPT_String& out, const char* in)
+{
+ unsigned int i=0;
+ while (i<NPT_StringLength(in)) {
+ if (NPT_String::CompareN(in+i, "&lt;", 4) == 0) {
+ out += '<';
+ i +=4;
+ } else if (NPT_String::CompareN(in+i, "&gt;", 4) == 0) {
+ out += '>';
+ i += 4;
+ } else if (NPT_String::CompareN(in+i, "&amp;", 5) == 0) {
+ out += '&';
+ i += 5;
+ } else if (NPT_String::CompareN(in+i, "&quot;", 6) == 0) {
+ out += '"';
+ i += 6;
+ } else if (NPT_String::CompareN(in+i, "&apos;", 6) == 0) {
+ out += '\'';
+ i += 6;
+ } else {
+ out += *(in+i);
+ i++;
+ }
+ }
+}
+
+/*----------------------------------------------------------------------
+| PLT_Didl::AppendXmlEscape
++---------------------------------------------------------------------*/
+void
+PLT_Didl::AppendXmlEscape(NPT_String& out, const char* in)
+{
+ if (!in) return;
+
+ for (int i=0; i<(int)NPT_StringLength(in); i++) {
+ if (*(in+i) == '<') {
+ out += "&lt;";
+ } else if (*(in+i) == '>') {
+ out += "&gt;";
+ } else if (*(in+i) == '&') {
+ out += "&amp;";
+ } else if (*(in+i) == '"') {
+ out += "&quot;";
+ } else if (*(in+i) == '\'') {
+ out += "&apos;";
+ } else {
+ out += *(in+i);
+ }
+ }
+}
+
+/*----------------------------------------------------------------------
+| PLT_Didl::FormatTimeStamp
++---------------------------------------------------------------------*/
+NPT_String
+PLT_Didl::FormatTimeStamp(NPT_UInt32 seconds)
+{
+ NPT_String result;
+ int hours = seconds/3600;
+ if (hours == 0) {
+ result += "0:";
+ } else {
+ result += NPT_String::FromInteger(hours) + ":";
+ }
+
+ int minutes = (seconds/60)%60;
+ if (minutes == 0) {
+ result += "00:";
+ } else {
+ if (minutes < 10) {
+ result += '0';
+ }
+ result += NPT_String::FromInteger(minutes) + ":";
+ }
+
+ int secs = seconds%60;
+ if (secs == 0) {
+ result += "00";
+ } else {
+ if (secs < 10) {
+ result += '0';
+ }
+ result += NPT_String::FromInteger(secs);
+ }
+
+ result += ".000"; // needed for XBOX360 otherwise it won't play the track
+ return result;
+}
+
+/*----------------------------------------------------------------------
+| PLT_Didl::ParseTimeStamp
++---------------------------------------------------------------------*/
+NPT_Result
+PLT_Didl::ParseTimeStamp(const NPT_String& timestamp, NPT_UInt32& seconds)
+{
+ // assume a timestamp in the format HH:MM:SS.FFF
+ int separator;
+ NPT_String str = timestamp;
+ NPT_UInt32 value;
+
+ // reset output params first
+ seconds = 0;
+
+ // remove milliseconds first if any
+ if ((separator = str.ReverseFind('.')) != -1) {
+ str = str.Left(separator);
+ }
+
+ // look for next separator
+ if ((separator = str.ReverseFind(':')) == -1) return NPT_FAILURE;
+
+ // extract seconds
+ NPT_CHECK_WARNING(str.SubString(separator+1).ToInteger(value));
+ seconds = value;
+ str = str.Left(separator);
+
+ // look for next separator
+ if ((separator = str.ReverseFind(':')) == -1) return NPT_FAILURE;
+
+ // extract minutes
+ NPT_CHECK_WARNING(str.SubString(separator+1).ToInteger(value));
+ seconds += 60*value;
+ str = str.Left(separator);
+
+ // extract hours
+ NPT_CHECK_WARNING(str.ToInteger(value));
+ seconds += 3600*value;
+
+ return NPT_SUCCESS;
+}
+
+/*----------------------------------------------------------------------
+| PLT_Didl::ToDidl
++---------------------------------------------------------------------*/
+NPT_Result
+PLT_Didl::ToDidl(PLT_MediaObject& object, const NPT_String& filter, NPT_String& didl)
+{
+ NPT_UInt64 mask = ConvertFilterToMask(filter);
+
+ // Allocate enough space for the didl
+ didl.Reserve(2048);
+
+ return object.ToDidl(mask, didl);
+}
+
+/*----------------------------------------------------------------------
+| PLT_Didl::FromDidl
++---------------------------------------------------------------------*/
+NPT_Result
+PLT_Didl::FromDidl(const char* xml, PLT_MediaObjectListReference& objects)
+{
+ NPT_String str;
+ PLT_MediaObject* object = NULL;
+ NPT_XmlNode* node = NULL;
+ NPT_XmlElementNode* didl = NULL;
+ NPT_XmlParser parser;
+
+ NPT_LOG_FINE("Parsing Didl...");
+
+ NPT_CHECK_LABEL_SEVERE(parser.Parse(xml, node), cleanup);
+ if (!node || !node->AsElementNode()) {
+ NPT_LOG_SEVERE("Invalid node type");
+ goto cleanup;
+ }
+
+ didl = node->AsElementNode();
+
+ if (didl->GetTag().Compare("DIDL-Lite", true)) {
+ NPT_LOG_SEVERE("Invalid node tag");
+ goto cleanup;
+ }
+
+ // create entry list
+ objects = new PLT_MediaObjectList();
+
+ // for each child, find out if it's a container or not
+ // and then invoke the FromDidl on it
+ for (NPT_List<NPT_XmlNode*>::Iterator children = didl->GetChildren().GetFirstItem(); children; children++) {
+ NPT_XmlElementNode* child = (*children)->AsElementNode();
+ if (!child) continue;
+
+ if (child->GetTag().Compare("Container", true) == 0) {
+ object = new PLT_MediaContainer();
+ } else if (child->GetTag().Compare("item", true) == 0) {
+ object = new PLT_MediaItem();
+ } else {
+ NPT_LOG_WARNING("Invalid node tag");
+ continue;
+ }
+
+ if (NPT_FAILED(object->FromDidl(child))) {
+ NPT_LOG_WARNING_1("Invalid didl for object: %s",
+ (const char*) PLT_XmlHelper::Serialize(*child, false));
+ continue;
+ }
+
+ objects->Add(object);
+ object = NULL; // reset to make sure it doesn't get deleted twice in case of error
+ }
+
+ delete node;
+ return NPT_SUCCESS;
+
+cleanup:
+ objects = NULL;
+ delete node;
+ delete object;
+ return NPT_FAILURE;
+}