Update to 3.5.2 and .net core 2.1

pull/1154/head
stefan 6 years ago
parent c32d865638
commit 48facb797e

1
.gitignore vendored

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

@ -1,73 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<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>{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 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>
<Project Sdk="Microsoft.NET.Sdk">
<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" />
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
</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>
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
</Project>

@ -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>

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

@ -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);
}
}
}
}

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

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

@ -1,67 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<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>{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 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>
<Project Sdk="Microsoft.NET.Sdk">
<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" />
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
</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>
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
</Project>

@ -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>

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

@ -1,15 +1,16 @@
using MediaBrowser.Controller.Dlna;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Threading.Tasks;
using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;
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", "GET", Summary = "Gets dlna server info")]
@ -98,30 +99,55 @@ namespace MediaBrowser.Api.Dlna
[Route("/Dlna/icons/{Filename}", "GET", Summary = "Gets a server icon")]
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; }
[ApiMember(Name = "Filename", Description = "The icon filename", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Filename { get; set; }
}
public class DlnaServerService : BaseApiService
public class DlnaServerService : IService, IRequiresRequest
{
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 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;
_contentDirectory = contentDirectory;
_connectionManager = connectionManager;
_mediaReceiverRegistrar = mediaReceiverRegistrar;
_memoryStreamProvider = memoryStreamProvider;
_resultFactory = httpResultFactory;
}
private string GetHeader(string name)
{
return Request.Headers[name];
}
public object Get(GetDescriptionXml request)
@ -130,49 +156,53 @@ namespace MediaBrowser.Api.Dlna
var serverAddress = url.Substring(0, url.IndexOf("/dlna/", StringComparison.OrdinalIgnoreCase));
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)
{
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)
{
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)
{
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)
{
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)
{
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)
{
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)
@ -188,49 +218,76 @@ namespace MediaBrowser.Api.Dlna
});
}
public object Get(GetIcon request)
protected string GetPathValue(int index)
{
using (var response = _dlnaManager.GetIcon(request.Filename))
var pathInfo = Parse(Request.PathInfo);
var first = pathInfo[0];
// backwards compatibility
if (string.Equals(first, "mediabrowser", StringComparison.OrdinalIgnoreCase) ||
string.Equals(first, "emby", StringComparison.OrdinalIgnoreCase))
{
using (var ms = _memoryStreamProvider.CreateNew())
{
response.Stream.CopyTo(ms);
index++;
}
ms.Position = 0;
var bytes = ms.ToArray();
return ResultFactory.GetResult(bytes, "image/" + response.Format.ToString().ToLower());
}
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)
{
var contentType = "image/" + Path.GetExtension(request.Filename).TrimStart('.').ToLower();
var cacheLength = TimeSpan.FromDays(365);
var cacheKey = Request.RawUrl.GetMD5();
return _resultFactory.GetStaticResult(Request, cacheKey, null, cacheLength, contentType, () => Task.FromResult<Stream>(_dlnaManager.GetIcon(request.Filename).Stream));
}
public object Subscribe(ProcessContentDirectoryEventRequest request)
{
return ProcessEventRequest(_contentDirectory);
return ProcessEventRequest(ContentDirectory);
}
public object Subscribe(ProcessConnectionManagerEventRequest request)
{
return ProcessEventRequest(_connectionManager);
return ProcessEventRequest(ConnectionManager);
}
public object Subscribe(ProcessMediaReceiverRegistrarEventRequest request)
{
return ProcessEventRequest(_mediaReceiverRegistrar);
return ProcessEventRequest(MediaReceiverRegistrar);
}
public object Unsubscribe(ProcessContentDirectoryEventRequest request)
{
return ProcessEventRequest(_contentDirectory);
return ProcessEventRequest(ContentDirectory);
}
public object Unsubscribe(ProcessConnectionManagerEventRequest request)
{
return ProcessEventRequest(_connectionManager);
return ProcessEventRequest(ConnectionManager);
}
public object Unsubscribe(ProcessMediaReceiverRegistrarEventRequest request)
{
return ProcessEventRequest(_mediaReceiverRegistrar);
return ProcessEventRequest(MediaReceiverRegistrar);
}
private object ProcessEventRequest(IEventManager eventManager)
@ -257,7 +314,7 @@ namespace MediaBrowser.Api.Dlna
private object GetSubscriptionResponse(EventSubscriptionResponse response)
{
return ResultFactory.GetResult(response.Content, response.ContentType, response.Headers);
return _resultFactory.GetResult(Request, response.Content, response.ContentType, response.Headers);
}
}
}

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

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

@ -1,5 +1,5 @@

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

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

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

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

@ -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; }
}
}

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

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

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

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

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

