diff options
Diffstat (limited to 'lib/libUPnP/Platinum/Source/Devices/MediaServer/PltDidl.cpp')
-rw-r--r-- | lib/libUPnP/Platinum/Source/Devices/MediaServer/PltDidl.cpp | 406 |
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, "<", 4) == 0) { + out += '<'; + i +=4; + } else if (NPT_String::CompareN(in+i, ">", 4) == 0) { + out += '>'; + i += 4; + } else if (NPT_String::CompareN(in+i, "&", 5) == 0) { + out += '&'; + i += 5; + } else if (NPT_String::CompareN(in+i, """, 6) == 0) { + out += '"'; + i += 6; + } else if (NPT_String::CompareN(in+i, "'", 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 += "<"; + } else if (*(in+i) == '>') { + out += ">"; + } else if (*(in+i) == '&') { + out += "&"; + } else if (*(in+i) == '"') { + out += """; + } else if (*(in+i) == '\'') { + out += "'"; + } 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; +} |