Merge pull request #25959 from neo1973/TagLib_deprecation_warnings
[xbmc.git] / lib / libUPnP / Platinum / Source / Devices / MediaServer / PltDidl.cpp
blob37d36ddec0a6ab7f57f2e7c3ffe14e4df12c2a07
1 /*****************************************************************
3 | Platinum - DIDL
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 "PltDidl.h"
39 #include "PltUtilities.h"
40 #include "PltService.h"
42 NPT_SET_LOCAL_LOGGER("platinum.media.server.didl")
44 /*----------------------------------------------------------------------
45 | globals
46 +---------------------------------------------------------------------*/
47 const char* didl_header = "<DIDL-Lite xmlns=\"urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/\""
48 " xmlns:dc=\"http://purl.org/dc/elements/1.1/\""
49 " xmlns:upnp=\"urn:schemas-upnp-org:metadata-1-0/upnp/\""
50 " xmlns:dlna=\"urn:schemas-dlna-org:metadata-1-0/\""
51 " xmlns:sec=\"http://www.sec.co.kr/\""
52 " xmlns:xbmc=\"urn:schemas-xbmc-org:metadata-1-0/\">";
53 const char* didl_footer = "</DIDL-Lite>";
54 const char* didl_namespace_dc = "http://purl.org/dc/elements/1.1/";
55 const char* didl_namespace_upnp = "urn:schemas-upnp-org:metadata-1-0/upnp/";
56 const char* didl_namespace_dlna = "urn:schemas-dlna-org:metadata-1-0/";
57 const char* didl_namespace_xbmc = "urn:schemas-xbmc-org:metadata-1-0/";
59 /*----------------------------------------------------------------------
60 | PLT_Didl::ConvertFilterToMask
61 +---------------------------------------------------------------------*/
62 NPT_UInt64
63 PLT_Didl::ConvertFilterToMask(const NPT_String& filter)
65 // easy out
66 if (filter.GetLength() == 0) return PLT_FILTER_MASK_ALL;
68 // a filter string is a comma delimited set of fields identifying
69 // a given DIDL property (or set of properties).
70 // These fields are or start with: upnp:, @, res@, res, dc:, container@
72 NPT_UInt64 mask = 0;
73 const char* s = filter;
74 int i = 0;
76 while (s[i] != '\0') {
77 int next_comma = filter.Find(',', i);
78 int len = ((next_comma < 0)?(int)filter.GetLength():next_comma)-i;
80 if (NPT_String::CompareN(s+i, "*", 1) == 0) {
81 // return now, there's no point in parsing the rest
82 return PLT_FILTER_MASK_ALL;
85 // title is required, so we return a non empty mask
86 mask |= PLT_FILTER_MASK_TITLE;
88 if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_TITLE, len, true) == 0) {
89 mask |= PLT_FILTER_MASK_TITLE;
90 } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_REFID, len, true) == 0) {
91 mask |= PLT_FILTER_MASK_REFID;
92 } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_CREATOR, len, true) == 0) {
93 mask |= PLT_FILTER_MASK_CREATOR;
94 } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_ARTIST, len, true) == 0) {
95 mask |= PLT_FILTER_MASK_ARTIST;
96 } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_ACTOR, len, true) == 0) {
97 mask |= PLT_FILTER_MASK_ACTOR;
98 } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_DIRECTOR, len, true) == 0) {
99 mask |= PLT_FILTER_MASK_DIRECTOR;
100 } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_AUTHOR, len, true) == 0) {
101 mask |= PLT_FILTER_MASK_AUTHOR;
102 } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_DATE, len, true) == 0) {
103 mask |= PLT_FILTER_MASK_DATE;
104 } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_ALBUM, len, true) == 0) {
105 mask |= PLT_FILTER_MASK_ALBUM;
106 } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_GENRE, len, true) == 0) {
107 mask |= PLT_FILTER_MASK_GENRE;
108 } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_ALBUMARTURI, len, true) == 0 ||
109 NPT_String::CompareN(s+i, PLT_FILTER_FIELD_ALBUMARTURI_DLNAPROFILEID, len, true) == 0) {
110 mask |= PLT_FILTER_MASK_ALBUMARTURI;
111 } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_DESCRIPTION, len, true) == 0) {
112 mask |= PLT_FILTER_MASK_DESCRIPTION;
113 } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_LONGDESCRIPTION, len, true) == 0) {
114 mask |= PLT_FILTER_MASK_LONGDESCRIPTION;
115 } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_ORIGINALTRACK, len, true) == 0) {
116 mask |= PLT_FILTER_MASK_ORIGINALTRACK;
117 } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_LASTPOSITION, len, true) == 0) {
118 mask |= PLT_FILTER_MASK_LASTPOSITION;
119 } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_LASTPLAYBACK, len, true) == 0) {
120 mask |= PLT_FILTER_MASK_LASTPLAYBACK;
121 } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_PLAYCOUNT, len, true) == 0) {
122 mask |= PLT_FILTER_MASK_PLAYCOUNT;
123 } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_SEARCHABLE, len, true) == 0) {
124 mask |= PLT_FILTER_MASK_SEARCHABLE;
125 } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_SEARCHCLASS, len, true) == 0) {
126 mask |= PLT_FILTER_MASK_SEARCHCLASS;
127 } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_CONTAINER_SEARCHABLE, len, true) == 0) {
128 mask |= PLT_FILTER_MASK_SEARCHABLE;
129 } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_CHILDCOUNT, len, true) == 0) {
130 mask |= PLT_FILTER_MASK_CHILDCOUNT;
131 } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_CONTAINER_CHILDCOUNT, len, true) == 0) {
132 mask |= PLT_FILTER_MASK_CHILDCOUNT;
133 } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_PROGRAMTITLE, len, true) == 0) {
134 mask |= PLT_FILTER_MASK_PROGRAMTITLE;
135 } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_SERIESTITLE, len, true) == 0) {
136 mask |= PLT_FILTER_MASK_SERIESTITLE;
137 } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_EPISODE, len, true) == 0) {
138 mask |= PLT_FILTER_MASK_EPISODE;
139 } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_RATING, len, true) == 0) {
140 mask |= PLT_FILTER_MASK_RATING;
141 } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_PUBLISHER, len, true) == 0) {
142 mask |= PLT_FILTER_MASK_PUBLISHER;
143 } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_RES, len, true) == 0) {
144 mask |= PLT_FILTER_MASK_RES;
145 } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_RES_DURATION, len, true) == 0 ||
146 NPT_String::CompareN(s+i, PLT_FILTER_FIELD_RES_DURATION_SHORT, len, true) == 0) {
147 mask |= PLT_FILTER_MASK_RES | PLT_FILTER_MASK_RES_DURATION;
148 } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_RES_SIZE, len, true) == 0) {
149 mask |= PLT_FILTER_MASK_RES | PLT_FILTER_MASK_RES_SIZE;
150 } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_RES_PROTECTION, len, true) == 0) {
151 mask |= PLT_FILTER_MASK_RES | PLT_FILTER_MASK_RES_PROTECTION;
152 } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_RES_RESOLUTION, len, true) == 0) {
153 mask |= PLT_FILTER_MASK_RES | PLT_FILTER_MASK_RES_RESOLUTION;
154 } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_RES_BITRATE, len, true) == 0) {
155 mask |= PLT_FILTER_MASK_RES | PLT_FILTER_MASK_RES_BITRATE;
156 } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_RES_BITSPERSAMPLE, len, true) == 0) {
157 mask |= PLT_FILTER_MASK_RES | PLT_FILTER_MASK_RES_BITSPERSAMPLE;
158 } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_RES_NRAUDIOCHANNELS, len, true) == 0) {
159 mask |= PLT_FILTER_MASK_RES | PLT_FILTER_MASK_RES_NRAUDIOCHANNELS;
160 } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_RES_SAMPLEFREQUENCY, len, true) == 0) {
161 mask |= PLT_FILTER_MASK_RES | PLT_FILTER_MASK_RES_SAMPLEFREQUENCY;
162 } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_EPISODE_COUNT, len, true) == 0) {
163 mask |= PLT_FILTER_MASK_EPISODE_COUNT;
164 } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_EPISODE_SEASON, len, true) == 0) {
165 mask |= PLT_FILTER_MASK_EPISODE_SEASON;
166 } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_XBMC_LASTPLAYERSTATE, len, true) == 0) {
167 mask |= PLT_FILTER_MASK_XBMC_LASTPLAYERSTATE;
168 } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_XBMC_DATEADDED, len, true) == 0) {
169 mask |= PLT_FILTER_MASK_XBMC_DATEADDED;
170 } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_XBMC_RATING, len, true) == 0) {
171 mask |= PLT_FILTER_MASK_XBMC_RATING;
172 } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_XBMC_VOTES, len, true) == 0) {
173 mask |= PLT_FILTER_MASK_XBMC_VOTES;
174 } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_XBMC_ARTWORK, len, true) == 0) {
175 mask |= PLT_FILTER_MASK_XBMC_ARTWORK;
176 } else if (NPT_String::CompareN(s+i, PLT_FILTER_FIELD_XBMC_UNIQUE_IDENTIFIER, len, true) == 0) {
177 mask |= PLT_FILTER_MASK_XBMC_UNIQUE_IDENTIFIER;
178 } else if (NPT_String::CompareN(s + i, PLT_FILTER_FIELD_XBMC_COUNTRY, len, true) == 0) {
179 mask |= PLT_FILTER_MASK_XBMC_COUNTRY;
180 } else if (NPT_String::CompareN(s + i, PLT_FILTER_FIELD_XBMC_USERRATING, len, true) == 0) {
181 mask |= PLT_FILTER_MASK_XBMC_USERRATING;
184 if (next_comma < 0) {
185 return mask;
188 i = next_comma + 1;
191 return mask;
194 /*----------------------------------------------------------------------
195 | PLT_Didl::AppendXmlUnEscape
196 +---------------------------------------------------------------------*/
197 void
198 PLT_Didl::AppendXmlUnEscape(NPT_String& out, const char* in)
200 unsigned int i=0;
201 while (i<NPT_StringLength(in)) {
202 if (NPT_String::CompareN(in+i, "&lt;", 4) == 0) {
203 out += '<';
204 i +=4;
205 } else if (NPT_String::CompareN(in+i, "&gt;", 4) == 0) {
206 out += '>';
207 i += 4;
208 } else if (NPT_String::CompareN(in+i, "&amp;", 5) == 0) {
209 out += '&';
210 i += 5;
211 } else if (NPT_String::CompareN(in+i, "&quot;", 6) == 0) {
212 out += '"';
213 i += 6;
214 } else if (NPT_String::CompareN(in+i, "&apos;", 6) == 0) {
215 out += '\'';
216 i += 6;
217 } else {
218 out += *(in+i);
219 i++;
224 /*----------------------------------------------------------------------
225 | PLT_Didl::AppendXmlEscape
226 +---------------------------------------------------------------------*/
227 void
228 PLT_Didl::AppendXmlEscape(NPT_String& out, const char* in)
230 if (!in) return;
232 for (int i=0; i<(int)NPT_StringLength(in); i++) {
233 if (*(in+i) == '<') {
234 out += "&lt;";
235 } else if (*(in+i) == '>') {
236 out += "&gt;";
237 } else if (*(in+i) == '&') {
238 out += "&amp;";
239 } else if (*(in+i) == '"') {
240 out += "&quot;";
241 } else if (*(in+i) == '\'') {
242 out += "&apos;";
243 } else {
244 out += *(in+i);
249 /*----------------------------------------------------------------------
250 | PLT_Didl::FormatTimeStamp
251 +---------------------------------------------------------------------*/
252 NPT_String
253 PLT_Didl::FormatTimeStamp(NPT_UInt32 seconds)
255 NPT_String result;
256 int hours = seconds/3600;
257 if (hours == 0) {
258 result += "0:";
259 } else {
260 result += NPT_String::FromInteger(hours) + ":";
263 int minutes = (seconds/60)%60;
264 if (minutes == 0) {
265 result += "00:";
266 } else {
267 if (minutes < 10) {
268 result += '0';
270 result += NPT_String::FromInteger(minutes) + ":";
273 int secs = seconds%60;
274 if (secs == 0) {
275 result += "00";
276 } else {
277 if (secs < 10) {
278 result += '0';
280 result += NPT_String::FromInteger(secs);
283 result += ".000"; // needed for XBOX360 otherwise it won't play the track
284 return result;
287 /*----------------------------------------------------------------------
288 | PLT_Didl::ParseTimeStamp
289 +---------------------------------------------------------------------*/
290 NPT_Result
291 PLT_Didl::ParseTimeStamp(const NPT_String& timestamp, NPT_UInt32& seconds)
293 // assume a timestamp in the format HH:MM:SS.FFF
294 int separator;
295 NPT_String str = timestamp;
296 NPT_UInt32 value;
298 // reset output params first
299 seconds = 0;
301 // remove milliseconds first if any
302 if ((separator = str.ReverseFind('.')) != -1) {
303 str = str.Left(separator);
306 // look for next separator
307 if ((separator = str.ReverseFind(':')) == -1) return NPT_FAILURE;
309 // extract seconds
310 NPT_CHECK_WARNING(str.SubString(separator+1).ToInteger(value));
311 seconds = value;
312 str = str.Left(separator);
314 // look for next separator
315 if ((separator = str.ReverseFind(':')) == -1) return NPT_FAILURE;
317 // extract minutes
318 NPT_CHECK_WARNING(str.SubString(separator+1).ToInteger(value));
319 seconds += 60*value;
320 str = str.Left(separator);
322 // extract hours
323 NPT_CHECK_WARNING(str.ToInteger(value));
324 seconds += 3600*value;
326 return NPT_SUCCESS;
329 /*----------------------------------------------------------------------
330 | PLT_Didl::ToDidl
331 +---------------------------------------------------------------------*/
332 NPT_Result
333 PLT_Didl::ToDidl(PLT_MediaObject& object, const NPT_String& filter, NPT_String& didl)
335 NPT_UInt64 mask = ConvertFilterToMask(filter);
337 // Allocate enough space for the didl
338 didl.Reserve(2048);
340 return object.ToDidl(mask, didl);
343 /*----------------------------------------------------------------------
344 | PLT_Didl::FromDidl
345 +---------------------------------------------------------------------*/
346 NPT_Result
347 PLT_Didl::FromDidl(const char* xml, PLT_MediaObjectListReference& objects)
349 NPT_String str;
350 PLT_MediaObject* object = NULL;
351 NPT_XmlNode* node = NULL;
352 NPT_XmlElementNode* didl = NULL;
353 NPT_XmlParser parser;
355 NPT_LOG_FINE("Parsing Didl...");
357 NPT_CHECK_LABEL_SEVERE(parser.Parse(xml, node), cleanup);
358 if (!node || !node->AsElementNode()) {
359 NPT_LOG_SEVERE("Invalid node type");
360 goto cleanup;
363 didl = node->AsElementNode();
365 if (didl->GetTag().Compare("DIDL-Lite", true)) {
366 NPT_LOG_SEVERE("Invalid node tag");
367 goto cleanup;
370 // create entry list
371 objects = new PLT_MediaObjectList();
373 // for each child, find out if it's a container or not
374 // and then invoke the FromDidl on it
375 for (NPT_List<NPT_XmlNode*>::Iterator children = didl->GetChildren().GetFirstItem(); children; children++) {
376 NPT_XmlElementNode* child = (*children)->AsElementNode();
377 if (!child) continue;
379 if (child->GetTag().Compare("Container", true) == 0) {
380 object = new PLT_MediaContainer();
381 } else if (child->GetTag().Compare("item", true) == 0) {
382 object = new PLT_MediaItem();
383 } else {
384 NPT_LOG_WARNING("Invalid node tag");
385 continue;
388 if (NPT_FAILED(object->FromDidl(child))) {
389 NPT_LOG_WARNING_1("Invalid didl for object: %s",
390 (const char*) PLT_XmlHelper::Serialize(*child, false));
391 continue;
394 objects->Add(object);
395 object = NULL; // reset to make sure it doesn't get deleted twice in case of error
398 delete node;
399 return NPT_SUCCESS;
401 cleanup:
402 objects = NULL;
403 delete node;
404 delete object;
405 return NPT_FAILURE;