|
@@ -18,6 +18,7 @@ using MediaBrowser.Model.Events;
|
|
|
using MediaBrowser.Model.Globalization;
|
|
|
using MediaBrowser.Model.Net;
|
|
|
using MediaBrowser.Model.Threading;
|
|
|
+using System.Threading;
|
|
|
|
|
|
namespace Emby.Dlna.PlayTo
|
|
|
{
|
|
@@ -44,6 +45,8 @@ namespace Emby.Dlna.PlayTo
|
|
|
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();
|
|
|
|
|
|
public PlayToManager(ILogger logger, ISessionManager sessionManager, ILibraryManager libraryManager, IUserManager userManager, IDlnaManager dlnaManager, IServerApplicationHost appHost, IImageProcessor imageProcessor, IDeviceDiscovery deviceDiscovery, IHttpClient httpClient, IServerConfigurationManager config, IUserDataManager userDataManager, ILocalizationManager localization, IMediaSourceManager mediaSourceManager, IMediaEncoder mediaEncoder, ITimerFactory timerFactory)
|
|
|
{
|
|
@@ -98,92 +101,104 @@ namespace Emby.Dlna.PlayTo
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ var cancellationToken = _disposeCancellationTokenSource.Token;
|
|
|
+
|
|
|
+ await _sessionLock.WaitAsync(cancellationToken).ConfigureAwait(false);
|
|
|
+
|
|
|
try
|
|
|
{
|
|
|
- lock (_nonRendererUrls)
|
|
|
+ if (_disposed)
|
|
|
{
|
|
|
- if ((DateTime.UtcNow - _lastRendererClear).TotalMinutes >= 10)
|
|
|
- {
|
|
|
- _nonRendererUrls.Clear();
|
|
|
- _lastRendererClear = DateTime.UtcNow;
|
|
|
- }
|
|
|
-
|
|
|
- if (_nonRendererUrls.Contains(location, StringComparer.OrdinalIgnoreCase))
|
|
|
- {
|
|
|
- return;
|
|
|
- }
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
- var uri = info.Location;
|
|
|
- _logger.Debug("Attempting to create PlayToController from location {0}", location);
|
|
|
- var device = await Device.CreateuPnpDeviceAsync(uri, _httpClient, _config, _logger, _timerFactory).ConfigureAwait(false);
|
|
|
+ await AddDevice(info, location, cancellationToken).ConfigureAwait(false);
|
|
|
+ }
|
|
|
+ catch (OperationCanceledException)
|
|
|
+ {
|
|
|
+
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ _logger.ErrorException("Error creating PlayTo device.", ex);
|
|
|
+
|
|
|
+ _nonRendererUrls.Add(location);
|
|
|
+ }
|
|
|
+ finally
|
|
|
+ {
|
|
|
+ _sessionLock.Release();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private async Task AddDevice(UpnpDeviceInfo info, string location, CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ if ((DateTime.UtcNow - _lastRendererClear).TotalMinutes >= 10)
|
|
|
+ {
|
|
|
+ _nonRendererUrls.Clear();
|
|
|
+ _lastRendererClear = DateTime.UtcNow;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (_nonRendererUrls.Contains(location, StringComparer.OrdinalIgnoreCase))
|
|
|
+ {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ 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)
|
|
|
+ {
|
|
|
+ _nonRendererUrls.Add(location);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ _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);
|
|
|
+
|
|
|
+ var controller = sessionInfo.SessionController as PlayToController;
|
|
|
|
|
|
- if (device.RendererCommands == null)
|
|
|
+ if (controller == null)
|
|
|
+ {
|
|
|
+ string serverAddress;
|
|
|
+ if (info.LocalIpAddress == null || info.LocalIpAddress.Equals(IpAddressInfo.Any) || info.LocalIpAddress.Equals(IpAddressInfo.IPv6Loopback))
|
|
|
{
|
|
|
- lock (_nonRendererUrls)
|
|
|
- {
|
|
|
- _nonRendererUrls.Add(location);
|
|
|
- return;
|
|
|
- }
|
|
|
+ serverAddress = await GetServerAddress(null, cancellationToken).ConfigureAwait(false);
|
|
|
}
|
|
|
-
|
|
|
- if (_disposed)
|
|
|
+ else
|
|
|
{
|
|
|
- return;
|
|
|
+ serverAddress = await GetServerAddress(info.LocalIpAddress, cancellationToken).ConfigureAwait(false);
|
|
|
}
|
|
|
|
|
|
- _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);
|
|
|
-
|
|
|
- var controller = sessionInfo.SessionController as PlayToController;
|
|
|
-
|
|
|
- if (controller == null)
|
|
|
+ string accessToken = null;
|
|
|
+
|
|
|
+ sessionInfo.SessionController = controller = new PlayToController(sessionInfo,
|
|
|
+ _sessionManager,
|
|
|
+ _libraryManager,
|
|
|
+ _logger,
|
|
|
+ _dlnaManager,
|
|
|
+ _userManager,
|
|
|
+ _imageProcessor,
|
|
|
+ serverAddress,
|
|
|
+ accessToken,
|
|
|
+ _deviceDiscovery,
|
|
|
+ _userDataManager,
|
|
|
+ _localization,
|
|
|
+ _mediaSourceManager,
|
|
|
+ _config,
|
|
|
+ _mediaEncoder);
|
|
|
+
|
|
|
+ controller.Init(device);
|
|
|
+
|
|
|
+ var profile = _dlnaManager.GetProfile(device.Properties.ToDeviceIdentification()) ??
|
|
|
+ _dlnaManager.GetDefaultProfile();
|
|
|
+
|
|
|
+ _sessionManager.ReportCapabilities(sessionInfo.Id, new ClientCapabilities
|
|
|
{
|
|
|
- if (_disposed)
|
|
|
- {
|
|
|
- return;
|
|
|
- }
|
|
|
+ PlayableMediaTypes = profile.GetSupportedMediaTypes(),
|
|
|
|
|
|
- string serverAddress;
|
|
|
- if (info.LocalIpAddress == null || info.LocalIpAddress.Equals(IpAddressInfo.Any) || info.LocalIpAddress.Equals(IpAddressInfo.IPv6Loopback))
|
|
|
+ SupportedCommands = new string[]
|
|
|
{
|
|
|
- serverAddress = await GetServerAddress(null).ConfigureAwait(false);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- serverAddress = await GetServerAddress(info.LocalIpAddress).ConfigureAwait(false);
|
|
|
- }
|
|
|
-
|
|
|
- string accessToken = null;
|
|
|
-
|
|
|
- sessionInfo.SessionController = controller = new PlayToController(sessionInfo,
|
|
|
- _sessionManager,
|
|
|
- _libraryManager,
|
|
|
- _logger,
|
|
|
- _dlnaManager,
|
|
|
- _userManager,
|
|
|
- _imageProcessor,
|
|
|
- serverAddress,
|
|
|
- accessToken,
|
|
|
- _deviceDiscovery,
|
|
|
- _userDataManager,
|
|
|
- _localization,
|
|
|
- _mediaSourceManager,
|
|
|
- _config,
|
|
|
- _mediaEncoder);
|
|
|
-
|
|
|
- controller.Init(device);
|
|
|
-
|
|
|
- var profile = _dlnaManager.GetProfile(device.Properties.ToDeviceIdentification()) ??
|
|
|
- _dlnaManager.GetDefaultProfile();
|
|
|
-
|
|
|
- _sessionManager.ReportCapabilities(sessionInfo.Id, new ClientCapabilities
|
|
|
- {
|
|
|
- PlayableMediaTypes = profile.GetSupportedMediaTypes(),
|
|
|
-
|
|
|
- SupportedCommands = new string[]
|
|
|
- {
|
|
|
GeneralCommandType.VolumeDown.ToString(),
|
|
|
GeneralCommandType.VolumeUp.ToString(),
|
|
|
GeneralCommandType.Mute.ToString(),
|
|
@@ -192,33 +207,23 @@ namespace Emby.Dlna.PlayTo
|
|
|
GeneralCommandType.SetVolume.ToString(),
|
|
|
GeneralCommandType.SetAudioStreamIndex.ToString(),
|
|
|
GeneralCommandType.SetSubtitleStreamIndex.ToString()
|
|
|
- },
|
|
|
+ },
|
|
|
|
|
|
- SupportsMediaControl = true,
|
|
|
+ SupportsMediaControl = true,
|
|
|
|
|
|
- // xbox one creates a new uuid everytime it restarts
|
|
|
- SupportsPersistentIdentifier = (device.Properties.ModelName ?? string.Empty).IndexOf("xbox", StringComparison.OrdinalIgnoreCase) == -1
|
|
|
- });
|
|
|
+ // xbox one creates a new uuid everytime it restarts
|
|
|
+ SupportsPersistentIdentifier = (device.Properties.ModelName ?? string.Empty).IndexOf("xbox", StringComparison.OrdinalIgnoreCase) == -1
|
|
|
+ });
|
|
|
|
|
|
- _logger.Info("DLNA Session created for {0} - {1}", device.Properties.Name, device.Properties.ModelName);
|
|
|
- }
|
|
|
- }
|
|
|
- catch (Exception ex)
|
|
|
- {
|
|
|
- _logger.ErrorException("Error creating PlayTo device.", ex);
|
|
|
-
|
|
|
- lock (_nonRendererUrls)
|
|
|
- {
|
|
|
- _nonRendererUrls.Add(location);
|
|
|
- }
|
|
|
+ _logger.Info("DLNA Session created for {0} - {1}", device.Properties.Name, device.Properties.ModelName);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private Task<string> GetServerAddress(IpAddressInfo address)
|
|
|
+ private Task<string> GetServerAddress(IpAddressInfo address, CancellationToken cancellationToken)
|
|
|
{
|
|
|
if (address == null)
|
|
|
{
|
|
|
- return _appHost.GetLocalApiUrl();
|
|
|
+ return _appHost.GetLocalApiUrl(cancellationToken);
|
|
|
}
|
|
|
|
|
|
return Task.FromResult(_appHost.GetLocalApiUrl(address));
|
|
@@ -226,6 +231,15 @@ namespace Emby.Dlna.PlayTo
|
|
|
|
|
|
public void Dispose()
|
|
|
{
|
|
|
+ try
|
|
|
+ {
|
|
|
+ _disposeCancellationTokenSource.Cancel();
|
|
|
+ }
|
|
|
+ catch
|
|
|
+ {
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
_disposed = true;
|
|
|
_deviceDiscovery.DeviceDiscovered -= _deviceDiscovery_DeviceDiscovered;
|
|
|
GC.SuppressFinalize(this);
|