@ -21,7 +21,7 @@ using System.Text;
using System.Threading.Tasks;
using System.Xml;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Configuration;
using Emby.Dlna.Configuration;
using MediaBrowser.Model.Globalization;
namespace Emby.Dlna.Didl
@ -62,6 +62,11 @@ namespace Emby.Dlna.Didl
_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)
{
var settings = new XmlWriterSettings
@ -72,28 +77,29 @@ namespace Emby.Dlna.Didl
ConformanceLevel = ConformanceLevel.Fragment
};
StringWriter builder = new StringWriterWithEncoding(Encoding.UTF8);
using (XmlWriter writer = XmlWriter.Create(builder, settings))
using (StringWriter builder = new StringWriterWithEncoding(Encoding.UTF8))
{
//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", "dlna", null, NS_DLNA);
writer.WriteAttributeString("xmlns", "upnp", null, NS_UPNP);
//didl.SetAttribute("xmlns:sec", NS_SEC);
writer.WriteAttributeString("xmlns", "dc", null, NS_DC);
writer.WriteAttributeString("xmlns", "dlna", null, NS_DLNA);
writer.WriteAttributeString("xmlns", "upnp", null, NS_UPNP);
//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.WriteEndDocument();
}
writer.WriteFullEndElement();
//writer.WriteEndDocument();
}
return builder.ToString();
return builder.ToString();
}
}
public static void WriteXmlRootAttributes(DeviceProfile profile, XmlWriter writer)
@ -136,9 +142,9 @@ namespace Emby.Dlna.Didl
else
{
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))
{
AddAudioResource(options, writer, hasMediaSources, deviceId, filter, streamInfo);
AddAudioResource(options, writer, item, deviceId, filter, streamInfo);
}
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);
// 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))
{
mime = "video/mpeg";
@ -189,7 +196,7 @@ namespace Emby.Dlna.Didl
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)
{
@ -197,8 +204,8 @@ namespace Emby.Dlna.Didl
streamInfo = new StreamBuilder(_mediaEncoder, GetStreamBuilderLogger(options)).BuildVideoItem(new VideoOptions
{
ItemId = GetClientId(video),
MediaSources = sources.ToArray(sources.Count),
ItemId = video.Id,
MediaSources = sources.ToArray(),
Profile = _profile,
DeviceId = deviceId,
MaxBitrate = _profile.MaxStreamingBitrate
@ -217,10 +224,10 @@ namespace Emby.Dlna.Didl
streamInfo.TargetVideoBitrate,
streamInfo.TargetTimestamp,
streamInfo.IsDirectStream,
streamInfo.RunTimeTicks,
streamInfo.RunTimeTicks ?? 0,
streamInfo.TargetVideoProfile,
streamInfo.TargetVideoLevel,
streamInfo.TargetFramerate,
streamInfo.TargetFramerate ?? 0,
streamInfo.TargetPacketLength,
streamInfo.TranscodeSeekInfo,
streamInfo.IsTargetAnamorphic,
@ -296,11 +303,11 @@ namespace Emby.Dlna.Didl
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);
var url = streamInfo.ToDlnaUrl(_serverAddress, _accessToken);
var url = NormalizeDlnaMediaUrl(streamInfo.ToUrl(_serverAddress, _accessToken));
var mediaSource = streamInfo.MediaSource;
@ -361,7 +368,7 @@ namespace Emby.Dlna.Didl
streamInfo.TargetVideoBitDepth,
streamInfo.TargetVideoProfile,
streamInfo.TargetVideoLevel,
streamInfo.TargetFramerate,
streamInfo.TargetFramerate ?? 0,
streamInfo.TargetPacketLength,
streamInfo.TargetTimestamp,
streamInfo.IsTargetAnamorphic,
@ -494,7 +501,7 @@ namespace Emby.Dlna.Didl
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);
@ -504,14 +511,14 @@ namespace Emby.Dlna.Didl
streamInfo = new StreamBuilder(_mediaEncoder, GetStreamBuilderLogger(options)).BuildAudioItem(new AudioOptions
{
ItemId = GetClientId(audio),
ItemId = audio.Id,
MediaSources = sources.ToArray(sources.Count),
Profile = _profile,
DeviceId = deviceId
});
}
var url = streamInfo.ToDlnaUrl(_serverAddress, _accessToken);
var url = NormalizeDlnaMediaUrl(streamInfo.ToUrl(_serverAddress, _accessToken));
var mediaSource = streamInfo.MediaSource;
@ -573,7 +580,7 @@ namespace Emby.Dlna.Didl
targetChannels,
targetAudioBitDepth,
streamInfo.IsDirectStream,
streamInfo.RunTimeTicks,
streamInfo.RunTimeTicks ?? 0,
streamInfo.TranscodeSeekInfo);
writer.WriteAttributeString("protocolInfo", String.Format(
@ -606,7 +613,7 @@ namespace Emby.Dlna.Didl
{
writer.WriteStartElement(string.Empty, "container", NS_DIDL);
writer.WriteAttributeString("restricted", "0");
writer.WriteAttributeString("restricted", "1");
writer.WriteAttributeString("searchable", "1");
writer.WriteAttributeString("childCount", childCount.ToString(_usCulture));
@ -628,13 +635,13 @@ namespace Emby.Dlna.Didl
else
{
var parent = folder.DisplayParentId;
if (!parent.HasValue)
if (parent.Equals(Guid.Empty))
{
writer.WriteAttributeString("parentID", "0");
}
else
{
writer.WriteAttributeString("parentID", GetClientId(parent.Value, null));
writer.WriteAttributeString("parentID", GetClientId(parent, null));
}
}
}
@ -669,7 +676,7 @@ namespace Emby.Dlna.Didl
return;
}
var userdata = _userDataManager.GetUserData(user.Id, item);
var userdata = _userDataManager.GetUserData(user, item);
if (userdata.PlaybackPositionTicks > 0)
{
@ -713,21 +720,24 @@ namespace Emby.Dlna.Didl
AddValue(writer, "upnp", "publisher", studio, NS_UPNP);
}
if (filter.Contains("dc:description"))
if (!(item is Folder))
{
var desc = item.Overview;
if (!string.IsNullOrWhiteSpace(desc))
if (filter.Contains("dc:description"))
{
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);
var desc = item.Overview;
if (!string.IsNullOrWhiteSpace(desc))
{
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 (!string.IsNullOrEmpty(item.OfficialRating))
@ -823,37 +833,37 @@ namespace Emby.Dlna.Didl
private void AddPeople(BaseItem item, XmlWriter writer)
{
var types = new[]
{
PersonType.Director,
PersonType.Writer,
PersonType.Producer,
PersonType.Composer,
"Creator"
};
//var types = new[]
//{
// PersonType.Director,
// PersonType.Writer,
// PersonType.Producer,
// PersonType.Composer,
// "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
// The actual issue might just be due to processing a more metadata than it can handle
var limit = 6;
//// 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
//var limit = 6;
foreach (var actor in people)
{
var type = types.FirstOrDefault(i => string.Equals(i, actor.Type, StringComparison.OrdinalIgnoreCase) || string.Equals(i, actor.Role, StringComparison.OrdinalIgnoreCase))
?? PersonType.Actor;
//foreach (var actor in people)
//{
// var type = types.FirstOrDefault(i => string.Equals(i, actor.Type, StringComparison.OrdinalIgnoreCase) || string.Equals(i, actor.Role, StringComparison.OrdinalIgnoreCase))
// ?? 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)
{
break;
}
}
// if (index >= limit)
// {
// break;
// }
//}
}
private void AddGeneralProperties(BaseItem item, StubType? itemStubType, BaseItem context, XmlWriter writer, Filter filter)
@ -935,19 +945,6 @@ namespace Emby.Dlna.Didl
{
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
if (imageInfo == null)
{
@ -959,34 +956,7 @@ namespace Emby.Dlna.Didl
return;
}
var playbackPercentage = 0;
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");
var albumartUrlInfo = GetImageUrl(imageInfo, _profile.MaxAlbumArtWidth, _profile.MaxAlbumArtHeight, "jpg");
writer.WriteStartElement("upnp", "albumArtURI", NS_UPNP);
writer.WriteAttributeString("dlna", "profileID", NS_DLNA, _profile.AlbumArtPn);
@ -994,7 +964,7 @@ namespace Emby.Dlna.Didl
writer.WriteFullEndElement();
// 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);
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, 1024, 768, playbackPercentage, unplayedCount, "jpg", "JPEG_MED");
AddImageResElement(item, writer, 640, 480, playbackPercentage, unplayedCount, "jpg", "JPEG_SM");
AddImageResElement(item, writer, 4096, 4096, playbackPercentage, unplayedCount, "png", "PNG_LRG");
AddImageResElement(item, writer, 160, 160, playbackPercentage, unplayedCount, "png", "PNG_TN");
AddImageResElement(item, writer, 4096, 4096, "jpg", "JPEG_LRG");
AddImageResElement(item, writer, 1024, 768, "jpg", "JPEG_MED");
AddImageResElement(item, writer, 640, 480, "jpg", "JPEG_SM");
AddImageResElement(item, writer, 4096, 4096, "png", "PNG_LRG");
AddImageResElement(item, writer, 160, 160, "png", "PNG_TN");
}
}
@ -1035,8 +1005,6 @@ namespace Emby.Dlna.Didl
XmlWriter writer,
int maxWidth,
int maxHeight,
int playbackPercentage,
int unplayedCount,
string format,
string org_Pn)
{
@ -1047,7 +1015,7 @@ namespace Emby.Dlna.Didl
return;
}
var albumartUrlInfo = GetImageUrl(imageInfo, maxWidth, maxHeight, playbackPercentage, unplayedCount, format);
var albumartUrlInfo = GetImageUrl(imageInfo, maxWidth, maxHeight, format);
writer.WriteStartElement(string.Empty, "res", NS_DIDL);
@ -1151,7 +1119,7 @@ namespace Emby.Dlna.Didl
return new ImageDownloadInfo
{
ItemId = item.Id.ToString("N"),
ItemId = item.Id,
Type = type,
ImageTag = tag,
Width = width,
@ -1163,7 +1131,7 @@ namespace Emby.Dlna.Didl
class ImageDownloadInfo
{
internal string ItemId;
internal Guid ItemId;
internal string ImageTag;
internal ImageType Type;
@ -1202,25 +1170,16 @@ namespace Emby.Dlna.Didl
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");
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}",
var url = string.Format("{0}/Items/{1}/Images/{2}/0/{3}/{4}/{5}/{6}/0/0",
_serverAddress,
info.ItemId,
info.ItemId.ToString("N"),
info.Type,
info.ImageTag,
format,
maxWidth.ToString(CultureInfo.InvariantCulture),
maxHeight.ToString(CultureInfo.InvariantCulture),
playbackPercentage.ToString(CultureInfo.InvariantCulture),
unplayedCount.ToString(CultureInfo.InvariantCulture)
maxHeight.ToString(CultureInfo.InvariantCulture)
);
var width = info.Width;
@ -1235,7 +1194,7 @@ namespace Emby.Dlna.Didl
Height = height.Value,
Width = width.Value
}, null, null, maxWidth, maxHeight);
}, 0, 0, maxWidth, maxHeight);
width = Convert.ToInt32(newSize.Width);
height = Convert.ToInt32(newSize.Height);
@ -1249,6 +1208,9 @@ namespace Emby.Dlna.Didl
}
}
// just lie
info.IsDirectStream = true;
return new ImageUrlInfo
{
Url = url,

@ -132,55 +132,55 @@ namespace Emby.Dlna
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))
return false;
}
if (!string.IsNullOrWhiteSpace(profileInfo.FriendlyName))
if (!string.IsNullOrEmpty(profileInfo.FriendlyName))
{
if (deviceInfo.FriendlyName == null || !IsRegexMatch(deviceInfo.FriendlyName, profileInfo.FriendlyName))
return false;
}
if (!string.IsNullOrWhiteSpace(profileInfo.Manufacturer))
if (!string.IsNullOrEmpty(profileInfo.Manufacturer))
{
if (deviceInfo.Manufacturer == null || !IsRegexMatch(deviceInfo.Manufacturer, profileInfo.Manufacturer))
return false;
}
if (!string.IsNullOrWhiteSpace(profileInfo.ManufacturerUrl))
if (!string.IsNullOrEmpty(profileInfo.ManufacturerUrl))
{
if (deviceInfo.ManufacturerUrl == null || !IsRegexMatch(deviceInfo.ManufacturerUrl, profileInfo.ManufacturerUrl))
return false;
}
if (!string.IsNullOrWhiteSpace(profileInfo.ModelDescription))
if (!string.IsNullOrEmpty(profileInfo.ModelDescription))
{
if (deviceInfo.ModelDescription == null || !IsRegexMatch(deviceInfo.ModelDescription, profileInfo.ModelDescription))
return false;
}
if (!string.IsNullOrWhiteSpace(profileInfo.ModelName))
if (!string.IsNullOrEmpty(profileInfo.ModelName))
{
if (deviceInfo.ModelName == null || !IsRegexMatch(deviceInfo.ModelName, profileInfo.ModelName))
return false;
}
if (!string.IsNullOrWhiteSpace(profileInfo.ModelNumber))
if (!string.IsNullOrEmpty(profileInfo.ModelNumber))
{
if (deviceInfo.ModelNumber == null || !IsRegexMatch(deviceInfo.ModelNumber, profileInfo.ModelNumber))
return false;
}
if (!string.IsNullOrWhiteSpace(profileInfo.ModelUrl))
if (!string.IsNullOrEmpty(profileInfo.ModelUrl))
{
if (deviceInfo.ModelUrl == null || !IsRegexMatch(deviceInfo.ModelUrl, profileInfo.ModelUrl))
return false;
}
if (!string.IsNullOrWhiteSpace(profileInfo.SerialNumber))
if (!string.IsNullOrEmpty(profileInfo.SerialNumber))
{
if (deviceInfo.SerialNumber == null || !IsRegexMatch(deviceInfo.SerialNumber, profileInfo.SerialNumber))
return false;
@ -220,7 +220,7 @@ namespace Emby.Dlna
}
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);
}
@ -235,7 +235,7 @@ namespace Emby.Dlna
private bool IsMatch(IDictionary<string, string> headers, HttpHeaderInfo header)
{
// Handle invalid user setup
if (string.IsNullOrWhiteSpace(header.Name))
if (string.IsNullOrEmpty(header.Name))
{
return false;
}
@ -332,7 +332,7 @@ namespace Emby.Dlna
public DeviceProfile GetProfile(string id)
{
if (string.IsNullOrWhiteSpace(id))
if (string.IsNullOrEmpty(id))
{
throw new ArgumentNullException("id");
}
@ -429,7 +429,7 @@ namespace Emby.Dlna
{
profile = ReserializeProfile(profile);
if (string.IsNullOrWhiteSpace(profile.Name))
if (string.IsNullOrEmpty(profile.Name))
{
throw new ArgumentException("Profile is missing Name");
}
@ -444,11 +444,11 @@ namespace Emby.Dlna
{
profile = ReserializeProfile(profile);
if (string.IsNullOrWhiteSpace(profile.Id))
if (string.IsNullOrEmpty(profile.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");
}
@ -531,7 +531,7 @@ namespace Emby.Dlna
}
}
class DlnaProfileEntryPoint : IServerEntryPoint
class DlnaProfileEntryPoint /*: IServerEntryPoint*/
{
private readonly IApplicationPaths _appPaths;
private readonly IFileSystem _fileSystem;
@ -546,7 +546,7 @@ namespace Emby.Dlna
public void Run()
{
//DumpProfiles();
DumpProfiles();
}
private void DumpProfiles()
@ -595,7 +595,6 @@ namespace Emby.Dlna
public void Dispose()
{
GC.SuppressFinalize(this);
}
}
}

@ -1,121 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<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>
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<Compile Include="Common\Argument.cs" />
<Compile Include="Common\DeviceIcon.cs" />
<Compile Include="Common\DeviceService.cs" />
<Compile Include="Common\ServiceAction.cs" />
<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" />
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
<ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />
<ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" />
<ProjectReference Include="..\RSSDP\RSSDP.csproj" />
</ItemGroup>
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="Images\logo120.jpg" />
<EmbeddedResource Include="Images\logo120.png" />
@ -128,27 +24,7 @@
<EmbeddedResource Include="Images\people480.jpg" />
<EmbeddedResource Include="Images\people480.png" />
</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>
<EmbeddedResource Include="Profiles\Xml\Default.xml" />
<EmbeddedResource Include="Profiles\Xml\Denon AVR.xml" />
@ -179,12 +55,5 @@
<ItemGroup>
<EmbeddedResource Include="Profiles\Xml\Marantz.xml" />
</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>
</Project>

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

@ -85,7 +85,7 @@ namespace Emby.Dlna.Eventing
int val;
if (int.TryParse(header, NumberStyles.Any, _usCulture, out val))
if (int.TryParse(header, NumberStyles.Integer, _usCulture, out val))
{
return val;
}
@ -118,7 +118,7 @@ namespace Emby.Dlna.Eventing
};
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;
}

