view deps/Platinum/Source/Devices/MediaServer/PltDidl.cpp @ 0:3425707ddbf6

Initial import (hopefully this mercurial stuff works...)
author fraserofthenight
date Mon, 06 Jul 2009 08:06:28 -0700
parents
children
line wrap: on
line source

/*****************************************************************
|
|   Platinum - DIDL
|
| Copyright (c) 2004-2008, 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.
| 
| 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 "PltXmlHelper.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/\">";
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/";

/*----------------------------------------------------------------------
|   PLT_Didl::ConvertFilterToMask
+---------------------------------------------------------------------*/
NPT_UInt32 
PLT_Didl::ConvertFilterToMask(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_UInt32  mask = 0;
    const char* s = filter;
    int         i = 0;

    while (s[i] != '\0') {
        int next_comma = filter.Find(',', i);
        int len = ((next_comma < 0)?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;
        } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_CREATOR, len) == 0) {
            mask |= PLT_FILTER_MASK_CREATOR;
        } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_ARTIST, len) == 0) {
            mask |= PLT_FILTER_MASK_ARTIST;
        } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_ACTOR, len) == 0) {
            mask |= PLT_FILTER_MASK_ACTOR;
        } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_AUTHOR, len) == 0) {
            mask |= PLT_FILTER_MASK_AUTHOR;       
        } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_DATE, len) == 0) {
            mask |= PLT_FILTER_MASK_DATE;
        } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_ALBUM, len) == 0) {
            mask |= PLT_FILTER_MASK_ALBUM;
        } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_GENRE, len) == 0) {
            mask |= PLT_FILTER_MASK_GENRE;
        } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_ALBUMARTURI, len) == 0) {
            mask |= PLT_FILTER_MASK_ALBUMARTURI;
        } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_DESCRIPTION, len) == 0) {
            mask |= PLT_FILTER_MASK_DESCRIPTION;
        } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_ORIGINALTRACK, len) == 0) {
            mask |= PLT_FILTER_MASK_ORIGINALTRACK;
        } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_SEARCHABLE, len) == 0) {
            mask |= PLT_FILTER_MASK_SEARCHABLE;
        } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_CONTAINER_SEARCHABLE, len) == 0) {
            mask |= PLT_FILTER_MASK_SEARCHABLE;       
        } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_CHILDCOUNT, len) == 0) {
            mask |= PLT_FILTER_MASK_CHILDCOUNT;
        } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_CONTAINER_CHILDCOUNT, len) == 0) {
            mask |= PLT_FILTER_MASK_CHILDCOUNT;
        } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_RES_DURATION, len) == 0) {
            mask |= PLT_FILTER_MASK_RES | PLT_FILTER_MASK_RES_DURATION;
        } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_RES_SIZE, len) == 0) {
            mask |= PLT_FILTER_MASK_RES | PLT_FILTER_MASK_RES_SIZE;
        } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_RES_PROTECTION, len) == 0) {
            mask |= PLT_FILTER_MASK_RES | PLT_FILTER_MASK_RES_PROTECTION;
        } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_RES_RESOLUTION, len) == 0) {
            mask |= PLT_FILTER_MASK_RES | PLT_FILTER_MASK_RES_RESOLUTION;
        } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_RES, len) == 0) {
            mask |= PLT_FILTER_MASK_RES;
        } 

        if (next_comma < 0) {
            return mask;
        }

        i = next_comma + 1;
    }

    return mask;
}

/*----------------------------------------------------------------------
|   PLT_Didl::AppendXmlUnEscape
+---------------------------------------------------------------------*/
void
PLT_Didl::AppendXmlUnEscape(NPT_String& out, NPT_String& in)
{
    const char* input = (const char*) in;
    unsigned int i=0;
    while (i<in.GetLength()) {
        if (NPT_String::CompareN(input+i, "&lt;", 4) == 0) {
            out += '<';
            i   +=4;
        } else if (NPT_String::CompareN(input+i, "&gt;", 4) == 0) {
            out += '>';
            i   += 4;
        } else if (NPT_String::CompareN(input+i, "&amp;", 5) == 0) {
            out += '&';
            i   += 5;
        } else if (NPT_String::CompareN(input+i, "&quot;", 6) == 0) {
            out += '"';
            i   += 6;
        } else if (NPT_String::CompareN(input+i, "&apos;", 6) == 0) {
            out += '\'';
            i   += 6;
        } else {
            out += *(input+i);
            i++;
        }
    }
}

