|
@@ -1,10 +1,14 @@
|
|
using MediaBrowser.Common.Configuration;
|
|
using MediaBrowser.Common.Configuration;
|
|
|
|
+using MediaBrowser.Common.Extensions;
|
|
using MediaBrowser.Common.IO;
|
|
using MediaBrowser.Common.IO;
|
|
using MediaBrowser.Controller.Dlna;
|
|
using MediaBrowser.Controller.Dlna;
|
|
using MediaBrowser.Dlna.Profiles;
|
|
using MediaBrowser.Dlna.Profiles;
|
|
|
|
+using MediaBrowser.Model.Dlna;
|
|
|
|
+using MediaBrowser.Model.Logging;
|
|
using MediaBrowser.Model.Serialization;
|
|
using MediaBrowser.Model.Serialization;
|
|
using System;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.Generic;
|
|
|
|
+using System.IO;
|
|
using System.Linq;
|
|
using System.Linq;
|
|
using System.Text.RegularExpressions;
|
|
using System.Text.RegularExpressions;
|
|
|
|
|
|
@@ -12,21 +16,36 @@ namespace MediaBrowser.Dlna
|
|
{
|
|
{
|
|
public class DlnaManager : IDlnaManager
|
|
public class DlnaManager : IDlnaManager
|
|
{
|
|
{
|
|
- private IApplicationPaths _appPaths;
|
|
|
|
|
|
+ private readonly IApplicationPaths _appPaths;
|
|
private readonly IXmlSerializer _xmlSerializer;
|
|
private readonly IXmlSerializer _xmlSerializer;
|
|
private readonly IFileSystem _fileSystem;
|
|
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;
|
|
_xmlSerializer = xmlSerializer;
|
|
_fileSystem = fileSystem;
|
|
_fileSystem = fileSystem;
|
|
- _jsonSerializer = jsonSerializer;
|
|
|
|
|
|
+ _appPaths = appPaths;
|
|
|
|
+ _logger = logger;
|
|
|
|
|
|
- GetProfiles();
|
|
|
|
|
|
+ //DumpProfiles();
|
|
}
|
|
}
|
|
|
|
|
|
public IEnumerable<DeviceProfile> GetProfiles()
|
|
public IEnumerable<DeviceProfile> 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<DeviceProfile>
|
|
var list = new List<DeviceProfile>
|
|
{
|
|
{
|
|
@@ -45,16 +64,40 @@ namespace MediaBrowser.Dlna
|
|
new DenonAvrProfile(),
|
|
new DenonAvrProfile(),
|
|
new LinksysDMA2100Profile(),
|
|
new LinksysDMA2100Profile(),
|
|
new LgTvProfile(),
|
|
new LgTvProfile(),
|
|
- new Foobar2000Profile()
|
|
|
|
|
|
+ new Foobar2000Profile(),
|
|
|
|
+ new DefaultProfile()
|
|
};
|
|
};
|
|
|
|
|
|
foreach (var item in list)
|
|
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()
|
|
public DeviceProfile GetDefaultProfile()
|
|
@@ -64,8 +107,12 @@ namespace MediaBrowser.Dlna
|
|
|
|
|
|
public DeviceProfile GetProfile(DeviceIdentification deviceInfo)
|
|
public DeviceProfile GetProfile(DeviceIdentification deviceInfo)
|
|
{
|
|
{
|
|
- return GetProfiles().FirstOrDefault(i => IsMatch(deviceInfo, i.Identification)) ??
|
|
|
|
|
|
+ var profile = GetProfiles().FirstOrDefault(i => IsMatch(deviceInfo, i.Identification)) ??
|
|
GetDefaultProfile();
|
|
GetDefaultProfile();
|
|
|
|
+
|
|
|
|
+ _logger.Debug("Found matching device profile: {0}", profile.Name);
|
|
|
|
+
|
|
|
|
+ return profile;
|
|
}
|
|
}
|
|
|
|
|
|
private bool IsMatch(DeviceIdentification deviceInfo, DeviceIdentification profileInfo)
|
|
private bool IsMatch(DeviceIdentification deviceInfo, DeviceIdentification profileInfo)
|
|
@@ -159,5 +206,145 @@ namespace MediaBrowser.Dlna
|
|
|
|
|
|
return false;
|
|
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<DeviceProfile> 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<DeviceProfile>();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ 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<InternalProfileInfo> 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<DeviceProfileInfo> GetProfileInfos()
|
|
|
|
+ {
|
|
|
|
+ return GetProfileInfosInternal().Select(i => i.Info);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private IEnumerable<InternalProfileInfo> 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<InternalProfileInfo>();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ 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; }
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|