@ -1,5 +1,5 @@

namespace MediaBrowser.Controller.Dlna
namespace Emby.Dlna
{
public interface IConnectionManager : IEventManager, IUpnpService
{

@ -1,5 +1,5 @@

namespace MediaBrowser.Controller.Dlna
namespace Emby.Dlna
{
public interface IContentDirectory : IEventManager, IUpnpService
{

@ -1,5 +1,5 @@

namespace MediaBrowser.Controller.Dlna
namespace Emby.Dlna
{
public interface IEventManager
{

@ -1,5 +1,5 @@

namespace MediaBrowser.Controller.Dlna
namespace Emby.Dlna
{
public interface IMediaReceiverRegistrar : IEventManager, IUpnpService
{

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

@ -8,13 +8,12 @@ using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Controller.Session;
using MediaBrowser.Controller.TV;
using Emby.Dlna.PlayTo;
using Emby.Dlna.Ssdp;
using MediaBrowser.Model.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Dlna;
@ -22,13 +21,14 @@ using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.System;
using MediaBrowser.Model.Threading;
using MediaBrowser.Model.Xml;
using Rssdp;
using Rssdp.Infrastructure;
using System.Threading;
namespace Emby.Dlna.Main
{
public class DlnaEntryPoint : IServerEntryPoint
public class DlnaEntryPoint : IServerEntryPoint, IRunBeforeStartup
{
private readonly IServerConfigurationManager _config;
private readonly ILogger _logger;
@ -48,8 +48,6 @@ namespace Emby.Dlna.Main
private readonly IDeviceDiscovery _deviceDiscovery;
private bool _ssdpHandlerStarted;
private bool _dlnaServerStarted;
private SsdpDevicePublisher _Publisher;
private readonly ITimerFactory _timerFactory;
@ -59,6 +57,12 @@ namespace Emby.Dlna.Main
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,
ILogManager logManager,
IServerApplicationHost appHost,
@ -69,9 +73,17 @@ namespace Emby.Dlna.Main
IDlnaManager dlnaManager,
IImageProcessor imageProcessor,
IUserDataManager userDataManager,
ILocalizationManager localization,
ILocalizationManager localizationManager,
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;
_appHost = appHost;
@ -82,7 +94,7 @@ namespace Emby.Dlna.Main
_dlnaManager = dlnaManager;
_imageProcessor = imageProcessor;
_userDataManager = userDataManager;
_localization = localization;
_localization = localizationManager;
_mediaSourceManager = mediaSourceManager;
_deviceDiscovery = deviceDiscovery;
_mediaEncoder = mediaEncoder;
@ -91,6 +103,26 @@ namespace Emby.Dlna.Main
_environmentInfo = environmentInfo;
_networkManager = networkManager;
_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()
@ -99,20 +131,9 @@ namespace Emby.Dlna.Main
ReloadComponents();
_config.ConfigurationUpdated += _config_ConfigurationUpdated;
_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)
{
if (string.Equals(e.Key, "dlna", StringComparison.OrdinalIgnoreCase))
@ -125,29 +146,22 @@ namespace Emby.Dlna.Main
{
var options = _config.GetDlnaConfiguration();
if (!_ssdpHandlerStarted)
{
StartSsdpHandler();
}
var isServerStarted = _dlnaServerStarted;
StartSsdpHandler();
if (options.EnableServer && !isServerStarted)
if (options.EnableServer)
{
await StartDlnaServer().ConfigureAwait(false);
await StartDevicePublisher(options).ConfigureAwait(false);
}
else if (!options.EnableServer && isServerStarted)
else
{
DisposeDlnaServer();
DisposeDevicePublisher();
}
var isPlayToStarted = _manager != null;
if (options.EnablePlayTo && !isPlayToStarted)
if (options.EnablePlayTo)
{
StartPlayToManager();
}
else if (!options.EnablePlayTo && isPlayToStarted)
else
{
DisposePlayToManager();
}
@ -165,12 +179,9 @@ namespace Emby.Dlna.Main
{
IsShared = true
};
}
StartPublishing(_communicationsServer);
_ssdpHandlerStarted = true;
StartDeviceDiscovery(_communicationsServer);
StartDeviceDiscovery(_communicationsServer);
}
}
catch (Exception ex)
{
@ -183,12 +194,6 @@ namespace Emby.Dlna.Main
_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)
{
try
@ -205,6 +210,7 @@ namespace Emby.Dlna.Main
{
try
{
_logger.Info("Disposing DeviceDiscovery");
((DeviceDiscovery)_deviceDiscovery).Dispose();
}
catch (Exception ex)
@ -213,29 +219,27 @@ namespace Emby.Dlna.Main
}
}
private void DisposeSsdpHandler()
public async Task StartDevicePublisher(Configuration.DlnaOptions options)
{
DisposeDeviceDiscovery();
try
if (!options.BlastAliveMessages)
{
((DeviceDiscovery)_deviceDiscovery).Dispose();
_ssdpHandlerStarted = false;
return;
}
catch (Exception ex)
if (_Publisher != null)
{
_logger.ErrorException("Error stopping ssdp handlers", ex);
return;
}
}
public async Task StartDlnaServer()
{
try
{
_Publisher = new SsdpDevicePublisher(_communicationsServer, _timerFactory, _environmentInfo.OperatingSystemName, _environmentInfo.OperatingSystemVersion);
_Publisher.LogFunction = LogMessage;
_Publisher.SupportPnpRootDevice = false;
await RegisterServerEndpoints().ConfigureAwait(false);
_dlnaServerStarted = true;
_Publisher.StartBroadcastingAliveMessages(TimeSpan.FromSeconds(options.BlastAliveMessageIntervalSeconds));
}
catch (Exception ex)
{
@ -245,23 +249,15 @@ namespace Emby.Dlna.Main
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 udn = CreateUuid(_appHost.SystemId);
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;
//}
@ -274,7 +270,7 @@ namespace Emby.Dlna.Main
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.
FriendlyName = "Emby Server",
Manufacturer = "Emby",
@ -286,7 +282,7 @@ namespace Emby.Dlna.Main
SetProperies(device, fullService);
_Publisher.AddDevice(device);
var embeddedDevices = new List<string>
var embeddedDevices = new []
{
"urn:schemas-upnp-org:service:ContentDirectory:1",
"urn:schemas-upnp-org:service:ConnectionManager:1",
@ -338,6 +334,11 @@ namespace Emby.Dlna.Main
{
lock (_syncLock)
{
if (_manager != null)
{
return;
}
try
{
_manager = new PlayToManager(_logger,
@ -373,6 +374,7 @@ namespace Emby.Dlna.Main
{
try
{
_logger.Info("Disposing PlayToManager");
_manager.Dispose();
}
catch (Exception ex)
@ -386,41 +388,31 @@ namespace Emby.Dlna.Main
public void Dispose()
{
DisposeDlnaServer();
DisposeDevicePublisher();
DisposePlayToManager();
DisposeSsdpHandler();
DisposeDeviceDiscovery();
if (_communicationsServer != null)
{
_logger.Info("Disposing SsdpCommunicationsServer");
_communicationsServer.Dispose();
_communicationsServer = null;
}
GC.SuppressFinalize(this);
ContentDirectory = null;
ConnectionManager = null;
MediaReceiverRegistrar = null;
Current = null;
}
public void DisposeDlnaServer()
public void DisposeDevicePublisher()
{
if (_Publisher != null)
{
var devices = _Publisher.Devices.ToList();
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);
// }
//}
_logger.Info("Disposing SsdpDevicePublisher");
_Publisher.Dispose();
_Publisher = null;
}
_dlnaServerStarted = false;
}
}
}

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

@ -106,27 +106,17 @@ namespace Emby.Dlna.PlayTo
_timerFactory = timerFactory;
}
private int GetPlaybackTimerIntervalMs()
{
return 1000;
}
private int GetInactiveTimerIntervalMs()
{
return 60000;
}
public void Start()
{
_timer = _timerFactory.Create(TimerCallback, null, GetPlaybackTimerIntervalMs(), GetInactiveTimerIntervalMs());
_timerActive = false;
_logger.Debug("Dlna Device.Start");
_timer = _timerFactory.Create(TimerCallback, null, 1000, Timeout.Infinite);
}
private DateTime _lastVolumeRefresh;
private bool _volumeRefreshActive;
private void RefreshVolumeIfNeeded()
{
if (!_timerActive)
if (!_volumeRefreshActive)
{
return;
}
@ -134,19 +124,19 @@ namespace Emby.Dlna.PlayTo
if (DateTime.UtcNow >= _lastVolumeRefresh.AddSeconds(5))
{
_lastVolumeRefresh = DateTime.UtcNow;
RefreshVolume();
RefreshVolume(CancellationToken.None);
}
}
private async void RefreshVolume()
private async void RefreshVolume(CancellationToken cancellationToken)
{
if (_disposed)
return;
try
{
await GetVolume().ConfigureAwait(false);
await GetMute().ConfigureAwait(false);
await GetVolume(cancellationToken).ConfigureAwait(false);
await GetMute(cancellationToken).ConfigureAwait(false);
}
catch (Exception ex)
{
@ -155,21 +145,17 @@ namespace Emby.Dlna.PlayTo
}
private readonly object _timerLock = new object();
private bool _timerActive;
private void RestartTimer()
private void RestartTimer(bool immediate = false)
{
if (_disposed)
return;
lock (_timerLock)
{
if (!_timerActive)
{
_logger.Debug("RestartTimer");
_timer.Change(10, GetPlaybackTimerIntervalMs());
}
if (_disposed)
return;
_volumeRefreshActive = true;
_timerActive = true;
var time = immediate ? 100 : 10000;
_timer.Change(time, Timeout.Infinite);
}
}
@ -178,71 +164,67 @@ namespace Emby.Dlna.PlayTo
/// </summary>
private void RestartTimerInactive()
{
if (_disposed)
return;
lock (_timerLock)
{
if (_timerActive)
{
_logger.Debug("RestartTimerInactive");
var interval = GetInactiveTimerIntervalMs();
if (_disposed)
return;
if (_timer != null)
{
_timer.Change(interval, interval);
}
}
_volumeRefreshActive = false;
_timerActive = false;
_timer.Change(Timeout.Infinite, Timeout.Infinite);
}
}
public void OnPlaybackStartedExternally()
{
RestartTimer(true);
}
#region Commanding
public Task VolumeDown()
public Task VolumeDown(CancellationToken cancellationToken)
{
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);
return SetVolume(sendVolume);
return SetVolume(sendVolume, cancellationToken);
}
public Task ToggleMute()
public Task ToggleMute(CancellationToken cancellationToken)
{
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)
{
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)
{
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));
}
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)
return false;
@ -278,7 +262,7 @@ namespace Emby.Dlna.PlayTo
_logger.Debug("Setting mute");
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);
IsMuted = mute;
@ -289,9 +273,11 @@ namespace Emby.Dlna.PlayTo
/// <summary>
/// Sets volume on a scale of 0-100
/// </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)
return;
@ -306,13 +292,15 @@ namespace Emby.Dlna.PlayTo
// Remote control will perform better
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);
}
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)
return;
@ -323,15 +311,21 @@ namespace Emby.Dlna.PlayTo
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);
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);
var command = AvCommands.ServiceActions.FirstOrDefault(c => c.Name == "SetAVTransportURI");
var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "SetAVTransportURI");
if (command == null)
return;
@ -348,7 +342,7 @@ namespace Emby.Dlna.PlayTo
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)
.ConfigureAwait(false);
@ -356,7 +350,7 @@ namespace Emby.Dlna.PlayTo
try
{
await SetPlay().ConfigureAwait(false);
await SetPlay(avCommands, CancellationToken.None).ConfigureAwait(false);
}
catch
{
@ -364,7 +358,7 @@ namespace Emby.Dlna.PlayTo
// Others won't
}
RestartTimer();
RestartTimer(true);
}
private string CreateDidlMeta(string value)
@ -375,11 +369,11 @@ namespace Emby.Dlna.PlayTo
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)
return;
return Task.CompletedTask;
var service = GetAvTransportService();
@ -388,52 +382,74 @@ namespace Emby.Dlna.PlayTo
throw new InvalidOperationException("Unable to find service");
}
await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, AvCommands.BuildPost(command, service.ServiceType, 1))
.ConfigureAwait(false);
return new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, 1));
}
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)
return;
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);
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)
return;
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);
TransportState = TRANSPORTSTATE.PAUSED;
RestartTimer(true);
}
#endregion
#region Get data
private int _successiveStopCount;
private int _connectFailureCount;
private async void TimerCallback(object sender)
{
if (_disposed)
return;
const int maxSuccessiveStopReturns = 5;
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)
{
@ -451,13 +467,13 @@ namespace Emby.Dlna.PlayTo
}
else
{
var tuple = await GetPositionInfo().ConfigureAwait(false);
var tuple = await GetPositionInfo(avCommands, cancellationToken).ConfigureAwait(false);
var currentObject = tuple.Item2;
if (tuple.Item1 && currentObject == null)
{
currentObject = await GetMediaInfo().ConfigureAwait(false);
currentObject = await GetMediaInfo(avCommands, cancellationToken).ConfigureAwait(false);
}
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 (transportState.Value == TRANSPORTSTATE.STOPPED)
{
_successiveStopCount++;
if (_successiveStopCount >= maxSuccessiveStopReturns)
{
RestartTimerInactive();
}
RestartTimerInactive();
}
else
{
_successiveStopCount = 0;
RestartTimer();
}
}
@ -492,54 +502,39 @@ namespace Emby.Dlna.PlayTo
RestartTimerInactive();
}
}
catch (HttpException ex)
catch (Exception ex)
{
if (_disposed)
return;
//_logger.ErrorException("Error updating device info for {0}", ex, Properties.Name);
_successiveStopCount++;
_connectFailureCount++;
if (_connectFailureCount >= 3)
{
if (OnDeviceUnavailable != null)
var action = OnDeviceUnavailable;
if (action != null)
{
_logger.Debug("Disposing device due to loss of connection");
OnDeviceUnavailable();
action();
return;
}
}
if (_successiveStopCount >= maxSuccessiveStopReturns)
{
RestartTimerInactive();
}
}
catch (Exception ex)
{
if (_disposed)
return;
_logger.ErrorException("Error updating device info for {0}", ex, Properties.Name);
_successiveStopCount++;
if (_successiveStopCount >= maxSuccessiveStopReturns)
{
RestartTimerInactive();
}
RestartTimerInactive();
}
}
private async Task GetVolume()
private async Task GetVolume(CancellationToken cancellationToken)
{
if (_disposed)
{
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)
return;
@ -550,7 +545,7 @@ namespace Emby.Dlna.PlayTo
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);
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)
{
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)
return;
@ -588,7 +585,7 @@ namespace Emby.Dlna.PlayTo
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);
if (result == null || result.Document == null)
@ -600,9 +597,9 @@ namespace Emby.Dlna.PlayTo
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)
return null;
@ -610,7 +607,7 @@ namespace Emby.Dlna.PlayTo
if (service == 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);
if (result == null || result.Document == null)
@ -634,9 +631,9 @@ namespace Emby.Dlna.PlayTo
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)
return null;
@ -647,7 +644,9 @@ namespace Emby.Dlna.PlayTo
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);
if (result == null || result.Document == null)
@ -691,9 +690,9 @@ namespace Emby.Dlna.PlayTo
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)
return new Tuple<bool, uBaseObject>(false, null);
@ -704,7 +703,9 @@ namespace Emby.Dlna.PlayTo
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);
if (result == null || result.Document == null)
@ -831,42 +832,67 @@ namespace Emby.Dlna.PlayTo
#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)
{
return;
throw new ObjectDisposedException(GetType().Name);
}
var avService = GetAvTransportService();
if (avService == null)
return;
{
return null;
}
string url = NormalizeUrl(Properties.BaseUrl, avService.ScpdUrl);
var httpClient = new SsdpHttpClient(_httpClient, _config);
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)
{
return;
throw new ObjectDisposedException(GetType().Name);
}
var avService = GetServiceRenderingControl();
if (avService == null)
return;
{
throw new ArgumentException("Device AvService is null");
}
string url = NormalizeUrl(Properties.BaseUrl, avService.ScpdUrl);
var httpClient = new SsdpHttpClient(_httpClient, _config);
_logger.Debug("Dlna Device.GetRenderingProtocolAsync");
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)
@ -891,7 +917,7 @@ namespace Emby.Dlna.PlayTo
set;
}
internal TransportCommands RendererCommands
private TransportCommands RendererCommands
{
get;
set;
@ -985,12 +1011,6 @@ namespace Emby.Dlna.PlayTo
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;
}
@ -1010,8 +1030,8 @@ namespace Emby.Dlna.PlayTo
var depth = element.GetDescendantValue(uPnpNamespaces.ud.GetName("depth"));
var url = element.GetDescendantValue(uPnpNamespaces.ud.GetName("url"));
var widthValue = int.Parse(width, NumberStyles.Any, UsCulture);
var heightValue = int.Parse(height, NumberStyles.Any, UsCulture);
var widthValue = int.Parse(width, NumberStyles.Integer, UsCulture);
var heightValue = int.Parse(height, NumberStyles.Integer, UsCulture);
return new DeviceIcon
{
@ -1089,6 +1109,12 @@ namespace Emby.Dlna.PlayTo
private void OnPlaybackProgress(uBaseObject mediaInfo)
{
var mediaUrl = mediaInfo.Url;
if (string.IsNullOrWhiteSpace(mediaUrl))
{
return;
}
if (PlaybackProgress != null)
{
PlaybackProgress.Invoke(this, new PlaybackProgressEventArgs
@ -1131,7 +1157,6 @@ namespace Emby.Dlna.PlayTo
_disposed = true;
DisposeTimer();
GC.SuppressFinalize(this);
}
}

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

@ -21,6 +21,8 @@ using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Extensions;
using System.Net.Http;
using MediaBrowser.Model.Services;
namespace Emby.Dlna.PlayTo
{
@ -53,10 +55,6 @@ namespace Emby.Dlna.PlayTo
}
}
public void OnActivity()
{
}
public bool SupportsMediaControl
{
get { return IsSessionActive; }
@ -141,17 +139,15 @@ namespace Emby.Dlna.PlayTo
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)
{
var progress = GetProgressInfo(e.OldMediaInfo, streamInfo);
var positionTicks = progress.PositionTicks;
var positionTicks = GetProgressPositionTicks(e.OldMediaInfo, streamInfo);
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;
var newItemProgress = GetProgressInfo(e.NewMediaInfo, streamInfo);
@ -173,20 +169,19 @@ namespace Emby.Dlna.PlayTo
try
{
var streamInfo = await StreamParams.ParseFromUrl(e.MediaInfo.Url, _libraryManager, _mediaSourceManager)
.ConfigureAwait(false);
var streamInfo = StreamParams.ParseFromUrl(e.MediaInfo.Url, _libraryManager, _mediaSourceManager);
if (streamInfo.Item == null) return;
var progress = GetProgressInfo(e.MediaInfo, streamInfo);
var positionTicks = progress.PositionTicks;
var positionTicks = GetProgressPositionTicks(e.MediaInfo, streamInfo);
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) :
streamInfo.MediaSource.RunTimeTicks;
mediaSource.RunTimeTicks;
var playedToCompletion = (positionTicks.HasValue && positionTicks.Value == 0);
@ -241,7 +236,7 @@ namespace Emby.Dlna.PlayTo
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)
{
@ -265,7 +260,14 @@ namespace Emby.Dlna.PlayTo
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)
{
@ -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;
@ -289,11 +291,16 @@ namespace Emby.Dlna.PlayTo
ticks += info.StartPositionTicks;
}
return ticks;
}
private PlaybackStartInfo GetProgressInfo(uBaseObject mediaInfo, StreamParams info)
{
return new PlaybackStartInfo
{
ItemId = info.ItemId,
SessionId = _session.Id,
PositionTicks = ticks,
PositionTicks = GetProgressPositionTicks(mediaInfo, info),
IsMuted = _device.IsMuted,
IsPaused = _device.IsPaused,
MediaSourceId = info.MediaSourceId,
@ -301,7 +308,8 @@ namespace Emby.Dlna.PlayTo
SubtitleStreamIndex = info.SubtitleStreamIndex,
VolumeLevel = _device.Volume,
CanSeek = info.MediaSource == null ? _device.Duration.HasValue : info.MediaSource.RunTimeTicks.HasValue,
// TODO
CanSeek = true,
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);
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>();
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;
@ -354,31 +362,31 @@ namespace Emby.Dlna.PlayTo
Playlist.AddRange(playlist);
}
if (!String.IsNullOrWhiteSpace(command.ControllingUserId))
if (!command.ControllingUserId.Equals(Guid.Empty))
{
await _sessionManager.LogSessionActivity(_session.Client, _session.ApplicationVersion, _session.DeviceId,
_session.DeviceName, _session.RemoteEndPoint, user).ConfigureAwait(false);
_sessionManager.LogSessionActivity(_session.Client, _session.ApplicationVersion, _session.DeviceId,
_session.DeviceName, _session.RemoteEndPoint, user);
}
await PlayItems(playlist).ConfigureAwait(false);
}
public Task SendPlaystateCommand(PlaystateRequest command, CancellationToken cancellationToken)
private Task SendPlaystateCommand(PlaystateRequest command, CancellationToken cancellationToken)
{
switch (command.Command)
{
case PlaystateCommand.Stop:
Playlist.Clear();
return _device.SetStop();
return _device.SetStop(CancellationToken.None);
case PlaystateCommand.Pause:
return _device.SetPause();
return _device.SetPause(CancellationToken.None);
case PlaystateCommand.Unpause:
return _device.SetPlay();
return _device.SetPlay(CancellationToken.None);
case PlaystateCommand.PlayPause:
return _device.IsPaused ? _device.SetPlay() : _device.SetPause();
return _device.IsPaused ? _device.SetPlay(CancellationToken.None) : _device.SetPause(CancellationToken.None);
case PlaystateCommand.Seek:
{
@ -392,7 +400,7 @@ namespace Emby.Dlna.PlayTo
return SetPlaylistIndex(_currentPlaylistIndex - 1);
}
return Task.FromResult(true);
return Task.CompletedTask;
}
private async Task Seek(long newPosition)
@ -401,17 +409,17 @@ namespace Emby.Dlna.PlayTo
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))
{
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);
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;
}
await SeekAfterTransportChange(newPosition).ConfigureAwait(false);
await SeekAfterTransportChange(newPosition, CancellationToken.None).ConfigureAwait(false);
}
}
@ -425,46 +433,6 @@ namespace Emby.Dlna.PlayTo
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
#region Playlist
@ -497,13 +465,13 @@ namespace Emby.Dlna.PlayTo
var hasMediaSources = item as IHasMediaSources;
var mediaSources = hasMediaSources != null
? (_mediaSourceManager.GetStaticMediaSources(hasMediaSources, true, user))
? (_mediaSourceManager.GetStaticMediaSources(item, true, user))
: new List<MediaSourceInfo>();
var playlistItem = GetPlaylistItem(item, mediaSources, profile, _session.DeviceId, mediaSourceId, audioStreamIndex, subtitleStreamIndex);
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)
.GetItemDidl(_config.GetDlnaConfiguration(), item, user, null, _session.DeviceId, new Filter(), playlistItem.StreamInfo);
@ -528,7 +496,7 @@ namespace Emby.Dlna.PlayTo
streamInfo.TargetAudioChannels,
streamInfo.TargetAudioBitDepth,
streamInfo.IsDirectStream,
streamInfo.RunTimeTicks,
streamInfo.RunTimeTicks ?? 0,
streamInfo.TranscodeSeekInfo);
}
@ -544,10 +512,10 @@ namespace Emby.Dlna.PlayTo
streamInfo.TargetVideoBitrate,
streamInfo.TargetTimestamp,
streamInfo.IsDirectStream,
streamInfo.RunTimeTicks,
streamInfo.RunTimeTicks ?? 0,
streamInfo.TargetVideoProfile,
streamInfo.TargetVideoLevel,
streamInfo.TargetFramerate,
streamInfo.TargetFramerate ?? 0,
streamInfo.TargetPacketLength,
streamInfo.TranscodeSeekInfo,
streamInfo.IsTargetAnamorphic,
@ -582,8 +550,8 @@ namespace Emby.Dlna.PlayTo
{
StreamInfo = new StreamBuilder(_mediaEncoder, GetStreamBuilderLogger()).BuildVideoItem(new VideoOptions
{
ItemId = item.Id.ToString("N"),
MediaSources = mediaSources.ToArray(mediaSources.Count),
ItemId = item.Id,
MediaSources = mediaSources.ToArray(),
Profile = profile,
DeviceId = deviceId,
MaxBitrate = profile.MaxStreamingBitrate,
@ -602,7 +570,7 @@ namespace Emby.Dlna.PlayTo
{
StreamInfo = new StreamBuilder(_mediaEncoder, GetStreamBuilderLogger()).BuildAudioItem(new AudioOptions
{
ItemId = item.Id.ToString("N"),
ItemId = item.Id,
MediaSources = mediaSources.ToArray(mediaSources.Count),
Profile = profile,
DeviceId = deviceId,
@ -642,19 +610,19 @@ namespace Emby.Dlna.PlayTo
if (index < 0 || index >= Playlist.Count)
{
Playlist.Clear();
await _device.SetStop();
await _device.SetStop(CancellationToken.None);
return;
}
_currentPlaylistIndex = 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;
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.Dispose();
GC.SuppressFinalize(this);
}
}
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
public Task SendGeneralCommand(GeneralCommand command, CancellationToken cancellationToken)
private Task SendGeneralCommand(GeneralCommand command, CancellationToken cancellationToken)
{
GeneralCommandType commandType;
@ -691,15 +658,15 @@ namespace Emby.Dlna.PlayTo
switch (commandType)
{
case GeneralCommandType.VolumeDown:
return _device.VolumeDown();
return _device.VolumeDown(cancellationToken);
case GeneralCommandType.VolumeUp:
return _device.VolumeUp();
return _device.VolumeUp(cancellationToken);
case GeneralCommandType.Mute:
return _device.Mute();
return _device.Mute(cancellationToken);
case GeneralCommandType.Unmute:
return _device.Unmute();
return _device.Unmute(cancellationToken);
case GeneralCommandType.ToggleMute:
return _device.ToggleMute();
return _device.ToggleMute(cancellationToken);
case GeneralCommandType.SetAudioStreamIndex:
{
string arg;
@ -708,7 +675,7 @@ namespace Emby.Dlna.PlayTo
{
int val;
if (Int32.TryParse(arg, NumberStyles.Any, _usCulture, out val))
if (int.TryParse(arg, NumberStyles.Integer, _usCulture, out val))
{
return SetAudioStreamIndex(val);
}
@ -726,7 +693,7 @@ namespace Emby.Dlna.PlayTo
{
int val;
if (Int32.TryParse(arg, NumberStyles.Any, _usCulture, out val))
if (int.TryParse(arg, NumberStyles.Integer, _usCulture, out val))
{
return SetSubtitleStreamIndex(val);
}
@ -744,9 +711,9 @@ namespace Emby.Dlna.PlayTo
{
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.");
@ -755,11 +722,11 @@ namespace Emby.Dlna.PlayTo
throw new ArgumentException("Volume argument cannot be null");
}
default:
return Task.FromResult(true);
return Task.CompletedTask;
}
}
return Task.FromResult(true);
return Task.CompletedTask;
}
private async Task SetAudioStreamIndex(int? newIndex)
@ -768,21 +735,20 @@ namespace Emby.Dlna.PlayTo
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)
{
var progress = GetProgressInfo(media, info);
var newPosition = progress.PositionTicks ?? 0;
var newPosition = GetProgressPositionTicks(media, info) ?? 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);
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))
{
await SeekAfterTransportChange(newPosition).ConfigureAwait(false);
await SeekAfterTransportChange(newPosition, CancellationToken.None).ConfigureAwait(false);
}
}
}
@ -794,27 +760,26 @@ namespace Emby.Dlna.PlayTo
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)
{
var progress = GetProgressInfo(media, info);
var newPosition = progress.PositionTicks ?? 0;
var newPosition = GetProgressPositionTicks(media, info) ?? 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);
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)
{
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 interval = 500;
@ -825,12 +790,12 @@ namespace Emby.Dlna.PlayTo
currentWait += interval;
}
await _device.Seek(TimeSpan.FromTicks(positionTicks)).ConfigureAwait(false);
await _device.Seek(TimeSpan.FromTicks(positionTicks), cancellationToken).ConfigureAwait(false);
}
private class StreamParams
{
public string ItemId { get; set; }
public Guid ItemId { get; set; }
public bool IsDirectStream { get; set; }
@ -847,10 +812,36 @@ namespace Emby.Dlna.PlayTo
public string LiveStreamId { get; set; }
public BaseItem Item { get; set; }
public MediaSourceInfo MediaSource { get; set; }
private MediaSourceInfo MediaSource;
private IMediaSourceManager _mediaSourceManager;
private static string GetItemId(string url)
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('/');
for (var i = 0; i < parts.Length; i++)
@ -862,96 +853,108 @@ namespace Emby.Dlna.PlayTo
{
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
{
ItemId = GetItemId(url)
};
Guid parsedId;
if (string.IsNullOrWhiteSpace(request.ItemId) || !Guid.TryParse(request.ItemId, out parsedId))
if (request.ItemId.Equals(Guid.Empty))
{
return request;
}
const string srch = "params=";
var index = url.IndexOf(srch, StringComparison.OrdinalIgnoreCase);
var index = url.IndexOf('?');
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++)
{
var val = vals[i];
request.DeviceProfileId = values.Get("DeviceProfileId");
request.DeviceId = values.Get("DeviceId");
request.MediaSourceId = values.Get("MediaSourceId");
request.LiveStreamId = values.Get("LiveStreamId");
request.IsDirectStream = string.Equals("true", values.Get("Static"), StringComparison.OrdinalIgnoreCase);
if (string.IsNullOrWhiteSpace(val))
{
continue;
}
request.AudioStreamIndex = GetIntValue(values, "AudioStreamIndex");
request.SubtitleStreamIndex = GetIntValue(values, "SubtitleStreamIndex");
request.StartPositionTicks = GetLongValue(values, "StartPositionTicks");
if (i == 0)
{
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 = libraryManager.GetItemById(request.ItemId);
request.Item = string.IsNullOrWhiteSpace(request.ItemId)
? null
: libraryManager.GetItemById(parsedId);
request._mediaSourceManager = mediaSourceManager;
var hasMediaSources = request.Item as IHasMediaSources;
return request;
}
}
request.MediaSource = hasMediaSources == null
? null
: (await mediaSourceManager.GetMediaSource(hasMediaSources, request.MediaSourceId, request.LiveStreamId, false, CancellationToken.None).ConfigureAwait(false));
private static int? GetIntValue(QueryParamCollection values, string name)
{
var value = values.Get(name);
return request;
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, T data, CancellationToken cancellationToken)
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
return Task.FromResult(true);
return Task.CompletedTask;
}
}
}

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

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

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

