jellyfin-server/Emby.Dlna/Main/DlnaEntryPoint.cs

478 lines
16 KiB
C#
Raw Normal View History

#pragma warning disable CS1591
using System;
using System.Globalization;
2020-09-12 15:41:37 +00:00
using System.Linq;
using System.Net.Http;
using System.Net.Sockets;
2019-01-13 19:16:19 +00:00
using System.Threading;
using System.Threading.Tasks;
using Emby.Dlna.PlayTo;
using Emby.Dlna.Ssdp;
2020-12-07 22:06:28 +00:00
using Jellyfin.Networking.Configuration;
2020-10-31 18:21:46 +00:00
using Jellyfin.Networking.Manager;
2019-01-13 19:16:19 +00:00
using MediaBrowser.Common.Configuration;
2016-10-29 22:22:20 +00:00
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Library;
2019-01-13 19:16:19 +00:00
using MediaBrowser.Controller.MediaEncoding;
2016-10-29 22:22:20 +00:00
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Controller.Session;
2018-09-12 17:26:21 +00:00
using MediaBrowser.Controller.TV;
2016-10-29 22:22:20 +00:00
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Globalization;
2016-11-04 08:31:05 +00:00
using MediaBrowser.Model.Net;
2016-11-04 19:51:59 +00:00
using MediaBrowser.Model.System;
2019-01-13 19:16:19 +00:00
using Microsoft.Extensions.Logging;
2016-10-29 22:22:20 +00:00
using Rssdp;
using Rssdp.Infrastructure;
using OperatingSystem = MediaBrowser.Common.System.OperatingSystem;
2016-10-29 22:22:20 +00:00
2016-10-29 22:34:54 +00:00
namespace Emby.Dlna.Main
2016-10-29 22:22:20 +00:00
{
public sealed class DlnaEntryPoint : IServerEntryPoint, IRunBeforeStartup
2016-10-29 22:22:20 +00:00
{
private readonly IServerConfigurationManager _config;
2020-06-06 00:15:56 +00:00
private readonly ILogger<DlnaEntryPoint> _logger;
2016-10-29 22:22:20 +00:00
private readonly IServerApplicationHost _appHost;
private readonly ISessionManager _sessionManager;
private readonly IHttpClientFactory _httpClientFactory;
2016-10-29 22:22:20 +00:00
private readonly ILibraryManager _libraryManager;
private readonly IUserManager _userManager;
private readonly IDlnaManager _dlnaManager;
private readonly IImageProcessor _imageProcessor;
private readonly IUserDataManager _userDataManager;
private readonly ILocalizationManager _localization;
private readonly IMediaSourceManager _mediaSourceManager;
private readonly IMediaEncoder _mediaEncoder;
private readonly IDeviceDiscovery _deviceDiscovery;
2016-11-04 08:31:05 +00:00
private readonly ISocketFactory _socketFactory;
2016-12-04 21:30:38 +00:00
private readonly INetworkManager _networkManager;
private readonly object _syncLock = new object();
2020-12-07 22:06:28 +00:00
private readonly NetworkConfiguration _netConfig;
private readonly bool _disabled;
2016-11-04 08:31:05 +00:00
private PlayToManager _manager;
private SsdpDevicePublisher _publisher;
2016-11-14 19:48:01 +00:00
private ISsdpCommunicationsServer _communicationsServer;
2020-08-20 15:01:04 +00:00
private bool _disposed;
2018-09-12 17:26:21 +00:00
public DlnaEntryPoint(
IServerConfigurationManager config,
ILoggerFactory loggerFactory,
2016-10-29 22:22:20 +00:00
IServerApplicationHost appHost,
ISessionManager sessionManager,
IHttpClientFactory httpClientFactory,
2016-10-29 22:22:20 +00:00
ILibraryManager libraryManager,
IUserManager userManager,
IDlnaManager dlnaManager,
IImageProcessor imageProcessor,
IUserDataManager userDataManager,
2018-09-12 17:26:21 +00:00
ILocalizationManager localizationManager,
2016-10-29 22:22:20 +00:00
IMediaSourceManager mediaSourceManager,
2019-01-07 23:27:46 +00:00
IDeviceDiscovery deviceDiscovery,
IMediaEncoder mediaEncoder,
ISocketFactory socketFactory,
2018-09-12 17:26:21 +00:00
INetworkManager networkManager,
IUserViewManager userViewManager,
ITVSeriesManager tvSeriesManager)
2016-10-29 22:22:20 +00:00
{
_config = config;
_appHost = appHost;
_sessionManager = sessionManager;
_httpClientFactory = httpClientFactory;
2016-10-29 22:22:20 +00:00
_libraryManager = libraryManager;
_userManager = userManager;
_dlnaManager = dlnaManager;
_imageProcessor = imageProcessor;
_userDataManager = userDataManager;
2018-09-12 17:26:21 +00:00
_localization = localizationManager;
2016-10-29 22:22:20 +00:00
_mediaSourceManager = mediaSourceManager;
_deviceDiscovery = deviceDiscovery;
_mediaEncoder = mediaEncoder;
2016-11-04 08:31:05 +00:00
_socketFactory = socketFactory;
2016-12-04 21:30:38 +00:00
_networkManager = networkManager;
2020-06-06 00:15:56 +00:00
_logger = loggerFactory.CreateLogger<DlnaEntryPoint>();
2018-09-12 17:26:21 +00:00
2020-08-20 19:04:57 +00:00
ContentDirectory = new ContentDirectory.ContentDirectoryService(
dlnaManager,
2019-01-07 23:27:46 +00:00
userDataManager,
imageProcessor,
libraryManager,
config,
userManager,
2020-08-20 19:04:57 +00:00
loggerFactory.CreateLogger<ContentDirectory.ContentDirectoryService>(),
httpClientFactory,
2019-01-07 23:27:46 +00:00
localizationManager,
mediaSourceManager,
2018-09-12 17:26:21 +00:00
userViewManager,
2019-01-07 23:27:46 +00:00
mediaEncoder,
2018-09-12 17:26:21 +00:00
tvSeriesManager);
2020-08-20 19:04:57 +00:00
ConnectionManager = new ConnectionManager.ConnectionManagerService(
dlnaManager,
config,
2020-08-20 19:04:57 +00:00
loggerFactory.CreateLogger<ConnectionManager.ConnectionManagerService>(),
httpClientFactory);
2018-09-12 17:26:21 +00:00
2020-08-20 19:04:57 +00:00
MediaReceiverRegistrar = new MediaReceiverRegistrar.MediaReceiverRegistrarService(
loggerFactory.CreateLogger<MediaReceiverRegistrar.MediaReceiverRegistrarService>(),
httpClientFactory,
config);
2018-09-12 17:26:21 +00:00
Current = this;
2020-12-07 22:06:28 +00:00
_netConfig = config.GetConfiguration<NetworkConfiguration>("network");
_disabled = appHost.ListenWithHttps && _netConfig.RequireHttps;
if (_disabled)
{
_logger.LogError("The DLNA specification does not support HTTPS.");
}
2016-10-29 22:22:20 +00:00
}
2020-08-20 19:04:57 +00:00
2020-08-20 16:50:15 +00:00
public static DlnaEntryPoint Current { get; private set; }
2016-10-29 22:22:20 +00:00
2020-12-18 23:29:21 +00:00
/// <summary>
/// Gets a value indicating whether the dlna server is enabled.
/// </summary>
public static bool Enabled { get; private set; }
2020-08-20 15:01:04 +00:00
public IContentDirectory ContentDirectory { get; private set; }
public IConnectionManager ConnectionManager { get; private set; }
public IMediaReceiverRegistrar MediaReceiverRegistrar { get; private set; }
2016-10-29 22:22:20 +00:00
2019-01-27 14:40:37 +00:00
public async Task RunAsync()
2016-10-29 22:22:20 +00:00
{
2019-01-27 14:40:37 +00:00
await ((DlnaManager)_dlnaManager).InitProfilesAsync().ConfigureAwait(false);
2016-10-29 22:22:20 +00:00
2020-12-07 22:06:28 +00:00
if (_disabled)
{
// No use starting as dlna won't work, as we're running purely on HTTPS.
return;
}
2020-09-12 15:41:37 +00:00
ReloadComponents();
2016-10-29 22:22:20 +00:00
2020-06-09 21:05:22 +00:00
_config.NamedConfigurationUpdated += OnNamedConfigurationUpdated;
2016-10-29 22:22:20 +00:00
}
2020-09-12 15:41:37 +00:00
private void OnNamedConfigurationUpdated(object sender, ConfigurationUpdateEventArgs e)
2016-10-29 22:22:20 +00:00
{
if (string.Equals(e.Key, "dlna", StringComparison.OrdinalIgnoreCase))
{
2020-09-12 15:41:37 +00:00
ReloadComponents();
2016-10-29 22:22:20 +00:00
}
}
2020-09-12 15:41:37 +00:00
private void ReloadComponents()
2016-10-29 22:22:20 +00:00
{
var options = _config.GetDlnaConfiguration();
2020-12-18 23:29:21 +00:00
Enabled = options.EnableServer;
2016-10-29 22:22:20 +00:00
2018-09-12 17:26:21 +00:00
StartSsdpHandler();
2016-10-29 22:22:20 +00:00
2018-09-12 17:26:21 +00:00
if (options.EnableServer)
2016-10-29 22:22:20 +00:00
{
2020-09-12 15:41:37 +00:00
StartDevicePublisher(options);
2016-10-29 22:22:20 +00:00
}
2018-09-12 17:26:21 +00:00
else
2016-10-29 22:22:20 +00:00
{
2018-09-12 17:26:21 +00:00
DisposeDevicePublisher();
2016-10-29 22:22:20 +00:00
}
2018-09-12 17:26:21 +00:00
if (options.EnablePlayTo)
2016-10-29 22:22:20 +00:00
{
StartPlayToManager();
}
2018-09-12 17:26:21 +00:00
else
2016-10-29 22:22:20 +00:00
{
DisposePlayToManager();
}
}
private void StartSsdpHandler()
{
try
{
2016-11-14 19:48:01 +00:00
if (_communicationsServer == null)
{
var enableMultiSocketBinding = OperatingSystem.Id == OperatingSystemId.Windows ||
OperatingSystem.Id == OperatingSystemId.Linux;
2016-12-05 18:46:38 +00:00
_communicationsServer = new SsdpCommunicationsServer(_socketFactory, _networkManager, _logger, enableMultiSocketBinding)
2016-11-14 19:48:01 +00:00
{
IsShared = true
};
2016-10-29 22:22:20 +00:00
2018-09-12 17:26:21 +00:00
StartDeviceDiscovery(_communicationsServer);
}
2016-10-29 22:22:20 +00:00
}
catch (Exception ex)
{
2018-12-20 12:11:26 +00:00
_logger.LogError(ex, "Error starting ssdp handlers");
2016-10-29 22:22:20 +00:00
}
}
private void LogMessage(string msg)
{
_logger.LogDebug(msg);
2016-10-29 22:22:20 +00:00
}
2016-11-14 19:48:01 +00:00
private void StartDeviceDiscovery(ISsdpCommunicationsServer communicationsServer)
2016-10-29 22:22:20 +00:00
{
try
{
2016-11-14 19:48:01 +00:00
((DeviceDiscovery)_deviceDiscovery).Start(communicationsServer);
2016-10-29 22:22:20 +00:00
}
catch (Exception ex)
{
2018-12-20 12:11:26 +00:00
_logger.LogError(ex, "Error starting device discovery");
2016-10-29 22:22:20 +00:00
}
}
private void DisposeDeviceDiscovery()
{
try
{
_logger.LogInformation("Disposing DeviceDiscovery");
2016-10-29 22:22:20 +00:00
((DeviceDiscovery)_deviceDiscovery).Dispose();
}
catch (Exception ex)
{
2018-12-20 12:11:26 +00:00
_logger.LogError(ex, "Error stopping device discovery");
2016-10-29 22:22:20 +00:00
}
}
2020-09-12 15:41:37 +00:00
public void StartDevicePublisher(Configuration.DlnaOptions options)
2016-10-29 22:22:20 +00:00
{
2018-09-12 17:26:21 +00:00
if (!options.BlastAliveMessages)
2016-10-29 22:22:20 +00:00
{
2018-09-12 17:26:21 +00:00
return;
2016-10-29 22:22:20 +00:00
}
2018-09-12 17:26:21 +00:00
2020-06-09 21:05:22 +00:00
if (_publisher != null)
2016-10-29 22:22:20 +00:00
{
2018-09-12 17:26:21 +00:00
return;
2016-10-29 22:22:20 +00:00
}
try
{
2020-06-09 21:05:22 +00:00
_publisher = new SsdpDevicePublisher(_communicationsServer, _networkManager, OperatingSystem.Name, Environment.OSVersion.VersionString, _config.GetDlnaConfiguration().SendOnlyMatchedHost)
{
LogFunction = LogMessage,
SupportPnpRootDevice = false
};
2018-09-12 17:26:21 +00:00
2020-09-12 15:41:37 +00:00
RegisterServerEndpoints();
2016-10-29 22:22:20 +00:00
2020-06-09 21:05:22 +00:00
_publisher.StartBroadcastingAliveMessages(TimeSpan.FromSeconds(options.BlastAliveMessageIntervalSeconds));
2016-10-29 22:22:20 +00:00
}
catch (Exception ex)
{
2018-12-20 12:11:26 +00:00
_logger.LogError(ex, "Error registering endpoint");
2016-10-29 22:22:20 +00:00
}
}
2020-09-12 15:41:37 +00:00
private void RegisterServerEndpoints()
2016-10-29 22:22:20 +00:00
{
2016-11-04 23:57:21 +00:00
var udn = CreateUuid(_appHost.SystemId);
2020-11-06 15:15:30 +00:00
var descriptorUri = "/dlna/" + udn + "/description.xml";
2016-11-04 23:57:21 +00:00
2020-10-31 18:21:46 +00:00
var bindAddresses = NetworkManager.CreateCollection(
2020-09-24 14:43:06 +00:00
_networkManager.GetInternalBindAddresses()
.Where(i => i.AddressFamily == AddressFamily.InterNetwork || (i.AddressFamily == AddressFamily.InterNetworkV6 && i.Address.ScopeId != 0)));
2020-09-24 15:02:29 +00:00
if (bindAddresses.Count == 0)
2016-10-29 22:22:20 +00:00
{
2020-09-12 15:41:37 +00:00
// No interfaces returned, so use loopback.
2020-09-24 15:02:29 +00:00
bindAddresses = _networkManager.GetLoopbacks();
2020-09-12 15:41:37 +00:00
}
foreach (IPNetAddress address in bindAddresses)
2020-09-12 15:41:37 +00:00
{
if (address.AddressFamily == AddressFamily.InterNetworkV6)
{
// Not supporting IPv6 right now
2020-03-24 15:12:06 +00:00
continue;
}
2016-10-29 22:22:20 +00:00
// Limit to LAN addresses only
if (!_networkManager.IsInLocalNetwork(address))
{
continue;
}
2016-10-29 22:22:20 +00:00
var fullService = "urn:schemas-upnp-org:device:MediaServer:1";
_logger.LogInformation("Registering publisher for {0} on {1}", fullService, address);
2016-10-29 22:22:20 +00:00
2020-12-07 22:06:28 +00:00
var uri = new UriBuilder(_appHost.GetSmartApiUrl(address.Address) + descriptorUri);
// DLNA will only work over http, so we must reset to http:// : {port}
uri.Scheme = "http://";
2021-01-07 17:17:22 +00:00
uri.Port = _netConfig.HttpServerPortNumber;
2016-10-29 22:22:20 +00:00
var device = new SsdpRootDevice
{
CacheLifetime = TimeSpan.FromSeconds(1800), // How long SSDP clients can cache this info.
2020-12-07 22:06:28 +00:00
Location = uri.Uri, // Must point to the URL that serves your devices UPnP description document.
Address = address.Address,
2020-10-30 14:06:11 +00:00
PrefixLength = address.PrefixLength,
2018-12-13 09:18:29 +00:00
FriendlyName = "Jellyfin",
Manufacturer = "Jellyfin",
2018-12-13 19:10:22 +00:00
ModelName = "Jellyfin Server",
2016-10-29 22:22:20 +00:00
Uuid = udn
2019-01-07 23:27:46 +00:00
// This must be a globally unique value that survives reboots etc. Get from storage or embedded hardware etc.
2016-10-29 22:22:20 +00:00
};
SetProperies(device, fullService);
_publisher.AddDevice(device);
2016-10-29 22:22:20 +00:00
2019-01-13 19:16:19 +00:00
var embeddedDevices = new[]
2016-10-29 22:22:20 +00:00
{
"urn:schemas-upnp-org:service:ContentDirectory:1",
"urn:schemas-upnp-org:service:ConnectionManager:1",
// "urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1"
2016-10-29 22:22:20 +00:00
};
foreach (var subDevice in embeddedDevices)
{
var embeddedDevice = new SsdpEmbeddedDevice
{
FriendlyName = device.FriendlyName,
Manufacturer = device.Manufacturer,
ModelName = device.ModelName,
Uuid = udn
2019-01-07 23:27:46 +00:00
// This must be a globally unique value that survives reboots etc. Get from storage or embedded hardware etc.
2016-10-29 22:22:20 +00:00
};
SetProperies(embeddedDevice, subDevice);
device.AddDevice(embeddedDevice);
}
}
}
private string CreateUuid(string text)
{
if (!Guid.TryParse(text, out var guid))
2016-11-04 23:57:21 +00:00
{
guid = text.GetMD5();
}
return guid.ToString("N", CultureInfo.InvariantCulture);
2016-10-29 22:22:20 +00:00
}
private void SetProperies(SsdpDevice device, string fullDeviceType)
{
var service = fullDeviceType.Replace("urn:", string.Empty, StringComparison.OrdinalIgnoreCase).Replace(":1", string.Empty, StringComparison.OrdinalIgnoreCase);
2016-10-29 22:22:20 +00:00
var serviceParts = service.Split(':');
var deviceTypeNamespace = serviceParts[0].Replace('.', '-');
device.DeviceTypeNamespace = deviceTypeNamespace;
device.DeviceClass = serviceParts[1];
device.DeviceType = serviceParts[2];
}
private void StartPlayToManager()
{
lock (_syncLock)
{
2018-09-12 17:26:21 +00:00
if (_manager != null)
{
return;
}
2016-10-29 22:22:20 +00:00
try
{
_manager = new PlayToManager(
_logger,
2016-10-29 22:22:20 +00:00
_sessionManager,
_libraryManager,
_userManager,
_dlnaManager,
_appHost,
_imageProcessor,
_deviceDiscovery,
_httpClientFactory,
2016-10-29 22:22:20 +00:00
_config,
_userDataManager,
_localization,
_mediaSourceManager,
2019-02-05 08:49:46 +00:00
_mediaEncoder);
2016-10-29 22:22:20 +00:00
_manager.Start();
}
catch (Exception ex)
{
2018-12-20 12:11:26 +00:00
_logger.LogError(ex, "Error starting PlayTo manager");
2016-10-29 22:22:20 +00:00
}
}
}
private void DisposePlayToManager()
{
lock (_syncLock)
{
if (_manager != null)
{
try
{
_logger.LogInformation("Disposing PlayToManager");
2016-10-29 22:22:20 +00:00
_manager.Dispose();
}
catch (Exception ex)
{
2018-12-20 12:11:26 +00:00
_logger.LogError(ex, "Error disposing PlayTo manager");
2016-10-29 22:22:20 +00:00
}
2016-10-29 22:22:20 +00:00
_manager = null;
}
}
}
2020-08-20 15:01:04 +00:00
public void DisposeDevicePublisher()
{
if (_publisher != null)
{
_logger.LogInformation("Disposing SsdpDevicePublisher");
_publisher.Dispose();
_publisher = null;
}
}
/// <inheritdoc />
2016-10-29 22:22:20 +00:00
public void Dispose()
2020-08-20 15:01:04 +00:00
{
if (_disposed)
{
return;
}
2020-08-20 19:04:57 +00:00
DisposeDevicePublisher();
DisposePlayToManager();
DisposeDeviceDiscovery();
2016-11-14 19:48:01 +00:00
if (_communicationsServer != null)
{
_logger.LogInformation("Disposing SsdpCommunicationsServer");
2016-11-14 19:48:01 +00:00
_communicationsServer.Dispose();
_communicationsServer = null;
}
2018-09-12 17:26:21 +00:00
ContentDirectory = null;
ConnectionManager = null;
MediaReceiverRegistrar = null;
Current = null;
2016-10-29 22:22:20 +00:00
2020-08-20 15:01:04 +00:00
_disposed = true;
2016-10-29 22:22:20 +00:00
}
}
}