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 /*----------------------------------------------------------------------
114 +---------------------------------------------------------------------*/
116 PLT_Artworks::Add(const NPT_String
& type
, const NPT_String
& url
)
122 return NPT_List
<PLT_Artwork
>::Add(artwork
);
125 /*----------------------------------------------------------------------
126 | PLT_Artworks::ToDidl
127 +---------------------------------------------------------------------*/
129 PLT_Artworks::ToDidl(NPT_String
& didl
, const NPT_String
& tag
)
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()) {
139 PLT_Didl::AppendXmlEscape(tmp
, it
->type
);
143 PLT_Didl::AppendXmlEscape(tmp
, it
->url
);
144 tmp
+= "</xbmc:" + tag
+ ">";
151 /*----------------------------------------------------------------------
152 | PLT_Artworks::ToDidl
153 +---------------------------------------------------------------------*/
155 PLT_Artworks::FromDidl(const NPT_Array
<NPT_XmlElementNode
*>& nodes
)
157 for (NPT_Cardinal i
=0; i
<nodes
.GetItemCount(); i
++) {
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
));
168 /*----------------------------------------------------------------------
169 | PLT_MediaItemResource::PLT_MediaItemResource
170 +---------------------------------------------------------------------*/
171 PLT_MediaItemResource::PLT_MediaItemResource()
174 m_ProtocolInfo
= PLT_ProtocolInfo();
175 m_Duration
= (NPT_UInt32
)-1;
176 m_Size
= (NPT_LargeSize
)-1;
178 m_Bitrate
= (NPT_UInt32
)-1;
179 m_BitsPerSample
= (NPT_UInt32
)-1;
180 m_SampleFrequency
= (NPT_UInt32
)-1;
181 m_NbAudioChannels
= (NPT_UInt32
)-1;
183 m_ColorDepth
= (NPT_UInt32
)-1;
186 /*----------------------------------------------------------------------
187 | PLT_MediaObject::GetUPnPClass
188 +---------------------------------------------------------------------*/
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";
211 /*----------------------------------------------------------------------
212 | PLT_MediaObject::Reset
213 +---------------------------------------------------------------------*/
215 PLT_MediaObject::Reset()
217 m_ObjectClass
.type
= "";
218 m_ObjectClass
.friendly_name
= "";
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;
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;
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;
273 /*----------------------------------------------------------------------
274 | PLT_MediaObject::ToDidl
275 +---------------------------------------------------------------------*/
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 +---------------------------------------------------------------------*/
286 PLT_MediaObject::ToDidl(NPT_UInt64 mask
, NPT_String
& didl
)
289 didl
+= "<dc:title>";
290 PLT_Didl::AppendXmlEscape(didl
, m_Title
);
291 didl
+= "</dc:title>";
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>";
302 if ((mask
& PLT_FILTER_MASK_DATE
) && !m_Date
.IsEmpty()) {
304 PLT_Didl::AppendXmlEscape(didl
, m_Date
);
305 didl
+= "</dc:date>";
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");
316 if (mask
& PLT_FILTER_MASK_ACTOR
) {
317 m_People
.actors
.ToDidl(didl
, "actor");
321 if (mask
& PLT_FILTER_MASK_AUTHOR
) {
322 m_People
.authors
.ToDidl(didl
, "author");
326 if (mask
& PLT_FILTER_MASK_DIRECTOR
) {
327 m_People
.directors
.ToDidl(didl
, "director");
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>";
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>";
352 if (mask
& PLT_FILTER_MASK_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>";
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();
370 didl
+= "<upnp:albumArtURI";
371 if (!(*iter
).dlna_profile
.IsEmpty()) {
372 didl
+= " dlna:profileID=\"";
373 PLT_Didl::AppendXmlEscape(didl
, (*iter
).dlna_profile
);
377 PLT_Didl::AppendXmlEscape(didl
, (*iter
).uri
);
378 didl
+= "</upnp:albumArtURI>";
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>";
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>";
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>";
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>";
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>";
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>";
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>";
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>";
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>";
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>";
480 if (mask
& PLT_FILTER_MASK_RES
) {
481 for (NPT_Cardinal i
=0; i
<m_Resources
.GetItemCount(); i
++) {
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
);
490 if ((mask
& PLT_FILTER_MASK_RES_SIZE
) && m_Resources
[i
].m_Size
!= (NPT_LargeSize
)-1) {
492 didl
+= NPT_String::FromIntegerU(m_Resources
[i
].m_Size
);
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
);
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
);
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
);
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
);
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
);
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
);
532 didl
+= " protocolInfo=\"";
533 PLT_Didl::AppendXmlEscape(didl
, m_Resources
[i
].m_ProtocolInfo
.ToString());
535 /* adding custom data */
536 NPT_List
<NPT_Map
<NPT_String
, NPT_String
>::Entry
*>::Iterator entry
= m_Resources
[i
].m_CustomData
.GetEntries().GetFirstItem();
540 PLT_Didl::AppendXmlEscape(didl
, (*entry
)->GetKey());
542 PLT_Didl::AppendXmlEscape(didl
, (*entry
)->GetValue());
549 PLT_Didl::AppendXmlEscape(didl
, m_Resources
[i
].m_Uri
);
554 //sec resources related
555 for (NPT_Cardinal i
= 0; i
< m_SecResources
.GetItemCount(); i
++) {
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();
563 PLT_Didl::AppendXmlEscape(didl
, (*entry
)->GetKey());
565 PLT_Didl::AppendXmlEscape(didl
, (*entry
)->GetValue());
572 PLT_Didl::AppendXmlEscape(didl
, m_SecResources
[i
].value
);
575 PLT_Didl::AppendXmlEscape(didl
, m_SecResources
[i
].name
);
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>";
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>";
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>";
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>";
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>";
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>";
637 didl
+= "<upnp:class";
638 if (!m_ObjectClass
.friendly_name
.IsEmpty()) {
639 didl
+= " name=\"" + m_ObjectClass
.friendly_name
+"\"";
642 PLT_Didl::AppendXmlEscape(didl
, m_ObjectClass
.type
);
643 didl
+= "</upnp:class>";
648 /*----------------------------------------------------------------------
649 | PLT_MediaObject::FromDidl
650 +---------------------------------------------------------------------*/
652 PLT_MediaObject::FromDidl(NPT_XmlElementNode
* entry
)
655 NPT_Array
<NPT_XmlElementNode
*> children
;
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
++) {
671 if (NPT_SUCCEEDED(date
.FromString(m_Date
, (NPT_DateTime::Format
)format
))) {
672 parsed_date
= date
.ToString((NPT_DateTime::Format
)format
);
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);
697 PLT_XmlHelper::GetChildren(entry
, children
, "artist", didl_namespace_upnp
);
698 m_People
.artists
.FromDidl(children
);
701 PLT_XmlHelper::GetChildren(entry
, children
, "author", didl_namespace_upnp
);
702 m_People
.authors
.FromDidl(children
);
705 PLT_XmlHelper::GetChildren(entry
, children
, "actor", didl_namespace_upnp
);
706 m_People
.actors
.FromDidl(children
);
709 PLT_XmlHelper::GetChildren(entry
, children
, "director", didl_namespace_upnp
);
710 m_People
.directors
.FromDidl(children
);
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
);
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
;
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
);
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
++) {
775 if (NPT_SUCCEEDED(date
.FromString(m_MiscInfo
.last_time
, (NPT_DateTime::Format
)format
))) {
776 parsed_last_time
= date
.ToString((NPT_DateTime::Format
)format
);
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
;
787 PLT_XmlHelper::GetChildren(entry
, children
, "res");
788 for (NPT_Cardinal i
=0; i
<children
.GetItemCount(); i
++) {
789 PLT_MediaItemResource resource
;
792 if (children
[i
]->GetText() == NULL
) {
793 NPT_LOG_WARNING_1("No resource text found in: %s", (const char*)PLT_XmlHelper::Serialize(*children
[i
]));
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
);
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
]));
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");
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
++) {
845 if (NPT_SUCCEEDED(date
.FromString(m_XbmcInfo
.date_added
, (NPT_DateTime::Format
)format
))) {
846 parsed_date
= date
.ToString((NPT_DateTime::Format
)format
);
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);
859 if (NPT_FAILED(str
.ToInteger(intValue
))) intValue
= 0;
860 m_XbmcInfo
.votes
= intValue
;
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);
873 res
= ToDidl(PLT_FILTER_MASK_ALL
, m_Didl
);
874 NPT_CHECK_SEVERE(res
);
876 m_Didl
= didl_header
+ m_Didl
+ didl_footer
;
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()
903 /*----------------------------------------------------------------------
904 | PLT_MediaItem::~PLT_MediaItem
905 +---------------------------------------------------------------------*/
906 PLT_MediaItem::~PLT_MediaItem()
910 /*----------------------------------------------------------------------
911 | PLT_MediaItem::ToDidl
912 +---------------------------------------------------------------------*/
914 PLT_MediaItem::ToDidl(const NPT_String
& filter
, NPT_String
& didl
)
916 return PLT_MediaObject::ToDidl(filter
, didl
);
919 /*----------------------------------------------------------------------
920 | PLT_MediaItem::ToDidl
921 +---------------------------------------------------------------------*/
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\"";
941 NPT_CHECK_SEVERE(PLT_MediaObject::ToDidl(mask
, didl
));
949 /*----------------------------------------------------------------------
950 | PLT_MediaItem::FromDidl
951 +---------------------------------------------------------------------*/
953 PLT_MediaItem::FromDidl(NPT_XmlElementNode
* entry
)
958 if (entry
->GetTag().Compare("item", true) != 0) {
959 NPT_CHECK_SEVERE(NPT_ERROR_INTERNAL
);
962 NPT_Result result
= PLT_MediaObject::FromDidl(entry
);
967 /*----------------------------------------------------------------------
968 | PLT_MediaContainer::PLT_MediaContainer
969 +---------------------------------------------------------------------*/
970 PLT_MediaContainer::PLT_MediaContainer()
975 /*----------------------------------------------------------------------
976 | PLT_MediaContainer::~PLT_MediaContainer
977 +---------------------------------------------------------------------*/
978 PLT_MediaContainer::~PLT_MediaContainer(void)
982 /*----------------------------------------------------------------------
983 | PLT_MediaContainer::Reset
984 +---------------------------------------------------------------------*/
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 +---------------------------------------------------------------------*/
1000 PLT_MediaContainer::ToDidl(const NPT_String
& filter
, NPT_String
& didl
)
1002 return PLT_MediaObject::ToDidl(filter
, didl
);
1005 /*----------------------------------------------------------------------
1006 | PLT_MediaContainer::ToDidl
1007 +---------------------------------------------------------------------*/
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
);
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
);
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
+ "\"";
1055 didl
+= (*search_class
).type
;
1056 didl
+= "</upnp:searchClass>";
1062 NPT_CHECK_SEVERE(PLT_MediaObject::ToDidl(mask
, didl
));
1065 didl
+= "</container>";
1069 /*----------------------------------------------------------------------
1070 | PLT_MediaContainer::FromDidl
1071 +---------------------------------------------------------------------*/
1073 PLT_MediaContainer::FromDidl(NPT_XmlElementNode
* entry
)
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))) {
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
;
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
]));
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
]));
1123 search_class
.include_derived
= PLT_Service::IsTrue(str
);
1124 m_SearchClasses
.Add(search_class
);
1127 return PLT_MediaObject::FromDidl(entry
);