1 /*****************************************************************
3 | Platinum - AV Media Item
5 | Copyright (c) 2004-2010, Plutinosoft, LLC.
7 | http://www.plutinosoft.com
9 | This program is free software; you can redistribute it and/or
10 | modify it under the terms of the GNU General Public License
11 | as published by the Free Software Foundation; either version 2
12 | of the License, or (at your option) any later version.
14 | OEMs, ISVs, VARs and other distributors that combine and
15 | distribute commercially licensed software with Platinum software
16 | and do not wish to distribute the source code for the commercially
17 | licensed software under version 2, or (at your option) any later
18 | version, of the GNU General Public License (the "GPL") must enter
19 | into a commercial license agreement with Plutinosoft, LLC.
20 | licensing@plutinosoft.com
22 | This program is distributed in the hope that it will be useful,
23 | but WITHOUT ANY WARRANTY; without even the implied warranty of
24 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 | GNU General Public License for more details.
27 | You should have received a copy of the GNU General Public License
28 | along with this program; see the file LICENSE.txt. If not, write to
29 | the Free Software Foundation, Inc.,
30 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
31 | http://www.gnu.org/licenses/gpl-2.0.html
33 ****************************************************************/
35 /*----------------------------------------------------------------------
37 +---------------------------------------------------------------------*/
38 #include "PltMediaItem.h"
39 #include "PltMediaServer.h"
41 #include "PltUtilities.h"
42 #include "PltService.h"
43 #include "PltMimeType.h"
45 NPT_SET_LOCAL_LOGGER("platinum.media.server.item")
47 /*----------------------------------------------------------------------
49 +---------------------------------------------------------------------*/
50 NPT_DEFINE_DYNAMIC_CAST_ANCHOR(PLT_MediaObject)
51 NPT_DEFINE_DYNAMIC_CAST_ANCHOR(PLT_MediaItem)
52 NPT_DEFINE_DYNAMIC_CAST_ANCHOR(PLT_MediaContainer)
54 /*----------------------------------------------------------------------
55 | PLT_PersonRoles::AddPerson
56 +---------------------------------------------------------------------*/
58 PLT_PersonRoles::Add(const NPT_String& name, const NPT_String& role /* = "" */)
60 PLT_PersonRole person;
64 return NPT_List<PLT_PersonRole>::Add(person);
67 /*----------------------------------------------------------------------
68 | PLT_PersonRoles::ToDidl
69 +---------------------------------------------------------------------*/
71 PLT_PersonRoles::ToDidl(NPT_String& didl, const NPT_String& tag)
74 for (NPT_List<PLT_PersonRole>::Iterator it =
75 NPT_List<PLT_PersonRole>::GetFirstItem(); it; it++) {
76 // if there's an empty artist, allow it only if there's nothing else
77 if (it->name.IsEmpty() && m_ItemCount>1 && !tmp.IsEmpty()) continue;
79 tmp += "<upnp:" + tag;
80 if (!it->role.IsEmpty()) {
82 PLT_Didl::AppendXmlEscape(tmp, it->role);
86 PLT_Didl::AppendXmlEscape(tmp, it->name);
87 tmp += "</upnp:" + tag + ">";
94 /*----------------------------------------------------------------------
95 | PLT_PersonRoles::ToDidl
96 +---------------------------------------------------------------------*/
98 PLT_PersonRoles::FromDidl(const NPT_Array<NPT_XmlElementNode*>& nodes)
100 for (NPT_Cardinal i=0; i<nodes.GetItemCount(); i++) {
101 PLT_PersonRole person;
102 const NPT_String* name = nodes[i]->GetText();
103 const NPT_String* role = nodes[i]->GetAttribute("role");
105 if (name) person.name = name->SubString(0, 1024);
106 if (role) person.role = role->SubString(0, 1024);
107 NPT_CHECK(NPT_List<PLT_PersonRole>::Add(person));
112 /*----------------------------------------------------------------------
113 | PLT_MediaItemResource::PLT_MediaItemResource
114 +---------------------------------------------------------------------*/
115 PLT_MediaItemResource::PLT_MediaItemResource()
118 m_ProtocolInfo = PLT_ProtocolInfo();
119 m_Duration = (NPT_UInt32)-1;
120 m_Size = (NPT_LargeSize)-1;
122 m_Bitrate = (NPT_UInt32)-1;
123 m_BitsPerSample = (NPT_UInt32)-1;
124 m_SampleFrequency = (NPT_UInt32)-1;
125 m_NbAudioChannels = (NPT_UInt32)-1;
127 m_ColorDepth = (NPT_UInt32)-1;
130 /*----------------------------------------------------------------------
131 | PLT_MediaObject::GetUPnPClass
132 +---------------------------------------------------------------------*/
134 PLT_MediaObject::GetUPnPClass(const char* filename,
135 const PLT_HttpRequestContext* context /* = NULL */)
137 NPT_COMPILER_UNUSED(context);
139 const char* ret = NULL;
140 NPT_String mime_type = PLT_MimeType::GetMimeType(filename, context);
142 if (mime_type.StartsWith("audio")) {
143 ret = "object.item.audioItem.musicTrack";
144 } else if (mime_type.StartsWith("video")) {
145 ret = "object.item.videoItem"; //Note: 360 wants "object.item.videoItem" and not "object.item.videoItem.Movie"
146 } else if (mime_type.StartsWith("image")) {
147 ret = "object.item.imageItem.photo";
155 /*----------------------------------------------------------------------
156 | PLT_MediaObject::Reset
157 +---------------------------------------------------------------------*/
159 PLT_MediaObject::Reset()
161 m_ObjectClass.type = "";
162 m_ObjectClass.friendly_name = "";
171 m_People.actors.Clear();
172 m_People.artists.Clear();
173 m_People.authors.Clear();
174 m_People.directors.Clear();
176 m_Affiliation.album = "";
177 m_Affiliation.genres.Clear();
178 m_Affiliation.playlist = "";
180 m_Description.description = "";
181 m_Description.long_description = "";
182 m_Description.icon_uri = "";
183 m_ExtraInfo.album_arts.Clear();
184 m_ExtraInfo.artist_discography_uri = "";
186 m_MiscInfo.original_track_number = 0;
187 m_MiscInfo.last_position = 0;
188 m_MiscInfo.last_time = "";
189 m_MiscInfo.play_count = -1;
190 m_MiscInfo.dvdregioncode = 0;
192 m_MiscInfo.user_annotation = "";
194 m_Recorded.program_title = "";
195 m_Recorded.series_title = "";
196 m_Recorded.episode_number = 0;
205 /*----------------------------------------------------------------------
206 | PLT_MediaObject::ToDidl
207 +---------------------------------------------------------------------*/
209 PLT_MediaObject::ToDidl(const NPT_String& filter, NPT_String& didl)
211 return ToDidl(PLT_Didl::ConvertFilterToMask(filter), didl);
214 /*----------------------------------------------------------------------
215 | PLT_MediaObject::ToDidl
216 +---------------------------------------------------------------------*/
218 PLT_MediaObject::ToDidl(NPT_UInt64 mask, NPT_String& didl)
221 didl += "<dc:title>";
222 PLT_Didl::AppendXmlEscape(didl, m_Title);
223 didl += "</dc:title>";
226 if (mask & PLT_FILTER_MASK_CREATOR) {
227 didl += "<dc:creator>";
228 if (m_Creator.IsEmpty()) m_Creator = "Unknown";
229 PLT_Didl::AppendXmlEscape(didl, m_Creator);
230 didl += "</dc:creator>";
234 if ((mask & PLT_FILTER_MASK_DATE) && !m_Date.IsEmpty()) {
236 PLT_Didl::AppendXmlEscape(didl, m_Date);
237 didl += "</dc:date>";
241 if (mask & PLT_FILTER_MASK_ARTIST) {
242 // force an empty artist just in case (not DLNA Compliant though)
243 //if (m_People.artists.GetItemCount() == 0) m_People.artists.Add("");
244 m_People.artists.ToDidl(didl, "artist");
248 if (mask & PLT_FILTER_MASK_ACTOR) {
249 m_People.actors.ToDidl(didl, "actor");
253 if (mask & PLT_FILTER_MASK_AUTHOR) {
254 m_People.authors.ToDidl(didl, "author");
258 if (mask & PLT_FILTER_MASK_DIRECTOR) {
259 m_People.directors.ToDidl(didl, "director");
263 if ((mask & PLT_FILTER_MASK_ALBUM) && !m_Affiliation.album.IsEmpty()) {
264 didl += "<upnp:album>";
265 PLT_Didl::AppendXmlEscape(didl, m_Affiliation.album);
266 didl += "</upnp:album>";
270 if (mask & PLT_FILTER_MASK_GENRE) {
272 if (m_Affiliation.genres.GetItemCount() == 0)
273 m_Affiliation.genres.Add("Unknown");
275 for (NPT_List<NPT_String>::Iterator it =
276 m_Affiliation.genres.GetFirstItem(); it; ++it) {
277 didl += "<upnp:genre>";
278 PLT_Didl::AppendXmlEscape(didl, (*it));
279 didl += "</upnp:genre>";
284 if ((mask & PLT_FILTER_MASK_ALBUMARTURI) && m_ExtraInfo.album_arts.GetItemCount()) {
285 for (NPT_List<PLT_AlbumArtInfo>::Iterator iter = m_ExtraInfo.album_arts.GetFirstItem();
288 didl += "<upnp:albumArtURI";
289 if (!(*iter).dlna_profile.IsEmpty()) {
290 didl += " dlna:profileID=\"";
291 PLT_Didl::AppendXmlEscape(didl, (*iter).dlna_profile);
295 PLT_Didl::AppendXmlEscape(didl, (*iter).uri);
296 didl += "</upnp:albumArtURI>";
301 if ((mask & PLT_FILTER_MASK_DESCRIPTION) && !m_Description.description.IsEmpty()) {
302 didl += "<dc:description>";
303 PLT_Didl::AppendXmlEscape(didl, m_Description.description);
304 didl += "</dc:description>";
308 if ((mask & PLT_FILTER_MASK_LONGDESCRIPTION) && !m_Description.long_description.IsEmpty()) {
309 didl += "<upnp:longDescription>";
310 PLT_Didl::AppendXmlEscape(didl, m_Description.long_description);
311 didl += "</upnp:longDescription>";
315 if ((mask & PLT_FILTER_MASK_ICON) && !m_Description.icon_uri.IsEmpty()) {
316 didl += "<upnp:icon>";
317 PLT_Didl::AppendXmlEscape(didl, m_Description.icon_uri);
318 didl += "</upnp:icon>";
322 if ((mask & PLT_FILTER_MASK_RATING) && !m_Description.rating.IsEmpty()) {
323 didl += "<upnp:rating>";
324 PLT_Didl::AppendXmlEscape(didl, m_Description.rating);
325 didl += "</upnp:rating>";
328 // original track number
329 if ((mask & PLT_FILTER_MASK_ORIGINALTRACK) && m_MiscInfo.original_track_number > 0) {
330 didl += "<upnp:originalTrackNumber>";
331 didl += NPT_String::FromInteger(m_MiscInfo.original_track_number);
332 didl += "</upnp:originalTrackNumber>";
335 // last playback position
336 if (mask & PLT_FILTER_MASK_LASTPOSITION && m_MiscInfo.last_position > 0) {
337 didl += "<upnp:lastPlaybackPosition>";
338 didl += NPT_String::FromInteger(m_MiscInfo.last_position);
339 didl += "</upnp:lastPlaybackPosition>";
342 // last playback datetime
343 if (mask & PLT_FILTER_MASK_LASTPLAYBACK && !m_MiscInfo.last_time.IsEmpty()) {
344 didl += "<upnp:lastPlaybackTime>";
345 PLT_Didl::AppendXmlEscape(didl, m_MiscInfo.last_time);
346 didl += "</upnp:lastPlaybackTime>";
350 if (mask & PLT_FILTER_MASK_PLAYCOUNT && m_MiscInfo.play_count > -1) {
351 didl += "<upnp:playbackCount>";
352 didl += NPT_String::FromInteger(m_MiscInfo.play_count);
353 didl += "</upnp:playbackCount>";
357 if (mask & PLT_FILTER_MASK_PROGRAMTITLE && !m_Recorded.program_title.IsEmpty()) {
358 didl += "<upnp:programTitle>";
359 PLT_Didl::AppendXmlEscape(didl, m_Recorded.program_title);
360 didl += "</upnp:programTitle>";
364 if ((mask & PLT_FILTER_MASK_SERIESTITLE) && !m_Recorded.series_title.IsEmpty()) {
365 didl += "<upnp:seriesTitle>";
366 PLT_Didl::AppendXmlEscape(didl, m_Recorded.series_title);
367 didl += "</upnp:seriesTitle>";
371 if ((mask & PLT_FILTER_MASK_EPISODE) && m_Recorded.episode_number > 0) {
372 didl += "<upnp:episodeNumber>";
373 didl += NPT_String::FromInteger(m_Recorded.episode_number);
374 didl += "</upnp:episodeNumber>";
377 if ((mask & PLT_FILTER_MASK_TOC) & !m_MiscInfo.toc.IsEmpty()) {
378 didl += "<upnp:toc>";
379 PLT_Didl::AppendXmlEscape(didl, m_MiscInfo.toc);
380 didl += "</upnp:toc>";
384 if (mask & PLT_FILTER_MASK_RES) {
385 for (NPT_Cardinal i=0; i<m_Resources.GetItemCount(); i++) {
388 if ((mask & PLT_FILTER_MASK_RES_DURATION) && m_Resources[i].m_Duration != (NPT_UInt32)-1) {
389 didl += " duration=\"";
390 didl += PLT_Didl::FormatTimeStamp(m_Resources[i].m_Duration);
394 if ((mask & PLT_FILTER_MASK_RES_SIZE) && m_Resources[i].m_Size != (NPT_LargeSize)-1) {
396 didl += NPT_String::FromIntegerU(m_Resources[i].m_Size);
400 if ((mask & PLT_FILTER_MASK_RES_PROTECTION) && !m_Resources[i].m_Protection.IsEmpty()) {
401 didl += " protection=\"";
402 PLT_Didl::AppendXmlEscape(didl, m_Resources[i].m_Protection);
406 if ((mask & PLT_FILTER_MASK_RES_RESOLUTION) && !m_Resources[i].m_Resolution.IsEmpty()) {
407 didl += " resolution=\"";
408 PLT_Didl::AppendXmlEscape(didl, m_Resources[i].m_Resolution);
412 if ((mask & PLT_FILTER_MASK_RES_BITRATE) && m_Resources[i].m_Bitrate != (NPT_Size)-1) {
413 didl += " bitrate=\"";
414 didl += NPT_String::FromIntegerU(m_Resources[i].m_Bitrate);
418 if ((mask & PLT_FILTER_MASK_RES_BITSPERSAMPLE) && m_Resources[i].m_BitsPerSample != (NPT_Size)-1) {
419 didl += " bitsPerSample=\"";
420 didl += NPT_String::FromIntegerU(m_Resources[i].m_BitsPerSample);
424 if ((mask & PLT_FILTER_MASK_RES_SAMPLEFREQUENCY) && m_Resources[i].m_SampleFrequency != (NPT_Size)-1) {
425 didl += " sampleFrequency=\"";
426 didl += NPT_String::FromIntegerU(m_Resources[i].m_SampleFrequency);
430 if ((mask & PLT_FILTER_MASK_RES_NRAUDIOCHANNELS) && m_Resources[i].m_NbAudioChannels != (NPT_Size)-1) {
431 didl += " nrAudioChannels=\"";
432 didl += NPT_String::FromIntegerU(m_Resources[i].m_NbAudioChannels);
436 didl += " protocolInfo=\"";
437 PLT_Didl::AppendXmlEscape(didl, m_Resources[i].m_ProtocolInfo.ToString());
439 PLT_Didl::AppendXmlEscape(didl, m_Resources[i].m_Uri);
445 didl += "<upnp:class";
446 if (!m_ObjectClass.friendly_name.IsEmpty()) {
447 didl += " name=\"" + m_ObjectClass.friendly_name+"\"";
450 PLT_Didl::AppendXmlEscape(didl, m_ObjectClass.type);
451 didl += "</upnp:class>";
456 /*----------------------------------------------------------------------
457 | PLT_MediaObject::FromDidl
458 +---------------------------------------------------------------------*/
460 PLT_MediaObject::FromDidl(NPT_XmlElementNode* entry)
463 NPT_Array<NPT_XmlElementNode*> children;
466 // check if item is restricted (is default true?)
467 if (NPT_SUCCEEDED(PLT_XmlHelper::GetAttribute(entry, "restricted", str, "", 5))) {
468 m_Restricted = PLT_Service::IsTrue(str);
471 // read non-required elements
472 PLT_XmlHelper::GetChildText(entry, "creator", m_Creator, didl_namespace_dc, 256);
473 PLT_XmlHelper::GetChildText(entry, "date", m_Date, didl_namespace_dc, 256);
475 // parse date and make sure it's valid
476 NPT_String parsed_date;
477 for (int format=0; format<=NPT_DateTime::FORMAT_RFC_1036; format++) {
479 if (NPT_SUCCEEDED(date.FromString(m_Date, (NPT_DateTime::Format)format))) {
480 parsed_date = date.ToString((NPT_DateTime::Format)format);
484 m_Date = parsed_date;
486 res = PLT_XmlHelper::GetAttribute(entry, "id", m_ObjectID);
487 NPT_CHECK_SEVERE(res);
489 res = PLT_XmlHelper::GetAttribute(entry, "parentID", m_ParentID);
490 NPT_CHECK_SEVERE(res);
492 PLT_XmlHelper::GetAttribute(entry, "refID", m_ReferenceID);
494 res = PLT_XmlHelper::GetChildText(entry, "title", m_Title, didl_namespace_dc);
495 NPT_CHECK_SEVERE(res);
497 res = PLT_XmlHelper::GetChildText(entry, "class", m_ObjectClass.type, didl_namespace_upnp);
498 NPT_CHECK_SEVERE(res);
500 // DLNA 7.3.17.3 max bytes for dc:title and upnp:class is 256 bytes
501 m_Title = m_Title.SubString(0, 256);
502 m_ObjectClass.type = m_ObjectClass.type.SubString(0, 256);
505 PLT_XmlHelper::GetChildren(entry, children, "artist", didl_namespace_upnp);
506 m_People.artists.FromDidl(children);
509 PLT_XmlHelper::GetChildren(entry, children, "author", didl_namespace_upnp);
510 m_People.authors.FromDidl(children);
513 PLT_XmlHelper::GetChildren(entry, children, "actor", didl_namespace_upnp);
514 m_People.actors.FromDidl(children);
517 PLT_XmlHelper::GetChildren(entry, children, "director", didl_namespace_upnp);
518 m_People.directors.FromDidl(children);
520 PLT_XmlHelper::GetChildText(entry, "album", m_Affiliation.album, didl_namespace_upnp, 256);
521 PLT_XmlHelper::GetChildText(entry, "programTitle", m_Recorded.program_title, didl_namespace_upnp);
522 PLT_XmlHelper::GetChildText(entry, "seriesTitle", m_Recorded.series_title, didl_namespace_upnp);
523 PLT_XmlHelper::GetChildText(entry, "episodeNumber", str, didl_namespace_upnp);
525 if (NPT_FAILED(str.ToInteger(value))) value = 0;
526 m_Recorded.episode_number = value;
529 PLT_XmlHelper::GetChildren(entry, children, "genre", didl_namespace_upnp);
530 for (NPT_Cardinal i=0; i<children.GetItemCount(); i++) {
531 if (children[i]->GetText()) {
532 m_Affiliation.genres.Add(children[i]->GetText()->SubString(0, 256));
536 PLT_XmlHelper::GetChildText(entry, "description", m_Description.description, didl_namespace_dc);
537 PLT_XmlHelper::GetChildText(entry, "longDescription", m_Description.long_description, didl_namespace_upnp);
538 PLT_XmlHelper::GetChildText(entry, "icon", m_Description.icon_uri, didl_namespace_upnp);
539 PLT_XmlHelper::GetChildText(entry, "rating", m_Description.rating, didl_namespace_upnp);
540 PLT_XmlHelper::GetChildText(entry, "toc", m_MiscInfo.toc, didl_namespace_upnp);
544 PLT_XmlHelper::GetChildren(entry, children, "albumArtURI", didl_namespace_upnp);
545 for (NPT_Cardinal i=0; i<children.GetItemCount(); i++) {
546 if (children[i]->GetText()) {
547 PLT_AlbumArtInfo info;
548 info.uri = children[i]->GetText()->SubString(0, 1024);
549 PLT_XmlHelper::GetAttribute(children[i], "profileID", info.dlna_profile, didl_namespace_dlna);
550 m_ExtraInfo.album_arts.Add(info);
554 PLT_XmlHelper::GetChildText(entry, "originalTrackNumber", str, didl_namespace_upnp);
555 if (NPT_FAILED(str.ToInteger(value))) value = 0;
556 m_MiscInfo.original_track_number = value;
558 PLT_XmlHelper::GetChildText(entry, "lastPlaybackPosition", str, didl_namespace_upnp);
559 if (NPT_FAILED(str.ToInteger(value))) value = 0;
560 m_MiscInfo.last_position = value;
562 PLT_XmlHelper::GetChildText(entry, "lastPlaybackTime", m_MiscInfo.last_time, didl_namespace_dc, 256);
563 NPT_String parsed_last_time;
564 for (int format=0; format<=NPT_DateTime::FORMAT_RFC_1036; format++) {
566 if (NPT_SUCCEEDED(date.FromString(m_MiscInfo.last_time, (NPT_DateTime::Format)format))) {
567 parsed_last_time = date.ToString((NPT_DateTime::Format)format);
571 m_MiscInfo.last_time = parsed_last_time;
573 PLT_XmlHelper::GetChildText(entry, "playbackCount", str, didl_namespace_upnp);
574 if (NPT_FAILED(str.ToInteger(value))) value = -1;
575 m_MiscInfo.play_count = value;
578 PLT_XmlHelper::GetChildren(entry, children, "res");
579 for (NPT_Cardinal i=0; i<children.GetItemCount(); i++) {
580 PLT_MediaItemResource resource;
583 if (children[i]->GetText() == NULL) {
584 NPT_LOG_WARNING_1("No resource text found in: %s", (const char*)PLT_XmlHelper::Serialize(*children[i]));
586 resource.m_Uri = children[i]->GetText()->SubString(0, 1024);
588 // basic uri validation, ignoring scheme (could be rtsp)
589 NPT_HttpUrl url(resource.m_Uri, true);
590 if (!url.IsValid()) {
591 NPT_LOG_WARNING_1("Invalid resource uri: %s", (const char*)resource.m_Uri);
596 // extract protocol info
597 NPT_String protocol_info;
598 res = PLT_XmlHelper::GetAttribute(children[i], "protocolInfo", protocol_info, "", 256);
599 if (NPT_FAILED(res)) {
600 NPT_LOG_WARNING_1("No protocol info found in: %s", (const char*)PLT_XmlHelper::Serialize(*children[i]));
602 resource.m_ProtocolInfo = PLT_ProtocolInfo(protocol_info);
603 if (!resource.m_ProtocolInfo.IsValid()) {
604 NPT_LOG_WARNING_1("Invalid resource protocol info: %s", (const char*)protocol_info);
608 // extract known attributes
609 PLT_XmlHelper::GetAttribute(children[i], "protection", resource.m_Protection, "", 256);
610 PLT_XmlHelper::GetAttribute(children[i], "resolution", resource.m_Resolution, "", 256);
612 if (NPT_SUCCEEDED(PLT_XmlHelper::GetAttribute(children[i], "size", str, "", 256))) {
613 if (NPT_FAILED(str.ToInteger64(resource.m_Size))) resource.m_Size = (NPT_Size)-1;
616 if (NPT_SUCCEEDED(PLT_XmlHelper::GetAttribute(children[i], "duration", str, "", 256))) {
617 if (NPT_FAILED(PLT_Didl::ParseTimeStamp(str, resource.m_Duration))) {
618 // if error while converting, ignore and set to -1 to indicate we don't know the duration
619 resource.m_Duration = (NPT_UInt32)-1;
620 PLT_XmlHelper::RemoveAttribute(children[i], "duration");
622 // DLNA: reformat duration in case it was not compliant
623 str = PLT_Didl::FormatTimeStamp(resource.m_Duration);
624 PLT_XmlHelper::SetAttribute(children[i], "duration", str);
627 m_Resources.Add(resource);
630 // re serialize the entry didl as a we might need to pass it to a renderer
631 // we may have modified the tree to "fix" issues, so as not to break a renderer
632 // (don't write xml prefix as this didl could be part of a larger document)
633 //res = PLT_XmlHelper::Serialize(*entry, xml, false);
635 res = ToDidl(PLT_FILTER_MASK_ALL, m_Didl);
636 NPT_CHECK_SEVERE(res);
638 m_Didl = didl_header + m_Didl + didl_footer;
642 /*----------------------------------------------------------------------
643 | PLT_MediaObjectList::PLT_MediaObjectList
644 +---------------------------------------------------------------------*/
645 PLT_MediaObjectList::PLT_MediaObjectList()
649 /*----------------------------------------------------------------------
650 | PLT_MediaObjectList::~PLT_MediaObjectList
651 +---------------------------------------------------------------------*/
652 PLT_MediaObjectList::~PLT_MediaObjectList()
654 Apply(NPT_ObjectDeleter<PLT_MediaObject>());
657 /*----------------------------------------------------------------------
658 | PLT_MediaItem::PLT_MediaItem
659 +---------------------------------------------------------------------*/
660 PLT_MediaItem::PLT_MediaItem()
665 /*----------------------------------------------------------------------
666 | PLT_MediaItem::~PLT_MediaItem
667 +---------------------------------------------------------------------*/
668 PLT_MediaItem::~PLT_MediaItem()
672 /*----------------------------------------------------------------------
673 | PLT_MediaItem::ToDidl
674 +---------------------------------------------------------------------*/
676 PLT_MediaItem::ToDidl(const NPT_String& filter, NPT_String& didl)
678 return PLT_MediaObject::ToDidl(filter, didl);
681 /*----------------------------------------------------------------------
682 | PLT_MediaItem::ToDidl
683 +---------------------------------------------------------------------*/
685 PLT_MediaItem::ToDidl(NPT_UInt64 mask, NPT_String& didl)
687 didl += "<item id=\"";
689 PLT_Didl::AppendXmlEscape(didl, m_ObjectID);
690 didl += "\" parentID=\"";
691 PLT_Didl::AppendXmlEscape(didl, m_ParentID);
693 if ((mask & PLT_FILTER_MASK_REFID) && !m_ReferenceID.IsEmpty()) {
694 didl += "\" refID=\"";
695 PLT_Didl::AppendXmlEscape(didl, m_ReferenceID);
698 didl += "\" restricted=\"";
699 didl += m_Restricted?"1\"":"0\"";
703 NPT_CHECK_SEVERE(PLT_MediaObject::ToDidl(mask, didl));
711 /*----------------------------------------------------------------------
712 | PLT_MediaItem::FromDidl
713 +---------------------------------------------------------------------*/
715 PLT_MediaItem::FromDidl(NPT_XmlElementNode* entry)
720 if (entry->GetTag().Compare("item", true) != 0) {
721 NPT_CHECK_SEVERE(NPT_ERROR_INTERNAL);
724 NPT_Result result = PLT_MediaObject::FromDidl(entry);
729 /*----------------------------------------------------------------------
730 | PLT_MediaContainer::PLT_MediaContainer
731 +---------------------------------------------------------------------*/
732 PLT_MediaContainer::PLT_MediaContainer()
737 /*----------------------------------------------------------------------
738 | PLT_MediaContainer::~PLT_MediaContainer
739 +---------------------------------------------------------------------*/
740 PLT_MediaContainer::~PLT_MediaContainer(void)
744 /*----------------------------------------------------------------------
745 | PLT_MediaContainer::Reset
746 +---------------------------------------------------------------------*/
748 PLT_MediaContainer::Reset()
750 m_SearchClasses.Clear();
751 m_Searchable = false;
752 m_ChildrenCount = -1;
753 m_ContainerUpdateID = 0;
755 return PLT_MediaObject::Reset();
758 /*----------------------------------------------------------------------
759 | PLT_MediaContainer::ToDidl
760 +---------------------------------------------------------------------*/
762 PLT_MediaContainer::ToDidl(const NPT_String& filter, NPT_String& didl)
764 return PLT_MediaObject::ToDidl(filter, didl);
767 /*----------------------------------------------------------------------
768 | PLT_MediaContainer::ToDidl
769 +---------------------------------------------------------------------*/
771 PLT_MediaContainer::ToDidl(NPT_UInt64 mask, NPT_String& didl)
773 // container id property
774 didl += "<container id=\"";
775 PLT_Didl::AppendXmlEscape(didl, m_ObjectID);
777 // parent id property
778 didl += "\" parentID=\"";
779 PLT_Didl::AppendXmlEscape(didl, m_ParentID);
782 if ((mask & PLT_FILTER_MASK_REFID) && !m_ReferenceID.IsEmpty()) {
783 didl += "\" refID=\"";
784 PLT_Didl::AppendXmlEscape(didl, m_ReferenceID);
787 // restricted property
788 didl += "\" restricted=\"";
789 didl += m_Restricted?"1\"":"0\"";
791 // searchable property
792 if (mask & PLT_FILTER_MASK_SEARCHABLE) {
793 didl += " searchable=\"";
794 didl += m_Searchable?"1\"":"0\"";
797 // childcount property
798 if ((mask & PLT_FILTER_MASK_CHILDCOUNT) && m_ChildrenCount != -1) {
799 didl += " childCount=\"";
800 didl += NPT_String::FromInteger(m_ChildrenCount);
806 if ((mask & PLT_FILTER_MASK_SEARCHCLASS) && m_SearchClasses.GetItemCount()) {
807 NPT_List<PLT_SearchClass>::Iterator search_class = m_SearchClasses.GetFirstItem();
808 while (search_class) {
809 didl += "<upnp:searchClass includeDerived=\"";
810 didl += (*search_class).include_derived?"1\"":"0\"";
812 // frienly name is any
813 if (!(*search_class).friendly_name.IsEmpty()) {
814 didl += " name=\"" + (*search_class).friendly_name + "\"";
817 didl += (*search_class).type;
818 didl += "</upnp:searchClass>";
824 NPT_CHECK_SEVERE(PLT_MediaObject::ToDidl(mask, didl));
827 didl += "</container>";
831 /*----------------------------------------------------------------------
832 | PLT_MediaContainer::FromDidl
833 +---------------------------------------------------------------------*/
835 PLT_MediaContainer::FromDidl(NPT_XmlElementNode* entry)
843 if (entry->GetTag().Compare("Container", true) != 0)
844 return NPT_ERROR_INTERNAL;
846 // check if item is searchable (is default true?)
847 if (NPT_SUCCEEDED(PLT_XmlHelper::GetAttribute(entry, "searchable", str, "", 5))) {
848 m_Searchable = PLT_Service::IsTrue(str);
851 // look for childCount
852 if (NPT_SUCCEEDED(PLT_XmlHelper::GetAttribute(entry, "childCount", str, "", 256))) {
854 NPT_CHECK_SEVERE(str.ToInteger(count));
855 m_ChildrenCount = count;
858 // upnp:searchClass child elements
859 NPT_Array<NPT_XmlElementNode*> children;
860 PLT_XmlHelper::GetChildren(entry, children, "upnp:searchClass");
862 for (NPT_Cardinal i=0; i<children.GetItemCount(); i++) {
863 PLT_SearchClass search_class;
866 if (children[i]->GetText() == NULL) {
867 NPT_LOG_WARNING_1("No searchClass text found in: %s",
868 (const char*)PLT_XmlHelper::Serialize(*children[i]));
873 search_class.type = children[i]->GetText()->SubString(0, 256);
875 // extract optional attribute name
876 PLT_XmlHelper::GetAttribute(children[i], "name", search_class.friendly_name);
878 // includeDerived property
879 if (NPT_FAILED(PLT_XmlHelper::GetAttribute(children[i], "includeDerived", str))) {
880 NPT_LOG_WARNING_1("No required attribute searchClass@includeDerived found in: %s",
881 (const char*)PLT_XmlHelper::Serialize(*children[i]));
885 search_class.include_derived = PLT_Service::IsTrue(str);
886 m_SearchClasses.Add(search_class);
889 return PLT_MediaObject::FromDidl(entry);