/*----------------------------------------------------------------------
|   PLT_Didl::AppendXmlEscape
+---------------------------------------------------------------------*/
void
PLT_Didl::AppendXmlEscape(NPT_String& out, NPT_String& in)
{
    for (int i=0; i<(int)in.GetLength(); 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
+---------------------------------------------------------------------*/
void
PLT_Didl::FormatTimeStamp(NPT_String& out, NPT_UInt32 seconds)
{
    int hours = seconds/3600;
    if (hours == 0) {
        out += "00:";
    } else {
        if (hours < 10) {
            out += '0';
        }
        out += NPT_String::FromInteger(hours) + ":";
    }

    int minutes = (seconds/60)%60;
    if (minutes == 0) {
        out += "00:";
    } else {
        if (minutes < 10) {
            out += '0';
        }
        out += NPT_String::FromInteger(minutes) + ":";
    }

    int secs = seconds%60;
    if (secs == 0) {
        out += "00";
    } else {
        if (secs < 10) {
            out += '0';
        }
        out += NPT_String::FromInteger(secs);
    }

    out += ".000";
}

/*----------------------------------------------------------------------
|   PLT_Didl::ParseTimeStamp
+---------------------------------------------------------------------*/
NPT_Result
PLT_Didl::ParseTimeStamp(NPT_String timestamp, NPT_UInt32& seconds)
{
    // assume a timestamp in the format HH:MM:SS
    int colon;
    NPT_String str = timestamp;
    NPT_UInt32 num;

    // extract millisecondsfirst
    if ((colon = timestamp.ReverseFind('.')) != -1) {
        str = timestamp.SubString(colon + 1);
        timestamp = timestamp.Left(colon);
    }

    // extract seconds first
    str = timestamp;
    if ((colon = timestamp.ReverseFind(':')) != -1) {
        str = timestamp.SubString(colon + 1);
        timestamp = timestamp.Left(colon);
    }

    if (NPT_FAILED(str.ToInteger(num))) {
        return NPT_FAILURE;
    }

    seconds = num;

    // extract minutes
    str = timestamp;
    if (timestamp.GetLength()) {
        if ((colon = timestamp.ReverseFind(':')) != -1) {
            str = timestamp.SubString(colon + 1);
            timestamp = timestamp.Left(colon);
        }

        if (NPT_FAILED(str.ToInteger(num))) {
            return NPT_FAILURE;
        }

        seconds += 60*num;
    }

    // extract hours
    str = timestamp;
    if (timestamp.GetLength()) {
        if ((colon = timestamp.ReverseFind(':')) != -1) {
            str = timestamp.SubString(colon + 1);
            timestamp = timestamp.Left(colon);
        }

        if (NPT_FAILED(str.ToInteger(num))) {
            return NPT_FAILURE;
        }

        seconds += 3600*num;
    }

    return NPT_SUCCESS;
}

/*----------------------------------------------------------------------
|   PLT_Didl::ToDidl
+---------------------------------------------------------------------*/
NPT_Result
PLT_Didl::ToDidl(PLT_MediaObject& object, NPT_String filter, NPT_String& didl)
{
    NPT_UInt32 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_LOG_FINE("Parsing Didl...");

    NPT_XmlParser parser;
    if (NPT_FAILED(parser.Parse(xml, node)) || !node || !node->AsElementNode()) {
        goto cleanup;
    }

    didl = node->AsElementNode();

    NPT_LOG_FINE("Processing Didl xml...");
    if (didl->GetTag().Compare("DIDL-Lite", true)) {
        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 {
            goto cleanup;
        }

        if (NPT_FAILED(object->FromDidl(child))) {
            goto cleanup;
        }
        objects->Add(object);
    }

    delete node;
    return NPT_SUCCESS;

cleanup:
    objects = NULL;
    delete node;
    delete object;
    return NPT_FAILURE;
}