Update to 3.5.2 and .net core 2.1

This commit is contained in:
stefan 2018-09-12 19:26:21 +02:00
parent c32d865638
commit 48facb797e
1419 changed files with 27525 additions and 88927 deletions

1
.gitignore vendored
View File

@ -233,3 +233,4 @@ pip-log.txt
#Mr Developer #Mr Developer
.mr.developer.cfg .mr.developer.cfg
MediaBrowser.WebDashboard/dashboard-ui/.idea/ MediaBrowser.WebDashboard/dashboard-ui/.idea/
/.vs

View File

@ -1,73 +1,12 @@
<?xml version="1.0" encoding="utf-8"?> <Project Sdk="Microsoft.NET.Sdk">
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> <ItemGroup>
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
</ItemGroup>
<PropertyGroup> <PropertyGroup>
<MinimumVisualStudioVersion>11.0</MinimumVisualStudioVersion> <TargetFramework>netcoreapp2.1</TargetFramework>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{88AE38DF-19D7-406F-A6A9-09527719A21E}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>BDInfo</RootNamespace>
<AssemblyName>BDInfo</AssemblyName>
<DefaultLanguage>en-US</DefaultLanguage>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<TargetFrameworkProfile>Profile7</TargetFrameworkProfile>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols> </Project>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Compile Include="BDInfoSettings.cs" />
<Compile Include="BDROM.cs" />
<Compile Include="BitVector32.cs" />
<Compile Include="LanguageCodes.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TSCodecAC3.cs" />
<Compile Include="TSCodecAVC.cs" />
<Compile Include="TSCodecDTS.cs" />
<Compile Include="TSCodecDTSHD.cs" />
<Compile Include="TSCodecLPCM.cs" />
<Compile Include="TSCodecMPEG2.cs" />
<Compile Include="TSCodecMVC.cs" />
<Compile Include="TSCodecTrueHD.cs" />
<Compile Include="TSCodecVC1.cs" />
<Compile Include="TSInterleavedFile.cs" />
<Compile Include="TSPlaylistFile.cs" />
<Compile Include="TSStream.cs" />
<Compile Include="TSStreamBuffer.cs" />
<Compile Include="TSStreamClip.cs" />
<Compile Include="TSStreamClipFile.cs" />
<Compile Include="TSStreamFile.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj">
<Project>{7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}</Project>
<Name>MediaBrowser.Model</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="EmitMSBuildWarning" BeforeTargets="Build">
<Warning Text="Packages containing MSBuild targets and props files cannot be fully installed in projects targeting multiple frameworks. The MSBuild targets and props files have been ignored." />
</Target>
</Project>

View File

@ -57,8 +57,6 @@ namespace BDInfo
public Dictionary<string, TSInterleavedFile> InterleavedFiles = public Dictionary<string, TSInterleavedFile> InterleavedFiles =
new Dictionary<string, TSInterleavedFile>(); new Dictionary<string, TSInterleavedFile>();
private static List<string> ExcludeDirs = new List<string> { "ANY!", "AACS", "BDSVM", "ANYVM", "SLYVM" };
public delegate bool OnStreamClipFileScanError( public delegate bool OnStreamClipFileScanError(
TSStreamClipFile streamClipFile, Exception ex); TSStreamClipFile streamClipFile, Exception ex);
@ -77,7 +75,7 @@ namespace BDInfo
public BDROM( public BDROM(
string path, IFileSystem fileSystem, ITextEncoding textEncoding) string path, IFileSystem fileSystem, ITextEncoding textEncoding)
{ {
if (string.IsNullOrWhiteSpace(path)) if (string.IsNullOrEmpty(path))
{ {
throw new ArgumentNullException("path"); throw new ArgumentNullException("path");
} }
@ -336,7 +334,7 @@ namespace BDInfo
private FileSystemMetadata GetDirectoryBDMV( private FileSystemMetadata GetDirectoryBDMV(
string path) string path)
{ {
if (string.IsNullOrWhiteSpace(path)) if (string.IsNullOrEmpty(path))
{ {
throw new ArgumentNullException("path"); throw new ArgumentNullException("path");
} }
@ -421,7 +419,7 @@ namespace BDInfo
return dir.Name; return dir.Name;
} }
public static int CompareStreamFiles( public int CompareStreamFiles(
TSStreamFile x, TSStreamFile x,
TSStreamFile y) TSStreamFile y)
{ {

View File

@ -1,308 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BDInfo
{
using System.Diagnostics;
using System.Text;
using System;
/// <devdoc>
/// <para>Provides a simple light bit vector with easy integer or Boolean access to
/// a 32 bit storage.</para>
/// </devdoc>
public struct BitVector32
{
private uint data;
/// <devdoc>
/// <para>Initializes a new instance of the BitVector32 structure with the specified internal data.</para>
/// </devdoc>
public BitVector32(int data)
{
this.data = (uint)data;
}
/// <devdoc>
/// <para>Initializes a new instance of the BitVector32 structure with the information in the specified
/// value.</para>
/// </devdoc>
public BitVector32(BitVector32 value)
{
this.data = value.data;
}
/// <devdoc>
/// <para>Gets or sets a value indicating whether all the specified bits are set.</para>
/// </devdoc>
public bool this[int bit]
{
get
{
return (data & bit) == (uint)bit;
}
set
{
if (value)
{
data |= (uint)bit;
}
else
{
data &= ~(uint)bit;
}
}
}
/// <devdoc>
/// <para>Gets or sets the value for the specified section.</para>
/// </devdoc>
public int this[Section section]
{
get
{
return (int)((data & (uint)(section.Mask << section.Offset)) >> section.Offset);
}
set
{
value <<= section.Offset;
int offsetMask = (0xFFFF & (int)section.Mask) << section.Offset;
data = (data & ~(uint)offsetMask) | ((uint)value & (uint)offsetMask);
}
}
/// <devdoc>
/// returns the raw data stored in this bit vector...
/// </devdoc>
public int Data
{
get
{
return (int)data;
}
}
private static short CountBitsSet(short mask)
{
// yes, I know there are better algorithms, however, we know the
// bits are always right aligned, with no holes (i.e. always 00000111,
// never 000100011), so this is just fine...
//
short value = 0;
while ((mask & 0x1) != 0)
{
value++;
mask >>= 1;
}
return value;
}
/// <devdoc>
/// <para> Creates the first mask in a series.</para>
/// </devdoc>
public static int CreateMask()
{
return CreateMask(0);
}
/// <devdoc>
/// Creates the next mask in a series.
/// </devdoc>
public static int CreateMask(int previous)
{
if (previous == 0)
{
return 1;
}
if (previous == unchecked((int)0x80000000))
{
throw new InvalidOperationException("Bit vector full");
}
return previous << 1;
}
/// <devdoc>
/// Given a highValue, creates the mask
/// </devdoc>
private static short CreateMaskFromHighValue(short highValue)
{
short required = 16;
while ((highValue & 0x8000) == 0)
{
required--;
highValue <<= 1;
}
ushort value = 0;
while (required > 0)
{
required--;
value <<= 1;
value |= 0x1;
}
return unchecked((short)value);
}
/// <devdoc>
/// <para>Creates the first section in a series, with the specified maximum value.</para>
/// </devdoc>
public static Section CreateSection(short maxValue)
{
return CreateSectionHelper(maxValue, 0, 0);
}
/// <devdoc>
/// <para>Creates the next section in a series, with the specified maximum value.</para>
/// </devdoc>
public static Section CreateSection(short maxValue, Section previous)
{
return CreateSectionHelper(maxValue, previous.Mask, previous.Offset);
}
private static Section CreateSectionHelper(short maxValue, short priorMask, short priorOffset)
{
if (maxValue < 1)
{
throw new ArgumentOutOfRangeException("maxValue");
}
#if DEBUG
int maskCheck = CreateMaskFromHighValue(maxValue);
int offsetCheck = priorOffset + CountBitsSet(priorMask);
Debug.Assert(maskCheck <= short.MaxValue && offsetCheck < 32, "Overflow on BitVector32");
#endif
short offset = (short)(priorOffset + CountBitsSet(priorMask));
if (offset >= 32)
{
throw new InvalidOperationException("Bit vector full");
}
return new Section(CreateMaskFromHighValue(maxValue), offset);
}
public override bool Equals(object o)
{
if (!(o is BitVector32))
{
return false;
}
return data == ((BitVector32)o).data;
}
public override int GetHashCode()
{
return base.GetHashCode();
}
/// <devdoc>
/// </devdoc>
public static string ToString(BitVector32 value)
{
StringBuilder sb = new StringBuilder(/*"BitVector32{".Length*/12 + /*32 bits*/32 + /*"}".Length"*/1);
sb.Append("BitVector32{");
int locdata = (int)value.data;
for (int i = 0; i < 32; i++)
{
if ((locdata & 0x80000000) != 0)
{
sb.Append("1");
}
else
{
sb.Append("0");
}
locdata <<= 1;
}
sb.Append("}");
return sb.ToString();
}
/// <devdoc>
/// </devdoc>
public override string ToString()
{
return BitVector32.ToString(this);
}
/// <devdoc>
/// <para>
/// Represents an section of the vector that can contain a integer number.</para>
/// </devdoc>
public struct Section
{
private readonly short mask;
private readonly short offset;
internal Section(short mask, short offset)
{
this.mask = mask;
this.offset = offset;
}
public short Mask
{
get
{
return mask;
}
}
public short Offset
{
get
{
return offset;
}
}
public override bool Equals(object o)
{
if (o is Section)
return Equals((Section)o);
else
return false;
}
public bool Equals(Section obj)
{
return obj.mask == mask && obj.offset == offset;
}
public static bool operator ==(Section a, Section b)
{
return a.Equals(b);
}
public static bool operator !=(Section a, Section b)
{
return !(a == b);
}
public override int GetHashCode()
{
return base.GetHashCode();
}
/// <devdoc>
/// </devdoc>
public static string ToString(Section value)
{
return "Section{0x" + Convert.ToString(value.Mask, 16) + ", 0x" + Convert.ToString(value.Offset, 16) + "}";
}
/// <devdoc>
/// </devdoc>
public override string ToString()
{
return Section.ToString(this);
}
}
}
}

View File

@ -954,7 +954,7 @@ namespace BDInfo
} }
} }
public static int CompareVideoStreams( public int CompareVideoStreams(
TSVideoStream x, TSVideoStream x,
TSVideoStream y) TSVideoStream y)
{ {
@ -995,7 +995,7 @@ namespace BDInfo
} }
} }
public static int CompareAudioStreams( public int CompareAudioStreams(
TSAudioStream x, TSAudioStream x,
TSAudioStream y) TSAudioStream y)
{ {
@ -1067,7 +1067,7 @@ namespace BDInfo
} }
} }
public static int CompareTextStreams( public int CompareTextStreams(
TSTextStream x, TSTextStream x,
TSTextStream y) TSTextStream y)
{ {
@ -1123,7 +1123,7 @@ namespace BDInfo
} }
} }
private static int CompareGraphicsStreams( private int CompareGraphicsStreams(
TSGraphicsStream x, TSGraphicsStream x,
TSGraphicsStream y) TSGraphicsStream y)
{ {
@ -1189,7 +1189,7 @@ namespace BDInfo
} }
} }
private static int GetStreamTypeSortIndex(TSStreamType streamType) private int GetStreamTypeSortIndex(TSStreamType streamType)
{ {
switch (streamType) switch (streamType)
{ {

View File

@ -865,7 +865,7 @@ namespace BDInfo
k += streamInfoLength; k += streamInfoLength;
} }
} }
catch (Exception ex) catch
{ {
// TODO // TODO
//Console.WriteLine(ex.Message); //Console.WriteLine(ex.Message);

View File

@ -1,67 +1,12 @@
<?xml version="1.0" encoding="utf-8"?> <Project Sdk="Microsoft.NET.Sdk">
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> <ItemGroup>
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
</ItemGroup>
<PropertyGroup> <PropertyGroup>
<MinimumVisualStudioVersion>11.0</MinimumVisualStudioVersion> <TargetFramework>netcoreapp2.1</TargetFramework>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{713F42B5-878E-499D-A878-E4C652B1D5E8}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>DvdLib</RootNamespace>
<AssemblyName>DvdLib</AssemblyName>
<DefaultLanguage>en-US</DefaultLanguage>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<TargetFrameworkProfile>Profile7</TargetFrameworkProfile>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols> </Project>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Compile Include="BigEndianBinaryReader.cs" />
<Compile Include="Ifo\AudioAttributes.cs" />
<Compile Include="Ifo\Cell.cs" />
<Compile Include="Ifo\CellPlaybackInfo.cs" />
<Compile Include="Ifo\CellPositionInfo.cs" />
<Compile Include="Ifo\Chapter.cs" />
<Compile Include="Ifo\Dvd.cs" />
<Compile Include="Ifo\DvdTime.cs" />
<Compile Include="Ifo\PgcCommandTable.cs" />
<Compile Include="Ifo\Program.cs" />
<Compile Include="Ifo\ProgramChain.cs" />
<Compile Include="Ifo\Title.cs" />
<Compile Include="Ifo\UserOperation.cs" />
<Compile Include="Ifo\VideoAttributes.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj">
<Project>{7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}</Project>
<Name>MediaBrowser.Model</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="EmitMSBuildWarning" BeforeTargets="Build">
<Warning Text="Packages containing MSBuild targets and props files cannot be fully installed in projects targeting multiple frameworks. The MSBuild targets and props files have been ignored." />
</Target>
</Project>

View File

@ -15,8 +15,6 @@ namespace DvdLib.Ifo
public class ProgramChain public class ProgramChain
{ {
private ushort _unknown1;
private byte _programCount; private byte _programCount;
public readonly List<Program> Programs; public readonly List<Program> Programs;

View File

@ -1,15 +1,16 @@
using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Dlna;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Services; using MediaBrowser.Model.Services;
using MediaBrowser.Common.Extensions;
using System.Text;
using MediaBrowser.Controller.Net;
using System.Linq;
using Emby.Dlna.Main;
namespace MediaBrowser.Api.Dlna namespace Emby.Dlna.Api
{ {
[Route("/Dlna/{UuId}/description.xml", "GET", Summary = "Gets dlna server info")] [Route("/Dlna/{UuId}/description.xml", "GET", Summary = "Gets dlna server info")]
[Route("/Dlna/{UuId}/description", "GET", Summary = "Gets dlna server info")] [Route("/Dlna/{UuId}/description", "GET", Summary = "Gets dlna server info")]
@ -98,30 +99,55 @@ namespace MediaBrowser.Api.Dlna
[Route("/Dlna/icons/{Filename}", "GET", Summary = "Gets a server icon")] [Route("/Dlna/icons/{Filename}", "GET", Summary = "Gets a server icon")]
public class GetIcon public class GetIcon
{ {
[ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")] [ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string UuId { get; set; } public string UuId { get; set; }
[ApiMember(Name = "Filename", Description = "The icon filename", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] [ApiMember(Name = "Filename", Description = "The icon filename", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Filename { get; set; } public string Filename { get; set; }
} }
public class DlnaServerService : BaseApiService public class DlnaServerService : IService, IRequiresRequest
{ {
private readonly IDlnaManager _dlnaManager; private readonly IDlnaManager _dlnaManager;
private readonly IContentDirectory _contentDirectory;
private readonly IConnectionManager _connectionManager;
private readonly IMediaReceiverRegistrar _mediaReceiverRegistrar;
private const string XMLContentType = "text/xml; charset=UTF-8"; private const string XMLContentType = "text/xml; charset=UTF-8";
private readonly IMemoryStreamFactory _memoryStreamProvider;
public DlnaServerService(IDlnaManager dlnaManager, IContentDirectory contentDirectory, IConnectionManager connectionManager, IMediaReceiverRegistrar mediaReceiverRegistrar, IMemoryStreamFactory memoryStreamProvider) public IRequest Request { get; set; }
private IHttpResultFactory _resultFactory;
private IContentDirectory ContentDirectory
{
get
{
return DlnaEntryPoint.Current.ContentDirectory;
}
}
private IConnectionManager ConnectionManager
{
get
{
return DlnaEntryPoint.Current.ConnectionManager;
}
}
private IMediaReceiverRegistrar MediaReceiverRegistrar
{
get
{
return DlnaEntryPoint.Current.MediaReceiverRegistrar;
}
}
public DlnaServerService(IDlnaManager dlnaManager, IHttpResultFactory httpResultFactory)
{ {
_dlnaManager = dlnaManager; _dlnaManager = dlnaManager;
_contentDirectory = contentDirectory; _resultFactory = httpResultFactory;
_connectionManager = connectionManager; }
_mediaReceiverRegistrar = mediaReceiverRegistrar;
_memoryStreamProvider = memoryStreamProvider; private string GetHeader(string name)
{
return Request.Headers[name];
} }
public object Get(GetDescriptionXml request) public object Get(GetDescriptionXml request)
@ -130,49 +156,53 @@ namespace MediaBrowser.Api.Dlna
var serverAddress = url.Substring(0, url.IndexOf("/dlna/", StringComparison.OrdinalIgnoreCase)); var serverAddress = url.Substring(0, url.IndexOf("/dlna/", StringComparison.OrdinalIgnoreCase));
var xml = _dlnaManager.GetServerDescriptionXml(Request.Headers.ToDictionary(), request.UuId, serverAddress); var xml = _dlnaManager.GetServerDescriptionXml(Request.Headers.ToDictionary(), request.UuId, serverAddress);
return ResultFactory.GetResult(xml, XMLContentType); var cacheLength = TimeSpan.FromDays(1);
var cacheKey = Request.RawUrl.GetMD5();
var bytes = Encoding.UTF8.GetBytes(xml);
return _resultFactory.GetStaticResult(Request, cacheKey, null, cacheLength, XMLContentType, () => Task.FromResult<Stream>(new MemoryStream(bytes)));
} }
public object Get(GetContentDirectory request) public object Get(GetContentDirectory request)
{ {
var xml = _contentDirectory.GetServiceXml(Request.Headers.ToDictionary()); var xml = ContentDirectory.GetServiceXml(Request.Headers.ToDictionary());
return ResultFactory.GetResult(xml, XMLContentType); return _resultFactory.GetResult(Request, xml, XMLContentType);
} }
public object Get(GetMediaReceiverRegistrar request) public object Get(GetMediaReceiverRegistrar request)
{ {
var xml = _mediaReceiverRegistrar.GetServiceXml(Request.Headers.ToDictionary()); var xml = MediaReceiverRegistrar.GetServiceXml(Request.Headers.ToDictionary());
return ResultFactory.GetResult(xml, XMLContentType); return _resultFactory.GetResult(Request, xml, XMLContentType);
} }
public object Get(GetConnnectionManager request) public object Get(GetConnnectionManager request)
{ {
var xml = _connectionManager.GetServiceXml(Request.Headers.ToDictionary()); var xml = ConnectionManager.GetServiceXml(Request.Headers.ToDictionary());
return ResultFactory.GetResult(xml, XMLContentType); return _resultFactory.GetResult(Request, xml, XMLContentType);
} }
public object Post(ProcessMediaReceiverRegistrarControlRequest request) public object Post(ProcessMediaReceiverRegistrarControlRequest request)
{ {
var response = PostAsync(request.RequestStream, _mediaReceiverRegistrar); var response = PostAsync(request.RequestStream, MediaReceiverRegistrar);
return ResultFactory.GetResult(response.Xml, XMLContentType); return _resultFactory.GetResult(Request, response.Xml, XMLContentType);
} }
public object Post(ProcessContentDirectoryControlRequest request) public object Post(ProcessContentDirectoryControlRequest request)
{ {
var response = PostAsync(request.RequestStream, _contentDirectory); var response = PostAsync(request.RequestStream, ContentDirectory);
return ResultFactory.GetResult(response.Xml, XMLContentType); return _resultFactory.GetResult(Request, response.Xml, XMLContentType);
} }
public object Post(ProcessConnectionManagerControlRequest request) public object Post(ProcessConnectionManagerControlRequest request)
{ {
var response = PostAsync(request.RequestStream, _connectionManager); var response = PostAsync(request.RequestStream, ConnectionManager);
return ResultFactory.GetResult(response.Xml, XMLContentType); return _resultFactory.GetResult(Request, response.Xml, XMLContentType);
} }
private ControlResponse PostAsync(Stream requestStream, IUpnpService service) private ControlResponse PostAsync(Stream requestStream, IUpnpService service)
@ -188,49 +218,76 @@ namespace MediaBrowser.Api.Dlna
}); });
} }
protected string GetPathValue(int index)
{
var pathInfo = Parse(Request.PathInfo);
var first = pathInfo[0];
// backwards compatibility
if (string.Equals(first, "mediabrowser", StringComparison.OrdinalIgnoreCase) ||
string.Equals(first, "emby", StringComparison.OrdinalIgnoreCase))
{
index++;
}
return pathInfo[index];
}
private List<string> Parse(string pathUri)
{
var actionParts = pathUri.Split(new[] { "://" }, StringSplitOptions.None);
var pathInfo = actionParts[actionParts.Length - 1];
var optionsPos = pathInfo.LastIndexOf('?');
if (optionsPos != -1)
{
pathInfo = pathInfo.Substring(0, optionsPos);
}
var args = pathInfo.Split('/');
return args.Skip(1).ToList();
}
public object Get(GetIcon request) public object Get(GetIcon request)
{ {
using (var response = _dlnaManager.GetIcon(request.Filename)) var contentType = "image/" + Path.GetExtension(request.Filename).TrimStart('.').ToLower();
{
using (var ms = _memoryStreamProvider.CreateNew())
{
response.Stream.CopyTo(ms);
ms.Position = 0; var cacheLength = TimeSpan.FromDays(365);
var bytes = ms.ToArray(); var cacheKey = Request.RawUrl.GetMD5();
return ResultFactory.GetResult(bytes, "image/" + response.Format.ToString().ToLower());
} return _resultFactory.GetStaticResult(Request, cacheKey, null, cacheLength, contentType, () => Task.FromResult<Stream>(_dlnaManager.GetIcon(request.Filename).Stream));
}
} }
public object Subscribe(ProcessContentDirectoryEventRequest request) public object Subscribe(ProcessContentDirectoryEventRequest request)
{ {
return ProcessEventRequest(_contentDirectory); return ProcessEventRequest(ContentDirectory);
} }
public object Subscribe(ProcessConnectionManagerEventRequest request) public object Subscribe(ProcessConnectionManagerEventRequest request)
{ {
return ProcessEventRequest(_connectionManager); return ProcessEventRequest(ConnectionManager);
} }
public object Subscribe(ProcessMediaReceiverRegistrarEventRequest request) public object Subscribe(ProcessMediaReceiverRegistrarEventRequest request)
{ {
return ProcessEventRequest(_mediaReceiverRegistrar); return ProcessEventRequest(MediaReceiverRegistrar);
} }
public object Unsubscribe(ProcessContentDirectoryEventRequest request) public object Unsubscribe(ProcessContentDirectoryEventRequest request)
{ {
return ProcessEventRequest(_contentDirectory); return ProcessEventRequest(ContentDirectory);
} }
public object Unsubscribe(ProcessConnectionManagerEventRequest request) public object Unsubscribe(ProcessConnectionManagerEventRequest request)
{ {
return ProcessEventRequest(_connectionManager); return ProcessEventRequest(ConnectionManager);
} }
public object Unsubscribe(ProcessMediaReceiverRegistrarEventRequest request) public object Unsubscribe(ProcessMediaReceiverRegistrarEventRequest request)
{ {
return ProcessEventRequest(_mediaReceiverRegistrar); return ProcessEventRequest(MediaReceiverRegistrar);
} }
private object ProcessEventRequest(IEventManager eventManager) private object ProcessEventRequest(IEventManager eventManager)
@ -257,7 +314,7 @@ namespace MediaBrowser.Api.Dlna
private object GetSubscriptionResponse(EventSubscriptionResponse response) private object GetSubscriptionResponse(EventSubscriptionResponse response)
{ {
return ResultFactory.GetResult(response.Content, response.ContentType, response.Headers); return _resultFactory.GetResult(Request, response.Content, response.ContentType, response.Headers);
} }
} }
} }

View File

@ -4,7 +4,7 @@ using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Services; using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.Dlna namespace Emby.Dlna.Api
{ {
[Route("/Dlna/ProfileInfos", "GET", Summary = "Gets a list of profiles")] [Route("/Dlna/ProfileInfos", "GET", Summary = "Gets a list of profiles")]
public class GetProfileInfos : IReturn<DeviceProfileInfo[]> public class GetProfileInfos : IReturn<DeviceProfileInfo[]>
@ -41,7 +41,7 @@ namespace MediaBrowser.Api.Dlna
} }
[Authenticated(Roles = "Admin")] [Authenticated(Roles = "Admin")]
public class DlnaService : BaseApiService public class DlnaService : IService
{ {
private readonly IDlnaManager _dlnaManager; private readonly IDlnaManager _dlnaManager;
@ -52,23 +52,17 @@ namespace MediaBrowser.Api.Dlna
public object Get(GetProfileInfos request) public object Get(GetProfileInfos request)
{ {
var result = _dlnaManager.GetProfileInfos().ToArray(); return _dlnaManager.GetProfileInfos().ToArray();
return ToOptimizedResult(result);
} }
public object Get(GetProfile request) public object Get(GetProfile request)
{ {
var result = _dlnaManager.GetProfile(request.Id); return _dlnaManager.GetProfile(request.Id);
return ToOptimizedResult(result);
} }
public object Get(GetDefaultProfile request) public object Get(GetDefaultProfile request)
{ {
var result = _dlnaManager.GetDefaultProfile(); return _dlnaManager.GetDefaultProfile();
return ToOptimizedResult(result);
} }
public void Delete(DeleteProfile request) public void Delete(DeleteProfile request)

View File

@ -1,4 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System;
namespace Emby.Dlna.Common namespace Emby.Dlna.Common
{ {
@ -10,7 +11,7 @@ namespace Emby.Dlna.Common
public bool SendsEvents { get; set; } public bool SendsEvents { get; set; }
public List<string> AllowedValues { get; set; } public string[] AllowedValues { get; set; }
public override string ToString() public override string ToString()
{ {
@ -19,7 +20,7 @@ namespace Emby.Dlna.Common
public StateVariable() public StateVariable()
{ {
AllowedValues = new List<string>(); AllowedValues = Array.Empty<string>();
} }
} }
} }

View File

@ -1,5 +1,5 @@
 
namespace MediaBrowser.Model.Configuration namespace Emby.Dlna.Configuration
{ {
public class DlnaOptions public class DlnaOptions
{ {
@ -17,7 +17,7 @@ namespace MediaBrowser.Model.Configuration
EnableServer = true; EnableServer = true;
BlastAliveMessages = true; BlastAliveMessages = true;
ClientDiscoveryIntervalSeconds = 60; ClientDiscoveryIntervalSeconds = 60;
BlastAliveMessageIntervalSeconds = 30; BlastAliveMessageIntervalSeconds = 1800;
} }
} }
} }