@ -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 element = container.Descendants(uPnpNamespaces.svc + "allowedValueList")
@ -108,7 +108,7 @@ namespace Emby.Dlna.PlayTo
{
Name = container.GetValue(uPnpNamespaces.svc + "name"),
DataType = container.GetValue(uPnpNamespaces.svc + "dataType"),
AllowedValues = allowedValues
AllowedValues = allowedValues.ToArray()
};
}

File diff suppressed because one or more lines are too long

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -9,11 +9,11 @@
</Headers>
</Identification>
<Manufacturer>Emby</Manufacturer>
<ManufacturerUrl>http://emby.media/</ManufacturerUrl>
<ManufacturerUrl>https://emby.media</ManufacturerUrl>
<ModelName>Emby Server</ModelName>
<ModelDescription>Emby</ModelDescription>
<ModelNumber>Emby</ModelNumber>
<ModelUrl>http://emby.media/</ModelUrl>
<ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
<ModelNumber>01</ModelNumber>
<ModelUrl>https://emby.media</ModelUrl>
<EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
<EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit>
<EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit>
@ -23,11 +23,10 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
<MaxStaticBitrate>40000000</MaxStaticBitrate>
<MaxStreamingBitrate>140000000</MaxStreamingBitrate>
<MaxStaticBitrate>140000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<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>
<TimelineOffsetSeconds>0</TimelineOffsetSeconds>
<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

