comparison deps/Platinum/Source/Devices/MediaServer/PltFileMediaServer.cpp @ 0:3425707ddbf6

Initial import (hopefully this mercurial stuff works...)
author fraserofthenight
date Mon, 06 Jul 2009 08:06:28 -0700
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:3425707ddbf6
1 /*****************************************************************
2 |
3 | Platinum - File Media Server
4 |
5 | Copyright (c) 2004-2008, Plutinosoft, LLC.
6 | All rights reserved.
7 | http://www.plutinosoft.com
8 |
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.
13 |
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 |
21 | This program is distributed in the hope that it will be useful,
22 | but WITHOUT ANY WARRANTY; without even the implied warranty of
23 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 | GNU General Public License for more details.
25 |
26 | You should have received a copy of the GNU General Public License
27 | along with this program; see the file LICENSE.txt. If not, write to
28 | the Free Software Foundation, Inc.,
29 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
30 | http://www.gnu.org/licenses/gpl-2.0.html
31 |
32 ****************************************************************/
33
34 /*----------------------------------------------------------------------
35 | includes
36 +---------------------------------------------------------------------*/
37 #include "PltUPnP.h"
38 #include "PltFileMediaServer.h"
39 #include "PltMediaItem.h"
40 #include "PltService.h"
41 #include "PltTaskManager.h"
42 #include "PltHttpServer.h"
43 #include "PltDidl.h"
44 #include "PltMetadataHandler.h"
45 #include "PltVersion.h"
46
47 NPT_SET_LOCAL_LOGGER("platinum.media.server.file")
48
49 /*----------------------------------------------------------------------
50 | PLT_FileMediaServer::PLT_FileMediaServer
51 +---------------------------------------------------------------------*/
52 PLT_FileMediaServer::PLT_FileMediaServer(const char* path,
53 const char* friendly_name,
54 bool show_ip /* = false */,
55 const char* uuid /* = NULL */,
56 NPT_UInt16 port /* = 0 */,
57 bool port_rebind /* = false */) :
58 PLT_MediaServer(friendly_name,
59 show_ip,
60 uuid,
61 port,
62 port_rebind)
63 {
64 /* set up the server root path */
65 m_Path = path;
66 m_Path.TrimRight("/\\");
67 }
68
69 /*----------------------------------------------------------------------
70 | PLT_FileMediaServer::~PLT_FileMediaServer
71 +---------------------------------------------------------------------*/
72 PLT_FileMediaServer::~PLT_FileMediaServer()
73 {
74 }
75
76 /*----------------------------------------------------------------------
77 | PLT_FileMediaServer::AddMetadataHandler
78 +---------------------------------------------------------------------*/
79 NPT_Result
80 PLT_FileMediaServer::AddMetadataHandler(PLT_MetadataHandler* handler)
81 {
82 // make sure we don't have a metadatahandler registered for the same extension
83 /* PLT_MetadataHandler* prev_handler;
84 NPT_Result ret = NPT_ContainerFind(m_MetadataHandlers, PLT_MetadataHandlerFinder(handler->GetExtension()), prev_handler);
85 if (NPT_SUCCEEDED(ret)) {
86 return NPT_ERROR_INVALID_PARAMETERS;
87 } */
88
89 m_MetadataHandlers.Add(handler);
90 return NPT_SUCCESS;
91 }
92
93 /*----------------------------------------------------------------------
94 | PLT_FileMediaServer::SetupDevice
95 +---------------------------------------------------------------------*/
96 NPT_Result
97 PLT_FileMediaServer::SetupDevice()
98 {
99 // FIXME: hack for now: find the first valid non local ip address
100 // to use in item resources. TODO: we should advertise all ips as
101 // multiple resources instead.
102 NPT_List<NPT_String> ips;
103 PLT_UPnPMessageHelper::GetIPAddresses(ips);
104 if (ips.GetItemCount() == 0) return NPT_ERROR_INTERNAL;
105
106 // set the base paths for content and album arts
107 m_FileBaseUri = NPT_HttpUrl(*ips.GetFirstItem(), GetPort(), "/content");
108 m_AlbumArtBaseUri = NPT_HttpUrl(*ips.GetFirstItem(), GetPort(), "/albumart");
109
110 return PLT_MediaServer::SetupDevice();
111 }
112
113 /*----------------------------------------------------------------------
114 | PLT_FileMediaServer::ProcessHttpRequest
115 +---------------------------------------------------------------------*/
116 NPT_Result
117 PLT_FileMediaServer::ProcessHttpRequest(NPT_HttpRequest& request,
118 const NPT_HttpRequestContext& context,
119 NPT_HttpResponse& response)
120 {
121 if (request.GetUrl().GetPath().StartsWith(m_FileBaseUri.GetPath()) ||
122 request.GetUrl().GetPath().StartsWith(m_AlbumArtBaseUri.GetPath())) {
123 return ProcessFileRequest(request, context, response);
124 }
125
126 return PLT_MediaServer::ProcessHttpRequest(request, context, response);
127 }
128
129 /*----------------------------------------------------------------------
130 | PLT_FileMediaServer::ProcessGetDescription
131 +---------------------------------------------------------------------*/
132 NPT_Result
133 PLT_FileMediaServer::ProcessGetDescription(NPT_HttpRequest& request,
134 const NPT_HttpRequestContext& context,
135 NPT_HttpResponse& response)
136 {
137 NPT_String m_OldModelName = m_ModelName;
138 NPT_String m_OldModelNumber = m_ModelNumber;
139
140 // change some things based on User-Agent header
141 NPT_HttpHeader* user_agent = request.GetHeaders().GetHeader(NPT_HTTP_HEADER_USER_AGENT);
142 if (user_agent && user_agent->GetValue().Find("Sonos", 0, true)>=0) {
143 // Force "Rhapsody" so that Sonos is happy to find us
144 m_ModelName = "Rhapsody";
145 m_ModelNumber = "3.0";
146
147 // return modified description
148 NPT_String doc;
149 NPT_Result res = GetDescription(doc);
150
151 // reset to old values now
152 m_ModelName = m_OldModelName;
153 m_ModelNumber = m_OldModelNumber;
154
155 NPT_CHECK_FATAL(res);
156
157 PLT_HttpHelper::SetBody(response, doc);
158 PLT_HttpHelper::SetContentType(response, "text/xml");
159
160 return NPT_SUCCESS;
161 }
162
163 return PLT_MediaServer::ProcessGetDescription(request, context, response);
164 }
165
166 /*----------------------------------------------------------------------
167 | PLT_FileMediaServer::ExtractResourcePath
168 +---------------------------------------------------------------------*/
169 NPT_Result
170 PLT_FileMediaServer::ExtractResourcePath(const NPT_HttpUrl& url, NPT_String& file_path)
171 {
172 // Extract uri path from url
173 NPT_String uri_path = NPT_Uri::PercentDecode(url.GetPath());
174
175 // extract file path from query
176 NPT_HttpUrlQuery query(url.GetQuery());
177 file_path = query.GetField("path");
178 if (file_path.GetLength()) return NPT_SUCCESS;
179
180 // hack for XBMC support for 360, we urlencoded the ? to that the 360 doesn't strip out the query
181 // but then the query ends being parsed as part of the path
182 int index = uri_path.Find("path=");
183 if (index>0) {
184 file_path = uri_path.Right(uri_path.GetLength()-index-5);
185 return NPT_SUCCESS;
186 }
187
188 uri_path = url.GetPath();
189 if (uri_path.StartsWith(m_FileBaseUri.GetPath(), true)) {
190 file_path = NPT_Uri::PercentDecode(uri_path.SubString(m_FileBaseUri.GetPath().GetLength()));
191 return NPT_SUCCESS;
192 } else if (uri_path.StartsWith(m_AlbumArtBaseUri.GetPath(), true)) {
193 file_path = NPT_Uri::PercentDecode(uri_path.SubString(m_AlbumArtBaseUri.GetPath().GetLength()));
194 return NPT_SUCCESS;
195 }
196
197 return NPT_FAILURE;
198 }
199
200 /*----------------------------------------------------------------------
201 | PLT_FileMediaServer::ProcessFileRequest
202 +---------------------------------------------------------------------*/
203 NPT_Result
204 PLT_FileMediaServer::ProcessFileRequest(NPT_HttpRequest& request,
205 const NPT_HttpRequestContext& context,
206 NPT_HttpResponse& response)
207 {
208 NPT_LOG_FINE("PLT_FileMediaServer::ProcessFileRequest Received Request:");
209 PLT_LOG_HTTP_MESSAGE(NPT_LOG_LEVEL_FINE, &request);
210
211 response.GetHeaders().SetHeader("Accept-Ranges", "bytes");
212
213 if (request.GetMethod().Compare("GET") && request.GetMethod().Compare("HEAD")) {
214 response.SetStatus(500, "Internal Server Error");
215 return NPT_SUCCESS;
216 }
217
218 // Extract file path from url
219 NPT_String file_path;
220 NPT_CHECK_LABEL_WARNING(ExtractResourcePath(request.GetUrl(), file_path),
221 failure);
222
223 // Serve file now
224 if (request.GetUrl().GetPath().StartsWith(m_FileBaseUri.GetPath(), true)) {
225 NPT_CHECK_WARNING(ServeFile(request, context, response, NPT_FilePath::Create(m_Path, file_path)));
226 return NPT_SUCCESS;
227 } else if (request.GetUrl().GetPath().StartsWith(m_AlbumArtBaseUri.GetPath(), true)) {
228 NPT_CHECK_WARNING(OnAlbumArtRequest(response, NPT_FilePath::Create(m_Path, file_path)));
229 return NPT_SUCCESS;
230 }
231
232 // fallthrough
233
234 failure:
235 response.SetStatus(404, "File Not Found");
236 return NPT_SUCCESS;
237 }
238
239 /*----------------------------------------------------------------------
240 | PLT_FileMediaServer::ServeFile
241 +---------------------------------------------------------------------*/
242 NPT_Result
243 PLT_FileMediaServer::ServeFile(NPT_HttpRequest& request,
244 const NPT_HttpRequestContext& context,
245 NPT_HttpResponse& response,
246 const NPT_String& file_path)
247 {
248 NPT_COMPILER_UNUSED(context);
249
250 NPT_Position start, end;
251 PLT_HttpHelper::GetRange(request, start, end);
252
253 return PLT_FileServer::ServeFile(response,
254 file_path,
255 start,
256 end,
257 !request.GetMethod().Compare("HEAD"));
258 }
259
260 /*----------------------------------------------------------------------
261 | PLT_FileMediaServer::OnAlbumArtRequest
262 +---------------------------------------------------------------------*/
263 NPT_Result
264 PLT_FileMediaServer::OnAlbumArtRequest(NPT_HttpResponse& response,
265 NPT_String file_path)
266 {
267 NPT_LargeSize total_len;
268 NPT_File file(file_path);
269 NPT_InputStreamReference stream;
270
271 // prevent hackers from accessing files outside of our root
272 if ((file_path.Find("/..") >= 0) || (file_path.Find("\\..") >= 0)) {
273 goto filenotfound;
274 }
275
276 if (NPT_FAILED(file.Open(NPT_FILE_OPEN_MODE_READ)) ||
277 NPT_FAILED(file.GetInputStream(stream)) ||
278 NPT_FAILED(stream->GetSize(total_len)) || (total_len == 0)) {
279 goto filenotfound;
280 } else {
281 NPT_String extension = NPT_FilePath::FileExtension(file_path);
282 if (extension.GetLength() == 0) {
283 goto filenotfound;
284 }
285
286 PLT_MetadataHandler* metadataHandler = NULL;
287 char* caData;
288 int caDataLen;
289 NPT_Result ret = NPT_ContainerFind(m_MetadataHandlers,
290 PLT_MetadataHandlerFinder(extension),
291 metadataHandler);
292 if (NPT_FAILED(ret) || metadataHandler == NULL) {
293 goto filenotfound;
294 }
295 // load the metadatahandler and read the cover art
296 if (NPT_FAILED(metadataHandler->Load(*stream)) ||
297 NPT_FAILED(metadataHandler->GetCoverArtData(caData, caDataLen))) {
298 goto filenotfound;
299 }
300
301 PLT_HttpHelper::SetContentType(response, "application/octet-stream");
302 PLT_HttpHelper::SetBody(response, caData, caDataLen);
303 delete caData;
304 return NPT_SUCCESS;
305 }
306
307 filenotfound:
308 response.SetStatus(404, "File Not Found");
309 return NPT_SUCCESS;
310 }
311
312 /*----------------------------------------------------------------------
313 | PLT_FileMediaServer::OnBrowseMetadata
314 +---------------------------------------------------------------------*/
315 NPT_Result
316 PLT_FileMediaServer::OnBrowseMetadata(PLT_ActionReference& action,
317 const char* object_id,
318 const PLT_HttpRequestContext& context)
319 {
320 NPT_String didl;
321 PLT_MediaObjectReference item;
322
323 /* locate the file from the object ID */
324 NPT_String filepath;
325 if (NPT_FAILED(GetFilePath(object_id, filepath))) {
326 /* error */
327 NPT_LOG_WARNING("PLT_FileMediaServer::OnBrowse - ObjectID not found.");
328 action->SetError(701, "No Such Object.");
329 return NPT_FAILURE;
330 }
331
332 item = BuildFromFilePath(filepath, context, true);
333
334 if (item.IsNull()) return NPT_FAILURE;
335
336 NPT_String filter;
337 NPT_CHECK_SEVERE(action->GetArgumentValue("Filter", filter));
338
339 NPT_String tmp;
340 NPT_CHECK_SEVERE(PLT_Didl::ToDidl(*item.AsPointer(), filter, tmp));
341
342 /* add didl header and footer */
343 didl = didl_header + tmp + didl_footer;
344
345 NPT_CHECK_SEVERE(action->SetArgumentValue("Result", didl));
346 NPT_CHECK_SEVERE(action->SetArgumentValue("NumberReturned", "1"));
347 NPT_CHECK_SEVERE(action->SetArgumentValue("TotalMatches", "1"));
348
349 // update ID may be wrong here, it should be the one of the container?
350 // TODO: We need to keep track of the overall updateID of the CDS
351 NPT_CHECK_SEVERE(action->SetArgumentValue("UpdateId", "1"));
352
353 return NPT_SUCCESS;
354 }
355
356 /*----------------------------------------------------------------------
357 | PLT_FileMediaServer::OnBrowseDirectChildren
358 +---------------------------------------------------------------------*/
359 NPT_Result
360 PLT_FileMediaServer::OnBrowseDirectChildren(PLT_ActionReference& action,
361 const char* object_id,
362 const PLT_HttpRequestContext& context)
363 {
364 /* locate the file from the object ID */
365 NPT_String dir;
366 if (NPT_FAILED(GetFilePath(object_id, dir))) {
367 /* error */
368 NPT_LOG_WARNING("PLT_FileMediaServer::OnBrowse - ObjectID not found.");
369 action->SetError(701, "No Such Object.");
370 return NPT_FAILURE;
371 }
372
373 /* retrieve the item type */
374 NPT_FileInfo info;
375 NPT_Result res = NPT_File::GetInfo(dir, &info);
376 if (NPT_FAILED(res)) {
377 /* Object does not exist */
378 NPT_LOG_WARNING_1("PLT_FileMediaServer::OnBrowse - BROWSEDIRECTCHILDREN failed for item %s", dir.GetChars());
379 action->SetError(800, "Can't retrieve info " + dir);
380 return NPT_FAILURE;
381 }
382
383 if (info.m_Type != NPT_FileInfo::FILE_TYPE_DIRECTORY) {
384 /* error */
385 NPT_LOG_WARNING("PLT_FileMediaServer::OnBrowse - BROWSEDIRECTCHILDREN not allowed on an item.");
386 action->SetError(710, "item is not a container.");
387 return NPT_FAILURE;
388 }
389
390 NPT_String filter;
391 NPT_String startingInd;
392 NPT_String reqCount;
393
394 NPT_CHECK_SEVERE(action->GetArgumentValue("Filter", filter));
395 NPT_CHECK_SEVERE(action->GetArgumentValue("StartingIndex", startingInd));
396 NPT_CHECK_SEVERE(action->GetArgumentValue("RequestedCount", reqCount));
397
398 // in case someone forgot to pass a filter
399 if (filter.GetLength() == 0) filter = "*";
400
401 NPT_UInt32 start_index, req_count;
402 if (NPT_FAILED(startingInd.ToInteger(start_index)) ||
403 NPT_FAILED(reqCount.ToInteger(req_count))) {
404 action->SetError(412, "Precondition failed");
405 return NPT_FAILURE;
406 }
407
408 NPT_List<NPT_String> entries;
409 res = NPT_File::ListDirectory(dir, entries, 0, 0);
410 if (NPT_FAILED(res)) {
411 NPT_LOG_WARNING_1("PLT_FileMediaServer::OnBrowseDirectChildren - failed to open dir %s", (const char*) dir);
412 return res;
413 }
414
415 unsigned long cur_index = 0;
416 unsigned long num_returned = 0;
417 unsigned long total_matches = 0;
418 NPT_String didl = didl_header;
419
420 PLT_MediaObjectReference item;
421 for (NPT_List<NPT_String>::Iterator it = entries.GetFirstItem();
422 it;
423 ++it) {
424 NPT_String filepath = NPT_FilePath::Create(dir, *it);
425
426 // verify we want to process this file first
427 if (!ProcessFile(filepath)) continue;
428
429 item = BuildFromFilePath(
430 filepath,
431 context,
432 true);
433
434 if (!item.IsNull()) {
435 if ((cur_index >= start_index) && ((num_returned < req_count) || (req_count == 0))) {
436 NPT_String tmp;
437 NPT_CHECK_SEVERE(PLT_Didl::ToDidl(*item.AsPointer(), filter, tmp));
438
439 didl += tmp;
440 num_returned++;
441 }
442 cur_index++;
443 total_matches++;
444 }
445 };
446
447 didl += didl_footer;
448
449 NPT_CHECK_SEVERE(action->SetArgumentValue("Result", didl));
450 NPT_CHECK_SEVERE(action->SetArgumentValue("NumberReturned", NPT_String::FromInteger(num_returned)));
451 NPT_CHECK_SEVERE(action->SetArgumentValue("TotalMatches", NPT_String::FromInteger(total_matches))); // 0 means we don't know how many we have but most browsers don't like that!!
452 NPT_CHECK_SEVERE(action->SetArgumentValue("UpdateId", "1"));
453
454 return NPT_SUCCESS;
455 }
456
457 /*----------------------------------------------------------------------
458 | PLT_FileMediaServer::GetFilePath
459 +---------------------------------------------------------------------*/
460 NPT_Result
461 PLT_FileMediaServer::GetFilePath(const char* object_id,
462 NPT_String& filepath)
463 {
464 if (!object_id) return NPT_ERROR_INVALID_PARAMETERS;
465
466 filepath = m_Path;
467
468 if (NPT_StringLength(object_id) > 2 || object_id[0]!='0') {
469 filepath += (const char*)object_id + (object_id[0]=='0'?1:0);
470 }
471
472 return NPT_SUCCESS;
473 }
474
475 /*----------------------------------------------------------------------
476 | PLT_FileMediaServer::BuildResourceUri
477 +---------------------------------------------------------------------*/
478 NPT_String
479 PLT_FileMediaServer::BuildResourceUri(const NPT_HttpUrl& base_uri,
480 const char* host,
481 const char* file_path)
482 {
483 NPT_String result;
484 NPT_HttpUrl uri = base_uri;
485
486 if (host) uri.SetHost(host);
487
488 if (file_path) {
489 NPT_HttpUrlQuery query(uri.GetQuery());
490 query.AddField("path", file_path);
491 uri.SetQuery(query.ToString());
492 }
493
494 // 360 hack: force inclusion of port
495 result = uri.ToStringWithDefaultPort(0);
496
497 // 360 hack: it removes the query, so we make it look like a path
498 // and we replace + with urlencoded value of space
499 result.Replace('?', "%3F");
500 result.Replace('+', "%20");
501 return result;
502 }
503
504 /*----------------------------------------------------------------------
505 | PLT_FileMediaServer::BuildFromFilePath
506 +---------------------------------------------------------------------*/
507 PLT_MediaObject*
508 PLT_FileMediaServer::BuildFromFilePath(const NPT_String& filepath,
509 const PLT_HttpRequestContext& context,
510 bool with_count /* = true */,
511 bool keep_extension_in_title /* = false */)
512 {
513 NPT_String root = m_Path;
514 PLT_MediaItemResource resource;
515 PLT_MediaObject* object = NULL;
516
517 NPT_LOG_INFO_1("Building didl for file '%s'", (const char*)filepath);
518
519 /* retrieve the entry type (directory or file) */
520 NPT_FileInfo info;
521 NPT_CHECK_LABEL_FATAL(NPT_File::GetInfo(filepath, &info), failure);
522
523 if (info.m_Type == NPT_FileInfo::FILE_TYPE_REGULAR) {
524 object = new PLT_MediaItem();
525
526 /* Set the title using the filename for now */
527 object->m_Title = NPT_FilePath::BaseName(filepath, keep_extension_in_title);
528 if (object->m_Title.GetLength() == 0) goto failure;
529
530 /* make sure we return something with a valid mimetype */
531 if (NPT_StringsEqual(PLT_MediaItem::GetMimeType(filepath), "application/unknown"))
532 goto failure;
533
534 /* Set the protocol Info from the extension */
535 resource.m_ProtocolInfo = PLT_MediaItem::GetProtInfo(filepath, context);
536 if (resource.m_ProtocolInfo.GetLength() == 0) goto failure;
537
538 /* Set the resource file size */
539 resource.m_Size = info.m_Size;
540
541 /* format the resource URI */
542 NPT_String url = filepath.SubString(root.GetLength()+1);
543
544 // get list of ip addresses
545 NPT_List<NPT_String> ips;
546 NPT_CHECK_LABEL_SEVERE(PLT_UPnPMessageHelper::GetIPAddresses(ips), failure);
547
548 // if we're passed an interface where we received the request from
549 // move the ip to the top
550 if (context.GetLocalAddress().GetIpAddress().ToString() != "0.0.0.0") {
551 ips.Remove(context.GetLocalAddress().GetIpAddress().ToString());
552 ips.Insert(ips.GetFirstItem(), context.GetLocalAddress().GetIpAddress().ToString());
553 }
554
555 // iterate through list and build list of resources
556 NPT_List<NPT_String>::Iterator ip = ips.GetFirstItem();
557
558 /* Look to see if a metadatahandler exists for this extension */
559 PLT_MetadataHandler* handler = NULL;
560 NPT_Result res = NPT_ContainerFind(
561 m_MetadataHandlers,
562 PLT_MetadataHandlerFinder(NPT_FilePath::FileExtension(filepath)),
563 handler);
564 if (NPT_SUCCEEDED(res) && handler) {
565 /* if it failed loading data, reset the metadatahandler so we don't use it */
566 if (NPT_SUCCEEDED(handler->LoadFile(filepath))) {
567 /* replace the title with the one from the Metadata */
568 NPT_String newTitle;
569 if (handler->GetTitle(newTitle) != NULL) {
570 object->m_Title = newTitle;
571 }
572
573 /* assign description */
574 handler->GetDescription(object->m_Description.long_description);
575
576 /* assign album art uri if we haven't yet */
577 /* prepend the album art base URI and url encode it */
578 if (object->m_ExtraInfo.album_art_uri.GetLength() == 0) {
579 object->m_ExtraInfo.album_art_uri =
580 NPT_Uri::PercentEncode(BuildResourceUri(m_AlbumArtBaseUri, *ip, url),
581 NPT_Uri::UnsafeCharsToEncode);
582 }
583
584 /* duration */
585 handler->GetDuration(resource.m_Duration);
586
587 /* protection */
588 handler->GetProtection(resource.m_Protection);
589 }
590 }
591
592 object->m_ObjectClass.type = PLT_MediaItem::GetUPnPClass(filepath, context);
593
594 while (ip) {
595 /* prepend the base URI and url encode it */
596 //resource.m_Uri = NPT_Uri::Encode(uri.ToString(), NPT_Uri::UnsafeCharsToEncode);
597 resource.m_Uri = BuildResourceUri(m_FileBaseUri, *ip, url);
598 object->m_Resources.Add(resource);
599 ++ip;
600 }
601 } else {
602 object = new PLT_MediaContainer;
603
604 /* Assign a title for this container */
605 if (filepath.Compare(root, true) == 0) {
606 object->m_Title = "Root";
607 } else {
608 object->m_Title = NPT_FilePath::BaseName(filepath, keep_extension_in_title);
609 if (object->m_Title.GetLength() == 0) goto failure;
610 }
611
612 /* Get the number of children for this container */
613 NPT_Cardinal count = 0;
614 if (with_count && NPT_SUCCEEDED(NPT_File::GetCount(filepath, count))) {
615 ((PLT_MediaContainer*)object)->m_ChildrenCount = count;
616 }
617
618 object->m_ObjectClass.type = "object.container.storageFolder";
619 }
620
621 /* is it the root? */
622 if (filepath.Compare(root, true) == 0) {
623 object->m_ParentID = "-1";
624 object->m_ObjectID = "0";
625 } else {
626 NPT_String directory = NPT_FilePath::DirectoryName(filepath);
627 /* is the parent path the root? */
628 if (directory.GetLength() == root.GetLength()) {
629 object->m_ParentID = "0";
630 } else {
631 object->m_ParentID = "0" + filepath.SubString(root.GetLength(), directory.GetLength() - root.GetLength());
632 }
633 object->m_ObjectID = "0" + filepath.SubString(root.GetLength());
634 }
635
636 return object;
637
638 failure:
639 delete object;
640 return NULL;
641 }