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