Merge pull request #25959 from neo1973/TagLib_deprecation_warnings
[xbmc.git] / lib / libUPnP / Platinum / Source / Devices / MediaServer / PltMediaItem.cpp
blobfac16902f78d027be24963581dd59c2c26572c22
1 /*****************************************************************
3 | Platinum - AV Media Item
5 | Copyright (c) 2004-2010, Plutinosoft, LLC.
6 | All rights reserved.
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 /*----------------------------------------------------------------------
36 | includes
37 +---------------------------------------------------------------------*/
38 #include "PltMediaItem.h"
39 #include "PltMediaServer.h"
40 #include "PltDidl.h"
41 #include "PltUtilities.h"
42 #include "PltService.h"
43 #include "PltMimeType.h"
45 NPT_SET_LOCAL_LOGGER("platinum.media.server.item")
47 /*----------------------------------------------------------------------
48 | globals
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 +---------------------------------------------------------------------*/
57 NPT_Result
58 PLT_PersonRoles::Add(const NPT_String& name, const NPT_String& role /* = "" */)
60 PLT_PersonRole person;
61 person.name = name;
62 person.role = role;
64 return NPT_List<PLT_PersonRole>::Add(person);
67 /*----------------------------------------------------------------------
68 | PLT_PersonRoles::ToDidl
69 +---------------------------------------------------------------------*/
70 NPT_Result
71 PLT_PersonRoles::ToDidl(NPT_String& didl, const NPT_String& tag)
73 NPT_String tmp;
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()) {
81 tmp += " role=\"";
82 PLT_Didl::AppendXmlEscape(tmp, it->role);
83 tmp += "\"";
85 tmp += ">";
86 PLT_Didl::AppendXmlEscape(tmp, it->name);
87 tmp += "</upnp:" + tag + ">";
90 didl += tmp;
91 return NPT_SUCCESS;
94 /*----------------------------------------------------------------------
95 | PLT_PersonRoles::ToDidl
96 +---------------------------------------------------------------------*/
97 NPT_Result
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");
104 // DLNA 7.3.17
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));
109 return NPT_SUCCESS;
112 /*----------------------------------------------------------------------
113 | PLT_Artworks::Add
114 +---------------------------------------------------------------------*/
115 NPT_Result
116 PLT_Artworks::Add(const NPT_String& type, const NPT_String& url)
118 PLT_Artwork artwork;
119 artwork.type = type;
120 artwork.url = url;
122 return NPT_List<PLT_Artwork>::Add(artwork);
125 /*----------------------------------------------------------------------
126 | PLT_Artworks::ToDidl
127 +---------------------------------------------------------------------*/
128 NPT_Result
129 PLT_Artworks::ToDidl(NPT_String& didl, const NPT_String& tag)
131 NPT_String tmp;
132 for (NPT_List<PLT_Artwork>::Iterator it =
133 NPT_List<PLT_Artwork>::GetFirstItem(); it; it++) {
134 if (it->type.IsEmpty()) continue;
136 tmp += "<xbmc:" + tag;
137 if (!it->type.IsEmpty()) {
138 tmp += " type=\"";
139 PLT_Didl::AppendXmlEscape(tmp, it->type);
140 tmp += "\"";
142 tmp += ">";
143 PLT_Didl::AppendXmlEscape(tmp, it->url);
144 tmp += "</xbmc:" + tag + ">";
147 didl += tmp;
148 return NPT_SUCCESS;
151 /*----------------------------------------------------------------------
152 | PLT_Artworks::ToDidl
153 +---------------------------------------------------------------------*/
154 NPT_Result
155 PLT_Artworks::FromDidl(const NPT_Array<NPT_XmlElementNode*>& nodes)
157 for (NPT_Cardinal i=0; i<nodes.GetItemCount(); i++) {
158 PLT_Artwork artwork;
159 const NPT_String* url = nodes[i]->GetText();
160 const NPT_String* type = nodes[i]->GetAttribute("type");
161 if (type) artwork.type = *type;
162 if (url) artwork.url = *url;
163 NPT_CHECK(NPT_List<PLT_Artwork>::Add(artwork));
165 return NPT_SUCCESS;
168 /*----------------------------------------------------------------------
169 | PLT_MediaItemResource::PLT_MediaItemResource
170 +---------------------------------------------------------------------*/
171 PLT_MediaItemResource::PLT_MediaItemResource()
173 m_Uri = "";
174 m_ProtocolInfo = PLT_ProtocolInfo();
175 m_Duration = (NPT_UInt32)-1;
176 m_Size = (NPT_LargeSize)-1;
177 m_Protection = "";
178 m_Bitrate = (NPT_UInt32)-1;
179 m_BitsPerSample = (NPT_UInt32)-1;
180 m_SampleFrequency = (NPT_UInt32)-1;
181 m_NbAudioChannels = (NPT_UInt32)-1;
182 m_Resolution = "";
183 m_ColorDepth = (NPT_UInt32)-1;
186 /*----------------------------------------------------------------------
187 | PLT_MediaObject::GetUPnPClass
188 +---------------------------------------------------------------------*/
189 const char*
190 PLT_MediaObject::GetUPnPClass(const char* filename,
191 const PLT_HttpRequestContext* context /* = NULL */)
193 NPT_COMPILER_UNUSED(context);
195 const char* ret = NULL;
196 NPT_String mime_type = PLT_MimeType::GetMimeType(filename, context);
198 if (mime_type.StartsWith("audio")) {
199 ret = "object.item.audioItem.musicTrack";
200 } else if (mime_type.StartsWith("video")) {
201 ret = "object.item.videoItem"; //Note: 360 wants "object.item.videoItem" and not "object.item.videoItem.Movie"
202 } else if (mime_type.StartsWith("image")) {
203 ret = "object.item.imageItem.photo";
204 } else {
205 ret = "object.item";
208 return ret;
211 /*----------------------------------------------------------------------
212 | PLT_MediaObject::Reset
213 +---------------------------------------------------------------------*/
214 NPT_Result
215 PLT_MediaObject::Reset()
217 m_ObjectClass.type = "";
218 m_ObjectClass.friendly_name = "";
219 m_ObjectID = "";
220 m_ParentID = "";
222 m_Title = "";
223 m_Creator = "";
224 m_Date = "";
225 m_Restricted = true;
227 m_People.actors.Clear();
228 m_People.artists.Clear();
229 m_People.authors.Clear();
230 m_People.directors.Clear();
231 m_People.publisher.Clear();
233 m_Affiliation.album = "";
234 m_Affiliation.genres.Clear();
235 m_Affiliation.playlist = "";
237 m_Description.description = "";
238 m_Description.long_description = "";
239 m_Description.icon_uri = "";
240 m_ExtraInfo.album_arts.Clear();
241 m_ExtraInfo.artist_discography_uri = "";
243 m_MiscInfo.original_track_number = 0;
244 m_MiscInfo.last_position = 0;
245 m_MiscInfo.last_time = "";
246 m_MiscInfo.play_count = -1;
247 m_MiscInfo.dvdregioncode = 0;
248 m_MiscInfo.toc = "";
249 m_MiscInfo.user_annotation = "";
251 m_Recorded.program_title = "";
252 m_Recorded.series_title = "";
253 m_Recorded.episode_number = 0;
254 m_Recorded.episode_count = 0;
255 m_Recorded.episode_season = 0;
257 m_Resources.Clear();
259 m_XbmcInfo.last_playerstate = "";
260 m_XbmcInfo.date_added = "";
261 m_XbmcInfo.rating = 0.0f;
262 m_XbmcInfo.votes = 0;
263 m_XbmcInfo.artwork.Clear();
264 m_XbmcInfo.unique_identifier = "";
265 m_XbmcInfo.countries.Clear();
266 m_XbmcInfo.user_rating = 0;
268 m_Didl = "";
270 return NPT_SUCCESS;
273 /*----------------------------------------------------------------------
274 | PLT_MediaObject::ToDidl
275 +---------------------------------------------------------------------*/
276 NPT_Result
277 PLT_MediaObject::ToDidl(const NPT_String& filter, NPT_String& didl)
279 return ToDidl(PLT_Didl::ConvertFilterToMask(filter), didl);
282 /*----------------------------------------------------------------------
283 | PLT_MediaObject::ToDidl
284 +---------------------------------------------------------------------*/
285 NPT_Result
286 PLT_MediaObject::ToDidl(NPT_UInt64 mask, NPT_String& didl)
288 // title is required
289 didl += "<dc:title>";
290 PLT_Didl::AppendXmlEscape(didl, m_Title);
291 didl += "</dc:title>";
293 // creator
294 if (mask & PLT_FILTER_MASK_CREATOR) {
295 didl += "<dc:creator>";
296 if (m_Creator.IsEmpty()) m_Creator = "Unknown";
297 PLT_Didl::AppendXmlEscape(didl, m_Creator);
298 didl += "</dc:creator>";
301 // date
302 if ((mask & PLT_FILTER_MASK_DATE) && !m_Date.IsEmpty()) {
303 didl += "<dc:date>";
304 PLT_Didl::AppendXmlEscape(didl, m_Date);
305 didl += "</dc:date>";
308 // artist
309 if (mask & PLT_FILTER_MASK_ARTIST) {
310 // force an empty artist just in case (not DLNA Compliant though)
311 //if (m_People.artists.GetItemCount() == 0) m_People.artists.Add("");
312 m_People.artists.ToDidl(didl, "artist");
315 // actor
316 if (mask & PLT_FILTER_MASK_ACTOR) {
317 m_People.actors.ToDidl(didl, "actor");
320 // actor
321 if (mask & PLT_FILTER_MASK_AUTHOR) {
322 m_People.authors.ToDidl(didl, "author");
325 // director
326 if (mask & PLT_FILTER_MASK_DIRECTOR) {
327 m_People.directors.ToDidl(didl, "director");
330 // publisher
331 if (mask & PLT_FILTER_MASK_PUBLISHER) {
332 // Add unknown publisher
333 if (m_People.publisher.GetItemCount() == 0)
334 m_People.publisher.Add("Unknown");
336 for (NPT_List<NPT_String>::Iterator it =
337 m_People.publisher.GetFirstItem(); it; ++it) {
338 didl += "<dc:publisher>";
339 PLT_Didl::AppendXmlEscape(didl, (*it));
340 didl += "</dc:publisher>";
344 // album
345 if ((mask & PLT_FILTER_MASK_ALBUM) && !m_Affiliation.album.IsEmpty()) {
346 didl += "<upnp:album>";
347 PLT_Didl::AppendXmlEscape(didl, m_Affiliation.album);
348 didl += "</upnp:album>";
351 // genre
352 if (mask & PLT_FILTER_MASK_GENRE) {
353 // Add unknown genre
354 if (m_Affiliation.genres.GetItemCount() == 0)
355 m_Affiliation.genres.Add("Unknown");
357 for (NPT_List<NPT_String>::Iterator it =
358 m_Affiliation.genres.GetFirstItem(); it; ++it) {
359 didl += "<upnp:genre>";
360 PLT_Didl::AppendXmlEscape(didl, (*it));
361 didl += "</upnp:genre>";
365 // album art URI
366 if ((mask & PLT_FILTER_MASK_ALBUMARTURI) && m_ExtraInfo.album_arts.GetItemCount()) {
367 for (NPT_List<PLT_AlbumArtInfo>::Iterator iter = m_ExtraInfo.album_arts.GetFirstItem();
368 iter;
369 iter++) {
370 didl += "<upnp:albumArtURI";
371 if (!(*iter).dlna_profile.IsEmpty()) {
372 didl += " dlna:profileID=\"";
373 PLT_Didl::AppendXmlEscape(didl, (*iter).dlna_profile);
374 didl += "\"";
376 didl += ">";
377 PLT_Didl::AppendXmlEscape(didl, (*iter).uri);
378 didl += "</upnp:albumArtURI>";
382 // description
383 if ((mask & PLT_FILTER_MASK_DESCRIPTION) && !m_Description.description.IsEmpty()) {
384 didl += "<dc:description>";
385 PLT_Didl::AppendXmlEscape(didl, m_Description.description);
386 didl += "</dc:description>";
389 // long description
390 if ((mask & PLT_FILTER_MASK_LONGDESCRIPTION) && !m_Description.long_description.IsEmpty()) {
391 didl += "<upnp:longDescription>";
392 PLT_Didl::AppendXmlEscape(didl, m_Description.long_description);
393 didl += "</upnp:longDescription>";
396 // icon
397 if ((mask & PLT_FILTER_MASK_ICON) && !m_Description.icon_uri.IsEmpty()) {
398 didl += "<upnp:icon>";
399 PLT_Didl::AppendXmlEscape(didl, m_Description.icon_uri);
400 didl += "</upnp:icon>";
403 // rating
404 if ((mask & PLT_FILTER_MASK_RATING) && !m_Description.rating.IsEmpty()) {
405 didl += "<upnp:rating>";
406 PLT_Didl::AppendXmlEscape(didl, m_Description.rating);
407 didl += "</upnp:rating>";
410 // original track number
411 if ((mask & PLT_FILTER_MASK_ORIGINALTRACK) && m_MiscInfo.original_track_number > 0) {
412 didl += "<upnp:originalTrackNumber>";
413 didl += NPT_String::FromInteger(m_MiscInfo.original_track_number);
414 didl += "</upnp:originalTrackNumber>";
417 // last playback position
418 if (mask & PLT_FILTER_MASK_LASTPOSITION && m_MiscInfo.last_position > 0) {
419 didl += "<upnp:lastPlaybackPosition>";
420 didl += NPT_String::FromInteger(m_MiscInfo.last_position);
421 didl += "</upnp:lastPlaybackPosition>";
424 // last playback datetime
425 if (mask & PLT_FILTER_MASK_LASTPLAYBACK && !m_MiscInfo.last_time.IsEmpty()) {
426 didl += "<upnp:lastPlaybackTime>";
427 PLT_Didl::AppendXmlEscape(didl, m_MiscInfo.last_time);
428 didl += "</upnp:lastPlaybackTime>";
431 // playcount
432 if (mask & PLT_FILTER_MASK_PLAYCOUNT && m_MiscInfo.play_count > -1) {
433 didl += "<upnp:playbackCount>";
434 didl += NPT_String::FromInteger(m_MiscInfo.play_count);
435 didl += "</upnp:playbackCount>";
438 // program title
439 if (mask & PLT_FILTER_MASK_PROGRAMTITLE && !m_Recorded.program_title.IsEmpty()) {
440 didl += "<upnp:programTitle>";
441 PLT_Didl::AppendXmlEscape(didl, m_Recorded.program_title);
442 didl += "</upnp:programTitle>";
445 // series title
446 if ((mask & PLT_FILTER_MASK_SERIESTITLE) && !m_Recorded.series_title.IsEmpty()) {
447 didl += "<upnp:seriesTitle>";
448 PLT_Didl::AppendXmlEscape(didl, m_Recorded.series_title);
449 didl += "</upnp:seriesTitle>";
452 // episode number
453 if ((mask & PLT_FILTER_MASK_EPISODE) && m_Recorded.episode_number > 0) {
454 didl += "<upnp:episodeNumber>";
455 didl += NPT_String::FromInteger(m_Recorded.episode_number);
456 didl += "</upnp:episodeNumber>";
459 // episode count
460 if ((mask & PLT_FILTER_MASK_EPISODE_COUNT) && m_Recorded.episode_count > 0) {
461 didl += "<upnp:episodeCount>";
462 didl += NPT_String::FromInteger(m_Recorded.episode_count);
463 didl += "</upnp:episodeCount>";
466 // episode count
467 if ((mask & PLT_FILTER_MASK_EPISODE_SEASON)) {
468 didl += "<upnp:episodeSeason>";
469 didl += NPT_String::FromInteger(m_Recorded.episode_season);
470 didl += "</upnp:episodeSeason>";
473 if ((mask & PLT_FILTER_MASK_TOC) && !m_MiscInfo.toc.IsEmpty()) {
474 didl += "<upnp:toc>";
475 PLT_Didl::AppendXmlEscape(didl, m_MiscInfo.toc);
476 didl += "</upnp:toc>";
479 // resource
480 if (mask & PLT_FILTER_MASK_RES) {
481 for (NPT_Cardinal i=0; i<m_Resources.GetItemCount(); i++) {
482 didl += "<res";
484 if ((mask & PLT_FILTER_MASK_RES_DURATION) && m_Resources[i].m_Duration != (NPT_UInt32)-1) {
485 didl += " duration=\"";
486 didl += PLT_Didl::FormatTimeStamp(m_Resources[i].m_Duration);
487 didl += "\"";
490 if ((mask & PLT_FILTER_MASK_RES_SIZE) && m_Resources[i].m_Size != (NPT_LargeSize)-1) {
491 didl += " size=\"";
492 didl += NPT_String::FromIntegerU(m_Resources[i].m_Size);
493 didl += "\"";
496 if ((mask & PLT_FILTER_MASK_RES_PROTECTION) && !m_Resources[i].m_Protection.IsEmpty()) {
497 didl += " protection=\"";
498 PLT_Didl::AppendXmlEscape(didl, m_Resources[i].m_Protection);
499 didl += "\"";
502 if ((mask & PLT_FILTER_MASK_RES_RESOLUTION) && !m_Resources[i].m_Resolution.IsEmpty()) {
503 didl += " resolution=\"";
504 PLT_Didl::AppendXmlEscape(didl, m_Resources[i].m_Resolution);
505 didl += "\"";
508 if ((mask & PLT_FILTER_MASK_RES_BITRATE) && m_Resources[i].m_Bitrate != (NPT_Size)-1) {
509 didl += " bitrate=\"";
510 didl += NPT_String::FromIntegerU(m_Resources[i].m_Bitrate);
511 didl += "\"";
514 if ((mask & PLT_FILTER_MASK_RES_BITSPERSAMPLE) && m_Resources[i].m_BitsPerSample != (NPT_Size)-1) {
515 didl += " bitsPerSample=\"";
516 didl += NPT_String::FromIntegerU(m_Resources[i].m_BitsPerSample);
517 didl += "\"";
520 if ((mask & PLT_FILTER_MASK_RES_SAMPLEFREQUENCY) && m_Resources[i].m_SampleFrequency != (NPT_Size)-1) {
521 didl += " sampleFrequency=\"";
522 didl += NPT_String::FromIntegerU(m_Resources[i].m_SampleFrequency);
523 didl += "\"";
526 if ((mask & PLT_FILTER_MASK_RES_NRAUDIOCHANNELS) && m_Resources[i].m_NbAudioChannels != (NPT_Size)-1) {
527 didl += " nrAudioChannels=\"";
528 didl += NPT_String::FromIntegerU(m_Resources[i].m_NbAudioChannels);
529 didl += "\"";
532 didl += " protocolInfo=\"";
533 PLT_Didl::AppendXmlEscape(didl, m_Resources[i].m_ProtocolInfo.ToString());
534 didl += "\"";
535 /* adding custom data */
536 NPT_List<NPT_Map<NPT_String, NPT_String>::Entry*>::Iterator entry = m_Resources[i].m_CustomData.GetEntries().GetFirstItem();
537 while (entry)
539 didl += " ";
540 PLT_Didl::AppendXmlEscape(didl, (*entry)->GetKey());
541 didl += "=\"";
542 PLT_Didl::AppendXmlEscape(didl, (*entry)->GetValue());
543 didl += "\"";
545 entry++;
548 didl += ">";
549 PLT_Didl::AppendXmlEscape(didl, m_Resources[i].m_Uri);
550 didl += "</res>";
554 //sec resources related
555 for (NPT_Cardinal i = 0; i < m_SecResources.GetItemCount(); i++) {
556 didl += "<sec:";
557 PLT_Didl::AppendXmlEscape(didl, m_SecResources[i].name);
559 NPT_List<NPT_Map<NPT_String, NPT_String>::Entry*>::Iterator entry = m_SecResources[i].attributes.GetEntries().GetFirstItem();
560 while (entry)
562 didl += " sec:";
563 PLT_Didl::AppendXmlEscape(didl, (*entry)->GetKey());
564 didl += "=\"";
565 PLT_Didl::AppendXmlEscape(didl, (*entry)->GetValue());
566 didl += "\"";
568 entry++;
571 didl += ">";
572 PLT_Didl::AppendXmlEscape(didl, m_SecResources[i].value);
574 didl += "</sec:";
575 PLT_Didl::AppendXmlEscape(didl, m_SecResources[i].name);
576 didl += ">";
579 // xbmc dateadded
580 if ((mask & PLT_FILTER_MASK_XBMC_DATEADDED) && !m_XbmcInfo.date_added.IsEmpty()) {
581 didl += "<xbmc:dateadded>";
582 PLT_Didl::AppendXmlEscape(didl, m_XbmcInfo.date_added);
583 didl += "</xbmc:dateadded>";
586 // xbmc rating
587 if (mask & PLT_FILTER_MASK_XBMC_RATING) {
588 didl += "<xbmc:rating>";
589 didl += NPT_String::Format("%.1f", m_XbmcInfo.rating);
590 didl += "</xbmc:rating>";
593 // xbmc votes
594 if (mask & PLT_FILTER_MASK_XBMC_VOTES && m_XbmcInfo.votes != 0) {
595 didl += "<xbmc:votes>";
596 didl += NPT_String::Format("%i", m_XbmcInfo.votes);
597 didl += "</xbmc:votes>";
600 // xbmc artwork
601 if (mask & PLT_FILTER_MASK_XBMC_ARTWORK) {
602 m_XbmcInfo.artwork.ToDidl(didl, "artwork");
605 // xbmc unique identifier
606 if (mask & PLT_FILTER_MASK_XBMC_UNIQUE_IDENTIFIER && !m_XbmcInfo.unique_identifier.IsEmpty()) {
607 didl += "<xbmc:uniqueidentifier>";
608 PLT_Didl::AppendXmlEscape(didl, m_XbmcInfo.unique_identifier);
609 didl += "</xbmc:uniqueidentifier>";
612 // country
613 if (mask & PLT_FILTER_MASK_XBMC_COUNTRY) {
614 for (NPT_List<NPT_String>::Iterator it =
615 m_XbmcInfo.countries.GetFirstItem(); it; ++it) {
616 didl += "<xbmc:country>";
617 PLT_Didl::AppendXmlEscape(didl, (*it));
618 didl += "</xbmc:country>";
622 // user rating
623 if (mask & PLT_FILTER_MASK_XBMC_USERRATING) {
624 didl += "<xbmc:userrating>";
625 didl += NPT_String::FromInteger(m_XbmcInfo.user_rating);
626 didl += "</xbmc:userrating>";
629 // xbmc last playback state
630 if (mask & PLT_FILTER_MASK_XBMC_LASTPLAYERSTATE && !m_XbmcInfo.last_playerstate.IsEmpty()) {
631 didl += "<xbmc:lastPlayerState>";
632 PLT_Didl::AppendXmlEscape(didl, m_XbmcInfo.last_playerstate);
633 didl += "</xbmc:lastPlayerState>";
636 // class is required
637 didl += "<upnp:class";
638 if (!m_ObjectClass.friendly_name.IsEmpty()) {
639 didl += " name=\"" + m_ObjectClass.friendly_name+"\"";
641 didl += ">";
642 PLT_Didl::AppendXmlEscape(didl, m_ObjectClass.type);
643 didl += "</upnp:class>";
645 return NPT_SUCCESS;
648 /*----------------------------------------------------------------------
649 | PLT_MediaObject::FromDidl
650 +---------------------------------------------------------------------*/
651 NPT_Result
652 PLT_MediaObject::FromDidl(NPT_XmlElementNode* entry)
654 NPT_String str, xml;
655 NPT_Array<NPT_XmlElementNode*> children;
656 NPT_Result res;
658 // check if item is restricted (is default true?)
659 if (NPT_SUCCEEDED(PLT_XmlHelper::GetAttribute(entry, "restricted", str, "", 5))) {
660 m_Restricted = PLT_Service::IsTrue(str);
663 // read non-required elements
664 PLT_XmlHelper::GetChildText(entry, "creator", m_Creator, didl_namespace_dc, 256);
665 PLT_XmlHelper::GetChildText(entry, "date", m_Date, didl_namespace_dc, 256);
667 // parse date and make sure it's valid
668 NPT_String parsed_date;
669 for (int format=0; format<=NPT_DateTime::FORMAT_RFC_1036; format++) {
670 NPT_DateTime date;
671 if (NPT_SUCCEEDED(date.FromString(m_Date, (NPT_DateTime::Format)format))) {
672 parsed_date = date.ToString((NPT_DateTime::Format)format);
673 break;
676 m_Date = parsed_date;
678 res = PLT_XmlHelper::GetAttribute(entry, "id", m_ObjectID);
679 NPT_CHECK_SEVERE(res);
681 res = PLT_XmlHelper::GetAttribute(entry, "parentID", m_ParentID);
682 NPT_CHECK_SEVERE(res);
684 PLT_XmlHelper::GetAttribute(entry, "refID", m_ReferenceID);
686 res = PLT_XmlHelper::GetChildText(entry, "title", m_Title, didl_namespace_dc);
687 NPT_CHECK_SEVERE(res);
689 res = PLT_XmlHelper::GetChildText(entry, "class", m_ObjectClass.type, didl_namespace_upnp);
690 NPT_CHECK_SEVERE(res);
692 // DLNA 7.3.17.3 max bytes for dc:title and upnp:class is 256 bytes
693 m_Title = m_Title.SubString(0, 256);
694 m_ObjectClass.type = m_ObjectClass.type.SubString(0, 256);
696 children.Clear();
697 PLT_XmlHelper::GetChildren(entry, children, "artist", didl_namespace_upnp);
698 m_People.artists.FromDidl(children);
700 children.Clear();
701 PLT_XmlHelper::GetChildren(entry, children, "author", didl_namespace_upnp);
702 m_People.authors.FromDidl(children);
704 children.Clear();
705 PLT_XmlHelper::GetChildren(entry, children, "actor", didl_namespace_upnp);
706 m_People.actors.FromDidl(children);
708 children.Clear();
709 PLT_XmlHelper::GetChildren(entry, children, "director", didl_namespace_upnp);
710 m_People.directors.FromDidl(children);
712 children.Clear();
713 PLT_XmlHelper::GetChildren(entry, children, "publisher", didl_namespace_dc);
714 for (NPT_Cardinal i=0; i<children.GetItemCount(); i++) {
715 if (children[i]->GetText()) {
716 m_People.publisher.Add(*children[i]->GetText());
720 PLT_XmlHelper::GetChildText(entry, "album", m_Affiliation.album, didl_namespace_upnp, 256);
721 PLT_XmlHelper::GetChildText(entry, "programTitle", m_Recorded.program_title, didl_namespace_upnp);
722 PLT_XmlHelper::GetChildText(entry, "seriesTitle", m_Recorded.series_title, didl_namespace_upnp);
723 PLT_XmlHelper::GetChildText(entry, "episodeNumber", str, didl_namespace_upnp);
724 NPT_UInt32 value;
725 if (NPT_FAILED(str.ToInteger(value))) value = 0;
726 m_Recorded.episode_number = value;
727 PLT_XmlHelper::GetChildText(entry, "episodeCount", str, didl_namespace_upnp);
728 if (NPT_FAILED(str.ToInteger(value))) value = 0;
729 m_Recorded.episode_count = value;
730 PLT_XmlHelper::GetChildText(entry, "episodeSeason", str, didl_namespace_upnp);
731 if (NPT_FAILED(str.ToInteger(value))) value = -1;
732 m_Recorded.episode_season = value;
734 children.Clear();
735 PLT_XmlHelper::GetChildren(entry, children, "genre", didl_namespace_upnp);
736 for (NPT_Cardinal i=0; i<children.GetItemCount(); i++) {
737 if (children[i]->GetText()) {
738 m_Affiliation.genres.Add(children[i]->GetText()->SubString(0, 256));
742 PLT_XmlHelper::GetChildText(entry, "description", m_Description.description, didl_namespace_dc);
743 PLT_XmlHelper::GetChildText(entry, "longDescription", m_Description.long_description, didl_namespace_upnp);
744 PLT_XmlHelper::GetChildText(entry, "icon", m_Description.icon_uri, didl_namespace_upnp);
745 PLT_XmlHelper::GetChildText(entry, "rating", m_Description.rating, didl_namespace_upnp);
746 PLT_XmlHelper::GetChildText(entry, "toc", m_MiscInfo.toc, didl_namespace_upnp);
748 // album arts
749 children.Clear();
750 PLT_XmlHelper::GetChildren(entry, children, "albumArtURI", didl_namespace_upnp);
751 for (NPT_Cardinal i=0; i<children.GetItemCount(); i++) {
752 if (children[i]->GetText()) {
753 PLT_AlbumArtInfo info;
754 info.uri = children[i]->GetText()->SubString(0, 1024);
755 PLT_XmlHelper::GetAttribute(children[i], "profileID", info.dlna_profile, didl_namespace_dlna);
756 m_ExtraInfo.album_arts.Add(info);
760 PLT_XmlHelper::GetChildText(entry, "originalTrackNumber", str, didl_namespace_upnp);
761 if (NPT_FAILED(str.ToInteger(value))) value = 0;
762 m_MiscInfo.original_track_number = value;
764 PLT_XmlHelper::GetChildText(entry, "lastPlaybackPosition", str, didl_namespace_upnp);
765 if (NPT_FAILED(PLT_Didl::ParseTimeStamp(str, value))) {
766 // fall back to raw integer parsing
767 if (NPT_FAILED(str.ToInteger(value))) value = 0;
769 m_MiscInfo.last_position = value;
771 PLT_XmlHelper::GetChildText(entry, "lastPlaybackTime", m_MiscInfo.last_time, didl_namespace_upnp, 256);
772 NPT_String parsed_last_time;
773 for (int format=0; format<=NPT_DateTime::FORMAT_RFC_1036; format++) {
774 NPT_DateTime date;
775 if (NPT_SUCCEEDED(date.FromString(m_MiscInfo.last_time, (NPT_DateTime::Format)format))) {
776 parsed_last_time = date.ToString((NPT_DateTime::Format)format);
777 break;
780 m_MiscInfo.last_time = parsed_last_time;
782 PLT_XmlHelper::GetChildText(entry, "playbackCount", str, didl_namespace_upnp);
783 if (NPT_FAILED(str.ToInteger(value))) value = -1;
784 m_MiscInfo.play_count = value;
786 children.Clear();
787 PLT_XmlHelper::GetChildren(entry, children, "res");
788 for (NPT_Cardinal i=0; i<children.GetItemCount(); i++) {
789 PLT_MediaItemResource resource;
791 // extract url
792 if (children[i]->GetText() == NULL) {
793 NPT_LOG_WARNING_1("No resource text found in: %s", (const char*)PLT_XmlHelper::Serialize(*children[i]));
794 } else {
795 resource.m_Uri = children[i]->GetText()->SubString(0, 1024);
797 // basic uri validation, ignoring scheme (could be rtsp)
798 NPT_HttpUrl url(resource.m_Uri, true);
799 if (!url.IsValid()) {
800 NPT_LOG_WARNING_1("Invalid resource uri: %s", (const char*)resource.m_Uri);
801 continue;
805 // extract protocol info
806 NPT_String protocol_info;
807 res = PLT_XmlHelper::GetAttribute(children[i], "protocolInfo", protocol_info, "", 256);
808 if (NPT_FAILED(res)) {
809 NPT_LOG_WARNING_1("No protocol info found in: %s", (const char*)PLT_XmlHelper::Serialize(*children[i]));
810 } else {
811 resource.m_ProtocolInfo = PLT_ProtocolInfo(protocol_info);
812 if (!resource.m_ProtocolInfo.IsValid()) {
813 NPT_LOG_WARNING_1("Invalid resource protocol info: %s", (const char*)protocol_info);
817 // extract known attributes
818 PLT_XmlHelper::GetAttribute(children[i], "protection", resource.m_Protection, "", 256);
819 PLT_XmlHelper::GetAttribute(children[i], "resolution", resource.m_Resolution, "", 256);
821 if (NPT_SUCCEEDED(PLT_XmlHelper::GetAttribute(children[i], "size", str, "", 256))) {
822 if (NPT_FAILED(str.ToInteger64(resource.m_Size))) resource.m_Size = (NPT_Size)-1;
825 if (NPT_SUCCEEDED(PLT_XmlHelper::GetAttribute(children[i], "duration", str, "", 256))) {
826 if (NPT_FAILED(PLT_Didl::ParseTimeStamp(str, resource.m_Duration))) {
827 // if error while converting, ignore and set to -1 to indicate we don't know the duration
828 resource.m_Duration = (NPT_UInt32)-1;
829 PLT_XmlHelper::RemoveAttribute(children[i], "duration");
830 } else {
831 // DLNA: reformat duration in case it was not compliant
832 str = PLT_Didl::FormatTimeStamp(resource.m_Duration);
833 PLT_XmlHelper::SetAttribute(children[i], "duration", str);
836 m_Resources.Add(resource);
839 PLT_XmlHelper::GetChildText(entry, "lastPlayerState", m_XbmcInfo.last_playerstate, didl_namespace_xbmc, 2048);
841 PLT_XmlHelper::GetChildText(entry, "dateadded", m_XbmcInfo.date_added, didl_namespace_xbmc, 256);
842 // parse date and make sure it's valid
843 for (int format=0; format<=NPT_DateTime::FORMAT_RFC_1036; format++) {
844 NPT_DateTime date;
845 if (NPT_SUCCEEDED(date.FromString(m_XbmcInfo.date_added, (NPT_DateTime::Format)format))) {
846 parsed_date = date.ToString((NPT_DateTime::Format)format);
847 break;
850 m_XbmcInfo.date_added = parsed_date;
852 PLT_XmlHelper::GetChildText(entry, "rating", str, didl_namespace_xbmc);
853 NPT_Float floatValue;
854 if (NPT_FAILED(str.ToFloat(floatValue))) floatValue = 0.0;
855 m_XbmcInfo.rating = floatValue;
857 PLT_XmlHelper::GetChildText(entry, "votes", str, didl_namespace_xbmc, 256);
858 NPT_Int32 intValue;
859 if (NPT_FAILED(str.ToInteger(intValue))) intValue = 0;
860 m_XbmcInfo.votes = intValue;
862 children.Clear();
863 PLT_XmlHelper::GetChildren(entry, children, "artwork", didl_namespace_xbmc);
864 m_XbmcInfo.artwork.FromDidl(children);
866 PLT_XmlHelper::GetChildText(entry, "uniqueidentifier", m_XbmcInfo.unique_identifier, didl_namespace_xbmc, 256);
868 // re serialize the entry didl as a we might need to pass it to a renderer
869 // we may have modified the tree to "fix" issues, so as not to break a renderer
870 // (don't write xml prefix as this didl could be part of a larger document)
871 //res = PLT_XmlHelper::Serialize(*entry, xml, false);
872 m_Didl = "";
873 res = ToDidl(PLT_FILTER_MASK_ALL, m_Didl);
874 NPT_CHECK_SEVERE(res);
876 m_Didl = didl_header + m_Didl + didl_footer;
877 return NPT_SUCCESS;
880 /*----------------------------------------------------------------------
881 | PLT_MediaObjectList::PLT_MediaObjectList
882 +---------------------------------------------------------------------*/
883 PLT_MediaObjectList::PLT_MediaObjectList()
887 /*----------------------------------------------------------------------
888 | PLT_MediaObjectList::~PLT_MediaObjectList
889 +---------------------------------------------------------------------*/
890 PLT_MediaObjectList::~PLT_MediaObjectList()
892 Apply(NPT_ObjectDeleter<PLT_MediaObject>());
895 /*----------------------------------------------------------------------
896 | PLT_MediaItem::PLT_MediaItem
897 +---------------------------------------------------------------------*/
898 PLT_MediaItem::PLT_MediaItem()
900 Reset();
903 /*----------------------------------------------------------------------
904 | PLT_MediaItem::~PLT_MediaItem
905 +---------------------------------------------------------------------*/
906 PLT_MediaItem::~PLT_MediaItem()
910 /*----------------------------------------------------------------------
911 | PLT_MediaItem::ToDidl
912 +---------------------------------------------------------------------*/
913 NPT_Result
914 PLT_MediaItem::ToDidl(const NPT_String& filter, NPT_String& didl)
916 return PLT_MediaObject::ToDidl(filter, didl);
919 /*----------------------------------------------------------------------
920 | PLT_MediaItem::ToDidl
921 +---------------------------------------------------------------------*/
922 NPT_Result
923 PLT_MediaItem::ToDidl(NPT_UInt64 mask, NPT_String& didl)
925 didl += "<item id=\"";
927 PLT_Didl::AppendXmlEscape(didl, m_ObjectID);
928 didl += "\" parentID=\"";
929 PLT_Didl::AppendXmlEscape(didl, m_ParentID);
931 if ((mask & PLT_FILTER_MASK_REFID) && !m_ReferenceID.IsEmpty()) {
932 didl += "\" refID=\"";
933 PLT_Didl::AppendXmlEscape(didl, m_ReferenceID);
936 didl += "\" restricted=\"";
937 didl += m_Restricted?"1\"":"0\"";
939 didl += ">";
941 NPT_CHECK_SEVERE(PLT_MediaObject::ToDidl(mask, didl));
943 /* close tag */
944 didl += "</item>";
946 return NPT_SUCCESS;
949 /*----------------------------------------------------------------------
950 | PLT_MediaItem::FromDidl
951 +---------------------------------------------------------------------*/
952 NPT_Result
953 PLT_MediaItem::FromDidl(NPT_XmlElementNode* entry)
955 /* reset first */
956 Reset();
958 if (entry->GetTag().Compare("item", true) != 0) {
959 NPT_CHECK_SEVERE(NPT_ERROR_INTERNAL);
962 NPT_Result result = PLT_MediaObject::FromDidl(entry);
964 return result;
967 /*----------------------------------------------------------------------
968 | PLT_MediaContainer::PLT_MediaContainer
969 +---------------------------------------------------------------------*/
970 PLT_MediaContainer::PLT_MediaContainer()
972 Reset();
975 /*----------------------------------------------------------------------
976 | PLT_MediaContainer::~PLT_MediaContainer
977 +---------------------------------------------------------------------*/
978 PLT_MediaContainer::~PLT_MediaContainer(void)
982 /*----------------------------------------------------------------------
983 | PLT_MediaContainer::Reset
984 +---------------------------------------------------------------------*/
985 NPT_Result
986 PLT_MediaContainer::Reset()
988 m_SearchClasses.Clear();
989 m_Searchable = false;
990 m_ChildrenCount = -1;
991 m_ContainerUpdateID = 0;
993 return PLT_MediaObject::Reset();
996 /*----------------------------------------------------------------------
997 | PLT_MediaContainer::ToDidl
998 +---------------------------------------------------------------------*/
999 NPT_Result
1000 PLT_MediaContainer::ToDidl(const NPT_String& filter, NPT_String& didl)
1002 return PLT_MediaObject::ToDidl(filter, didl);
1005 /*----------------------------------------------------------------------
1006 | PLT_MediaContainer::ToDidl
1007 +---------------------------------------------------------------------*/
1008 NPT_Result
1009 PLT_MediaContainer::ToDidl(NPT_UInt64 mask, NPT_String& didl)
1011 // container id property
1012 didl += "<container id=\"";
1013 PLT_Didl::AppendXmlEscape(didl, m_ObjectID);
1015 // parent id property
1016 didl += "\" parentID=\"";
1017 PLT_Didl::AppendXmlEscape(didl, m_ParentID);
1019 // ref id
1020 if ((mask & PLT_FILTER_MASK_REFID) && !m_ReferenceID.IsEmpty()) {
1021 didl += "\" refID=\"";
1022 PLT_Didl::AppendXmlEscape(didl, m_ReferenceID);
1025 // restricted property
1026 didl += "\" restricted=\"";
1027 didl += m_Restricted?"1\"":"0\"";
1029 // searchable property
1030 if (mask & PLT_FILTER_MASK_SEARCHABLE) {
1031 didl += " searchable=\"";
1032 didl += m_Searchable?"1\"":"0\"";
1035 // childcount property
1036 if ((mask & PLT_FILTER_MASK_CHILDCOUNT) && m_ChildrenCount != -1) {
1037 didl += " childCount=\"";
1038 didl += NPT_String::FromInteger(m_ChildrenCount);
1039 didl += "\"";
1042 didl += ">";
1044 if ((mask & PLT_FILTER_MASK_SEARCHCLASS) && m_SearchClasses.GetItemCount()) {
1045 NPT_List<PLT_SearchClass>::Iterator search_class = m_SearchClasses.GetFirstItem();
1046 while (search_class) {
1047 didl += "<upnp:searchClass includeDerived=\"";
1048 didl += (*search_class).include_derived?"1\"":"0\"";
1050 // frienly name is any
1051 if (!(*search_class).friendly_name.IsEmpty()) {
1052 didl += " name=\"" + (*search_class).friendly_name + "\"";
1054 didl += ">";
1055 didl += (*search_class).type;
1056 didl += "</upnp:searchClass>";
1058 ++search_class;
1062 NPT_CHECK_SEVERE(PLT_MediaObject::ToDidl(mask, didl));
1064 /* close tag */
1065 didl += "</container>";
1066 return NPT_SUCCESS;
1069 /*----------------------------------------------------------------------
1070 | PLT_MediaContainer::FromDidl
1071 +---------------------------------------------------------------------*/
1072 NPT_Result
1073 PLT_MediaContainer::FromDidl(NPT_XmlElementNode* entry)
1075 NPT_String str;
1077 /* reset first */
1078 Reset();
1080 // check entry type
1081 if (entry->GetTag().Compare("Container", true) != 0)
1082 return NPT_ERROR_INTERNAL;
1084 // check if item is searchable (is default true?)
1085 if (NPT_SUCCEEDED(PLT_XmlHelper::GetAttribute(entry, "searchable", str, "", 5))) {
1086 m_Searchable = PLT_Service::IsTrue(str);
1089 // look for childCount
1090 if (NPT_SUCCEEDED(PLT_XmlHelper::GetAttribute(entry, "childCount", str, "", 256))) {
1091 NPT_UInt32 count;
1092 NPT_CHECK_SEVERE(str.ToInteger(count));
1093 m_ChildrenCount = count;
1096 // upnp:searchClass child elements
1097 NPT_Array<NPT_XmlElementNode*> children;
1098 PLT_XmlHelper::GetChildren(entry, children, "upnp:searchClass");
1100 for (NPT_Cardinal i=0; i<children.GetItemCount(); i++) {
1101 PLT_SearchClass search_class;
1103 // extract url
1104 if (children[i]->GetText() == NULL) {
1105 NPT_LOG_WARNING_1("No searchClass text found in: %s",
1106 (const char*)PLT_XmlHelper::Serialize(*children[i]));
1107 continue;
1110 // DLNA 7.3.17.4
1111 search_class.type = children[i]->GetText()->SubString(0, 256);
1113 // extract optional attribute name
1114 PLT_XmlHelper::GetAttribute(children[i], "name", search_class.friendly_name);
1116 // includeDerived property
1117 if (NPT_FAILED(PLT_XmlHelper::GetAttribute(children[i], "includeDerived", str))) {
1118 NPT_LOG_WARNING_1("No required attribute searchClass@includeDerived found in: %s",
1119 (const char*)PLT_XmlHelper::Serialize(*children[i]));
1120 continue;
1123 search_class.include_derived = PLT_Service::IsTrue(str);
1124 m_SearchClasses.Add(search_class);
1127 return PLT_MediaObject::FromDidl(entry);