@ -11,7 +11,7 @@
<Manufacturer>Microsoft Corporation</Manufacturer>
<ManufacturerUrl>http://www.microsoft.com/</ManufacturerUrl>
<ModelName>Windows Media Player Sharing</ModelName>
<ModelDescription>Emby</ModelDescription>
<ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
<ModelNumber>3.0</ModelNumber>
<ModelUrl>http://www.microsoft.com/</ModelUrl>
<EnableAlbumArtInDidl>true</EnableAlbumArtInDidl>
@ -23,11 +23,10 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
<MaxStreamingBitrate>40000000</MaxStreamingBitrate>
<MaxStaticBitrate>40000000</MaxStaticBitrate>
<MaxStreamingBitrate>140000000</MaxStreamingBitrate>
<MaxStaticBitrate>140000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
<XDlnaDoc>DMS-1.50</XDlnaDoc>
<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>
<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

@ -22,12 +22,12 @@ namespace Emby.Dlna.Server
public DescriptionXmlBuilder(DeviceProfile profile, string serverUdn, string serverAddress, string serverName, string serverId)
{
if (string.IsNullOrWhiteSpace(serverUdn))
if (string.IsNullOrEmpty(serverUdn))
{
throw new ArgumentNullException("serverUdn");
}
if (string.IsNullOrWhiteSpace(serverAddress))
if (string.IsNullOrEmpty(serverAddress))
{
throw new ArgumentNullException("serverAddress");
}
@ -77,6 +77,11 @@ namespace Emby.Dlna.Server
builder.Append("<minor>0</minor>");
builder.Append("</specVersion>");
if (!EnableAbsoluteUrls)
{
builder.Append("<URLBase>" + Escape(_serverAddress) + "</URLBase>");
}
AppendDeviceInfo(builder);
builder.Append("</root>");
@ -90,6 +95,9 @@ namespace Emby.Dlna.Server
AppendDeviceProperties(builder);
AppendIconList(builder);
builder.Append("<presentationURL>" + Escape(_serverAddress) + "/web/index.html</presentationURL>");
AppendServiceList(builder);
builder.Append("</device>");
}
@ -169,12 +177,12 @@ namespace Emby.Dlna.Server
private void AppendDeviceProperties(StringBuilder builder)
{
builder.Append("<deviceType>urn:schemas-upnp-org:device:MediaServer:1</deviceType>");
builder.Append("<dlna:X_DLNACAP>" + Escape(_profile.XDlnaCap ?? string.Empty) + "</dlna:X_DLNACAP>");
builder.Append("<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\">" + 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("<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("<modelURL>" + Escape(_profile.ModelUrl ?? string.Empty) + "</modelURL>");
if (string.IsNullOrWhiteSpace(_profile.SerialNumber))
if (string.IsNullOrEmpty(_profile.SerialNumber))
{
builder.Append("<serialNumber>" + Escape(_serverId) + "</serialNumber>");
}
@ -195,15 +203,11 @@ namespace Emby.Dlna.Server
builder.Append("<serialNumber>" + Escape(_profile.SerialNumber) + "</serialNumber>");
}
builder.Append("<UDN>uuid:" + Escape(_serverUdn) + "</UDN>");
builder.Append("<presentationURL>" + Escape(_serverAddress) + "</presentationURL>");
builder.Append("<UPC/>");
if (!EnableAbsoluteUrls)
{
//builder.Append("<URLBase>" + Escape(_serverAddress) + "</URLBase>");
}
builder.Append("<UDN>uuid:" + Escape(_serverUdn) + "</UDN>");
if (!string.IsNullOrWhiteSpace(_profile.SonyAggregationFlags))
if (!string.IsNullOrEmpty(_profile.SonyAggregationFlags))
{
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()
{
if (string.IsNullOrWhiteSpace(_profile.FriendlyName))
if (string.IsNullOrEmpty(_profile.FriendlyName))
{
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);
@ -277,7 +281,7 @@ namespace Emby.Dlna.Server
private string BuildUrl(string url)
{
if (string.IsNullOrWhiteSpace(url))
if (string.IsNullOrEmpty(url))
{
return string.Empty;
}

@ -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);
}
}
}
}

@ -255,7 +255,7 @@ namespace Emby.Dlna.Service
}
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);
Logger.Debug("Control response. Headers: {0}", headers);

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

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

Loading…
Cancel
Save