aboutsummaryrefslogtreecommitdiff
path: root/xbmc/network/UPnP.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/network/UPnP.cpp')
-rw-r--r--xbmc/network/UPnP.cpp2420
1 files changed, 0 insertions, 2420 deletions
diff --git a/xbmc/network/UPnP.cpp b/xbmc/network/UPnP.cpp
deleted file mode 100644
index 5130f85c06..0000000000
--- a/xbmc/network/UPnP.cpp
+++ /dev/null
@@ -1,2420 +0,0 @@
-/*
-* UPnP Support for XBMC
-* Copyright (c) 2006 c0diq (Sylvain Rebaud)
-* Portions Copyright (c) by the authors of libPlatinum
-*
-* http://www.plutinosoft.com/blog/category/platinum/
-*
-* This program is free software; you can redistribute it and/or modify
-* it under the terms of the GNU General Public License as published by
-* the Free Software Foundation; either version 2 of the License, or
-* (at your option) any later version.
-*
-* This program is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-* GNU General Public License for more details.
-*
-* You should have received a copy of the GNU General Public License
-* along with this program; if not, write to the Free Software
-* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#include "threads/SystemClock.h"
-#include "UPnP.h"
-#include "utils/URIUtils.h"
-#include "Application.h"
-#include "ApplicationMessenger.h"
-#include "Network.h"
-#include "utils/log.h"
-#include "filesystem/MusicDatabaseDirectory.h"
-#include "filesystem/VideoDatabaseDirectory.h"
-#include "music/MusicDatabase.h"
-#include "video/VideoDatabase.h"
-#include "filesystem/VideoDatabaseDirectory/DirectoryNode.h"
-#include "filesystem/VideoDatabaseDirectory/QueryParams.h"
-#include "filesystem/File.h"
-#include "NptStrings.h"
-#include "Platinum.h"
-#include "PltMediaConnect.h"
-#include "PltMediaRenderer.h"
-#include "PltSyncMediaBrowser.h"
-#include "PltDidl.h"
-#include "NptNetwork.h"
-#include "NptConsole.h"
-#include "music/tags/MusicInfoTag.h"
-#include "pictures/PictureInfoTag.h"
-#include "pictures/GUIWindowSlideShow.h"
-#include "filesystem/Directory.h"
-#include "URL.h"
-#include "settings/GUISettings.h"
-#include "GUIUserMessages.h"
-#include "settings/Settings.h"
-#include "settings/AdvancedSettings.h"
-#include "utils/StringUtils.h"
-#include "FileItem.h"
-#include "guilib/GUIWindowManager.h"
-#include "GUIInfoManager.h"
-#include "utils/TimeUtils.h"
-#include "utils/md5.h"
-#include "guilib/Key.h"
-#include "ThumbLoader.h"
-#include "Util.h"
-
-using namespace std;
-using namespace MUSIC_INFO;
-using namespace XFILE;
-
-NPT_SET_LOCAL_LOGGER("xbmc.upnp")
-
-#define UPNP_DEFAULT_MAX_RETURNED_ITEMS 200
-#define UPNP_DEFAULT_MIN_RETURNED_ITEMS 30
-
-/*
-# Play speed
-# 1 normal
-# 0 invalid
-DLNA_ORG_PS = 'DLNA.ORG_PS'
-DLNA_ORG_PS_VAL = '1'
-
-# Convertion Indicator
-# 1 transcoded
-# 0 not transcoded
-DLNA_ORG_CI = 'DLNA.ORG_CI'
-DLNA_ORG_CI_VAL = '0'
-
-# Operations
-# 00 not time seek range, not range
-# 01 range supported
-# 10 time seek range supported
-# 11 both supported
-DLNA_ORG_OP = 'DLNA.ORG_OP'
-DLNA_ORG_OP_VAL = '01'
-
-# Flags
-# senderPaced 80000000 31
-# lsopTimeBasedSeekSupported 40000000 30
-# lsopByteBasedSeekSupported 20000000 29
-# playcontainerSupported 10000000 28
-# s0IncreasingSupported 08000000 27
-# sNIncreasingSupported 04000000 26
-# rtspPauseSupported 02000000 25
-# streamingTransferModeSupported 01000000 24
-# interactiveTransferModeSupported 00800000 23
-# backgroundTransferModeSupported 00400000 22
-# connectionStallingSupported 00200000 21
-# dlnaVersion15Supported 00100000 20
-DLNA_ORG_FLAGS = 'DLNA.ORG_FLAGS'
-DLNA_ORG_FLAGS_VAL = '01500000000000000000000000000000'
-*/
-
-/*----------------------------------------------------------------------
-| static
-+---------------------------------------------------------------------*/
-CUPnP* CUPnP::upnp = NULL;
-
-namespace
-{
-
- enum EClientQuirks
- {
- ECLIENTQUIRKS_NONE = 0x0
-
- /* Client requires folder's to be marked as storageFolers as verndor type (360)*/
- , ECLIENTQUIRKS_ONLYSTORAGEFOLDER = 0x01
-
- /* Client can't handle subtypes for videoItems (360) */
- , ECLIENTQUIRKS_BASICVIDEOCLASS = 0x02
-
- /* Client requires album to be set to [Unknown Series] to show title (WMP) */
- , ECLIENTQUIRKS_UNKNOWNSERIES = 0x04
- };
-
- static EClientQuirks GetClientQuirks(const PLT_HttpRequestContext* context)
- {
- if(context == NULL)
- return ECLIENTQUIRKS_NONE;
-
- unsigned int quirks = 0;
- const NPT_String* user_agent = context->GetRequest().GetHeaders().GetHeaderValue(NPT_HTTP_HEADER_USER_AGENT);
- const NPT_String* server = context->GetRequest().GetHeaders().GetHeaderValue(NPT_HTTP_HEADER_SERVER);
-
- if (user_agent) {
- if (user_agent->Find("XBox", 0, true) >= 0 ||
- user_agent->Find("Xenon", 0, true) >= 0)
- quirks |= ECLIENTQUIRKS_ONLYSTORAGEFOLDER | ECLIENTQUIRKS_BASICVIDEOCLASS;
-
- if (user_agent->Find("Windows-Media-Player", 0, true) >= 0)
- quirks |= ECLIENTQUIRKS_UNKNOWNSERIES;
-
- }
- if (server) {
- if (server->Find("Xbox", 0, true) >= 0)
- quirks |= ECLIENTQUIRKS_ONLYSTORAGEFOLDER | ECLIENTQUIRKS_BASICVIDEOCLASS;
- }
-
- return (EClientQuirks)quirks;
- }
-}
-
-
-/*----------------------------------------------------------------------
-| NPT_Console::Output
-+---------------------------------------------------------------------*/
-void
-NPT_Console::Output(const char* message)
-{
- CLog::Log(LOGDEBUG, "%s", message);
-}
-
-/*----------------------------------------------------------------------
-| CDeviceHostReferenceHolder class
-+---------------------------------------------------------------------*/
-class CDeviceHostReferenceHolder
-{
-public:
- PLT_DeviceHostReference m_Device;
-};
-
-/*----------------------------------------------------------------------
-| CCtrlPointReferenceHolder class
-+---------------------------------------------------------------------*/
-class CCtrlPointReferenceHolder
-{
-public:
- PLT_CtrlPointReference m_CtrlPoint;
-};
-
-/*----------------------------------------------------------------------
-| CUPnPCleaner class
-+---------------------------------------------------------------------*/
-class CUPnPCleaner : public NPT_Thread
-{
-public:
- CUPnPCleaner(CUPnP* upnp) : NPT_Thread(true), m_UPnP(upnp) {}
- void Run() {
- delete m_UPnP;
- }
-
- CUPnP* m_UPnP;
-};
-
-/*----------------------------------------------------------------------
-| CUPnP::CUPnP
-+---------------------------------------------------------------------*/
-class CUPnPServer : public PLT_MediaConnect,
- public PLT_FileMediaConnectDelegate
-{
-public:
- CUPnPServer(const char* friendly_name, const char* uuid = NULL, int port = 0) :
- PLT_MediaConnect(friendly_name, false, uuid, port),
- PLT_FileMediaConnectDelegate("/", "/") {
- }
-
- // PLT_MediaServer methods
- virtual NPT_Result OnBrowseMetadata(PLT_ActionReference& action,
- const char* object_id,
- const char* filter,
- NPT_UInt32 starting_index,
- NPT_UInt32 requested_count,
- const char* sort_criteria,
- const PLT_HttpRequestContext& context);
- virtual NPT_Result OnBrowseDirectChildren(PLT_ActionReference& action,
- const char* object_id,
- const char* filter,
- NPT_UInt32 starting_index,
- NPT_UInt32 requested_count,
- const char* sort_criteria,
- const PLT_HttpRequestContext& context);
- virtual NPT_Result OnSearchContainer(PLT_ActionReference& action,
- const char* container_id,
- const char* search_criteria,
- const char* filter,
- NPT_UInt32 starting_index,
- NPT_UInt32 requested_count,
- const char* sort_criteria,
- const PLT_HttpRequestContext& context);
-
- // PLT_FileMediaServer methods
- virtual NPT_Result ServeFile(const NPT_HttpRequest& request,
- const NPT_HttpRequestContext& context,
- NPT_HttpResponse& response,
- const NPT_String& file_path);
-
- // class methods
- static NPT_Result PopulateObjectFromTag(CMusicInfoTag& tag,
- PLT_MediaObject& object,
- NPT_String* file_path,
- PLT_MediaItemResource* resource,
- EClientQuirks quirks);
- static NPT_Result PopulateObjectFromTag(CVideoInfoTag& tag,
- PLT_MediaObject& object,
- NPT_String* file_path,
- PLT_MediaItemResource* resource,
- EClientQuirks quirks);
- static PLT_MediaObject* BuildObject(const CFileItem& item,
- NPT_String& file_path,
- bool with_count,
- const PLT_HttpRequestContext* context = NULL,
- CUPnPServer* upnp_server = NULL);
- NPT_String BuildSafeResourceUri(const NPT_HttpUrl &rooturi,
- const char* host,
- const char* file_path);
-
- void AddSafeResourceUri(PLT_MediaObject* object, const NPT_HttpUrl& rooturi, NPT_List<NPT_IpAddress> ips, const char* file_path, const NPT_String& info)
- {
- PLT_MediaItemResource res;
- for(NPT_List<NPT_IpAddress>::Iterator ip = ips.GetFirstItem(); ip; ++ip) {
- res.m_ProtocolInfo = PLT_ProtocolInfo(info);
- res.m_Uri = BuildSafeResourceUri(rooturi, (*ip).ToString(), file_path);
- object->m_Resources.Add(res);
- }
- }
-
-
- static const char* GetMimeTypeFromExtension(const char* extension, const PLT_HttpRequestContext* context = NULL);
- static NPT_String GetMimeType(const CFileItem& item, const PLT_HttpRequestContext* context = NULL);
- static NPT_String GetMimeType(const char* filename, const PLT_HttpRequestContext* context = NULL);
- static const CStdString& CorrectAllItemsSortHack(const CStdString &item);
-
-private:
- PLT_MediaObject* Build(CFileItemPtr item,
- bool with_count,
- const PLT_HttpRequestContext& context,
- const char* parent_id = NULL);
- NPT_Result BuildResponse(PLT_ActionReference& action,
- CFileItemList& items,
- const char* filter,
- NPT_UInt32 starting_index,
- NPT_UInt32 requested_count,
- const char* sort_criteria,
- const PLT_HttpRequestContext& context,
- const char* parent_id /* = NULL */);
-
- // class methods
- static NPT_String GetParentFolder(NPT_String file_path) {
- int index = file_path.ReverseFind("\\");
- if (index == -1) return "";
-
- return file_path.Left(index);
- }
- static const NPT_String GetProtocolInfo(const CFileItem& item,
- const char* protocol,
- const PLT_HttpRequestContext* context = NULL);
-
- NPT_Mutex m_FileMutex;
- NPT_Map<NPT_String, NPT_String> m_FileMap;
-
-public:
- // class members
- static NPT_UInt32 m_MaxReturnedItems;
-};
-
-NPT_UInt32 CUPnPServer::m_MaxReturnedItems = 0;
-
-/*----------------------------------------------------------------------
-| CUPnPServer::BuildSafeResourceUri
-+---------------------------------------------------------------------*/
-NPT_String CUPnPServer::BuildSafeResourceUri(const NPT_HttpUrl &rooturi,
- const char* host,
- const char* file_path)
-{
- CStdString md5;
- XBMC::XBMC_MD5 md5state;
- md5state.append(file_path);
- md5state.getDigest(md5);
- md5 += "/" + URIUtils::GetFileName(file_path);
- { NPT_AutoLock lock(m_FileMutex);
- NPT_CHECK(m_FileMap.Put(md5.c_str(), file_path));
- }
- return PLT_FileMediaServer::BuildSafeResourceUri(rooturi, host, md5.c_str());
-}
-
-
-/*----------------------------------------------------------------------
-| CUPnPServer::GetMimeType
-+---------------------------------------------------------------------*/
-NPT_String
-CUPnPServer::GetMimeType(const char* filename,
- const PLT_HttpRequestContext* context /* = NULL */)
-{
- NPT_String ext = URIUtils::GetExtension(filename).c_str();
- ext.TrimLeft('.');
- ext = ext.ToLowercase();
-
- return PLT_MimeType::GetMimeTypeFromExtension(ext, context);
-}
-
-/*----------------------------------------------------------------------
-| CUPnPServer::GetMimeType
-+---------------------------------------------------------------------*/
-NPT_String
-CUPnPServer::GetMimeType(const CFileItem& item,
- const PLT_HttpRequestContext* context /* = NULL */)
-{
- CStdString path = item.GetPath();
- if (item.HasVideoInfoTag() && !item.GetVideoInfoTag()->GetPath().IsEmpty()) {
- path = item.GetVideoInfoTag()->GetPath();
- } else if (item.HasMusicInfoTag() && !item.GetMusicInfoTag()->GetURL().IsEmpty()) {
- path = item.GetMusicInfoTag()->GetURL();
- }
-
- if(path.Left(8).Equals("stack://"))
- return "audio/x-mpegurl";
-
- NPT_String ext = URIUtils::GetExtension(path).c_str();
- ext.TrimLeft('.');
- ext = ext.ToLowercase();
-
- NPT_String mime;
-
- /* We always use Platinum mime type first
- as it is defined to map extension to DLNA compliant mime type
- or custom according to context (who asked for it) */
- if (!ext.IsEmpty()) {
- mime = PLT_MimeType::GetMimeTypeFromExtension(ext, context);
- if (mime == "application/octet-stream") mime = "";
- }
-
- /* if Platinum couldn't map it, default to XBMC mapping */
- if (mime.IsEmpty()) {
- NPT_String mime = item.GetMimeType().c_str();
- if (mime == "application/octet-stream") mime = "";
- }
-
- /* fallback to generic mime type if not found */
- if (mime.IsEmpty()) {
- if (item.IsVideo() || item.IsVideoDb() )
- mime = "video/" + ext;
- else if (item.IsAudio() || item.IsMusicDb() )
- mime = "audio/" + ext;
- else if (item.IsPicture() )
- mime = "image/" + ext;
- }
-
- /* nothing we can figure out */
- if (mime.IsEmpty()) {
- mime = "application/octet-stream";
- }
-
- return mime;
-}
-
-/*----------------------------------------------------------------------
-| CUPnPServer::GetProtocolInfo
-+---------------------------------------------------------------------*/
-const NPT_String
-CUPnPServer::GetProtocolInfo(const CFileItem& item,
- const char* protocol,
- const PLT_HttpRequestContext* context /* = NULL */)
-{
- NPT_String proto = protocol;
-
- /* fixup the protocol just in case nothing was passed */
- if (proto.IsEmpty()) {
- proto = item.GetAsUrl().GetProtocol();
- }
-
- /*
- map protocol to right prefix and use xbmc-get for
- unsupported UPnP protocols for other xbmc clients
- TODO: add rtsp ?
- */
- if (proto == "http") {
- proto = "http-get";
- } else {
- proto = "xbmc-get";
- }
-
- /* we need a valid extension to retrieve the mimetype for the protocol info */
- NPT_String mime = GetMimeType(item, context);
- proto += ":*:" + mime + ":" + PLT_ProtocolInfo::GetDlnaExtension(mime, context);
- return proto;
-}
-
-/*----------------------------------------------------------------------
-| CUPnPServer::PopulateObjectFromTag
-+---------------------------------------------------------------------*/
-NPT_Result
-CUPnPServer::PopulateObjectFromTag(CMusicInfoTag& tag,
- PLT_MediaObject& object,
- NPT_String* file_path, /* = NULL */
- PLT_MediaItemResource* resource, /* = NULL */
- EClientQuirks quirks)
-{
- if (!tag.GetURL().IsEmpty() && file_path)
- *file_path = tag.GetURL();
-
- std::vector<std::string> genres = tag.GetGenre();
- for (unsigned int index = 0; index < genres.size(); index++)
- object.m_Affiliation.genres.Add(genres.at(index).c_str());
- object.m_Title = tag.GetTitle();
- object.m_Affiliation.album = tag.GetAlbum();
- for (unsigned int index = 0; index < tag.GetArtist().size(); index++)
- {
- object.m_People.artists.Add(tag.GetArtist().at(index).c_str());
- object.m_People.artists.Add(tag.GetArtist().at(index).c_str(), "Performer");
- }
- object.m_People.artists.Add(StringUtils::Join(!tag.GetAlbumArtist().empty() ? tag.GetAlbumArtist() : tag.GetArtist(), g_advancedSettings.m_musicItemSeparator).c_str(), "AlbumArtist");
- if(tag.GetAlbumArtist().empty())
- object.m_Creator = StringUtils::Join(tag.GetArtist(), g_advancedSettings.m_musicItemSeparator);
- else
- object.m_Creator = StringUtils::Join(tag.GetAlbumArtist(), g_advancedSettings.m_musicItemSeparator);
- object.m_MiscInfo.original_track_number = tag.GetTrackNumber();
- if(tag.GetDatabaseId() >= 0) {
- object.m_ReferenceID = NPT_String::Format("musicdb://4/%i%s", tag.GetDatabaseId(), URIUtils::GetExtension(tag.GetURL()).c_str());
- }
- if (object.m_ReferenceID == object.m_ObjectID)
- object.m_ReferenceID = "";
-
- if (resource) resource->m_Duration = tag.GetDuration();
-
- return NPT_SUCCESS;
-}
-
-/*----------------------------------------------------------------------
-| CUPnPServer::PopulateObjectFromTag
-+---------------------------------------------------------------------*/
-NPT_Result
-CUPnPServer::PopulateObjectFromTag(CVideoInfoTag& tag,
- PLT_MediaObject& object,
- NPT_String* file_path, /* = NULL */
- PLT_MediaItemResource* resource, /* = NULL */
- EClientQuirks quirks)
-{
- // some usefull buffers
- CStdStringArray strings;
-
- if (!tag.m_strFileNameAndPath.IsEmpty() && file_path)
- *file_path = tag.m_strFileNameAndPath;
-
- if (tag.m_iDbId != -1 ) {
- if (!tag.m_artist.empty()) {
- object.m_ObjectClass.type = "object.item.videoItem.musicVideoClip";
- object.m_Creator = StringUtils::Join(tag.m_artist, g_advancedSettings.m_videoItemSeparator);
- object.m_Title = tag.m_strTitle;
- object.m_ReferenceID = NPT_String::Format("videodb://3/2/%i", tag.m_iDbId);
- } else if (!tag.m_strShowTitle.IsEmpty()) {
- object.m_ObjectClass.type = "object.item.videoItem.videoBroadcast";
- object.m_Recorded.program_title = "S" + ("0" + NPT_String::FromInteger(tag.m_iSeason)).Right(2);
- object.m_Recorded.program_title += "E" + ("0" + NPT_String::FromInteger(tag.m_iEpisode)).Right(2);
- object.m_Recorded.program_title += " : " + tag.m_strTitle;
- object.m_Recorded.series_title = tag.m_strShowTitle;
- object.m_Recorded.episode_number = tag.m_iSeason * 100 + tag.m_iEpisode;
- object.m_Title = object.m_Recorded.series_title + " - " + object.m_Recorded.program_title;
- object.m_Date = tag.m_firstAired.GetAsLocalizedDate();
- if(tag.m_iSeason != -1)
- object.m_ReferenceID = NPT_String::Format("videodb://2/0/%i", tag.m_iDbId);
- } else {
- object.m_ObjectClass.type = "object.item.videoItem.movie";
- object.m_Title = tag.m_strTitle;
- object.m_Date = NPT_String::FromInteger(tag.m_iYear) + "-01-01";
- object.m_ReferenceID = NPT_String::Format("videodb://1/2/%i", tag.m_iDbId);
- }
- }
-
- if(quirks & ECLIENTQUIRKS_BASICVIDEOCLASS)
- object.m_ObjectClass.type = "object.item.videoItem";
-
- if(object.m_ReferenceID == object.m_ObjectID)
- object.m_ReferenceID = "";
-
- for (unsigned int index = 0; index < tag.m_genre.size(); index++)
- object.m_Affiliation.genres.Add(tag.m_genre.at(index).c_str());
-
- for(CVideoInfoTag::iCast it = tag.m_cast.begin();it != tag.m_cast.end();it++) {
- object.m_People.actors.Add(it->strName.c_str(), it->strRole.c_str());
- }
-
- object.m_People.director = StringUtils::Join(tag.m_director, g_advancedSettings.m_videoItemSeparator);
- for (unsigned int index = 0; index < tag.m_writingCredits.size(); index++)
- object.m_People.authors.Add(tag.m_writingCredits[index].c_str());
-
- object.m_Description.description = tag.m_strTagLine;
- object.m_Description.long_description = tag.m_strPlot;
- if (resource) resource->m_Duration = tag.m_streamDetails.GetVideoDuration();
- if (resource) resource->m_Resolution = NPT_String::FromInteger(tag.m_streamDetails.GetVideoWidth()) + "x" + NPT_String::FromInteger(tag.m_streamDetails.GetVideoHeight());
-
- return NPT_SUCCESS;
-}
-
-/*----------------------------------------------------------------------
-| CUPnPServer::CorrectAllItemsSortHack
-+---------------------------------------------------------------------*/
-const CStdString&
-CUPnPServer::CorrectAllItemsSortHack(const CStdString &item)
-{
- // This is required as in order for the "* All Albums" etc. items to sort
- // correctly, they must have fake artist/album etc. information generated.
- // This looks nasty if we attempt to render it to the GUI, thus this (further)
- // workaround
- if ((item.size() == 1 && item[0] == 0x01) || (item.size() > 1 && ((unsigned char) item[1]) == 0xff))
- return StringUtils::EmptyString;
-
- return item;
-}
-
-/*----------------------------------------------------------------------
-| CUPnPServer::BuildObject
-+---------------------------------------------------------------------*/
-PLT_MediaObject*
-CUPnPServer::BuildObject(const CFileItem& item,
- NPT_String& file_path,
- bool with_count,
- const PLT_HttpRequestContext* context /* = NULL */,
- CUPnPServer* upnp_server /* = NULL */)
-{
- PLT_MediaItemResource resource;
- PLT_MediaObject* object = NULL;
-
- CLog::Log(LOGDEBUG, "Building didl for object '%s'", (const char*)item.GetPath());
-
- EClientQuirks quirks = GetClientQuirks(context);
-
- // get list of ip addresses
- NPT_List<NPT_IpAddress> ips;
- NPT_HttpUrl rooturi;
- NPT_CHECK_LABEL(PLT_UPnPMessageHelper::GetIPAddresses(ips), failure);
-
- // if we're passed an interface where we received the request from
- // move the ip to the top
- if (context && context->GetLocalAddress().GetIpAddress().ToString() != "0.0.0.0") {
- rooturi = NPT_HttpUrl(context->GetLocalAddress().GetIpAddress().ToString(), context->GetLocalAddress().GetPort(), "/");
- ips.Remove(context->GetLocalAddress().GetIpAddress());
- ips.Insert(ips.GetFirstItem(), context->GetLocalAddress().GetIpAddress());
- }
-
- if (!item.m_bIsFolder) {
- object = new PLT_MediaItem();
- object->m_ObjectID = item.GetPath();
-
- /* Setup object type */
- if (item.IsMusicDb() || item.IsAudio()) {
- object->m_ObjectClass.type = "object.item.audioItem.musicTrack";
-
- if (item.HasMusicInfoTag()) {
- CMusicInfoTag *tag = (CMusicInfoTag*)item.GetMusicInfoTag();
- PopulateObjectFromTag(*tag, *object, &file_path, &resource, quirks);
- }
- } else if (item.IsVideoDb() || item.IsVideo()) {
- object->m_ObjectClass.type = "object.item.videoItem";
-
- if(quirks & ECLIENTQUIRKS_UNKNOWNSERIES)
- object->m_Affiliation.album = "[Unknown Series]";
-
- if (item.HasVideoInfoTag()) {
- CVideoInfoTag *tag = (CVideoInfoTag*)item.GetVideoInfoTag();
- PopulateObjectFromTag(*tag, *object, &file_path, &resource, quirks);
- }
- } else if (item.IsPicture()) {
- object->m_ObjectClass.type = "object.item.imageItem.photo";
- } else {
- object->m_ObjectClass.type = "object.item";
- }
-
- // duration of zero is invalid
- if (resource.m_Duration == 0) resource.m_Duration = -1;
-
- // Set the resource file size
- resource.m_Size = item.m_dwSize;
- if (resource.m_Size == 0) {
- struct __stat64 info;
- if(CFile::Stat((const char*)file_path, &info) == 0 && info.st_size >= 0)
- resource.m_Size = info.st_size;
- }
- if(resource.m_Size == 0)
- resource.m_Size = (NPT_LargeSize)-1;
-
- // set date
- if (object->m_Date.IsEmpty() && item.m_dateTime.IsValid()) {
- object->m_Date = item.m_dateTime.GetAsLocalizedDate();
- }
-
- if (upnp_server) {
- upnp_server->AddSafeResourceUri(object, rooturi, ips, file_path, GetProtocolInfo(item, "http", context));
- }
-
- // if the item is remote, add a direct link to the item
- if (URIUtils::IsRemote((const char*)file_path)) {
- resource.m_ProtocolInfo = PLT_ProtocolInfo(CUPnPServer::GetProtocolInfo(item, item.GetAsUrl().GetProtocol(), context));
- resource.m_Uri = file_path;
-
- // if the direct link can be served directly using http, then push it in front
- // otherwise keep the xbmc-get resource last and let a compatible client look for it
- if (resource.m_ProtocolInfo.ToString().StartsWith("xbmc", true)) {
- object->m_Resources.Add(resource);
- } else {
- object->m_Resources.Insert(object->m_Resources.GetFirstItem(), resource);
- }
- }
-
- // Some upnp clients expect all audio items to have parent root id 4
-#ifdef WMP_ID_MAPPING
- object->m_ParentID = "4";
-#endif
- } else {
- PLT_MediaContainer* container = new PLT_MediaContainer;
- object = container;
-
- /* Assign a title and id for this container */
- container->m_ObjectID = item.GetPath();
- container->m_ObjectClass.type = "object.container";
- container->m_ChildrenCount = -1;
-
- CStdStringArray strings;
-
- /* this might be overkill, but hey */
- if (item.IsMusicDb()) {
- MUSICDATABASEDIRECTORY::NODE_TYPE node = CMusicDatabaseDirectory::GetDirectoryType(item.GetPath());
- switch(node) {
- case MUSICDATABASEDIRECTORY::NODE_TYPE_ARTIST: {
- container->m_ObjectClass.type += ".person.musicArtist";
- CMusicInfoTag *tag = (CMusicInfoTag*)item.GetMusicInfoTag();
- if (tag) {
- container->m_People.artists.Add(
- CorrectAllItemsSortHack(StringUtils::Join(tag->GetArtist(), g_advancedSettings.m_musicItemSeparator)).c_str(), "Performer");
- container->m_People.artists.Add(
- CorrectAllItemsSortHack(StringUtils::Join(!tag->GetAlbumArtist().empty() ? tag->GetAlbumArtist() : tag->GetArtist(), g_advancedSettings.m_musicItemSeparator)).c_str(), "AlbumArtist");
- }
-#ifdef WMP_ID_MAPPING
- // Some upnp clients expect all artists to have parent root id 107
- container->m_ParentID = "107";
-#endif
- }
- break;
- case MUSICDATABASEDIRECTORY::NODE_TYPE_ALBUM:
- case MUSICDATABASEDIRECTORY::NODE_TYPE_ALBUM_COMPILATIONS:
- case MUSICDATABASEDIRECTORY::NODE_TYPE_ALBUM_RECENTLY_ADDED:
- case MUSICDATABASEDIRECTORY::NODE_TYPE_YEAR_ALBUM: {
- container->m_ObjectClass.type += ".album.musicAlbum";
- // for Sonos to be happy
- CMusicInfoTag *tag = (CMusicInfoTag*)item.GetMusicInfoTag();
- if (tag) {
- container->m_People.artists.Add(
- CorrectAllItemsSortHack(StringUtils::Join(tag->GetArtist(), g_advancedSettings.m_musicItemSeparator)).c_str(), "Performer");
- container->m_People.artists.Add(
- CorrectAllItemsSortHack(StringUtils::Join(!tag->GetAlbumArtist().empty() ? tag->GetAlbumArtist() : tag->GetArtist(), g_advancedSettings.m_musicItemSeparator)).c_str(), "AlbumArtist");
- container->m_Affiliation.album = CorrectAllItemsSortHack(tag->GetAlbum()).c_str();
- }
-#ifdef WMP_ID_MAPPING
- // Some upnp clients expect all albums to have parent root id 7
- container->m_ParentID = "7";
-#endif
- }
- break;
- case MUSICDATABASEDIRECTORY::NODE_TYPE_GENRE:
- container->m_ObjectClass.type += ".genre.musicGenre";
- break;
- default:
- break;
- }
- } else if (item.IsVideoDb()) {
- VIDEODATABASEDIRECTORY::NODE_TYPE node = CVideoDatabaseDirectory::GetDirectoryType(item.GetPath());
- CVideoInfoTag &tag = *(CVideoInfoTag*)item.GetVideoInfoTag();
- switch(node) {
- case VIDEODATABASEDIRECTORY::NODE_TYPE_GENRE:
- container->m_ObjectClass.type += ".genre.movieGenre";
- break;
- case VIDEODATABASEDIRECTORY::NODE_TYPE_ACTOR:
- container->m_ObjectClass.type += ".person.videoArtist";
- container->m_Creator = StringUtils::Join(tag.m_artist, g_advancedSettings.m_videoItemSeparator);
- container->m_Title = tag.m_strTitle;
- break;
- case VIDEODATABASEDIRECTORY::NODE_TYPE_TITLE_TVSHOWS:
- container->m_ObjectClass.type += ".album.videoAlbum";
- container->m_Recorded.program_title = "S" + ("0" + NPT_String::FromInteger(tag.m_iSeason)).Right(2);
- container->m_Recorded.program_title += "E" + ("0" + NPT_String::FromInteger(tag.m_iEpisode)).Right(2);
- container->m_Recorded.program_title += " : " + tag.m_strTitle;
- container->m_Recorded.series_title = tag.m_strShowTitle;
- container->m_Recorded.episode_number = tag.m_iSeason * 100 + tag.m_iEpisode;
- container->m_Title = container->m_Recorded.series_title + " - " + container->m_Recorded.program_title;
- container->m_Title = tag.m_strTitle;
- if(!tag.m_firstAired.IsValid() && tag.m_iYear)
- container->m_Date = NPT_String::FromInteger(tag.m_iYear) + "-01-01";
- else
- container->m_Date = tag.m_firstAired.GetAsLocalizedDate();
-
- for (unsigned int index = 0; index < tag.m_genre.size(); index++)
- container->m_Affiliation.genres.Add(tag.m_genre.at(index).c_str());
-
- for(CVideoInfoTag::iCast it = tag.m_cast.begin();it != tag.m_cast.end();it++) {
- container->m_People.actors.Add(it->strName.c_str(), it->strRole.c_str());
- }
-
- container->m_People.director = StringUtils::Join(tag.m_director, g_advancedSettings.m_videoItemSeparator);;
- for (unsigned int index = 0; index < tag.m_writingCredits.size(); index++)
- container->m_People.authors.Add(tag.m_writingCredits[index].c_str());
-
- container->m_Description.description = tag.m_strTagLine;
- container->m_Description.long_description = tag.m_strPlot;
-
- break;
- default:
- container->m_ObjectClass.type += ".storageFolder";
- break;
- }
- } else if (item.IsPlayList()) {
- container->m_ObjectClass.type += ".playlistContainer";
- }
-
- if(quirks & ECLIENTQUIRKS_ONLYSTORAGEFOLDER) {
- container->m_ObjectClass.type = "object.container.storageFolder";
- }
-
- /* Get the number of children for this container */
- if (with_count && upnp_server) {
- if (object->m_ObjectID.StartsWith("virtualpath://")) {
- NPT_LargeSize count = 0;
- NPT_CHECK_LABEL(NPT_File::GetSize(file_path, count), failure);
- container->m_ChildrenCount = count;
- } else {
- /* this should be a standard path */
- // TODO - get file count of this directory
- }
- }
- }
-
- // set a title for the object
- if (object->m_Title.IsEmpty()) {
- if (!item.GetLabel().IsEmpty()) {
- CStdString title = item.GetLabel();
- if (item.IsPlayList() || !item.m_bIsFolder) URIUtils::RemoveExtension(title);
- object->m_Title = title;
- } else {
- CStdString title, volumeNumber;
- CUtil::GetVolumeFromFileName(item.GetPath(), title, volumeNumber);
- if (!item.m_bIsFolder) URIUtils::RemoveExtension(title);
- object->m_Title = title;
- }
- }
- // set a thumbnail if we have one
- if (item.HasThumbnail() && upnp_server) {
- PLT_AlbumArtInfo art;
- art.uri = upnp_server->BuildSafeResourceUri(
- rooturi,
- (*ips.GetFirstItem()).ToString(),
- item.GetThumbnailImage());
- // Set DLNA profileID by extension, defaulting to JPEG.
- NPT_String ext = URIUtils::GetExtension(item.GetThumbnailImage()).c_str();
- if (strcmp(ext, ".png") == 0) {
- art.dlna_profile = "PNG_TN";
- } else {
- art.dlna_profile = "JPEG_TN";
- }
- object->m_ExtraInfo.album_arts.Add(art);
- }
-
- if (upnp_server) {
- if (item.HasProperty("fanart_image")) {
- upnp_server->AddSafeResourceUri(object, rooturi, ips, item.GetProperty("fanart_image").asString().c_str(), "xbmc.org:*:fanart:*" );
- }
- }
-
- return object;
-
-failure:
- delete object;
- return NULL;
-}
-
-/*----------------------------------------------------------------------
-| CUPnPServer::Build
-+---------------------------------------------------------------------*/
-PLT_MediaObject*
-CUPnPServer::Build(CFileItemPtr item,
- bool with_count,
- const PLT_HttpRequestContext& context,
- const char* parent_id /* = NULL */)
-{
- PLT_MediaObject* object = NULL;
- NPT_String path = item->GetPath().c_str();
-
- //HACK: temporary disabling count as it thrashes HDD
- with_count = false;
-
- CLog::Log(LOGDEBUG, "Preparing upnp object for item '%s'", (const char*)path);
-
- if (path == "virtualpath://upnproot") {
- path.TrimRight("/");
- if (path.StartsWith("virtualpath://")) {
- object = new PLT_MediaContainer;
- object->m_Title = item->GetLabel();
- object->m_ObjectClass.type = "object.container";
- object->m_ObjectID = path;
-
- // root
- object->m_ObjectID = "0";
- object->m_ParentID = "-1";
- // root has 5 children
- if (with_count) {
- ((PLT_MediaContainer*)object)->m_ChildrenCount = 5;
- }
- } else {
- goto failure;
- }
-
- } else {
- // db path handling
- NPT_String file_path, share_name;
- file_path = item->GetPath();
- share_name = "";
-
- if (path.StartsWith("musicdb://")) {
- if (path == "musicdb://" ) {
- item->SetLabel("Music Library");
- item->SetLabelPreformated(true);
- } else {
- if (!item->HasMusicInfoTag() || !item->GetMusicInfoTag()->Loaded() )
- item->LoadMusicTag();
-
- if (!item->HasThumbnail() )
- item->SetThumbnailImage(CThumbLoader::GetCachedImage(*item, "thumb"));
-
- if (item->GetLabel().IsEmpty()) {
- /* if no label try to grab it from node type */
- CStdString label;
- if (CMusicDatabaseDirectory::GetLabel((const char*)path, label)) {
- item->SetLabel(label);
- item->SetLabelPreformated(true);
- }
- }
- }
- } else if (file_path.StartsWith("videodb://")) {
- if (path == "videodb://" ) {
- item->SetLabel("Video Library");
- item->SetLabelPreformated(true);
- } else {
- if (!item->HasVideoInfoTag()) {
- XFILE::VIDEODATABASEDIRECTORY::CQueryParams params;
- XFILE::VIDEODATABASEDIRECTORY::CDirectoryNode::GetDatabaseInfo((const char*)path, params);
-
- CVideoDatabase db;
- if (!db.Open() ) return NULL;
-
- if (params.GetMovieId() >= 0 )
- db.GetMovieInfo((const char*)path, *item->GetVideoInfoTag(), params.GetMovieId());
- else if (params.GetEpisodeId() >= 0 )
- db.GetEpisodeInfo((const char*)path, *item->GetVideoInfoTag(), params.GetEpisodeId());
- else if (params.GetTvShowId() >= 0 )
- db.GetTvShowInfo((const char*)path, *item->GetVideoInfoTag(), params.GetTvShowId());
- }
-
- // try to grab title from tag
- if (item->HasVideoInfoTag() && !item->GetVideoInfoTag()->m_strTitle.IsEmpty()) {
- item->SetLabel(item->GetVideoInfoTag()->m_strTitle);
- item->SetLabelPreformated(true);
- }
-
- // try to grab it from the folder
- if (item->GetLabel().IsEmpty()) {
- CStdString label;
- if (CVideoDatabaseDirectory::GetLabel((const char*)path, label)) {
- item->SetLabel(label);
- item->SetLabelPreformated(true);
- }
- }
-
- if (!item->HasThumbnail() )
- item->SetThumbnailImage(CThumbLoader::GetCachedImage(*item, "thumb"));
- }
- }
-
- // not a virtual path directory, new system
- object = BuildObject(*item.get(), file_path, with_count, &context, this);
-
- // set parent id if passed, otherwise it should have been determined
- if (object && parent_id) {
- object->m_ParentID = parent_id;
- }
- }
-
- if (object) {
- // remap Root virtualpath://upnproot/ to id "0"
- if (object->m_ObjectID == "virtualpath://upnproot/")
- object->m_ObjectID = "0";
-
- // remap Parent Root virtualpath://upnproot/ to id "0"
- if (object->m_ParentID == "virtualpath://upnproot/")
- object->m_ParentID = "0";
- }
- return object;
-
-failure:
- delete object;
- return NULL;
-}
-
-/*----------------------------------------------------------------------
-| TranslateWMPObjectId
-+---------------------------------------------------------------------*/
-static NPT_String TranslateWMPObjectId(NPT_String id)
-{
- if (id == "0") {
- id = "virtualpath://upnproot/";
- } else if (id == "15") {
- // Xbox 360 asking for videos
- id = "videodb://";
- } else if (id == "16") {
- // Xbox 360 asking for photos
- } else if (id == "107") {
- // Sonos uses 107 for artists root container id
- id = "musicdb://2/";
- } else if (id == "7") {
- // Sonos uses 7 for albums root container id
- id = "musicdb://3/";
- } else if (id == "4") {
- // Sonos uses 4 for tracks root container id
- id = "musicdb://4/";
- }
-
- CLog::Log(LOGDEBUG, "UPnP Translated id to '%s'", (const char*)id);
- return id;
-}
-
-/*----------------------------------------------------------------------
-| CUPnPServer::OnBrowseMetadata
-+---------------------------------------------------------------------*/
-NPT_Result
-CUPnPServer::OnBrowseMetadata(PLT_ActionReference& action,
- const char* object_id,
- const char* filter,
- NPT_UInt32 starting_index,
- NPT_UInt32 requested_count,
- const char* sort_criteria,
- const PLT_HttpRequestContext& context)
-{
- NPT_COMPILER_UNUSED(sort_criteria);
- NPT_COMPILER_UNUSED(requested_count);
- NPT_COMPILER_UNUSED(starting_index);
-
- NPT_String didl;
- NPT_Reference<PLT_MediaObject> object;
- NPT_String id = TranslateWMPObjectId(object_id);
- vector<CStdString> paths;
- CFileItemPtr item;
-
- CLog::Log(LOGINFO, "Received UPnP Browse Metadata request for object '%s'", (const char*)object_id);
-
- if (id.StartsWith("virtualpath://")) {
- id.TrimRight("/");
- if (id == "virtualpath://upnproot") {
- id += "/";
- item.reset(new CFileItem((const char*)id, true));
- item->SetLabel("Root");
- item->SetLabelPreformated(true);
- object = Build(item, true, context);
- } else {
- return NPT_FAILURE;
- }
- } else {
- // determine if it's a container by calling CDirectory::Exists
- item.reset(new CFileItem((const char*)id, CDirectory::Exists((const char*)id)));
-
- // determine parent id for shared paths only
- // otherwise let db find out
- CStdString parent;
- if (!URIUtils::GetParentPath((const char*)id, parent)) parent = "0";
-
-//#ifdef WMP_ID_MAPPING
-// if (!id.StartsWith("musicdb://") && !id.StartsWith("videodb://")) {
-// parent = "";
-// }
-//#endif
-
- object = Build(item, true, context, parent.empty()?NULL:parent.c_str());
- }
-
- if (object.IsNull()) {
- /* error */
- NPT_LOG_WARNING_1("CUPnPServer::OnBrowseMetadata - Object null (%s)", object_id);
- action->SetError(701, "No Such Object.");
- return NPT_FAILURE;
- }
-
- NPT_String tmp;
- NPT_CHECK(PLT_Didl::ToDidl(*object.AsPointer(), filter, tmp));
-
- /* add didl header and footer */
- didl = didl_header + tmp + didl_footer;
-
- NPT_CHECK(action->SetArgumentValue("Result", didl));
- NPT_CHECK(action->SetArgumentValue("NumberReturned", "1"));
- NPT_CHECK(action->SetArgumentValue("TotalMatches", "1"));
-
- // update ID may be wrong here, it should be the one of the container?
- NPT_CHECK(action->SetArgumentValue("UpdateId", "0"));
-
- // TODO: We need to keep track of the overall SystemUpdateID of the CDS
-
- return NPT_SUCCESS;
-}
-
-/*----------------------------------------------------------------------
-| CUPnPServer::OnBrowseDirectChildren
-+---------------------------------------------------------------------*/
-NPT_Result
-CUPnPServer::OnBrowseDirectChildren(PLT_ActionReference& action,
- const char* object_id,
- const char* filter,
- NPT_UInt32 starting_index,
- NPT_UInt32 requested_count,
- const char* sort_criteria,
- const PLT_HttpRequestContext& context)
-{
- CFileItemList items;
- NPT_String parent_id = TranslateWMPObjectId(object_id);
-
- CLog::Log(LOGINFO, "UPnP: Received Browse DirectChildren request for object '%s', with sort criteria %s", object_id, sort_criteria);
-
- items.SetPath(CStdString(parent_id));
- if (!items.Load()) {
- // cache anything that takes more than a second to retrieve
- unsigned int time = XbmcThreads::SystemClockMillis();
-
- if (parent_id.StartsWith("virtualpath://upnproot")) {
- CFileItemPtr item;
-
- // music library
- item.reset(new CFileItem("musicdb://", true));
- item->SetLabel("Music Library");
- item->SetLabelPreformated(true);
- items.Add(item);
-
- // video library
- item.reset(new CFileItem("videodb://", true));
- item->SetLabel("Video Library");
- item->SetLabelPreformated(true);
- items.Add(item);
-
- } else {
- CDirectory::GetDirectory((const char*)parent_id, items);
- }
-
- if (items.CacheToDiscAlways() || (items.CacheToDiscIfSlow() && (XbmcThreads::SystemClockMillis() - time) > 1000 )) {
- items.Save();
- }
- }
-
- // Always sort by label
- items.Sort(SORT_METHOD_LABEL, SortOrderAscending);
-
- // Don't pass parent_id if action is Search not BrowseDirectChildren, as
- // we want the engine to determine the best parent id, not necessarily the one
- // passed
- NPT_String action_name = action->GetActionDesc().GetName();
- return BuildResponse(
- action,
- items,
- filter,
- starting_index,
- requested_count,
- sort_criteria,
- context,
- (action_name.Compare("Search", true)==0)?NULL:parent_id.GetChars());
-}
-
-/*----------------------------------------------------------------------
-| CUPnPServer::BuildResponse
-+---------------------------------------------------------------------*/
-NPT_Result
-CUPnPServer::BuildResponse(PLT_ActionReference& action,
- CFileItemList& items,
- const char* filter,
- NPT_UInt32 starting_index,
- NPT_UInt32 requested_count,
- const char* sort_criteria,
- const PLT_HttpRequestContext& context,
- const char* parent_id /* = NULL */)
-{
- NPT_COMPILER_UNUSED(sort_criteria);
-
- CLog::Log(LOGDEBUG, "Building UPnP response with filter '%s', starting @ %d with %d requested",
- (const char*)filter,
- starting_index,
- requested_count);
-
- // won't return more than UPNP_MAX_RETURNED_ITEMS items at a time to keep things smooth
- // 0 requested means as many as possible
- NPT_UInt32 max_count = (requested_count == 0)?m_MaxReturnedItems:min((unsigned long)requested_count, (unsigned long)m_MaxReturnedItems);
- NPT_UInt32 stop_index = min((unsigned long)(starting_index + max_count), (unsigned long)items.Size()); // don't return more than we can
-
- NPT_Cardinal count = 0;
- NPT_String didl = didl_header;
- PLT_MediaObjectReference object;
- for (unsigned long i=starting_index; i<stop_index; ++i) {
- object = Build(items[i], true, context, parent_id);
- if (object.IsNull()) {
- continue;
- }
-
- NPT_String tmp;
- NPT_CHECK(PLT_Didl::ToDidl(*object.AsPointer(), filter, tmp));
-
- // Neptunes string growing is dead slow for small additions
- if (didl.GetCapacity() < tmp.GetLength() + didl.GetLength()) {
- didl.Reserve((tmp.GetLength() + didl.GetLength())*2);
- }
- didl += tmp;
- ++count;
- }
-
- didl += didl_footer;
-
- CLog::Log(LOGDEBUG, "Returning UPnP response with %d items out of %d total matches",
- count,
- items.Size());
-
- NPT_CHECK(action->SetArgumentValue("Result", didl));
- NPT_CHECK(action->SetArgumentValue("NumberReturned", NPT_String::FromInteger(count)));
- NPT_CHECK(action->SetArgumentValue("TotalMatches", NPT_String::FromInteger(items.Size())));
- NPT_CHECK(action->SetArgumentValue("UpdateId", "0"));
- return NPT_SUCCESS;
-}
-
-/*----------------------------------------------------------------------
-| FindSubCriteria
-+---------------------------------------------------------------------*/
-static
-NPT_String
-FindSubCriteria(NPT_String criteria, const char* name)
-{
- NPT_String result;
- int search = criteria.Find(name);
- if (search >= 0) {
- criteria = criteria.Right(criteria.GetLength() - search - NPT_StringLength(name));
- criteria.TrimLeft(" ");
- if (criteria.GetLength()>0 && criteria[0] == '=') {
- criteria.TrimLeft("= ");
- if (criteria.GetLength()>0 && criteria[0] == '\"') {
- search = criteria.Find("\"", 1);
- if (search > 0) result = criteria.SubString(1, search-1);
- }
- }
- }
- return result;
-}
-
-/*----------------------------------------------------------------------
-| CUPnPServer::OnSearchContainer
-+---------------------------------------------------------------------*/
-NPT_Result
-CUPnPServer::OnSearchContainer(PLT_ActionReference& action,
- const char* object_id,
- const char* search_criteria,
- const char* filter,
- NPT_UInt32 starting_index,
- NPT_UInt32 requested_count,
- const char* sort_criteria,
- const PLT_HttpRequestContext& context)
-{
- CLog::Log(LOGDEBUG, "Received Search request for object '%s' with search '%s'",
- (const char*)object_id,
- (const char*)search_criteria);
-
- NPT_String id = object_id;
- if (id.StartsWith("musicdb://")) {
- // we browse for all tracks given a genre, artist or album
- if (NPT_String(search_criteria).Find("object.item.audioItem") >= 0) {
- if (!id.EndsWith("/")) id += "/";
- NPT_Cardinal count = id.SubString(10).Split("/").GetItemCount();
- // remove extra empty node count
- count = count?count-1:0;
-
- // genre
- if (id.StartsWith("musicdb://1/")) {
- // all tracks of all genres
- if (count == 1)
- id += "-1/-1/-1/";
- // all tracks of a specific genre
- else if (count == 2)
- id += "-1/-1/";
- // all tracks of a specific genre of a specfic artist
- else if (count == 3)
- id += "-1/";
- } else if (id.StartsWith("musicdb://2/")) {
- // all tracks by all artists
- if (count == 1)
- id += "-1/-1/";
- // all tracks of a specific artist
- else if (count == 2)
- id += "-1/";
- } else if (id.StartsWith("musicdb://3/")) {
- // all albums ?
- if (count == 1) id += "-1/";
- }
- }
- return OnBrowseDirectChildren(action, id, filter, starting_index, requested_count, sort_criteria, context);
- } else if (NPT_String(search_criteria).Find("object.item.audioItem") >= 0) {
- // look for artist, album & genre filters
- NPT_String genre = FindSubCriteria(search_criteria, "upnp:genre");
- NPT_String album = FindSubCriteria(search_criteria, "upnp:album");
- NPT_String artist = FindSubCriteria(search_criteria, "upnp:artist");
- // sonos looks for microsoft specific stuff
- artist = artist.GetLength()?artist:FindSubCriteria(search_criteria, "microsoft:artistPerformer");
- artist = artist.GetLength()?artist:FindSubCriteria(search_criteria, "microsoft:artistAlbumArtist");
- artist = artist.GetLength()?artist:FindSubCriteria(search_criteria, "microsoft:authorComposer");
-
- CMusicDatabase database;
- database.Open();
-
- if (genre.GetLength() > 0) {
- // all tracks by genre filtered by artist and/or album
- CStdString strPath;
- strPath.Format("musicdb://1/%ld/%ld/%ld/",
- database.GetGenreByName((const char*)genre),
- database.GetArtistByName((const char*)artist), // will return -1 if no artist
- database.GetAlbumByName((const char*)album)); // will return -1 if no album
-
- return OnBrowseDirectChildren(action, strPath.c_str(), filter, starting_index, requested_count, sort_criteria, context);
- } else if (artist.GetLength() > 0) {
- // all tracks by artist name filtered by album if passed
- CStdString strPath;
- strPath.Format("musicdb://2/%ld/%ld/",
- database.GetArtistByName((const char*)artist),
- database.GetAlbumByName((const char*)album)); // will return -1 if no album
-
- return OnBrowseDirectChildren(action, strPath.c_str(), filter, starting_index, requested_count, sort_criteria, context);
- } else if (album.GetLength() > 0) {
- // all tracks by album name
- CStdString strPath;
- strPath.Format("musicdb://3/%ld/",
- database.GetAlbumByName((const char*)album));
-
- return OnBrowseDirectChildren(action, strPath.c_str(), filter, starting_index, requested_count, sort_criteria, context);
- }
-
- // browse all songs
- return OnBrowseDirectChildren(action, "musicdb://4/", filter, starting_index, requested_count, sort_criteria, context);
- } else if (NPT_String(search_criteria).Find("object.container.album.musicAlbum") >= 0) {
- // sonos filters by genre
- NPT_String genre = FindSubCriteria(search_criteria, "upnp:genre");
-
- // 360 hack: artist/albums using search
- NPT_String artist = FindSubCriteria(search_criteria, "upnp:artist");
- // sonos looks for microsoft specific stuff
- artist = artist.GetLength()?artist:FindSubCriteria(search_criteria, "microsoft:artistPerformer");
- artist = artist.GetLength()?artist:FindSubCriteria(search_criteria, "microsoft:artistAlbumArtist");
- artist = artist.GetLength()?artist:FindSubCriteria(search_criteria, "microsoft:authorComposer");
-
- CMusicDatabase database;
- database.Open();
-
- if (genre.GetLength() > 0) {
- CStdString strPath;
- strPath.Format("musicdb://1/%ld/%ld/",
- database.GetGenreByName((const char*)genre),
- database.GetArtistByName((const char*)artist)); // no artist should return -1
- return OnBrowseDirectChildren(action, strPath.c_str(), filter, starting_index, requested_count, sort_criteria, context);
- } else if (artist.GetLength() > 0) {
- CStdString strPath;
- strPath.Format("musicdb://2/%ld/",
- database.GetArtistByName((const char*)artist));
- return OnBrowseDirectChildren(action, strPath.c_str(), filter, starting_index, requested_count, sort_criteria, context);
- }
-
- // all albums
- return OnBrowseDirectChildren(action, "musicdb://3/", filter, starting_index, requested_count, sort_criteria, context);
- } else if (NPT_String(search_criteria).Find("object.container.person.musicArtist") >= 0) {
- // Sonos filters by genre
- NPT_String genre = FindSubCriteria(search_criteria, "upnp:genre");
- if (genre.GetLength() > 0) {
- CMusicDatabase database;
- database.Open();
- CStdString strPath;
- strPath.Format("musicdb://1/%ld/", database.GetGenreByName((const char*)genre));
- return OnBrowseDirectChildren(action, strPath.c_str(), filter, starting_index, requested_count, sort_criteria, context);
- }
- return OnBrowseDirectChildren(action, "musicdb://2/", filter, starting_index, requested_count, sort_criteria, context);
- } else if (NPT_String(search_criteria).Find("object.container.genre.musicGenre") >= 0) {
- return OnBrowseDirectChildren(action, "musicdb://1/", filter, starting_index, requested_count, sort_criteria, context);
- } else if (NPT_String(search_criteria).Find("object.container.playlistContainer") >= 0) {
- return OnBrowseDirectChildren(action, "special://musicplaylists/", filter, starting_index, requested_count, sort_criteria, context);
- } else if (NPT_String(search_criteria).Find("object.item.videoItem") >= 0) {
- CFileItemList items, itemsall;
-
- CVideoDatabase database;
- if (!database.Open()) {
- action->SetError(800, "Internal Error");
- return NPT_SUCCESS;
- }
-
- if (!database.GetMoviesNav("videodb://1/2/", items)) {
- action->SetError(800, "Internal Error");
- return NPT_SUCCESS;
- }
- itemsall.Append(items);
- items.Clear();
-
- // TODO - set proper base url for this
- if (!database.GetEpisodesByWhere("videodb://2/0/", "", items, false)) {
- action->SetError(800, "Internal Error");
- return NPT_SUCCESS;
- }
- itemsall.Append(items);
- items.Clear();
-
- return BuildResponse(action, itemsall, filter, starting_index, requested_count, sort_criteria, context, NULL);
- } else if (NPT_String(search_criteria).Find("object.item.imageItem") >= 0) {
- CFileItemList items;
- return BuildResponse(action, items, filter, starting_index, requested_count, sort_criteria, context, NULL);;
- }
-
- return NPT_FAILURE;
-}
-
-/*----------------------------------------------------------------------
-| CUPnPServer::ServeFile
-+---------------------------------------------------------------------*/
-NPT_Result
-CUPnPServer::ServeFile(const NPT_HttpRequest& request,
- const NPT_HttpRequestContext& context,
- NPT_HttpResponse& response,
- const NPT_String& md5)
-{
- // Translate hash to filename
- NPT_String file_path(md5), *file_path2;
- { NPT_AutoLock lock(m_FileMutex);
- if(NPT_SUCCEEDED(m_FileMap.Get(md5, file_path2))) {
- file_path = *file_path2;
- CLog::Log(LOGDEBUG, "Received request to serve '%s' = '%s'", (const char*)md5, (const char*)file_path);
- } else {
- CLog::Log(LOGDEBUG, "Received request to serve unknown md5 '%s'", (const char*)md5);
- response.SetStatus(404, "File Not Found");
- return NPT_SUCCESS;
- }
- }
-
- // File requested
- NPT_HttpUrl rooturi(context.GetLocalAddress().GetIpAddress().ToString(), context.GetLocalAddress().GetPort(), "/");
-
- if (file_path.Left(8).Compare("stack://", true) == 0) {
-
- NPT_List<NPT_String> files = file_path.SubString(8).Split(" , ");
- if (files.GetItemCount() == 0) {
- response.SetStatus(404, "File Not Found");
- return NPT_SUCCESS;
- }
-
- NPT_String output;
- output.Reserve(file_path.GetLength()*2);
- output += "#EXTM3U\r\n";
-
- NPT_List<NPT_String>::Iterator url = files.GetFirstItem();
- for (;url;url++) {
- output += "#EXTINF:-1," + URIUtils::GetFileName((const char*)*url);
- output += "\r\n";
- output += BuildSafeResourceUri(
- rooturi,
- context.GetLocalAddress().GetIpAddress().ToString(),
- *url);
- output += "\r\n";
- }
-
- PLT_HttpHelper::SetBody(response, (const char*)output, output.GetLength());
- response.GetHeaders().SetHeader("Content-Disposition", "inline; filename=\"stack.m3u\"");
- return NPT_SUCCESS;
- }
-
- if(URIUtils::IsURL((const char*)file_path))
- {
- CStdString disp = "inline; filename=\"" + URIUtils::GetFileName((const char*)file_path) + "\"";
- response.GetHeaders().SetHeader("Content-Disposition", disp.c_str());
- }
-
- return PLT_HttpServer::ServeFile(request,
- context,
- response,
- file_path);
-}
-
-/*----------------------------------------------------------------------
-| CUPnPRenderer
-+---------------------------------------------------------------------*/
-class CUPnPRenderer : public PLT_MediaRenderer
-{
-public:
- CUPnPRenderer(const char* friendly_name,
- bool show_ip = false,
- const char* uuid = NULL,
- unsigned int port = 0);
-
- void UpdateState();
-
- // Http server handler
- virtual NPT_Result ProcessHttpRequest(NPT_HttpRequest& request,
- const NPT_HttpRequestContext& context,
- NPT_HttpResponse& response);
-
- // AVTransport methods
- virtual NPT_Result OnNext(PLT_ActionReference& action);
- virtual NPT_Result OnPause(PLT_ActionReference& action);
- virtual NPT_Result OnPlay(PLT_ActionReference& action);
- virtual NPT_Result OnPrevious(PLT_ActionReference& action);
- virtual NPT_Result OnStop(PLT_ActionReference& action);
- virtual NPT_Result OnSeek(PLT_ActionReference& action);
- virtual NPT_Result OnSetAVTransportURI(PLT_ActionReference& action);
-
- // RenderingControl methods
- virtual NPT_Result OnSetVolume(PLT_ActionReference& action);
- virtual NPT_Result OnSetMute(PLT_ActionReference& action);
-
-private:
- NPT_Result SetupServices();
- NPT_Result GetMetadata(NPT_String& meta);
- NPT_Result PlayMedia(const char* uri,
- const char* metadata = NULL,
- PLT_Action* action = NULL);
- NPT_Mutex m_state;
-};
-
-/*----------------------------------------------------------------------
-| CUPnPRenderer::CUPnPRenderer
-+---------------------------------------------------------------------*/
-CUPnPRenderer::CUPnPRenderer(const char* friendly_name,
- bool show_ip /* = false */,
- const char* uuid /* = NULL */,
- unsigned int port /* = 0 */) :
- PLT_MediaRenderer(friendly_name,
- show_ip,
- uuid,
- port)
-{
-}
-
-/*----------------------------------------------------------------------
-| CUPnPRenderer::SetupServices
-+---------------------------------------------------------------------*/
-NPT_Result
-CUPnPRenderer::SetupServices()
-{
- NPT_CHECK(PLT_MediaRenderer::SetupServices());
-
- // update what we can play
- PLT_Service* service = NULL;
- NPT_CHECK_FATAL(FindServiceByType("urn:schemas-upnp-org:service:ConnectionManager:1", service));
- service->SetStateVariable("SinkProtocolInfo"
- ,"http-get:*:*:*"
- ",xbmc-get:*:*:*"
- ",http-get:*:audio/mpegurl:*"
- ",http-get:*:audio/mpeg:*"
- ",http-get:*:audio/mpeg3:*"
- ",http-get:*:audio/mp3:*"
- ",http-get:*:audio/mp4:*"
- ",http-get:*:audio/basic:*"
- ",http-get:*:audio/midi:*"
- ",http-get:*:audio/ulaw:*"
- ",http-get:*:audio/ogg:*"
- ",http-get:*:audio/DVI4:*"
- ",http-get:*:audio/G722:*"
- ",http-get:*:audio/G723:*"
- ",http-get:*:audio/G726-16:*"
- ",http-get:*:audio/G726-24:*"
- ",http-get:*:audio/G726-32:*"
- ",http-get:*:audio/G726-40:*"
- ",http-get:*:audio/G728:*"
- ",http-get:*:audio/G729:*"
- ",http-get:*:audio/G729D:*"
- ",http-get:*:audio/G729E:*"
- ",http-get:*:audio/GSM:*"
- ",http-get:*:audio/GSM-EFR:*"
- ",http-get:*:audio/L8:*"
- ",http-get:*:audio/L16:*"
- ",http-get:*:audio/LPC:*"
- ",http-get:*:audio/MPA:*"
- ",http-get:*:audio/PCMA:*"
- ",http-get:*:audio/PCMU:*"
- ",http-get:*:audio/QCELP:*"
- ",http-get:*:audio/RED:*"
- ",http-get:*:audio/VDVI:*"
- ",http-get:*:audio/ac3:*"
- ",http-get:*:audio/vorbis:*"
- ",http-get:*:audio/speex:*"
- ",http-get:*:audio/x-aiff:*"
- ",http-get:*:audio/x-pn-realaudio:*"
- ",http-get:*:audio/x-realaudio:*"
- ",http-get:*:audio/x-wav:*"
- ",http-get:*:audio/x-ms-wma:*"
- ",http-get:*:audio/x-mpegurl:*"
- ",http-get:*:application/x-shockwave-flash:*"
- ",http-get:*:application/ogg:*"
- ",http-get:*:application/sdp:*"
- ",http-get:*:image/gif:*"
- ",http-get:*:image/jpeg:*"
- ",http-get:*:image/ief:*"
- ",http-get:*:image/png:*"
- ",http-get:*:image/tiff:*"
- ",http-get:*:video/avi:*"
- ",http-get:*:video/mpeg:*"
- ",http-get:*:video/fli:*"
- ",http-get:*:video/flv:*"
- ",http-get:*:video/quicktime:*"
- ",http-get:*:video/vnd.vivo:*"
- ",http-get:*:video/vc1:*"
- ",http-get:*:video/ogg:*"
- ",http-get:*:video/mp4:*"
- ",http-get:*:video/BT656:*"
- ",http-get:*:video/CelB:*"
- ",http-get:*:video/JPEG:*"
- ",http-get:*:video/H261:*"
- ",http-get:*:video/H263:*"
- ",http-get:*:video/H263-1998:*"
- ",http-get:*:video/H263-2000:*"
- ",http-get:*:video/MPV:*"
- ",http-get:*:video/MP2T:*"
- ",http-get:*:video/MP1S:*"
- ",http-get:*:video/MP2P:*"
- ",http-get:*:video/BMPEG:*"
- ",http-get:*:video/x-ms-wmv:*"
- ",http-get:*:video/x-ms-avi:*"
- ",http-get:*:video/x-flv:*"
- ",http-get:*:video/x-fli:*"
- ",http-get:*:video/x-ms-asf:*"
- ",http-get:*:video/x-ms-asx:*"
- ",http-get:*:video/x-ms-wmx:*"
- ",http-get:*:video/x-ms-wvx:*"
- ",http-get:*:video/x-msvideo:*"
- );
- return NPT_SUCCESS;
-}
-
-/*----------------------------------------------------------------------
-| CUPnPRenderer::ProcessHttpRequest
-+---------------------------------------------------------------------*/
-NPT_Result
-CUPnPRenderer::ProcessHttpRequest(NPT_HttpRequest& request,
- const NPT_HttpRequestContext& context,
- NPT_HttpResponse& response)
-{
- // get the address of who sent us some data back
- NPT_String ip_address = context.GetRemoteAddress().GetIpAddress().ToString();
- NPT_String method = request.GetMethod();
- NPT_String protocol = request.GetProtocol();
- NPT_HttpUrl url = request.GetUrl();
-
- if (url.GetPath() == "/thumb.jpg") {
- NPT_HttpUrlQuery query(url.GetQuery());
- NPT_String filepath = query.GetField("path");
- if (!filepath.IsEmpty()) {
- NPT_HttpEntity* entity = response.GetEntity();
- if (entity == NULL) return NPT_ERROR_INVALID_STATE;
-
- // check the method
- if (request.GetMethod() != NPT_HTTP_METHOD_GET &&
- request.GetMethod() != NPT_HTTP_METHOD_HEAD) {
- response.SetStatus(405, "Method Not Allowed");
- return NPT_SUCCESS;
- }
-
- // ensure that the request's path is a valid thumb path
- if (URIUtils::IsRemote(filepath.GetChars()) ||
- !filepath.StartsWith(g_settings.GetUserDataFolder())) {
- response.SetStatus(404, "Not Found");
- return NPT_SUCCESS;
- }
-
- // prevent hackers from accessing files outside of our root
- if ((filepath.Find("/..") >= 0) || (filepath.Find("\\..") >=0)) {
- return NPT_FAILURE;
- }
-
- // open the file
- NPT_File file(filepath);
- NPT_Result result = file.Open(NPT_FILE_OPEN_MODE_READ);
- if (NPT_FAILED(result)) {
- response.SetStatus(404, "Not Found");
- return NPT_SUCCESS;
- }
- NPT_InputStreamReference stream;
- file.GetInputStream(stream);
- entity->SetContentType(CUPnPServer::GetMimeType(filepath));
- entity->SetInputStream(stream, true);
-
- return NPT_SUCCESS;
- }
- }
-
- return PLT_MediaRenderer::ProcessHttpGetRequest(request, context, response);
-}
-
-/*----------------------------------------------------------------------
-| CUPnPRenderer::UpdateState
-+---------------------------------------------------------------------*/
-void
-CUPnPRenderer::UpdateState()
-{
- NPT_AutoLock lock(m_state);
-
- PLT_Service *avt, *rct;
- if (NPT_FAILED(FindServiceByType("urn:schemas-upnp-org:service:AVTransport:1", avt)))
- return;
- if (NPT_FAILED(FindServiceByType("urn:schemas-upnp-org:service:RenderingControl:1", rct)))
- return;
-
- /* don't update state while transitioning */
- NPT_String state;
- avt->GetStateVariableValue("TransportState", state);
- if(state == "TRANSITIONING")
- return;
-
- CStdString buffer;
- int volume;
- if (g_settings.m_bMute) {
- rct->SetStateVariable("Mute", "1");
- } else {
- rct->SetStateVariable("Mute", "0");
- }
- volume = g_application.GetVolume();
-
- buffer.Format("%d", volume);
- rct->SetStateVariable("Volume", buffer.c_str());
-
- buffer.Format("%d", 256 * (volume * 60 - 60) / 100);
- rct->SetStateVariable("VolumeDb", buffer.c_str());
-
- if (g_application.IsPlaying() || g_application.IsPaused()) {
- if (g_application.IsPaused()) {
- avt->SetStateVariable("TransportState", "PAUSED_PLAYBACK");
- } else {
- avt->SetStateVariable("TransportState", "PLAYING");
- }
-
- avt->SetStateVariable("TransportStatus", "OK");
- avt->SetStateVariable("TransportPlaySpeed", (const char*)NPT_String::FromInteger(g_application.GetPlaySpeed()));
- avt->SetStateVariable("NumberOfTracks", "1");
- avt->SetStateVariable("CurrentTrack", "1");
-
- buffer = g_infoManager.GetCurrentPlayTime(TIME_FORMAT_HH_MM_SS);
- avt->SetStateVariable("RelativeTimePosition", buffer.c_str());
- avt->SetStateVariable("AbsoluteTimePosition", buffer.c_str());
-
- buffer = g_infoManager.GetDuration(TIME_FORMAT_HH_MM_SS);
- if (buffer.length() > 0) {
- avt->SetStateVariable("CurrentTrackDuration", buffer.c_str());
- avt->SetStateVariable("CurrentMediaDuration", buffer.c_str());
- } else {
- avt->SetStateVariable("CurrentTrackDuration", "00:00:00");
- avt->SetStateVariable("CurrentMediaDuration", "00:00:00");
- }
-
- avt->SetStateVariable("AVTransportURI", g_application.CurrentFile().c_str());
- avt->SetStateVariable("CurrentTrackURI", g_application.CurrentFile().c_str());
-
- NPT_String metadata;
- avt->GetStateVariableValue("AVTransportURIMetaData", metadata);
- // try to recreate the didl dynamically if not set
- if (metadata.IsEmpty()) {
- GetMetadata(metadata);
- }
- avt->SetStateVariable("CurrentTrackMetadata", metadata);
- avt->SetStateVariable("AVTransportURIMetaData", metadata);
- } else if (g_windowManager.GetActiveWindow() == WINDOW_SLIDESHOW) {
- avt->SetStateVariable("TransportState", "PLAYING");
-
- avt->SetStateVariable("AVTransportURI" , g_infoManager.GetPictureLabel(SLIDE_FILE_PATH));
- avt->SetStateVariable("CurrentTrackURI", g_infoManager.GetPictureLabel(SLIDE_FILE_PATH));
- avt->SetStateVariable("TransportPlaySpeed", "1");
-
- CGUIWindowSlideShow *slideshow = (CGUIWindowSlideShow *)g_windowManager.GetWindow(WINDOW_SLIDESHOW);
- if (slideshow)
- {
- CStdString index;
- index.Format("%d", slideshow->NumSlides());
- avt->SetStateVariable("NumberOfTracks", index.c_str());
- index.Format("%d", slideshow->CurrentSlide());
- avt->SetStateVariable("CurrentTrack", index.c_str());
-
- }
-
- avt->SetStateVariable("CurrentTrackMetadata", "");
- avt->SetStateVariable("AVTransportURIMetaData", "");
-
- } else {
- avt->SetStateVariable("TransportState", "STOPPED");
- avt->SetStateVariable("TransportPlaySpeed", "1");
- avt->SetStateVariable("NumberOfTracks", "0");
- avt->SetStateVariable("CurrentTrack", "0");
- avt->SetStateVariable("RelativeTimePosition", "00:00:00");
- avt->SetStateVariable("AbsoluteTimePosition", "00:00:00");
- avt->SetStateVariable("CurrentTrackDuration", "00:00:00");
- avt->SetStateVariable("CurrentMediaDuration", "00:00:00");
- }
-}
-
-/*----------------------------------------------------------------------
-| CUPnPRenderer::GetMetadata
-+---------------------------------------------------------------------*/
-NPT_Result
-CUPnPRenderer::GetMetadata(NPT_String& meta)
-{
- NPT_Result res = NPT_FAILURE;
- const CFileItem &item = g_application.CurrentFileItem();
- NPT_String file_path;
- PLT_MediaObject* object = CUPnPServer::BuildObject(item, file_path, false);
- if (object) {
- // fetch the path to the thumbnail
- CStdString thumb = g_infoManager.GetImage(MUSICPLAYER_COVER, -1); //TODO: Only audio for now
-
- NPT_String ip;
- if (g_application.getNetwork().GetFirstConnectedInterface()) {
- ip = g_application.getNetwork().GetFirstConnectedInterface()->GetCurrentIPAddress().c_str();
- }
- // build url, use the internal device http server to serv the image
- NPT_HttpUrlQuery query;
- query.AddField("path", thumb.c_str());
- PLT_AlbumArtInfo art;
- art.uri = NPT_HttpUrl(
- ip,
- m_URLDescription.GetPort(),
- "/thumb.jpg",
- query.ToString()).ToString();
- art.dlna_profile = "JPEG_TN";
- object->m_ExtraInfo.album_arts.Add(art);
-
- res = PLT_Didl::ToDidl(*object, "*", meta);
- delete object;
- }
- return res;
-}
-
-/*----------------------------------------------------------------------
-| CUPnPRenderer::OnNext
-+---------------------------------------------------------------------*/
-NPT_Result
-CUPnPRenderer::OnNext(PLT_ActionReference& action)
-{
- if (g_windowManager.GetActiveWindow() == WINDOW_SLIDESHOW) {
- CAction action(ACTION_NEXT_PICTURE);
- CApplicationMessenger::Get().SendAction(action, WINDOW_SLIDESHOW);
- } else {
- CApplicationMessenger::Get().PlayListPlayerNext();
- }
- return NPT_SUCCESS;
-}
-
-/*----------------------------------------------------------------------
-| CUPnPRenderer::OnPause
-+---------------------------------------------------------------------*/
-NPT_Result
-CUPnPRenderer::OnPause(PLT_ActionReference& action)
-{
- if (g_windowManager.GetActiveWindow() == WINDOW_SLIDESHOW) {
- CAction action(ACTION_PAUSE);
- CApplicationMessenger::Get().SendAction(action, WINDOW_SLIDESHOW);
- } else if (!g_application.IsPaused())
- CApplicationMessenger::Get().MediaPause();
- return NPT_SUCCESS;
-}
-
-/*----------------------------------------------------------------------
-| CUPnPRenderer::OnPlay
-+---------------------------------------------------------------------*/
-NPT_Result
-CUPnPRenderer::OnPlay(PLT_ActionReference& action)
-{
- if (g_windowManager.GetActiveWindow() == WINDOW_SLIDESHOW) {
- return NPT_SUCCESS;
- } else if (g_application.IsPaused()) {
- CApplicationMessenger::Get().MediaPause();
- } else if (!g_application.IsPlaying()) {
- NPT_String uri, meta;
- PLT_Service* service;
- // look for value set previously by SetAVTransportURI
- NPT_CHECK_SEVERE(FindServiceByType("urn:schemas-upnp-org:service:AVTransport:1", service));
- NPT_CHECK_SEVERE(service->GetStateVariableValue("AVTransportURI", uri));
- NPT_CHECK_SEVERE(service->GetStateVariableValue("AVTransportURIMetaData", meta));
-
- // if not set, use the current file being played
- PlayMedia(uri, meta);
- }
- return NPT_SUCCESS;
-}
-
-/*----------------------------------------------------------------------
-| CUPnPRenderer::OnPrevious
-+---------------------------------------------------------------------*/
-NPT_Result
-CUPnPRenderer::OnPrevious(PLT_ActionReference& action)
-{
- if (g_windowManager.GetActiveWindow() == WINDOW_SLIDESHOW) {
- CAction action(ACTION_PREV_PICTURE);
- CApplicationMessenger::Get().SendAction(action, WINDOW_SLIDESHOW);
- } else {
- CApplicationMessenger::Get().PlayListPlayerPrevious();
- }
- return NPT_SUCCESS;
-}
-
-/*----------------------------------------------------------------------
-| CUPnPRenderer::OnStop
-+---------------------------------------------------------------------*/
-NPT_Result
-CUPnPRenderer::OnStop(PLT_ActionReference& action)
-{
- if (g_windowManager.GetActiveWindow() == WINDOW_SLIDESHOW) {
- CAction action(ACTION_STOP);
- CApplicationMessenger::Get().SendAction(action, WINDOW_SLIDESHOW);
- } else {
- CApplicationMessenger::Get().MediaStop();
- }
- return NPT_SUCCESS;
-}
-
-/*----------------------------------------------------------------------
-| CUPnPRenderer::OnSetAVTransportURI
-+---------------------------------------------------------------------*/
-NPT_Result
-CUPnPRenderer::OnSetAVTransportURI(PLT_ActionReference& action)
-{
- NPT_String uri, meta;
- PLT_Service* service;
- NPT_CHECK_SEVERE(FindServiceByType("urn:schemas-upnp-org:service:AVTransport:1", service));
-
- NPT_CHECK_SEVERE(action->GetArgumentValue("CurrentURI", uri));
- NPT_CHECK_SEVERE(action->GetArgumentValue("CurrentURIMetaData", meta));
-
- // if not playing already, just keep around uri & metadata
- // and wait for play command
- if (!g_application.IsPlaying() && g_windowManager.GetActiveWindow() != WINDOW_SLIDESHOW) {
- service->SetStateVariable("TransportState", "STOPPED");
- service->SetStateVariable("TransportStatus", "OK");
- service->SetStateVariable("TransportPlaySpeed", "1");
- service->SetStateVariable("AVTransportURI", uri);
- service->SetStateVariable("AVTransportURIMetaData", meta);
-
- NPT_CHECK_SEVERE(action->SetArgumentsOutFromStateVariable());
- return NPT_SUCCESS;
- }
-
- return PlayMedia(uri, meta, action.AsPointer());
-}
-
-/*----------------------------------------------------------------------
-| CUPnPRenderer::PlayMedia
-+---------------------------------------------------------------------*/
-NPT_Result
-CUPnPRenderer::PlayMedia(const char* uri, const char* meta, PLT_Action* action)
-{
- bool bImageFile = false;
- PLT_Service* service;
- NPT_CHECK_SEVERE(FindServiceByType("urn:schemas-upnp-org:service:AVTransport:1", service));
-
- { NPT_AutoLock lock(m_state);
- service->SetStateVariable("TransportState", "TRANSITIONING");
- service->SetStateVariable("TransportStatus", "OK");
- }
-
- PLT_MediaObjectListReference list;
- PLT_MediaObject* object = NULL;
-
- if (meta && NPT_SUCCEEDED(PLT_Didl::FromDidl(meta, list))) {
- list->Get(0, object);
- }
-
- if (object) {
- CFileItem item(uri, false);
-
- PLT_MediaItemResource* res = object->m_Resources.GetFirstItem();
- for(NPT_Cardinal i = 0; i < object->m_Resources.GetItemCount(); i++) {
- if(object->m_Resources[i].m_Uri == uri) {
- res = &object->m_Resources[i];
- break;
- }
- }
- for(NPT_Cardinal i = 0; i < object->m_Resources.GetItemCount(); i++) {
- if(object->m_Resources[i].m_ProtocolInfo.ToString().StartsWith("xbmc-get:")) {
- res = &object->m_Resources[i];
- item.SetPath(CStdString(res->m_Uri));
- break;
- }
- }
-
- if (res && res->m_ProtocolInfo.IsValid()) {
- item.SetMimeType((const char*)res->m_ProtocolInfo.GetContentType());
- }
-
- item.m_dateTime.SetFromDateString((const char*)object->m_Date);
- item.m_strTitle = (const char*)object->m_Title;
- item.SetLabel((const char*)object->m_Title);
- item.SetLabelPreformated(true);
- if (object->m_ExtraInfo.album_arts.GetItem(0)) {
- // only considers first album art
- item.SetThumbnailImage((const char*)object->m_ExtraInfo.album_arts.GetItem(0)->uri);
- }
- if (object->m_ObjectClass.type.StartsWith("object.item.audioItem")) {
- if(NPT_SUCCEEDED(CUPnP::PopulateTagFromObject(*item.GetMusicInfoTag(), *object, res)))
- item.SetLabelPreformated(false);
- } else if (object->m_ObjectClass.type.StartsWith("object.item.videoItem")) {
- if(NPT_SUCCEEDED(CUPnP::PopulateTagFromObject(*item.GetVideoInfoTag(), *object, res)))
- item.SetLabelPreformated(false);
- } else if (object->m_ObjectClass.type.StartsWith("object.item.imageItem")) {
- bImageFile = true;
- }
- bImageFile?CApplicationMessenger::Get().PictureShow(item.GetPath())
- :CApplicationMessenger::Get().MediaPlay(item);
- } else {
- bImageFile = NPT_String(PLT_MediaObject::GetUPnPClass(uri)).StartsWith("object.item.imageItem", true);
-
- bImageFile?CApplicationMessenger::Get().PictureShow((const char*)uri)
- :CApplicationMessenger::Get().MediaPlay((const char*)uri);
- }
-
- if (g_application.IsPlaying() || g_windowManager.GetActiveWindow() == WINDOW_SLIDESHOW) {
- NPT_AutoLock lock(m_state);
- service->SetStateVariable("TransportState", "PLAYING");
- service->SetStateVariable("TransportStatus", "OK");
- service->SetStateVariable("AVTransportURI", uri);
- service->SetStateVariable("AVTransportURIMetaData", meta);
- } else {
- NPT_AutoLock lock(m_state);
- service->SetStateVariable("TransportState", "STOPPED");
- service->SetStateVariable("TransportStatus", "ERROR_OCCURRED");
- }
-
- if (action) {
- NPT_CHECK_SEVERE(action->SetArgumentsOutFromStateVariable());
- }
- return NPT_SUCCESS;
-}
-
-/*----------------------------------------------------------------------
-| CUPnPRenderer::OnSetVolume
-+---------------------------------------------------------------------*/
-NPT_Result
-CUPnPRenderer::OnSetVolume(PLT_ActionReference& action)
-{
- NPT_String volume;
- NPT_CHECK_SEVERE(action->GetArgumentValue("DesiredVolume", volume));
- g_application.SetVolume((float)strtod((const char*)volume, NULL));
- return NPT_SUCCESS;
-}
-
-/*----------------------------------------------------------------------
-| CUPnPRenderer::OnSetMute
-+---------------------------------------------------------------------*/
-NPT_Result
-CUPnPRenderer::OnSetMute(PLT_ActionReference& action)
-{
- NPT_String mute;
- NPT_CHECK_SEVERE(action->GetArgumentValue("DesiredMute",mute));
- if((mute == "1") ^ g_settings.m_bMute)
- g_application.ToggleMute();
- return NPT_SUCCESS;
-}
-
-/*----------------------------------------------------------------------
-| CUPnPRenderer::OnSeek
-+---------------------------------------------------------------------*/
-NPT_Result
-CUPnPRenderer::OnSeek(PLT_ActionReference& action)
-{
- if (!g_application.IsPlaying()) return NPT_ERROR_INVALID_STATE;
-
- NPT_String unit, target;
- NPT_CHECK_SEVERE(action->GetArgumentValue("Unit", unit));
- NPT_CHECK_SEVERE(action->GetArgumentValue("Target", target));
-
- if (!unit.Compare("REL_TIME")) {
- // converts target to seconds
- NPT_UInt32 seconds;
- NPT_CHECK_SEVERE(PLT_Didl::ParseTimeStamp(target, seconds));
- g_application.SeekTime(seconds);
- }
-
- return NPT_SUCCESS;
-}
-
-/*----------------------------------------------------------------------
-| CRendererReferenceHolder class
-+---------------------------------------------------------------------*/
-class CRendererReferenceHolder
-{
-public:
- PLT_DeviceHostReference m_Device;
-};
-
-/*----------------------------------------------------------------------
-| CMediaBrowser class
-+---------------------------------------------------------------------*/
-class CMediaBrowser : public PLT_SyncMediaBrowser,
- public PLT_MediaContainerChangesListener
-{
-public:
- CMediaBrowser(PLT_CtrlPointReference& ctrlPoint)
- : PLT_SyncMediaBrowser(ctrlPoint, true)
- {
- SetContainerListener(this);
- }
-
- // PLT_MediaBrowser methods
- virtual bool OnMSAdded(PLT_DeviceDataReference& device)
- {
- CGUIMessage message(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_PATH);
- message.SetStringParam("upnp://");
- g_windowManager.SendThreadMessage(message);
-
- return PLT_SyncMediaBrowser::OnMSAdded(device);
- }
- virtual void OnMSRemoved(PLT_DeviceDataReference& device)
- {
- PLT_SyncMediaBrowser::OnMSRemoved(device);
-
- CGUIMessage message(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_PATH);
- message.SetStringParam("upnp://");
- g_windowManager.SendThreadMessage(message);
-
- PLT_SyncMediaBrowser::OnMSRemoved(device);
- }
-
- // PLT_MediaContainerChangesListener methods
- virtual void OnContainerChanged(PLT_DeviceDataReference& device,
- const char* item_id,
- const char* update_id)
- {
- NPT_String path = "upnp://"+device->GetUUID()+"/";
- if (!NPT_StringsEqual(item_id, "0")) {
- CStdString id = item_id;
- CURL::Encode(id);
- path += id.c_str();
- path += "/";
- }
-
- CGUIMessage message(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_PATH);
- message.SetStringParam(path.GetChars());
- g_windowManager.SendThreadMessage(message);
- }
-};
-
-
-/*----------------------------------------------------------------------
-| CUPnP::CUPnP
-+---------------------------------------------------------------------*/
-CUPnP::CUPnP() :
- m_MediaBrowser(NULL),
- m_ServerHolder(new CDeviceHostReferenceHolder()),
- m_RendererHolder(new CRendererReferenceHolder()),
- m_CtrlPointHolder(new CCtrlPointReferenceHolder())
-{
- // initialize upnp context
- m_UPnP = new PLT_UPnP();
-
- // keep main IP around
- if (g_application.getNetwork().GetFirstConnectedInterface()) {
- m_IP = g_application.getNetwork().GetFirstConnectedInterface()->GetCurrentIPAddress().c_str();
- }
- NPT_List<NPT_IpAddress> list;
- if (NPT_SUCCEEDED(PLT_UPnPMessageHelper::GetIPAddresses(list))) {
- m_IP = (*(list.GetFirstItem())).ToString();
- }
-
- // start upnp monitoring
- m_UPnP->Start();
-}
-
-/*----------------------------------------------------------------------
-| CUPnP::~CUPnP
-+---------------------------------------------------------------------*/
-CUPnP::~CUPnP()
-{
- m_UPnP->Stop();
- StopClient();
- StopServer();
-
- delete m_UPnP;
- delete m_ServerHolder;
- delete m_RendererHolder;
- delete m_CtrlPointHolder;
-}
-
-/*----------------------------------------------------------------------
-| CUPnP::GetInstance
-+---------------------------------------------------------------------*/
-CUPnP*
-CUPnP::GetInstance()
-{
- if (!upnp) {
- upnp = new CUPnP();
- }
-
- return upnp;
-}
-
-/*----------------------------------------------------------------------
-| CUPnP::ReleaseInstance
-+---------------------------------------------------------------------*/
-void
-CUPnP::ReleaseInstance(bool bWait)
-{
- if (upnp) {
- CUPnP* _upnp = upnp;
- upnp = NULL;
-
- if (bWait) {
- delete _upnp;
- } else {
- // since it takes a while to clean up
- // starts a detached thread to do this
- CUPnPCleaner* cleaner = new CUPnPCleaner(_upnp);
- cleaner->Start();
- }
- }
-}
-
-/*----------------------------------------------------------------------
-| CUPnP::StartClient
-+---------------------------------------------------------------------*/
-void
-CUPnP::StartClient()
-{
- if (!m_CtrlPointHolder->m_CtrlPoint.IsNull()) return;
-
- // create controlpoint
- m_CtrlPointHolder->m_CtrlPoint = new PLT_CtrlPoint();
-
- // start it
- m_UPnP->AddCtrlPoint(m_CtrlPointHolder->m_CtrlPoint);
-
- // start browser
- m_MediaBrowser = new CMediaBrowser(m_CtrlPointHolder->m_CtrlPoint);
-}
-
-/*----------------------------------------------------------------------
-| CUPnP::StopClient
-+---------------------------------------------------------------------*/
-void
-CUPnP::StopClient()
-{
- if (m_CtrlPointHolder->m_CtrlPoint.IsNull()) return;
-
- m_UPnP->RemoveCtrlPoint(m_CtrlPointHolder->m_CtrlPoint);
- m_CtrlPointHolder->m_CtrlPoint = NULL;
-
- delete m_MediaBrowser;
- m_MediaBrowser = NULL;
-}
-
-/*----------------------------------------------------------------------
-| CUPnP::CreateServer
-+---------------------------------------------------------------------*/
-CUPnPServer*
-CUPnP::CreateServer(int port /* = 0 */)
-{
- CUPnPServer* device =
- new CUPnPServer(g_infoManager.GetLabel(SYSTEM_FRIENDLY_NAME),
- g_settings.m_UPnPUUIDServer.length()?g_settings.m_UPnPUUIDServer.c_str():NULL,
- port);
-
- // trying to set optional upnp values for XP UPnP UI Icons to detect us
- // but it doesn't work anyways as it requires multicast for XP to detect us
- device->m_PresentationURL =
- NPT_HttpUrl(m_IP,
- atoi(g_guiSettings.GetString("services.webserverport")),
- "/").ToString();
-
- device->m_ModelName = "XBMC Media Center";
- device->m_ModelNumber = g_infoManager.GetVersion().c_str();
- device->m_ModelDescription = "XBMC Media Center - Media Server";
- device->m_ModelURL = "http://www.xbmc.org/";
- device->m_Manufacturer = "Team XBMC";
- device->m_ManufacturerURL = "http://www.xbmc.org/";
-
- device->SetDelegate(device);
- return device;
-}
-
-/*----------------------------------------------------------------------
-| CUPnP::StartServer
-+---------------------------------------------------------------------*/
-void
-CUPnP::StartServer()
-{
- if (!m_ServerHolder->m_Device.IsNull()) return;
-
- // load upnpserver.xml so that g_settings.m_vecUPnPMusiCMediaSources, etc.. are loaded
- CStdString filename;
- URIUtils::AddFileToFolder(g_settings.GetUserDataFolder(), "upnpserver.xml", filename);
- g_settings.LoadUPnPXml(filename);
-
- // create the server with a XBox compatible friendlyname and UUID from upnpserver.xml if found
- m_ServerHolder->m_Device = CreateServer(g_settings.m_UPnPPortServer);
-
- // start server
- NPT_Result res = m_UPnP->AddDevice(m_ServerHolder->m_Device);
- if (NPT_FAILED(res)) {
- // if the upnp device port was not 0, it could have failed because
- // of port being in used, so restart with a random port
- if (g_settings.m_UPnPPortServer > 0) m_ServerHolder->m_Device = CreateServer(0);
-
- res = m_UPnP->AddDevice(m_ServerHolder->m_Device);
- }
-
- // save port but don't overwrite saved settings if port was random
- if (NPT_SUCCEEDED(res)) {
- if (g_settings.m_UPnPPortServer == 0) {
- g_settings.m_UPnPPortServer = m_ServerHolder->m_Device->GetPort();
- }
- CUPnPServer::m_MaxReturnedItems = UPNP_DEFAULT_MAX_RETURNED_ITEMS;
- if (g_settings.m_UPnPMaxReturnedItems > 0) {
- // must be > UPNP_DEFAULT_MIN_RETURNED_ITEMS
- CUPnPServer::m_MaxReturnedItems = max(UPNP_DEFAULT_MIN_RETURNED_ITEMS, g_settings.m_UPnPMaxReturnedItems);
- }
- g_settings.m_UPnPMaxReturnedItems = CUPnPServer::m_MaxReturnedItems;
- }
-
- // save UUID
- g_settings.m_UPnPUUIDServer = m_ServerHolder->m_Device->GetUUID();
- g_settings.SaveUPnPXml(filename);
-}
-
-/*----------------------------------------------------------------------
-| CUPnP::StopServer
-+---------------------------------------------------------------------*/
-void
-CUPnP::StopServer()
-{
- if (m_ServerHolder->m_Device.IsNull()) return;
-
- m_UPnP->RemoveDevice(m_ServerHolder->m_Device);
- m_ServerHolder->m_Device = NULL;
-}
-
-/*----------------------------------------------------------------------
-| CUPnP::CreateRenderer
-+---------------------------------------------------------------------*/
-CUPnPRenderer*
-CUPnP::CreateRenderer(int port /* = 0 */)
-{
- CUPnPRenderer* device =
- new CUPnPRenderer(g_infoManager.GetLabel(SYSTEM_FRIENDLY_NAME),
- false,
- (g_settings.m_UPnPUUIDRenderer.length() ? g_settings.m_UPnPUUIDRenderer.c_str() : NULL),
- port);
-
- device->m_PresentationURL =
- NPT_HttpUrl(m_IP,
- atoi(g_guiSettings.GetString("services.webserverport")),
- "/").ToString();
- device->m_ModelName = "XBMC Media Center";
- device->m_ModelNumber = g_infoManager.GetVersion().c_str();
- device->m_ModelDescription = "XBMC Media Center - Media Renderer";
- device->m_ModelURL = "http://www.xbmc.org/";
- device->m_Manufacturer = "Team XBMC";
- device->m_ManufacturerURL = "http://www.xbmc.org/";
-
- return device;
-}
-
-/*----------------------------------------------------------------------
-| CUPnP::StartRenderer
-+---------------------------------------------------------------------*/
-void CUPnP::StartRenderer()
-{
- if (!m_RendererHolder->m_Device.IsNull()) return;
-
- CStdString filename;
- URIUtils::AddFileToFolder(g_settings.GetUserDataFolder(), "upnpserver.xml", filename);
- g_settings.LoadUPnPXml(filename);
-
- m_RendererHolder->m_Device = CreateRenderer(g_settings.m_UPnPPortRenderer);
-
- NPT_Result res = m_UPnP->AddDevice(m_RendererHolder->m_Device);
-
- // failed most likely because port is in use, try again with random port now
- if (NPT_FAILED(res) && g_settings.m_UPnPPortRenderer != 0) {
- m_RendererHolder->m_Device = CreateRenderer(0);
-
- res = m_UPnP->AddDevice(m_RendererHolder->m_Device);
- }
-
- // save port but don't overwrite saved settings if random
- if (NPT_SUCCEEDED(res) && g_settings.m_UPnPPortRenderer == 0) {
- g_settings.m_UPnPPortRenderer = m_RendererHolder->m_Device->GetPort();
- }
-
- // save UUID
- g_settings.m_UPnPUUIDRenderer = m_RendererHolder->m_Device->GetUUID();
- g_settings.SaveUPnPXml(filename);
-}
-
-/*----------------------------------------------------------------------
-| CUPnP::StopRenderer
-+---------------------------------------------------------------------*/
-void CUPnP::StopRenderer()
-{
- if (m_RendererHolder->m_Device.IsNull()) return;
-
- m_UPnP->RemoveDevice(m_RendererHolder->m_Device);
- m_RendererHolder->m_Device = NULL;
-}
-
-/*----------------------------------------------------------------------
-| CUPnP::UpdateState
-+---------------------------------------------------------------------*/
-void CUPnP::UpdateState()
-{
- if (!m_RendererHolder->m_Device.IsNull())
- ((CUPnPRenderer*)m_RendererHolder->m_Device.AsPointer())->UpdateState();
-}
-
-int CUPnP::PopulateTagFromObject(CMusicInfoTag& tag,
- PLT_MediaObject& object,
- PLT_MediaItemResource* resource /* = NULL */)
-{
- tag.SetTitle((const char*)object.m_Title);
- tag.SetArtist((const char*)object.m_Creator);
- for(PLT_PersonRoles::Iterator it = object.m_People.artists.GetFirstItem(); it; it++) {
- if (it->role == "") tag.SetArtist((const char*)it->name);
- else if(it->role == "Performer") tag.SetArtist((const char*)it->name);
- else if(it->role == "AlbumArtist") tag.SetAlbumArtist((const char*)it->name);
- }
- tag.SetTrackNumber(object.m_MiscInfo.original_track_number);
- for (NPT_List<NPT_String>::Iterator it = object.m_Affiliation.genres.GetFirstItem(); it; it++)
- tag.SetGenre((const char*) *it);
- tag.SetAlbum((const char*)object.m_Affiliation.album);
- if(resource)
- tag.SetDuration(resource->m_Duration);
- tag.SetLoaded();
- return NPT_SUCCESS;
-}
-
-int CUPnP::PopulateTagFromObject(CVideoInfoTag& tag,
- PLT_MediaObject& object,
- PLT_MediaItemResource* resource /* = NULL */)
-{
- CDateTime date;
- date.SetFromDateString((const char*)object.m_Date);
-
- if(!object.m_Recorded.program_title.IsEmpty())
- {
- int episode;
- int season;
- int title = object.m_Recorded.program_title.Find(" : ");
- if(sscanf(object.m_Recorded.program_title, "S%2dE%2d", &season, &episode) == 2 && title >= 0) {
- tag.m_strTitle = object.m_Recorded.program_title.SubString(title + 3);
- tag.m_iEpisode = episode;
- tag.m_iSeason = season;
- } else {
- tag.m_strTitle = object.m_Recorded.program_title;
- tag.m_iSeason = object.m_Recorded.episode_number / 100;
- tag.m_iEpisode = object.m_Recorded.episode_number % 100;
- }
- tag.m_firstAired = date;
- }
- else
- {
- tag.m_strTitle = object.m_Title;
- tag.m_premiered = date;
- }
- tag.m_iYear = date.GetYear();
- for (unsigned int index = 0; index < object.m_Affiliation.genres.GetItemCount(); index++)
- tag.m_genre.push_back(object.m_Affiliation.genres.GetItem(index)->GetChars());
- tag.m_director = StringUtils::Split((CStdString)object.m_People.director, g_advancedSettings.m_videoItemSeparator);
- tag.m_strTagLine = object.m_Description.description;
- tag.m_strPlot = object.m_Description.long_description;
- tag.m_strShowTitle = object.m_Recorded.series_title;
-
- if(resource)
- tag.m_strRuntime.Format("%d",resource->m_Duration);
- return NPT_SUCCESS;
-}
-
-