diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs
index f65949ac7d..c365fcd829 100644
--- a/MediaBrowser.Api/Playback/BaseStreamingService.cs
+++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs
@@ -1210,85 +1210,89 @@ namespace MediaBrowser.Api.Playback
if (i == 0)
{
- request.DeviceId = val;
+ request.DeviceProfileId = val;
}
else if (i == 1)
{
- request.MediaSourceId = val;
+ request.DeviceId = val;
}
else if (i == 2)
{
- request.Static = string.Equals("true", val, StringComparison.OrdinalIgnoreCase);
+ request.MediaSourceId = val;
}
else if (i == 3)
+ {
+ request.Static = string.Equals("true", val, StringComparison.OrdinalIgnoreCase);
+ }
+ else if (i == 4)
{
if (videoRequest != null)
{
videoRequest.VideoCodec = val;
}
}
- else if (i == 4)
+ else if (i == 5)
{
request.AudioCodec = val;
}
- else if (i == 5)
+ else if (i == 6)
{
if (videoRequest != null)
{
videoRequest.AudioStreamIndex = int.Parse(val, UsCulture);
}
}
- else if (i == 6)
+ else if (i == 7)
{
if (videoRequest != null)
{
videoRequest.SubtitleStreamIndex = int.Parse(val, UsCulture);
}
}
- else if (i == 7)
+ else if (i == 8)
{
if (videoRequest != null)
{
videoRequest.VideoBitRate = int.Parse(val, UsCulture);
}
}
- else if (i == 8)
+ else if (i == 9)
{
request.AudioBitRate = int.Parse(val, UsCulture);
}
- else if (i == 9)
+ else if (i == 10)
{
request.MaxAudioChannels = int.Parse(val, UsCulture);
}
- else if (i == 10)
+ else if (i == 11)
{
if (videoRequest != null)
{
videoRequest.MaxWidth = int.Parse(val, UsCulture);
}
}
- else if (i == 11)
+ else if (i == 12)
{
if (videoRequest != null)
{
videoRequest.MaxHeight = int.Parse(val, UsCulture);
}
}
- else if (i == 12)
+ else if (i == 13)
{
if (videoRequest != null)
{
videoRequest.Framerate = int.Parse(val, UsCulture);
}
}
- else if (i == 13)
+ else if (i == 14)
{
if (videoRequest != null)
{
request.StartTimeTicks = long.Parse(val, UsCulture);
}
}
- else if (i == 14)
+ else if (i == 15)
{
if (videoRequest != null)
{
diff --git a/MediaBrowser.Api/Playback/StreamRequest.cs b/MediaBrowser.Api/Playback/StreamRequest.cs
index 8db5920f6b..0eb2984fb5 100644
--- a/MediaBrowser.Api/Playback/StreamRequest.cs
+++ b/MediaBrowser.Api/Playback/StreamRequest.cs
@@ -65,6 +65,9 @@ namespace MediaBrowser.Api.Playback
[ApiMember(Name = "Static", Description = "Optional. If true, the original file will be streamed statically without any encoding. Use either no url extension or the original file extension. true/false", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool Static { get; set; }
+ [ApiMember(Name = "DeviceProfileId", Description = "Optional. The dlna device profile id to utilize.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public string DeviceProfileId { get; set; }
+
///
/// For testing purposes
///
diff --git a/MediaBrowser.Controller/Dlna/CodecProfile.cs b/MediaBrowser.Controller/Dlna/CodecProfile.cs
index 0f61cad98d..75f80ed3b1 100644
--- a/MediaBrowser.Controller/Dlna/CodecProfile.cs
+++ b/MediaBrowser.Controller/Dlna/CodecProfile.cs
@@ -1,13 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Xml.Serialization;
namespace MediaBrowser.Controller.Dlna
{
public class CodecProfile
{
+ [XmlAttribute("type")]
public CodecType Type { get; set; }
+
public ProfileCondition[] Conditions { get; set; }
+
+ [XmlAttribute("codec")]
public string Codec { get; set; }
public CodecProfile()
@@ -37,9 +42,16 @@ namespace MediaBrowser.Controller.Dlna
public class ProfileCondition
{
+ [XmlAttribute("condition")]
public ProfileConditionType Condition { get; set; }
+
+ [XmlAttribute("property")]
public ProfileConditionValue Property { get; set; }
+
+ [XmlAttribute("value")]
public string Value { get; set; }
+
+ [XmlAttribute("isRequired")]
public bool IsRequired { get; set; }
public ProfileCondition()
diff --git a/MediaBrowser.Controller/Dlna/ContainerProfile.cs b/MediaBrowser.Controller/Dlna/ContainerProfile.cs
index 3bd3c9eaf6..1029ba72cb 100644
--- a/MediaBrowser.Controller/Dlna/ContainerProfile.cs
+++ b/MediaBrowser.Controller/Dlna/ContainerProfile.cs
@@ -1,12 +1,16 @@
using System.Collections.Generic;
using System.Linq;
+using System.Xml.Serialization;
namespace MediaBrowser.Controller.Dlna
{
public class ContainerProfile
{
+ [XmlAttribute("type")]
public DlnaProfileType Type { get; set; }
public ProfileCondition[] Conditions { get; set; }
+
+ [XmlAttribute("container")]
public string Container { get; set; }
public ContainerProfile()
diff --git a/MediaBrowser.Controller/Dlna/DeviceIdentification.cs b/MediaBrowser.Controller/Dlna/DeviceIdentification.cs
index 7b8e3a1e72..c9cd4bc703 100644
--- a/MediaBrowser.Controller/Dlna/DeviceIdentification.cs
+++ b/MediaBrowser.Controller/Dlna/DeviceIdentification.cs
@@ -1,4 +1,6 @@
+using System.Xml.Serialization;
+
namespace MediaBrowser.Controller.Dlna
{
public class DeviceIdentification
@@ -62,8 +64,13 @@ namespace MediaBrowser.Controller.Dlna
public class HttpHeaderInfo
{
+ [XmlAttribute("name")]
public string Name { get; set; }
+
+ [XmlAttribute("value")]
public string Value { get; set; }
+
+ [XmlAttribute("match")]
public HeaderMatchType Match { get; set; }
}
diff --git a/MediaBrowser.Controller/Dlna/DeviceProfile.cs b/MediaBrowser.Controller/Dlna/DeviceProfile.cs
index f34c4bf645..f5aff02629 100644
--- a/MediaBrowser.Controller/Dlna/DeviceProfile.cs
+++ b/MediaBrowser.Controller/Dlna/DeviceProfile.cs
@@ -1,9 +1,12 @@
using MediaBrowser.Model.Entities;
using System;
using System.Linq;
+using System.Runtime.Serialization;
+using System.Xml.Serialization;
namespace MediaBrowser.Controller.Dlna
{
+ [XmlRoot("Profile")]
public class DeviceProfile
{
///
@@ -12,19 +15,9 @@ namespace MediaBrowser.Controller.Dlna
/// The name.
public string Name { get; set; }
- ///
- /// Gets or sets the transcoding profiles.
- ///
- /// The transcoding profiles.
- public TranscodingProfile[] TranscodingProfiles { get; set; }
-
- ///
- /// Gets or sets the direct play profiles.
- ///
- /// The direct play profiles.
- public DirectPlayProfile[] DirectPlayProfiles { get; set; }
-
- public ContainerProfile[] ContainerProfiles { get; set; }
+ [XmlIgnore]
+ [IgnoreDataMember]
+ public string Id { get; set; }
///
/// Gets or sets the identification.
@@ -57,14 +50,27 @@ namespace MediaBrowser.Controller.Dlna
public string ProtocolInfo { get; set; }
- public MediaProfile[] MediaProfiles { get; set; }
- public CodecProfile[] CodecProfiles { get; set; }
-
public int TimelineOffsetSeconds { get; set; }
-
public bool RequiresPlainVideoItems { get; set; }
public bool RequiresPlainFolders { get; set; }
+ ///
+ /// Gets or sets the direct play profiles.
+ ///
+ /// The direct play profiles.
+ public DirectPlayProfile[] DirectPlayProfiles { get; set; }
+
+ ///
+ /// Gets or sets the transcoding profiles.
+ ///
+ /// The transcoding profiles.
+ public TranscodingProfile[] TranscodingProfiles { get; set; }
+
+ public ContainerProfile[] ContainerProfiles { get; set; }
+
+ public CodecProfile[] CodecProfiles { get; set; }
+ public MediaProfile[] MediaProfiles { get; set; }
+
public DeviceProfile()
{
DirectPlayProfiles = new DirectPlayProfile[] { };
diff --git a/MediaBrowser.Controller/Dlna/DirectPlayProfile.cs b/MediaBrowser.Controller/Dlna/DirectPlayProfile.cs
index 686b31287e..ad70640daa 100644
--- a/MediaBrowser.Controller/Dlna/DirectPlayProfile.cs
+++ b/MediaBrowser.Controller/Dlna/DirectPlayProfile.cs
@@ -1,14 +1,21 @@
using System.Collections.Generic;
using System.Linq;
+using System.Xml.Serialization;
namespace MediaBrowser.Controller.Dlna
{
public class DirectPlayProfile
{
+ [XmlAttribute("container")]
public string Container { get; set; }
+
+ [XmlAttribute("audioCodec")]
public string AudioCodec { get; set; }
+
+ [XmlAttribute("videoCodec")]
public string VideoCodec { get; set; }
+ [XmlAttribute("type")]
public DlnaProfileType Type { get; set; }
public List GetContainers()
diff --git a/MediaBrowser.Controller/Dlna/IDlnaManager.cs b/MediaBrowser.Controller/Dlna/IDlnaManager.cs
index 22d13fc3ad..dd9b0e8a84 100644
--- a/MediaBrowser.Controller/Dlna/IDlnaManager.cs
+++ b/MediaBrowser.Controller/Dlna/IDlnaManager.cs
@@ -1,20 +1,15 @@
-using System.Collections.Generic;
+using MediaBrowser.Model.Dlna;
+using System.Collections.Generic;
namespace MediaBrowser.Controller.Dlna
{
public interface IDlnaManager
{
///
- /// Gets the dlna profiles.
+ /// Gets the profile infos.
///
- /// IEnumerable{DlnaProfile}.
- IEnumerable GetProfiles();
-
- ///
- /// Gets the default profile.
- ///
- /// DlnaProfile.
- DeviceProfile GetDefaultProfile();
+ /// IEnumerable{DeviceProfileInfo}.
+ IEnumerable GetProfileInfos();
///
/// Gets the profile.
@@ -23,6 +18,13 @@ namespace MediaBrowser.Controller.Dlna
/// DeviceProfile.
DeviceProfile GetProfile(IDictionary headers);
+ ///
+ /// Gets the profile.
+ ///
+ /// The identifier.
+ /// DeviceProfile.
+ DeviceProfile GetProfile(string id);
+
///
/// Gets the profile.
///
diff --git a/MediaBrowser.Controller/Dlna/MediaProfile.cs b/MediaBrowser.Controller/Dlna/MediaProfile.cs
index 9a9b56ddd5..bf3057294c 100644
--- a/MediaBrowser.Controller/Dlna/MediaProfile.cs
+++ b/MediaBrowser.Controller/Dlna/MediaProfile.cs
@@ -1,16 +1,27 @@
using System.Collections.Generic;
using System.Linq;
+using System.Xml.Serialization;
namespace MediaBrowser.Controller.Dlna
{
public class MediaProfile
{
+ [XmlAttribute("container")]
public string Container { get; set; }
+
+ [XmlAttribute("audioCodec")]
public string AudioCodec { get; set; }
+
+ [XmlAttribute("videoCodec")]
public string VideoCodec { get; set; }
+ [XmlAttribute("type")]
public DlnaProfileType Type { get; set; }
+
+ [XmlAttribute("orgPn")]
public string OrgPn { get; set; }
+
+ [XmlAttribute("mimeType")]
public string MimeType { get; set; }
public ProfileCondition[] Conditions { get; set; }
diff --git a/MediaBrowser.Controller/Dlna/TranscodingProfile.cs b/MediaBrowser.Controller/Dlna/TranscodingProfile.cs
index d4cfae9893..289333aa77 100644
--- a/MediaBrowser.Controller/Dlna/TranscodingProfile.cs
+++ b/MediaBrowser.Controller/Dlna/TranscodingProfile.cs
@@ -1,19 +1,30 @@
using System.Collections.Generic;
using System.Linq;
+using System.Xml.Serialization;
namespace MediaBrowser.Controller.Dlna
{
public class TranscodingProfile
{
+ [XmlAttribute("container")]
public string Container { get; set; }
+ [XmlAttribute("type")]
public DlnaProfileType Type { get; set; }
+ [XmlAttribute("videoCodec")]
public string VideoCodec { get; set; }
+
+ [XmlAttribute("audioCodec")]
public string AudioCodec { get; set; }
+ [XmlAttribute("estimateContentLength")]
public bool EstimateContentLength { get; set; }
+
+ [XmlAttribute("enableMpegtsM2TsMode")]
public bool EnableMpegtsM2TsMode { get; set; }
+
+ [XmlAttribute("transcodeSeekInfo")]
public TranscodeSeekInfo TranscodeSeekInfo { get; set; }
public TranscodingSetting[] Settings { get; set; }
@@ -32,7 +43,10 @@ namespace MediaBrowser.Controller.Dlna
public class TranscodingSetting
{
+ [XmlAttribute("name")]
public TranscodingSettingType Name { get; set; }
+
+ [XmlAttribute("value")]
public string Value { get; set; }
}
diff --git a/MediaBrowser.Dlna/DlnaManager.cs b/MediaBrowser.Dlna/DlnaManager.cs
index 78876d239c..9d9df01e6d 100644
--- a/MediaBrowser.Dlna/DlnaManager.cs
+++ b/MediaBrowser.Dlna/DlnaManager.cs
@@ -1,10 +1,14 @@
using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Dlna.Profiles;
+using MediaBrowser.Model.Dlna;
+using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;
using System;
using System.Collections.Generic;
+using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
@@ -12,21 +16,36 @@ namespace MediaBrowser.Dlna
{
public class DlnaManager : IDlnaManager
{
- private IApplicationPaths _appPaths;
+ private readonly IApplicationPaths _appPaths;
private readonly IXmlSerializer _xmlSerializer;
private readonly IFileSystem _fileSystem;
- private readonly IJsonSerializer _jsonSerializer;
+ private readonly ILogger _logger;
- public DlnaManager(IXmlSerializer xmlSerializer, IFileSystem fileSystem, IJsonSerializer jsonSerializer)
+ public DlnaManager(IXmlSerializer xmlSerializer, IFileSystem fileSystem, IApplicationPaths appPaths, ILogger logger)
{
_xmlSerializer = xmlSerializer;
_fileSystem = fileSystem;
- _jsonSerializer = jsonSerializer;
+ _appPaths = appPaths;
+ _logger = logger;
- GetProfiles();
+ //DumpProfiles();
}
public IEnumerable GetProfiles()
+ {
+ ExtractProfilesIfNeeded();
+
+ var list = GetProfiles(UserProfilesPath)
+ .OrderBy(i => i.Name)
+ .ToList();
+
+ list.AddRange(GetProfiles(SystemProfilesPath)
+ .OrderBy(i => i.Name));
+
+ return list;
+ }
+
+ private void DumpProfiles()
{
var list = new List
{
@@ -45,16 +64,40 @@ namespace MediaBrowser.Dlna
new DenonAvrProfile(),
new LinksysDMA2100Profile(),
new LgTvProfile(),
- new Foobar2000Profile()
+ new Foobar2000Profile(),
+ new DefaultProfile()
};
foreach (var item in list)
{
- //_xmlSerializer.SerializeToFile(item, "d:\\" + _fileSystem.GetValidFilename(item.Name) + ".xml");
- //_jsonSerializer.SerializeToFile(item, "d:\\" + _fileSystem.GetValidFilename(item.Name) + ".json");
+ _xmlSerializer.SerializeToFile(item, "d:\\" + _fileSystem.GetValidFilename(item.Name) + ".xml");
}
+ }
- return list;
+ private bool _extracted;
+ private readonly object _syncLock = new object();
+ private void ExtractProfilesIfNeeded()
+ {
+ if (!_extracted)
+ {
+ lock (_syncLock)
+ {
+ if (!_extracted)
+ {
+ try
+ {
+ ExtractSystemProfiles();
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error extracting DLNA profiles.", ex);
+ }
+
+ _extracted = true;
+ }
+
+ }
+ }
}
public DeviceProfile GetDefaultProfile()
@@ -64,8 +107,12 @@ namespace MediaBrowser.Dlna
public DeviceProfile GetProfile(DeviceIdentification deviceInfo)
{
- return GetProfiles().FirstOrDefault(i => IsMatch(deviceInfo, i.Identification)) ??
+ var profile = GetProfiles().FirstOrDefault(i => IsMatch(deviceInfo, i.Identification)) ??
GetDefaultProfile();
+
+ _logger.Debug("Found matching device profile: {0}", profile.Name);
+
+ return profile;
}
private bool IsMatch(DeviceIdentification deviceInfo, DeviceIdentification profileInfo)
@@ -159,5 +206,145 @@ namespace MediaBrowser.Dlna
return false;
}
+
+ private string UserProfilesPath
+ {
+ get
+ {
+ return Path.Combine(_appPaths.ConfigurationDirectoryPath, "dlna", "user");
+ }
+ }
+
+ private string SystemProfilesPath
+ {
+ get
+ {
+ return Path.Combine(_appPaths.ConfigurationDirectoryPath, "dlna", "system");
+ }
+ }
+
+ private IEnumerable GetProfiles(string path)
+ {
+ try
+ {
+ return new DirectoryInfo(path)
+ .EnumerateFiles("*", SearchOption.TopDirectoryOnly)
+ .Where(i => string.Equals(i.Extension, ".xml", StringComparison.OrdinalIgnoreCase))
+ .Select(i => ParseProfileXmlFile(i.FullName))
+ .Where(i => i != null)
+ .ToList();
+ }
+ catch (DirectoryNotFoundException)
+ {
+ return new List();
+ }
+ }
+
+ private DeviceProfile ParseProfileXmlFile(string path)
+ {
+ try
+ {
+ var profile = (DeviceProfile)_xmlSerializer.DeserializeFromFile(typeof(DeviceProfile), path);
+
+ profile.Id = path.ToLower().GetMD5().ToString("N");
+
+ return profile;
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error parsing profile xml: {0}", ex, path);
+
+ return null;
+ }
+ }
+
+ public DeviceProfile GetProfile(string id)
+ {
+ var info = GetProfileInfosInternal().First(i => string.Equals(i.Info.Id, id));
+
+ return ParseProfileXmlFile(info.Path);
+ }
+
+ private IEnumerable GetProfileInfosInternal()
+ {
+ ExtractProfilesIfNeeded();
+
+ return GetProfileInfos(UserProfilesPath, DeviceProfileType.User)
+ .Concat(GetProfileInfos(SystemProfilesPath, DeviceProfileType.System))
+ .OrderBy(i => i.Info.Type == DeviceProfileType.User ? 0 : 1)
+ .ThenBy(i => i.Info.Name);
+ }
+
+ public IEnumerable GetProfileInfos()
+ {
+ return GetProfileInfosInternal().Select(i => i.Info);
+ }
+
+ private IEnumerable GetProfileInfos(string path, DeviceProfileType type)
+ {
+ try
+ {
+ return new DirectoryInfo(path)
+ .EnumerateFiles("*", SearchOption.TopDirectoryOnly)
+ .Where(i => string.Equals(i.Extension, ".xml", StringComparison.OrdinalIgnoreCase))
+ .Select(i => new InternalProfileInfo
+ {
+ Path = i.FullName,
+
+ Info = new DeviceProfileInfo
+ {
+ Id = i.FullName.ToLower().GetMD5().ToString("N"),
+ Name = Path.GetFileNameWithoutExtension(i.FullName),
+ Type = type
+ }
+ })
+ .ToList();
+ }
+ catch (DirectoryNotFoundException)
+ {
+ return new List();
+ }
+ }
+
+ private void ExtractSystemProfiles()
+ {
+ var assembly = GetType().Assembly;
+ var namespaceName = GetType().Namespace + ".Profiles.Xml.";
+
+ var systemProfilesPath = SystemProfilesPath;
+
+ foreach (var name in assembly.GetManifestResourceNames()
+ .Where(i => i.StartsWith(namespaceName))
+ .ToList())
+ {
+ var filename = Path.GetFileName(name).Substring(namespaceName.Length);
+
+ var path = Path.Combine(systemProfilesPath, filename);
+
+ using (var stream = assembly.GetManifestResourceStream(name))
+ {
+ var fileInfo = new FileInfo(path);
+
+ if (!fileInfo.Exists || fileInfo.Length != stream.Length)
+ {
+ Directory.CreateDirectory(systemProfilesPath);
+
+ using (var fileStream = _fileSystem.GetFileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read))
+ {
+ stream.CopyTo(fileStream);
+ }
+ }
+ }
+ }
+
+ // Not necessary, but just to make it easy to find
+ Directory.CreateDirectory(UserProfilesPath);
+ }
+
+ class InternalProfileInfo
+ {
+ internal DeviceProfileInfo Info { get; set; }
+ internal string Path { get; set; }
+ }
}
}
\ No newline at end of file
diff --git a/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj b/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj
index bdfcae39b6..df1fed12f0 100644
--- a/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj
+++ b/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj
@@ -117,7 +117,27 @@
MediaBrowser.Model
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+