aboutsummaryrefslogtreecommitdiff
path: root/Emby.Dlna
diff options
context:
space:
mode:
Diffstat (limited to 'Emby.Dlna')
-rw-r--r--Emby.Dlna/ContentDirectory/ControlHandler.cs54
-rw-r--r--Emby.Dlna/Didl/DidlBuilder.cs24
-rw-r--r--Emby.Dlna/DlnaManager.cs2
-rw-r--r--Emby.Dlna/Emby.Dlna.csproj6
-rw-r--r--Emby.Dlna/Extensions/DlnaServiceCollectionExtensions.cs72
-rw-r--r--Emby.Dlna/Main/DlnaEntryPoint.cs463
-rw-r--r--Emby.Dlna/Main/DlnaHost.cs389
-rw-r--r--Emby.Dlna/PlayTo/Device.cs17
-rw-r--r--Emby.Dlna/PlayTo/DlnaHttpClient.cs55
-rw-r--r--Emby.Dlna/PlayTo/PlayToController.cs20
-rw-r--r--Emby.Dlna/PlayTo/PlayToManager.cs4
-rw-r--r--Emby.Dlna/PlayTo/uBaseObject.cs13
12 files changed, 553 insertions, 566 deletions
diff --git a/Emby.Dlna/ContentDirectory/ControlHandler.cs b/Emby.Dlna/ContentDirectory/ControlHandler.cs
index abd594a3a1..99068826d9 100644
--- a/Emby.Dlna/ContentDirectory/ControlHandler.cs
+++ b/Emby.Dlna/ContentDirectory/ControlHandler.cs
@@ -494,7 +494,7 @@ namespace Emby.Dlna.ContentDirectory
{
var folder = (Folder)item;
- string[] mediaTypes = Array.Empty<string>();
+ MediaType[] mediaTypes = Array.Empty<MediaType>();
bool? isFolder = null;
switch (search.SearchType)
@@ -565,30 +565,18 @@ namespace Emby.Dlna.ContentDirectory
if (stubType != StubType.Folder && item is IHasCollectionType collectionFolder)
{
- var collectionType = collectionFolder.CollectionType;
- if (string.Equals(CollectionType.Music, collectionType, StringComparison.OrdinalIgnoreCase))
+ switch (collectionFolder.CollectionType)
{
- return GetMusicFolders(item, user, stubType, sort, startIndex, limit);
- }
-
- if (string.Equals(CollectionType.Movies, collectionType, StringComparison.OrdinalIgnoreCase))
- {
- return GetMovieFolders(item, user, stubType, sort, startIndex, limit);
- }
-
- if (string.Equals(CollectionType.TvShows, collectionType, StringComparison.OrdinalIgnoreCase))
- {
- return GetTvFolders(item, user, stubType, sort, startIndex, limit);
- }
-
- if (string.Equals(CollectionType.Folders, collectionType, StringComparison.OrdinalIgnoreCase))
- {
- return GetFolders(user, startIndex, limit);
- }
-
- if (string.Equals(CollectionType.LiveTv, collectionType, StringComparison.OrdinalIgnoreCase))
- {
- return GetLiveTvChannels(user, sort, startIndex, limit);
+ case CollectionType.Music:
+ return GetMusicFolders(item, user, stubType, sort, startIndex, limit);
+ case CollectionType.Movies:
+ return GetMovieFolders(item, user, stubType, sort, startIndex, limit);
+ case CollectionType.TvShows:
+ return GetTvFolders(item, user, stubType, sort, startIndex, limit);
+ case CollectionType.Folders:
+ return GetFolders(user, startIndex, limit);
+ case CollectionType.LiveTv:
+ return GetLiveTvChannels(user, sort, startIndex, limit);
}
}
@@ -917,7 +905,7 @@ namespace Emby.Dlna.ContentDirectory
private QueryResult<ServerItem> GetGenres(BaseItem parent, InternalItemsQuery query)
{
// Don't sort
- query.OrderBy = Array.Empty<(string, SortOrder)>();
+ query.OrderBy = Array.Empty<(ItemSortBy, SortOrder)>();
query.AncestorIds = new[] { parent.Id };
var genresResult = _libraryManager.GetGenres(query);
@@ -933,7 +921,7 @@ namespace Emby.Dlna.ContentDirectory
private QueryResult<ServerItem> GetMusicGenres(BaseItem parent, InternalItemsQuery query)
{
// Don't sort
- query.OrderBy = Array.Empty<(string, SortOrder)>();
+ query.OrderBy = Array.Empty<(ItemSortBy, SortOrder)>();
query.AncestorIds = new[] { parent.Id };
var genresResult = _libraryManager.GetMusicGenres(query);
@@ -949,7 +937,7 @@ namespace Emby.Dlna.ContentDirectory
private QueryResult<ServerItem> GetMusicAlbumArtists(BaseItem parent, InternalItemsQuery query)
{
// Don't sort
- query.OrderBy = Array.Empty<(string, SortOrder)>();
+ query.OrderBy = Array.Empty<(ItemSortBy, SortOrder)>();
query.AncestorIds = new[] { parent.Id };
var artists = _libraryManager.GetAlbumArtists(query);
@@ -965,7 +953,7 @@ namespace Emby.Dlna.ContentDirectory
private QueryResult<ServerItem> GetMusicArtists(BaseItem parent, InternalItemsQuery query)
{
// Don't sort
- query.OrderBy = Array.Empty<(string, SortOrder)>();
+ query.OrderBy = Array.Empty<(ItemSortBy, SortOrder)>();
query.AncestorIds = new[] { parent.Id };
var artists = _libraryManager.GetArtists(query);
return ToResult(query.StartIndex, artists);
@@ -980,7 +968,7 @@ namespace Emby.Dlna.ContentDirectory
private QueryResult<ServerItem> GetFavoriteArtists(BaseItem parent, InternalItemsQuery query)
{
// Don't sort
- query.OrderBy = Array.Empty<(string, SortOrder)>();
+ query.OrderBy = Array.Empty<(ItemSortBy, SortOrder)>();
query.AncestorIds = new[] { parent.Id };
query.IsFavorite = true;
var artists = _libraryManager.GetArtists(query);
@@ -1011,7 +999,7 @@ namespace Emby.Dlna.ContentDirectory
/// <returns>The <see cref="QueryResult{ServerItem}"/>.</returns>
private QueryResult<ServerItem> GetNextUp(BaseItem parent, InternalItemsQuery query)
{
- query.OrderBy = Array.Empty<(string, SortOrder)>();
+ query.OrderBy = Array.Empty<(ItemSortBy, SortOrder)>();
var result = _tvSeriesManager.GetNextUp(
new NextUpQuery
@@ -1036,7 +1024,7 @@ namespace Emby.Dlna.ContentDirectory
/// <returns>The <see cref="QueryResult{ServerItem}"/>.</returns>
private QueryResult<ServerItem> GetLatest(BaseItem parent, InternalItemsQuery query, BaseItemKind itemType)
{
- query.OrderBy = Array.Empty<(string, SortOrder)>();
+ query.OrderBy = Array.Empty<(ItemSortBy, SortOrder)>();
var items = _userViewManager.GetLatestItems(
new LatestItemsQuery
@@ -1203,9 +1191,9 @@ namespace Emby.Dlna.ContentDirectory
/// </summary>
/// <param name="sort">The <see cref="SortCriteria"/>.</param>
/// <param name="isPreSorted">True if pre-sorted.</param>
- private static (string SortName, SortOrder SortOrder)[] GetOrderBy(SortCriteria sort, bool isPreSorted)
+ private static (ItemSortBy SortName, SortOrder SortOrder)[] GetOrderBy(SortCriteria sort, bool isPreSorted)
{
- return isPreSorted ? Array.Empty<(string, SortOrder)>() : new[] { (ItemSortBy.SortName, sort.SortOrder) };
+ return isPreSorted ? Array.Empty<(ItemSortBy, SortOrder)>() : new[] { (ItemSortBy.SortName, sort.SortOrder) };
}
/// <summary>
diff --git a/Emby.Dlna/Didl/DidlBuilder.cs b/Emby.Dlna/Didl/DidlBuilder.cs
index 5ed982876d..9f152df132 100644
--- a/Emby.Dlna/Didl/DidlBuilder.cs
+++ b/Emby.Dlna/Didl/DidlBuilder.cs
@@ -174,13 +174,14 @@ namespace Emby.Dlna.Didl
if (item is IHasMediaSources)
{
- if (string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase))
+ switch (item.MediaType)
{
- AddAudioResource(writer, item, deviceId, filter, streamInfo);
- }
- else if (string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
- {
- AddVideoResource(writer, item, deviceId, filter, streamInfo);
+ case MediaType.Audio:
+ AddAudioResource(writer, item, deviceId, filter, streamInfo);
+ break;
+ case MediaType.Video:
+ AddVideoResource(writer, item, deviceId, filter, streamInfo);
+ break;
}
}
@@ -821,15 +822,15 @@ namespace Emby.Dlna.Didl
writer.WriteString(classType ?? "object.container.storageFolder");
}
- else if (string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase))
+ else if (item.MediaType == MediaType.Audio)
{
writer.WriteString("object.item.audioItem.musicTrack");
}
- else if (string.Equals(item.MediaType, MediaType.Photo, StringComparison.OrdinalIgnoreCase))
+ else if (item.MediaType == MediaType.Photo)
{
writer.WriteString("object.item.imageItem.photo");
}
- else if (string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
+ else if (item.MediaType == MediaType.Video)
{
if (!_profile.RequiresPlainVideoItems && item is Movie)
{
@@ -1006,8 +1007,7 @@ namespace Emby.Dlna.Didl
if (!_profile.EnableAlbumArtInDidl)
{
- if (string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase)
- || string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
+ if (item.MediaType == MediaType.Audio || item.MediaType == MediaType.Video)
{
if (!stubType.HasValue)
{
@@ -1016,7 +1016,7 @@ namespace Emby.Dlna.Didl
}
}
- if (!_profile.EnableSingleAlbumArtLimit || string.Equals(item.MediaType, MediaType.Photo, StringComparison.OrdinalIgnoreCase))
+ if (!_profile.EnableSingleAlbumArtLimit || item.MediaType == MediaType.Photo)
{
AddImageResElement(item, writer, 4096, 4096, "jpg", "JPEG_LRG");
AddImageResElement(item, writer, 1024, 768, "jpg", "JPEG_MED");
diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs
index 99b3e6e7ef..d67cb67b54 100644
--- a/Emby.Dlna/DlnaManager.cs
+++ b/Emby.Dlna/DlnaManager.cs
@@ -228,7 +228,7 @@ namespace Emby.Dlna
try
{
return _fileSystem.GetFilePaths(path)
- .Where(i => string.Equals(Path.GetExtension(i), ".xml", StringComparison.OrdinalIgnoreCase))
+ .Where(i => Path.GetExtension(i.AsSpan()).Equals(".xml", StringComparison.OrdinalIgnoreCase))
.Select(i => ParseProfileFile(i, type))
.Where(i => i is not null)
.ToList()!; // We just filtered out all the nulls
diff --git a/Emby.Dlna/Emby.Dlna.csproj b/Emby.Dlna/Emby.Dlna.csproj
index aca2399644..efbef05640 100644
--- a/Emby.Dlna/Emby.Dlna.csproj
+++ b/Emby.Dlna/Emby.Dlna.csproj
@@ -26,8 +26,12 @@
<CodeAnalysisTreatWarningsAsErrors>false</CodeAnalysisTreatWarningsAsErrors>
</PropertyGroup>
- <!-- Code Analyzers-->
+ <!-- Code Analyzers -->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
+ <PackageReference Include="IDisposableAnalyzers">
+ <PrivateAssets>all</PrivateAssets>
+ <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
+ </PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
diff --git a/Emby.Dlna/Extensions/DlnaServiceCollectionExtensions.cs b/Emby.Dlna/Extensions/DlnaServiceCollectionExtensions.cs
new file mode 100644
index 0000000000..82c80070a5
--- /dev/null
+++ b/Emby.Dlna/Extensions/DlnaServiceCollectionExtensions.cs
@@ -0,0 +1,72 @@
+using System;
+using System.Globalization;
+using System.Net;
+using System.Net.Http;
+using System.Text;
+using Emby.Dlna.ConnectionManager;
+using Emby.Dlna.ContentDirectory;
+using Emby.Dlna.Main;
+using Emby.Dlna.MediaReceiverRegistrar;
+using Emby.Dlna.Ssdp;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Controller;
+using MediaBrowser.Controller.Dlna;
+using MediaBrowser.Model.Dlna;
+using MediaBrowser.Model.Net;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Rssdp.Infrastructure;
+
+namespace Emby.Dlna.Extensions;
+
+/// <summary>
+/// Extension methods for adding DLNA services.
+/// </summary>
+public static class DlnaServiceCollectionExtensions
+{
+ /// <summary>
+ /// Adds DLNA services to the provided <see cref="IServiceCollection"/>.
+ /// </summary>
+ /// <param name="services">The <see cref="IServiceCollection"/>.</param>
+ /// <param name="applicationHost">The <see cref="IServerApplicationHost"/>.</param>
+ public static void AddDlnaServices(
+ this IServiceCollection services,
+ IServerApplicationHost applicationHost)
+ {
+ services.AddHttpClient(NamedClient.Dlna, c =>
+ {
+ c.DefaultRequestHeaders.UserAgent.ParseAdd(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ "{0}/{1} UPnP/1.0 {2}/{3}",
+ Environment.OSVersion.Platform,
+ Environment.OSVersion,
+ applicationHost.Name,
+ applicationHost.ApplicationVersionString));
+
+ c.DefaultRequestHeaders.Add("CPFN.UPNP.ORG", applicationHost.FriendlyName); // Required for UPnP DeviceArchitecture v2.0
+ c.DefaultRequestHeaders.Add("FriendlyName.DLNA.ORG", applicationHost.FriendlyName); // REVIEW: where does this come from?
+ })
+ .ConfigurePrimaryHttpMessageHandler(_ => new SocketsHttpHandler
+ {
+ AutomaticDecompression = DecompressionMethods.All,
+ RequestHeaderEncodingSelector = (_, _) => Encoding.UTF8
+ });
+
+ services.AddSingleton<IDlnaManager, DlnaManager>();
+ services.AddSingleton<IDeviceDiscovery, DeviceDiscovery>();
+ services.AddSingleton<IContentDirectory, ContentDirectoryService>();
+ services.AddSingleton<IConnectionManager, ConnectionManagerService>();
+ services.AddSingleton<IMediaReceiverRegistrar, MediaReceiverRegistrarService>();
+
+ services.AddSingleton<ISsdpCommunicationsServer>(provider => new SsdpCommunicationsServer(
+ provider.GetRequiredService<ISocketFactory>(),
+ provider.GetRequiredService<INetworkManager>(),
+ provider.GetRequiredService<ILogger<SsdpCommunicationsServer>>())
+ {
+ IsShared = true
+ });
+
+ services.AddHostedService<DlnaHost>();
+ }
+}
diff --git a/Emby.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs
deleted file mode 100644
index 1a4bec398c..0000000000
--- a/Emby.Dlna/Main/DlnaEntryPoint.cs
+++ /dev/null
@@ -1,463 +0,0 @@
-#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.Extensions;
-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 (_publisher is not null)
- {
- return;
- }
-
- try
- {
- _publisher = new SsdpDevicePublisher(
- _communicationsServer,
- Environment.OSVersion.Platform.ToString(),
- // Can not use VersionString here since that includes OS and version
- Environment.OSVersion.Version.ToString(),
- _config.GetDlnaConfiguration().SendOnlyMatchedHost)
- {
- LogFunction = (msg) => _logger.LogDebug("{Msg}", msg),
- SupportPnpRootDevice = false
- };
-
- RegisterServerEndpoints();
-
- if (options.BlastAliveMessages)
- {
- _publisher.StartSendingAliveNotifications(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";
-
- // Only get bind addresses in LAN
- // IPv6 is currently unsupported
- var validInterfaces = _networkManager.GetInternalBindAddresses()
- .Where(x => x.Address is not null)
- .Where(x => x.AddressFamily != AddressFamily.InterNetworkV6)
- .ToList();
-
- if (validInterfaces.Count == 0)
- {
- // No interfaces returned, fall back to loopback
- validInterfaces = _networkManager.GetLoopbacks().ToList();
- }
-
- foreach (var intf in validInterfaces)
- {
- var fullService = "urn:schemas-upnp-org:device:MediaServer:1";
-
- _logger.LogInformation("Registering publisher for {ResourceName} on {DeviceAddress}", fullService, intf.Address);
-
- var uri = new UriBuilder(_appHost.GetApiUrlForLocalAccess(intf.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 = intf.Address,
- PrefixLength = NetworkExtensions.MaskToCidr(intf.Subnet.Prefix),
- 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;
- }
- }
-}
diff --git a/Emby.Dlna/Main/DlnaHost.cs b/Emby.Dlna/Main/DlnaHost.cs
new file mode 100644
index 0000000000..3896b74a1b
--- /dev/null
+++ b/Emby.Dlna/Main/DlnaHost.cs
@@ -0,0 +1,389 @@
+#pragma warning disable CA1031 // Do not catch general exception types.
+
+using System;
+using System.Globalization;
+using System.Linq;
+using System.Net.Http;
+using System.Net.Sockets;
+using System.Threading;
+using System.Threading.Tasks;
+using Emby.Dlna.PlayTo;
+using Emby.Dlna.Ssdp;
+using Jellyfin.Networking.Configuration;
+using Jellyfin.Networking.Extensions;
+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.Session;
+using MediaBrowser.Model.Dlna;
+using MediaBrowser.Model.Globalization;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+using Rssdp;
+using Rssdp.Infrastructure;
+
+namespace Emby.Dlna.Main;
+
+/// <summary>
+/// A <see cref="IHostedService"/> that manages a DLNA server.
+/// </summary>
+public sealed class DlnaHost : IHostedService, IDisposable
+{
+ private readonly ILogger<DlnaHost> _logger;
+ private readonly IServerConfigurationManager _config;
+ 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 ISsdpCommunicationsServer _communicationsServer;
+ private readonly INetworkManager _networkManager;
+ private readonly object _syncLock = new();
+
+ private SsdpDevicePublisher? _publisher;
+ private PlayToManager? _manager;
+ private bool _disposed;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DlnaHost"/> class.
+ /// </summary>
+ /// <param name="config">The <see cref="IServerConfigurationManager"/>.</param>
+ /// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
+ /// <param name="appHost">The <see cref="IServerApplicationHost"/>.</param>
+ /// <param name="sessionManager">The <see cref="ISessionManager"/>.</param>
+ /// <param name="httpClientFactory">The <see cref="IHttpClientFactory"/>.</param>
+ /// <param name="libraryManager">The <see cref="ILibraryManager"/>.</param>
+ /// <param name="userManager">The <see cref="IUserManager"/>.</param>
+ /// <param name="dlnaManager">The <see cref="IDlnaManager"/>.</param>
+ /// <param name="imageProcessor">The <see cref="IImageProcessor"/>.</param>
+ /// <param name="userDataManager">The <see cref="IUserDataManager"/>.</param>
+ /// <param name="localizationManager">The <see cref="ILocalizationManager"/>.</param>
+ /// <param name="mediaSourceManager">The <see cref="IMediaSourceManager"/>.</param>
+ /// <param name="deviceDiscovery">The <see cref="IDeviceDiscovery"/>.</param>
+ /// <param name="mediaEncoder">The <see cref="IMediaEncoder"/>.</param>
+ /// <param name="communicationsServer">The <see cref="ISsdpCommunicationsServer"/>.</param>
+ /// <param name="networkManager">The <see cref="INetworkManager"/>.</param>
+ public DlnaHost(
+ 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,
+ ISsdpCommunicationsServer communicationsServer,
+ INetworkManager networkManager)
+ {
+ _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;
+ _communicationsServer = communicationsServer;
+ _networkManager = networkManager;
+ _logger = loggerFactory.CreateLogger<DlnaHost>();
+ }
+
+ /// <inheritdoc />
+ public async Task StartAsync(CancellationToken cancellationToken)
+ {
+ var netConfig = _config.GetConfiguration<NetworkConfiguration>(NetworkConfigurationStore.StoreKey);
+ if (_appHost.ListenWithHttps && netConfig.RequireHttps)
+ {
+ if (_config.GetDlnaConfiguration().EnableServer)
+ {
+ _logger.LogError("The DLNA specification does not support HTTPS.");
+ }
+
+ // No use starting as dlna won't work, as we're running purely on HTTPS.
+ return;
+ }
+
+ await ((DlnaManager)_dlnaManager).InitProfilesAsync().ConfigureAwait(false);
+ ReloadComponents();
+
+ _config.NamedConfigurationUpdated += OnNamedConfigurationUpdated;
+ }
+
+ /// <inheritdoc />
+ public Task StopAsync(CancellationToken cancellationToken)
+ {
+ Stop();
+
+ return Task.CompletedTask;
+ }
+
+ /// <inheritdoc />
+ public void Dispose()
+ {
+ if (!_disposed)
+ {
+ Stop();
+ _disposed = true;
+ }
+ }
+
+ private void OnNamedConfigurationUpdated(object? sender, ConfigurationUpdateEventArgs e)
+ {
+ if (string.Equals(e.Key, "dlna", StringComparison.OrdinalIgnoreCase))
+ {
+ ReloadComponents();
+ }
+ }
+
+ private void ReloadComponents()
+ {
+ var options = _config.GetDlnaConfiguration();
+ StartDeviceDiscovery();
+
+ if (options.EnableServer)
+ {
+ StartDevicePublisher(options);
+ }
+ else
+ {
+ DisposeDevicePublisher();
+ }
+
+ if (options.EnablePlayTo)
+ {
+ StartPlayToManager();
+ }
+ else
+ {
+ DisposePlayToManager();
+ }
+ }
+
+ private static string CreateUuid(string text)
+ {
+ if (!Guid.TryParse(text, out var guid))
+ {
+ guid = text.GetMD5();
+ }
+
+ return guid.ToString("D", CultureInfo.InvariantCulture);
+ }
+
+ private static void SetProperties(SsdpDevice device, string fullDeviceType)
+ {
+ var serviceParts = fullDeviceType
+ .Replace("urn:", string.Empty, StringComparison.OrdinalIgnoreCase)
+ .Replace(":1", string.Empty, StringComparison.OrdinalIgnoreCase)
+ .Split(':');
+
+ device.DeviceTypeNamespace = serviceParts[0].Replace('.', '-');
+ device.DeviceClass = serviceParts[1];
+ device.DeviceType = serviceParts[2];
+ }
+
+ private void StartDeviceDiscovery()
+ {
+ try
+ {
+ ((DeviceDiscovery)_deviceDiscovery).Start(_communicationsServer);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error starting device discovery");
+ }
+ }
+
+ private void StartDevicePublisher(Configuration.DlnaOptions options)
+ {
+ if (_publisher is not null)
+ {
+ return;
+ }
+
+ try
+ {
+ _publisher = new SsdpDevicePublisher(
+ _communicationsServer,
+ Environment.OSVersion.Platform.ToString(),
+ // Can not use VersionString here since that includes OS and version
+ Environment.OSVersion.Version.ToString(),
+ _config.GetDlnaConfiguration().SendOnlyMatchedHost)
+ {
+ LogFunction = msg => _logger.LogDebug("{Msg}", msg),
+ SupportPnpRootDevice = false
+ };
+
+ RegisterServerEndpoints();
+
+ if (options.BlastAliveMessages)
+ {
+ _publisher.StartSendingAliveNotifications(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";
+
+ // Only get bind addresses in LAN
+ // IPv6 is currently unsupported
+ var validInterfaces = _networkManager.GetInternalBindAddresses()
+ .Where(x => x.AddressFamily != AddressFamily.InterNetworkV6)
+ .ToList();
+
+ if (validInterfaces.Count == 0)
+ {
+ // No interfaces returned, fall back to loopback
+ validInterfaces = _networkManager.GetLoopbacks().ToList();
+ }
+
+ foreach (var intf in validInterfaces)
+ {
+ var fullService = "urn:schemas-upnp-org:device:MediaServer:1";
+
+ _logger.LogInformation("Registering publisher for {ResourceName} on {DeviceAddress}", fullService, intf.Address);
+
+ var uri = new UriBuilder(_appHost.GetApiUrlForLocalAccess(intf.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 = intf.Address,
+ PrefixLength = NetworkExtensions.MaskToCidr(intf.Subnet.Prefix),
+ 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.
+ };
+
+ SetProperties(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.
+ };
+
+ SetProperties(embeddedDevice, subDevice);
+ device.AddDevice(embeddedDevice);
+ }
+ }
+ }
+
+ 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;
+ }
+ }
+ }
+
+ private void DisposeDevicePublisher()
+ {
+ if (_publisher is not null)
+ {
+ _logger.LogInformation("Disposing SsdpDevicePublisher");
+ _publisher.Dispose();
+ _publisher = null;
+ }
+ }
+
+ private void Stop()
+ {
+ DisposeDevicePublisher();
+ DisposePlayToManager();
+ }
+}
diff --git a/Emby.Dlna/PlayTo/Device.cs b/Emby.Dlna/PlayTo/Device.cs
index d21cc69132..18fa196508 100644
--- a/Emby.Dlna/PlayTo/Device.cs
+++ b/Emby.Dlna/PlayTo/Device.cs
@@ -927,14 +927,11 @@ namespace Emby.Dlna.PlayTo
var resElement = container.Element(UPnpNamespaces.Res);
- if (resElement is not null)
- {
- var info = resElement.Attribute(UPnpNamespaces.ProtocolInfo);
+ var info = resElement?.Attribute(UPnpNamespaces.ProtocolInfo);
- if (info is not null && !string.IsNullOrWhiteSpace(info.Value))
- {
- return info.Value.Split(':');
- }
+ if (info is not null && !string.IsNullOrWhiteSpace(info.Value))
+ {
+ return info.Value.Split(':');
}
return new string[4];
@@ -1139,7 +1136,6 @@ namespace Emby.Dlna.PlayTo
return new Device(deviceProperties, httpClientFactory, logger);
}
-#nullable enable
private static DeviceIcon CreateIcon(XElement element)
{
ArgumentNullException.ThrowIfNull(element);
@@ -1252,11 +1248,10 @@ namespace Emby.Dlna.PlayTo
if (disposing)
{
_timer?.Dispose();
+ _timer = null;
+ Properties = null!;
}
- _timer = null;
- Properties = null!;
-
_disposed = true;
}
diff --git a/Emby.Dlna/PlayTo/DlnaHttpClient.cs b/Emby.Dlna/PlayTo/DlnaHttpClient.cs
index 220aa1a8dc..255c51f19a 100644
--- a/Emby.Dlna/PlayTo/DlnaHttpClient.cs
+++ b/Emby.Dlna/PlayTo/DlnaHttpClient.cs
@@ -55,41 +55,42 @@ namespace Emby.Dlna.PlayTo
var client = _httpClientFactory.CreateClient(NamedClient.Dlna);
using var response = await client.SendAsync(request, cancellationToken).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
- await using MemoryStream ms = new MemoryStream();
- await response.Content.CopyToAsync(ms, cancellationToken).ConfigureAwait(false);
- ms.Position = 0;
- try
+ Stream stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
+ await using (stream.ConfigureAwait(false))
{
- return await XDocument.LoadAsync(
- ms,
- LoadOptions.None,
- cancellationToken).ConfigureAwait(false);
- }
- catch (XmlException)
- {
- // try correcting the Xml response with common errors
- ms.Position = 0;
- using StreamReader sr = new StreamReader(ms);
- var xmlString = await sr.ReadToEndAsync(cancellationToken).ConfigureAwait(false);
-
- // find and replace unescaped ampersands (&)
- xmlString = EscapeAmpersandRegex().Replace(xmlString, "&amp;");
-
try
{
- // retry reading Xml
- using var xmlReader = new StringReader(xmlString);
return await XDocument.LoadAsync(
- xmlReader,
+ stream,
LoadOptions.None,
cancellationToken).ConfigureAwait(false);
}
- catch (XmlException ex)
+ catch (XmlException)
{
- _logger.LogError(ex, "Failed to parse response");
- _logger.LogDebug("Malformed response: {Content}\n", xmlString);
-
- return null;
+ // try correcting the Xml response with common errors
+ stream.Position = 0;
+ using StreamReader sr = new StreamReader(stream);
+ var xmlString = await sr.ReadToEndAsync(cancellationToken).ConfigureAwait(false);
+
+ // find and replace unescaped ampersands (&)
+ xmlString = EscapeAmpersandRegex().Replace(xmlString, "&amp;");
+
+ try
+ {
+ // retry reading Xml
+ using var xmlReader = new StringReader(xmlString);
+ return await XDocument.LoadAsync(
+ xmlReader,
+ LoadOptions.None,
+ cancellationToken).ConfigureAwait(false);
+ }
+ catch (XmlException ex)
+ {
+ _logger.LogError(ex, "Failed to parse response");
+ _logger.LogDebug("Malformed response: {Content}\n", xmlString);
+
+ return null;
+ }
}
}
}
diff --git a/Emby.Dlna/PlayTo/PlayToController.cs b/Emby.Dlna/PlayTo/PlayToController.cs
index b1ad15cdc9..f70ebf3ebc 100644
--- a/Emby.Dlna/PlayTo/PlayToController.cs
+++ b/Emby.Dlna/PlayTo/PlayToController.cs
@@ -8,6 +8,7 @@ using System.Threading;
using System.Threading.Tasks;
using Emby.Dlna.Didl;
using Jellyfin.Data.Entities;
+using Jellyfin.Data.Enums;
using Jellyfin.Data.Events;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Drawing;
@@ -577,7 +578,7 @@ namespace Emby.Dlna.PlayTo
private PlaylistItem GetPlaylistItem(BaseItem item, MediaSourceInfo[] mediaSources, DeviceProfile profile, string deviceId, string? mediaSourceId, int? audioStreamIndex, int? subtitleStreamIndex)
{
- if (string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
+ if (item.MediaType == MediaType.Video)
{
return new PlaylistItem
{
@@ -597,7 +598,7 @@ namespace Emby.Dlna.PlayTo
};
}
- if (string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase))
+ if (item.MediaType == MediaType.Audio)
{
return new PlaylistItem
{
@@ -615,7 +616,7 @@ namespace Emby.Dlna.PlayTo
};
}
- if (string.Equals(item.MediaType, MediaType.Photo, StringComparison.OrdinalIgnoreCase))
+ if (item.MediaType == MediaType.Photo)
{
return PlaylistItemFactory.Create((Photo)item, profile);
}
@@ -683,16 +684,15 @@ namespace Emby.Dlna.PlayTo
if (disposing)
{
+ _device.PlaybackStart -= OnDevicePlaybackStart;
+ _device.PlaybackProgress -= OnDevicePlaybackProgress;
+ _device.PlaybackStopped -= OnDevicePlaybackStopped;
+ _device.MediaChanged -= OnDeviceMediaChanged;
+ _deviceDiscovery.DeviceLeft -= OnDeviceDiscoveryDeviceLeft;
+ _device.OnDeviceUnavailable = null;
_device.Dispose();
}
- _device.PlaybackStart -= OnDevicePlaybackStart;
- _device.PlaybackProgress -= OnDevicePlaybackProgress;
- _device.PlaybackStopped -= OnDevicePlaybackStopped;
- _device.MediaChanged -= OnDeviceMediaChanged;
- _deviceDiscovery.DeviceLeft -= OnDeviceDiscoveryDeviceLeft;
- _device.OnDeviceUnavailable = null;
-
_disposed = true;
}
diff --git a/Emby.Dlna/PlayTo/PlayToManager.cs b/Emby.Dlna/PlayTo/PlayToManager.cs
index ef617422c4..b05e0a0957 100644
--- a/Emby.Dlna/PlayTo/PlayToManager.cs
+++ b/Emby.Dlna/PlayTo/PlayToManager.cs
@@ -39,9 +39,9 @@ namespace Emby.Dlna.PlayTo
private readonly IMediaSourceManager _mediaSourceManager;
private readonly IMediaEncoder _mediaEncoder;
+ private readonly SemaphoreSlim _sessionLock = new SemaphoreSlim(1, 1);
+ private readonly CancellationTokenSource _disposeCancellationTokenSource = new CancellationTokenSource();
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, IHttpClientFactory httpClientFactory, IUserDataManager userDataManager, ILocalizationManager localization, IMediaSourceManager mediaSourceManager, IMediaEncoder mediaEncoder)
{
diff --git a/Emby.Dlna/PlayTo/uBaseObject.cs b/Emby.Dlna/PlayTo/uBaseObject.cs
index 2e0f2063be..a8f451405c 100644
--- a/Emby.Dlna/PlayTo/uBaseObject.cs
+++ b/Emby.Dlna/PlayTo/uBaseObject.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
+using Jellyfin.Data.Enums;
namespace Emby.Dlna.PlayTo
{
@@ -33,19 +34,19 @@ namespace Emby.Dlna.PlayTo
{
var classType = UpnpClass ?? string.Empty;
- if (classType.IndexOf(MediaBrowser.Model.Entities.MediaType.Audio, StringComparison.Ordinal) != -1)
+ if (classType.Contains("Audio", StringComparison.Ordinal))
{
- return MediaBrowser.Model.Entities.MediaType.Audio;
+ return "Audio";
}
- if (classType.IndexOf(MediaBrowser.Model.Entities.MediaType.Video, StringComparison.Ordinal) != -1)
+ if (classType.Contains("Video", StringComparison.Ordinal))
{
- return MediaBrowser.Model.Entities.MediaType.Video;
+ return "Video";
}
- if (classType.IndexOf("image", StringComparison.Ordinal) != -1)
+ if (classType.Contains("image", StringComparison.Ordinal))
{
- return MediaBrowser.Model.Entities.MediaType.Photo;
+ return "Photo";
}
return null;