diff options
Diffstat (limited to 'lib/libUPnP/Platinum/Source/Devices/MediaServer/PltMediaItem.cpp')
-rw-r--r-- | lib/libUPnP/Platinum/Source/Devices/MediaServer/PltMediaItem.cpp | 1128 |
1 files changed, 1128 insertions, 0 deletions
diff --git a/lib/libUPnP/Platinum/Source/Devices/MediaServer/PltMediaItem.cpp b/lib/libUPnP/Platinum/Source/Devices/MediaServer/PltMediaItem.cpp new file mode 100644 index 0000000..fac1690 --- /dev/null +++ b/lib/libUPnP/Platinum/Source/Devices/MediaServer/PltMediaItem.cpp @@ -0,0 +1,1128 @@ +/***************************************************************** +| +| Platinum - AV Media Item +| +| 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 "PltMediaItem.h" +#include "PltMediaServer.h" +#include "PltDidl.h" +#include "PltUtilities.h" +#include "PltService.h" +#include "PltMimeType.h" + +NPT_SET_LOCAL_LOGGER("platinum.media.server.item") + +/*---------------------------------------------------------------------- +| globals ++---------------------------------------------------------------------*/ +NPT_DEFINE_DYNAMIC_CAST_ANCHOR(PLT_MediaObject) +NPT_DEFINE_DYNAMIC_CAST_ANCHOR(PLT_MediaItem) +NPT_DEFINE_DYNAMIC_CAST_ANCHOR(PLT_MediaContainer) + +/*---------------------------------------------------------------------- +| PLT_PersonRoles::AddPerson ++---------------------------------------------------------------------*/ +NPT_Result +PLT_PersonRoles::Add(const NPT_String& name, const NPT_String& role /* = "" */) +{ + PLT_PersonRole person; + person.name = name; + person.role = role; + + return NPT_List<PLT_PersonRole>::Add(person); +} + +/*---------------------------------------------------------------------- +| PLT_PersonRoles::ToDidl ++---------------------------------------------------------------------*/ +NPT_Result +PLT_PersonRoles::ToDidl(NPT_String& didl, const NPT_String& tag) +{ + NPT_String tmp; + for (NPT_List<PLT_PersonRole>::Iterator it = + NPT_List<PLT_PersonRole>::GetFirstItem(); it; it++) { + // if there's an empty artist, allow it only if there's nothing else + if (it->name.IsEmpty() && m_ItemCount>1 && !tmp.IsEmpty()) continue; + + tmp += "<upnp:" + tag; + if (!it->role.IsEmpty()) { + tmp += " role=\""; + PLT_Didl::AppendXmlEscape(tmp, it->role); + tmp += "\""; + } + tmp += ">"; + PLT_Didl::AppendXmlEscape(tmp, it->name); + tmp += "</upnp:" + tag + ">"; + } + + didl += tmp; + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| PLT_PersonRoles::ToDidl ++---------------------------------------------------------------------*/ +NPT_Result +PLT_PersonRoles::FromDidl(const NPT_Array<NPT_XmlElementNode*>& nodes) +{ + for (NPT_Cardinal i=0; i<nodes.GetItemCount(); i++) { + PLT_PersonRole person; + const NPT_String* name = nodes[i]->GetText(); + const NPT_String* role = nodes[i]->GetAttribute("role"); + // DLNA 7.3.17 + if (name) person.name = name->SubString(0, 1024); + if (role) person.role = role->SubString(0, 1024); + NPT_CHECK(NPT_List<PLT_PersonRole>::Add(person)); + } + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| PLT_Artworks::Add ++---------------------------------------------------------------------*/ +NPT_Result +PLT_Artworks::Add(const NPT_String& type, const NPT_String& url) +{ + PLT_Artwork artwork; + artwork.type = type; + artwork.url = url; + + return NPT_List<PLT_Artwork>::Add(artwork); +} + +/*---------------------------------------------------------------------- +| PLT_Artworks::ToDidl ++---------------------------------------------------------------------*/ +NPT_Result +PLT_Artworks::ToDidl(NPT_String& didl, const NPT_String& tag) +{ + NPT_String tmp; + for (NPT_List<PLT_Artwork>::Iterator it = + NPT_List<PLT_Artwork>::GetFirstItem(); it; it++) { + if (it->type.IsEmpty()) continue; + + tmp += "<xbmc:" + tag; + if (!it->type.IsEmpty()) { + tmp += " type=\""; + PLT_Didl::AppendXmlEscape(tmp, it->type); + tmp += "\""; + } + tmp += ">"; + PLT_Didl::AppendXmlEscape(tmp, it->url); + tmp += "</xbmc:" + tag + ">"; + } + + didl += tmp; + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| PLT_Artworks::ToDidl ++---------------------------------------------------------------------*/ +NPT_Result +PLT_Artworks::FromDidl(const NPT_Array<NPT_XmlElementNode*>& nodes) +{ + for (NPT_Cardinal i=0; i<nodes.GetItemCount(); i++) { + PLT_Artwork artwork; + const NPT_String* url = nodes[i]->GetText(); + const NPT_String* type = nodes[i]->GetAttribute("type"); + if (type) artwork.type = *type; + if (url) artwork.url = *url; + NPT_CHECK(NPT_List<PLT_Artwork>::Add(artwork)); + } + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| PLT_MediaItemResource::PLT_MediaItemResource ++---------------------------------------------------------------------*/ +PLT_MediaItemResource::PLT_MediaItemResource() +{ + m_Uri = ""; + m_ProtocolInfo = PLT_ProtocolInfo(); + m_Duration = (NPT_UInt32)-1; + m_Size = (NPT_LargeSize)-1; + m_Protection = ""; + m_Bitrate = (NPT_UInt32)-1; + m_BitsPerSample = (NPT_UInt32)-1; + m_SampleFrequency = (NPT_UInt32)-1; + m_NbAudioChannels = (NPT_UInt32)-1; + m_Resolution = ""; + m_ColorDepth = (NPT_UInt32)-1; +} + +/*---------------------------------------------------------------------- +| PLT_MediaObject::GetUPnPClass ++---------------------------------------------------------------------*/ +const char* +PLT_MediaObject::GetUPnPClass(const char* filename, + const PLT_HttpRequestContext* context /* = NULL */) +{ + NPT_COMPILER_UNUSED(context); + + const char* ret = NULL; + NPT_String mime_type = PLT_MimeType::GetMimeType(filename, context); + + if (mime_type.StartsWith("audio")) { + ret = "object.item.audioItem.musicTrack"; + } else if (mime_type.StartsWith("video")) { + ret = "object.item.videoItem"; //Note: 360 wants "object.item.videoItem" and not "object.item.videoItem.Movie" + } else if (mime_type.StartsWith("image")) { + ret = "object.item.imageItem.photo"; + } else { + ret = "object.item"; + } + + return ret; +} + +/*---------------------------------------------------------------------- +| PLT_MediaObject::Reset ++---------------------------------------------------------------------*/ +NPT_Result +PLT_MediaObject::Reset() +{ + m_ObjectClass.type = ""; + m_ObjectClass.friendly_name = ""; + m_ObjectID = ""; + m_ParentID = ""; + + m_Title = ""; + m_Creator = ""; + m_Date = ""; + m_Restricted = true; + + m_People.actors.Clear(); + m_People.artists.Clear(); + m_People.authors.Clear(); + m_People.directors.Clear(); + m_People.publisher.Clear(); + + m_Affiliation.album = ""; + m_Affiliation.genres.Clear(); + m_Affiliation.playlist = ""; + + m_Description.description = ""; + m_Description.long_description = ""; + m_Description.icon_uri = ""; + m_ExtraInfo.album_arts.Clear(); + m_ExtraInfo.artist_discography_uri = ""; + + m_MiscInfo.original_track_number = 0; + m_MiscInfo.last_position = 0; + m_MiscInfo.last_time = ""; + m_MiscInfo.play_count = -1; + m_MiscInfo.dvdregioncode = 0; + m_MiscInfo.toc = ""; + m_MiscInfo.user_annotation = ""; + + m_Recorded.program_title = ""; + m_Recorded.series_title = ""; + m_Recorded.episode_number = 0; + m_Recorded.episode_count = 0; + m_Recorded.episode_season = 0; + + m_Resources.Clear(); + + m_XbmcInfo.last_playerstate = ""; + m_XbmcInfo.date_added = ""; + m_XbmcInfo.rating = 0.0f; + m_XbmcInfo.votes = 0; + m_XbmcInfo.artwork.Clear(); + m_XbmcInfo.unique_identifier = ""; + m_XbmcInfo.countries.Clear(); + m_XbmcInfo.user_rating = 0; + + m_Didl = ""; + + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| PLT_MediaObject::ToDidl ++---------------------------------------------------------------------*/ +NPT_Result +PLT_MediaObject::ToDidl(const NPT_String& filter, NPT_String& didl) +{ + return ToDidl(PLT_Didl::ConvertFilterToMask(filter), didl); +} + +/*---------------------------------------------------------------------- +| PLT_MediaObject::ToDidl ++---------------------------------------------------------------------*/ +NPT_Result +PLT_MediaObject::ToDidl(NPT_UInt64 mask, NPT_String& didl) +{ + // title is required + didl += "<dc:title>"; + PLT_Didl::AppendXmlEscape(didl, m_Title); + didl += "</dc:title>"; + + // creator + if (mask & PLT_FILTER_MASK_CREATOR) { + didl += "<dc:creator>"; + if (m_Creator.IsEmpty()) m_Creator = "Unknown"; + PLT_Didl::AppendXmlEscape(didl, m_Creator); + didl += "</dc:creator>"; + } + + // date + if ((mask & PLT_FILTER_MASK_DATE) && !m_Date.IsEmpty()) { + didl += "<dc:date>"; + PLT_Didl::AppendXmlEscape(didl, m_Date); + didl += "</dc:date>"; + } + + // artist + if (mask & PLT_FILTER_MASK_ARTIST) { + // force an empty artist just in case (not DLNA Compliant though) + //if (m_People.artists.GetItemCount() == 0) m_People.artists.Add(""); + m_People.artists.ToDidl(didl, "artist"); + } + + // actor + if (mask & PLT_FILTER_MASK_ACTOR) { + m_People.actors.ToDidl(didl, "actor"); + } + + // actor + if (mask & PLT_FILTER_MASK_AUTHOR) { + m_People.authors.ToDidl(didl, "author"); + } + + // director + if (mask & PLT_FILTER_MASK_DIRECTOR) { + m_People.directors.ToDidl(didl, "director"); + } + + // publisher + if (mask & PLT_FILTER_MASK_PUBLISHER) { + // Add unknown publisher + if (m_People.publisher.GetItemCount() == 0) + m_People.publisher.Add("Unknown"); + + for (NPT_List<NPT_String>::Iterator it = + m_People.publisher.GetFirstItem(); it; ++it) { + didl += "<dc:publisher>"; + PLT_Didl::AppendXmlEscape(didl, (*it)); + didl += "</dc:publisher>"; + } + } + + // album + if ((mask & PLT_FILTER_MASK_ALBUM) && !m_Affiliation.album.IsEmpty()) { + didl += "<upnp:album>"; + PLT_Didl::AppendXmlEscape(didl, m_Affiliation.album); + didl += "</upnp:album>"; + } + + // genre + if (mask & PLT_FILTER_MASK_GENRE) { + // Add unknown genre + if (m_Affiliation.genres.GetItemCount() == 0) + m_Affiliation.genres.Add("Unknown"); + + for (NPT_List<NPT_String>::Iterator it = + m_Affiliation.genres.GetFirstItem(); it; ++it) { + didl += "<upnp:genre>"; + PLT_Didl::AppendXmlEscape(didl, (*it)); + didl += "</upnp:genre>"; + } + } + + // album art URI + if ((mask & PLT_FILTER_MASK_ALBUMARTURI) && m_ExtraInfo.album_arts.GetItemCount()) { + for (NPT_List<PLT_AlbumArtInfo>::Iterator iter = m_ExtraInfo.album_arts.GetFirstItem(); + iter; + iter++) { + didl += "<upnp:albumArtURI"; + if (!(*iter).dlna_profile.IsEmpty()) { + didl += " dlna:profileID=\""; + PLT_Didl::AppendXmlEscape(didl, (*iter).dlna_profile); + didl += "\""; + } + didl += ">"; + PLT_Didl::AppendXmlEscape(didl, (*iter).uri); + didl += "</upnp:albumArtURI>"; + } + } + + // description + if ((mask & PLT_FILTER_MASK_DESCRIPTION) && !m_Description.description.IsEmpty()) { + didl += "<dc:description>"; + PLT_Didl::AppendXmlEscape(didl, m_Description.description); + didl += "</dc:description>"; + } + + // long description + if ((mask & PLT_FILTER_MASK_LONGDESCRIPTION) && !m_Description.long_description.IsEmpty()) { + didl += "<upnp:longDescription>"; + PLT_Didl::AppendXmlEscape(didl, m_Description.long_description); + didl += "</upnp:longDescription>"; + } + + // icon + if ((mask & PLT_FILTER_MASK_ICON) && !m_Description.icon_uri.IsEmpty()) { + didl += "<upnp:icon>"; + PLT_Didl::AppendXmlEscape(didl, m_Description.icon_uri); + didl += "</upnp:icon>"; + } + + // rating + if ((mask & PLT_FILTER_MASK_RATING) && !m_Description.rating.IsEmpty()) { + didl += "<upnp:rating>"; + PLT_Didl::AppendXmlEscape(didl, m_Description.rating); + didl += "</upnp:rating>"; + } + + // original track number + if ((mask & PLT_FILTER_MASK_ORIGINALTRACK) && m_MiscInfo.original_track_number > 0) { + didl += "<upnp:originalTrackNumber>"; + didl += NPT_String::FromInteger(m_MiscInfo.original_track_number); + didl += "</upnp:originalTrackNumber>"; + } + + // last playback position + if (mask & PLT_FILTER_MASK_LASTPOSITION && m_MiscInfo.last_position > 0) { + didl += "<upnp:lastPlaybackPosition>"; + didl += NPT_String::FromInteger(m_MiscInfo.last_position); + didl += "</upnp:lastPlaybackPosition>"; + } + + // last playback datetime + if (mask & PLT_FILTER_MASK_LASTPLAYBACK && !m_MiscInfo.last_time.IsEmpty()) { + didl += "<upnp:lastPlaybackTime>"; + PLT_Didl::AppendXmlEscape(didl, m_MiscInfo.last_time); + didl += "</upnp:lastPlaybackTime>"; + } + + // playcount + if (mask & PLT_FILTER_MASK_PLAYCOUNT && m_MiscInfo.play_count > -1) { + didl += "<upnp:playbackCount>"; + didl += NPT_String::FromInteger(m_MiscInfo.play_count); + didl += "</upnp:playbackCount>"; + } + + // program title + if (mask & PLT_FILTER_MASK_PROGRAMTITLE && !m_Recorded.program_title.IsEmpty()) { + didl += "<upnp:programTitle>"; + PLT_Didl::AppendXmlEscape(didl, m_Recorded.program_title); + didl += "</upnp:programTitle>"; + } + + // series title + if ((mask & PLT_FILTER_MASK_SERIESTITLE) && !m_Recorded.series_title.IsEmpty()) { + didl += "<upnp:seriesTitle>"; + PLT_Didl::AppendXmlEscape(didl, m_Recorded.series_title); + didl += "</upnp:seriesTitle>"; + } + + // episode number + if ((mask & PLT_FILTER_MASK_EPISODE) && m_Recorded.episode_number > 0) { + didl += "<upnp:episodeNumber>"; + didl += NPT_String::FromInteger(m_Recorded.episode_number); + didl += "</upnp:episodeNumber>"; + } + + // episode count + if ((mask & PLT_FILTER_MASK_EPISODE_COUNT) && m_Recorded.episode_count > 0) { + didl += "<upnp:episodeCount>"; + didl += NPT_String::FromInteger(m_Recorded.episode_count); + didl += "</upnp:episodeCount>"; + } + + // episode count + if ((mask & PLT_FILTER_MASK_EPISODE_SEASON)) { + didl += "<upnp:episodeSeason>"; + didl += NPT_String::FromInteger(m_Recorded.episode_season); + didl += "</upnp:episodeSeason>"; + } + + if ((mask & PLT_FILTER_MASK_TOC) && !m_MiscInfo.toc.IsEmpty()) { + didl += "<upnp:toc>"; + PLT_Didl::AppendXmlEscape(didl, m_MiscInfo.toc); + didl += "</upnp:toc>"; + } + + // resource + if (mask & PLT_FILTER_MASK_RES) { + for (NPT_Cardinal i=0; i<m_Resources.GetItemCount(); i++) { + didl += "<res"; + + if ((mask & PLT_FILTER_MASK_RES_DURATION) && m_Resources[i].m_Duration != (NPT_UInt32)-1) { + didl += " duration=\""; + didl += PLT_Didl::FormatTimeStamp(m_Resources[i].m_Duration); + didl += "\""; + } + + if ((mask & PLT_FILTER_MASK_RES_SIZE) && m_Resources[i].m_Size != (NPT_LargeSize)-1) { + didl += " size=\""; + didl += NPT_String::FromIntegerU(m_Resources[i].m_Size); + didl += "\""; + } + + if ((mask & PLT_FILTER_MASK_RES_PROTECTION) && !m_Resources[i].m_Protection.IsEmpty()) { + didl += " protection=\""; + PLT_Didl::AppendXmlEscape(didl, m_Resources[i].m_Protection); + didl += "\""; + } + + if ((mask & PLT_FILTER_MASK_RES_RESOLUTION) && !m_Resources[i].m_Resolution.IsEmpty()) { + didl += " resolution=\""; + PLT_Didl::AppendXmlEscape(didl, m_Resources[i].m_Resolution); + didl += "\""; + } + + if ((mask & PLT_FILTER_MASK_RES_BITRATE) && m_Resources[i].m_Bitrate != (NPT_Size)-1) { + didl += " bitrate=\""; + didl += NPT_String::FromIntegerU(m_Resources[i].m_Bitrate); + didl += "\""; + } + + if ((mask & PLT_FILTER_MASK_RES_BITSPERSAMPLE) && m_Resources[i].m_BitsPerSample != (NPT_Size)-1) { + didl += " bitsPerSample=\""; + didl += NPT_String::FromIntegerU(m_Resources[i].m_BitsPerSample); + didl += "\""; + } + + if ((mask & PLT_FILTER_MASK_RES_SAMPLEFREQUENCY) && m_Resources[i].m_SampleFrequency != (NPT_Size)-1) { + didl += " sampleFrequency=\""; + didl += NPT_String::FromIntegerU(m_Resources[i].m_SampleFrequency); + didl += "\""; + } + + if ((mask & PLT_FILTER_MASK_RES_NRAUDIOCHANNELS) && m_Resources[i].m_NbAudioChannels != (NPT_Size)-1) { + didl += " nrAudioChannels=\""; + didl += NPT_String::FromIntegerU(m_Resources[i].m_NbAudioChannels); + didl += "\""; + } + + didl += " protocolInfo=\""; + PLT_Didl::AppendXmlEscape(didl, m_Resources[i].m_ProtocolInfo.ToString()); + didl += "\""; + /* adding custom data */ + NPT_List<NPT_Map<NPT_String, NPT_String>::Entry*>::Iterator entry = m_Resources[i].m_CustomData.GetEntries().GetFirstItem(); + while (entry) + { + didl += " "; + PLT_Didl::AppendXmlEscape(didl, (*entry)->GetKey()); + didl += "=\""; + PLT_Didl::AppendXmlEscape(didl, (*entry)->GetValue()); + didl += "\""; + + entry++; + } + + didl += ">"; + PLT_Didl::AppendXmlEscape(didl, m_Resources[i].m_Uri); + didl += "</res>"; + } + } + + //sec resources related + for (NPT_Cardinal i = 0; i < m_SecResources.GetItemCount(); i++) { + didl += "<sec:"; + PLT_Didl::AppendXmlEscape(didl, m_SecResources[i].name); + + NPT_List<NPT_Map<NPT_String, NPT_String>::Entry*>::Iterator entry = m_SecResources[i].attributes.GetEntries().GetFirstItem(); + while (entry) + { + didl += " sec:"; + PLT_Didl::AppendXmlEscape(didl, (*entry)->GetKey()); + didl += "=\""; + PLT_Didl::AppendXmlEscape(didl, (*entry)->GetValue()); + didl += "\""; + + entry++; + } + + didl += ">"; + PLT_Didl::AppendXmlEscape(didl, m_SecResources[i].value); + + didl += "</sec:"; + PLT_Didl::AppendXmlEscape(didl, m_SecResources[i].name); + didl += ">"; + } + + // xbmc dateadded + if ((mask & PLT_FILTER_MASK_XBMC_DATEADDED) && !m_XbmcInfo.date_added.IsEmpty()) { + didl += "<xbmc:dateadded>"; + PLT_Didl::AppendXmlEscape(didl, m_XbmcInfo.date_added); + didl += "</xbmc:dateadded>"; + } + + // xbmc rating + if (mask & PLT_FILTER_MASK_XBMC_RATING) { + didl += "<xbmc:rating>"; + didl += NPT_String::Format("%.1f", m_XbmcInfo.rating); + didl += "</xbmc:rating>"; + } + + // xbmc votes + if (mask & PLT_FILTER_MASK_XBMC_VOTES && m_XbmcInfo.votes != 0) { + didl += "<xbmc:votes>"; + didl += NPT_String::Format("%i", m_XbmcInfo.votes); + didl += "</xbmc:votes>"; + } + + // xbmc artwork + if (mask & PLT_FILTER_MASK_XBMC_ARTWORK) { + m_XbmcInfo.artwork.ToDidl(didl, "artwork"); + } + + // xbmc unique identifier + if (mask & PLT_FILTER_MASK_XBMC_UNIQUE_IDENTIFIER && !m_XbmcInfo.unique_identifier.IsEmpty()) { + didl += "<xbmc:uniqueidentifier>"; + PLT_Didl::AppendXmlEscape(didl, m_XbmcInfo.unique_identifier); + didl += "</xbmc:uniqueidentifier>"; + } + + // country + if (mask & PLT_FILTER_MASK_XBMC_COUNTRY) { + for (NPT_List<NPT_String>::Iterator it = + m_XbmcInfo.countries.GetFirstItem(); it; ++it) { + didl += "<xbmc:country>"; + PLT_Didl::AppendXmlEscape(didl, (*it)); + didl += "</xbmc:country>"; + } + } + + // user rating + if (mask & PLT_FILTER_MASK_XBMC_USERRATING) { + didl += "<xbmc:userrating>"; + didl += NPT_String::FromInteger(m_XbmcInfo.user_rating); + didl += "</xbmc:userrating>"; + } + + // xbmc last playback state + if (mask & PLT_FILTER_MASK_XBMC_LASTPLAYERSTATE && !m_XbmcInfo.last_playerstate.IsEmpty()) { + didl += "<xbmc:lastPlayerState>"; + PLT_Didl::AppendXmlEscape(didl, m_XbmcInfo.last_playerstate); + didl += "</xbmc:lastPlayerState>"; + } + + // class is required + didl += "<upnp:class"; + if (!m_ObjectClass.friendly_name.IsEmpty()) { + didl += " name=\"" + m_ObjectClass.friendly_name+"\""; + } + didl += ">"; + PLT_Didl::AppendXmlEscape(didl, m_ObjectClass.type); + didl += "</upnp:class>"; + + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| PLT_MediaObject::FromDidl ++---------------------------------------------------------------------*/ +NPT_Result +PLT_MediaObject::FromDidl(NPT_XmlElementNode* entry) +{ + NPT_String str, xml; + NPT_Array<NPT_XmlElementNode*> children; + NPT_Result res; + + // check if item is restricted (is default true?) + if (NPT_SUCCEEDED(PLT_XmlHelper::GetAttribute(entry, "restricted", str, "", 5))) { + m_Restricted = PLT_Service::IsTrue(str); + } + + // read non-required elements + PLT_XmlHelper::GetChildText(entry, "creator", m_Creator, didl_namespace_dc, 256); + PLT_XmlHelper::GetChildText(entry, "date", m_Date, didl_namespace_dc, 256); + + // parse date and make sure it's valid + NPT_String parsed_date; + for (int format=0; format<=NPT_DateTime::FORMAT_RFC_1036; format++) { + NPT_DateTime date; + if (NPT_SUCCEEDED(date.FromString(m_Date, (NPT_DateTime::Format)format))) { + parsed_date = date.ToString((NPT_DateTime::Format)format); + break; + } + } + m_Date = parsed_date; + + res = PLT_XmlHelper::GetAttribute(entry, "id", m_ObjectID); + NPT_CHECK_SEVERE(res); + + res = PLT_XmlHelper::GetAttribute(entry, "parentID", m_ParentID); + NPT_CHECK_SEVERE(res); + + PLT_XmlHelper::GetAttribute(entry, "refID", m_ReferenceID); + + res = PLT_XmlHelper::GetChildText(entry, "title", m_Title, didl_namespace_dc); + NPT_CHECK_SEVERE(res); + + res = PLT_XmlHelper::GetChildText(entry, "class", m_ObjectClass.type, didl_namespace_upnp); + NPT_CHECK_SEVERE(res); + + // DLNA 7.3.17.3 max bytes for dc:title and upnp:class is 256 bytes + m_Title = m_Title.SubString(0, 256); + m_ObjectClass.type = m_ObjectClass.type.SubString(0, 256); + + children.Clear(); + PLT_XmlHelper::GetChildren(entry, children, "artist", didl_namespace_upnp); + m_People.artists.FromDidl(children); + + children.Clear(); + PLT_XmlHelper::GetChildren(entry, children, "author", didl_namespace_upnp); + m_People.authors.FromDidl(children); + + children.Clear(); + PLT_XmlHelper::GetChildren(entry, children, "actor", didl_namespace_upnp); + m_People.actors.FromDidl(children); + + children.Clear(); + PLT_XmlHelper::GetChildren(entry, children, "director", didl_namespace_upnp); + m_People.directors.FromDidl(children); + + children.Clear(); + PLT_XmlHelper::GetChildren(entry, children, "publisher", didl_namespace_dc); + for (NPT_Cardinal i=0; i<children.GetItemCount(); i++) { + if (children[i]->GetText()) { + m_People.publisher.Add(*children[i]->GetText()); + } + } + + PLT_XmlHelper::GetChildText(entry, "album", m_Affiliation.album, didl_namespace_upnp, 256); + PLT_XmlHelper::GetChildText(entry, "programTitle", m_Recorded.program_title, didl_namespace_upnp); + PLT_XmlHelper::GetChildText(entry, "seriesTitle", m_Recorded.series_title, didl_namespace_upnp); + PLT_XmlHelper::GetChildText(entry, "episodeNumber", str, didl_namespace_upnp); + NPT_UInt32 value; + if (NPT_FAILED(str.ToInteger(value))) value = 0; + m_Recorded.episode_number = value; + PLT_XmlHelper::GetChildText(entry, "episodeCount", str, didl_namespace_upnp); + if (NPT_FAILED(str.ToInteger(value))) value = 0; + m_Recorded.episode_count = value; + PLT_XmlHelper::GetChildText(entry, "episodeSeason", str, didl_namespace_upnp); + if (NPT_FAILED(str.ToInteger(value))) value = -1; + m_Recorded.episode_season = value; + + children.Clear(); + PLT_XmlHelper::GetChildren(entry, children, "genre", didl_namespace_upnp); + for (NPT_Cardinal i=0; i<children.GetItemCount(); i++) { + if (children[i]->GetText()) { + m_Affiliation.genres.Add(children[i]->GetText()->SubString(0, 256)); + } + } + + PLT_XmlHelper::GetChildText(entry, "description", m_Description.description, didl_namespace_dc); + PLT_XmlHelper::GetChildText(entry, "longDescription", m_Description.long_description, didl_namespace_upnp); + PLT_XmlHelper::GetChildText(entry, "icon", m_Description.icon_uri, didl_namespace_upnp); + PLT_XmlHelper::GetChildText(entry, "rating", m_Description.rating, didl_namespace_upnp); + PLT_XmlHelper::GetChildText(entry, "toc", m_MiscInfo.toc, didl_namespace_upnp); + + // album arts + children.Clear(); + PLT_XmlHelper::GetChildren(entry, children, "albumArtURI", didl_namespace_upnp); + for (NPT_Cardinal i=0; i<children.GetItemCount(); i++) { + if (children[i]->GetText()) { + PLT_AlbumArtInfo info; + info.uri = children[i]->GetText()->SubString(0, 1024); + PLT_XmlHelper::GetAttribute(children[i], "profileID", info.dlna_profile, didl_namespace_dlna); + m_ExtraInfo.album_arts.Add(info); + } + } + + PLT_XmlHelper::GetChildText(entry, "originalTrackNumber", str, didl_namespace_upnp); + if (NPT_FAILED(str.ToInteger(value))) value = 0; + m_MiscInfo.original_track_number = value; + + PLT_XmlHelper::GetChildText(entry, "lastPlaybackPosition", str, didl_namespace_upnp); + if (NPT_FAILED(PLT_Didl::ParseTimeStamp(str, value))) { + // fall back to raw integer parsing + if (NPT_FAILED(str.ToInteger(value))) value = 0; + } + m_MiscInfo.last_position = value; + + PLT_XmlHelper::GetChildText(entry, "lastPlaybackTime", m_MiscInfo.last_time, didl_namespace_upnp, 256); + NPT_String parsed_last_time; + for (int format=0; format<=NPT_DateTime::FORMAT_RFC_1036; format++) { + NPT_DateTime date; + if (NPT_SUCCEEDED(date.FromString(m_MiscInfo.last_time, (NPT_DateTime::Format)format))) { + parsed_last_time = date.ToString((NPT_DateTime::Format)format); + break; + } + } + m_MiscInfo.last_time = parsed_last_time; + + PLT_XmlHelper::GetChildText(entry, "playbackCount", str, didl_namespace_upnp); + if (NPT_FAILED(str.ToInteger(value))) value = -1; + m_MiscInfo.play_count = value; + + children.Clear(); + PLT_XmlHelper::GetChildren(entry, children, "res"); + for (NPT_Cardinal i=0; i<children.GetItemCount(); i++) { + PLT_MediaItemResource resource; + + // extract url + if (children[i]->GetText() == NULL) { + NPT_LOG_WARNING_1("No resource text found in: %s", (const char*)PLT_XmlHelper::Serialize(*children[i])); + } else { + resource.m_Uri = children[i]->GetText()->SubString(0, 1024); + + // basic uri validation, ignoring scheme (could be rtsp) + NPT_HttpUrl url(resource.m_Uri, true); + if (!url.IsValid()) { + NPT_LOG_WARNING_1("Invalid resource uri: %s", (const char*)resource.m_Uri); + continue; + } + } + + // extract protocol info + NPT_String protocol_info; + res = PLT_XmlHelper::GetAttribute(children[i], "protocolInfo", protocol_info, "", 256); + if (NPT_FAILED(res)) { + NPT_LOG_WARNING_1("No protocol info found in: %s", (const char*)PLT_XmlHelper::Serialize(*children[i])); + } else { + resource.m_ProtocolInfo = PLT_ProtocolInfo(protocol_info); + if (!resource.m_ProtocolInfo.IsValid()) { + NPT_LOG_WARNING_1("Invalid resource protocol info: %s", (const char*)protocol_info); + } + } + + // extract known attributes + PLT_XmlHelper::GetAttribute(children[i], "protection", resource.m_Protection, "", 256); + PLT_XmlHelper::GetAttribute(children[i], "resolution", resource.m_Resolution, "", 256); + + if (NPT_SUCCEEDED(PLT_XmlHelper::GetAttribute(children[i], "size", str, "", 256))) { + if (NPT_FAILED(str.ToInteger64(resource.m_Size))) resource.m_Size = (NPT_Size)-1; + } + + if (NPT_SUCCEEDED(PLT_XmlHelper::GetAttribute(children[i], "duration", str, "", 256))) { + if (NPT_FAILED(PLT_Didl::ParseTimeStamp(str, resource.m_Duration))) { + // if error while converting, ignore and set to -1 to indicate we don't know the duration + resource.m_Duration = (NPT_UInt32)-1; + PLT_XmlHelper::RemoveAttribute(children[i], "duration"); + } else { + // DLNA: reformat duration in case it was not compliant + str = PLT_Didl::FormatTimeStamp(resource.m_Duration); + PLT_XmlHelper::SetAttribute(children[i], "duration", str); + } + } + m_Resources.Add(resource); + } + + PLT_XmlHelper::GetChildText(entry, "lastPlayerState", m_XbmcInfo.last_playerstate, didl_namespace_xbmc, 2048); + + PLT_XmlHelper::GetChildText(entry, "dateadded", m_XbmcInfo.date_added, didl_namespace_xbmc, 256); + // parse date and make sure it's valid + for (int format=0; format<=NPT_DateTime::FORMAT_RFC_1036; format++) { + NPT_DateTime date; + if (NPT_SUCCEEDED(date.FromString(m_XbmcInfo.date_added, (NPT_DateTime::Format)format))) { + parsed_date = date.ToString((NPT_DateTime::Format)format); + break; + } + } + m_XbmcInfo.date_added = parsed_date; + + PLT_XmlHelper::GetChildText(entry, "rating", str, didl_namespace_xbmc); + NPT_Float floatValue; + if (NPT_FAILED(str.ToFloat(floatValue))) floatValue = 0.0; + m_XbmcInfo.rating = floatValue; + + PLT_XmlHelper::GetChildText(entry, "votes", str, didl_namespace_xbmc, 256); + NPT_Int32 intValue; + if (NPT_FAILED(str.ToInteger(intValue))) intValue = 0; + m_XbmcInfo.votes = intValue; + + children.Clear(); + PLT_XmlHelper::GetChildren(entry, children, "artwork", didl_namespace_xbmc); + m_XbmcInfo.artwork.FromDidl(children); + + PLT_XmlHelper::GetChildText(entry, "uniqueidentifier", m_XbmcInfo.unique_identifier, didl_namespace_xbmc, 256); + + // re serialize the entry didl as a we might need to pass it to a renderer + // we may have modified the tree to "fix" issues, so as not to break a renderer + // (don't write xml prefix as this didl could be part of a larger document) + //res = PLT_XmlHelper::Serialize(*entry, xml, false); + m_Didl = ""; + res = ToDidl(PLT_FILTER_MASK_ALL, m_Didl); + NPT_CHECK_SEVERE(res); + + m_Didl = didl_header + m_Didl + didl_footer; + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| PLT_MediaObjectList::PLT_MediaObjectList ++---------------------------------------------------------------------*/ +PLT_MediaObjectList::PLT_MediaObjectList() +{ +} + +/*---------------------------------------------------------------------- +| PLT_MediaObjectList::~PLT_MediaObjectList ++---------------------------------------------------------------------*/ +PLT_MediaObjectList::~PLT_MediaObjectList() +{ + Apply(NPT_ObjectDeleter<PLT_MediaObject>()); +} + +/*---------------------------------------------------------------------- +| PLT_MediaItem::PLT_MediaItem ++---------------------------------------------------------------------*/ +PLT_MediaItem::PLT_MediaItem() +{ + Reset(); +} + +/*---------------------------------------------------------------------- +| PLT_MediaItem::~PLT_MediaItem ++---------------------------------------------------------------------*/ +PLT_MediaItem::~PLT_MediaItem() +{ +} + +/*---------------------------------------------------------------------- +| PLT_MediaItem::ToDidl ++---------------------------------------------------------------------*/ +NPT_Result +PLT_MediaItem::ToDidl(const NPT_String& filter, NPT_String& didl) +{ + return PLT_MediaObject::ToDidl(filter, didl); +} + +/*---------------------------------------------------------------------- +| PLT_MediaItem::ToDidl ++---------------------------------------------------------------------*/ +NPT_Result +PLT_MediaItem::ToDidl(NPT_UInt64 mask, NPT_String& didl) +{ + didl += "<item id=\""; + + PLT_Didl::AppendXmlEscape(didl, m_ObjectID); + didl += "\" parentID=\""; + PLT_Didl::AppendXmlEscape(didl, m_ParentID); + + if ((mask & PLT_FILTER_MASK_REFID) && !m_ReferenceID.IsEmpty()) { + didl += "\" refID=\""; + PLT_Didl::AppendXmlEscape(didl, m_ReferenceID); + } + + didl += "\" restricted=\""; + didl += m_Restricted?"1\"":"0\""; + + didl += ">"; + + NPT_CHECK_SEVERE(PLT_MediaObject::ToDidl(mask, didl)); + + /* close tag */ + didl += "</item>"; + + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| PLT_MediaItem::FromDidl ++---------------------------------------------------------------------*/ +NPT_Result +PLT_MediaItem::FromDidl(NPT_XmlElementNode* entry) +{ + /* reset first */ + Reset(); + + if (entry->GetTag().Compare("item", true) != 0) { + NPT_CHECK_SEVERE(NPT_ERROR_INTERNAL); + } + + NPT_Result result = PLT_MediaObject::FromDidl(entry); + + return result; +} + +/*---------------------------------------------------------------------- +| PLT_MediaContainer::PLT_MediaContainer ++---------------------------------------------------------------------*/ +PLT_MediaContainer::PLT_MediaContainer() +{ + Reset(); +} + +/*---------------------------------------------------------------------- +| PLT_MediaContainer::~PLT_MediaContainer ++---------------------------------------------------------------------*/ +PLT_MediaContainer::~PLT_MediaContainer(void) +{ +} + +/*---------------------------------------------------------------------- +| PLT_MediaContainer::Reset ++---------------------------------------------------------------------*/ +NPT_Result +PLT_MediaContainer::Reset() +{ + m_SearchClasses.Clear(); + m_Searchable = false; + m_ChildrenCount = -1; + m_ContainerUpdateID = 0; + + return PLT_MediaObject::Reset(); +} + +/*---------------------------------------------------------------------- +| PLT_MediaContainer::ToDidl ++---------------------------------------------------------------------*/ +NPT_Result +PLT_MediaContainer::ToDidl(const NPT_String& filter, NPT_String& didl) +{ + return PLT_MediaObject::ToDidl(filter, didl); +} + +/*---------------------------------------------------------------------- +| PLT_MediaContainer::ToDidl ++---------------------------------------------------------------------*/ +NPT_Result +PLT_MediaContainer::ToDidl(NPT_UInt64 mask, NPT_String& didl) +{ + // container id property + didl += "<container id=\""; + PLT_Didl::AppendXmlEscape(didl, m_ObjectID); + + // parent id property + didl += "\" parentID=\""; + PLT_Didl::AppendXmlEscape(didl, m_ParentID); + + // ref id + if ((mask & PLT_FILTER_MASK_REFID) && !m_ReferenceID.IsEmpty()) { + didl += "\" refID=\""; + PLT_Didl::AppendXmlEscape(didl, m_ReferenceID); + } + + // restricted property + didl += "\" restricted=\""; + didl += m_Restricted?"1\"":"0\""; + + // searchable property + if (mask & PLT_FILTER_MASK_SEARCHABLE) { + didl += " searchable=\""; + didl += m_Searchable?"1\"":"0\""; + } + + // childcount property + if ((mask & PLT_FILTER_MASK_CHILDCOUNT) && m_ChildrenCount != -1) { + didl += " childCount=\""; + didl += NPT_String::FromInteger(m_ChildrenCount); + didl += "\""; + } + + didl += ">"; + + if ((mask & PLT_FILTER_MASK_SEARCHCLASS) && m_SearchClasses.GetItemCount()) { + NPT_List<PLT_SearchClass>::Iterator search_class = m_SearchClasses.GetFirstItem(); + while (search_class) { + didl += "<upnp:searchClass includeDerived=\""; + didl += (*search_class).include_derived?"1\"":"0\""; + + // frienly name is any + if (!(*search_class).friendly_name.IsEmpty()) { + didl += " name=\"" + (*search_class).friendly_name + "\""; + } + didl += ">"; + didl += (*search_class).type; + didl += "</upnp:searchClass>"; + + ++search_class; + } + } + + NPT_CHECK_SEVERE(PLT_MediaObject::ToDidl(mask, didl)); + + /* close tag */ + didl += "</container>"; + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| PLT_MediaContainer::FromDidl ++---------------------------------------------------------------------*/ +NPT_Result +PLT_MediaContainer::FromDidl(NPT_XmlElementNode* entry) +{ + NPT_String str; + + /* reset first */ + Reset(); + + // check entry type + if (entry->GetTag().Compare("Container", true) != 0) + return NPT_ERROR_INTERNAL; + + // check if item is searchable (is default true?) + if (NPT_SUCCEEDED(PLT_XmlHelper::GetAttribute(entry, "searchable", str, "", 5))) { + m_Searchable = PLT_Service::IsTrue(str); + } + + // look for childCount + if (NPT_SUCCEEDED(PLT_XmlHelper::GetAttribute(entry, "childCount", str, "", 256))) { + NPT_UInt32 count; + NPT_CHECK_SEVERE(str.ToInteger(count)); + m_ChildrenCount = count; + } + + // upnp:searchClass child elements + NPT_Array<NPT_XmlElementNode*> children; + PLT_XmlHelper::GetChildren(entry, children, "upnp:searchClass"); + + for (NPT_Cardinal i=0; i<children.GetItemCount(); i++) { + PLT_SearchClass search_class; + + // extract url + if (children[i]->GetText() == NULL) { + NPT_LOG_WARNING_1("No searchClass text found in: %s", + (const char*)PLT_XmlHelper::Serialize(*children[i])); + continue; + } + + // DLNA 7.3.17.4 + search_class.type = children[i]->GetText()->SubString(0, 256); + + // extract optional attribute name + PLT_XmlHelper::GetAttribute(children[i], "name", search_class.friendly_name); + + // includeDerived property + if (NPT_FAILED(PLT_XmlHelper::GetAttribute(children[i], "includeDerived", str))) { + NPT_LOG_WARNING_1("No required attribute searchClass@includeDerived found in: %s", + (const char*)PLT_XmlHelper::Serialize(*children[i])); + continue; + } + + search_class.include_derived = PLT_Service::IsTrue(str); + m_SearchClasses.Add(search_class); + } + + return PLT_MediaObject::FromDidl(entry); +} |