@ -1,8 +1,14 @@
using MediaBrowser.Common.Extensions ;
using MediaBrowser.Common.Extensions ;
using MediaBrowser.Controller.Dlna ;
using MediaBrowser.Controller.Dlna ;
using MediaBrowser.Controller.Entities ;
using MediaBrowser.Controller.Library ;
using MediaBrowser.Model.Dlna ;
using MediaBrowser.Model.Entities ;
using MediaBrowser.Model.Logging ;
using MediaBrowser.Model.Logging ;
using System ;
using System ;
using System.Collections.Generic ;
using System.Collections.Generic ;
using System.Globalization ;
using System.Linq ;
using System.Text ;
using System.Text ;
using System.Xml ;
using System.Xml ;
@ -11,6 +17,9 @@ namespace MediaBrowser.Dlna.Server
public class ControlHandler
public class ControlHandler
{
{
private readonly ILogger _logger ;
private readonly ILogger _logger ;
private readonly IUserManager _userManager ;
private readonly ILibraryManager _libraryManager ;
private DeviceProfile _profile ;
private const string NS_DC = "http://purl.org/dc/elements/1.1/" ;
private const string NS_DC = "http://purl.org/dc/elements/1.1/" ;
private const string NS_DIDL = "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/" ;
private const string NS_DIDL = "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/" ;
@ -19,14 +28,29 @@ namespace MediaBrowser.Dlna.Server
private const string NS_SOAPENV = "http://schemas.xmlsoap.org/soap/envelope/" ;
private const string NS_SOAPENV = "http://schemas.xmlsoap.org/soap/envelope/" ;
private const string NS_UPNP = "urn:schemas-upnp-org:metadata-1-0/upnp/" ;
private const string NS_UPNP = "urn:schemas-upnp-org:metadata-1-0/upnp/" ;
private const int systemID = 0 ;
private int systemID = 0 ;
private readonly CultureInfo _usCulture = new CultureInfo ( "en-US" ) ;
public ControlHandler ( ILogger logger )
public ControlHandler ( ILogger logger , IUserManager userManager , ILibraryManager libraryManager )
{
{
_logger = logger ;
_logger = logger ;
_userManager = userManager ;
_libraryManager = libraryManager ;
}
}
public ControlResponse ProcessControlRequest ( ControlRequest request )
public ControlResponse ProcessControlRequest ( ControlRequest request )
{
try
{
return ProcessControlRequestInternal ( request ) ;
}
catch ( Exception ex )
{
return GetErrorResponse ( ex ) ;
}
}
private ControlResponse ProcessControlRequestInternal ( ControlRequest request )
{
{
var soap = new XmlDocument ( ) ;
var soap = new XmlDocument ( ) ;
soap . LoadXml ( request . InputXml ) ;
soap . LoadXml ( request . InputXml ) ;
@ -55,6 +79,11 @@ namespace MediaBrowser.Dlna.Server
env . DocumentElement . AppendChild ( rbody ) ;
env . DocumentElement . AppendChild ( rbody ) ;
IEnumerable < KeyValuePair < string , string > > result ;
IEnumerable < KeyValuePair < string , string > > result ;
_logger . Debug ( "Received control request {0}" , method . Name ) ;
var user = _userManager . Users . First ( ) ;
switch ( method . LocalName )
switch ( method . LocalName )
{
{
case "GetSearchCapabilities" :
case "GetSearchCapabilities" :
@ -67,13 +96,13 @@ namespace MediaBrowser.Dlna.Server
result = HandleGetSystemUpdateID ( ) ;
result = HandleGetSystemUpdateID ( ) ;
break ;
break ;
case "Browse" :
case "Browse" :
result = HandleBrowse ( sparams );
result = HandleBrowse ( sparams , user );
break ;
break ;
case "X_GetFeatureList" :
case "X_GetFeatureList" :
result = HandleXGetFeatureList ( ) ;
result = HandleXGetFeatureList ( ) ;
break ;
break ;
case "X_SetBookmark" :
case "X_SetBookmark" :
result = HandleXSetBookmark ( sparams );
result = HandleXSetBookmark ( sparams , user );
break ;
break ;
default :
default :
throw new ResourceNotFoundException ( ) ;
throw new ResourceNotFoundException ( ) ;
@ -99,41 +128,60 @@ namespace MediaBrowser.Dlna.Server
return controlResponse ;
return controlResponse ;
}
}
private Headers HandleXSetBookmark ( Headers sparams )
private ControlResponse GetErrorResponse ( Exception ex )
{
var env = new XmlDocument ( ) ;
env . AppendChild ( env . CreateXmlDeclaration ( "1.0" , "utf-8" , "yes" ) ) ;
var envelope = env . CreateElement ( "SOAP-ENV" , "Envelope" , NS_SOAPENV ) ;
env . AppendChild ( envelope ) ;
envelope . SetAttribute ( "encodingStyle" , NS_SOAPENV , "http://schemas.xmlsoap.org/soap/encoding/" ) ;
var rbody = env . CreateElement ( "SOAP-ENV:Body" , NS_SOAPENV ) ;
env . DocumentElement . AppendChild ( rbody ) ;
var fault = env . CreateElement ( "SOAP-ENV" , "Fault" , NS_SOAPENV ) ;
var faultCode = env . CreateElement ( "faultcode" ) ;
faultCode . InnerText = "500" ;
fault . AppendChild ( faultCode ) ;
var faultString = env . CreateElement ( "faultstring" ) ;
faultString . InnerText = ex . ToString ( ) ;
fault . AppendChild ( faultString ) ;
var detail = env . CreateDocumentFragment ( ) ;
detail . InnerXml = "<detail><UPnPError xmlns=\"urn:schemas-upnp-org:control-1-0\"><errorCode>401</errorCode><errorDescription>Invalid Action</errorDescription></UPnPError></detail>" ;
fault . AppendChild ( detail ) ;
rbody . AppendChild ( fault ) ;
return new ControlResponse
{
Xml = env . OuterXml
} ;
}
private IEnumerable < KeyValuePair < string , string > > HandleXSetBookmark ( IDictionary < string , string > sparams , User user )
{
{
var id = sparams [ "ObjectID" ] ;
var id = sparams [ "ObjectID" ] ;
//var item = GetItem(id) as IBookmarkable;
//if (item != null)
var newbookmark = long . Parse ( sparams [ "PosSecond" ] ) ;
//{
// var newbookmark = long.Parse(sparams["PosSecond"]);
// if (newbookmark > 30)
// {
// newbookmark -= 5;
// }
// if (newbookmark > 30 || !item.Bookmark.HasValue || item.Bookmark.Value < 60)
// {
// item.Bookmark = newbookmark;
// }
//}
return new Headers ( ) ;
return new Headers ( ) ;
}
}
private Headers HandleGetSearchCapabilities ( )
private IEnumerable < KeyValuePair < string , string > > HandleGetSearchCapabilities ( )
{
{
return new Headers { { "SearchCaps" , string . Empty } } ;
return new Headers { { "SearchCaps" , string . Empty } } ;
}
}
private Headers HandleGetSortCapabilities ( )
private IEnumerable < KeyValuePair < string , string > > HandleGetSortCapabilities ( )
{
{
return new Headers { { "SortCaps" , string . Empty } } ;
return new Headers { { "SortCaps" , string . Empty } } ;
}
}
private Headers HandleGetSystemUpdateID ( )
private IEnumerable< KeyValuePair < string , string > > HandleGetSystemUpdateID ( )
{
{
return new Headers { { "Id" , systemID . ToString ( ) } } ;
return new Headers { { "Id" , systemID . ToString ( _usCulture ) } } ;
}
}
private Headers HandleXGetFeatureList ( )
private IEnumerable< KeyValuePair < string , string > > HandleXGetFeatureList ( )
{
{
return new Headers { { "FeatureList" , GetFeatureListXml ( ) } } ;
return new Headers { { "FeatureList" , GetFeatureListXml ( ) } } ;
}
}
@ -156,14 +204,14 @@ namespace MediaBrowser.Dlna.Server
return builder . ToString ( ) ;
return builder . ToString ( ) ;
}
}
private IEnumerable < KeyValuePair < string , string > > HandleBrowse ( Headers sparams )
private IEnumerable < KeyValuePair < string , string > > HandleBrowse ( Headers sparams , User user )
{
{
var id = sparams [ "ObjectID" ] ;
var id = sparams [ "ObjectID" ] ;
var flag = sparams [ "BrowseFlag" ] ;
var flag = sparams [ "BrowseFlag" ] ;
int requested ;
int requested = 20 ;
var provided = 0 ;
var provided = 0 ;
int start ;
int start = 0 ;
if ( sparams . ContainsKey ( "RequestedCount" ) & & int . TryParse ( sparams [ "RequestedCount" ] , out requested ) & & requested < = 0 )
if ( sparams . ContainsKey ( "RequestedCount" ) & & int . TryParse ( sparams [ "RequestedCount" ] , out requested ) & & requested < = 0 )
{
{
@ -184,7 +232,327 @@ namespace MediaBrowser.Dlna.Server
didl . SetAttribute ( "xmlns:sec" , NS_SEC ) ;
didl . SetAttribute ( "xmlns:sec" , NS_SEC ) ;
result . AppendChild ( didl ) ;
result . AppendChild ( didl ) ;
return null ;
var folder = string . IsNullOrWhiteSpace ( id )
? user . RootFolder
: ( Folder ) _libraryManager . GetItemById ( new Guid ( id ) ) ;
var children = folder . GetChildren ( user , true ) . ToList ( ) ;
if ( string . Equals ( flag , "BrowseMetadata" ) )
{
Browse_AddFolder ( result , folder , children . Count ) ;
provided + + ;
}
else
{
foreach ( var i in children . OfType < Folder > ( ) )
{
if ( start > 0 )
{
start - - ;
continue ;
}
var childCount = i . GetChildren ( user , true ) . Count ( ) ;
Browse_AddFolder ( result , i , childCount ) ;
if ( + + provided = = requested )
{
break ;
}
}
if ( provided ! = requested )
{
foreach ( var i in children . Where ( i = > ! i . IsFolder ) )
{
if ( start > 0 )
{
start - - ;
continue ;
}
Browse_AddItem ( result , i , user ) ;
if ( + + provided = = requested )
{
break ;
}
}
}
}
var resXML = result . OuterXml ;
return new List < KeyValuePair < string , string > >
{
new KeyValuePair < string , string > ( "Result" , resXML ) ,
new KeyValuePair < string , string > ( "NumberReturned" , provided . ToString ( _usCulture ) ) ,
new KeyValuePair < string , string > ( "TotalMatches" , children . Count . ToString ( _usCulture ) ) ,
new KeyValuePair < string , string > ( "UpdateID" , systemID . ToString ( _usCulture ) )
} ;
}
private void Browse_AddFolder ( XmlDocument result , Folder f , int childCount )
{
var container = result . CreateElement ( string . Empty , "container" , NS_DIDL ) ;
container . SetAttribute ( "restricted" , "0" ) ;
container . SetAttribute ( "childCount" , childCount . ToString ( _usCulture ) ) ;
container . SetAttribute ( "id" , f . Id . ToString ( "N" ) ) ;
var parent = f . Parent ;
if ( parent = = null )
{
container . SetAttribute ( "parentID" , "0" ) ;
}
else
{
container . SetAttribute ( "parentID" , parent . Id . ToString ( "N" ) ) ;
}
var title = result . CreateElement ( "dc" , "title" , NS_DC ) ;
title . InnerText = f . Name ;
container . AppendChild ( title ) ;
var date = result . CreateElement ( "dc" , "date" , NS_DC ) ;
date . InnerText = f . DateModified . ToString ( "o" ) ;
container . AppendChild ( date ) ;
var objectClass = result . CreateElement ( "upnp" , "class" , NS_UPNP ) ;
objectClass . InnerText = "object.container.storageFolder" ;
container . AppendChild ( objectClass ) ;
result . DocumentElement . AppendChild ( container ) ;
}
private void Browse_AddItem ( XmlDocument result , BaseItem item , User user )
{
var element = result . CreateElement ( string . Empty , "item" , NS_DIDL ) ;
element . SetAttribute ( "restricted" , "1" ) ;
element . SetAttribute ( "id" , item . Id . ToString ( "N" ) ) ;
if ( item . Parent ! = null )
{
element . SetAttribute ( "parentID" , item . Parent . Id . ToString ( "N" ) ) ;
}
element . AppendChild ( CreateObjectClass ( result , item ) ) ;
AddBookmarkInfo ( item , user , element ) ;
AddGeneralProperties ( item , element ) ;
AddActors ( item , element ) ;
var title = result . CreateElement ( "dc" , "title" , NS_DC ) ;
title . InnerText = item . Name ;
element . AppendChild ( title ) ;
var res = result . CreateElement ( string . Empty , "res" , NS_DIDL ) ;
//res.InnerText = String.Format(
// "http://{0}:{1}{2}file/{3}",
// request.LocalEndPoint.Address,
// request.LocalEndPoint.Port,
// prefix,
// resource.Id
// );
//if (props.TryGetValue("SizeRaw", out prop))
//{
// res.SetAttribute("size", prop);
//}
//if (props.TryGetValue("Resolution", out prop))
//{
// res.SetAttribute("resolution", prop);
//}
//if (props.TryGetValue("Duration", out prop))
//{
// res.SetAttribute("duration", prop);
//}
//res.SetAttribute("protocolInfo", String.Format(
// "http-get:*:{1}:{0};DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS={2}",
// resource.PN, DlnaMaps.Mime[resource.Type], DlnaMaps.DefaultStreaming
// ));
element . AppendChild ( res ) ;
AddCover ( item , element ) ;
result . DocumentElement . AppendChild ( element ) ;
}
private XmlElement CreateObjectClass ( XmlDocument result , BaseItem item )
{
var objectClass = result . CreateElement ( "upnp" , "class" , NS_UPNP ) ;
if ( string . Equals ( item . MediaType , MediaType . Audio , StringComparison . OrdinalIgnoreCase ) )
{
objectClass . InnerText = "object.item.audioItem.musicTrack" ;
}
else if ( string . Equals ( item . MediaType , MediaType . Photo , StringComparison . OrdinalIgnoreCase ) )
{
objectClass . InnerText = "object.item.imageItem.photo" ;
}
else if ( string . Equals ( item . MediaType , MediaType . Video , StringComparison . OrdinalIgnoreCase ) )
{
objectClass . InnerText = "object.item.videoItem.movie" ;
}
else
{
throw new NotSupportedException ( ) ;
}
return objectClass ;
}
private void AddActors ( BaseItem item , XmlElement element )
{
foreach ( var actor in item . People )
{
var e = element . OwnerDocument . CreateElement ( "upnp" , "actor" , NS_UPNP ) ;
e . InnerText = actor . Name ;
element . AppendChild ( e ) ;
}
}
private void AddBookmarkInfo ( BaseItem item , User user , XmlElement element )
{
//var bookmark = bookmarkable.Bookmark;
//if (bookmark.HasValue)
//{
// var dcmInfo = item.OwnerDocument.CreateElement("sec", "dcmInfo", NS_SEC);
// dcmInfo.InnerText = string.Format("BM={0}", bookmark.Value);
// item.AppendChild(dcmInfo);
//}
}
private void AddGeneralProperties ( BaseItem item , XmlElement element )
{
//var prop = string.Empty;
//if (props.TryGetValue("DateO", out prop))
//{
// var e = item.OwnerDocument.CreateElement("dc", "date", NS_DC);
// e.InnerText = prop;
// item.AppendChild(e);
//}
//if (props.TryGetValue("Genre", out prop))
//{
// var e = item.OwnerDocument.CreateElement("upnp", "genre", NS_UPNP);
// e.InnerText = prop;
// item.AppendChild(e);
//}
if ( ! string . IsNullOrWhiteSpace ( item . Overview ) )
{
var e = element . OwnerDocument . CreateElement ( "dc" , "description" , NS_DC ) ;
e . InnerText = item . Overview ;
element . AppendChild ( e ) ;
}
//if (props.TryGetValue("Artist", out prop))
//{
// var e = item.OwnerDocument.CreateElement("upnp", "artist", NS_UPNP);
// e.SetAttribute("role", "AlbumArtist");
// e.InnerText = prop;
// item.AppendChild(e);
//}
//if (props.TryGetValue("Performer", out prop))
//{
// var e = item.OwnerDocument.CreateElement("upnp", "artist", NS_UPNP);
// e.SetAttribute("role", "Performer");
// e.InnerText = prop;
// item.AppendChild(e);
// e = item.OwnerDocument.CreateElement("dc", "creator", NS_DC);
// e.InnerText = prop;
// item.AppendChild(e);
//}
//if (props.TryGetValue("Album", out prop))
//{
// var e = item.OwnerDocument.CreateElement("upnp", "album", NS_UPNP);
// e.InnerText = prop;
// item.AppendChild(e);
//}
//if (props.TryGetValue("Track", out prop))
//{
// var e = item.OwnerDocument.CreateElement("upnp", "originalTrackNumber", NS_UPNP);
// e.InnerText = prop;
// item.AppendChild(e);
//}
//if (props.TryGetValue("Creator", out prop))
//{
// var e = item.OwnerDocument.CreateElement("dc", "creator", NS_DC);
// e.InnerText = prop;
// item.AppendChild(e);
//}
//if (props.TryGetValue("Director", out prop))
//{
// var e = item.OwnerDocument.CreateElement("upnp", "director", NS_UPNP);
// e.InnerText = prop;
// item.AppendChild(e);
//}
}
private void AddCover ( BaseItem item , XmlElement element )
{
//var result = item.OwnerDocument;
//var cover = resource as IMediaCover;
//if (cover == null)
//{
// return;
//}
//try
//{
// var c = cover.Cover;
// var curl = String.Format(
// "http://{0}:{1}{2}cover/{3}",
// request.LocalEndPoint.Address,
// request.LocalEndPoint.Port,
// prefix,
// resource.Id
// );
// var icon = result.CreateElement("upnp", "albumArtURI", NS_UPNP);
// var profile = result.CreateAttribute("dlna", "profileID", NS_DLNA);
// profile.InnerText = "JPEG_TN";
// icon.SetAttributeNode(profile);
// icon.InnerText = curl;
// item.AppendChild(icon);
// icon = result.CreateElement("upnp", "icon", NS_UPNP);
// profile = result.CreateAttribute("dlna", "profileID", NS_DLNA);
// profile.InnerText = "JPEG_TN";
// icon.SetAttributeNode(profile);
// icon.InnerText = curl;
// item.AppendChild(icon);
// var res = result.CreateElement(string.Empty, "res", NS_DIDL);
// res.InnerText = curl;
// res.SetAttribute("protocolInfo", string.Format(
// "http-get:*:{1}:{0};DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS={2}",
// c.PN, DlnaMaps.Mime[c.Type], DlnaMaps.DefaultStreaming
// ));
// var width = c.MetaWidth;
// var height = c.MetaHeight;
// if (width.HasValue && height.HasValue)
// {
// res.SetAttribute("resolution", string.Format("{0}x{1}", width.Value, height.Value));
// }
// else
// {
// res.SetAttribute("resolution", "200x200");
// }
// res.SetAttribute("protocolInfo", string.Format(
// "http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=01;DLNA.ORG_CI=1;DLNA.ORG_FLAGS={0}",
// DlnaMaps.DefaultInteractive
// ));
// item.AppendChild(res);
//}
//catch (Exception)
//{
// return;
//}
}
}
}
}
}
}