summaryrefslogtreecommitdiffstats
path: root/lib/libUPnP/Platinum/Source/Devices/MediaServer/PltMediaItem.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libUPnP/Platinum/Source/Devices/MediaServer/PltMediaItem.cpp')
-rw-r--r--lib/libUPnP/Platinum/Source/Devices/MediaServer/PltMediaItem.cpp1128
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);
+}