xbmc:dateadded to pass dateadded with items and not need to stat upnp resources for that
xbmc:rating for number-based rating
xbmc:votes for string-based votes number
xbmc:artwork for a type-url-based mapping of artwork
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:dlna=\"urn:schemas-dlna-org:metadata-1-0/\""
+ " 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
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_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;
}
if (next_comma < 0) {
#define PLT_FILTER_MASK_EPISODE_COUNT NPT_UINT64_C(0x0000001000000000)
#define PLT_FILTER_MASK_EPISODE_SEASON NPT_UINT64_C(0x0000002000000000)
+#define PLT_FILTER_MASK_XBMC_DATEADDED NPT_UINT64_C(0x0000100000000000)
+#define PLT_FILTER_MASK_XBMC_RATING NPT_UINT64_C(0x0000200000000000)
+#define PLT_FILTER_MASK_XBMC_VOTES NPT_UINT64_C(0x0000300000000000)
+#define PLT_FILTER_MASK_XBMC_ARTWORK NPT_UINT64_C(0x0000400000000000)
+
#define PLT_FILTER_FIELD_TITLE "dc:title"
#define PLT_FILTER_FIELD_CREATOR "dc:creator"
#define PLT_FILTER_FIELD_DATE "dc:date"
#define PLT_FILTER_FIELD_EPISODE_COUNT "upnp:episodeCount"
#define PLT_FILTER_FIELD_EPISODE_SEASON "upnp:episodeSeason"
+#define PLT_FILTER_FIELD_XBMC_DATEADDED "xbmc:dateadded"
+#define PLT_FILTER_FIELD_XBMC_RATING "xbmc:rating"
+#define PLT_FILTER_FIELD_XBMC_VOTES "xbmc:votes"
+#define PLT_FILTER_FIELD_XBMC_ARTWORK "xbmc:artwork"
+
extern const char* didl_header;
extern const char* didl_footer;
extern const char* didl_namespace_dc;
extern const char* didl_namespace_upnp;
extern const char* didl_namespace_dlna;
+extern const char* didl_namespace_xbmc;
/*----------------------------------------------------------------------
| PLT_Didl
}
/*----------------------------------------------------------------------
+| 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_Resources.Clear();
+ m_XbmcInfo.date_added = "";
+ m_XbmcInfo.rating = 0.0f;
+ m_XbmcInfo.votes = "";
+ m_XbmcInfo.artwork.Clear();
+
m_Didl = "";
return NPT_SUCCESS;
}
}
+ // 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.IsEmpty()) {
+ didl += "<xbmc:votes>";
+ PLT_Didl::AppendXmlEscape(didl, m_XbmcInfo.votes);
+ didl += "</xbmc:votes>";
+ }
+
+ // xbmc artwork
+ if (mask & PLT_FILTER_MASK_XBMC_ARTWORK) {
+ m_XbmcInfo.artwork.ToDidl(didl, "artwork");
+ }
+
// class is required
didl += "<upnp:class";
if (!m_ObjectClass.friendly_name.IsEmpty()) {
m_Resources.Add(resource);
}
+ 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", m_XbmcInfo.votes, didl_namespace_xbmc, 256);
+
+ children.Clear();
+ PLT_XmlHelper::GetChildren(entry, children, "artwork", didl_namespace_xbmc);
+ m_XbmcInfo.artwork.FromDidl(children);
+
// 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)
NPT_UInt32 episode_season;
} PLT_RecordedInfo;
+typedef struct {
+ NPT_String type;
+ NPT_String url;
+} PLT_Artwork;
+
+class PLT_Artworks : public NPT_List<PLT_Artwork>
+{
+public:
+ NPT_Result Add(const NPT_String& type, const NPT_String& url);
+ NPT_Result ToDidl(NPT_String& didl, const NPT_String& tag);
+ NPT_Result FromDidl(const NPT_Array<NPT_XmlElementNode*>& nodes);
+};
+
+typedef struct {
+ NPT_String date_added;
+ NPT_Float rating;
+ NPT_String votes;
+ PLT_Artworks artwork;
+} PLT_XbmcInfo;
+
/*----------------------------------------------------------------------
| PLT_MediaItemResource
+---------------------------------------------------------------------*/
/* resources related */
NPT_Array<PLT_MediaItemResource> m_Resources;
+ /* XBMC specific */
+ PLT_XbmcInfo m_XbmcInfo;
+
/* original DIDL for Control Points to pass to a renderer when invoking SetAVTransportURI */
NPT_String m_Didl;
};
NPT_Int32 index,
NPT_Int32 count,
bool browse_metadata = false,
- const char* filter = "dc:date,dc:description,upnp:longDescription,upnp:genre,res,res@duration,res@size,upnp:albumArtURI,upnp:rating,upnp:lastPlaybackPosition,upnp:lastPlaybackTime,upnp:playbackCount,upnp:originalTrackNumber,upnp:episodeNumber,upnp:programTitle,upnp:seriesTitle,upnp:album,upnp:artist,upnp:author,upnp:director,dc:publisher,searchable,childCount,dc:title,dc:creator,upnp:actor,res@resolution,upnp:episodeCount,upnp:episodeSeason", // explicitely specify res otherwise WMP won't return a URL!
+ const char* filter = "dc:date,dc:description,upnp:longDescription,upnp:genre,res,res@duration,res@size,upnp:albumArtURI,upnp:rating,upnp:lastPlaybackPosition,upnp:lastPlaybackTime,upnp:playbackCount,upnp:originalTrackNumber,upnp:episodeNumber,upnp:programTitle,upnp:seriesTitle,upnp:album,upnp:artist,upnp:author,upnp:director,dc:publisher,searchable,childCount,dc:title,dc:creator,upnp:actor,res@resolution,upnp:episodeCount,upnp:episodeSeason,xbmc:dateadded,xbmc:rating,xbmc:votes,xbmc:artwork", // explicitely specify res otherwise WMP won't return a URL!
const char* sort = "");
private:
NPT_Result Find(const char* ip, PLT_DeviceDataReference& device);