View File

@ -1,5 +1,5 @@
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.Configuration; using Emby.Dlna.Configuration;
using System.Collections.Generic; using System.Collections.Generic;
namespace Emby.Dlna namespace Emby.Dlna
@ -16,7 +16,7 @@ namespace Emby.Dlna
{ {
public IEnumerable<ConfigurationStore> GetConfigurations() public IEnumerable<ConfigurationStore> GetConfigurations()
{ {
return new List<ConfigurationStore> return new ConfigurationStore[]
{ {
new ConfigurationStore new ConfigurationStore
{ {

View File

@ -42,7 +42,7 @@ namespace Emby.Dlna.ConnectionManager
DataType = "string", DataType = "string",
SendsEvents = false, SendsEvents = false,
AllowedValues = new List<string> AllowedValues = new string[]
{ {
"OK", "OK",
"ContentFormatMismatch", "ContentFormatMismatch",
@ -65,7 +65,7 @@ namespace Emby.Dlna.ConnectionManager
DataType = "string", DataType = "string",
SendsEvents = false, SendsEvents = false,
AllowedValues = new List<string> AllowedValues = new string[]
{ {
"Output", "Output",
"Input" "Input"

View File

@ -1,5 +1,4 @@
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Drawing;
@ -26,10 +25,9 @@ namespace Emby.Dlna.ContentDirectory
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
private readonly IUserManager _userManager; private readonly IUserManager _userManager;
private readonly ILocalizationManager _localization; private readonly ILocalizationManager _localization;
private readonly IChannelManager _channelManager;
private readonly IMediaSourceManager _mediaSourceManager; private readonly IMediaSourceManager _mediaSourceManager;
private readonly IUserViewManager _userViewManager; private readonly IUserViewManager _userViewManager;
private readonly Func<IMediaEncoder> _mediaEncoder; private readonly IMediaEncoder _mediaEncoder;
protected readonly IXmlReaderSettingsFactory XmlReaderSettingsFactory; protected readonly IXmlReaderSettingsFactory XmlReaderSettingsFactory;
private readonly ITVSeriesManager _tvSeriesManager; private readonly ITVSeriesManager _tvSeriesManager;
@ -40,7 +38,7 @@ namespace Emby.Dlna.ContentDirectory
IServerConfigurationManager config, IServerConfigurationManager config,
IUserManager userManager, IUserManager userManager,
ILogger logger, ILogger logger,
IHttpClient httpClient, ILocalizationManager localization, IChannelManager channelManager, IMediaSourceManager mediaSourceManager, IUserViewManager userViewManager, Func<IMediaEncoder> mediaEncoder, IXmlReaderSettingsFactory xmlReaderSettingsFactory, ITVSeriesManager tvSeriesManager) IHttpClient httpClient, ILocalizationManager localization, IMediaSourceManager mediaSourceManager, IUserViewManager userViewManager, IMediaEncoder mediaEncoder, IXmlReaderSettingsFactory xmlReaderSettingsFactory, ITVSeriesManager tvSeriesManager)
: base(logger, httpClient) : base(logger, httpClient)
{ {
_dlna = dlna; _dlna = dlna;
@ -50,7 +48,6 @@ namespace Emby.Dlna.ContentDirectory
_config = config; _config = config;
_userManager = userManager; _userManager = userManager;
_localization = localization; _localization = localization;
_channelManager = channelManager;
_mediaSourceManager = mediaSourceManager; _mediaSourceManager = mediaSourceManager;
_userViewManager = userViewManager; _userViewManager = userViewManager;
_mediaEncoder = mediaEncoder; _mediaEncoder = mediaEncoder;
@ -95,10 +92,9 @@ namespace Emby.Dlna.ContentDirectory
SystemUpdateId, SystemUpdateId,
_config, _config,
_localization, _localization,
_channelManager,
_mediaSourceManager, _mediaSourceManager,
_userViewManager, _userViewManager,
_mediaEncoder(), _mediaEncoder,
XmlReaderSettingsFactory, XmlReaderSettingsFactory,
_tvSeriesManager) _tvSeriesManager)
.ProcessControlRequest(request); .ProcessControlRequest(request);

View File

@ -1,127 +0,0 @@
using System.Linq;
using System.Xml.Linq;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Querying;
using System.Globalization;
using System.IO;
using System.Security;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Emby.Dlna.Server;
namespace Emby.Dlna.ContentDirectory
{
public class ContentDirectoryBrowser
{
private readonly IHttpClient _httpClient;
private readonly ILogger _logger;
public ContentDirectoryBrowser(IHttpClient httpClient, ILogger logger)
{
_httpClient = httpClient;
_logger = logger;
}
private static XNamespace UNamespace = "u";
public async Task<QueryResult<ChannelItemInfo>> Browse(ContentDirectoryBrowseRequest request, CancellationToken cancellationToken)
{
var options = new HttpRequestOptions
{
CancellationToken = cancellationToken,
UserAgent = "Emby",
RequestContentType = "text/xml",
LogErrorResponseBody = true,
Url = request.ContentDirectoryUrl,
BufferContent = false
};
options.RequestHeaders["SOAPACTION"] = "urn:schemas-upnp-org:service:ContentDirectory:1#Browse";
options.RequestContent = GetRequestBody(request);
using (var response = await _httpClient.SendAsync(options, "POST"))
{
using (var reader = new StreamReader(response.Content))
{
var doc = XDocument.Parse(reader.ReadToEnd(), LoadOptions.PreserveWhitespace);
var queryResult = new QueryResult<ChannelItemInfo>();
if (doc.Document == null)
return queryResult;
var responseElement = doc.Document.Descendants(UNamespace + "BrowseResponse").ToList();
var countElement = responseElement.Select(i => i.Element("TotalMatches")).FirstOrDefault(i => i != null);
var countValue = countElement == null ? null : countElement.Value;
int count;
if (!string.IsNullOrWhiteSpace(countValue) && int.TryParse(countValue, NumberStyles.Integer, CultureInfo.InvariantCulture, out count))
{
queryResult.TotalRecordCount = count;
var resultElement = responseElement.Select(i => i.Element("Result")).FirstOrDefault(i => i != null);
var resultString = (string)resultElement;
if (resultElement != null)
{
var xElement = XElement.Parse(resultString);
}
}
return queryResult;
}
}
}
private string GetRequestBody(ContentDirectoryBrowseRequest request)
{
var builder = new StringBuilder();
builder.Append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
builder.Append("<s:Envelope s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\" xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"><s:Body>");
builder.Append("<u:Browse xmlns:u=\"urn:schemas-upnp-org:service:ContentDirectory:1\">");
if (string.IsNullOrWhiteSpace(request.ParentId))
{
request.ParentId = "1";
}
builder.AppendFormat("<ObjectID>{0}</ObjectID>", DescriptionXmlBuilder.Escape(request.ParentId));
builder.Append("<BrowseFlag>BrowseDirectChildren</BrowseFlag>");
//builder.Append("<BrowseFlag>BrowseMetadata</BrowseFlag>");
builder.Append("<Filter>*</Filter>");
request.StartIndex = request.StartIndex ?? 0;
builder.AppendFormat("<StartingIndex>{0}</StartingIndex>", DescriptionXmlBuilder.Escape(request.StartIndex.Value.ToString(CultureInfo.InvariantCulture)));
request.Limit = request.Limit ?? 20;
if (request.Limit.HasValue)
{
builder.AppendFormat("<RequestedCount>{0}</RequestedCount>", DescriptionXmlBuilder.Escape(request.Limit.Value.ToString(CultureInfo.InvariantCulture)));
}
builder.Append("<SortCriteria></SortCriteria>");
builder.Append("</u:Browse>");
builder.Append("</s:Body></s:Envelope>");
return builder.ToString();
}
}
public class ContentDirectoryBrowseRequest
{
public int? StartIndex { get; set; }
public int? Limit { get; set; }
public string ParentId { get; set; }
public string ContentDirectoryUrl { get; set; }
}
}

View File

@ -99,7 +99,7 @@ namespace Emby.Dlna.ContentDirectory
DataType = "string", DataType = "string",
SendsEvents = false, SendsEvents = false,
AllowedValues = new List<string> AllowedValues = new string[]
{ {
"BrowseMetadata", "BrowseMetadata",
"BrowseDirectChildren" "BrowseDirectChildren"

View File

@ -31,13 +31,13 @@ using MediaBrowser.Controller.TV;
using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Xml; using MediaBrowser.Model.Xml;
using MediaBrowser.Model.Extensions; using MediaBrowser.Model.Extensions;
using MediaBrowser.Controller.LiveTv;
namespace Emby.Dlna.ContentDirectory namespace Emby.Dlna.ContentDirectory
{ {
public class ControlHandler : BaseControlHandler public class ControlHandler : BaseControlHandler
{ {
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly IChannelManager _channelManager;
private readonly IUserDataManager _userDataManager; private readonly IUserDataManager _userDataManager;
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
private readonly User _user; private readonly User _user;
@ -56,14 +56,13 @@ namespace Emby.Dlna.ContentDirectory
private readonly DeviceProfile _profile; private readonly DeviceProfile _profile;
public ControlHandler(ILogger logger, ILibraryManager libraryManager, DeviceProfile profile, string serverAddress, string accessToken, IImageProcessor imageProcessor, IUserDataManager userDataManager, User user, int systemUpdateId, IServerConfigurationManager config, ILocalizationManager localization, IChannelManager channelManager, IMediaSourceManager mediaSourceManager, IUserViewManager userViewManager, IMediaEncoder mediaEncoder, IXmlReaderSettingsFactory xmlReaderSettingsFactory, ITVSeriesManager tvSeriesManager) public ControlHandler(ILogger logger, ILibraryManager libraryManager, DeviceProfile profile, string serverAddress, string accessToken, IImageProcessor imageProcessor, IUserDataManager userDataManager, User user, int systemUpdateId, IServerConfigurationManager config, ILocalizationManager localization, IMediaSourceManager mediaSourceManager, IUserViewManager userViewManager, IMediaEncoder mediaEncoder, IXmlReaderSettingsFactory xmlReaderSettingsFactory, ITVSeriesManager tvSeriesManager)
: base(config, logger, xmlReaderSettingsFactory) : base(config, logger, xmlReaderSettingsFactory)
{ {
_libraryManager = libraryManager; _libraryManager = libraryManager;
_userDataManager = userDataManager; _userDataManager = userDataManager;
_user = user; _user = user;
_systemUpdateId = systemUpdateId; _systemUpdateId = systemUpdateId;
_channelManager = channelManager;
_userViewManager = userViewManager; _userViewManager = userViewManager;
_tvSeriesManager = tvSeriesManager; _tvSeriesManager = tvSeriesManager;
_profile = profile; _profile = profile;
@ -125,7 +124,7 @@ namespace Emby.Dlna.ContentDirectory
userdata.PlaybackPositionTicks = TimeSpan.FromSeconds(newbookmark).Ticks; userdata.PlaybackPositionTicks = TimeSpan.FromSeconds(newbookmark).Ticks;
_userDataManager.SaveUserData(user.Id, item, userdata, UserDataSaveReason.TogglePlayed, _userDataManager.SaveUserData(user, item, userdata, UserDataSaveReason.TogglePlayed,
CancellationToken.None); CancellationToken.None);
return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
@ -246,6 +245,8 @@ namespace Emby.Dlna.ContentDirectory
int totalCount; int totalCount;
var dlnaOptions = _config.GetDlnaConfiguration();
using (XmlWriter writer = XmlWriter.Create(builder, settings)) using (XmlWriter writer = XmlWriter.Create(builder, settings))
{ {
//writer.WriteStartDocument(); //writer.WriteStartDocument();
@ -274,7 +275,7 @@ namespace Emby.Dlna.ContentDirectory
} }
else else
{ {
_didlBuilder.WriteItemElement(_config.GetDlnaConfiguration(), writer, item, user, null, null, deviceId, filter); _didlBuilder.WriteItemElement(dlnaOptions, writer, item, user, null, null, deviceId, filter);
} }
provided++; provided++;
@ -300,7 +301,7 @@ namespace Emby.Dlna.ContentDirectory
} }
else else
{ {
_didlBuilder.WriteItemElement(_config.GetDlnaConfiguration(), writer, childItem, user, item, serverItem.StubType, deviceId, filter); _didlBuilder.WriteItemElement(dlnaOptions, writer, childItem, user, item, serverItem.StubType, deviceId, filter);
} }
} }
} }
@ -310,7 +311,7 @@ namespace Emby.Dlna.ContentDirectory
var resXML = builder.ToString(); var resXML = builder.ToString();
return new List<KeyValuePair<string, string>> return new []
{ {
new KeyValuePair<string,string>("Result", resXML), new KeyValuePair<string,string>("Result", resXML),
new KeyValuePair<string,string>("NumberReturned", provided.ToString(_usCulture)), new KeyValuePair<string,string>("NumberReturned", provided.ToString(_usCulture)),
@ -385,6 +386,8 @@ namespace Emby.Dlna.ContentDirectory
provided = childrenResult.Items.Length; provided = childrenResult.Items.Length;
var dlnaOptions = _config.GetDlnaConfiguration();
foreach (var i in childrenResult.Items) foreach (var i in childrenResult.Items)
{ {
if (i.IsDisplayedAsFolder) if (i.IsDisplayedAsFolder)
@ -396,7 +399,7 @@ namespace Emby.Dlna.ContentDirectory
} }
else else
{ {
_didlBuilder.WriteItemElement(_config.GetDlnaConfiguration(), writer, i, user, item, serverItem.StubType, deviceId, filter); _didlBuilder.WriteItemElement(dlnaOptions, writer, i, user, item, serverItem.StubType, deviceId, filter);
} }
} }
@ -458,7 +461,7 @@ namespace Emby.Dlna.ContentDirectory
{ {
Limit = limit, Limit = limit,
StartIndex = startIndex, StartIndex = startIndex,
OrderBy = sortOrders.Select(i => new Tuple<string, SortOrder>(i, sort.SortOrder)).ToArray(), OrderBy = sortOrders.Select(i => new ValueTuple<string, SortOrder>(i, sort.SortOrder)).ToArray(),
User = user, User = user,
Recursive = true, Recursive = true,
IsMissing = false, IsMissing = false,
@ -478,22 +481,22 @@ namespace Emby.Dlna.ContentDirectory
{ {
if (item is MusicGenre) if (item is MusicGenre)
{ {
return GetMusicGenreItems(item, null, user, sort, startIndex, limit); return GetMusicGenreItems(item, Guid.Empty, user, sort, startIndex, limit);
} }
if (item is MusicArtist) if (item is MusicArtist)
{ {
return GetMusicArtistItems(item, null, user, sort, startIndex, limit); return GetMusicArtistItems(item, Guid.Empty, user, sort, startIndex, limit);
} }
if (item is Genre) if (item is Genre)
{ {
return GetGenreItems(item, null, user, sort, startIndex, limit); return GetGenreItems(item, Guid.Empty, user, sort, startIndex, limit);
} }
if (!stubType.HasValue || stubType.Value != StubType.Folder) if (!stubType.HasValue || stubType.Value != StubType.Folder)
{ {
var collectionFolder = item as ICollectionFolder; var collectionFolder = item as IHasCollectionType;
if (collectionFolder != null && string.Equals(CollectionType.Music, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase)) if (collectionFolder != null && string.Equals(CollectionType.Music, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase))
{ {
return GetMusicFolders(item, user, stubType, sort, startIndex, limit); return GetMusicFolders(item, user, stubType, sort, startIndex, limit);
@ -507,21 +510,18 @@ namespace Emby.Dlna.ContentDirectory
return GetTvFolders(item, user, stubType, sort, startIndex, limit); return GetTvFolders(item, user, stubType, sort, startIndex, limit);
} }
var userView = item as UserView; if (collectionFolder != null && string.Equals(CollectionType.Folders, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase))
if (userView != null && string.Equals(CollectionType.Folders, userView.ViewType, StringComparison.OrdinalIgnoreCase))
{ {
return GetFolders(item, user, stubType, sort, startIndex, limit); return GetFolders(item, user, stubType, sort, startIndex, limit);
} }
if (collectionFolder != null && string.Equals(CollectionType.LiveTv, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase))
{
return GetLiveTvChannels(item, user, stubType, sort, startIndex, limit);
}
} }
if (stubType.HasValue) if (stubType.HasValue)
{ {
var person = item as Person;
if (person != null)
{
return GetItemsFromPerson(person, user, startIndex, limit);
}
if (stubType.Value != StubType.Folder) if (stubType.Value != StubType.Folder)
{ {
return ApplyPaging(new QueryResult<ServerItem>(), startIndex, limit); return ApplyPaging(new QueryResult<ServerItem>(), startIndex, limit);
@ -530,13 +530,11 @@ namespace Emby.Dlna.ContentDirectory
var folder = (Folder)item; var folder = (Folder)item;
var query = new InternalItemsQuery var query = new InternalItemsQuery(user)
{ {
Limit = limit, Limit = limit,
StartIndex = startIndex, StartIndex = startIndex,
User = user,
IsVirtualItem = false, IsVirtualItem = false,
PresetViews = new string[] { },
ExcludeItemTypes = new[] { typeof(Game).Name, typeof(Book).Name }, ExcludeItemTypes = new[] { typeof(Game).Name, typeof(Book).Name },
IsPlaceHolder = false, IsPlaceHolder = false,
DtoOptions = GetDtoOptions() DtoOptions = GetDtoOptions()
@ -549,6 +547,22 @@ namespace Emby.Dlna.ContentDirectory
return ToResult(queryResult); return ToResult(queryResult);
} }
private QueryResult<ServerItem> GetLiveTvChannels(BaseItem item, User user, StubType? stubType, SortCriteria sort, int? startIndex, int? limit)
{
var query = new InternalItemsQuery(user)
{
StartIndex = startIndex,
Limit = limit,
};
query.IncludeItemTypes = new[] { typeof(LiveTvChannel).Name };
SetSorting(query, sort, false);
var result = _libraryManager.GetItemsResult(query);
return ToResult(result);
}
private QueryResult<ServerItem> GetMusicFolders(BaseItem item, User user, StubType? stubType, SortCriteria sort, int? startIndex, int? limit) private QueryResult<ServerItem> GetMusicFolders(BaseItem item, User user, StubType? stubType, SortCriteria sort, int? startIndex, int? limit)
{ {
var query = new InternalItemsQuery(user) var query = new InternalItemsQuery(user)
@ -747,7 +761,7 @@ namespace Emby.Dlna.ContentDirectory
private QueryResult<ServerItem> GetFolders(BaseItem item, User user, StubType? stubType, SortCriteria sort, int? startIndex, int? limit) private QueryResult<ServerItem> GetFolders(BaseItem item, User user, StubType? stubType, SortCriteria sort, int? startIndex, int? limit)
{ {
var folders = user.RootFolder.GetChildren(user, true) var folders = _libraryManager.GetUserRootFolder().GetChildren(user, true)
.OrderBy(i => i.SortName) .OrderBy(i => i.SortName)
.Select(i => new ServerItem(i) .Select(i => new ServerItem(i)
{ {
@ -856,10 +870,10 @@ namespace Emby.Dlna.ContentDirectory
query.Parent = parent; query.Parent = parent;
query.SetUser(user); query.SetUser(user);
query.OrderBy = new Tuple<string, SortOrder>[] query.OrderBy = new ValueTuple<string, SortOrder>[]
{ {
new Tuple<string, SortOrder> (ItemSortBy.DatePlayed, SortOrder.Descending), new ValueTuple<string, SortOrder> (ItemSortBy.DatePlayed, SortOrder.Descending),
new Tuple<string, SortOrder> (ItemSortBy.SortName, SortOrder.Ascending) new ValueTuple<string, SortOrder> (ItemSortBy.SortName, SortOrder.Ascending)
}; };
query.IsResumable = true; query.IsResumable = true;
@ -1004,7 +1018,7 @@ namespace Emby.Dlna.ContentDirectory
{ {
var genresResult = _libraryManager.GetGenres(new InternalItemsQuery(user) var genresResult = _libraryManager.GetGenres(new InternalItemsQuery(user)
{ {
AncestorIds = new[] { parent.Id.ToString("N") }, AncestorIds = new[] { parent.Id },
StartIndex = query.StartIndex, StartIndex = query.StartIndex,
Limit = query.Limit Limit = query.Limit
}); });
@ -1022,7 +1036,7 @@ namespace Emby.Dlna.ContentDirectory
{ {
var genresResult = _libraryManager.GetMusicGenres(new InternalItemsQuery(user) var genresResult = _libraryManager.GetMusicGenres(new InternalItemsQuery(user)
{ {
AncestorIds = new[] { parent.Id.ToString("N") }, AncestorIds = new[] { parent.Id },
StartIndex = query.StartIndex, StartIndex = query.StartIndex,
Limit = query.Limit Limit = query.Limit
}); });
@ -1040,7 +1054,7 @@ namespace Emby.Dlna.ContentDirectory
{ {
var artists = _libraryManager.GetAlbumArtists(new InternalItemsQuery(user) var artists = _libraryManager.GetAlbumArtists(new InternalItemsQuery(user)
{ {
AncestorIds = new[] { parent.Id.ToString("N") }, AncestorIds = new[] { parent.Id },
StartIndex = query.StartIndex, StartIndex = query.StartIndex,
Limit = query.Limit Limit = query.Limit
}); });
@ -1058,7 +1072,7 @@ namespace Emby.Dlna.ContentDirectory
{ {
var artists = _libraryManager.GetArtists(new InternalItemsQuery(user) var artists = _libraryManager.GetArtists(new InternalItemsQuery(user)
{ {
AncestorIds = new[] { parent.Id.ToString("N") }, AncestorIds = new[] { parent.Id },
StartIndex = query.StartIndex, StartIndex = query.StartIndex,
Limit = query.Limit Limit = query.Limit
}); });
@ -1076,7 +1090,7 @@ namespace Emby.Dlna.ContentDirectory
{ {
var artists = _libraryManager.GetArtists(new InternalItemsQuery(user) var artists = _libraryManager.GetArtists(new InternalItemsQuery(user)
{ {
AncestorIds = new[] { parent.Id.ToString("N") }, AncestorIds = new[] { parent.Id },
StartIndex = query.StartIndex, StartIndex = query.StartIndex,
Limit = query.Limit, Limit = query.Limit,
IsFavorite = true IsFavorite = true
@ -1105,77 +1119,77 @@ namespace Emby.Dlna.ContentDirectory
private QueryResult<ServerItem> GetMusicLatest(BaseItem parent, User user, InternalItemsQuery query) private QueryResult<ServerItem> GetMusicLatest(BaseItem parent, User user, InternalItemsQuery query)
{ {
query.OrderBy = new Tuple<string, SortOrder>[] { }; query.OrderBy = new ValueTuple<string, SortOrder>[] { };
var items = _userViewManager.GetLatestItems(new LatestItemsQuery var items = _userViewManager.GetLatestItems(new LatestItemsQuery
{ {
UserId = user.Id.ToString("N"), UserId = user.Id,
Limit = 50, Limit = 50,
IncludeItemTypes = new[] { typeof(Audio).Name }, IncludeItemTypes = new[] { typeof(Audio).Name },
ParentId = parent == null ? null : parent.Id.ToString("N"), ParentId = parent == null ? Guid.Empty : parent.Id,
GroupItems = true GroupItems = true
}, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToList(); }, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToArray();
return ToResult(items); return ToResult(items);
} }
private QueryResult<ServerItem> GetNextUp(BaseItem parent, User user, InternalItemsQuery query) private QueryResult<ServerItem> GetNextUp(BaseItem parent, User user, InternalItemsQuery query)
{ {
query.OrderBy = new Tuple<string, SortOrder>[] { }; query.OrderBy = new ValueTuple<string, SortOrder>[] { };
var result = _tvSeriesManager.GetNextUp(new NextUpQuery var result = _tvSeriesManager.GetNextUp(new NextUpQuery
{ {
Limit = query.Limit, Limit = query.Limit,
StartIndex = query.StartIndex, StartIndex = query.StartIndex,
UserId = query.User.Id.ToString("N") UserId = query.User.Id
}, new List<BaseItem> { parent }, query.DtoOptions); }, new [] { parent }, query.DtoOptions);
return ToResult(result); return ToResult(result);
} }
private QueryResult<ServerItem> GetTvLatest(BaseItem parent, User user, InternalItemsQuery query) private QueryResult<ServerItem> GetTvLatest(BaseItem parent, User user, InternalItemsQuery query)
{ {
query.OrderBy = new Tuple<string, SortOrder>[] { }; query.OrderBy = new ValueTuple<string, SortOrder>[] { };
var items = _userViewManager.GetLatestItems(new LatestItemsQuery var items = _userViewManager.GetLatestItems(new LatestItemsQuery
{ {
UserId = user.Id.ToString("N"), UserId = user.Id,
Limit = 50, Limit = 50,
IncludeItemTypes = new[] { typeof(Episode).Name }, IncludeItemTypes = new[] { typeof(Episode).Name },
ParentId = parent == null ? null : parent.Id.ToString("N"), ParentId = parent == null ? Guid.Empty : parent.Id,
GroupItems = false GroupItems = false
}, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToList(); }, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToArray();
return ToResult(items); return ToResult(items);
} }
private QueryResult<ServerItem> GetMovieLatest(BaseItem parent, User user, InternalItemsQuery query) private QueryResult<ServerItem> GetMovieLatest(BaseItem parent, User user, InternalItemsQuery query)
{ {
query.OrderBy = new Tuple<string, SortOrder>[] { }; query.OrderBy = new ValueTuple<string, SortOrder>[] { };
var items = _userViewManager.GetLatestItems(new LatestItemsQuery var items = _userViewManager.GetLatestItems(new LatestItemsQuery
{ {
UserId = user.Id.ToString("N"), UserId = user.Id,
Limit = 50, Limit = 50,
IncludeItemTypes = new[] { typeof(Movie).Name }, IncludeItemTypes = new[] { typeof(Movie).Name },
ParentId = parent == null ? null : parent.Id.ToString("N"), ParentId = parent == null ? Guid.Empty : parent.Id,
GroupItems = true GroupItems = true
}, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToList(); }, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToArray();
return ToResult(items); return ToResult(items);
} }
private QueryResult<ServerItem> GetMusicArtistItems(BaseItem item, Guid? parentId, User user, SortCriteria sort, int? startIndex, int? limit) private QueryResult<ServerItem> GetMusicArtistItems(BaseItem item, Guid parentId, User user, SortCriteria sort, int? startIndex, int? limit)
{ {
var query = new InternalItemsQuery(user) var query = new InternalItemsQuery(user)
{ {
Recursive = true, Recursive = true,
ParentId = parentId, ParentId = parentId,
ArtistIds = new[] { item.Id.ToString("N") }, ArtistIds = new[] { item.Id },
IncludeItemTypes = new[] { typeof(MusicAlbum).Name }, IncludeItemTypes = new[] { typeof(MusicAlbum).Name },
Limit = limit, Limit = limit,
StartIndex = startIndex, StartIndex = startIndex,
@ -1189,13 +1203,13 @@ namespace Emby.Dlna.ContentDirectory
return ToResult(result); return ToResult(result);
} }
private QueryResult<ServerItem> GetGenreItems(BaseItem item, Guid? parentId, User user, SortCriteria sort, int? startIndex, int? limit) private QueryResult<ServerItem> GetGenreItems(BaseItem item, Guid parentId, User user, SortCriteria sort, int? startIndex, int? limit)
{ {
var query = new InternalItemsQuery(user) var query = new InternalItemsQuery(user)
{ {
Recursive = true, Recursive = true,
ParentId = parentId, ParentId = parentId,
GenreIds = new[] { item.Id.ToString("N") }, GenreIds = new[] { item.Id },
IncludeItemTypes = new[] { typeof(Movie).Name, typeof(Series).Name }, IncludeItemTypes = new[] { typeof(Movie).Name, typeof(Series).Name },
Limit = limit, Limit = limit,
StartIndex = startIndex, StartIndex = startIndex,
@ -1209,13 +1223,13 @@ namespace Emby.Dlna.ContentDirectory
return ToResult(result); return ToResult(result);
} }
private QueryResult<ServerItem> GetMusicGenreItems(BaseItem item, Guid? parentId, User user, SortCriteria sort, int? startIndex, int? limit) private QueryResult<ServerItem> GetMusicGenreItems(BaseItem item, Guid parentId, User user, SortCriteria sort, int? startIndex, int? limit)
{ {
var query = new InternalItemsQuery(user) var query = new InternalItemsQuery(user)
{ {
Recursive = true, Recursive = true,
ParentId = parentId, ParentId = parentId,
GenreIds = new[] { item.Id.ToString("N") }, GenreIds = new[] { item.Id },
IncludeItemTypes = new[] { typeof(MusicAlbum).Name }, IncludeItemTypes = new[] { typeof(MusicAlbum).Name },
Limit = limit, Limit = limit,
StartIndex = startIndex, StartIndex = startIndex,
@ -1229,15 +1243,15 @@ namespace Emby.Dlna.ContentDirectory
return ToResult(result); return ToResult(result);
} }
private QueryResult<ServerItem> ToResult(List<BaseItem> result) private QueryResult<ServerItem> ToResult(BaseItem[] result)
{ {
var serverItems = result var serverItems = result
.Select(i => new ServerItem(i)) .Select(i => new ServerItem(i))
.ToArray(result.Count); .ToArray(result.Length);
return new QueryResult<ServerItem> return new QueryResult<ServerItem>
{ {
TotalRecordCount = result.Count, TotalRecordCount = result.Length,
Items = serverItems Items = serverItems
}; };
} }
@ -1247,7 +1261,7 @@ namespace Emby.Dlna.ContentDirectory
var serverItems = result var serverItems = result
.Items .Items
.Select(i => new ServerItem(i)) .Select(i => new ServerItem(i))
.ToArray(result.Items.Length); .ToArray();
return new QueryResult<ServerItem> return new QueryResult<ServerItem>
{ {
@ -1264,28 +1278,7 @@ namespace Emby.Dlna.ContentDirectory
sortOrders.Add(ItemSortBy.SortName); sortOrders.Add(ItemSortBy.SortName);
} }
query.OrderBy = sortOrders.Select(i => new Tuple<string, SortOrder>(i, sort.SortOrder)).ToArray(); query.OrderBy = sortOrders.Select(i => new ValueTuple<string, SortOrder>(i, sort.SortOrder)).ToArray();
}
private QueryResult<ServerItem> GetItemsFromPerson(Person person, User user, int? startIndex, int? limit)
{
var itemsResult = _libraryManager.GetItemsResult(new InternalItemsQuery(user)
{
PersonIds = new[] { person.Id.ToString("N") },
IncludeItemTypes = new[] { typeof(Movie).Name, typeof(Series).Name, typeof(Trailer).Name },
OrderBy = new[] { ItemSortBy.SortName }.Select(i => new Tuple<string, SortOrder>(i, SortOrder.Ascending)).ToArray(),
Limit = limit,
StartIndex = startIndex,
DtoOptions = GetDtoOptions()
});
var serverItems = itemsResult.Items.Select(i => new ServerItem(i)).ToArray(itemsResult.Items.Length);
return new QueryResult<ServerItem>
{
TotalRecordCount = itemsResult.TotalRecordCount,
Items = serverItems
};
} }
private QueryResult<ServerItem> ApplyPaging(QueryResult<ServerItem> result, int? startIndex, int? limit) private QueryResult<ServerItem> ApplyPaging(QueryResult<ServerItem> result, int? startIndex, int? limit)
@ -1299,7 +1292,7 @@ namespace Emby.Dlna.ContentDirectory
{ {
return DidlBuilder.IsIdRoot(id) return DidlBuilder.IsIdRoot(id)
? new ServerItem(user.RootFolder) ? new ServerItem(_libraryManager.GetUserRootFolder())
: ParseItemId(id, user); : ParseItemId(id, user);
} }
@ -1343,7 +1336,7 @@ namespace Emby.Dlna.ContentDirectory
Logger.Error("Error parsing item Id: {0}. Returning user root folder.", id); Logger.Error("Error parsing item Id: {0}. Returning user root folder.", id);
return new ServerItem(user.RootFolder); return new ServerItem(_libraryManager.GetUserRootFolder());
} }
} }

View File

@ -7,7 +7,7 @@ namespace Emby.Dlna.ContentDirectory
{ {
public IEnumerable<ServiceAction> GetActions() public IEnumerable<ServiceAction> GetActions()
{ {
var list = new List<ServiceAction> return new []
{ {
GetSearchCapabilitiesAction(), GetSearchCapabilitiesAction(),
GetSortCapabilitiesAction(), GetSortCapabilitiesAction(),
@ -18,8 +18,6 @@ namespace Emby.Dlna.ContentDirectory
GetXSetBookmarkAction(), GetXSetBookmarkAction(),
GetBrowseByLetterAction() GetBrowseByLetterAction()
}; };
return list;
} }
private ServiceAction GetGetSystemUpdateIDAction() private ServiceAction GetGetSystemUpdateIDAction()

View File

@ -1,7 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
namespace MediaBrowser.Controller.Dlna namespace Emby.Dlna
{ {
public class ControlRequest public class ControlRequest
{ {

View File

@ -1,6 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace MediaBrowser.Controller.Dlna namespace Emby.Dlna
{ {
public class ControlResponse public class ControlResponse
{ {

View File

@ -21,7 +21,7 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Xml; using System.Xml;
using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Configuration; using Emby.Dlna.Configuration;
using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Globalization;
namespace Emby.Dlna.Didl namespace Emby.Dlna.Didl
@ -62,6 +62,11 @@ namespace Emby.Dlna.Didl
_user = user; _user = user;
} }
public static string NormalizeDlnaMediaUrl(string url)
{
return url + "&dlnaheaders=true";
}
public string GetItemDidl(DlnaOptions options, BaseItem item, User user, BaseItem context, string deviceId, Filter filter, StreamInfo streamInfo) public string GetItemDidl(DlnaOptions options, BaseItem item, User user, BaseItem context, string deviceId, Filter filter, StreamInfo streamInfo)
{ {
var settings = new XmlWriterSettings var settings = new XmlWriterSettings
@ -72,28 +77,29 @@ namespace Emby.Dlna.Didl
ConformanceLevel = ConformanceLevel.Fragment ConformanceLevel = ConformanceLevel.Fragment
}; };
StringWriter builder = new StringWriterWithEncoding(Encoding.UTF8); using (StringWriter builder = new StringWriterWithEncoding(Encoding.UTF8))
using (XmlWriter writer = XmlWriter.Create(builder, settings))
{ {
//writer.WriteStartDocument(); using (XmlWriter writer = XmlWriter.Create(builder, settings))
{
//writer.WriteStartDocument();
writer.WriteStartElement(string.Empty, "DIDL-Lite", NS_DIDL); writer.WriteStartElement(string.Empty, "DIDL-Lite", NS_DIDL);
writer.WriteAttributeString("xmlns", "dc", null, NS_DC); writer.WriteAttributeString("xmlns", "dc", null, NS_DC);
writer.WriteAttributeString("xmlns", "dlna", null, NS_DLNA); writer.WriteAttributeString("xmlns", "dlna", null, NS_DLNA);
writer.WriteAttributeString("xmlns", "upnp", null, NS_UPNP); writer.WriteAttributeString("xmlns", "upnp", null, NS_UPNP);
//didl.SetAttribute("xmlns:sec", NS_SEC); //didl.SetAttribute("xmlns:sec", NS_SEC);
WriteXmlRootAttributes(_profile, writer); WriteXmlRootAttributes(_profile, writer);
WriteItemElement(options, writer, item, user, context, null, deviceId, filter, streamInfo); WriteItemElement(options, writer, item, user, context, null, deviceId, filter, streamInfo);
writer.WriteFullEndElement(); writer.WriteFullEndElement();
//writer.WriteEndDocument(); //writer.WriteEndDocument();
}
return builder.ToString();
} }
return builder.ToString();
} }
public static void WriteXmlRootAttributes(DeviceProfile profile, XmlWriter writer) public static void WriteXmlRootAttributes(DeviceProfile profile, XmlWriter writer)
@ -136,9 +142,9 @@ namespace Emby.Dlna.Didl
else else
{ {
var parent = item.DisplayParentId; var parent = item.DisplayParentId;
if (parent.HasValue) if (!parent.Equals(Guid.Empty))
{ {
writer.WriteAttributeString("parentID", GetClientId(parent.Value, null)); writer.WriteAttributeString("parentID", GetClientId(parent, null));
} }
} }
@ -155,11 +161,11 @@ namespace Emby.Dlna.Didl
{ {
if (string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase)) if (string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase))
{ {
AddAudioResource(options, writer, hasMediaSources, deviceId, filter, streamInfo); AddAudioResource(options, writer, item, deviceId, filter, streamInfo);
} }
else if (string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase)) else if (string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
{ {
AddVideoResource(options, writer, hasMediaSources, deviceId, filter, streamInfo); AddVideoResource(options, writer, item, deviceId, filter, streamInfo);
} }
} }
@ -181,6 +187,7 @@ namespace Emby.Dlna.Didl
{ {
var mime = MimeTypes.GetMimeType(input); var mime = MimeTypes.GetMimeType(input);
// TODO: Instead of being hard-coded here, this should probably be moved into all of the existing profiles
if (string.Equals(mime, "video/mp2t", StringComparison.OrdinalIgnoreCase)) if (string.Equals(mime, "video/mp2t", StringComparison.OrdinalIgnoreCase))
{ {
mime = "video/mpeg"; mime = "video/mpeg";
@ -189,7 +196,7 @@ namespace Emby.Dlna.Didl
return mime; return mime;
} }
private void AddVideoResource(DlnaOptions options, XmlWriter writer, IHasMediaSources video, string deviceId, Filter filter, StreamInfo streamInfo = null) private void AddVideoResource(DlnaOptions options, XmlWriter writer, BaseItem video, string deviceId, Filter filter, StreamInfo streamInfo = null)
{ {
if (streamInfo == null) if (streamInfo == null)
{ {
@ -197,8 +204,8 @@ namespace Emby.Dlna.Didl
streamInfo = new StreamBuilder(_mediaEncoder, GetStreamBuilderLogger(options)).BuildVideoItem(new VideoOptions streamInfo = new StreamBuilder(_mediaEncoder, GetStreamBuilderLogger(options)).BuildVideoItem(new VideoOptions
{ {
ItemId = GetClientId(video), ItemId = video.Id,
MediaSources = sources.ToArray(sources.Count), MediaSources = sources.ToArray(),
Profile = _profile, Profile = _profile,
DeviceId = deviceId, DeviceId = deviceId,
MaxBitrate = _profile.MaxStreamingBitrate MaxBitrate = _profile.MaxStreamingBitrate
@ -217,10 +224,10 @@ namespace Emby.Dlna.Didl
streamInfo.TargetVideoBitrate, streamInfo.TargetVideoBitrate,
streamInfo.TargetTimestamp, streamInfo.TargetTimestamp,
streamInfo.IsDirectStream, streamInfo.IsDirectStream,
streamInfo.RunTimeTicks, streamInfo.RunTimeTicks ?? 0,
streamInfo.TargetVideoProfile, streamInfo.TargetVideoProfile,
streamInfo.TargetVideoLevel, streamInfo.TargetVideoLevel,
streamInfo.TargetFramerate, streamInfo.TargetFramerate ?? 0,
streamInfo.TargetPacketLength, streamInfo.TargetPacketLength,
streamInfo.TranscodeSeekInfo, streamInfo.TranscodeSeekInfo,
streamInfo.IsTargetAnamorphic, streamInfo.IsTargetAnamorphic,
@ -296,11 +303,11 @@ namespace Emby.Dlna.Didl
return true; return true;
} }
private void AddVideoResource(XmlWriter writer, IHasMediaSources video, string deviceId, Filter filter, string contentFeatures, StreamInfo streamInfo) private void AddVideoResource(XmlWriter writer, BaseItem video, string deviceId, Filter filter, string contentFeatures, StreamInfo streamInfo)
{ {
writer.WriteStartElement(string.Empty, "res", NS_DIDL); writer.WriteStartElement(string.Empty, "res", NS_DIDL);
var url = streamInfo.ToDlnaUrl(_serverAddress, _accessToken); var url = NormalizeDlnaMediaUrl(streamInfo.ToUrl(_serverAddress, _accessToken));
var mediaSource = streamInfo.MediaSource; var mediaSource = streamInfo.MediaSource;
@ -361,7 +368,7 @@ namespace Emby.Dlna.Didl
streamInfo.TargetVideoBitDepth, streamInfo.TargetVideoBitDepth,
streamInfo.TargetVideoProfile, streamInfo.TargetVideoProfile,
streamInfo.TargetVideoLevel, streamInfo.TargetVideoLevel,
streamInfo.TargetFramerate, streamInfo.TargetFramerate ?? 0,
streamInfo.TargetPacketLength, streamInfo.TargetPacketLength,
streamInfo.TargetTimestamp, streamInfo.TargetTimestamp,
streamInfo.IsTargetAnamorphic, streamInfo.IsTargetAnamorphic,
@ -494,7 +501,7 @@ namespace Emby.Dlna.Didl
return item.Name; return item.Name;
} }
private void AddAudioResource(DlnaOptions options, XmlWriter writer, IHasMediaSources audio, string deviceId, Filter filter, StreamInfo streamInfo = null) private void AddAudioResource(DlnaOptions options, XmlWriter writer, BaseItem audio, string deviceId, Filter filter, StreamInfo streamInfo = null)
{ {
writer.WriteStartElement(string.Empty, "res", NS_DIDL); writer.WriteStartElement(string.Empty, "res", NS_DIDL);
@ -504,14 +511,14 @@ namespace Emby.Dlna.Didl
streamInfo = new StreamBuilder(_mediaEncoder, GetStreamBuilderLogger(options)).BuildAudioItem(new AudioOptions streamInfo = new StreamBuilder(_mediaEncoder, GetStreamBuilderLogger(options)).BuildAudioItem(new AudioOptions
{ {
ItemId = GetClientId(audio), ItemId = audio.Id,
MediaSources = sources.ToArray(sources.Count), MediaSources = sources.ToArray(sources.Count),
Profile = _profile, Profile = _profile,
DeviceId = deviceId DeviceId = deviceId
}); });
} }
var url = streamInfo.ToDlnaUrl(_serverAddress, _accessToken); var url = NormalizeDlnaMediaUrl(streamInfo.ToUrl(_serverAddress, _accessToken));
var mediaSource = streamInfo.MediaSource; var mediaSource = streamInfo.MediaSource;
@ -573,7 +580,7 @@ namespace Emby.Dlna.Didl
targetChannels, targetChannels,
targetAudioBitDepth, targetAudioBitDepth,
streamInfo.IsDirectStream, streamInfo.IsDirectStream,
streamInfo.RunTimeTicks, streamInfo.RunTimeTicks ?? 0,
streamInfo.TranscodeSeekInfo); streamInfo.TranscodeSeekInfo);
writer.WriteAttributeString("protocolInfo", String.Format( writer.WriteAttributeString("protocolInfo", String.Format(
@ -606,7 +613,7 @@ namespace Emby.Dlna.Didl
{ {
writer.WriteStartElement(string.Empty, "container", NS_DIDL); writer.WriteStartElement(string.Empty, "container", NS_DIDL);
writer.WriteAttributeString("restricted", "0"); writer.WriteAttributeString("restricted", "1");
writer.WriteAttributeString("searchable", "1"); writer.WriteAttributeString("searchable", "1");
writer.WriteAttributeString("childCount", childCount.ToString(_usCulture)); writer.WriteAttributeString("childCount", childCount.ToString(_usCulture));
@ -628,13 +635,13 @@ namespace Emby.Dlna.Didl
else else
{ {
var parent = folder.DisplayParentId; var parent = folder.DisplayParentId;
if (!parent.HasValue) if (parent.Equals(Guid.Empty))
{ {
writer.WriteAttributeString("parentID", "0"); writer.WriteAttributeString("parentID", "0");
} }
else else
{ {
writer.WriteAttributeString("parentID", GetClientId(parent.Value, null)); writer.WriteAttributeString("parentID", GetClientId(parent, null));
} }
} }
} }
@ -669,7 +676,7 @@ namespace Emby.Dlna.Didl
return; return;
} }
var userdata = _userDataManager.GetUserData(user.Id, item); var userdata = _userDataManager.GetUserData(user, item);
if (userdata.PlaybackPositionTicks > 0) if (userdata.PlaybackPositionTicks > 0)
{ {
@ -713,21 +720,24 @@ namespace Emby.Dlna.Didl
AddValue(writer, "upnp", "publisher", studio, NS_UPNP); AddValue(writer, "upnp", "publisher", studio, NS_UPNP);
} }
if (filter.Contains("dc:description")) if (!(item is Folder))
{ {
var desc = item.Overview; if (filter.Contains("dc:description"))
{
var desc = item.Overview;
if (!string.IsNullOrWhiteSpace(desc)) if (!string.IsNullOrWhiteSpace(desc))
{ {
AddValue(writer, "dc", "description", desc, NS_DC); AddValue(writer, "dc", "description", desc, NS_DC);
} }
}
if (filter.Contains("upnp:longDescription"))
{
if (!string.IsNullOrWhiteSpace(item.Overview))
{
AddValue(writer, "upnp", "longDescription", item.Overview, NS_UPNP);
} }
//if (filter.Contains("upnp:longDescription"))
//{
// if (!string.IsNullOrWhiteSpace(item.Overview))
// {
// AddValue(writer, "upnp", "longDescription", item.Overview, NS_UPNP);
// }
//}
} }
if (!string.IsNullOrEmpty(item.OfficialRating)) if (!string.IsNullOrEmpty(item.OfficialRating))
@ -823,37 +833,37 @@ namespace Emby.Dlna.Didl
private void AddPeople(BaseItem item, XmlWriter writer) private void AddPeople(BaseItem item, XmlWriter writer)
{ {
var types = new[] //var types = new[]
{ //{
PersonType.Director, // PersonType.Director,
PersonType.Writer, // PersonType.Writer,
PersonType.Producer, // PersonType.Producer,
PersonType.Composer, // PersonType.Composer,
"Creator" // "Creator"
}; //};
var people = _libraryManager.GetPeople(item); //var people = _libraryManager.GetPeople(item);
var index = 0; //var index = 0;
// Seeing some LG models locking up due content with large lists of people //// Seeing some LG models locking up due content with large lists of people
// The actual issue might just be due to processing a more metadata than it can handle //// The actual issue might just be due to processing a more metadata than it can handle
var limit = 6; //var limit = 6;
foreach (var actor in people) //foreach (var actor in people)
{ //{
var type = types.FirstOrDefault(i => string.Equals(i, actor.Type, StringComparison.OrdinalIgnoreCase) || string.Equals(i, actor.Role, StringComparison.OrdinalIgnoreCase)) // var type = types.FirstOrDefault(i => string.Equals(i, actor.Type, StringComparison.OrdinalIgnoreCase) || string.Equals(i, actor.Role, StringComparison.OrdinalIgnoreCase))
?? PersonType.Actor; // ?? PersonType.Actor;
AddValue(writer, "upnp", type.ToLower(), actor.Name, NS_UPNP); // AddValue(writer, "upnp", type.ToLower(), actor.Name, NS_UPNP);
index++; // index++;
if (index >= limit) // if (index >= limit)
{ // {
break; // break;
} // }
} //}
} }
private void AddGeneralProperties(BaseItem item, StubType? itemStubType, BaseItem context, XmlWriter writer, Filter filter) private void AddGeneralProperties(BaseItem item, StubType? itemStubType, BaseItem context, XmlWriter writer, Filter filter)
@ -935,19 +945,6 @@ namespace Emby.Dlna.Didl
{ {
ImageDownloadInfo imageInfo = null; ImageDownloadInfo imageInfo = null;
if (context is UserView)
{
var episode = item as Episode;
if (episode != null)
{
var parent = episode.Series;
if (parent != null)
{
imageInfo = GetImageInfo(parent);
}
}
}
// Finally, just use the image from the item // Finally, just use the image from the item
if (imageInfo == null) if (imageInfo == null)
{ {
@ -959,34 +956,7 @@ namespace Emby.Dlna.Didl
return; return;
} }
var playbackPercentage = 0; var albumartUrlInfo = GetImageUrl(imageInfo, _profile.MaxAlbumArtWidth, _profile.MaxAlbumArtHeight, "jpg");
var unplayedCount = 0;
if (item is Video)
{
var userData = _userDataManager.GetUserDataDto(item, _user);
playbackPercentage = Convert.ToInt32(userData.PlayedPercentage ?? 0);
if (playbackPercentage >= 100 || userData.Played)
{
playbackPercentage = 100;
}
}
else if (item is Series || item is Season || item is BoxSet)
{
var userData = _userDataManager.GetUserDataDto(item, _user);
if (userData.Played)
{
playbackPercentage = 100;
}
else
{
unplayedCount = userData.UnplayedItemCount ?? 0;
}
}
var albumartUrlInfo = GetImageUrl(imageInfo, _profile.MaxAlbumArtWidth, _profile.MaxAlbumArtHeight, playbackPercentage, unplayedCount, "jpg");
writer.WriteStartElement("upnp", "albumArtURI", NS_UPNP); writer.WriteStartElement("upnp", "albumArtURI", NS_UPNP);
writer.WriteAttributeString("dlna", "profileID", NS_DLNA, _profile.AlbumArtPn); writer.WriteAttributeString("dlna", "profileID", NS_DLNA, _profile.AlbumArtPn);
@ -994,7 +964,7 @@ namespace Emby.Dlna.Didl
writer.WriteFullEndElement(); writer.WriteFullEndElement();
// TOOD: Remove these default values // TOOD: Remove these default values
var iconUrlInfo = GetImageUrl(imageInfo, _profile.MaxIconWidth ?? 48, _profile.MaxIconHeight ?? 48, playbackPercentage, unplayedCount, "jpg"); var iconUrlInfo = GetImageUrl(imageInfo, _profile.MaxIconWidth ?? 48, _profile.MaxIconHeight ?? 48, "jpg");
writer.WriteElementString("upnp", "icon", NS_UPNP, iconUrlInfo.Url); writer.WriteElementString("upnp", "icon", NS_UPNP, iconUrlInfo.Url);
if (!_profile.EnableAlbumArtInDidl) if (!_profile.EnableAlbumArtInDidl)
@ -1009,15 +979,15 @@ namespace Emby.Dlna.Didl
} }
} }
AddImageResElement(item, writer, 160, 160, playbackPercentage, unplayedCount, "jpg", "JPEG_TN"); AddImageResElement(item, writer, 160, 160, "jpg", "JPEG_TN");
if (!_profile.EnableSingleAlbumArtLimit) if (!_profile.EnableSingleAlbumArtLimit || string.Equals(item.MediaType, MediaType.Photo, StringComparison.OrdinalIgnoreCase))
{ {
AddImageResElement(item, writer, 4096, 4096, playbackPercentage, unplayedCount, "jpg", "JPEG_LRG"); AddImageResElement(item, writer, 4096, 4096, "jpg", "JPEG_LRG");
AddImageResElement(item, writer, 1024, 768, playbackPercentage, unplayedCount, "jpg", "JPEG_MED"); AddImageResElement(item, writer, 1024, 768, "jpg", "JPEG_MED");
AddImageResElement(item, writer, 640, 480, playbackPercentage, unplayedCount, "jpg", "JPEG_SM"); AddImageResElement(item, writer, 640, 480, "jpg", "JPEG_SM");
AddImageResElement(item, writer, 4096, 4096, playbackPercentage, unplayedCount, "png", "PNG_LRG"); AddImageResElement(item, writer, 4096, 4096, "png", "PNG_LRG");
AddImageResElement(item, writer, 160, 160, playbackPercentage, unplayedCount, "png", "PNG_TN"); AddImageResElement(item, writer, 160, 160, "png", "PNG_TN");
} }
} }
@ -1035,8 +1005,6 @@ namespace Emby.Dlna.Didl
XmlWriter writer, XmlWriter writer,
int maxWidth, int maxWidth,
int maxHeight, int maxHeight,
int playbackPercentage,
int unplayedCount,
string format, string format,
string org_Pn) string org_Pn)
{ {
@ -1047,7 +1015,7 @@ namespace Emby.Dlna.Didl
return; return;
} }
var albumartUrlInfo = GetImageUrl(imageInfo, maxWidth, maxHeight, playbackPercentage, unplayedCount, format); var albumartUrlInfo = GetImageUrl(imageInfo, maxWidth, maxHeight, format);
writer.WriteStartElement(string.Empty, "res", NS_DIDL); writer.WriteStartElement(string.Empty, "res", NS_DIDL);
@ -1151,7 +1119,7 @@ namespace Emby.Dlna.Didl
return new ImageDownloadInfo return new ImageDownloadInfo
{ {
ItemId = item.Id.ToString("N"), ItemId = item.Id,
Type = type, Type = type,
ImageTag = tag, ImageTag = tag,
Width = width, Width = width,
@ -1163,7 +1131,7 @@ namespace Emby.Dlna.Didl
class ImageDownloadInfo class ImageDownloadInfo
{ {
internal string ItemId; internal Guid ItemId;
internal string ImageTag; internal string ImageTag;
internal ImageType Type; internal ImageType Type;
@ -1202,25 +1170,16 @@ namespace Emby.Dlna.Didl
return id; return id;
} }
public static string GetClientId(IHasMediaSources item) private ImageUrlInfo GetImageUrl(ImageDownloadInfo info, int maxWidth, int maxHeight, string format)
{ {
var id = item.Id.ToString("N"); var url = string.Format("{0}/Items/{1}/Images/{2}/0/{3}/{4}/{5}/{6}/0/0",
return id;
}
private ImageUrlInfo GetImageUrl(ImageDownloadInfo info, int maxWidth, int maxHeight, int playbackPercentage, int unplayedCount, string format)
{
var url = string.Format("{0}/Items/{1}/Images/{2}/0/{3}/{4}/{5}/{6}/{7}/{8}",
_serverAddress, _serverAddress,
info.ItemId, info.ItemId.ToString("N"),
info.Type, info.Type,
info.ImageTag, info.ImageTag,
format, format,
maxWidth.ToString(CultureInfo.InvariantCulture), maxWidth.ToString(CultureInfo.InvariantCulture),
maxHeight.ToString(CultureInfo.InvariantCulture), maxHeight.ToString(CultureInfo.InvariantCulture)
playbackPercentage.ToString(CultureInfo.InvariantCulture),
unplayedCount.ToString(CultureInfo.InvariantCulture)
); );
var width = info.Width; var width = info.Width;
@ -1235,7 +1194,7 @@ namespace Emby.Dlna.Didl
Height = height.Value, Height = height.Value,
Width = width.Value Width = width.Value
}, null, null, maxWidth, maxHeight); }, 0, 0, maxWidth, maxHeight);
width = Convert.ToInt32(newSize.Width); width = Convert.ToInt32(newSize.Width);
height = Convert.ToInt32(newSize.Height); height = Convert.ToInt32(newSize.Height);
@ -1249,6 +1208,9 @@ namespace Emby.Dlna.Didl
} }
} }
// just lie
info.IsDirectStream = true;
return new ImageUrlInfo return new ImageUrlInfo
{ {
Url = url, Url = url,

View File

@ -132,55 +132,55 @@ namespace Emby.Dlna
private bool IsMatch(DeviceIdentification deviceInfo, DeviceIdentification profileInfo) private bool IsMatch(DeviceIdentification deviceInfo, DeviceIdentification profileInfo)
{ {
if (!string.IsNullOrWhiteSpace(profileInfo.DeviceDescription)) if (!string.IsNullOrEmpty(profileInfo.DeviceDescription))
{ {
if (deviceInfo.DeviceDescription == null || !IsRegexMatch(deviceInfo.DeviceDescription, profileInfo.DeviceDescription)) if (deviceInfo.DeviceDescription == null || !IsRegexMatch(deviceInfo.DeviceDescription, profileInfo.DeviceDescription))
return false; return false;
} }
if (!string.IsNullOrWhiteSpace(profileInfo.FriendlyName)) if (!string.IsNullOrEmpty(profileInfo.FriendlyName))
{ {
if (deviceInfo.FriendlyName == null || !IsRegexMatch(deviceInfo.FriendlyName, profileInfo.FriendlyName)) if (deviceInfo.FriendlyName == null || !IsRegexMatch(deviceInfo.FriendlyName, profileInfo.FriendlyName))
return false; return false;
} }
if (!string.IsNullOrWhiteSpace(profileInfo.Manufacturer)) if (!string.IsNullOrEmpty(profileInfo.Manufacturer))
{ {
if (deviceInfo.Manufacturer == null || !IsRegexMatch(deviceInfo.Manufacturer, profileInfo.Manufacturer)) if (deviceInfo.Manufacturer == null || !IsRegexMatch(deviceInfo.Manufacturer, profileInfo.Manufacturer))
return false; return false;
} }
if (!string.IsNullOrWhiteSpace(profileInfo.ManufacturerUrl)) if (!string.IsNullOrEmpty(profileInfo.ManufacturerUrl))
{ {
if (deviceInfo.ManufacturerUrl == null || !IsRegexMatch(deviceInfo.ManufacturerUrl, profileInfo.ManufacturerUrl)) if (deviceInfo.ManufacturerUrl == null || !IsRegexMatch(deviceInfo.ManufacturerUrl, profileInfo.ManufacturerUrl))
return false; return false;
} }
if (!string.IsNullOrWhiteSpace(profileInfo.ModelDescription)) if (!string.IsNullOrEmpty(profileInfo.ModelDescription))
{ {
if (deviceInfo.ModelDescription == null || !IsRegexMatch(deviceInfo.ModelDescription, profileInfo.ModelDescription)) if (deviceInfo.ModelDescription == null || !IsRegexMatch(deviceInfo.ModelDescription, profileInfo.ModelDescription))
return false; return false;
} }
if (!string.IsNullOrWhiteSpace(profileInfo.ModelName)) if (!string.IsNullOrEmpty(profileInfo.ModelName))
{ {
if (deviceInfo.ModelName == null || !IsRegexMatch(deviceInfo.ModelName, profileInfo.ModelName)) if (deviceInfo.ModelName == null || !IsRegexMatch(deviceInfo.ModelName, profileInfo.ModelName))
return false; return false;
} }
if (!string.IsNullOrWhiteSpace(profileInfo.ModelNumber)) if (!string.IsNullOrEmpty(profileInfo.ModelNumber))
{ {
if (deviceInfo.ModelNumber == null || !IsRegexMatch(deviceInfo.ModelNumber, profileInfo.ModelNumber)) if (deviceInfo.ModelNumber == null || !IsRegexMatch(deviceInfo.ModelNumber, profileInfo.ModelNumber))
return false; return false;
} }
if (!string.IsNullOrWhiteSpace(profileInfo.ModelUrl)) if (!string.IsNullOrEmpty(profileInfo.ModelUrl))
{ {
if (deviceInfo.ModelUrl == null || !IsRegexMatch(deviceInfo.ModelUrl, profileInfo.ModelUrl)) if (deviceInfo.ModelUrl == null || !IsRegexMatch(deviceInfo.ModelUrl, profileInfo.ModelUrl))
return false; return false;
} }
if (!string.IsNullOrWhiteSpace(profileInfo.SerialNumber)) if (!string.IsNullOrEmpty(profileInfo.SerialNumber))
{ {
if (deviceInfo.SerialNumber == null || !IsRegexMatch(deviceInfo.SerialNumber, profileInfo.SerialNumber)) if (deviceInfo.SerialNumber == null || !IsRegexMatch(deviceInfo.SerialNumber, profileInfo.SerialNumber))
return false; return false;
@ -220,7 +220,7 @@ namespace Emby.Dlna
} }
else else
{ {
var headerString = string.Join(", ", headers.Select(i => string.Format("{0}={1}", i.Key, i.Value)).ToArray(headers.Count)); var headerString = string.Join(", ", headers.Select(i => string.Format("{0}={1}", i.Key, i.Value)).ToArray());
_logger.Debug("No matching device profile found. {0}", headerString); _logger.Debug("No matching device profile found. {0}", headerString);
} }
@ -235,7 +235,7 @@ namespace Emby.Dlna
private bool IsMatch(IDictionary<string, string> headers, HttpHeaderInfo header) private bool IsMatch(IDictionary<string, string> headers, HttpHeaderInfo header)
{ {
// Handle invalid user setup // Handle invalid user setup
if (string.IsNullOrWhiteSpace(header.Name)) if (string.IsNullOrEmpty(header.Name))
{ {
return false; return false;
} }
@ -332,7 +332,7 @@ namespace Emby.Dlna
public DeviceProfile GetProfile(string id) public DeviceProfile GetProfile(string id)
{ {
if (string.IsNullOrWhiteSpace(id)) if (string.IsNullOrEmpty(id))
{ {
throw new ArgumentNullException("id"); throw new ArgumentNullException("id");
} }
@ -429,7 +429,7 @@ namespace Emby.Dlna
{ {
profile = ReserializeProfile(profile); profile = ReserializeProfile(profile);
if (string.IsNullOrWhiteSpace(profile.Name)) if (string.IsNullOrEmpty(profile.Name))
{ {
throw new ArgumentException("Profile is missing Name"); throw new ArgumentException("Profile is missing Name");
} }
@ -444,11 +444,11 @@ namespace Emby.Dlna
{ {
profile = ReserializeProfile(profile); profile = ReserializeProfile(profile);
if (string.IsNullOrWhiteSpace(profile.Id)) if (string.IsNullOrEmpty(profile.Id))
{ {
throw new ArgumentException("Profile is missing Id"); throw new ArgumentException("Profile is missing Id");
} }
if (string.IsNullOrWhiteSpace(profile.Name)) if (string.IsNullOrEmpty(profile.Name))
{ {
throw new ArgumentException("Profile is missing Name"); throw new ArgumentException("Profile is missing Name");
} }
@ -531,7 +531,7 @@ namespace Emby.Dlna
} }
} }
class DlnaProfileEntryPoint : IServerEntryPoint class DlnaProfileEntryPoint /*: IServerEntryPoint*/
{ {
private readonly IApplicationPaths _appPaths; private readonly IApplicationPaths _appPaths;
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
@ -546,7 +546,7 @@ namespace Emby.Dlna
public void Run() public void Run()
{ {
//DumpProfiles(); DumpProfiles();
} }
private void DumpProfiles() private void DumpProfiles()
@ -595,7 +595,6 @@ namespace Emby.Dlna
public void Dispose() public void Dispose()
{ {
GC.SuppressFinalize(this);
} }
} }
} }

View File

@ -1,121 +1,17 @@
<?xml version="1.0" encoding="utf-8"?> <Project Sdk="Microsoft.NET.Sdk">
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<MinimumVisualStudioVersion>11.0</MinimumVisualStudioVersion>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{805844AB-E92F-45E6-9D99-4F6D48D129A5}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Emby.Dlna</RootNamespace>
<AssemblyName>Emby.Dlna</AssemblyName>
<DefaultLanguage>en-US</DefaultLanguage>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<TargetFrameworkProfile>Profile7</TargetFrameworkProfile>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Common\Argument.cs" /> <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
<Compile Include="Common\DeviceIcon.cs" /> <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />
<Compile Include="Common\DeviceService.cs" /> <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" />
<Compile Include="Common\ServiceAction.cs" /> <ProjectReference Include="..\RSSDP\RSSDP.csproj" />
<Compile Include="Common\StateVariable.cs" />
<Compile Include="ConfigurationExtension.cs" />
<Compile Include="ConnectionManager\ConnectionManager.cs" />
<Compile Include="ConnectionManager\ConnectionManagerXmlBuilder.cs" />
<Compile Include="ConnectionManager\ControlHandler.cs" />
<Compile Include="ConnectionManager\ServiceActionListBuilder.cs" />
<Compile Include="ContentDirectory\ContentDirectory.cs" />
<Compile Include="ContentDirectory\ContentDirectoryBrowser.cs" />
<Compile Include="ContentDirectory\ContentDirectoryXmlBuilder.cs" />
<Compile Include="ContentDirectory\ControlHandler.cs" />
<Compile Include="ContentDirectory\ServiceActionListBuilder.cs" />
<Compile Include="Didl\DidlBuilder.cs" />
<Compile Include="Didl\Filter.cs" />
<Compile Include="Didl\StringWriterWithEncoding.cs" />
<Compile Include="DlnaManager.cs" />
<Compile Include="Eventing\EventManager.cs" />
<Compile Include="Eventing\EventSubscription.cs" />
<Compile Include="Main\DlnaEntryPoint.cs" />
<Compile Include="MediaReceiverRegistrar\ControlHandler.cs" />
<Compile Include="MediaReceiverRegistrar\MediaReceiverRegistrar.cs" />
<Compile Include="MediaReceiverRegistrar\MediaReceiverRegistrarXmlBuilder.cs" />
<Compile Include="MediaReceiverRegistrar\ServiceActionListBuilder.cs" />
<Compile Include="PlayTo\CurrentIdEventArgs.cs" />
<Compile Include="PlayTo\Device.cs" />
<Compile Include="PlayTo\DeviceInfo.cs" />
<Compile Include="PlayTo\PlaybackProgressEventArgs.cs" />
<Compile Include="PlayTo\PlaybackStartEventArgs.cs" />
<Compile Include="PlayTo\PlaybackStoppedEventArgs.cs" />
<Compile Include="PlayTo\PlaylistItem.cs" />
<Compile Include="PlayTo\PlaylistItemFactory.cs" />
<Compile Include="PlayTo\PlayToController.cs" />
<Compile Include="PlayTo\PlayToManager.cs" />
<Compile Include="PlayTo\SsdpHttpClient.cs" />
<Compile Include="PlayTo\TransportCommands.cs" />
<Compile Include="PlayTo\TRANSPORTSTATE.cs" />
<Compile Include="PlayTo\TransportStateEventArgs.cs" />
<Compile Include="PlayTo\uBaseObject.cs" />
<Compile Include="PlayTo\uParser.cs" />
<Compile Include="PlayTo\uParserObject.cs" />
<Compile Include="PlayTo\UpnpContainer.cs" />
<Compile Include="PlayTo\uPnpNamespaces.cs" />
<Compile Include="Profiles\DefaultProfile.cs" />
<Compile Include="Profiles\DenonAvrProfile.cs" />
<Compile Include="Profiles\DirectTvProfile.cs" />
<Compile Include="Profiles\DishHopperJoeyProfile.cs" />
<Compile Include="Profiles\Foobar2000Profile.cs" />
<Compile Include="Profiles\LgTvProfile.cs" />
<Compile Include="Profiles\LinksysDMA2100Profile.cs" />
<Compile Include="Profiles\MarantzProfile.cs" />
<Compile Include="Profiles\MediaMonkeyProfile.cs" />
<Compile Include="Profiles\PanasonicVieraProfile.cs" />
<Compile Include="Profiles\PopcornHourProfile.cs" />
<Compile Include="Profiles\SamsungSmartTvProfile.cs" />
<Compile Include="Profiles\SharpSmartTvProfile.cs" />
<Compile Include="Profiles\SonyBlurayPlayer2013.cs" />
<Compile Include="Profiles\SonyBlurayPlayer2014.cs" />
<Compile Include="Profiles\SonyBlurayPlayer2015.cs" />
<Compile Include="Profiles\SonyBlurayPlayer2016.cs" />
<Compile Include="Profiles\SonyBlurayPlayerProfile.cs" />
<Compile Include="Profiles\SonyBravia2010Profile.cs" />
<Compile Include="Profiles\SonyBravia2011Profile.cs" />
<Compile Include="Profiles\SonyBravia2012Profile.cs" />
<Compile Include="Profiles\SonyBravia2013Profile.cs" />
<Compile Include="Profiles\SonyBravia2014Profile.cs" />
<Compile Include="Profiles\SonyPs3Profile.cs" />
<Compile Include="Profiles\SonyPs4Profile.cs" />
<Compile Include="Profiles\WdtvLiveProfile.cs" />
<Compile Include="Profiles\XboxOneProfile.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Server\DescriptionXmlBuilder.cs" />
<Compile Include="Server\UpnpDevice.cs" />
<Compile Include="Service\BaseControlHandler.cs" />
<Compile Include="Service\BaseService.cs" />
<Compile Include="Service\ControlErrorHandler.cs" />
<Compile Include="Service\ServiceXmlBuilder.cs" />
<Compile Include="Ssdp\DeviceDiscovery.cs" />
<Compile Include="Ssdp\Extensions.cs" />
</ItemGroup> </ItemGroup>
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<EmbeddedResource Include="Images\logo120.jpg" /> <EmbeddedResource Include="Images\logo120.jpg" />
<EmbeddedResource Include="Images\logo120.png" /> <EmbeddedResource Include="Images\logo120.png" />
@ -128,27 +24,7 @@
<EmbeddedResource Include="Images\people480.jpg" /> <EmbeddedResource Include="Images\people480.jpg" />
<EmbeddedResource Include="Images\people480.png" /> <EmbeddedResource Include="Images\people480.png" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
<Project>{9142eefa-7570-41e1-bfcc-468bb571af2f}</Project>
<Name>MediaBrowser.Common</Name>
</ProjectReference>
<ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj">
<Project>{17e1f4e6-8abd-4fe5-9ecf-43d4b6087ba2}</Project>
<Name>MediaBrowser.Controller</Name>
</ProjectReference>
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj">
<Project>{7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}</Project>
<Name>MediaBrowser.Model</Name>
</ProjectReference>
<ProjectReference Include="..\RSSDP\RSSDP.csproj">
<Project>{21002819-c39a-4d3e-be83-2a276a77fb1f}</Project>
<Name>RSSDP</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Profiles\Xml\Sharp Smart TV.xml" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<EmbeddedResource Include="Profiles\Xml\Default.xml" /> <EmbeddedResource Include="Profiles\Xml\Default.xml" />
<EmbeddedResource Include="Profiles\Xml\Denon AVR.xml" /> <EmbeddedResource Include="Profiles\Xml\Denon AVR.xml" />
@ -179,12 +55,5 @@
<ItemGroup> <ItemGroup>
<EmbeddedResource Include="Profiles\Xml\Marantz.xml" /> <EmbeddedResource Include="Profiles\Xml\Marantz.xml" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. </Project>
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@ -1,6 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace MediaBrowser.Controller.Dlna namespace Emby.Dlna
{ {
public class EventSubscriptionResponse public class EventSubscriptionResponse
{ {

View File

@ -85,7 +85,7 @@ namespace Emby.Dlna.Eventing
int val; int val;
if (int.TryParse(header, NumberStyles.Any, _usCulture, out val)) if (int.TryParse(header, NumberStyles.Integer, _usCulture, out val))
{ {
return val; return val;
} }
@ -118,7 +118,7 @@ namespace Emby.Dlna.Eventing
}; };
response.Headers["SID"] = subscriptionId; response.Headers["SID"] = subscriptionId;
response.Headers["TIMEOUT"] = string.IsNullOrWhiteSpace(requestedTimeoutString) ? ("SECOND-" + timeoutSeconds.ToString(_usCulture)) : requestedTimeoutString; response.Headers["TIMEOUT"] = string.IsNullOrEmpty(requestedTimeoutString) ? ("SECOND-" + timeoutSeconds.ToString(_usCulture)) : requestedTimeoutString;
return response; return response;
} }

View File

@ -1,5 +1,5 @@
 
namespace MediaBrowser.Controller.Dlna namespace Emby.Dlna
{ {
public interface IConnectionManager : IEventManager, IUpnpService public interface IConnectionManager : IEventManager, IUpnpService
{ {

View File

@ -1,5 +1,5 @@
 
namespace MediaBrowser.Controller.Dlna namespace Emby.Dlna
{ {
public interface IContentDirectory : IEventManager, IUpnpService public interface IContentDirectory : IEventManager, IUpnpService
{ {

View File

@ -1,5 +1,5 @@
 
namespace MediaBrowser.Controller.Dlna namespace Emby.Dlna
{ {
public interface IEventManager public interface IEventManager
{ {

View File

@ -1,5 +1,5 @@
 
namespace MediaBrowser.Controller.Dlna namespace Emby.Dlna
{ {
public interface IMediaReceiverRegistrar : IEventManager, IUpnpService public interface IMediaReceiverRegistrar : IEventManager, IUpnpService
{ {

View File

@ -1,6 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace MediaBrowser.Controller.Dlna namespace Emby.Dlna
{ {
public interface IUpnpService public interface IUpnpService
{ {

View File

@ -8,13 +8,12 @@ using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Plugins;
using MediaBrowser.Controller.Session; using MediaBrowser.Controller.Session;
using MediaBrowser.Controller.TV;
using Emby.Dlna.PlayTo; using Emby.Dlna.PlayTo;
using Emby.Dlna.Ssdp; using Emby.Dlna.Ssdp;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
@ -22,13 +21,14 @@ using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Net; using MediaBrowser.Model.Net;
using MediaBrowser.Model.System; using MediaBrowser.Model.System;
using MediaBrowser.Model.Threading; using MediaBrowser.Model.Threading;
using MediaBrowser.Model.Xml;
using Rssdp; using Rssdp;
using Rssdp.Infrastructure; using Rssdp.Infrastructure;
using System.Threading; using System.Threading;
namespace Emby.Dlna.Main namespace Emby.Dlna.Main
{ {
public class DlnaEntryPoint : IServerEntryPoint public class DlnaEntryPoint : IServerEntryPoint, IRunBeforeStartup
{ {
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
private readonly ILogger _logger; private readonly ILogger _logger;
@ -48,8 +48,6 @@ namespace Emby.Dlna.Main
private readonly IDeviceDiscovery _deviceDiscovery; private readonly IDeviceDiscovery _deviceDiscovery;
private bool _ssdpHandlerStarted;
private bool _dlnaServerStarted;
private SsdpDevicePublisher _Publisher; private SsdpDevicePublisher _Publisher;
private readonly ITimerFactory _timerFactory; private readonly ITimerFactory _timerFactory;
@ -59,6 +57,12 @@ namespace Emby.Dlna.Main
private ISsdpCommunicationsServer _communicationsServer; private ISsdpCommunicationsServer _communicationsServer;
internal IContentDirectory ContentDirectory { get; private set; }
internal IConnectionManager ConnectionManager { get; private set; }
internal IMediaReceiverRegistrar MediaReceiverRegistrar { get; private set; }
public static DlnaEntryPoint Current;
public DlnaEntryPoint(IServerConfigurationManager config, public DlnaEntryPoint(IServerConfigurationManager config,
ILogManager logManager, ILogManager logManager,
IServerApplicationHost appHost, IServerApplicationHost appHost,
@ -69,9 +73,17 @@ namespace Emby.Dlna.Main
IDlnaManager dlnaManager, IDlnaManager dlnaManager,
IImageProcessor imageProcessor, IImageProcessor imageProcessor,
IUserDataManager userDataManager, IUserDataManager userDataManager,
ILocalizationManager localization, ILocalizationManager localizationManager,
IMediaSourceManager mediaSourceManager, IMediaSourceManager mediaSourceManager,
IDeviceDiscovery deviceDiscovery, IMediaEncoder mediaEncoder, ISocketFactory socketFactory, ITimerFactory timerFactory, IEnvironmentInfo environmentInfo, INetworkManager networkManager) IDeviceDiscovery deviceDiscovery,
IMediaEncoder mediaEncoder,
ISocketFactory socketFactory,
ITimerFactory timerFactory,
IEnvironmentInfo environmentInfo,
INetworkManager networkManager,
IUserViewManager userViewManager,
IXmlReaderSettingsFactory xmlReaderSettingsFactory,
ITVSeriesManager tvSeriesManager)
{ {
_config = config; _config = config;
_appHost = appHost; _appHost = appHost;
@ -82,7 +94,7 @@ namespace Emby.Dlna.Main
_dlnaManager = dlnaManager; _dlnaManager = dlnaManager;
_imageProcessor = imageProcessor; _imageProcessor = imageProcessor;
_userDataManager = userDataManager; _userDataManager = userDataManager;
_localization = localization; _localization = localizationManager;
_mediaSourceManager = mediaSourceManager; _mediaSourceManager = mediaSourceManager;
_deviceDiscovery = deviceDiscovery; _deviceDiscovery = deviceDiscovery;
_mediaEncoder = mediaEncoder; _mediaEncoder = mediaEncoder;
@ -91,6 +103,26 @@ namespace Emby.Dlna.Main
_environmentInfo = environmentInfo; _environmentInfo = environmentInfo;
_networkManager = networkManager; _networkManager = networkManager;
_logger = logManager.GetLogger("Dlna"); _logger = logManager.GetLogger("Dlna");
ContentDirectory = new ContentDirectory.ContentDirectory(dlnaManager,
userDataManager,
imageProcessor,
libraryManager,
config,
userManager,
_logger,
httpClient,
localizationManager,
mediaSourceManager,
userViewManager,
mediaEncoder,
xmlReaderSettingsFactory,
tvSeriesManager);
ConnectionManager = new ConnectionManager.ConnectionManager(dlnaManager, config, _logger, httpClient, xmlReaderSettingsFactory);
MediaReceiverRegistrar = new MediaReceiverRegistrar.MediaReceiverRegistrar(_logger, httpClient, config, xmlReaderSettingsFactory);
Current = this;
} }
public void Run() public void Run()
@ -99,20 +131,9 @@ namespace Emby.Dlna.Main
ReloadComponents(); ReloadComponents();
_config.ConfigurationUpdated += _config_ConfigurationUpdated;
_config.NamedConfigurationUpdated += _config_NamedConfigurationUpdated; _config.NamedConfigurationUpdated += _config_NamedConfigurationUpdated;
} }
private bool _lastEnableUpnP;
void _config_ConfigurationUpdated(object sender, EventArgs e)
{
if (_lastEnableUpnP != _config.Configuration.EnableUPnP)
{
ReloadComponents();
}
_lastEnableUpnP = _config.Configuration.EnableUPnP;
}
void _config_NamedConfigurationUpdated(object sender, ConfigurationUpdateEventArgs e) void _config_NamedConfigurationUpdated(object sender, ConfigurationUpdateEventArgs e)
{ {
if (string.Equals(e.Key, "dlna", StringComparison.OrdinalIgnoreCase)) if (string.Equals(e.Key, "dlna", StringComparison.OrdinalIgnoreCase))
@ -125,29 +146,22 @@ namespace Emby.Dlna.Main
{ {
var options = _config.GetDlnaConfiguration(); var options = _config.GetDlnaConfiguration();
if (!_ssdpHandlerStarted) StartSsdpHandler();
if (options.EnableServer)
{ {
StartSsdpHandler(); await StartDevicePublisher(options).ConfigureAwait(false);
}
else
{
DisposeDevicePublisher();
} }
var isServerStarted = _dlnaServerStarted; if (options.EnablePlayTo)
if (options.EnableServer && !isServerStarted)
{
await StartDlnaServer().ConfigureAwait(false);
}
else if (!options.EnableServer && isServerStarted)
{
DisposeDlnaServer();
}
var isPlayToStarted = _manager != null;
if (options.EnablePlayTo && !isPlayToStarted)
{ {
StartPlayToManager(); StartPlayToManager();
} }
else if (!options.EnablePlayTo && isPlayToStarted) else
{ {
DisposePlayToManager(); DisposePlayToManager();
} }
@ -165,12 +179,9 @@ namespace Emby.Dlna.Main
{ {
IsShared = true IsShared = true
}; };
StartDeviceDiscovery(_communicationsServer);
} }
StartPublishing(_communicationsServer);
_ssdpHandlerStarted = true;
StartDeviceDiscovery(_communicationsServer);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -183,12 +194,6 @@ namespace Emby.Dlna.Main
_logger.Debug(msg); _logger.Debug(msg);
} }
private void StartPublishing(ISsdpCommunicationsServer communicationsServer)
{
SsdpDevicePublisherBase.LogFunction = LogMessage;
_Publisher = new SsdpDevicePublisher(communicationsServer, _timerFactory, _environmentInfo.OperatingSystemName, _environmentInfo.OperatingSystemVersion);
}
private void StartDeviceDiscovery(ISsdpCommunicationsServer communicationsServer) private void StartDeviceDiscovery(ISsdpCommunicationsServer communicationsServer)
{ {
try try
@ -205,6 +210,7 @@ namespace Emby.Dlna.Main
{ {
try try
{ {
_logger.Info("Disposing DeviceDiscovery");
((DeviceDiscovery)_deviceDiscovery).Dispose(); ((DeviceDiscovery)_deviceDiscovery).Dispose();
} }
catch (Exception ex) catch (Exception ex)
@ -213,29 +219,27 @@ namespace Emby.Dlna.Main
} }
} }
private void DisposeSsdpHandler() public async Task StartDevicePublisher(Configuration.DlnaOptions options)
{ {
DisposeDeviceDiscovery(); if (!options.BlastAliveMessages)
{
return;
}
if (_Publisher != null)
{
return;
}
try try
{ {
((DeviceDiscovery)_deviceDiscovery).Dispose(); _Publisher = new SsdpDevicePublisher(_communicationsServer, _timerFactory, _environmentInfo.OperatingSystemName, _environmentInfo.OperatingSystemVersion);
_Publisher.LogFunction = LogMessage;
_Publisher.SupportPnpRootDevice = false;
_ssdpHandlerStarted = false;
}
catch (Exception ex)
{
_logger.ErrorException("Error stopping ssdp handlers", ex);
}
}
public async Task StartDlnaServer()
{
try
{
await RegisterServerEndpoints().ConfigureAwait(false); await RegisterServerEndpoints().ConfigureAwait(false);
_dlnaServerStarted = true; _Publisher.StartBroadcastingAliveMessages(TimeSpan.FromSeconds(options.BlastAliveMessageIntervalSeconds));
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -245,23 +249,15 @@ namespace Emby.Dlna.Main
private async Task RegisterServerEndpoints() private async Task RegisterServerEndpoints()
{ {
if (!_config.GetDlnaConfiguration().BlastAliveMessages)
{
return;
}
var cacheLength = _config.GetDlnaConfiguration().BlastAliveMessageIntervalSeconds;
_Publisher.SupportPnpRootDevice = false;
var addresses = (await _appHost.GetLocalIpAddresses(CancellationToken.None).ConfigureAwait(false)).ToList(); var addresses = (await _appHost.GetLocalIpAddresses(CancellationToken.None).ConfigureAwait(false)).ToList();
var udn = CreateUuid(_appHost.SystemId); var udn = CreateUuid(_appHost.SystemId);
foreach (var address in addresses) foreach (var address in addresses)
{ {
//if (IPAddress.IsLoopback(address)) // TODO: Remove this condition on platforms that support it
//if (address.AddressFamily == IpAddressFamily.InterNetworkV6)
//{ //{
// // Should we allow this?
// continue; // continue;
//} //}
@ -274,7 +270,7 @@ namespace Emby.Dlna.Main
var device = new SsdpRootDevice var device = new SsdpRootDevice
{ {
CacheLifetime = TimeSpan.FromSeconds(cacheLength), //How long SSDP clients can cache this info. CacheLifetime = TimeSpan.FromSeconds(1800), //How long SSDP clients can cache this info.
Location = uri, // Must point to the URL that serves your devices UPnP description document. Location = uri, // Must point to the URL that serves your devices UPnP description document.
FriendlyName = "Emby Server", FriendlyName = "Emby Server",
Manufacturer = "Emby", Manufacturer = "Emby",
@ -286,7 +282,7 @@ namespace Emby.Dlna.Main
SetProperies(device, fullService); SetProperies(device, fullService);
_Publisher.AddDevice(device); _Publisher.AddDevice(device);
var embeddedDevices = new List<string> var embeddedDevices = new []
{ {
"urn:schemas-upnp-org:service:ContentDirectory:1", "urn:schemas-upnp-org:service:ContentDirectory:1",
"urn:schemas-upnp-org:service:ConnectionManager:1", "urn:schemas-upnp-org:service:ConnectionManager:1",
@ -338,6 +334,11 @@ namespace Emby.Dlna.Main
{ {
lock (_syncLock) lock (_syncLock)
{ {
if (_manager != null)
{
return;
}
try try
{ {
_manager = new PlayToManager(_logger, _manager = new PlayToManager(_logger,
@ -373,6 +374,7 @@ namespace Emby.Dlna.Main
{ {
try try
{ {
_logger.Info("Disposing PlayToManager");
_manager.Dispose(); _manager.Dispose();
} }
catch (Exception ex) catch (Exception ex)
@ -386,41 +388,31 @@ namespace Emby.Dlna.Main
public void Dispose() public void Dispose()
{ {
DisposeDlnaServer(); DisposeDevicePublisher();
DisposePlayToManager(); DisposePlayToManager();
DisposeSsdpHandler(); DisposeDeviceDiscovery();
if (_communicationsServer != null) if (_communicationsServer != null)
{ {
_logger.Info("Disposing SsdpCommunicationsServer");
_communicationsServer.Dispose(); _communicationsServer.Dispose();
_communicationsServer = null; _communicationsServer = null;
} }
GC.SuppressFinalize(this);
ContentDirectory = null;
ConnectionManager = null;
MediaReceiverRegistrar = null;
Current = null;
} }
public void DisposeDlnaServer() public void DisposeDevicePublisher()
{ {
if (_Publisher != null) if (_Publisher != null)
{ {
var devices = _Publisher.Devices.ToList(); _logger.Info("Disposing SsdpDevicePublisher");
var tasks = devices.Select(i => _Publisher.RemoveDevice(i)).ToArray();
Task.WaitAll(tasks);
//foreach (var device in devices)
//{
// try
// {
// _Publisher.RemoveDevice(device);
// }
// catch (Exception ex)
// {
// _logger.ErrorException("Error sending bye bye", ex);
// }
//}
_Publisher.Dispose(); _Publisher.Dispose();
_Publisher = null;
} }
_dlnaServerStarted = false;
} }
} }
} }

View File

@ -7,7 +7,7 @@ namespace Emby.Dlna.MediaReceiverRegistrar
{ {
public IEnumerable<ServiceAction> GetActions() public IEnumerable<ServiceAction> GetActions()
{ {
var list = new List<ServiceAction> return new []
{ {
GetIsValidated(), GetIsValidated(),
GetIsAuthorized(), GetIsAuthorized(),
@ -17,8 +17,6 @@ namespace Emby.Dlna.MediaReceiverRegistrar
GetGetValidationRevokedUpdateID(), GetGetValidationRevokedUpdateID(),
GetGetValidationSucceededUpdateID() GetGetValidationSucceededUpdateID()
}; };
return list;
} }
private ServiceAction GetIsValidated() private ServiceAction GetIsValidated()

View File

@ -106,27 +106,17 @@ namespace Emby.Dlna.PlayTo
_timerFactory = timerFactory; _timerFactory = timerFactory;
} }
private int GetPlaybackTimerIntervalMs()
{
return 1000;
}
private int GetInactiveTimerIntervalMs()
{
return 60000;
}
public void Start() public void Start()
{ {
_timer = _timerFactory.Create(TimerCallback, null, GetPlaybackTimerIntervalMs(), GetInactiveTimerIntervalMs()); _logger.Debug("Dlna Device.Start");
_timer = _timerFactory.Create(TimerCallback, null, 1000, Timeout.Infinite);
_timerActive = false;
} }
private DateTime _lastVolumeRefresh; private DateTime _lastVolumeRefresh;
private bool _volumeRefreshActive;
private void RefreshVolumeIfNeeded() private void RefreshVolumeIfNeeded()
{ {
if (!_timerActive) if (!_volumeRefreshActive)
{ {
return; return;
} }
@ -134,19 +124,19 @@ namespace Emby.Dlna.PlayTo
if (DateTime.UtcNow >= _lastVolumeRefresh.AddSeconds(5)) if (DateTime.UtcNow >= _lastVolumeRefresh.AddSeconds(5))
{ {
_lastVolumeRefresh = DateTime.UtcNow; _lastVolumeRefresh = DateTime.UtcNow;
RefreshVolume(); RefreshVolume(CancellationToken.None);
} }
} }
private async void RefreshVolume() private async void RefreshVolume(CancellationToken cancellationToken)
{ {
if (_disposed) if (_disposed)
return; return;
try try
{ {
await GetVolume().ConfigureAwait(false); await GetVolume(cancellationToken).ConfigureAwait(false);
await GetMute().ConfigureAwait(false); await GetMute(cancellationToken).ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -155,21 +145,17 @@ namespace Emby.Dlna.PlayTo
} }
private readonly object _timerLock = new object(); private readonly object _timerLock = new object();
private bool _timerActive; private void RestartTimer(bool immediate = false)
private void RestartTimer()
{ {
if (_disposed)
return;
lock (_timerLock) lock (_timerLock)
{ {
if (!_timerActive) if (_disposed)
{ return;
_logger.Debug("RestartTimer");
_timer.Change(10, GetPlaybackTimerIntervalMs());
}
_timerActive = true; _volumeRefreshActive = true;
var time = immediate ? 100 : 10000;
_timer.Change(time, Timeout.Infinite);
} }
} }
@ -178,71 +164,67 @@ namespace Emby.Dlna.PlayTo
/// </summary> /// </summary>
private void RestartTimerInactive() private void RestartTimerInactive()
{ {
if (_disposed)
return;
lock (_timerLock) lock (_timerLock)
{ {
if (_timerActive) if (_disposed)
{ return;
_logger.Debug("RestartTimerInactive");
var interval = GetInactiveTimerIntervalMs();
if (_timer != null) _volumeRefreshActive = false;
{
_timer.Change(interval, interval);
}
}
_timerActive = false; _timer.Change(Timeout.Infinite, Timeout.Infinite);
} }
} }
public void OnPlaybackStartedExternally()
{
RestartTimer(true);
}
#region Commanding #region Commanding
public Task VolumeDown() public Task VolumeDown(CancellationToken cancellationToken)
{ {
var sendVolume = Math.Max(Volume - 5, 0); var sendVolume = Math.Max(Volume - 5, 0);
return SetVolume(sendVolume); return SetVolume(sendVolume, cancellationToken);
} }
public Task VolumeUp() public Task VolumeUp(CancellationToken cancellationToken)
{ {
var sendVolume = Math.Min(Volume + 5, 100); var sendVolume = Math.Min(Volume + 5, 100);
return SetVolume(sendVolume); return SetVolume(sendVolume, cancellationToken);
} }
public Task ToggleMute() public Task ToggleMute(CancellationToken cancellationToken)
{ {
if (IsMuted) if (IsMuted)
{ {
return Unmute(); return Unmute(cancellationToken);
} }
return Mute(); return Mute(cancellationToken);
} }
public async Task Mute() public async Task Mute(CancellationToken cancellationToken)
{ {
var success = await SetMute(true).ConfigureAwait(true); var success = await SetMute(true, cancellationToken).ConfigureAwait(true);
if (!success) if (!success)
{ {
await SetVolume(0).ConfigureAwait(false); await SetVolume(0, cancellationToken).ConfigureAwait(false);
} }
} }
public async Task Unmute() public async Task Unmute(CancellationToken cancellationToken)
{ {
var success = await SetMute(false).ConfigureAwait(true); var success = await SetMute(false, cancellationToken).ConfigureAwait(true);
if (!success) if (!success)
{ {
var sendVolume = _muteVol <= 0 ? 20 : _muteVol; var sendVolume = _muteVol <= 0 ? 20 : _muteVol;
await SetVolume(sendVolume).ConfigureAwait(false); await SetVolume(sendVolume, cancellationToken).ConfigureAwait(false);
} }
} }
@ -262,9 +244,11 @@ namespace Emby.Dlna.PlayTo
services.FirstOrDefault(s => (s.ServiceType ?? string.Empty).StartsWith("urn:schemas-upnp-org:service:AVTransport", StringComparison.OrdinalIgnoreCase)); services.FirstOrDefault(s => (s.ServiceType ?? string.Empty).StartsWith("urn:schemas-upnp-org:service:AVTransport", StringComparison.OrdinalIgnoreCase));
} }
private async Task<bool> SetMute(bool mute) private async Task<bool> SetMute(bool mute, CancellationToken cancellationToken)
{ {
var command = RendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "SetMute"); var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false);
var command = rendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "SetMute");
if (command == null) if (command == null)
return false; return false;
@ -278,7 +262,7 @@ namespace Emby.Dlna.PlayTo
_logger.Debug("Setting mute"); _logger.Debug("Setting mute");
var value = mute ? 1 : 0; var value = mute ? 1 : 0;
await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, RendererCommands.BuildPost(command, service.ServiceType, value)) await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType, value))
.ConfigureAwait(false); .ConfigureAwait(false);
IsMuted = mute; IsMuted = mute;
@ -289,9 +273,11 @@ namespace Emby.Dlna.PlayTo
/// <summary> /// <summary>
/// Sets volume on a scale of 0-100 /// Sets volume on a scale of 0-100
/// </summary> /// </summary>
public async Task SetVolume(int value) public async Task SetVolume(int value, CancellationToken cancellationToken)
{ {
var command = RendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "SetVolume"); var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false);
var command = rendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "SetVolume");
if (command == null) if (command == null)
return; return;
@ -306,13 +292,15 @@ namespace Emby.Dlna.PlayTo
// Remote control will perform better // Remote control will perform better
Volume = value; Volume = value;
await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, RendererCommands.BuildPost(command, service.ServiceType, value)) await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType, value))
.ConfigureAwait(false); .ConfigureAwait(false);
} }
public async Task Seek(TimeSpan value) public async Task Seek(TimeSpan value, CancellationToken cancellationToken)
{ {
var command = AvCommands.ServiceActions.FirstOrDefault(c => c.Name == "Seek"); var avCommands = await GetAVProtocolAsync(cancellationToken).ConfigureAwait(false);
var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "Seek");
if (command == null) if (command == null)
return; return;
@ -323,15 +311,21 @@ namespace Emby.Dlna.PlayTo
throw new InvalidOperationException("Unable to find service"); throw new InvalidOperationException("Unable to find service");
} }
await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, AvCommands.BuildPost(command, service.ServiceType, String.Format("{0:hh}:{0:mm}:{0:ss}", value), "REL_TIME")) await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, String.Format("{0:hh}:{0:mm}:{0:ss}", value), "REL_TIME"))
.ConfigureAwait(false); .ConfigureAwait(false);
RestartTimer(true);
} }
public async Task SetAvTransport(string url, string header, string metaData) public async Task SetAvTransport(string url, string header, string metaData, CancellationToken cancellationToken)
{ {
var avCommands = await GetAVProtocolAsync(cancellationToken).ConfigureAwait(false);
url = url.Replace("&", "&amp;");
_logger.Debug("{0} - SetAvTransport Uri: {1} DlnaHeaders: {2}", Properties.Name, url, header); _logger.Debug("{0} - SetAvTransport Uri: {1} DlnaHeaders: {2}", Properties.Name, url, header);
var command = AvCommands.ServiceActions.FirstOrDefault(c => c.Name == "SetAVTransportURI"); var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "SetAVTransportURI");
if (command == null) if (command == null)
return; return;
@ -348,7 +342,7 @@ namespace Emby.Dlna.PlayTo
throw new InvalidOperationException("Unable to find service"); throw new InvalidOperationException("Unable to find service");
} }
var post = AvCommands.BuildPost(command, service.ServiceType, url, dictionary); var post = avCommands.BuildPost(command, service.ServiceType, url, dictionary);
await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, post, header: header) await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, post, header: header)
.ConfigureAwait(false); .ConfigureAwait(false);
@ -356,7 +350,7 @@ namespace Emby.Dlna.PlayTo
try try
{ {
await SetPlay().ConfigureAwait(false); await SetPlay(avCommands, CancellationToken.None).ConfigureAwait(false);
} }
catch catch
{ {
@ -364,7 +358,7 @@ namespace Emby.Dlna.PlayTo
// Others won't // Others won't
} }
RestartTimer(); RestartTimer(true);
} }
private string CreateDidlMeta(string value) private string CreateDidlMeta(string value)
@ -375,11 +369,11 @@ namespace Emby.Dlna.PlayTo
return DescriptionXmlBuilder.Escape(value); return DescriptionXmlBuilder.Escape(value);
} }
public async Task SetPlay() private Task SetPlay(TransportCommands avCommands, CancellationToken cancellationToken)
{ {
var command = AvCommands.ServiceActions.FirstOrDefault(c => c.Name == "Play"); var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "Play");
if (command == null) if (command == null)
return; return Task.CompletedTask;
var service = GetAvTransportService(); var service = GetAvTransportService();
@ -388,52 +382,74 @@ namespace Emby.Dlna.PlayTo
throw new InvalidOperationException("Unable to find service"); throw new InvalidOperationException("Unable to find service");
} }
await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, AvCommands.BuildPost(command, service.ServiceType, 1)) return new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, 1));
.ConfigureAwait(false);
} }
public async Task SetStop() public async Task SetPlay(CancellationToken cancellationToken)
{ {
var command = AvCommands.ServiceActions.FirstOrDefault(c => c.Name == "Stop"); var avCommands = await GetAVProtocolAsync(cancellationToken).ConfigureAwait(false);
await SetPlay(avCommands, cancellationToken).ConfigureAwait(false);
RestartTimer(true);
}
public async Task SetStop(CancellationToken cancellationToken)
{
var avCommands = await GetAVProtocolAsync(cancellationToken).ConfigureAwait(false);
var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "Stop");
if (command == null) if (command == null)
return; return;
var service = GetAvTransportService(); var service = GetAvTransportService();
await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, AvCommands.BuildPost(command, service.ServiceType, 1)) await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, 1))
.ConfigureAwait(false); .ConfigureAwait(false);
RestartTimer(true);
} }
public async Task SetPause() public async Task SetPause(CancellationToken cancellationToken)
{ {
var command = AvCommands.ServiceActions.FirstOrDefault(c => c.Name == "Pause"); var avCommands = await GetAVProtocolAsync(cancellationToken).ConfigureAwait(false);
var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "Pause");
if (command == null) if (command == null)
return; return;
var service = GetAvTransportService(); var service = GetAvTransportService();
await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, AvCommands.BuildPost(command, service.ServiceType, 1)) await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, 1))
.ConfigureAwait(false); .ConfigureAwait(false);
TransportState = TRANSPORTSTATE.PAUSED; TransportState = TRANSPORTSTATE.PAUSED;
RestartTimer(true);
} }
#endregion #endregion
#region Get data #region Get data
private int _successiveStopCount;
private int _connectFailureCount; private int _connectFailureCount;
private async void TimerCallback(object sender) private async void TimerCallback(object sender)
{ {
if (_disposed) if (_disposed)
return; return;
const int maxSuccessiveStopReturns = 5;
try try
{ {
var transportState = await GetTransportInfo().ConfigureAwait(false); var cancellationToken = CancellationToken.None;
var avCommands = await GetAVProtocolAsync(cancellationToken).ConfigureAwait(false);
if (avCommands == null)
{
return;
}
var transportState = await GetTransportInfo(avCommands, cancellationToken).ConfigureAwait(false);
if (_disposed) if (_disposed)
{ {
@ -451,13 +467,13 @@ namespace Emby.Dlna.PlayTo
} }
else else
{ {
var tuple = await GetPositionInfo().ConfigureAwait(false); var tuple = await GetPositionInfo(avCommands, cancellationToken).ConfigureAwait(false);
var currentObject = tuple.Item2; var currentObject = tuple.Item2;
if (tuple.Item1 && currentObject == null) if (tuple.Item1 && currentObject == null)
{ {
currentObject = await GetMediaInfo().ConfigureAwait(false); currentObject = await GetMediaInfo(avCommands, cancellationToken).ConfigureAwait(false);
} }
if (currentObject != null) if (currentObject != null)
@ -474,16 +490,10 @@ namespace Emby.Dlna.PlayTo
// If we're not playing anything make sure we don't get data more often than neccessry to keep the Session alive // If we're not playing anything make sure we don't get data more often than neccessry to keep the Session alive
if (transportState.Value == TRANSPORTSTATE.STOPPED) if (transportState.Value == TRANSPORTSTATE.STOPPED)
{ {
_successiveStopCount++; RestartTimerInactive();
if (_successiveStopCount >= maxSuccessiveStopReturns)
{
RestartTimerInactive();
}
} }
else else
{ {
_successiveStopCount = 0;
RestartTimer(); RestartTimer();
} }
} }
@ -492,54 +502,39 @@ namespace Emby.Dlna.PlayTo
RestartTimerInactive(); RestartTimerInactive();
} }
} }
catch (HttpException ex)
{
if (_disposed)
return;
//_logger.ErrorException("Error updating device info for {0}", ex, Properties.Name);
_successiveStopCount++;
_connectFailureCount++;
if (_connectFailureCount >= 3)
{
if (OnDeviceUnavailable != null)
{
_logger.Debug("Disposing device due to loss of connection");
OnDeviceUnavailable();
return;
}
}
if (_successiveStopCount >= maxSuccessiveStopReturns)
{
RestartTimerInactive();
}
}
catch (Exception ex) catch (Exception ex)
{ {
if (_disposed) if (_disposed)
return; return;
_logger.ErrorException("Error updating device info for {0}", ex, Properties.Name); //_logger.ErrorException("Error updating device info for {0}", ex, Properties.Name);
_successiveStopCount++; _connectFailureCount++;
if (_successiveStopCount >= maxSuccessiveStopReturns) if (_connectFailureCount >= 3)
{ {
RestartTimerInactive(); var action = OnDeviceUnavailable;
if (action != null)
{
_logger.Debug("Disposing device due to loss of connection");
action();
return;
}
} }
RestartTimerInactive();
} }
} }
private async Task GetVolume() private async Task GetVolume(CancellationToken cancellationToken)
{ {
if (_disposed) if (_disposed)
{ {
return; return;
} }
var command = RendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetVolume"); var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false);
var command = rendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetVolume");
if (command == null) if (command == null)
return; return;
@ -550,7 +545,7 @@ namespace Emby.Dlna.PlayTo
return; return;
} }
var result = await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, RendererCommands.BuildPost(command, service.ServiceType), true) var result = await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType), true)
.ConfigureAwait(false); .ConfigureAwait(false);
if (result == null || result.Document == null) if (result == null || result.Document == null)
@ -570,14 +565,16 @@ namespace Emby.Dlna.PlayTo
} }
} }
private async Task GetMute() private async Task GetMute(CancellationToken cancellationToken)
{ {
if (_disposed) if (_disposed)
{ {
return; return;
} }
var command = RendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetMute"); var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false);
var command = rendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetMute");
if (command == null) if (command == null)
return; return;
@ -588,7 +585,7 @@ namespace Emby.Dlna.PlayTo
return; return;
} }
var result = await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, RendererCommands.BuildPost(command, service.ServiceType), true) var result = await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType), true)
.ConfigureAwait(false); .ConfigureAwait(false);
if (result == null || result.Document == null) if (result == null || result.Document == null)
@ -600,9 +597,9 @@ namespace Emby.Dlna.PlayTo
IsMuted = string.Equals(value, "1", StringComparison.OrdinalIgnoreCase); IsMuted = string.Equals(value, "1", StringComparison.OrdinalIgnoreCase);
} }
private async Task<TRANSPORTSTATE?> GetTransportInfo() private async Task<TRANSPORTSTATE?> GetTransportInfo(TransportCommands avCommands, CancellationToken cancellationToken)
{ {
var command = AvCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetTransportInfo"); var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetTransportInfo");
if (command == null) if (command == null)
return null; return null;
@ -610,7 +607,7 @@ namespace Emby.Dlna.PlayTo
if (service == null) if (service == null)
return null; return null;
var result = await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, AvCommands.BuildPost(command, service.ServiceType), false) var result = await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType), false)
.ConfigureAwait(false); .ConfigureAwait(false);
if (result == null || result.Document == null) if (result == null || result.Document == null)
@ -634,9 +631,9 @@ namespace Emby.Dlna.PlayTo
return null; return null;
} }
private async Task<uBaseObject> GetMediaInfo() private async Task<uBaseObject> GetMediaInfo(TransportCommands avCommands, CancellationToken cancellationToken)
{ {
var command = AvCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetMediaInfo"); var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetMediaInfo");
if (command == null) if (command == null)
return null; return null;
@ -647,7 +644,9 @@ namespace Emby.Dlna.PlayTo
throw new InvalidOperationException("Unable to find service"); throw new InvalidOperationException("Unable to find service");
} }
var result = await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, RendererCommands.BuildPost(command, service.ServiceType), false) var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false);
var result = await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType), false)
.ConfigureAwait(false); .ConfigureAwait(false);
if (result == null || result.Document == null) if (result == null || result.Document == null)
@ -691,9 +690,9 @@ namespace Emby.Dlna.PlayTo
return null; return null;
} }
private async Task<Tuple<bool, uBaseObject>> GetPositionInfo() private async Task<Tuple<bool, uBaseObject>> GetPositionInfo(TransportCommands avCommands, CancellationToken cancellationToken)
{ {
var command = AvCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetPositionInfo"); var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetPositionInfo");
if (command == null) if (command == null)
return new Tuple<bool, uBaseObject>(false, null); return new Tuple<bool, uBaseObject>(false, null);
@ -704,7 +703,9 @@ namespace Emby.Dlna.PlayTo
throw new InvalidOperationException("Unable to find service"); throw new InvalidOperationException("Unable to find service");
} }
var result = await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, RendererCommands.BuildPost(command, service.ServiceType), false) var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false);
var result = await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType), false)
.ConfigureAwait(false); .ConfigureAwait(false);
if (result == null || result.Document == null) if (result == null || result.Document == null)
@ -831,42 +832,67 @@ namespace Emby.Dlna.PlayTo
#region From XML #region From XML
private async Task GetAVProtocolAsync(CancellationToken cancellationToken) private async Task<TransportCommands> GetAVProtocolAsync(CancellationToken cancellationToken)
{ {
var avCommands = AvCommands;
if (avCommands != null)
{
return avCommands;
}
if (_disposed) if (_disposed)
{ {
return; throw new ObjectDisposedException(GetType().Name);
} }
var avService = GetAvTransportService(); var avService = GetAvTransportService();
if (avService == null) if (avService == null)
return; {
return null;
}
string url = NormalizeUrl(Properties.BaseUrl, avService.ScpdUrl); string url = NormalizeUrl(Properties.BaseUrl, avService.ScpdUrl);
var httpClient = new SsdpHttpClient(_httpClient, _config); var httpClient = new SsdpHttpClient(_httpClient, _config);
var document = await httpClient.GetDataAsync(url, cancellationToken).ConfigureAwait(false); var document = await httpClient.GetDataAsync(url, cancellationToken).ConfigureAwait(false);
AvCommands = TransportCommands.Create(document); avCommands = TransportCommands.Create(document);
AvCommands = avCommands;
return avCommands;
} }
private async Task GetRenderingProtocolAsync(CancellationToken cancellationToken) private async Task<TransportCommands> GetRenderingProtocolAsync(CancellationToken cancellationToken)
{ {
var rendererCommands = RendererCommands;
if (rendererCommands != null)
{
return rendererCommands;
}
if (_disposed) if (_disposed)
{ {
return; throw new ObjectDisposedException(GetType().Name);
} }
var avService = GetServiceRenderingControl(); var avService = GetServiceRenderingControl();
if (avService == null) if (avService == null)
return; {
throw new ArgumentException("Device AvService is null");
}
string url = NormalizeUrl(Properties.BaseUrl, avService.ScpdUrl); string url = NormalizeUrl(Properties.BaseUrl, avService.ScpdUrl);
var httpClient = new SsdpHttpClient(_httpClient, _config); var httpClient = new SsdpHttpClient(_httpClient, _config);
_logger.Debug("Dlna Device.GetRenderingProtocolAsync");
var document = await httpClient.GetDataAsync(url, cancellationToken).ConfigureAwait(false); var document = await httpClient.GetDataAsync(url, cancellationToken).ConfigureAwait(false);
RendererCommands = TransportCommands.Create(document); rendererCommands = TransportCommands.Create(document);
RendererCommands = rendererCommands;
return rendererCommands;
} }
private string NormalizeUrl(string baseUrl, string url) private string NormalizeUrl(string baseUrl, string url)
@ -891,7 +917,7 @@ namespace Emby.Dlna.PlayTo
set; set;
} }
internal TransportCommands RendererCommands private TransportCommands RendererCommands
{ {
get; get;
set; set;
@ -985,12 +1011,6 @@ namespace Emby.Dlna.PlayTo
var device = new Device(deviceProperties, httpClient, logger, config, timerFactory); var device = new Device(deviceProperties, httpClient, logger, config, timerFactory);
if (device.GetAvTransportService() != null)
{
await device.GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false);
await device.GetAVProtocolAsync(cancellationToken).ConfigureAwait(false);
}
return device; return device;
} }
@ -1010,8 +1030,8 @@ namespace Emby.Dlna.PlayTo
var depth = element.GetDescendantValue(uPnpNamespaces.ud.GetName("depth")); var depth = element.GetDescendantValue(uPnpNamespaces.ud.GetName("depth"));
var url = element.GetDescendantValue(uPnpNamespaces.ud.GetName("url")); var url = element.GetDescendantValue(uPnpNamespaces.ud.GetName("url"));
var widthValue = int.Parse(width, NumberStyles.Any, UsCulture); var widthValue = int.Parse(width, NumberStyles.Integer, UsCulture);
var heightValue = int.Parse(height, NumberStyles.Any, UsCulture); var heightValue = int.Parse(height, NumberStyles.Integer, UsCulture);
return new DeviceIcon return new DeviceIcon
{ {
@ -1089,6 +1109,12 @@ namespace Emby.Dlna.PlayTo
private void OnPlaybackProgress(uBaseObject mediaInfo) private void OnPlaybackProgress(uBaseObject mediaInfo)
{ {
var mediaUrl = mediaInfo.Url;
if (string.IsNullOrWhiteSpace(mediaUrl))
{
return;
}
if (PlaybackProgress != null) if (PlaybackProgress != null)
{ {
PlaybackProgress.Invoke(this, new PlaybackProgressEventArgs PlaybackProgress.Invoke(this, new PlaybackProgressEventArgs
@ -1131,7 +1157,6 @@ namespace Emby.Dlna.PlayTo
_disposed = true; _disposed = true;
DisposeTimer(); DisposeTimer();
GC.SuppressFinalize(this);
} }
} }

View File

@ -8,7 +8,6 @@ namespace Emby.Dlna.PlayTo
{ {
public DeviceInfo() public DeviceInfo()
{ {
ClientType = "DLNA";
Name = "Generic Device"; Name = "Generic Device";
} }
@ -16,8 +15,6 @@ namespace Emby.Dlna.PlayTo
public string Name { get; set; } public string Name { get; set; }
public string ClientType { get; set; }
public string ModelName { get; set; } public string ModelName { get; set; }
public string ModelNumber { get; set; } public string ModelNumber { get; set; }

View File

@ -21,6 +21,8 @@ using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Events; using MediaBrowser.Model.Events;
using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Extensions; using MediaBrowser.Model.Extensions;
using System.Net.Http;
using MediaBrowser.Model.Services;
namespace Emby.Dlna.PlayTo namespace Emby.Dlna.PlayTo
{ {
@ -53,10 +55,6 @@ namespace Emby.Dlna.PlayTo
} }
} }
public void OnActivity()
{
}
public bool SupportsMediaControl public bool SupportsMediaControl
{ {
get { return IsSessionActive; } get { return IsSessionActive; }
@ -141,17 +139,15 @@ namespace Emby.Dlna.PlayTo
try try
{ {
var streamInfo = await StreamParams.ParseFromUrl(e.OldMediaInfo.Url, _libraryManager, _mediaSourceManager).ConfigureAwait(false); var streamInfo = StreamParams.ParseFromUrl(e.OldMediaInfo.Url, _libraryManager, _mediaSourceManager);
if (streamInfo.Item != null) if (streamInfo.Item != null)
{ {
var progress = GetProgressInfo(e.OldMediaInfo, streamInfo); var positionTicks = GetProgressPositionTicks(e.OldMediaInfo, streamInfo);
var positionTicks = progress.PositionTicks;
ReportPlaybackStopped(e.OldMediaInfo, streamInfo, positionTicks); ReportPlaybackStopped(e.OldMediaInfo, streamInfo, positionTicks);
} }
streamInfo = await StreamParams.ParseFromUrl(e.NewMediaInfo.Url, _libraryManager, _mediaSourceManager).ConfigureAwait(false); streamInfo = StreamParams.ParseFromUrl(e.NewMediaInfo.Url, _libraryManager, _mediaSourceManager);
if (streamInfo.Item == null) return; if (streamInfo.Item == null) return;
var newItemProgress = GetProgressInfo(e.NewMediaInfo, streamInfo); var newItemProgress = GetProgressInfo(e.NewMediaInfo, streamInfo);
@ -173,20 +169,19 @@ namespace Emby.Dlna.PlayTo
try try
{ {
var streamInfo = await StreamParams.ParseFromUrl(e.MediaInfo.Url, _libraryManager, _mediaSourceManager) var streamInfo = StreamParams.ParseFromUrl(e.MediaInfo.Url, _libraryManager, _mediaSourceManager);
.ConfigureAwait(false);
if (streamInfo.Item == null) return; if (streamInfo.Item == null) return;
var progress = GetProgressInfo(e.MediaInfo, streamInfo); var positionTicks = GetProgressPositionTicks(e.MediaInfo, streamInfo);
var positionTicks = progress.PositionTicks;
ReportPlaybackStopped(e.MediaInfo, streamInfo, positionTicks); ReportPlaybackStopped(e.MediaInfo, streamInfo, positionTicks);
var duration = streamInfo.MediaSource == null ? var mediaSource = await streamInfo.GetMediaSource(CancellationToken.None).ConfigureAwait(false);
var duration = mediaSource == null ?
(_device.Duration == null ? (long?)null : _device.Duration.Value.Ticks) : (_device.Duration == null ? (long?)null : _device.Duration.Value.Ticks) :
streamInfo.MediaSource.RunTimeTicks; mediaSource.RunTimeTicks;
var playedToCompletion = (positionTicks.HasValue && positionTicks.Value == 0); var playedToCompletion = (positionTicks.HasValue && positionTicks.Value == 0);
@ -241,7 +236,7 @@ namespace Emby.Dlna.PlayTo
try try
{ {
var info = await StreamParams.ParseFromUrl(e.MediaInfo.Url, _libraryManager, _mediaSourceManager).ConfigureAwait(false); var info = StreamParams.ParseFromUrl(e.MediaInfo.Url, _libraryManager, _mediaSourceManager);
if (info.Item != null) if (info.Item != null)
{ {
@ -265,7 +260,14 @@ namespace Emby.Dlna.PlayTo
try try
{ {
var info = await StreamParams.ParseFromUrl(e.MediaInfo.Url, _libraryManager, _mediaSourceManager).ConfigureAwait(false); var mediaUrl = e.MediaInfo.Url;
if (string.IsNullOrWhiteSpace(mediaUrl))
{
return;
}
var info = StreamParams.ParseFromUrl(mediaUrl, _libraryManager, _mediaSourceManager);
if (info.Item != null) if (info.Item != null)
{ {
@ -280,7 +282,7 @@ namespace Emby.Dlna.PlayTo
} }
} }
private PlaybackStartInfo GetProgressInfo(uBaseObject mediaInfo, StreamParams info) private long? GetProgressPositionTicks(uBaseObject mediaInfo, StreamParams info)
{ {
var ticks = _device.Position.Ticks; var ticks = _device.Position.Ticks;
@ -289,11 +291,16 @@ namespace Emby.Dlna.PlayTo
ticks += info.StartPositionTicks; ticks += info.StartPositionTicks;
} }
return ticks;
}
private PlaybackStartInfo GetProgressInfo(uBaseObject mediaInfo, StreamParams info)
{
return new PlaybackStartInfo return new PlaybackStartInfo
{ {
ItemId = info.ItemId, ItemId = info.ItemId,
SessionId = _session.Id, SessionId = _session.Id,
PositionTicks = ticks, PositionTicks = GetProgressPositionTicks(mediaInfo, info),
IsMuted = _device.IsMuted, IsMuted = _device.IsMuted,
IsPaused = _device.IsPaused, IsPaused = _device.IsPaused,
MediaSourceId = info.MediaSourceId, MediaSourceId = info.MediaSourceId,
@ -301,7 +308,8 @@ namespace Emby.Dlna.PlayTo
SubtitleStreamIndex = info.SubtitleStreamIndex, SubtitleStreamIndex = info.SubtitleStreamIndex,
VolumeLevel = _device.Volume, VolumeLevel = _device.Volume,
CanSeek = info.MediaSource == null ? _device.Duration.HasValue : info.MediaSource.RunTimeTicks.HasValue, // TODO
CanSeek = true,
PlayMethod = info.IsDirectStream ? PlayMethod.DirectStream : PlayMethod.Transcode PlayMethod = info.IsDirectStream ? PlayMethod.DirectStream : PlayMethod.Transcode
}; };
@ -313,12 +321,12 @@ namespace Emby.Dlna.PlayTo
{ {
_logger.Debug("{0} - Received PlayRequest: {1}", this._session.DeviceName, command.PlayCommand); _logger.Debug("{0} - Received PlayRequest: {1}", this._session.DeviceName, command.PlayCommand);
var user = String.IsNullOrEmpty(command.ControllingUserId) ? null : _userManager.GetUserById(command.ControllingUserId); var user = command.ControllingUserId.Equals(Guid.Empty) ? null : _userManager.GetUserById(command.ControllingUserId);
var items = new List<BaseItem>(); var items = new List<BaseItem>();
foreach (string id in command.ItemIds) foreach (var id in command.ItemIds)
{ {
AddItemFromId(Guid.Parse(id), items); AddItemFromId(id, items);
} }
var startIndex = command.StartIndex ?? 0; var startIndex = command.StartIndex ?? 0;
@ -354,31 +362,31 @@ namespace Emby.Dlna.PlayTo
Playlist.AddRange(playlist); Playlist.AddRange(playlist);
} }
if (!String.IsNullOrWhiteSpace(command.ControllingUserId)) if (!command.ControllingUserId.Equals(Guid.Empty))
{ {
await _sessionManager.LogSessionActivity(_session.Client, _session.ApplicationVersion, _session.DeviceId, _sessionManager.LogSessionActivity(_session.Client, _session.ApplicationVersion, _session.DeviceId,
_session.DeviceName, _session.RemoteEndPoint, user).ConfigureAwait(false); _session.DeviceName, _session.RemoteEndPoint, user);
} }
await PlayItems(playlist).ConfigureAwait(false); await PlayItems(playlist).ConfigureAwait(false);
} }
public Task SendPlaystateCommand(PlaystateRequest command, CancellationToken cancellationToken) private Task SendPlaystateCommand(PlaystateRequest command, CancellationToken cancellationToken)
{ {
switch (command.Command) switch (command.Command)
{ {
case PlaystateCommand.Stop: case PlaystateCommand.Stop:
Playlist.Clear(); Playlist.Clear();
return _device.SetStop(); return _device.SetStop(CancellationToken.None);
case PlaystateCommand.Pause: case PlaystateCommand.Pause:
return _device.SetPause(); return _device.SetPause(CancellationToken.None);
case PlaystateCommand.Unpause: case PlaystateCommand.Unpause:
return _device.SetPlay(); return _device.SetPlay(CancellationToken.None);
case PlaystateCommand.PlayPause: case PlaystateCommand.PlayPause:
return _device.IsPaused ? _device.SetPlay() : _device.SetPause(); return _device.IsPaused ? _device.SetPlay(CancellationToken.None) : _device.SetPause(CancellationToken.None);
case PlaystateCommand.Seek: case PlaystateCommand.Seek:
{ {
@ -392,7 +400,7 @@ namespace Emby.Dlna.PlayTo
return SetPlaylistIndex(_currentPlaylistIndex - 1); return SetPlaylistIndex(_currentPlaylistIndex - 1);
} }
return Task.FromResult(true); return Task.CompletedTask;
} }
private async Task Seek(long newPosition) private async Task Seek(long newPosition)
@ -401,17 +409,17 @@ namespace Emby.Dlna.PlayTo
if (media != null) if (media != null)
{ {
var info = await StreamParams.ParseFromUrl(media.Url, _libraryManager, _mediaSourceManager).ConfigureAwait(false); var info = StreamParams.ParseFromUrl(media.Url, _libraryManager, _mediaSourceManager);
if (info.Item != null && !EnableClientSideSeek(info)) if (info.Item != null && !EnableClientSideSeek(info))
{ {
var user = _session.UserId.HasValue ? _userManager.GetUserById(_session.UserId.Value) : null; var user = !_session.UserId.Equals(Guid.Empty) ? _userManager.GetUserById(_session.UserId) : null;
var newItem = CreatePlaylistItem(info.Item, user, newPosition, info.MediaSourceId, info.AudioStreamIndex, info.SubtitleStreamIndex); var newItem = CreatePlaylistItem(info.Item, user, newPosition, info.MediaSourceId, info.AudioStreamIndex, info.SubtitleStreamIndex);
await _device.SetAvTransport(newItem.StreamUrl, GetDlnaHeaders(newItem), newItem.Didl).ConfigureAwait(false); await _device.SetAvTransport(newItem.StreamUrl, GetDlnaHeaders(newItem), newItem.Didl, CancellationToken.None).ConfigureAwait(false);
return; return;
} }
await SeekAfterTransportChange(newPosition).ConfigureAwait(false); await SeekAfterTransportChange(newPosition, CancellationToken.None).ConfigureAwait(false);
} }
} }
@ -425,46 +433,6 @@ namespace Emby.Dlna.PlayTo
return info.IsDirectStream; return info.IsDirectStream;
} }
public Task SendUserDataChangeInfo(UserDataChangeInfo info, CancellationToken cancellationToken)
{
return Task.FromResult(true);
}
public Task SendRestartRequiredNotification(CancellationToken cancellationToken)
{
return Task.FromResult(true);
}
public Task SendServerRestartNotification(CancellationToken cancellationToken)
{
return Task.FromResult(true);
}
public Task SendSessionEndedNotification(SessionInfoDto sessionInfo, CancellationToken cancellationToken)
{
return Task.FromResult(true);
}
public Task SendPlaybackStartNotification(SessionInfoDto sessionInfo, CancellationToken cancellationToken)
{
return Task.FromResult(true);
}
public Task SendPlaybackStoppedNotification(SessionInfoDto sessionInfo, CancellationToken cancellationToken)
{
return Task.FromResult(true);
}
public Task SendServerShutdownNotification(CancellationToken cancellationToken)
{
return Task.FromResult(true);
}
public Task SendLibraryUpdateInfo(LibraryUpdateInfo info, CancellationToken cancellationToken)
{
return Task.FromResult(true);
}
#endregion #endregion
#region Playlist #region Playlist
@ -497,13 +465,13 @@ namespace Emby.Dlna.PlayTo
var hasMediaSources = item as IHasMediaSources; var hasMediaSources = item as IHasMediaSources;
var mediaSources = hasMediaSources != null var mediaSources = hasMediaSources != null
? (_mediaSourceManager.GetStaticMediaSources(hasMediaSources, true, user)) ? (_mediaSourceManager.GetStaticMediaSources(item, true, user))
: new List<MediaSourceInfo>(); : new List<MediaSourceInfo>();
var playlistItem = GetPlaylistItem(item, mediaSources, profile, _session.DeviceId, mediaSourceId, audioStreamIndex, subtitleStreamIndex); var playlistItem = GetPlaylistItem(item, mediaSources, profile, _session.DeviceId, mediaSourceId, audioStreamIndex, subtitleStreamIndex);
playlistItem.StreamInfo.StartPositionTicks = startPostionTicks; playlistItem.StreamInfo.StartPositionTicks = startPostionTicks;
playlistItem.StreamUrl = playlistItem.StreamInfo.ToDlnaUrl(_serverAddress, _accessToken); playlistItem.StreamUrl = DidlBuilder.NormalizeDlnaMediaUrl(playlistItem.StreamInfo.ToUrl(_serverAddress, _accessToken));
var itemXml = new DidlBuilder(profile, user, _imageProcessor, _serverAddress, _accessToken, _userDataManager, _localization, _mediaSourceManager, _logger, _libraryManager, _mediaEncoder) var itemXml = new DidlBuilder(profile, user, _imageProcessor, _serverAddress, _accessToken, _userDataManager, _localization, _mediaSourceManager, _logger, _libraryManager, _mediaEncoder)
.GetItemDidl(_config.GetDlnaConfiguration(), item, user, null, _session.DeviceId, new Filter(), playlistItem.StreamInfo); .GetItemDidl(_config.GetDlnaConfiguration(), item, user, null, _session.DeviceId, new Filter(), playlistItem.StreamInfo);
@ -528,7 +496,7 @@ namespace Emby.Dlna.PlayTo
streamInfo.TargetAudioChannels, streamInfo.TargetAudioChannels,
streamInfo.TargetAudioBitDepth, streamInfo.TargetAudioBitDepth,
streamInfo.IsDirectStream, streamInfo.IsDirectStream,
streamInfo.RunTimeTicks, streamInfo.RunTimeTicks ?? 0,
streamInfo.TranscodeSeekInfo); streamInfo.TranscodeSeekInfo);
} }
@ -544,10 +512,10 @@ namespace Emby.Dlna.PlayTo
streamInfo.TargetVideoBitrate, streamInfo.TargetVideoBitrate,
streamInfo.TargetTimestamp, streamInfo.TargetTimestamp,
streamInfo.IsDirectStream, streamInfo.IsDirectStream,
streamInfo.RunTimeTicks, streamInfo.RunTimeTicks ?? 0,
streamInfo.TargetVideoProfile, streamInfo.TargetVideoProfile,
streamInfo.TargetVideoLevel, streamInfo.TargetVideoLevel,
streamInfo.TargetFramerate, streamInfo.TargetFramerate ?? 0,
streamInfo.TargetPacketLength, streamInfo.TargetPacketLength,
streamInfo.TranscodeSeekInfo, streamInfo.TranscodeSeekInfo,
streamInfo.IsTargetAnamorphic, streamInfo.IsTargetAnamorphic,
@ -582,8 +550,8 @@ namespace Emby.Dlna.PlayTo
{ {
StreamInfo = new StreamBuilder(_mediaEncoder, GetStreamBuilderLogger()).BuildVideoItem(new VideoOptions StreamInfo = new StreamBuilder(_mediaEncoder, GetStreamBuilderLogger()).BuildVideoItem(new VideoOptions
{ {
ItemId = item.Id.ToString("N"), ItemId = item.Id,
MediaSources = mediaSources.ToArray(mediaSources.Count), MediaSources = mediaSources.ToArray(),
Profile = profile, Profile = profile,
DeviceId = deviceId, DeviceId = deviceId,
MaxBitrate = profile.MaxStreamingBitrate, MaxBitrate = profile.MaxStreamingBitrate,
@ -602,7 +570,7 @@ namespace Emby.Dlna.PlayTo
{ {
StreamInfo = new StreamBuilder(_mediaEncoder, GetStreamBuilderLogger()).BuildAudioItem(new AudioOptions StreamInfo = new StreamBuilder(_mediaEncoder, GetStreamBuilderLogger()).BuildAudioItem(new AudioOptions
{ {
ItemId = item.Id.ToString("N"), ItemId = item.Id,
MediaSources = mediaSources.ToArray(mediaSources.Count), MediaSources = mediaSources.ToArray(mediaSources.Count),
Profile = profile, Profile = profile,
DeviceId = deviceId, DeviceId = deviceId,
@ -642,19 +610,19 @@ namespace Emby.Dlna.PlayTo
if (index < 0 || index >= Playlist.Count) if (index < 0 || index >= Playlist.Count)
{ {
Playlist.Clear(); Playlist.Clear();
await _device.SetStop(); await _device.SetStop(CancellationToken.None);
return; return;
} }
_currentPlaylistIndex = index; _currentPlaylistIndex = index;
var currentitem = Playlist[index]; var currentitem = Playlist[index];
await _device.SetAvTransport(currentitem.StreamUrl, GetDlnaHeaders(currentitem), currentitem.Didl); await _device.SetAvTransport(currentitem.StreamUrl, GetDlnaHeaders(currentitem), currentitem.Didl, CancellationToken.None);
var streamInfo = currentitem.StreamInfo; var streamInfo = currentitem.StreamInfo;
if (streamInfo.StartPositionTicks > 0 && EnableClientSideSeek(streamInfo)) if (streamInfo.StartPositionTicks > 0 && EnableClientSideSeek(streamInfo))
{ {
await SeekAfterTransportChange(streamInfo.StartPositionTicks).ConfigureAwait(false); await SeekAfterTransportChange(streamInfo.StartPositionTicks, CancellationToken.None).ConfigureAwait(false);
} }
} }
@ -676,13 +644,12 @@ namespace Emby.Dlna.PlayTo
_device.OnDeviceUnavailable = null; _device.OnDeviceUnavailable = null;
_device.Dispose(); _device.Dispose();
GC.SuppressFinalize(this);
} }
} }
private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private readonly CultureInfo _usCulture = new CultureInfo("en-US");
public Task SendGeneralCommand(GeneralCommand command, CancellationToken cancellationToken) private Task SendGeneralCommand(GeneralCommand command, CancellationToken cancellationToken)
{ {
GeneralCommandType commandType; GeneralCommandType commandType;
@ -691,15 +658,15 @@ namespace Emby.Dlna.PlayTo
switch (commandType) switch (commandType)
{ {
case GeneralCommandType.VolumeDown: case GeneralCommandType.VolumeDown:
return _device.VolumeDown(); return _device.VolumeDown(cancellationToken);
case GeneralCommandType.VolumeUp: case GeneralCommandType.VolumeUp:
return _device.VolumeUp(); return _device.VolumeUp(cancellationToken);
case GeneralCommandType.Mute: case GeneralCommandType.Mute:
return _device.Mute(); return _device.Mute(cancellationToken);
case GeneralCommandType.Unmute: case GeneralCommandType.Unmute:
return _device.Unmute(); return _device.Unmute(cancellationToken);
case GeneralCommandType.ToggleMute: case GeneralCommandType.ToggleMute:
return _device.ToggleMute(); return _device.ToggleMute(cancellationToken);
case GeneralCommandType.SetAudioStreamIndex: case GeneralCommandType.SetAudioStreamIndex:
{ {
string arg; string arg;
@ -708,7 +675,7 @@ namespace Emby.Dlna.PlayTo
{ {
int val; int val;
if (Int32.TryParse(arg, NumberStyles.Any, _usCulture, out val)) if (int.TryParse(arg, NumberStyles.Integer, _usCulture, out val))
{ {
return SetAudioStreamIndex(val); return SetAudioStreamIndex(val);
} }
@ -726,7 +693,7 @@ namespace Emby.Dlna.PlayTo
{ {
int val; int val;
if (Int32.TryParse(arg, NumberStyles.Any, _usCulture, out val)) if (int.TryParse(arg, NumberStyles.Integer, _usCulture, out val))
{ {
return SetSubtitleStreamIndex(val); return SetSubtitleStreamIndex(val);
} }
@ -744,9 +711,9 @@ namespace Emby.Dlna.PlayTo
{ {
int volume; int volume;
if (Int32.TryParse(arg, NumberStyles.Any, _usCulture, out volume)) if (int.TryParse(arg, NumberStyles.Integer, _usCulture, out volume))
{ {
return _device.SetVolume(volume); return _device.SetVolume(volume, cancellationToken);
} }
throw new ArgumentException("Unsupported volume value supplied."); throw new ArgumentException("Unsupported volume value supplied.");
@ -755,11 +722,11 @@ namespace Emby.Dlna.PlayTo
throw new ArgumentException("Volume argument cannot be null"); throw new ArgumentException("Volume argument cannot be null");
} }
default: default:
return Task.FromResult(true); return Task.CompletedTask;
} }
} }
return Task.FromResult(true); return Task.CompletedTask;
} }
private async Task SetAudioStreamIndex(int? newIndex) private async Task SetAudioStreamIndex(int? newIndex)
@ -768,21 +735,20 @@ namespace Emby.Dlna.PlayTo
if (media != null) if (media != null)
{ {
var info = await StreamParams.ParseFromUrl(media.Url, _libraryManager, _mediaSourceManager).ConfigureAwait(false); var info = StreamParams.ParseFromUrl(media.Url, _libraryManager, _mediaSourceManager);
if (info.Item != null) if (info.Item != null)
{ {
var progress = GetProgressInfo(media, info); var newPosition = GetProgressPositionTicks(media, info) ?? 0;
var newPosition = progress.PositionTicks ?? 0;
var user = _session.UserId.HasValue ? _userManager.GetUserById(_session.UserId.Value) : null; var user = !_session.UserId.Equals(Guid.Empty) ? _userManager.GetUserById(_session.UserId) : null;
var newItem = CreatePlaylistItem(info.Item, user, newPosition, info.MediaSourceId, newIndex, info.SubtitleStreamIndex); var newItem = CreatePlaylistItem(info.Item, user, newPosition, info.MediaSourceId, newIndex, info.SubtitleStreamIndex);
await _device.SetAvTransport(newItem.StreamUrl, GetDlnaHeaders(newItem), newItem.Didl).ConfigureAwait(false); await _device.SetAvTransport(newItem.StreamUrl, GetDlnaHeaders(newItem), newItem.Didl, CancellationToken.None).ConfigureAwait(false);
if (EnableClientSideSeek(newItem.StreamInfo)) if (EnableClientSideSeek(newItem.StreamInfo))
{ {
await SeekAfterTransportChange(newPosition).ConfigureAwait(false); await SeekAfterTransportChange(newPosition, CancellationToken.None).ConfigureAwait(false);
} }
} }
} }
@ -794,27 +760,26 @@ namespace Emby.Dlna.PlayTo
if (media != null) if (media != null)
{ {
var info = await StreamParams.ParseFromUrl(media.Url, _libraryManager, _mediaSourceManager).ConfigureAwait(false); var info = StreamParams.ParseFromUrl(media.Url, _libraryManager, _mediaSourceManager);
if (info.Item != null) if (info.Item != null)
{ {
var progress = GetProgressInfo(media, info); var newPosition = GetProgressPositionTicks(media, info) ?? 0;
var newPosition = progress.PositionTicks ?? 0;
var user = _session.UserId.HasValue ? _userManager.GetUserById(_session.UserId.Value) : null; var user = !_session.UserId.Equals(Guid.Empty) ? _userManager.GetUserById(_session.UserId) : null;
var newItem = CreatePlaylistItem(info.Item, user, newPosition, info.MediaSourceId, info.AudioStreamIndex, newIndex); var newItem = CreatePlaylistItem(info.Item, user, newPosition, info.MediaSourceId, info.AudioStreamIndex, newIndex);
await _device.SetAvTransport(newItem.StreamUrl, GetDlnaHeaders(newItem), newItem.Didl).ConfigureAwait(false); await _device.SetAvTransport(newItem.StreamUrl, GetDlnaHeaders(newItem), newItem.Didl, CancellationToken.None).ConfigureAwait(false);
if (EnableClientSideSeek(newItem.StreamInfo) && newPosition > 0) if (EnableClientSideSeek(newItem.StreamInfo) && newPosition > 0)
{ {
await SeekAfterTransportChange(newPosition).ConfigureAwait(false); await SeekAfterTransportChange(newPosition, CancellationToken.None).ConfigureAwait(false);
} }
} }
} }
} }
private async Task SeekAfterTransportChange(long positionTicks) private async Task SeekAfterTransportChange(long positionTicks, CancellationToken cancellationToken)
{ {
const int maxWait = 15000000; const int maxWait = 15000000;
const int interval = 500; const int interval = 500;
@ -825,12 +790,12 @@ namespace Emby.Dlna.PlayTo
currentWait += interval; currentWait += interval;
} }
await _device.Seek(TimeSpan.FromTicks(positionTicks)).ConfigureAwait(false); await _device.Seek(TimeSpan.FromTicks(positionTicks), cancellationToken).ConfigureAwait(false);
} }
private class StreamParams private class StreamParams
{ {
public string ItemId { get; set; } public Guid ItemId { get; set; }
public bool IsDirectStream { get; set; } public bool IsDirectStream { get; set; }
@ -847,10 +812,36 @@ namespace Emby.Dlna.PlayTo
public string LiveStreamId { get; set; } public string LiveStreamId { get; set; }
public BaseItem Item { get; set; } public BaseItem Item { get; set; }
public MediaSourceInfo MediaSource { get; set; } private MediaSourceInfo MediaSource;
private static string GetItemId(string url) private IMediaSourceManager _mediaSourceManager;
public async Task<MediaSourceInfo> GetMediaSource(CancellationToken cancellationToken)
{ {
if (MediaSource != null)
{
return MediaSource;
}
var hasMediaSources = Item as IHasMediaSources;
if (hasMediaSources == null)
{
return null;
}
MediaSource = await _mediaSourceManager.GetMediaSource(Item, MediaSourceId, LiveStreamId, false, cancellationToken).ConfigureAwait(false);
return MediaSource;
}
private static Guid GetItemId(string url)
{
if (string.IsNullOrEmpty(url))
{
throw new ArgumentNullException("url");
}
var parts = url.Split('/'); var parts = url.Split('/');
for (var i = 0; i < parts.Length; i++) for (var i = 0; i < parts.Length; i++)
@ -862,96 +853,108 @@ namespace Emby.Dlna.PlayTo
{ {
if (parts.Length > i + 1) if (parts.Length > i + 1)
{ {
return parts[i + 1]; return Guid.Parse(parts[i + 1]);
} }
} }
} }
return null; return Guid.Empty;
} }
public static async Task<StreamParams> ParseFromUrl(string url, ILibraryManager libraryManager, IMediaSourceManager mediaSourceManager) public static StreamParams ParseFromUrl(string url, ILibraryManager libraryManager, IMediaSourceManager mediaSourceManager)
{ {
if (string.IsNullOrEmpty(url))
{
throw new ArgumentNullException("url");
}
var request = new StreamParams var request = new StreamParams
{ {
ItemId = GetItemId(url) ItemId = GetItemId(url)
}; };
Guid parsedId; if (request.ItemId.Equals(Guid.Empty))
if (string.IsNullOrWhiteSpace(request.ItemId) || !Guid.TryParse(request.ItemId, out parsedId))
{ {
return request; return request;
} }
const string srch = "params="; var index = url.IndexOf('?');
var index = url.IndexOf(srch, StringComparison.OrdinalIgnoreCase);
if (index == -1) return request; if (index == -1) return request;
var vals = url.Substring(index + srch.Length).Split(';'); var query = url.Substring(index + 1);
QueryParamCollection values = MyHttpUtility.ParseQueryString(query);
for (var i = 0; i < vals.Length; i++) request.DeviceProfileId = values.Get("DeviceProfileId");
{ request.DeviceId = values.Get("DeviceId");
var val = vals[i]; request.MediaSourceId = values.Get("MediaSourceId");
request.LiveStreamId = values.Get("LiveStreamId");
request.IsDirectStream = string.Equals("true", values.Get("Static"), StringComparison.OrdinalIgnoreCase);
if (string.IsNullOrWhiteSpace(val)) request.AudioStreamIndex = GetIntValue(values, "AudioStreamIndex");
{ request.SubtitleStreamIndex = GetIntValue(values, "SubtitleStreamIndex");
continue; request.StartPositionTicks = GetLongValue(values, "StartPositionTicks");
}
if (i == 0) request.Item = libraryManager.GetItemById(request.ItemId);
{
request.DeviceProfileId = val;
}
else if (i == 1)
{
request.DeviceId = val;
}
else if (i == 2)
{
request.MediaSourceId = val;
}
else if (i == 3)
{
request.IsDirectStream = string.Equals("true", val, StringComparison.OrdinalIgnoreCase);
}
else if (i == 6)
{
request.AudioStreamIndex = int.Parse(val, CultureInfo.InvariantCulture);
}
else if (i == 7)
{
request.SubtitleStreamIndex = int.Parse(val, CultureInfo.InvariantCulture);
}
else if (i == 14)
{
request.StartPositionTicks = long.Parse(val, CultureInfo.InvariantCulture);
}
else if (i == 22)
{
request.LiveStreamId = val;
}
}
request.Item = string.IsNullOrWhiteSpace(request.ItemId) request._mediaSourceManager = mediaSourceManager;
? null
: libraryManager.GetItemById(parsedId);
var hasMediaSources = request.Item as IHasMediaSources;
request.MediaSource = hasMediaSources == null
? null
: (await mediaSourceManager.GetMediaSource(hasMediaSources, request.MediaSourceId, request.LiveStreamId, false, CancellationToken.None).ConfigureAwait(false));
return request; return request;
} }
} }
public Task SendMessage<T>(string name, T data, CancellationToken cancellationToken) private static int? GetIntValue(QueryParamCollection values, string name)
{ {
var value = values.Get(name);
int result;
if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out result))
{
return result;
}
return null;
}
private static long GetLongValue(QueryParamCollection values, string name)
{
var value = values.Get(name);
long result;
if (long.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out result))
{
return result;
}
return 0;
}
public Task SendMessage<T>(string name, string messageId, T data, ISessionController[] allControllers, CancellationToken cancellationToken)
{
if (_disposed)
{
throw new ObjectDisposedException(GetType().Name);
}
if (_device == null)
{
return Task.CompletedTask;
}
if (string.Equals(name, "Play", StringComparison.OrdinalIgnoreCase))
{
return SendPlayCommand(data as PlayRequest, cancellationToken);
}
if (string.Equals(name, "PlayState", StringComparison.OrdinalIgnoreCase))
{
return SendPlaystateCommand(data as PlaystateRequest, cancellationToken);
}
if (string.Equals(name, "GeneralCommand", StringComparison.OrdinalIgnoreCase))
{
return SendGeneralCommand(data as GeneralCommand, cancellationToken);
}
// Not supported or needed right now // Not supported or needed right now
return Task.FromResult(true); return Task.CompletedTask;
} }
} }
} }

View File

@ -19,6 +19,8 @@ using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Net; using MediaBrowser.Model.Net;
using MediaBrowser.Model.Threading; using MediaBrowser.Model.Threading;
using System.Threading; using System.Threading;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Devices;
namespace Emby.Dlna.PlayTo namespace Emby.Dlna.PlayTo
{ {
@ -42,8 +44,6 @@ namespace Emby.Dlna.PlayTo
private readonly IMediaEncoder _mediaEncoder; private readonly IMediaEncoder _mediaEncoder;
private readonly ITimerFactory _timerFactory; private readonly ITimerFactory _timerFactory;
private readonly List<string> _nonRendererUrls = new List<string>();
private DateTime _lastRendererClear;
private bool _disposed; private bool _disposed;
private SemaphoreSlim _sessionLock = new SemaphoreSlim(1, 1); private SemaphoreSlim _sessionLock = new SemaphoreSlim(1, 1);
private CancellationTokenSource _disposeCancellationTokenSource = new CancellationTokenSource(); private CancellationTokenSource _disposeCancellationTokenSource = new CancellationTokenSource();
@ -122,8 +122,6 @@ namespace Emby.Dlna.PlayTo
catch (Exception ex) catch (Exception ex)
{ {
_logger.ErrorException("Error creating PlayTo device.", ex); _logger.ErrorException("Error creating PlayTo device.", ex);
_nonRendererUrls.Add(location);
} }
finally finally
{ {
@ -131,64 +129,88 @@ namespace Emby.Dlna.PlayTo
} }
} }
private string GetUuid(string usn)
{
var found = false;
var index = usn.IndexOf("uuid:", StringComparison.OrdinalIgnoreCase);
if (index != -1)
{
usn = usn.Substring(index);
found = true;
}
index = usn.IndexOf("::", StringComparison.OrdinalIgnoreCase);
if (index != -1)
{
usn = usn.Substring(0, index);
}
if (found)
{
return usn;
}
return usn.GetMD5().ToString("N");
}
private async Task AddDevice(UpnpDeviceInfo info, string location, CancellationToken cancellationToken) private async Task AddDevice(UpnpDeviceInfo info, string location, CancellationToken cancellationToken)
{ {
if ((DateTime.UtcNow - _lastRendererClear).TotalMinutes >= 10)
{
_nonRendererUrls.Clear();
_lastRendererClear = DateTime.UtcNow;
}
if (_nonRendererUrls.Contains(location, StringComparer.OrdinalIgnoreCase))
{
return;
}
var uri = info.Location; var uri = info.Location;
_logger.Debug("Attempting to create PlayToController from location {0}", location); _logger.Debug("Attempting to create PlayToController from location {0}", location);
var device = await Device.CreateuPnpDeviceAsync(uri, _httpClient, _config, _logger, _timerFactory, cancellationToken).ConfigureAwait(false);
if (device.RendererCommands == null)
{
//_logger.Debug("Upnp device {0} does not contain a MediaRenderer device (1).", location);
_nonRendererUrls.Add(location);
return;
}
_logger.Debug("Logging session activity from location {0}", location); _logger.Debug("Logging session activity from location {0}", location);
var sessionInfo = await _sessionManager.LogSessionActivity(device.Properties.ClientType, _appHost.ApplicationVersion.ToString(), device.Properties.UUID, device.Properties.Name, uri.OriginalString, null).ConfigureAwait(false); string uuid;
if (info.Headers.TryGetValue("USN", out uuid))
{
uuid = GetUuid(uuid);
}
else
{
uuid = location.GetMD5().ToString("N");
}
var controller = sessionInfo.SessionController as PlayToController; string deviceName = null;
var sessionInfo = _sessionManager.LogSessionActivity("DLNA", _appHost.ApplicationVersion.ToString(), uuid, deviceName, uri.OriginalString, null);
var controller = sessionInfo.SessionControllers.OfType<PlayToController>().FirstOrDefault();
if (controller == null) if (controller == null)
{ {
var device = await Device.CreateuPnpDeviceAsync(uri, _httpClient, _config, _logger, _timerFactory, cancellationToken).ConfigureAwait(false);
deviceName = device.Properties.Name;
_sessionManager.UpdateDeviceName(sessionInfo.Id, deviceName);
string serverAddress; string serverAddress;
if (info.LocalIpAddress == null || info.LocalIpAddress.Equals(IpAddressInfo.Any) || info.LocalIpAddress.Equals(IpAddressInfo.IPv6Loopback)) if (info.LocalIpAddress == null || info.LocalIpAddress.Equals(IpAddressInfo.Any) || info.LocalIpAddress.Equals(IpAddressInfo.IPv6Any))
{ {
serverAddress = await GetServerAddress(null, cancellationToken).ConfigureAwait(false); serverAddress = await _appHost.GetLocalApiUrl(cancellationToken).ConfigureAwait(false);
} }
else else
{ {
serverAddress = await GetServerAddress(info.LocalIpAddress, cancellationToken).ConfigureAwait(false); serverAddress = _appHost.GetLocalApiUrl(info.LocalIpAddress);
} }
string accessToken = null; string accessToken = null;
sessionInfo.SessionController = controller = new PlayToController(sessionInfo, controller = new PlayToController(sessionInfo,
_sessionManager, _sessionManager,
_libraryManager, _libraryManager,
_logger, _logger,
_dlnaManager, _dlnaManager,
_userManager, _userManager,
_imageProcessor, _imageProcessor,
serverAddress, serverAddress,
accessToken, accessToken,
_deviceDiscovery, _deviceDiscovery,
_userDataManager, _userDataManager,
_localization, _localization,
_mediaSourceManager, _mediaSourceManager,
_config, _config,
_mediaEncoder); _mediaEncoder);
sessionInfo.AddController(controller);
controller.Init(device); controller.Init(device);
@ -208,31 +230,21 @@ namespace Emby.Dlna.PlayTo
GeneralCommandType.ToggleMute.ToString(), GeneralCommandType.ToggleMute.ToString(),
GeneralCommandType.SetVolume.ToString(), GeneralCommandType.SetVolume.ToString(),
GeneralCommandType.SetAudioStreamIndex.ToString(), GeneralCommandType.SetAudioStreamIndex.ToString(),
GeneralCommandType.SetSubtitleStreamIndex.ToString() GeneralCommandType.SetSubtitleStreamIndex.ToString(),
GeneralCommandType.PlayMediaSource.ToString()
}, },
SupportsMediaControl = true, SupportsMediaControl = true
// xbox one creates a new uuid everytime it restarts
SupportsPersistentIdentifier = (device.Properties.ModelName ?? string.Empty).IndexOf("xbox", StringComparison.OrdinalIgnoreCase) == -1
}); });
_logger.Info("DLNA Session created for {0} - {1}", device.Properties.Name, device.Properties.ModelName); _logger.Info("DLNA Session created for {0} - {1}", device.Properties.Name, device.Properties.ModelName);
} }
} }
private Task<string> GetServerAddress(IpAddressInfo address, CancellationToken cancellationToken)
{
if (address == null)
{
return _appHost.GetLocalApiUrl(cancellationToken);
}
return Task.FromResult(_appHost.GetLocalApiUrl(address));
}
public void Dispose() public void Dispose()
{ {
_deviceDiscovery.DeviceDiscovered -= _deviceDiscovery_DeviceDiscovered;
try try
{ {
_disposeCancellationTokenSource.Cancel(); _disposeCancellationTokenSource.Cancel();
@ -243,8 +255,6 @@ namespace Emby.Dlna.PlayTo
} }
_disposed = true; _disposed = true;
_deviceDiscovery.DeviceDiscovered -= _deviceDiscovery_DeviceDiscovered;
GC.SuppressFinalize(this);
} }
} }
} }

View File

@ -18,7 +18,7 @@ namespace Emby.Dlna.PlayTo
{ {
StreamInfo = new StreamInfo StreamInfo = new StreamInfo
{ {
ItemId = item.Id.ToString("N"), ItemId = item.Id,
MediaType = DlnaProfileType.Photo, MediaType = DlnaProfileType.Photo,
DeviceProfile = profile DeviceProfile = profile
}, },

View File

@ -32,7 +32,9 @@ namespace Emby.Dlna.PlayTo
bool logRequest = true, bool logRequest = true,
string header = null) string header = null)
{ {
using (var response = await PostSoapDataAsync(NormalizeServiceUrl(baseUrl, service.ControlUrl), "\"" + service.ServiceType + "#" + command + "\"", postData, header, logRequest) var cancellationToken = CancellationToken.None;
using (var response = await PostSoapDataAsync(NormalizeServiceUrl(baseUrl, service.ControlUrl), "\"" + service.ServiceType + "#" + command + "\"", postData, header, logRequest, cancellationToken)
.ConfigureAwait(false)) .ConfigureAwait(false))
{ {
using (var stream = response.Content) using (var stream = response.Content)
@ -123,7 +125,8 @@ namespace Emby.Dlna.PlayTo
string soapAction, string soapAction,
string postData, string postData,
string header, string header,
bool logRequest) bool logRequest,
CancellationToken cancellationToken)
{ {
if (!soapAction.StartsWith("\"")) if (!soapAction.StartsWith("\""))
soapAction = "\"" + soapAction + "\""; soapAction = "\"" + soapAction + "\"";
@ -137,14 +140,16 @@ namespace Emby.Dlna.PlayTo
BufferContent = false, BufferContent = false,
// The periodic requests may keep some devices awake // The periodic requests may keep some devices awake
LogRequestAsDebug = true LogRequestAsDebug = true,
CancellationToken = cancellationToken
}; };
options.RequestHeaders["SOAPAction"] = soapAction; options.RequestHeaders["SOAPAction"] = soapAction;
options.RequestHeaders["Pragma"] = "no-cache"; options.RequestHeaders["Pragma"] = "no-cache";
options.RequestHeaders["FriendlyName.DLNA.ORG"] = FriendlyName; options.RequestHeaders["FriendlyName.DLNA.ORG"] = FriendlyName;
if (!string.IsNullOrWhiteSpace(header)) if (!string.IsNullOrEmpty(header))
{ {
options.RequestHeaders["contentFeatures.dlna.org"] = header; options.RequestHeaders["contentFeatures.dlna.org"] = header;
} }

View File

@ -91,7 +91,7 @@ namespace Emby.Dlna.PlayTo
}; };
} }
public static StateVariable FromXml(XElement container) private static StateVariable FromXml(XElement container)
{ {
var allowedValues = new List<string>(); var allowedValues = new List<string>();
var element = container.Descendants(uPnpNamespaces.svc + "allowedValueList") var element = container.Descendants(uPnpNamespaces.svc + "allowedValueList")
@ -108,7 +108,7 @@ namespace Emby.Dlna.PlayTo
{ {
Name = container.GetValue(uPnpNamespaces.svc + "name"), Name = container.GetValue(uPnpNamespaces.svc + "name"),
DataType = container.GetValue(uPnpNamespaces.svc + "dataType"), DataType = container.GetValue(uPnpNamespaces.svc + "dataType"),
AllowedValues = allowedValues AllowedValues = allowedValues.ToArray()
}; };
} }

File diff suppressed because one or more lines are too long

View File

@ -3,7 +3,7 @@ using System.Xml.Serialization;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles
{ {
[XmlRoot("Profile")] [System.Xml.Serialization.XmlRoot("Profile")]
public class DenonAvrProfile : DefaultProfile public class DenonAvrProfile : DefaultProfile
{ {
public DenonAvrProfile() public DenonAvrProfile()

View File

@ -3,7 +3,7 @@ using System.Xml.Serialization;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles
{ {
[XmlRoot("Profile")] [System.Xml.Serialization.XmlRoot("Profile")]
public class DirectTvProfile : DefaultProfile public class DirectTvProfile : DefaultProfile
{ {
public DirectTvProfile() public DirectTvProfile()

View File

@ -3,7 +3,7 @@ using System.Xml.Serialization;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles
{ {
[XmlRoot("Profile")] [System.Xml.Serialization.XmlRoot("Profile")]
public class DishHopperJoeyProfile : DefaultProfile public class DishHopperJoeyProfile : DefaultProfile
{ {
public DishHopperJoeyProfile() public DishHopperJoeyProfile()

View File

@ -3,7 +3,7 @@ using System.Xml.Serialization;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles
{ {
[XmlRoot("Profile")] [System.Xml.Serialization.XmlRoot("Profile")]
public class Foobar2000Profile : DefaultProfile public class Foobar2000Profile : DefaultProfile
{ {
public Foobar2000Profile() public Foobar2000Profile()

View File

@ -3,7 +3,7 @@ using System.Xml.Serialization;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles
{ {
[XmlRoot("Profile")] [System.Xml.Serialization.XmlRoot("Profile")]
public class LgTvProfile : DefaultProfile public class LgTvProfile : DefaultProfile
{ {
public LgTvProfile() public LgTvProfile()
@ -53,7 +53,7 @@ namespace Emby.Dlna.Profiles
{ {
new DirectPlayProfile new DirectPlayProfile
{ {
Container = "ts,mpegts,avi,mkv", Container = "ts,mpegts,avi,mkv,m2ts",
VideoCodec = "h264", VideoCodec = "h264",
AudioCodec = "aac,ac3,eac3,mp3,dca,dts", AudioCodec = "aac,ac3,eac3,mp3,dca,dts",
Type = DlnaProfileType.Video Type = DlnaProfileType.Video
@ -151,12 +151,6 @@ namespace Emby.Dlna.Profiles
Value = "1080" Value = "1080"
}, },
new ProfileCondition new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.VideoFramerate,
Value = "30"
},
new ProfileCondition
{ {
Condition = ProfileConditionType.LessThanEqual, Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.VideoLevel, Property = ProfileConditionValue.VideoLevel,
@ -203,6 +197,12 @@ namespace Emby.Dlna.Profiles
Container = "m4v", Container = "m4v",
Type = DlnaProfileType.Video, Type = DlnaProfileType.Video,
MimeType = "video/mp4" MimeType = "video/mp4"
},
new ResponseProfile
{
Container = "ts,mpegts",
Type = DlnaProfileType.Video,
MimeType = "video/mpeg"
} }
}; };
} }

View File

@ -3,7 +3,7 @@ using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles
{ {
[XmlRoot("Profile")] [System.Xml.Serialization.XmlRoot("Profile")]
public class LinksysDMA2100Profile : DefaultProfile public class LinksysDMA2100Profile : DefaultProfile
{ {
public LinksysDMA2100Profile() public LinksysDMA2100Profile()

View File

@ -3,7 +3,7 @@ using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles
{ {
[XmlRoot("Profile")] [System.Xml.Serialization.XmlRoot("Profile")]
public class MarantzProfile : DefaultProfile public class MarantzProfile : DefaultProfile
{ {
public MarantzProfile() public MarantzProfile()

View File

@ -3,7 +3,7 @@ using System.Xml.Serialization;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles
{ {
[XmlRoot("Profile")] [System.Xml.Serialization.XmlRoot("Profile")]
public class MediaMonkeyProfile : DefaultProfile public class MediaMonkeyProfile : DefaultProfile
{ {
public MediaMonkeyProfile() public MediaMonkeyProfile()

View File

@ -3,7 +3,7 @@ using System.Xml.Serialization;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles
{ {
[XmlRoot("Profile")] [System.Xml.Serialization.XmlRoot("Profile")]
public class PanasonicVieraProfile : DefaultProfile public class PanasonicVieraProfile : DefaultProfile
{ {
public PanasonicVieraProfile() public PanasonicVieraProfile()

View File

@ -3,7 +3,7 @@ using System.Xml.Serialization;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles
{ {
[XmlRoot("Profile")] [System.Xml.Serialization.XmlRoot("Profile")]
public class PopcornHourProfile : DefaultProfile public class PopcornHourProfile : DefaultProfile
{ {
public PopcornHourProfile() public PopcornHourProfile()

View File

@ -3,7 +3,7 @@ using System.Xml.Serialization;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles
{ {
[XmlRoot("Profile")] [System.Xml.Serialization.XmlRoot("Profile")]
public class SamsungSmartTvProfile : DefaultProfile public class SamsungSmartTvProfile : DefaultProfile
{ {
public SamsungSmartTvProfile() public SamsungSmartTvProfile()
@ -42,7 +42,7 @@ namespace Emby.Dlna.Profiles
}, },
new TranscodingProfile new TranscodingProfile
{ {
Container = "ts,mpegts", Container = "ts",
AudioCodec = "ac3", AudioCodec = "ac3",
VideoCodec = "h264", VideoCodec = "h264",
Type = DlnaProfileType.Video, Type = DlnaProfileType.Video,

View File

@ -3,7 +3,7 @@ using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles
{ {
[XmlRoot("Profile")] [System.Xml.Serialization.XmlRoot("Profile")]
public class SharpSmartTvProfile : DefaultProfile public class SharpSmartTvProfile : DefaultProfile
{ {
public SharpSmartTvProfile() public SharpSmartTvProfile()

View File

@ -3,7 +3,7 @@ using System.Xml.Serialization;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles
{ {
[XmlRoot("Profile")] [System.Xml.Serialization.XmlRoot("Profile")]
public class SonyBlurayPlayer2013 : DefaultProfile public class SonyBlurayPlayer2013 : DefaultProfile
{ {
public SonyBlurayPlayer2013() public SonyBlurayPlayer2013()

View File

@ -3,7 +3,7 @@ using System.Xml.Serialization;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles
{ {
[XmlRoot("Profile")] [System.Xml.Serialization.XmlRoot("Profile")]
public class SonyBlurayPlayer2014 : DefaultProfile public class SonyBlurayPlayer2014 : DefaultProfile
{ {
public SonyBlurayPlayer2014() public SonyBlurayPlayer2014()

View File

@ -3,7 +3,7 @@ using System.Xml.Serialization;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles
{ {
[XmlRoot("Profile")] [System.Xml.Serialization.XmlRoot("Profile")]
public class SonyBlurayPlayer2015 : DefaultProfile public class SonyBlurayPlayer2015 : DefaultProfile
{ {
public SonyBlurayPlayer2015() public SonyBlurayPlayer2015()

View File

@ -3,7 +3,7 @@ using System.Xml.Serialization;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles
{ {
[XmlRoot("Profile")] [System.Xml.Serialization.XmlRoot("Profile")]
public class SonyBlurayPlayer2016 : DefaultProfile public class SonyBlurayPlayer2016 : DefaultProfile
{ {
public SonyBlurayPlayer2016() public SonyBlurayPlayer2016()

View File

@ -3,7 +3,7 @@ using System.Xml.Serialization;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles
{ {
[XmlRoot("Profile")] [System.Xml.Serialization.XmlRoot("Profile")]
public class SonyBlurayPlayerProfile : DefaultProfile public class SonyBlurayPlayerProfile : DefaultProfile
{ {
public SonyBlurayPlayerProfile() public SonyBlurayPlayerProfile()

View File

@ -3,7 +3,7 @@ using System.Xml.Serialization;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles
{ {
[XmlRoot("Profile")] [System.Xml.Serialization.XmlRoot("Profile")]
public class SonyBravia2010Profile : DefaultProfile public class SonyBravia2010Profile : DefaultProfile
{ {
public SonyBravia2010Profile() public SonyBravia2010Profile()

View File

@ -3,7 +3,7 @@ using System.Xml.Serialization;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles
{ {
[XmlRoot("Profile")] [System.Xml.Serialization.XmlRoot("Profile")]
public class SonyBravia2011Profile : DefaultProfile public class SonyBravia2011Profile : DefaultProfile
{ {
public SonyBravia2011Profile() public SonyBravia2011Profile()

View File

@ -3,7 +3,7 @@ using System.Xml.Serialization;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles
{ {
[XmlRoot("Profile")] [System.Xml.Serialization.XmlRoot("Profile")]
public class SonyBravia2012Profile : DefaultProfile public class SonyBravia2012Profile : DefaultProfile
{ {
public SonyBravia2012Profile() public SonyBravia2012Profile()

View File

@ -3,7 +3,7 @@ using System.Xml.Serialization;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles
{ {
[XmlRoot("Profile")] [System.Xml.Serialization.XmlRoot("Profile")]
public class SonyBravia2013Profile : DefaultProfile public class SonyBravia2013Profile : DefaultProfile
{ {
public SonyBravia2013Profile() public SonyBravia2013Profile()

View File

@ -3,7 +3,7 @@ using System.Xml.Serialization;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles
{ {
[XmlRoot("Profile")] [System.Xml.Serialization.XmlRoot("Profile")]
public class SonyBravia2014Profile : DefaultProfile public class SonyBravia2014Profile : DefaultProfile
{ {
public SonyBravia2014Profile() public SonyBravia2014Profile()

View File

@ -3,7 +3,7 @@ using System.Xml.Serialization;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles
{ {
[XmlRoot("Profile")] [System.Xml.Serialization.XmlRoot("Profile")]
public class SonyPs3Profile : DefaultProfile public class SonyPs3Profile : DefaultProfile
{ {
public SonyPs3Profile() public SonyPs3Profile()
@ -35,7 +35,6 @@ namespace Emby.Dlna.Profiles
AlbumArtPn = "JPEG_TN"; AlbumArtPn = "JPEG_TN";
SonyAggregationFlags = "10"; SonyAggregationFlags = "10";
XDlnaDoc = "DMS-1.50";
EnableSingleAlbumArtLimit = true; EnableSingleAlbumArtLimit = true;
DirectPlayProfiles = new[] DirectPlayProfiles = new[]

View File

@ -3,7 +3,7 @@ using System.Xml.Serialization;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles
{ {
[XmlRoot("Profile")] [System.Xml.Serialization.XmlRoot("Profile")]
public class SonyPs4Profile : DefaultProfile public class SonyPs4Profile : DefaultProfile
{ {
public SonyPs4Profile() public SonyPs4Profile()
@ -35,7 +35,6 @@ namespace Emby.Dlna.Profiles
AlbumArtPn = "JPEG_TN"; AlbumArtPn = "JPEG_TN";
SonyAggregationFlags = "10"; SonyAggregationFlags = "10";
XDlnaDoc = "DMS-1.50";
EnableSingleAlbumArtLimit = true; EnableSingleAlbumArtLimit = true;
DirectPlayProfiles = new[] DirectPlayProfiles = new[]

View File

@ -3,7 +3,7 @@ using System.Xml.Serialization;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles
{ {
[XmlRoot("Profile")] [System.Xml.Serialization.XmlRoot("Profile")]
public class WdtvLiveProfile : DefaultProfile public class WdtvLiveProfile : DefaultProfile
{ {
public WdtvLiveProfile() public WdtvLiveProfile()

View File

@ -3,7 +3,7 @@ using System.Xml.Serialization;
namespace Emby.Dlna.Profiles namespace Emby.Dlna.Profiles
{ {
[XmlRoot("Profile")] [System.Xml.Serialization.XmlRoot("Profile")]
public class XboxOneProfile : DefaultProfile public class XboxOneProfile : DefaultProfile
{ {
public XboxOneProfile() public XboxOneProfile()

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -9,11 +9,11 @@
</Headers> </Headers>
</Identification> </Identification>
<Manufacturer>Emby</Manufacturer> <Manufacturer>Emby</Manufacturer>
<ManufacturerUrl>http://emby.media/</ManufacturerUrl> <ManufacturerUrl>https://emby.media</ManufacturerUrl>
<ModelName>Emby Server</ModelName> <ModelName>Emby Server</ModelName>
<ModelDescription>Emby</ModelDescription> <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
<ModelNumber>Emby</ModelNumber> <ModelNumber>01</ModelNumber>
<ModelUrl>http://emby.media/</ModelUrl> <ModelUrl>https://emby.media</ModelUrl>
<EnableAlbumArtInDidl>false</EnableAlbumArtInDidl> <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
<EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit> <EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit>
<EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit> <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit>
@ -23,11 +23,10 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight> <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth> <MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight> <MaxIconHeight>48</MaxIconHeight>
<MaxStreamingBitrate>40000000</MaxStreamingBitrate> <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
<MaxStaticBitrate>40000000</MaxStaticBitrate> <MaxStaticBitrate>140000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate> <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" /> <MaxStaticMusicBitrate xsi:nil="true" />
<XDlnaDoc>DMS-1.50</XDlnaDoc>
<ProtocolInfo>http-get:*:video/mp2t:*,http-get:*:video/MP1S:*,http-get:*:video/mpeg2:*,http-get:*:video/mp4:*,http-get:*:video/x-matroska:*,http-get:*:audio/mpeg:*,http-get:*:audio/mpeg3:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/mp4a-latm:*,http-get:*:image/jpeg:*</ProtocolInfo> <ProtocolInfo>http-get:*:video/mp2t:*,http-get:*:video/MP1S:*,http-get:*:video/mpeg2:*,http-get:*:video/mp4:*,http-get:*:video/x-matroska:*,http-get:*:audio/mpeg:*,http-get:*:audio/mpeg3:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/mp4a-latm:*,http-get:*:image/jpeg:*</ProtocolInfo>
<TimelineOffsetSeconds>0</TimelineOffsetSeconds> <TimelineOffsetSeconds>0</TimelineOffsetSeconds>
<RequiresPlainVideoItems>false</RequiresPlainVideoItems> <RequiresPlainVideoItems>false</RequiresPlainVideoItems>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -11,7 +11,7 @@
<Manufacturer>Microsoft Corporation</Manufacturer> <Manufacturer>Microsoft Corporation</Manufacturer>
<ManufacturerUrl>http://www.microsoft.com/</ManufacturerUrl> <ManufacturerUrl>http://www.microsoft.com/</ManufacturerUrl>
<ModelName>Windows Media Player Sharing</ModelName> <ModelName>Windows Media Player Sharing</ModelName>
<ModelDescription>Emby</ModelDescription> <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
<ModelNumber>3.0</ModelNumber> <ModelNumber>3.0</ModelNumber>
<ModelUrl>http://www.microsoft.com/</ModelUrl> <ModelUrl>http://www.microsoft.com/</ModelUrl>
<EnableAlbumArtInDidl>true</EnableAlbumArtInDidl> <EnableAlbumArtInDidl>true</EnableAlbumArtInDidl>
@ -23,11 +23,10 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight> <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth> <MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight> <MaxIconHeight>48</MaxIconHeight>
<MaxStreamingBitrate>40000000</MaxStreamingBitrate> <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
<MaxStaticBitrate>40000000</MaxStaticBitrate> <MaxStaticBitrate>140000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate> <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" /> <MaxStaticMusicBitrate xsi:nil="true" />
<XDlnaDoc>DMS-1.50</XDlnaDoc>
<SonyAggregationFlags>10</SonyAggregationFlags> <SonyAggregationFlags>10</SonyAggregationFlags>
<ProtocolInfo>http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000</ProtocolInfo> <ProtocolInfo>http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000</ProtocolInfo>
<TimelineOffsetSeconds>0</TimelineOffsetSeconds> <TimelineOffsetSeconds>0</TimelineOffsetSeconds>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -22,12 +22,12 @@ namespace Emby.Dlna.Server
public DescriptionXmlBuilder(DeviceProfile profile, string serverUdn, string serverAddress, string serverName, string serverId) public DescriptionXmlBuilder(DeviceProfile profile, string serverUdn, string serverAddress, string serverName, string serverId)
{ {
if (string.IsNullOrWhiteSpace(serverUdn)) if (string.IsNullOrEmpty(serverUdn))
{ {
throw new ArgumentNullException("serverUdn"); throw new ArgumentNullException("serverUdn");
} }
if (string.IsNullOrWhiteSpace(serverAddress)) if (string.IsNullOrEmpty(serverAddress))
{ {
throw new ArgumentNullException("serverAddress"); throw new ArgumentNullException("serverAddress");
} }
@ -77,6 +77,11 @@ namespace Emby.Dlna.Server
builder.Append("<minor>0</minor>"); builder.Append("<minor>0</minor>");
builder.Append("</specVersion>"); builder.Append("</specVersion>");
if (!EnableAbsoluteUrls)
{
builder.Append("<URLBase>" + Escape(_serverAddress) + "</URLBase>");
}
AppendDeviceInfo(builder); AppendDeviceInfo(builder);
builder.Append("</root>"); builder.Append("</root>");
@ -90,6 +95,9 @@ namespace Emby.Dlna.Server
AppendDeviceProperties(builder); AppendDeviceProperties(builder);
AppendIconList(builder); AppendIconList(builder);
builder.Append("<presentationURL>" + Escape(_serverAddress) + "/web/index.html</presentationURL>");
AppendServiceList(builder); AppendServiceList(builder);
builder.Append("</device>"); builder.Append("</device>");
} }
@ -169,12 +177,12 @@ namespace Emby.Dlna.Server
private void AppendDeviceProperties(StringBuilder builder) private void AppendDeviceProperties(StringBuilder builder)
{ {
builder.Append("<deviceType>urn:schemas-upnp-org:device:MediaServer:1</deviceType>"); builder.Append("<dlna:X_DLNACAP/>");
builder.Append("<dlna:X_DLNACAP>" + Escape(_profile.XDlnaCap ?? string.Empty) + "</dlna:X_DLNACAP>");
builder.Append("<dlna:X_DLNADOC xmlns:dlna=\"urn:schemas-dlna-org:device-1-0\">DMS-1.50</dlna:X_DLNADOC>");
builder.Append("<dlna:X_DLNADOC xmlns:dlna=\"urn:schemas-dlna-org:device-1-0\">M-DMS-1.50</dlna:X_DLNADOC>"); builder.Append("<dlna:X_DLNADOC xmlns:dlna=\"urn:schemas-dlna-org:device-1-0\">M-DMS-1.50</dlna:X_DLNADOC>");
builder.Append("<dlna:X_DLNADOC xmlns:dlna=\"urn:schemas-dlna-org:device-1-0\">" + Escape(_profile.XDlnaDoc ?? string.Empty) + "</dlna:X_DLNADOC>");
builder.Append("<deviceType>urn:schemas-upnp-org:device:MediaServer:1</deviceType>");
builder.Append("<friendlyName>" + Escape(GetFriendlyName()) + "</friendlyName>"); builder.Append("<friendlyName>" + Escape(GetFriendlyName()) + "</friendlyName>");
builder.Append("<manufacturer>" + Escape(_profile.Manufacturer ?? string.Empty) + "</manufacturer>"); builder.Append("<manufacturer>" + Escape(_profile.Manufacturer ?? string.Empty) + "</manufacturer>");
@ -186,7 +194,7 @@ namespace Emby.Dlna.Server
builder.Append("<modelNumber>" + Escape(_profile.ModelNumber ?? string.Empty) + "</modelNumber>"); builder.Append("<modelNumber>" + Escape(_profile.ModelNumber ?? string.Empty) + "</modelNumber>");
builder.Append("<modelURL>" + Escape(_profile.ModelUrl ?? string.Empty) + "</modelURL>"); builder.Append("<modelURL>" + Escape(_profile.ModelUrl ?? string.Empty) + "</modelURL>");
if (string.IsNullOrWhiteSpace(_profile.SerialNumber)) if (string.IsNullOrEmpty(_profile.SerialNumber))
{ {
builder.Append("<serialNumber>" + Escape(_serverId) + "</serialNumber>"); builder.Append("<serialNumber>" + Escape(_serverId) + "</serialNumber>");
} }
@ -195,15 +203,11 @@ namespace Emby.Dlna.Server
builder.Append("<serialNumber>" + Escape(_profile.SerialNumber) + "</serialNumber>"); builder.Append("<serialNumber>" + Escape(_profile.SerialNumber) + "</serialNumber>");
} }
builder.Append("<UPC/>");
builder.Append("<UDN>uuid:" + Escape(_serverUdn) + "</UDN>"); builder.Append("<UDN>uuid:" + Escape(_serverUdn) + "</UDN>");
builder.Append("<presentationURL>" + Escape(_serverAddress) + "</presentationURL>");
if (!EnableAbsoluteUrls) if (!string.IsNullOrEmpty(_profile.SonyAggregationFlags))
{
//builder.Append("<URLBase>" + Escape(_serverAddress) + "</URLBase>");
}
if (!string.IsNullOrWhiteSpace(_profile.SonyAggregationFlags))
{ {
builder.Append("<av:aggregationFlags xmlns:av=\"urn:schemas-sony-com:av\">" + Escape(_profile.SonyAggregationFlags) + "</av:aggregationFlags>"); builder.Append("<av:aggregationFlags xmlns:av=\"urn:schemas-sony-com:av\">" + Escape(_profile.SonyAggregationFlags) + "</av:aggregationFlags>");
} }
@ -211,7 +215,7 @@ namespace Emby.Dlna.Server
private string GetFriendlyName() private string GetFriendlyName()
{ {
if (string.IsNullOrWhiteSpace(_profile.FriendlyName)) if (string.IsNullOrEmpty(_profile.FriendlyName))
{ {
return "Emby - " + _serverName; return "Emby - " + _serverName;
} }
@ -226,7 +230,7 @@ namespace Emby.Dlna.Server
} }
} }
var characters = characterList.ToArray(characterList.Count); var characters = characterList.ToArray();
var serverName = new string(characters); var serverName = new string(characters);
@ -277,7 +281,7 @@ namespace Emby.Dlna.Server
private string BuildUrl(string url) private string BuildUrl(string url)
{ {
if (string.IsNullOrWhiteSpace(url)) if (string.IsNullOrEmpty(url))
{ {
return string.Empty; return string.Empty;
} }

View File

@ -1,38 +0,0 @@
using System;
using System.Net;
using MediaBrowser.Model.Net;
namespace Emby.Dlna.Server
{
public sealed class UpnpDevice
{
public readonly Uri Descriptor;
public readonly string Type;
public readonly string USN;
public readonly string Uuid;
public readonly IpAddressInfo Address;
public UpnpDevice(string aUuid, string aType, Uri aDescriptor, IpAddressInfo address)
{
Uuid = aUuid;
Type = aType;
Descriptor = aDescriptor;
Address = address;
USN = CreateUSN(aUuid, aType);
}
private static string CreateUSN(string aUuid, string aType)
{
if (aType.StartsWith("uuid:", StringComparison.OrdinalIgnoreCase))
{
return aType;
}
else
{
return String.Format("uuid:{0}::{1}", aUuid, aType);
}
}
}
}

View File

@ -255,7 +255,7 @@ namespace Emby.Dlna.Service
} }
var originalHeaders = response.Headers; var originalHeaders = response.Headers;
var headers = string.Join(", ", originalHeaders.Select(i => string.Format("{0}={1}", i.Key, i.Value)).ToArray(originalHeaders.Count)); var headers = string.Join(", ", originalHeaders.Select(i => string.Format("{0}={1}", i.Key, i.Value)).ToArray());
//builder.Append(response.Xml); //builder.Append(response.Xml);
Logger.Debug("Control response. Headers: {0}", headers); Logger.Debug("Control response. Headers: {0}", headers);

View File

@ -72,7 +72,7 @@ namespace Emby.Dlna.Service
builder.Append("<name>" + DescriptionXmlBuilder.Escape(item.Name ?? string.Empty) + "</name>"); builder.Append("<name>" + DescriptionXmlBuilder.Escape(item.Name ?? string.Empty) + "</name>");
builder.Append("<dataType>" + DescriptionXmlBuilder.Escape(item.DataType ?? string.Empty) + "</dataType>"); builder.Append("<dataType>" + DescriptionXmlBuilder.Escape(item.DataType ?? string.Empty) + "</dataType>");
if (item.AllowedValues.Count > 0) if (item.AllowedValues.Length > 0)
{ {
builder.Append("<allowedValueList>"); builder.Append("<allowedValueList>");
foreach (var allowedValue in item.AllowedValues) foreach (var allowedValue in item.AllowedValues)

Some files were not shown because too many files have changed in this diff Show More