Mercurial > projects > hoofbaby
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 } |