mirror of
				https://github.com/jellyfin/jellyfin.git
				synced 2025-11-03 19:17:24 -05:00 
			
		
		
		
	Merge remote-tracking branch 'upstream/master' into package-install-repo
This commit is contained in:
		
						commit
						f443c534bf
					
				@ -147,19 +147,42 @@ jobs:
 | 
				
			|||||||
  displayName: 'Publish NuGet packages'
 | 
					  displayName: 'Publish NuGet packages'
 | 
				
			||||||
  dependsOn:
 | 
					  dependsOn:
 | 
				
			||||||
  - BuildPackage
 | 
					  - BuildPackage
 | 
				
			||||||
  condition: and(succeeded('BuildPackage'), startsWith(variables['Build.SourceBranch'], 'refs/tags'))
 | 
					  condition: succeeded('BuildPackage')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  pool:
 | 
					  pool:
 | 
				
			||||||
    vmImage: 'ubuntu-latest'
 | 
					    vmImage: 'ubuntu-latest'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  steps:
 | 
					  steps:
 | 
				
			||||||
  - task: NuGetCommand@2
 | 
					  - task: DotNetCoreCLI@2
 | 
				
			||||||
 | 
					    displayName: 'Build Stable Nuget packages'
 | 
				
			||||||
 | 
					    condition: startsWith(variables['Build.SourceBranch'], 'refs/tags')
 | 
				
			||||||
    inputs:
 | 
					    inputs:
 | 
				
			||||||
      command: 'pack'
 | 
					      command: 'pack'
 | 
				
			||||||
      packagesToPack: Jellyfin.Data/Jellyfin.Data.csproj;MediaBrowser.Common/MediaBrowser.Common.csproj;MediaBrowser.Controller/MediaBrowser.Controller.csproj;MediaBrowser.Model/MediaBrowser.Model.csproj;Emby.Naming/Emby.Naming.csproj
 | 
					      packagesToPack: 'Jellyfin.Data/Jellyfin.Data.csproj;MediaBrowser.Common/MediaBrowser.Common.csproj;MediaBrowser.Controller/MediaBrowser.Controller.csproj;MediaBrowser.Model/MediaBrowser.Model.csproj;Emby.Naming/Emby.Naming.csproj'
 | 
				
			||||||
      packDestination: '$(Build.ArtifactStagingDirectory)'
 | 
					      versioningScheme: 'off'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - task: DotNetCoreCLI@2
 | 
				
			||||||
 | 
					    displayName: 'Build Unstable Nuget packages'
 | 
				
			||||||
 | 
					    inputs:
 | 
				
			||||||
 | 
					      command: 'custom'
 | 
				
			||||||
 | 
					      projects: |
 | 
				
			||||||
 | 
					        Jellyfin.Data/Jellyfin.Data.csproj
 | 
				
			||||||
 | 
					        MediaBrowser.Common/MediaBrowser.Common.csproj
 | 
				
			||||||
 | 
					        MediaBrowser.Controller/MediaBrowser.Controller.csproj
 | 
				
			||||||
 | 
					        MediaBrowser.Model/MediaBrowser.Model.csproj
 | 
				
			||||||
 | 
					        Emby.Naming/Emby.Naming.csproj
 | 
				
			||||||
 | 
					      custom: 'pack'
 | 
				
			||||||
 | 
					      arguments: '--version-suffix $(Build.BuildNumber) -o $(Build.ArtifactStagingDirectory)'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - task: PublishBuildArtifacts@1
 | 
				
			||||||
 | 
					    displayName: 'Publish Nuget packages'
 | 
				
			||||||
 | 
					    inputs:
 | 
				
			||||||
 | 
					      pathToPublish: $(Build.ArtifactStagingDirectory)
 | 
				
			||||||
 | 
					      artifactName: Jellyfin Nuget Packages
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  - task: NuGetCommand@2
 | 
					  - task: NuGetCommand@2
 | 
				
			||||||
 | 
					    displayName: 'Push Nuget packages to feed'
 | 
				
			||||||
 | 
					    condition: startsWith(variables['Build.SourceBranch'], 'refs/tags')
 | 
				
			||||||
    inputs:
 | 
					    inputs:
 | 
				
			||||||
      command: 'push'
 | 
					      command: 'push'
 | 
				
			||||||
      packagesToPush: '$(Build.ArtifactStagingDirectory)/**/*.nupkg'
 | 
					      packagesToPush: '$(Build.ArtifactStagingDirectory)/**/*.nupkg'
 | 
				
			||||||
 | 
				
			|||||||
@ -16,6 +16,7 @@
 | 
				
			|||||||
 - [bugfixin](https://github.com/bugfixin)
 | 
					 - [bugfixin](https://github.com/bugfixin)
 | 
				
			||||||
 - [chaosinnovator](https://github.com/chaosinnovator)
 | 
					 - [chaosinnovator](https://github.com/chaosinnovator)
 | 
				
			||||||
 - [ckcr4lyf](https://github.com/ckcr4lyf)
 | 
					 - [ckcr4lyf](https://github.com/ckcr4lyf)
 | 
				
			||||||
 | 
					 - [ConfusedPolarBear](https://github.com/ConfusedPolarBear)
 | 
				
			||||||
 - [crankdoofus](https://github.com/crankdoofus)
 | 
					 - [crankdoofus](https://github.com/crankdoofus)
 | 
				
			||||||
 - [crobibero](https://github.com/crobibero)
 | 
					 - [crobibero](https://github.com/crobibero)
 | 
				
			||||||
 - [cromefire](https://github.com/cromefire)
 | 
					 - [cromefire](https://github.com/cromefire)
 | 
				
			||||||
 | 
				
			|||||||
@ -13,7 +13,7 @@ namespace Emby.Dlna.Common
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        public string Name { get; set; }
 | 
					        public string Name { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public List<Argument> ArgumentList { get; set; }
 | 
					        public List<Argument> ArgumentList { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <inheritdoc />
 | 
					        /// <inheritdoc />
 | 
				
			||||||
        public override string ToString()
 | 
					        public override string ToString()
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,7 @@
 | 
				
			|||||||
#pragma warning disable CS1591
 | 
					#pragma warning disable CS1591
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using System;
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Emby.Dlna.Common
 | 
					namespace Emby.Dlna.Common
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@ -17,7 +18,7 @@ namespace Emby.Dlna.Common
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        public bool SendsEvents { get; set; }
 | 
					        public bool SendsEvents { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public string[] AllowedValues { get; set; }
 | 
					        public IReadOnlyList<string> AllowedValues { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <inheritdoc />
 | 
					        /// <inheritdoc />
 | 
				
			||||||
        public override string ToString()
 | 
					        public override string ToString()
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,6 @@
 | 
				
			|||||||
#nullable enable
 | 
					#nullable enable
 | 
				
			||||||
#pragma warning disable CS1591
 | 
					#pragma warning disable CS1591
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using System.Collections.Generic;
 | 
					 | 
				
			||||||
using Emby.Dlna.Configuration;
 | 
					using Emby.Dlna.Configuration;
 | 
				
			||||||
using MediaBrowser.Common.Configuration;
 | 
					using MediaBrowser.Common.Configuration;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -14,19 +13,4 @@ namespace Emby.Dlna
 | 
				
			|||||||
            return manager.GetConfiguration<DlnaOptions>("dlna");
 | 
					            return manager.GetConfiguration<DlnaOptions>("dlna");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    public class DlnaConfigurationFactory : IConfigurationFactory
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        public IEnumerable<ConfigurationStore> GetConfigurations()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return new ConfigurationStore[]
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                new ConfigurationStore
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    Key = "dlna",
 | 
					 | 
				
			||||||
                    ConfigurationType = typeof (DlnaOptions)
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            };
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -9,22 +9,20 @@ using Microsoft.Extensions.Logging;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace Emby.Dlna.ConnectionManager
 | 
					namespace Emby.Dlna.ConnectionManager
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public class ConnectionManager : BaseService, IConnectionManager
 | 
					    public class ConnectionManagerService : BaseService, IConnectionManager
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        private readonly IDlnaManager _dlna;
 | 
					        private readonly IDlnaManager _dlna;
 | 
				
			||||||
        private readonly ILogger _logger;
 | 
					 | 
				
			||||||
        private readonly IServerConfigurationManager _config;
 | 
					        private readonly IServerConfigurationManager _config;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public ConnectionManager(
 | 
					        public ConnectionManagerService(
 | 
				
			||||||
            IDlnaManager dlna,
 | 
					            IDlnaManager dlna,
 | 
				
			||||||
            IServerConfigurationManager config,
 | 
					            IServerConfigurationManager config,
 | 
				
			||||||
            ILogger<ConnectionManager> logger,
 | 
					            ILogger<ConnectionManagerService> logger,
 | 
				
			||||||
            IHttpClient httpClient)
 | 
					            IHttpClient httpClient)
 | 
				
			||||||
            : base(logger, httpClient)
 | 
					            : base(logger, httpClient)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _dlna = dlna;
 | 
					            _dlna = dlna;
 | 
				
			||||||
            _config = config;
 | 
					            _config = config;
 | 
				
			||||||
            _logger = logger;
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <inheritdoc />
 | 
					        /// <inheritdoc />
 | 
				
			||||||
@ -39,7 +37,7 @@ namespace Emby.Dlna.ConnectionManager
 | 
				
			|||||||
            var profile = _dlna.GetProfile(request.Headers) ??
 | 
					            var profile = _dlna.GetProfile(request.Headers) ??
 | 
				
			||||||
                         _dlna.GetDefaultProfile();
 | 
					                         _dlna.GetDefaultProfile();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return new ControlHandler(_config, _logger, profile).ProcessControlRequestAsync(request);
 | 
					            return new ControlHandler(_config, Logger, profile).ProcessControlRequestAsync(request);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -44,7 +44,7 @@ namespace Emby.Dlna.ConnectionManager
 | 
				
			|||||||
                DataType = "string",
 | 
					                DataType = "string",
 | 
				
			||||||
                SendsEvents = false,
 | 
					                SendsEvents = false,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                AllowedValues = new string[]
 | 
					                AllowedValues = new[]
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    "OK",
 | 
					                    "OK",
 | 
				
			||||||
                    "ContentFormatMismatch",
 | 
					                    "ContentFormatMismatch",
 | 
				
			||||||
@ -67,7 +67,7 @@ namespace Emby.Dlna.ConnectionManager
 | 
				
			|||||||
                DataType = "string",
 | 
					                DataType = "string",
 | 
				
			||||||
                SendsEvents = false,
 | 
					                SendsEvents = false,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                AllowedValues = new string[]
 | 
					                AllowedValues = new[]
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    "Output",
 | 
					                    "Output",
 | 
				
			||||||
                    "Input"
 | 
					                    "Input"
 | 
				
			||||||
 | 
				
			|||||||
@ -19,7 +19,7 @@ using Microsoft.Extensions.Logging;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace Emby.Dlna.ContentDirectory
 | 
					namespace Emby.Dlna.ContentDirectory
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public class ContentDirectory : BaseService, IContentDirectory
 | 
					    public class ContentDirectoryService : BaseService, IContentDirectory
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        private readonly ILibraryManager _libraryManager;
 | 
					        private readonly ILibraryManager _libraryManager;
 | 
				
			||||||
        private readonly IImageProcessor _imageProcessor;
 | 
					        private readonly IImageProcessor _imageProcessor;
 | 
				
			||||||
@ -33,14 +33,14 @@ namespace Emby.Dlna.ContentDirectory
 | 
				
			|||||||
        private readonly IMediaEncoder _mediaEncoder;
 | 
					        private readonly IMediaEncoder _mediaEncoder;
 | 
				
			||||||
        private readonly ITVSeriesManager _tvSeriesManager;
 | 
					        private readonly ITVSeriesManager _tvSeriesManager;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public ContentDirectory(
 | 
					        public ContentDirectoryService(
 | 
				
			||||||
            IDlnaManager dlna,
 | 
					            IDlnaManager dlna,
 | 
				
			||||||
            IUserDataManager userDataManager,
 | 
					            IUserDataManager userDataManager,
 | 
				
			||||||
            IImageProcessor imageProcessor,
 | 
					            IImageProcessor imageProcessor,
 | 
				
			||||||
            ILibraryManager libraryManager,
 | 
					            ILibraryManager libraryManager,
 | 
				
			||||||
            IServerConfigurationManager config,
 | 
					            IServerConfigurationManager config,
 | 
				
			||||||
            IUserManager userManager,
 | 
					            IUserManager userManager,
 | 
				
			||||||
            ILogger<ContentDirectory> logger,
 | 
					            ILogger<ContentDirectoryService> logger,
 | 
				
			||||||
            IHttpClient httpClient,
 | 
					            IHttpClient httpClient,
 | 
				
			||||||
            ILocalizationManager localization,
 | 
					            ILocalizationManager localization,
 | 
				
			||||||
            IMediaSourceManager mediaSourceManager,
 | 
					            IMediaSourceManager mediaSourceManager,
 | 
				
			||||||
@ -10,7 +10,8 @@ namespace Emby.Dlna.ContentDirectory
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        public string GetXml()
 | 
					        public string GetXml()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return new ServiceXmlBuilder().GetXml(new ServiceActionListBuilder().GetActions(),
 | 
					            return new ServiceXmlBuilder().GetXml(
 | 
				
			||||||
 | 
					                new ServiceActionListBuilder().GetActions(),
 | 
				
			||||||
                GetStateVariables());
 | 
					                GetStateVariables());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -101,7 +102,7 @@ namespace Emby.Dlna.ContentDirectory
 | 
				
			|||||||
                DataType = "string",
 | 
					                DataType = "string",
 | 
				
			||||||
                SendsEvents = false,
 | 
					                SendsEvents = false,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                AllowedValues = new string[]
 | 
					                AllowedValues = new[]
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    "BrowseMetadata",
 | 
					                    "BrowseMetadata",
 | 
				
			||||||
                    "BrowseDirectChildren"
 | 
					                    "BrowseDirectChildren"
 | 
				
			||||||
 | 
				
			|||||||
@ -40,6 +40,11 @@ namespace Emby.Dlna.ContentDirectory
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    public class ControlHandler : BaseControlHandler
 | 
					    public class ControlHandler : BaseControlHandler
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					        private const string NsDc = "http://purl.org/dc/elements/1.1/";
 | 
				
			||||||
 | 
					        private const string NsDidl = "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/";
 | 
				
			||||||
 | 
					        private const string NsDlna = "urn:schemas-dlna-org:metadata-1-0/";
 | 
				
			||||||
 | 
					        private const string NsUpnp = "urn:schemas-upnp-org:metadata-1-0/upnp/";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private readonly ILibraryManager _libraryManager;
 | 
					        private readonly ILibraryManager _libraryManager;
 | 
				
			||||||
        private readonly IUserDataManager _userDataManager;
 | 
					        private readonly IUserDataManager _userDataManager;
 | 
				
			||||||
        private readonly IServerConfigurationManager _config;
 | 
					        private readonly IServerConfigurationManager _config;
 | 
				
			||||||
@ -47,11 +52,6 @@ namespace Emby.Dlna.ContentDirectory
 | 
				
			|||||||
        private readonly IUserViewManager _userViewManager;
 | 
					        private readonly IUserViewManager _userViewManager;
 | 
				
			||||||
        private readonly ITVSeriesManager _tvSeriesManager;
 | 
					        private readonly ITVSeriesManager _tvSeriesManager;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private const string NS_DC = "http://purl.org/dc/elements/1.1/";
 | 
					 | 
				
			||||||
        private const string NS_DIDL = "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/";
 | 
					 | 
				
			||||||
        private const string NS_DLNA = "urn:schemas-dlna-org:metadata-1-0/";
 | 
					 | 
				
			||||||
        private const string NS_UPNP = "urn:schemas-upnp-org:metadata-1-0/upnp/";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private readonly int _systemUpdateId;
 | 
					        private readonly int _systemUpdateId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private readonly DidlBuilder _didlBuilder;
 | 
					        private readonly DidlBuilder _didlBuilder;
 | 
				
			||||||
@ -181,7 +181,11 @@ namespace Emby.Dlna.ContentDirectory
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            userdata.PlaybackPositionTicks = TimeSpan.FromSeconds(newbookmark).Ticks;
 | 
					            userdata.PlaybackPositionTicks = TimeSpan.FromSeconds(newbookmark).Ticks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            _userDataManager.SaveUserData(_user, item, userdata, UserDataSaveReason.TogglePlayed,
 | 
					            _userDataManager.SaveUserData(
 | 
				
			||||||
 | 
					                _user,
 | 
				
			||||||
 | 
					                item,
 | 
				
			||||||
 | 
					                userdata,
 | 
				
			||||||
 | 
					                UserDataSaveReason.TogglePlayed,
 | 
				
			||||||
                CancellationToken.None);
 | 
					                CancellationToken.None);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -253,7 +257,7 @@ namespace Emby.Dlna.ContentDirectory
 | 
				
			|||||||
            var id = sparams["ObjectID"];
 | 
					            var id = sparams["ObjectID"];
 | 
				
			||||||
            var flag = sparams["BrowseFlag"];
 | 
					            var flag = sparams["BrowseFlag"];
 | 
				
			||||||
            var filter = new Filter(GetValueOrDefault(sparams, "Filter", "*"));
 | 
					            var filter = new Filter(GetValueOrDefault(sparams, "Filter", "*"));
 | 
				
			||||||
            var sortCriteria = new SortCriteria(GetValueOrDefault(sparams, "SortCriteria", ""));
 | 
					            var sortCriteria = new SortCriteria(GetValueOrDefault(sparams, "SortCriteria", string.Empty));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var provided = 0;
 | 
					            var provided = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -286,18 +290,17 @@ namespace Emby.Dlna.ContentDirectory
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                using (var writer = XmlWriter.Create(builder, settings))
 | 
					                using (var writer = XmlWriter.Create(builder, settings))
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    writer.WriteStartElement(string.Empty, "DIDL-Lite", NS_DIDL);
 | 
					                    writer.WriteStartElement(string.Empty, "DIDL-Lite", NsDidl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    writer.WriteAttributeString("xmlns", "dc", null, NS_DC);
 | 
					                    writer.WriteAttributeString("xmlns", "dc", null, NsDc);
 | 
				
			||||||
                    writer.WriteAttributeString("xmlns", "dlna", null, NS_DLNA);
 | 
					                    writer.WriteAttributeString("xmlns", "dlna", null, NsDlna);
 | 
				
			||||||
                    writer.WriteAttributeString("xmlns", "upnp", null, NS_UPNP);
 | 
					                    writer.WriteAttributeString("xmlns", "upnp", null, NsUpnp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    DidlBuilder.WriteXmlRootAttributes(_profile, writer);
 | 
					                    DidlBuilder.WriteXmlRootAttributes(_profile, writer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    var serverItem = GetItemFromObjectId(id);
 | 
					                    var serverItem = GetItemFromObjectId(id);
 | 
				
			||||||
                    var item = serverItem.Item;
 | 
					                    var item = serverItem.Item;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
                    if (string.Equals(flag, "BrowseMetadata", StringComparison.Ordinal))
 | 
					                    if (string.Equals(flag, "BrowseMetadata", StringComparison.Ordinal))
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        totalCount = 1;
 | 
					                        totalCount = 1;
 | 
				
			||||||
@ -362,8 +365,8 @@ namespace Emby.Dlna.ContentDirectory
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        private void HandleSearch(XmlWriter xmlWriter, IDictionary<string, string> sparams, string deviceId)
 | 
					        private void HandleSearch(XmlWriter xmlWriter, IDictionary<string, string> sparams, string deviceId)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var searchCriteria = new SearchCriteria(GetValueOrDefault(sparams, "SearchCriteria", ""));
 | 
					            var searchCriteria = new SearchCriteria(GetValueOrDefault(sparams, "SearchCriteria", string.Empty));
 | 
				
			||||||
            var sortCriteria = new SortCriteria(GetValueOrDefault(sparams, "SortCriteria", ""));
 | 
					            var sortCriteria = new SortCriteria(GetValueOrDefault(sparams, "SortCriteria", string.Empty));
 | 
				
			||||||
            var filter = new Filter(GetValueOrDefault(sparams, "Filter", "*"));
 | 
					            var filter = new Filter(GetValueOrDefault(sparams, "Filter", "*"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // sort example: dc:title, dc:date
 | 
					            // sort example: dc:title, dc:date
 | 
				
			||||||
@ -397,11 +400,11 @@ namespace Emby.Dlna.ContentDirectory
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                using (var writer = XmlWriter.Create(builder, settings))
 | 
					                using (var writer = XmlWriter.Create(builder, settings))
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    writer.WriteStartElement(string.Empty, "DIDL-Lite", NS_DIDL);
 | 
					                    writer.WriteStartElement(string.Empty, "DIDL-Lite", NsDidl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    writer.WriteAttributeString("xmlns", "dc", null, NS_DC);
 | 
					                    writer.WriteAttributeString("xmlns", "dc", null, NsDc);
 | 
				
			||||||
                    writer.WriteAttributeString("xmlns", "dlna", null, NS_DLNA);
 | 
					                    writer.WriteAttributeString("xmlns", "dlna", null, NsDlna);
 | 
				
			||||||
                    writer.WriteAttributeString("xmlns", "upnp", null, NS_UPNP);
 | 
					                    writer.WriteAttributeString("xmlns", "upnp", null, NsUpnp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    DidlBuilder.WriteXmlRootAttributes(_profile, writer);
 | 
					                    DidlBuilder.WriteXmlRootAttributes(_profile, writer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -783,11 +786,14 @@ namespace Emby.Dlna.ContentDirectory
 | 
				
			|||||||
                })
 | 
					                })
 | 
				
			||||||
                .ToArray();
 | 
					                .ToArray();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return ApplyPaging(new QueryResult<ServerItem>
 | 
					            return ApplyPaging(
 | 
				
			||||||
 | 
					                new QueryResult<ServerItem>
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Items = folders,
 | 
					                    Items = folders,
 | 
				
			||||||
                    TotalRecordCount = folders.Length
 | 
					                    TotalRecordCount = folders.Length
 | 
				
			||||||
            }, startIndex, limit);
 | 
					                },
 | 
				
			||||||
 | 
					                startIndex,
 | 
				
			||||||
 | 
					                limit);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private QueryResult<ServerItem> GetTvFolders(BaseItem item, User user, StubType? stubType, SortCriteria sort, int? startIndex, int? limit)
 | 
					        private QueryResult<ServerItem> GetTvFolders(BaseItem item, User user, StubType? stubType, SortCriteria sort, int? startIndex, int? limit)
 | 
				
			||||||
@ -1135,14 +1141,16 @@ namespace Emby.Dlna.ContentDirectory
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            query.OrderBy = Array.Empty<(string, SortOrder)>();
 | 
					            query.OrderBy = Array.Empty<(string, SortOrder)>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var items = _userViewManager.GetLatestItems(new LatestItemsQuery
 | 
					            var items = _userViewManager.GetLatestItems(
 | 
				
			||||||
 | 
					                new LatestItemsQuery
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    UserId = user.Id,
 | 
					                    UserId = user.Id,
 | 
				
			||||||
                    Limit = 50,
 | 
					                    Limit = 50,
 | 
				
			||||||
                    IncludeItemTypes = new[] { nameof(Audio) },
 | 
					                    IncludeItemTypes = new[] { nameof(Audio) },
 | 
				
			||||||
                    ParentId = parent?.Id ?? Guid.Empty,
 | 
					                    ParentId = parent?.Id ?? Guid.Empty,
 | 
				
			||||||
                    GroupItems = true
 | 
					                    GroupItems = true
 | 
				
			||||||
            }, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToArray();
 | 
					                },
 | 
				
			||||||
 | 
					                query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToArray();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return ToResult(items);
 | 
					            return ToResult(items);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -1151,12 +1159,15 @@ namespace Emby.Dlna.ContentDirectory
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            query.OrderBy = Array.Empty<(string, SortOrder)>();
 | 
					            query.OrderBy = Array.Empty<(string, SortOrder)>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var result = _tvSeriesManager.GetNextUp(new NextUpQuery
 | 
					            var result = _tvSeriesManager.GetNextUp(
 | 
				
			||||||
 | 
					                new NextUpQuery
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Limit = query.Limit,
 | 
					                    Limit = query.Limit,
 | 
				
			||||||
                    StartIndex = query.StartIndex,
 | 
					                    StartIndex = query.StartIndex,
 | 
				
			||||||
                    UserId = query.User.Id
 | 
					                    UserId = query.User.Id
 | 
				
			||||||
            }, new[] { parent }, query.DtoOptions);
 | 
					                },
 | 
				
			||||||
 | 
					                new[] { parent },
 | 
				
			||||||
 | 
					                query.DtoOptions);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return ToResult(result);
 | 
					            return ToResult(result);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -1165,14 +1176,16 @@ namespace Emby.Dlna.ContentDirectory
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            query.OrderBy = Array.Empty<(string, SortOrder)>();
 | 
					            query.OrderBy = Array.Empty<(string, SortOrder)>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var items = _userViewManager.GetLatestItems(new LatestItemsQuery
 | 
					            var items = _userViewManager.GetLatestItems(
 | 
				
			||||||
 | 
					                new LatestItemsQuery
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    UserId = user.Id,
 | 
					                    UserId = user.Id,
 | 
				
			||||||
                    Limit = 50,
 | 
					                    Limit = 50,
 | 
				
			||||||
                    IncludeItemTypes = new[] { typeof(Episode).Name },
 | 
					                    IncludeItemTypes = new[] { typeof(Episode).Name },
 | 
				
			||||||
                    ParentId = parent == null ? Guid.Empty : parent.Id,
 | 
					                    ParentId = parent == null ? Guid.Empty : parent.Id,
 | 
				
			||||||
                    GroupItems = false
 | 
					                    GroupItems = false
 | 
				
			||||||
            }, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToArray();
 | 
					                },
 | 
				
			||||||
 | 
					                query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToArray();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return ToResult(items);
 | 
					            return ToResult(items);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -1189,7 +1202,8 @@ namespace Emby.Dlna.ContentDirectory
 | 
				
			|||||||
                    IncludeItemTypes = new[] { nameof(Movie) },
 | 
					                    IncludeItemTypes = new[] { nameof(Movie) },
 | 
				
			||||||
                    ParentId = parent?.Id ?? Guid.Empty,
 | 
					                    ParentId = parent?.Id ?? Guid.Empty,
 | 
				
			||||||
                    GroupItems = true
 | 
					                    GroupItems = true
 | 
				
			||||||
            }, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToArray();
 | 
					                },
 | 
				
			||||||
 | 
					                query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToArray();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return ToResult(items);
 | 
					            return ToResult(items);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -1354,44 +1368,4 @@ namespace Emby.Dlna.ContentDirectory
 | 
				
			|||||||
            return new ServerItem(_libraryManager.GetUserRootFolder());
 | 
					            return new ServerItem(_libraryManager.GetUserRootFolder());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    internal class ServerItem
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        public BaseItem Item { get; set; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public StubType? StubType { get; set; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public ServerItem(BaseItem item)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            Item = item;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (item is IItemByName && !(item is Folder))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                StubType = Dlna.ContentDirectory.StubType.Folder;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public enum StubType
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        Folder = 0,
 | 
					 | 
				
			||||||
        Latest = 2,
 | 
					 | 
				
			||||||
        Playlists = 3,
 | 
					 | 
				
			||||||
        Albums = 4,
 | 
					 | 
				
			||||||
        AlbumArtists = 5,
 | 
					 | 
				
			||||||
        Artists = 6,
 | 
					 | 
				
			||||||
        Songs = 7,
 | 
					 | 
				
			||||||
        Genres = 8,
 | 
					 | 
				
			||||||
        FavoriteSongs = 9,
 | 
					 | 
				
			||||||
        FavoriteArtists = 10,
 | 
					 | 
				
			||||||
        FavoriteAlbums = 11,
 | 
					 | 
				
			||||||
        ContinueWatching = 12,
 | 
					 | 
				
			||||||
        Movies = 13,
 | 
					 | 
				
			||||||
        Collections = 14,
 | 
					 | 
				
			||||||
        Favorites = 15,
 | 
					 | 
				
			||||||
        NextUp = 16,
 | 
					 | 
				
			||||||
        Series = 17,
 | 
					 | 
				
			||||||
        FavoriteSeries = 18,
 | 
					 | 
				
			||||||
        FavoriteEpisodes = 19
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										23
									
								
								Emby.Dlna/ContentDirectory/ServerItem.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								Emby.Dlna/ContentDirectory/ServerItem.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,23 @@
 | 
				
			|||||||
 | 
					#pragma warning disable CS1591
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using MediaBrowser.Controller.Entities;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Emby.Dlna.ContentDirectory
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    internal class ServerItem
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public ServerItem(BaseItem item)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            Item = item;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (item is IItemByName && !(item is Folder))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                StubType = Dlna.ContentDirectory.StubType.Folder;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public BaseItem Item { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public StubType? StubType { get; set; }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										28
									
								
								Emby.Dlna/ContentDirectory/StubType.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								Emby.Dlna/ContentDirectory/StubType.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					#pragma warning disable CS1591
 | 
				
			||||||
 | 
					#pragma warning disable SA1602
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Emby.Dlna.ContentDirectory
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public enum StubType
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Folder = 0,
 | 
				
			||||||
 | 
					        Latest = 2,
 | 
				
			||||||
 | 
					        Playlists = 3,
 | 
				
			||||||
 | 
					        Albums = 4,
 | 
				
			||||||
 | 
					        AlbumArtists = 5,
 | 
				
			||||||
 | 
					        Artists = 6,
 | 
				
			||||||
 | 
					        Songs = 7,
 | 
				
			||||||
 | 
					        Genres = 8,
 | 
				
			||||||
 | 
					        FavoriteSongs = 9,
 | 
				
			||||||
 | 
					        FavoriteArtists = 10,
 | 
				
			||||||
 | 
					        FavoriteAlbums = 11,
 | 
				
			||||||
 | 
					        ContinueWatching = 12,
 | 
				
			||||||
 | 
					        Movies = 13,
 | 
				
			||||||
 | 
					        Collections = 14,
 | 
				
			||||||
 | 
					        Favorites = 15,
 | 
				
			||||||
 | 
					        NextUp = 16,
 | 
				
			||||||
 | 
					        Series = 17,
 | 
				
			||||||
 | 
					        FavoriteSeries = 18,
 | 
				
			||||||
 | 
					        FavoriteEpisodes = 19
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -7,17 +7,17 @@ namespace Emby.Dlna
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    public class ControlRequest
 | 
					    public class ControlRequest
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        public IHeaderDictionary Headers { get; set; }
 | 
					        public ControlRequest(IHeaderDictionary headers)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            Headers = headers;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public IHeaderDictionary Headers { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public Stream InputXml { get; set; }
 | 
					        public Stream InputXml { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public string TargetServerUuId { get; set; }
 | 
					        public string TargetServerUuId { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public string RequestedUrl { get; set; }
 | 
					        public string RequestedUrl { get; set; }
 | 
				
			||||||
 | 
					 | 
				
			||||||
        public ControlRequest()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            Headers = new HeaderDictionary();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -11,7 +11,7 @@ namespace Emby.Dlna
 | 
				
			|||||||
            Headers = new Dictionary<string, string>();
 | 
					            Headers = new Dictionary<string, string>();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public IDictionary<string, string> Headers { get; set; }
 | 
					        public IDictionary<string, string> Headers { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public string Xml { get; set; }
 | 
					        public string Xml { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -34,12 +34,12 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    public class DidlBuilder
 | 
					    public class DidlBuilder
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        private readonly CultureInfo _usCulture = new CultureInfo("en-US");
 | 
					        private const string NsDidl = "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/";
 | 
				
			||||||
 | 
					        private const string NsDc = "http://purl.org/dc/elements/1.1/";
 | 
				
			||||||
 | 
					        private const string NsUpnp = "urn:schemas-upnp-org:metadata-1-0/upnp/";
 | 
				
			||||||
 | 
					        private const string NsDlna = "urn:schemas-dlna-org:metadata-1-0/";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private const string NS_DIDL = "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/";
 | 
					        private readonly CultureInfo _usCulture = new CultureInfo("en-US");
 | 
				
			||||||
        private const string NS_DC = "http://purl.org/dc/elements/1.1/";
 | 
					 | 
				
			||||||
        private const string NS_UPNP = "urn:schemas-upnp-org:metadata-1-0/upnp/";
 | 
					 | 
				
			||||||
        private const string NS_DLNA = "urn:schemas-dlna-org:metadata-1-0/";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private readonly DeviceProfile _profile;
 | 
					        private readonly DeviceProfile _profile;
 | 
				
			||||||
        private readonly IImageProcessor _imageProcessor;
 | 
					        private readonly IImageProcessor _imageProcessor;
 | 
				
			||||||
@ -100,11 +100,11 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    // writer.WriteStartDocument();
 | 
					                    // writer.WriteStartDocument();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    writer.WriteStartElement(string.Empty, "DIDL-Lite", NS_DIDL);
 | 
					                    writer.WriteStartElement(string.Empty, "DIDL-Lite", NsDidl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    writer.WriteAttributeString("xmlns", "dc", null, NS_DC);
 | 
					                    writer.WriteAttributeString("xmlns", "dc", null, NsDc);
 | 
				
			||||||
                    writer.WriteAttributeString("xmlns", "dlna", null, NS_DLNA);
 | 
					                    writer.WriteAttributeString("xmlns", "dlna", null, NsDlna);
 | 
				
			||||||
                    writer.WriteAttributeString("xmlns", "upnp", null, NS_UPNP);
 | 
					                    writer.WriteAttributeString("xmlns", "upnp", null, NsUpnp);
 | 
				
			||||||
                    // didl.SetAttribute("xmlns:sec", NS_SEC);
 | 
					                    // didl.SetAttribute("xmlns:sec", NS_SEC);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    WriteXmlRootAttributes(_profile, writer);
 | 
					                    WriteXmlRootAttributes(_profile, writer);
 | 
				
			||||||
@ -147,7 +147,7 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            var clientId = GetClientId(item, null);
 | 
					            var clientId = GetClientId(item, null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            writer.WriteStartElement(string.Empty, "item", NS_DIDL);
 | 
					            writer.WriteStartElement(string.Empty, "item", NsDidl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            writer.WriteAttributeString("restricted", "1");
 | 
					            writer.WriteAttributeString("restricted", "1");
 | 
				
			||||||
            writer.WriteAttributeString("id", clientId);
 | 
					            writer.WriteAttributeString("id", clientId);
 | 
				
			||||||
@ -207,7 +207,8 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
            var targetWidth = streamInfo.TargetWidth;
 | 
					            var targetWidth = streamInfo.TargetWidth;
 | 
				
			||||||
            var targetHeight = streamInfo.TargetHeight;
 | 
					            var targetHeight = streamInfo.TargetHeight;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var contentFeatureList = new ContentFeatureBuilder(_profile).BuildVideoHeader(streamInfo.Container,
 | 
					            var contentFeatureList = new ContentFeatureBuilder(_profile).BuildVideoHeader(
 | 
				
			||||||
 | 
					                streamInfo.Container,
 | 
				
			||||||
                streamInfo.TargetVideoCodec.FirstOrDefault(),
 | 
					                streamInfo.TargetVideoCodec.FirstOrDefault(),
 | 
				
			||||||
                streamInfo.TargetAudioCodec.FirstOrDefault(),
 | 
					                streamInfo.TargetAudioCodec.FirstOrDefault(),
 | 
				
			||||||
                targetWidth,
 | 
					                targetWidth,
 | 
				
			||||||
@ -279,7 +280,7 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
            else if (string.Equals(subtitleMode, "smi", StringComparison.OrdinalIgnoreCase))
 | 
					            else if (string.Equals(subtitleMode, "smi", StringComparison.OrdinalIgnoreCase))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                writer.WriteStartElement(string.Empty, "res", NS_DIDL);
 | 
					                writer.WriteStartElement(string.Empty, "res", NsDidl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                writer.WriteAttributeString("protocolInfo", "http-get:*:smi/caption:*");
 | 
					                writer.WriteAttributeString("protocolInfo", "http-get:*:smi/caption:*");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -288,7 +289,7 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                writer.WriteStartElement(string.Empty, "res", NS_DIDL);
 | 
					                writer.WriteStartElement(string.Empty, "res", NsDidl);
 | 
				
			||||||
                var protocolInfo = string.Format(
 | 
					                var protocolInfo = string.Format(
 | 
				
			||||||
                    CultureInfo.InvariantCulture,
 | 
					                    CultureInfo.InvariantCulture,
 | 
				
			||||||
                    "http-get:*:text/{0}:*",
 | 
					                    "http-get:*:text/{0}:*",
 | 
				
			||||||
@ -304,7 +305,7 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        private void AddVideoResource(XmlWriter writer, Filter filter, string contentFeatures, StreamInfo streamInfo)
 | 
					        private void AddVideoResource(XmlWriter writer, Filter filter, string contentFeatures, StreamInfo streamInfo)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            writer.WriteStartElement(string.Empty, "res", NS_DIDL);
 | 
					            writer.WriteStartElement(string.Empty, "res", NsDidl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var url = NormalizeDlnaMediaUrl(streamInfo.ToUrl(_serverAddress, _accessToken));
 | 
					            var url = NormalizeDlnaMediaUrl(streamInfo.ToUrl(_serverAddress, _accessToken));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -526,7 +527,7 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        private void AddAudioResource(XmlWriter writer, BaseItem audio, string deviceId, Filter filter, StreamInfo streamInfo = null)
 | 
					        private void AddAudioResource(XmlWriter writer, BaseItem audio, string deviceId, Filter filter, StreamInfo streamInfo = null)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            writer.WriteStartElement(string.Empty, "res", NS_DIDL);
 | 
					            writer.WriteStartElement(string.Empty, "res", NsDidl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (streamInfo == null)
 | 
					            if (streamInfo == null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@ -583,7 +584,8 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
                writer.WriteAttributeString("bitrate", targetAudioBitrate.Value.ToString(_usCulture));
 | 
					                writer.WriteAttributeString("bitrate", targetAudioBitrate.Value.ToString(_usCulture));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var mediaProfile = _profile.GetAudioMediaProfile(streamInfo.Container,
 | 
					            var mediaProfile = _profile.GetAudioMediaProfile(
 | 
				
			||||||
 | 
					                streamInfo.Container,
 | 
				
			||||||
                streamInfo.TargetAudioCodec.FirstOrDefault(),
 | 
					                streamInfo.TargetAudioCodec.FirstOrDefault(),
 | 
				
			||||||
                targetChannels,
 | 
					                targetChannels,
 | 
				
			||||||
                targetAudioBitrate,
 | 
					                targetAudioBitrate,
 | 
				
			||||||
@ -596,7 +598,8 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
                ? MimeTypes.GetMimeType(filename)
 | 
					                ? MimeTypes.GetMimeType(filename)
 | 
				
			||||||
                : mediaProfile.MimeType;
 | 
					                : mediaProfile.MimeType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var contentFeatures = new ContentFeatureBuilder(_profile).BuildAudioHeader(streamInfo.Container,
 | 
					            var contentFeatures = new ContentFeatureBuilder(_profile).BuildAudioHeader(
 | 
				
			||||||
 | 
					                streamInfo.Container,
 | 
				
			||||||
                streamInfo.TargetAudioCodec.FirstOrDefault(),
 | 
					                streamInfo.TargetAudioCodec.FirstOrDefault(),
 | 
				
			||||||
                targetAudioBitrate,
 | 
					                targetAudioBitrate,
 | 
				
			||||||
                targetSampleRate,
 | 
					                targetSampleRate,
 | 
				
			||||||
@ -627,7 +630,7 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        public void WriteFolderElement(XmlWriter writer, BaseItem folder, StubType? stubType, BaseItem context, int childCount, Filter filter, string requestedId = null)
 | 
					        public void WriteFolderElement(XmlWriter writer, BaseItem folder, StubType? stubType, BaseItem context, int childCount, Filter filter, string requestedId = null)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            writer.WriteStartElement(string.Empty, "container", NS_DIDL);
 | 
					            writer.WriteStartElement(string.Empty, "container", NsDidl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            writer.WriteAttributeString("restricted", "1");
 | 
					            writer.WriteAttributeString("restricted", "1");
 | 
				
			||||||
            writer.WriteAttributeString("searchable", "1");
 | 
					            writer.WriteAttributeString("searchable", "1");
 | 
				
			||||||
@ -714,7 +717,7 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
            // MediaMonkey for example won't display content without a title
 | 
					            // MediaMonkey for example won't display content without a title
 | 
				
			||||||
            // if (filter.Contains("dc:title"))
 | 
					            // if (filter.Contains("dc:title"))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                AddValue(writer, "dc", "title", GetDisplayName(item, itemStubType, context), NS_DC);
 | 
					                AddValue(writer, "dc", "title", GetDisplayName(item, itemStubType, context), NsDc);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            WriteObjectClass(writer, item, itemStubType);
 | 
					            WriteObjectClass(writer, item, itemStubType);
 | 
				
			||||||
@ -723,7 +726,7 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
            {
 | 
					            {
 | 
				
			||||||
                if (item.PremiereDate.HasValue)
 | 
					                if (item.PremiereDate.HasValue)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    AddValue(writer, "dc", "date", item.PremiereDate.Value.ToString("o", CultureInfo.InvariantCulture), NS_DC);
 | 
					                    AddValue(writer, "dc", "date", item.PremiereDate.Value.ToString("o", CultureInfo.InvariantCulture), NsDc);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -731,13 +734,13 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
            {
 | 
					            {
 | 
				
			||||||
                foreach (var genre in item.Genres)
 | 
					                foreach (var genre in item.Genres)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    AddValue(writer, "upnp", "genre", genre, NS_UPNP);
 | 
					                    AddValue(writer, "upnp", "genre", genre, NsUpnp);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            foreach (var studio in item.Studios)
 | 
					            foreach (var studio in item.Studios)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                AddValue(writer, "upnp", "publisher", studio, NS_UPNP);
 | 
					                AddValue(writer, "upnp", "publisher", studio, NsUpnp);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (!(item is Folder))
 | 
					            if (!(item is Folder))
 | 
				
			||||||
@ -748,28 +751,29 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    if (!string.IsNullOrWhiteSpace(desc))
 | 
					                    if (!string.IsNullOrWhiteSpace(desc))
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        AddValue(writer, "dc", "description", desc, NS_DC);
 | 
					                        AddValue(writer, "dc", "description", desc, NsDc);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                // if (filter.Contains("upnp:longDescription"))
 | 
					                // if (filter.Contains("upnp:longDescription"))
 | 
				
			||||||
                //{
 | 
					                // {
 | 
				
			||||||
                //    if (!string.IsNullOrWhiteSpace(item.Overview))
 | 
					                //    if (!string.IsNullOrWhiteSpace(item.Overview))
 | 
				
			||||||
                //    {
 | 
					                //    {
 | 
				
			||||||
                //        AddValue(writer, "upnp", "longDescription", item.Overview, NS_UPNP);
 | 
					                //        AddValue(writer, "upnp", "longDescription", item.Overview, NsUpnp);
 | 
				
			||||||
 | 
					                //    }
 | 
				
			||||||
                // }
 | 
					                // }
 | 
				
			||||||
                //}
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (!string.IsNullOrEmpty(item.OfficialRating))
 | 
					            if (!string.IsNullOrEmpty(item.OfficialRating))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                if (filter.Contains("dc:rating"))
 | 
					                if (filter.Contains("dc:rating"))
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    AddValue(writer, "dc", "rating", item.OfficialRating, NS_DC);
 | 
					                    AddValue(writer, "dc", "rating", item.OfficialRating, NsDc);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (filter.Contains("upnp:rating"))
 | 
					                if (filter.Contains("upnp:rating"))
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    AddValue(writer, "upnp", "rating", item.OfficialRating, NS_UPNP);
 | 
					                    AddValue(writer, "upnp", "rating", item.OfficialRating, NsUpnp);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -781,7 +785,7 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
            // More types here
 | 
					            // More types here
 | 
				
			||||||
            // http://oss.linn.co.uk/repos/Public/LibUpnpCil/DidlLite/UpnpAv/Test/TestDidlLite.cs
 | 
					            // http://oss.linn.co.uk/repos/Public/LibUpnpCil/DidlLite/UpnpAv/Test/TestDidlLite.cs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            writer.WriteStartElement("upnp", "class", NS_UPNP);
 | 
					            writer.WriteStartElement("upnp", "class", NsUpnp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (item.IsDisplayedAsFolder || stubType.HasValue)
 | 
					            if (item.IsDisplayedAsFolder || stubType.HasValue)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@ -882,7 +886,7 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
                var type = types.FirstOrDefault(i => string.Equals(i, actor.Type, StringComparison.OrdinalIgnoreCase) || string.Equals(i, actor.Role, StringComparison.OrdinalIgnoreCase))
 | 
					                var type = types.FirstOrDefault(i => string.Equals(i, actor.Type, StringComparison.OrdinalIgnoreCase) || string.Equals(i, actor.Role, StringComparison.OrdinalIgnoreCase))
 | 
				
			||||||
                    ?? PersonType.Actor;
 | 
					                    ?? PersonType.Actor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                AddValue(writer, "upnp", type.ToLowerInvariant(), actor.Name, NS_UPNP);
 | 
					                AddValue(writer, "upnp", type.ToLowerInvariant(), actor.Name, NsUpnp);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -896,8 +900,8 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
            {
 | 
					            {
 | 
				
			||||||
                foreach (var artist in hasArtists.Artists)
 | 
					                foreach (var artist in hasArtists.Artists)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    AddValue(writer, "upnp", "artist", artist, NS_UPNP);
 | 
					                    AddValue(writer, "upnp", "artist", artist, NsUpnp);
 | 
				
			||||||
                    AddValue(writer, "dc", "creator", artist, NS_DC);
 | 
					                    AddValue(writer, "dc", "creator", artist, NsDc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    // If it doesn't support album artists (musicvideo), then tag as both
 | 
					                    // If it doesn't support album artists (musicvideo), then tag as both
 | 
				
			||||||
                    if (hasAlbumArtists == null)
 | 
					                    if (hasAlbumArtists == null)
 | 
				
			||||||
@ -917,16 +921,16 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            if (!string.IsNullOrWhiteSpace(item.Album))
 | 
					            if (!string.IsNullOrWhiteSpace(item.Album))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                AddValue(writer, "upnp", "album", item.Album, NS_UPNP);
 | 
					                AddValue(writer, "upnp", "album", item.Album, NsUpnp);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (item.IndexNumber.HasValue)
 | 
					            if (item.IndexNumber.HasValue)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                AddValue(writer, "upnp", "originalTrackNumber", item.IndexNumber.Value.ToString(_usCulture), NS_UPNP);
 | 
					                AddValue(writer, "upnp", "originalTrackNumber", item.IndexNumber.Value.ToString(_usCulture), NsUpnp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (item is Episode)
 | 
					                if (item is Episode)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    AddValue(writer, "upnp", "episodeNumber", item.IndexNumber.Value.ToString(_usCulture), NS_UPNP);
 | 
					                    AddValue(writer, "upnp", "episodeNumber", item.IndexNumber.Value.ToString(_usCulture), NsUpnp);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -935,7 +939,7 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            try
 | 
					            try
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                writer.WriteStartElement("upnp", "artist", NS_UPNP);
 | 
					                writer.WriteStartElement("upnp", "artist", NsUpnp);
 | 
				
			||||||
                writer.WriteAttributeString("role", "AlbumArtist");
 | 
					                writer.WriteAttributeString("role", "AlbumArtist");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                writer.WriteString(name);
 | 
					                writer.WriteString(name);
 | 
				
			||||||
@ -971,14 +975,14 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            var albumartUrlInfo = GetImageUrl(imageInfo, _profile.MaxAlbumArtWidth, _profile.MaxAlbumArtHeight, "jpg");
 | 
					            var albumartUrlInfo = GetImageUrl(imageInfo, _profile.MaxAlbumArtWidth, _profile.MaxAlbumArtHeight, "jpg");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            writer.WriteStartElement("upnp", "albumArtURI", NS_UPNP);
 | 
					            writer.WriteStartElement("upnp", "albumArtURI", NsUpnp);
 | 
				
			||||||
            writer.WriteAttributeString("dlna", "profileID", NS_DLNA, _profile.AlbumArtPn);
 | 
					            writer.WriteAttributeString("dlna", "profileID", NsDlna, _profile.AlbumArtPn);
 | 
				
			||||||
            writer.WriteString(albumartUrlInfo.Url);
 | 
					            writer.WriteString(albumartUrlInfo.url);
 | 
				
			||||||
            writer.WriteFullEndElement();
 | 
					            writer.WriteFullEndElement();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // TOOD: Remove these default values
 | 
					            // TOOD: Remove these default values
 | 
				
			||||||
            var iconUrlInfo = GetImageUrl(imageInfo, _profile.MaxIconWidth ?? 48, _profile.MaxIconHeight ?? 48, "jpg");
 | 
					            var iconUrlInfo = GetImageUrl(imageInfo, _profile.MaxIconWidth ?? 48, _profile.MaxIconHeight ?? 48, "jpg");
 | 
				
			||||||
            writer.WriteElementString("upnp", "icon", NS_UPNP, iconUrlInfo.Url);
 | 
					            writer.WriteElementString("upnp", "icon", NsUpnp, iconUrlInfo.url);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (!_profile.EnableAlbumArtInDidl)
 | 
					            if (!_profile.EnableAlbumArtInDidl)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@ -1021,12 +1025,12 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            var albumartUrlInfo = GetImageUrl(imageInfo, maxWidth, maxHeight, format);
 | 
					            var albumartUrlInfo = GetImageUrl(imageInfo, maxWidth, maxHeight, format);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            writer.WriteStartElement(string.Empty, "res", NS_DIDL);
 | 
					            writer.WriteStartElement(string.Empty, "res", NsDidl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Images must have a reported size or many clients (Bubble upnp), will only use the first thumbnail
 | 
					            // Images must have a reported size or many clients (Bubble upnp), will only use the first thumbnail
 | 
				
			||||||
            // rather than using a larger one when available
 | 
					            // rather than using a larger one when available
 | 
				
			||||||
            var width = albumartUrlInfo.Width ?? maxWidth;
 | 
					            var width = albumartUrlInfo.width ?? maxWidth;
 | 
				
			||||||
            var height = albumartUrlInfo.Height ?? maxHeight;
 | 
					            var height = albumartUrlInfo.height ?? maxHeight;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var contentFeatures = new ContentFeatureBuilder(_profile)
 | 
					            var contentFeatures = new ContentFeatureBuilder(_profile)
 | 
				
			||||||
                .BuildImageHeader(format, width, height, imageInfo.IsDirectStream, org_Pn);
 | 
					                .BuildImageHeader(format, width, height, imageInfo.IsDirectStream, org_Pn);
 | 
				
			||||||
@ -1043,7 +1047,7 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
                "resolution",
 | 
					                "resolution",
 | 
				
			||||||
                string.Format(CultureInfo.InvariantCulture, "{0}x{1}", width, height));
 | 
					                string.Format(CultureInfo.InvariantCulture, "{0}x{1}", width, height));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            writer.WriteString(albumartUrlInfo.Url);
 | 
					            writer.WriteString(albumartUrlInfo.url);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            writer.WriteFullEndElement();
 | 
					            writer.WriteFullEndElement();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -1139,7 +1143,6 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            if (width == 0 || height == 0)
 | 
					            if (width == 0 || height == 0)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                // _imageProcessor.GetImageSize(item, imageInfo);
 | 
					 | 
				
			||||||
                width = null;
 | 
					                width = null;
 | 
				
			||||||
                height = null;
 | 
					                height = null;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@ -1149,18 +1152,6 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
                height = null;
 | 
					                height = null;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // try
 | 
					 | 
				
			||||||
            //{
 | 
					 | 
				
			||||||
            //    var size = _imageProcessor.GetImageSize(imageInfo);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            //    width = size.Width;
 | 
					 | 
				
			||||||
            //    height = size.Height;
 | 
					 | 
				
			||||||
            //}
 | 
					 | 
				
			||||||
            // catch
 | 
					 | 
				
			||||||
            //{
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            //}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var inputFormat = (Path.GetExtension(imageInfo.Path) ?? string.Empty)
 | 
					            var inputFormat = (Path.GetExtension(imageInfo.Path) ?? string.Empty)
 | 
				
			||||||
                .TrimStart('.')
 | 
					                .TrimStart('.')
 | 
				
			||||||
                .Replace("jpeg", "jpg", StringComparison.OrdinalIgnoreCase);
 | 
					                .Replace("jpeg", "jpg", StringComparison.OrdinalIgnoreCase);
 | 
				
			||||||
@ -1177,30 +1168,6 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
            };
 | 
					            };
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private class ImageDownloadInfo
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            internal Guid ItemId;
 | 
					 | 
				
			||||||
            internal string ImageTag;
 | 
					 | 
				
			||||||
            internal ImageType Type;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            internal int? Width;
 | 
					 | 
				
			||||||
            internal int? Height;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            internal bool IsDirectStream;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            internal string Format;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            internal ItemImageInfo ItemImageInfo;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private class ImageUrlInfo
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            internal string Url;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            internal int? Width;
 | 
					 | 
				
			||||||
            internal int? Height;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public static string GetClientId(BaseItem item, StubType? stubType)
 | 
					        public static string GetClientId(BaseItem item, StubType? stubType)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return GetClientId(item.Id, stubType);
 | 
					            return GetClientId(item.Id, stubType);
 | 
				
			||||||
@ -1218,7 +1185,7 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
            return id;
 | 
					            return id;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private ImageUrlInfo GetImageUrl(ImageDownloadInfo info, int maxWidth, int maxHeight, string format)
 | 
					        private (string url, int? width, int? height) GetImageUrl(ImageDownloadInfo info, int maxWidth, int maxHeight, string format)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var url = string.Format(
 | 
					            var url = string.Format(
 | 
				
			||||||
                CultureInfo.InvariantCulture,
 | 
					                CultureInfo.InvariantCulture,
 | 
				
			||||||
@ -1256,12 +1223,26 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
            // just lie
 | 
					            // just lie
 | 
				
			||||||
            info.IsDirectStream = true;
 | 
					            info.IsDirectStream = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return new ImageUrlInfo
 | 
					            return (url, width, height);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private class ImageDownloadInfo
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
                Url = url,
 | 
					            internal Guid ItemId { get; set; }
 | 
				
			||||||
                Width = width,
 | 
					
 | 
				
			||||||
                Height = height
 | 
					            internal string ImageTag { get; set; }
 | 
				
			||||||
            };
 | 
					
 | 
				
			||||||
 | 
					            internal ImageType Type { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            internal int? Width { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            internal int? Height { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            internal bool IsDirectStream { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            internal string Format { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            internal ItemImageInfo ItemImageInfo { get; set; }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -23,9 +23,7 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        public bool Contains(string field)
 | 
					        public bool Contains(string field)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            // Don't bother with this. Some clients (media monkey) use the filter and then don't display very well when very little data comes back.
 | 
					            return _all || Array.Exists(_fields, x => x.Equals(field, StringComparison.OrdinalIgnoreCase));
 | 
				
			||||||
            return true;
 | 
					 | 
				
			||||||
            // return _all || ListHelper.ContainsIgnoreCase(_fields, field);
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,5 @@
 | 
				
			|||||||
#pragma warning disable CS1591
 | 
					#pragma warning disable CS1591
 | 
				
			||||||
 | 
					#pragma warning disable CA1305
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using System;
 | 
					using System;
 | 
				
			||||||
using System.IO;
 | 
					using System.IO;
 | 
				
			||||||
@ -29,7 +30,6 @@ namespace Emby.Dlna.Didl
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
        public StringWriterWithEncoding(Encoding encoding)
 | 
					        public StringWriterWithEncoding(Encoding encoding)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _encoding = encoding;
 | 
					            _encoding = encoding;
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										24
									
								
								Emby.Dlna/DlnaConfigurationFactory.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								Emby.Dlna/DlnaConfigurationFactory.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,24 @@
 | 
				
			|||||||
 | 
					#nullable enable
 | 
				
			||||||
 | 
					#pragma warning disable CS1591
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using Emby.Dlna.Configuration;
 | 
				
			||||||
 | 
					using MediaBrowser.Common.Configuration;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Emby.Dlna
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class DlnaConfigurationFactory : IConfigurationFactory
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public IEnumerable<ConfigurationStore> GetConfigurations()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return new[]
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                new ConfigurationStore
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    Key = "dlna",
 | 
				
			||||||
 | 
					                    ConfigurationType = typeof(DlnaOptions)
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -54,11 +54,15 @@ namespace Emby.Dlna
 | 
				
			|||||||
            _appHost = appHost;
 | 
					            _appHost = appHost;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private string UserProfilesPath => Path.Combine(_appPaths.ConfigurationDirectoryPath, "dlna", "user");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private string SystemProfilesPath => Path.Combine(_appPaths.ConfigurationDirectoryPath, "dlna", "system");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public async Task InitProfilesAsync()
 | 
					        public async Task InitProfilesAsync()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            try
 | 
					            try
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await ExtractSystemProfilesAsync();
 | 
					                await ExtractSystemProfilesAsync().ConfigureAwait(false);
 | 
				
			||||||
                LoadProfiles();
 | 
					                LoadProfiles();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            catch (Exception ex)
 | 
					            catch (Exception ex)
 | 
				
			||||||
@ -240,7 +244,7 @@ namespace Emby.Dlna
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var headerString = string.Join(", ", headers.Select(i => string.Format("{0}={1}", i.Key, i.Value)).ToArray());
 | 
					                var headerString = string.Join(", ", headers.Select(i => string.Format(CultureInfo.InvariantCulture, "{0}={1}", i.Key, i.Value)));
 | 
				
			||||||
                _logger.LogDebug("No matching device profile found. {0}", headerString);
 | 
					                _logger.LogDebug("No matching device profile found. {0}", headerString);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -280,10 +284,6 @@ namespace Emby.Dlna
 | 
				
			|||||||
            return false;
 | 
					            return false;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private string UserProfilesPath => Path.Combine(_appPaths.ConfigurationDirectoryPath, "dlna", "user");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private string SystemProfilesPath => Path.Combine(_appPaths.ConfigurationDirectoryPath, "dlna", "system");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private IEnumerable<DeviceProfile> GetProfiles(string path, DeviceProfileType type)
 | 
					        private IEnumerable<DeviceProfile> GetProfiles(string path, DeviceProfileType type)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            try
 | 
					            try
 | 
				
			||||||
@ -495,8 +495,8 @@ namespace Emby.Dlna
 | 
				
			|||||||
        /// Recreates the object using serialization, to ensure it's not a subclass.
 | 
					        /// Recreates the object using serialization, to ensure it's not a subclass.
 | 
				
			||||||
        /// If it's a subclass it may not serlialize properly to xml (different root element tag name).
 | 
					        /// If it's a subclass it may not serlialize properly to xml (different root element tag name).
 | 
				
			||||||
        /// </summary>
 | 
					        /// </summary>
 | 
				
			||||||
        /// <param name="profile"></param>
 | 
					        /// <param name="profile">The device profile.</param>
 | 
				
			||||||
        /// <returns></returns>
 | 
					        /// <returns>The reserialized device profile.</returns>
 | 
				
			||||||
        private DeviceProfile ReserializeProfile(DeviceProfile profile)
 | 
					        private DeviceProfile ReserializeProfile(DeviceProfile profile)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (profile.GetType() == typeof(DeviceProfile))
 | 
					            if (profile.GetType() == typeof(DeviceProfile))
 | 
				
			||||||
@ -509,13 +509,6 @@ namespace Emby.Dlna
 | 
				
			|||||||
            return _jsonSerializer.DeserializeFromString<DeviceProfile>(json);
 | 
					            return _jsonSerializer.DeserializeFromString<DeviceProfile>(json);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private class InternalProfileInfo
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            internal DeviceProfileInfo Info { get; set; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            internal string Path { get; set; }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public string GetServerDescriptionXml(IHeaderDictionary headers, string serverUuId, string serverAddress)
 | 
					        public string GetServerDescriptionXml(IHeaderDictionary headers, string serverUuId, string serverAddress)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var profile = GetProfile(headers) ??
 | 
					            var profile = GetProfile(headers) ??
 | 
				
			||||||
@ -540,7 +533,15 @@ namespace Emby.Dlna
 | 
				
			|||||||
                Stream = _assembly.GetManifestResourceStream(resource)
 | 
					                Stream = _assembly.GetManifestResourceStream(resource)
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private class InternalProfileInfo
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            internal DeviceProfileInfo Info { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            internal string Path { get; set; }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /*
 | 
					    /*
 | 
				
			||||||
    class DlnaProfileEntryPoint : IServerEntryPoint
 | 
					    class DlnaProfileEntryPoint : IServerEntryPoint
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
				
			|||||||
@ -20,7 +20,7 @@
 | 
				
			|||||||
    <TargetFramework>netstandard2.1</TargetFramework>
 | 
					    <TargetFramework>netstandard2.1</TargetFramework>
 | 
				
			||||||
    <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
 | 
					    <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
 | 
				
			||||||
    <GenerateDocumentationFile>true</GenerateDocumentationFile>
 | 
					    <GenerateDocumentationFile>true</GenerateDocumentationFile>
 | 
				
			||||||
    <TreatWarningsAsErrors Condition=" '$(Configuration)' == 'Release'" >true</TreatWarningsAsErrors>
 | 
					    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
 | 
				
			||||||
  </PropertyGroup>
 | 
					  </PropertyGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <!-- Code Analyzers-->
 | 
					  <!-- Code Analyzers-->
 | 
				
			||||||
 | 
				
			|||||||
@ -15,6 +15,6 @@ namespace Emby.Dlna
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        public string ContentType { get; set; }
 | 
					        public string ContentType { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public Dictionary<string, string> Headers { get; set; }
 | 
					        public Dictionary<string, string> Headers { get; }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -14,7 +14,7 @@ using Microsoft.Extensions.Logging;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace Emby.Dlna.Eventing
 | 
					namespace Emby.Dlna.Eventing
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public class EventManager : IEventManager
 | 
					    public class DlnaEventManager : IDlnaEventManager
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        private readonly ConcurrentDictionary<string, EventSubscription> _subscriptions =
 | 
					        private readonly ConcurrentDictionary<string, EventSubscription> _subscriptions =
 | 
				
			||||||
            new ConcurrentDictionary<string, EventSubscription>(StringComparer.OrdinalIgnoreCase);
 | 
					            new ConcurrentDictionary<string, EventSubscription>(StringComparer.OrdinalIgnoreCase);
 | 
				
			||||||
@ -22,7 +22,9 @@ namespace Emby.Dlna.Eventing
 | 
				
			|||||||
        private readonly ILogger _logger;
 | 
					        private readonly ILogger _logger;
 | 
				
			||||||
        private readonly IHttpClient _httpClient;
 | 
					        private readonly IHttpClient _httpClient;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public EventManager(ILogger logger, IHttpClient httpClient)
 | 
					        private readonly CultureInfo _usCulture = new CultureInfo("en-US");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public DlnaEventManager(ILogger logger, IHttpClient httpClient)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _httpClient = httpClient;
 | 
					            _httpClient = httpClient;
 | 
				
			||||||
            _logger = logger;
 | 
					            _logger = logger;
 | 
				
			||||||
@ -58,7 +60,8 @@ namespace Emby.Dlna.Eventing
 | 
				
			|||||||
            var timeout = ParseTimeout(requestedTimeoutString) ?? 300;
 | 
					            var timeout = ParseTimeout(requestedTimeoutString) ?? 300;
 | 
				
			||||||
            var id = "uuid:" + Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
 | 
					            var id = "uuid:" + Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            _logger.LogDebug("Creating event subscription for {0} with timeout of {1} to {2}",
 | 
					            _logger.LogDebug(
 | 
				
			||||||
 | 
					                "Creating event subscription for {0} with timeout of {1} to {2}",
 | 
				
			||||||
                notificationType,
 | 
					                notificationType,
 | 
				
			||||||
                timeout,
 | 
					                timeout,
 | 
				
			||||||
                callbackUrl);
 | 
					                callbackUrl);
 | 
				
			||||||
@ -94,7 +97,7 @@ namespace Emby.Dlna.Eventing
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            _logger.LogDebug("Cancelling event subscription {0}", subscriptionId);
 | 
					            _logger.LogDebug("Cancelling event subscription {0}", subscriptionId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            _subscriptions.TryRemove(subscriptionId, out EventSubscription sub);
 | 
					            _subscriptions.TryRemove(subscriptionId, out _);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return new EventSubscriptionResponse
 | 
					            return new EventSubscriptionResponse
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@ -103,7 +106,6 @@ namespace Emby.Dlna.Eventing
 | 
				
			|||||||
            };
 | 
					            };
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private readonly CultureInfo _usCulture = new CultureInfo("en-US");
 | 
					 | 
				
			||||||
        private EventSubscriptionResponse GetEventSubscriptionResponse(string subscriptionId, string requestedTimeoutString, int timeoutSeconds)
 | 
					        private EventSubscriptionResponse GetEventSubscriptionResponse(string subscriptionId, string requestedTimeoutString, int timeoutSeconds)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var response = new EventSubscriptionResponse
 | 
					            var response = new EventSubscriptionResponse
 | 
				
			||||||
@ -2,7 +2,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace Emby.Dlna
 | 
					namespace Emby.Dlna
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public interface IConnectionManager : IEventManager, IUpnpService
 | 
					    public interface IConnectionManager : IDlnaEventManager, IUpnpService
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -2,7 +2,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace Emby.Dlna
 | 
					namespace Emby.Dlna
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public interface IContentDirectory : IEventManager, IUpnpService
 | 
					    public interface IContentDirectory : IDlnaEventManager, IUpnpService
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -2,22 +2,32 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace Emby.Dlna
 | 
					namespace Emby.Dlna
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public interface IEventManager
 | 
					    public interface IDlnaEventManager
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
        /// Cancels the event subscription.
 | 
					        /// Cancels the event subscription.
 | 
				
			||||||
        /// </summary>
 | 
					        /// </summary>
 | 
				
			||||||
        /// <param name="subscriptionId">The subscription identifier.</param>
 | 
					        /// <param name="subscriptionId">The subscription identifier.</param>
 | 
				
			||||||
 | 
					        /// <returns>The response.</returns>
 | 
				
			||||||
        EventSubscriptionResponse CancelEventSubscription(string subscriptionId);
 | 
					        EventSubscriptionResponse CancelEventSubscription(string subscriptionId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
        /// Renews the event subscription.
 | 
					        /// Renews the event subscription.
 | 
				
			||||||
        /// </summary>
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        /// <param name="subscriptionId">The subscription identifier.</param>
 | 
				
			||||||
 | 
					        /// <param name="notificationType">The notification type.</param>
 | 
				
			||||||
 | 
					        /// <param name="requestedTimeoutString">The requested timeout as a sting.</param>
 | 
				
			||||||
 | 
					        /// <param name="callbackUrl">The callback url.</param>
 | 
				
			||||||
 | 
					        /// <returns>The response.</returns>
 | 
				
			||||||
        EventSubscriptionResponse RenewEventSubscription(string subscriptionId, string notificationType, string requestedTimeoutString, string callbackUrl);
 | 
					        EventSubscriptionResponse RenewEventSubscription(string subscriptionId, string notificationType, string requestedTimeoutString, string callbackUrl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
        /// Creates the event subscription.
 | 
					        /// Creates the event subscription.
 | 
				
			||||||
        /// </summary>
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        /// <param name="notificationType">The notification type.</param>
 | 
				
			||||||
 | 
					        /// <param name="requestedTimeoutString">The requested timeout as a sting.</param>
 | 
				
			||||||
 | 
					        /// <param name="callbackUrl">The callback url.</param>
 | 
				
			||||||
 | 
					        /// <returns>The response.</returns>
 | 
				
			||||||
        EventSubscriptionResponse CreateEventSubscription(string notificationType, string requestedTimeoutString, string callbackUrl);
 | 
					        EventSubscriptionResponse CreateEventSubscription(string notificationType, string requestedTimeoutString, string callbackUrl);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -2,7 +2,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace Emby.Dlna
 | 
					namespace Emby.Dlna
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public interface IMediaReceiverRegistrar : IEventManager, IUpnpService
 | 
					    public interface IMediaReceiverRegistrar : IDlnaEventManager, IUpnpService
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -30,7 +30,7 @@ using OperatingSystem = MediaBrowser.Common.System.OperatingSystem;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace Emby.Dlna.Main
 | 
					namespace Emby.Dlna.Main
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public class DlnaEntryPoint : IServerEntryPoint, IRunBeforeStartup
 | 
					    public sealed class DlnaEntryPoint : IServerEntryPoint, IRunBeforeStartup
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        private readonly IServerConfigurationManager _config;
 | 
					        private readonly IServerConfigurationManager _config;
 | 
				
			||||||
        private readonly ILogger<DlnaEntryPoint> _logger;
 | 
					        private readonly ILogger<DlnaEntryPoint> _logger;
 | 
				
			||||||
@ -54,13 +54,7 @@ namespace Emby.Dlna.Main
 | 
				
			|||||||
        private SsdpDevicePublisher _publisher;
 | 
					        private SsdpDevicePublisher _publisher;
 | 
				
			||||||
        private ISsdpCommunicationsServer _communicationsServer;
 | 
					        private ISsdpCommunicationsServer _communicationsServer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public IContentDirectory ContentDirectory { get; private set; }
 | 
					        private bool _disposed;
 | 
				
			||||||
 | 
					 | 
				
			||||||
        public IConnectionManager ConnectionManager { get; private set; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public IMediaReceiverRegistrar MediaReceiverRegistrar { get; private set; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public static DlnaEntryPoint Current;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public DlnaEntryPoint(
 | 
					        public DlnaEntryPoint(
 | 
				
			||||||
            IServerConfigurationManager config,
 | 
					            IServerConfigurationManager config,
 | 
				
			||||||
@ -99,14 +93,14 @@ namespace Emby.Dlna.Main
 | 
				
			|||||||
            _networkManager = networkManager;
 | 
					            _networkManager = networkManager;
 | 
				
			||||||
            _logger = loggerFactory.CreateLogger<DlnaEntryPoint>();
 | 
					            _logger = loggerFactory.CreateLogger<DlnaEntryPoint>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            ContentDirectory = new ContentDirectory.ContentDirectory(
 | 
					            ContentDirectory = new ContentDirectory.ContentDirectoryService(
 | 
				
			||||||
                dlnaManager,
 | 
					                dlnaManager,
 | 
				
			||||||
                userDataManager,
 | 
					                userDataManager,
 | 
				
			||||||
                imageProcessor,
 | 
					                imageProcessor,
 | 
				
			||||||
                libraryManager,
 | 
					                libraryManager,
 | 
				
			||||||
                config,
 | 
					                config,
 | 
				
			||||||
                userManager,
 | 
					                userManager,
 | 
				
			||||||
                loggerFactory.CreateLogger<ContentDirectory.ContentDirectory>(),
 | 
					                loggerFactory.CreateLogger<ContentDirectory.ContentDirectoryService>(),
 | 
				
			||||||
                httpClient,
 | 
					                httpClient,
 | 
				
			||||||
                localizationManager,
 | 
					                localizationManager,
 | 
				
			||||||
                mediaSourceManager,
 | 
					                mediaSourceManager,
 | 
				
			||||||
@ -114,19 +108,27 @@ namespace Emby.Dlna.Main
 | 
				
			|||||||
                mediaEncoder,
 | 
					                mediaEncoder,
 | 
				
			||||||
                tvSeriesManager);
 | 
					                tvSeriesManager);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            ConnectionManager = new ConnectionManager.ConnectionManager(
 | 
					            ConnectionManager = new ConnectionManager.ConnectionManagerService(
 | 
				
			||||||
                dlnaManager,
 | 
					                dlnaManager,
 | 
				
			||||||
                config,
 | 
					                config,
 | 
				
			||||||
                loggerFactory.CreateLogger<ConnectionManager.ConnectionManager>(),
 | 
					                loggerFactory.CreateLogger<ConnectionManager.ConnectionManagerService>(),
 | 
				
			||||||
                httpClient);
 | 
					                httpClient);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            MediaReceiverRegistrar = new MediaReceiverRegistrar.MediaReceiverRegistrar(
 | 
					            MediaReceiverRegistrar = new MediaReceiverRegistrar.MediaReceiverRegistrarService(
 | 
				
			||||||
                loggerFactory.CreateLogger<MediaReceiverRegistrar.MediaReceiverRegistrar>(),
 | 
					                loggerFactory.CreateLogger<MediaReceiverRegistrar.MediaReceiverRegistrarService>(),
 | 
				
			||||||
                httpClient,
 | 
					                httpClient,
 | 
				
			||||||
                config);
 | 
					                config);
 | 
				
			||||||
            Current = this;
 | 
					            Current = this;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static DlnaEntryPoint Current { 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()
 | 
					        public async Task RunAsync()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            await ((DlnaManager)_dlnaManager).InitProfilesAsync().ConfigureAwait(false);
 | 
					            await ((DlnaManager)_dlnaManager).InitProfilesAsync().ConfigureAwait(false);
 | 
				
			||||||
@ -399,8 +401,24 @@ namespace Emby.Dlna.Main
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void DisposeDevicePublisher()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (_publisher != null)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                _logger.LogInformation("Disposing SsdpDevicePublisher");
 | 
				
			||||||
 | 
					                _publisher.Dispose();
 | 
				
			||||||
 | 
					                _publisher = null;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <inheritdoc />
 | 
				
			||||||
        public void Dispose()
 | 
					        public void Dispose()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					            if (_disposed)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            DisposeDevicePublisher();
 | 
					            DisposeDevicePublisher();
 | 
				
			||||||
            DisposePlayToManager();
 | 
					            DisposePlayToManager();
 | 
				
			||||||
            DisposeDeviceDiscovery();
 | 
					            DisposeDeviceDiscovery();
 | 
				
			||||||
@ -416,16 +434,8 @@ namespace Emby.Dlna.Main
 | 
				
			|||||||
            ConnectionManager = null;
 | 
					            ConnectionManager = null;
 | 
				
			||||||
            MediaReceiverRegistrar = null;
 | 
					            MediaReceiverRegistrar = null;
 | 
				
			||||||
            Current = null;
 | 
					            Current = null;
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public void DisposeDevicePublisher()
 | 
					            _disposed = true;
 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (_publisher != null)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                _logger.LogInformation("Disposing SsdpDevicePublisher");
 | 
					 | 
				
			||||||
                _publisher.Dispose();
 | 
					 | 
				
			||||||
                _publisher = null;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -8,12 +8,12 @@ using Microsoft.Extensions.Logging;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace Emby.Dlna.MediaReceiverRegistrar
 | 
					namespace Emby.Dlna.MediaReceiverRegistrar
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public class MediaReceiverRegistrar : BaseService, IMediaReceiverRegistrar
 | 
					    public class MediaReceiverRegistrarService : BaseService, IMediaReceiverRegistrar
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        private readonly IServerConfigurationManager _config;
 | 
					        private readonly IServerConfigurationManager _config;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public MediaReceiverRegistrar(
 | 
					        public MediaReceiverRegistrarService(
 | 
				
			||||||
            ILogger<MediaReceiverRegistrar> logger,
 | 
					            ILogger<MediaReceiverRegistrarService> logger,
 | 
				
			||||||
            IHttpClient httpClient,
 | 
					            IHttpClient httpClient,
 | 
				
			||||||
            IServerConfigurationManager config)
 | 
					            IServerConfigurationManager config)
 | 
				
			||||||
            : base(logger, httpClient)
 | 
					            : base(logger, httpClient)
 | 
				
			||||||
@ -10,7 +10,8 @@ namespace Emby.Dlna.MediaReceiverRegistrar
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        public string GetXml()
 | 
					        public string GetXml()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return new ServiceXmlBuilder().GetXml(new ServiceActionListBuilder().GetActions(),
 | 
					            return new ServiceXmlBuilder().GetXml(
 | 
				
			||||||
 | 
					                new ServiceActionListBuilder().GetActions(),
 | 
				
			||||||
                GetStateVariables());
 | 
					                GetStateVariables());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -19,15 +19,40 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    public class Device : IDisposable
 | 
					    public class Device : IDisposable
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					        private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private readonly IHttpClient _httpClient;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private readonly ILogger _logger;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private readonly object _timerLock = new object();
 | 
				
			||||||
        private Timer _timer;
 | 
					        private Timer _timer;
 | 
				
			||||||
 | 
					        private int _muteVol;
 | 
				
			||||||
 | 
					        private int _volume;
 | 
				
			||||||
 | 
					        private DateTime _lastVolumeRefresh;
 | 
				
			||||||
 | 
					        private bool _volumeRefreshActive;
 | 
				
			||||||
 | 
					        private int _connectFailureCount;
 | 
				
			||||||
 | 
					        private bool _disposed;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public Device(DeviceInfo deviceProperties, IHttpClient httpClient, ILogger logger)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            Properties = deviceProperties;
 | 
				
			||||||
 | 
					            _httpClient = httpClient;
 | 
				
			||||||
 | 
					            _logger = logger;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public event EventHandler<PlaybackStartEventArgs> PlaybackStart;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public event EventHandler<PlaybackProgressEventArgs> PlaybackProgress;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public event EventHandler<PlaybackStoppedEventArgs> PlaybackStopped;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public event EventHandler<MediaChangedEventArgs> MediaChanged;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public DeviceInfo Properties { get; set; }
 | 
					        public DeviceInfo Properties { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private int _muteVol;
 | 
					 | 
				
			||||||
        public bool IsMuted { get; set; }
 | 
					        public bool IsMuted { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private int _volume;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public int Volume
 | 
					        public int Volume
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            get
 | 
					            get
 | 
				
			||||||
@ -43,29 +68,21 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        public TimeSpan Position { get; set; } = TimeSpan.FromSeconds(0);
 | 
					        public TimeSpan Position { get; set; } = TimeSpan.FromSeconds(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public TRANSPORTSTATE TransportState { get; private set; }
 | 
					        public TransportState TransportState { get; private set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public bool IsPlaying => TransportState == TRANSPORTSTATE.PLAYING;
 | 
					        public bool IsPlaying => TransportState == TransportState.Playing;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public bool IsPaused => TransportState == TRANSPORTSTATE.PAUSED || TransportState == TRANSPORTSTATE.PAUSED_PLAYBACK;
 | 
					        public bool IsPaused => TransportState == TransportState.Paused || TransportState == TransportState.PausedPlayback;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public bool IsStopped => TransportState == TRANSPORTSTATE.STOPPED;
 | 
					        public bool IsStopped => TransportState == TransportState.Stopped;
 | 
				
			||||||
 | 
					 | 
				
			||||||
        private readonly IHttpClient _httpClient;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private readonly ILogger _logger;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private readonly IServerConfigurationManager _config;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public Action OnDeviceUnavailable { get; set; }
 | 
					        public Action OnDeviceUnavailable { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public Device(DeviceInfo deviceProperties, IHttpClient httpClient, ILogger logger, IServerConfigurationManager config)
 | 
					        private TransportCommands AvCommands { get; set; }
 | 
				
			||||||
        {
 | 
					
 | 
				
			||||||
            Properties = deviceProperties;
 | 
					        private TransportCommands RendererCommands { get; set; }
 | 
				
			||||||
            _httpClient = httpClient;
 | 
					
 | 
				
			||||||
            _logger = logger;
 | 
					        public UBaseObject CurrentMediaInfo { get; private set; }
 | 
				
			||||||
            _config = config;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public void Start()
 | 
					        public void Start()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@ -73,8 +90,6 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
            _timer = new Timer(TimerCallback, null, 1000, Timeout.Infinite);
 | 
					            _timer = new Timer(TimerCallback, null, 1000, Timeout.Infinite);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private DateTime _lastVolumeRefresh;
 | 
					 | 
				
			||||||
        private bool _volumeRefreshActive;
 | 
					 | 
				
			||||||
        private Task RefreshVolumeIfNeeded()
 | 
					        private Task RefreshVolumeIfNeeded()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (_volumeRefreshActive
 | 
					            if (_volumeRefreshActive
 | 
				
			||||||
@ -105,7 +120,6 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private readonly object _timerLock = new object();
 | 
					 | 
				
			||||||
        private void RestartTimer(bool immediate = false)
 | 
					        private void RestartTimer(bool immediate = false)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            lock (_timerLock)
 | 
					            lock (_timerLock)
 | 
				
			||||||
@ -233,6 +247,9 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
        /// Sets volume on a scale of 0-100.
 | 
					        /// Sets volume on a scale of 0-100.
 | 
				
			||||||
        /// </summary>
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        /// <param name="value">The volume on a scale of 0-100.</param>
 | 
				
			||||||
 | 
					        /// <param name="cancellationToken">The cancellation token to cancel operation.</param>
 | 
				
			||||||
 | 
					        /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
 | 
				
			||||||
        public async Task SetVolume(int value, CancellationToken cancellationToken)
 | 
					        public async Task SetVolume(int value, CancellationToken cancellationToken)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false);
 | 
					            var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false);
 | 
				
			||||||
@ -275,7 +292,7 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
                throw new InvalidOperationException("Unable to find service");
 | 
					                throw new InvalidOperationException("Unable to find service");
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, string.Format("{0:hh}:{0:mm}:{0:ss}", value), "REL_TIME"))
 | 
					            await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, string.Format(CultureInfo.InvariantCulture, "{0:hh}:{0:mm}:{0:ss}", value), "REL_TIME"))
 | 
				
			||||||
                .ConfigureAwait(false);
 | 
					                .ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            RestartTimer(true);
 | 
					            RestartTimer(true);
 | 
				
			||||||
@ -285,7 +302,7 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            var avCommands = await GetAVProtocolAsync(cancellationToken).ConfigureAwait(false);
 | 
					            var avCommands = await GetAVProtocolAsync(cancellationToken).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            url = url.Replace("&", "&");
 | 
					            url = url.Replace("&", "&", StringComparison.Ordinal);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            _logger.LogDebug("{0} - SetAvTransport Uri: {1} DlnaHeaders: {2}", Properties.Name, url, header);
 | 
					            _logger.LogDebug("{0} - SetAvTransport Uri: {1} DlnaHeaders: {2}", Properties.Name, url, header);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -297,8 +314,8 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            var dictionary = new Dictionary<string, string>
 | 
					            var dictionary = new Dictionary<string, string>
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                {"CurrentURI", url},
 | 
					                { "CurrentURI", url },
 | 
				
			||||||
                {"CurrentURIMetaData", CreateDidlMeta(metaData)}
 | 
					                { "CurrentURIMetaData", CreateDidlMeta(metaData) }
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var service = GetAvTransportService();
 | 
					            var service = GetAvTransportService();
 | 
				
			||||||
@ -401,13 +418,11 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
            await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, 1))
 | 
					            await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, 1))
 | 
				
			||||||
                .ConfigureAwait(false);
 | 
					                .ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            TransportState = TRANSPORTSTATE.PAUSED;
 | 
					            TransportState = TransportState.Paused;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            RestartTimer(true);
 | 
					            RestartTimer(true);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private int _connectFailureCount;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private async void TimerCallback(object sender)
 | 
					        private async void TimerCallback(object sender)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (_disposed)
 | 
					            if (_disposed)
 | 
				
			||||||
@ -436,7 +451,7 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
                if (transportState.HasValue)
 | 
					                if (transportState.HasValue)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    // If we're not playing anything no need to get additional data
 | 
					                    // If we're not playing anything no need to get additional data
 | 
				
			||||||
                    if (transportState.Value == TRANSPORTSTATE.STOPPED)
 | 
					                    if (transportState.Value == TransportState.Stopped)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        UpdateMediaInfo(null, transportState.Value);
 | 
					                        UpdateMediaInfo(null, transportState.Value);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
@ -465,7 +480,7 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    // If we're not playing anything make sure we don't get data more often than neccessry to keep the Session alive
 | 
					                    // If we're not playing anything make sure we don't get data more often than neccessry to keep the Session alive
 | 
				
			||||||
                    if (transportState.Value == TRANSPORTSTATE.STOPPED)
 | 
					                    if (transportState.Value == TransportState.Stopped)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        RestartTimerInactive();
 | 
					                        RestartTimerInactive();
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
@ -539,7 +554,7 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var volume = result.Document.Descendants(uPnpNamespaces.RenderingControl + "GetVolumeResponse").Select(i => i.Element("CurrentVolume")).FirstOrDefault(i => i != null);
 | 
					            var volume = result.Document.Descendants(UPnpNamespaces.RenderingControl + "GetVolumeResponse").Select(i => i.Element("CurrentVolume")).FirstOrDefault(i => i != null);
 | 
				
			||||||
            var volumeValue = volume?.Value;
 | 
					            var volumeValue = volume?.Value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (string.IsNullOrWhiteSpace(volumeValue))
 | 
					            if (string.IsNullOrWhiteSpace(volumeValue))
 | 
				
			||||||
@ -589,14 +604,14 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var valueNode = result.Document.Descendants(uPnpNamespaces.RenderingControl + "GetMuteResponse")
 | 
					            var valueNode = result.Document.Descendants(UPnpNamespaces.RenderingControl + "GetMuteResponse")
 | 
				
			||||||
                                            .Select(i => i.Element("CurrentMute"))
 | 
					                                            .Select(i => i.Element("CurrentMute"))
 | 
				
			||||||
                                            .FirstOrDefault(i => i != null);
 | 
					                                            .FirstOrDefault(i => i != null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            IsMuted = string.Equals(valueNode?.Value, "1", StringComparison.OrdinalIgnoreCase);
 | 
					            IsMuted = string.Equals(valueNode?.Value, "1", StringComparison.OrdinalIgnoreCase);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private async Task<TRANSPORTSTATE?> GetTransportInfo(TransportCommands avCommands, CancellationToken cancellationToken)
 | 
					        private async Task<TransportState?> GetTransportInfo(TransportCommands avCommands, CancellationToken cancellationToken)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetTransportInfo");
 | 
					            var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetTransportInfo");
 | 
				
			||||||
            if (command == null)
 | 
					            if (command == null)
 | 
				
			||||||
@ -623,12 +638,12 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var transportState =
 | 
					            var transportState =
 | 
				
			||||||
                result.Document.Descendants(uPnpNamespaces.AvTransport + "GetTransportInfoResponse").Select(i => i.Element("CurrentTransportState")).FirstOrDefault(i => i != null);
 | 
					                result.Document.Descendants(UPnpNamespaces.AvTransport + "GetTransportInfoResponse").Select(i => i.Element("CurrentTransportState")).FirstOrDefault(i => i != null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var transportStateValue = transportState?.Value;
 | 
					            var transportStateValue = transportState?.Value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (transportStateValue != null
 | 
					            if (transportStateValue != null
 | 
				
			||||||
                && Enum.TryParse(transportStateValue, true, out TRANSPORTSTATE state))
 | 
					                && Enum.TryParse(transportStateValue, true, out TransportState state))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                return state;
 | 
					                return state;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@ -636,7 +651,7 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
            return null;
 | 
					            return null;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private async Task<uBaseObject> GetMediaInfo(TransportCommands avCommands, CancellationToken cancellationToken)
 | 
					        private async Task<UBaseObject> GetMediaInfo(TransportCommands avCommands, CancellationToken cancellationToken)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetMediaInfo");
 | 
					            var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetMediaInfo");
 | 
				
			||||||
            if (command == null)
 | 
					            if (command == null)
 | 
				
			||||||
@ -671,7 +686,7 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
                return null;
 | 
					                return null;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var e = track.Element(uPnpNamespaces.items) ?? track;
 | 
					            var e = track.Element(UPnpNamespaces.Items) ?? track;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var elementString = (string)e;
 | 
					            var elementString = (string)e;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -687,13 +702,13 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
                return null;
 | 
					                return null;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            e = track.Element(uPnpNamespaces.items) ?? track;
 | 
					            e = track.Element(UPnpNamespaces.Items) ?? track;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            elementString = (string)e;
 | 
					            elementString = (string)e;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (!string.IsNullOrWhiteSpace(elementString))
 | 
					            if (!string.IsNullOrWhiteSpace(elementString))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                return new uBaseObject
 | 
					                return new UBaseObject
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Url = elementString
 | 
					                    Url = elementString
 | 
				
			||||||
                };
 | 
					                };
 | 
				
			||||||
@ -702,7 +717,7 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
            return null;
 | 
					            return null;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private async Task<(bool, uBaseObject)> GetPositionInfo(TransportCommands avCommands, CancellationToken cancellationToken)
 | 
					        private async Task<(bool, UBaseObject)> GetPositionInfo(TransportCommands avCommands, CancellationToken cancellationToken)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetPositionInfo");
 | 
					            var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetPositionInfo");
 | 
				
			||||||
            if (command == null)
 | 
					            if (command == null)
 | 
				
			||||||
@ -731,11 +746,11 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
                return (false, null);
 | 
					                return (false, null);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var trackUriElem = result.Document.Descendants(uPnpNamespaces.AvTransport + "GetPositionInfoResponse").Select(i => i.Element("TrackURI")).FirstOrDefault(i => i != null);
 | 
					            var trackUriElem = result.Document.Descendants(UPnpNamespaces.AvTransport + "GetPositionInfoResponse").Select(i => i.Element("TrackURI")).FirstOrDefault(i => i != null);
 | 
				
			||||||
            var trackUri = trackUriElem == null ? null : trackUriElem.Value;
 | 
					            var trackUri = trackUriElem?.Value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var durationElem = result.Document.Descendants(uPnpNamespaces.AvTransport + "GetPositionInfoResponse").Select(i => i.Element("TrackDuration")).FirstOrDefault(i => i != null);
 | 
					            var durationElem = result.Document.Descendants(UPnpNamespaces.AvTransport + "GetPositionInfoResponse").Select(i => i.Element("TrackDuration")).FirstOrDefault(i => i != null);
 | 
				
			||||||
            var duration = durationElem == null ? null : durationElem.Value;
 | 
					            var duration = durationElem?.Value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (!string.IsNullOrWhiteSpace(duration)
 | 
					            if (!string.IsNullOrWhiteSpace(duration)
 | 
				
			||||||
                && !string.Equals(duration, "NOT_IMPLEMENTED", StringComparison.OrdinalIgnoreCase))
 | 
					                && !string.Equals(duration, "NOT_IMPLEMENTED", StringComparison.OrdinalIgnoreCase))
 | 
				
			||||||
@ -747,8 +762,8 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
                Duration = null;
 | 
					                Duration = null;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var positionElem = result.Document.Descendants(uPnpNamespaces.AvTransport + "GetPositionInfoResponse").Select(i => i.Element("RelTime")).FirstOrDefault(i => i != null);
 | 
					            var positionElem = result.Document.Descendants(UPnpNamespaces.AvTransport + "GetPositionInfoResponse").Select(i => i.Element("RelTime")).FirstOrDefault(i => i != null);
 | 
				
			||||||
            var position = positionElem == null ? null : positionElem.Value;
 | 
					            var position = positionElem?.Value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (!string.IsNullOrWhiteSpace(position) && !string.Equals(position, "NOT_IMPLEMENTED", StringComparison.OrdinalIgnoreCase))
 | 
					            if (!string.IsNullOrWhiteSpace(position) && !string.Equals(position, "NOT_IMPLEMENTED", StringComparison.OrdinalIgnoreCase))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@ -787,7 +802,7 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
                return (true, null);
 | 
					                return (true, null);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var e = uPnpResponse.Element(uPnpNamespaces.items);
 | 
					            var e = uPnpResponse.Element(UPnpNamespaces.Items);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var uTrack = CreateUBaseObject(e, trackUri);
 | 
					            var uTrack = CreateUBaseObject(e, trackUri);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -819,7 +834,7 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
            // some devices send back invalid xml
 | 
					            // some devices send back invalid xml
 | 
				
			||||||
            try
 | 
					            try
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                return XElement.Parse(xml.Replace("&", "&"));
 | 
					                return XElement.Parse(xml.Replace("&", "&", StringComparison.Ordinal));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            catch (XmlException)
 | 
					            catch (XmlException)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@ -828,27 +843,27 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
            return null;
 | 
					            return null;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private static uBaseObject CreateUBaseObject(XElement container, string trackUri)
 | 
					        private static UBaseObject CreateUBaseObject(XElement container, string trackUri)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (container == null)
 | 
					            if (container == null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                throw new ArgumentNullException(nameof(container));
 | 
					                throw new ArgumentNullException(nameof(container));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var url = container.GetValue(uPnpNamespaces.Res);
 | 
					            var url = container.GetValue(UPnpNamespaces.Res);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (string.IsNullOrWhiteSpace(url))
 | 
					            if (string.IsNullOrWhiteSpace(url))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                url = trackUri;
 | 
					                url = trackUri;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return new uBaseObject
 | 
					            return new UBaseObject
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                Id = container.GetAttributeValue(uPnpNamespaces.Id),
 | 
					                Id = container.GetAttributeValue(UPnpNamespaces.Id),
 | 
				
			||||||
                ParentId = container.GetAttributeValue(uPnpNamespaces.ParentId),
 | 
					                ParentId = container.GetAttributeValue(UPnpNamespaces.ParentId),
 | 
				
			||||||
                Title = container.GetValue(uPnpNamespaces.title),
 | 
					                Title = container.GetValue(UPnpNamespaces.Title),
 | 
				
			||||||
                IconUrl = container.GetValue(uPnpNamespaces.Artwork),
 | 
					                IconUrl = container.GetValue(UPnpNamespaces.Artwork),
 | 
				
			||||||
                SecondText = "",
 | 
					                SecondText = string.Empty,
 | 
				
			||||||
                Url = url,
 | 
					                Url = url,
 | 
				
			||||||
                ProtocolInfo = GetProtocolInfo(container),
 | 
					                ProtocolInfo = GetProtocolInfo(container),
 | 
				
			||||||
                MetaData = container.ToString()
 | 
					                MetaData = container.ToString()
 | 
				
			||||||
@ -862,11 +877,11 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
                throw new ArgumentNullException(nameof(container));
 | 
					                throw new ArgumentNullException(nameof(container));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var resElement = container.Element(uPnpNamespaces.Res);
 | 
					            var resElement = container.Element(UPnpNamespaces.Res);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (resElement != null)
 | 
					            if (resElement != null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var info = resElement.Attribute(uPnpNamespaces.ProtocolInfo);
 | 
					                var info = resElement.Attribute(UPnpNamespaces.ProtocolInfo);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (info != null && !string.IsNullOrWhiteSpace(info.Value))
 | 
					                if (info != null && !string.IsNullOrWhiteSpace(info.Value))
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
@ -941,12 +956,12 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
                return url;
 | 
					                return url;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (!url.Contains("/"))
 | 
					            if (!url.Contains('/', StringComparison.Ordinal))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                url = "/dmr/" + url;
 | 
					                url = "/dmr/" + url;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (!url.StartsWith("/"))
 | 
					            if (!url.StartsWith("/", StringComparison.Ordinal))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                url = "/" + url;
 | 
					                url = "/" + url;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@ -954,11 +969,7 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
            return baseUrl + url;
 | 
					            return baseUrl + url;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private TransportCommands AvCommands { get; set; }
 | 
					        public static async Task<Device> CreateuPnpDeviceAsync(Uri url, IHttpClient httpClient, ILogger logger, CancellationToken cancellationToken)
 | 
				
			||||||
 | 
					 | 
				
			||||||
        private TransportCommands RendererCommands { get; set; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public static async Task<Device> CreateuPnpDeviceAsync(Uri url, IHttpClient httpClient, IServerConfigurationManager config, ILogger logger, CancellationToken cancellationToken)
 | 
					 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var ssdpHttpClient = new SsdpHttpClient(httpClient);
 | 
					            var ssdpHttpClient = new SsdpHttpClient(httpClient);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -966,13 +977,13 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            var friendlyNames = new List<string>();
 | 
					            var friendlyNames = new List<string>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var name = document.Descendants(uPnpNamespaces.ud.GetName("friendlyName")).FirstOrDefault();
 | 
					            var name = document.Descendants(UPnpNamespaces.Ud.GetName("friendlyName")).FirstOrDefault();
 | 
				
			||||||
            if (name != null && !string.IsNullOrWhiteSpace(name.Value))
 | 
					            if (name != null && !string.IsNullOrWhiteSpace(name.Value))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                friendlyNames.Add(name.Value);
 | 
					                friendlyNames.Add(name.Value);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var room = document.Descendants(uPnpNamespaces.ud.GetName("roomName")).FirstOrDefault();
 | 
					            var room = document.Descendants(UPnpNamespaces.Ud.GetName("roomName")).FirstOrDefault();
 | 
				
			||||||
            if (room != null && !string.IsNullOrWhiteSpace(room.Value))
 | 
					            if (room != null && !string.IsNullOrWhiteSpace(room.Value))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                friendlyNames.Add(room.Value);
 | 
					                friendlyNames.Add(room.Value);
 | 
				
			||||||
@ -981,77 +992,77 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
            var deviceProperties = new DeviceInfo()
 | 
					            var deviceProperties = new DeviceInfo()
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                Name = string.Join(" ", friendlyNames),
 | 
					                Name = string.Join(" ", friendlyNames),
 | 
				
			||||||
                BaseUrl = string.Format("http://{0}:{1}", url.Host, url.Port)
 | 
					                BaseUrl = string.Format(CultureInfo.InvariantCulture, "http://{0}:{1}", url.Host, url.Port)
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var model = document.Descendants(uPnpNamespaces.ud.GetName("modelName")).FirstOrDefault();
 | 
					            var model = document.Descendants(UPnpNamespaces.Ud.GetName("modelName")).FirstOrDefault();
 | 
				
			||||||
            if (model != null)
 | 
					            if (model != null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                deviceProperties.ModelName = model.Value;
 | 
					                deviceProperties.ModelName = model.Value;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var modelNumber = document.Descendants(uPnpNamespaces.ud.GetName("modelNumber")).FirstOrDefault();
 | 
					            var modelNumber = document.Descendants(UPnpNamespaces.Ud.GetName("modelNumber")).FirstOrDefault();
 | 
				
			||||||
            if (modelNumber != null)
 | 
					            if (modelNumber != null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                deviceProperties.ModelNumber = modelNumber.Value;
 | 
					                deviceProperties.ModelNumber = modelNumber.Value;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var uuid = document.Descendants(uPnpNamespaces.ud.GetName("UDN")).FirstOrDefault();
 | 
					            var uuid = document.Descendants(UPnpNamespaces.Ud.GetName("UDN")).FirstOrDefault();
 | 
				
			||||||
            if (uuid != null)
 | 
					            if (uuid != null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                deviceProperties.UUID = uuid.Value;
 | 
					                deviceProperties.UUID = uuid.Value;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var manufacturer = document.Descendants(uPnpNamespaces.ud.GetName("manufacturer")).FirstOrDefault();
 | 
					            var manufacturer = document.Descendants(UPnpNamespaces.Ud.GetName("manufacturer")).FirstOrDefault();
 | 
				
			||||||
            if (manufacturer != null)
 | 
					            if (manufacturer != null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                deviceProperties.Manufacturer = manufacturer.Value;
 | 
					                deviceProperties.Manufacturer = manufacturer.Value;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var manufacturerUrl = document.Descendants(uPnpNamespaces.ud.GetName("manufacturerURL")).FirstOrDefault();
 | 
					            var manufacturerUrl = document.Descendants(UPnpNamespaces.Ud.GetName("manufacturerURL")).FirstOrDefault();
 | 
				
			||||||
            if (manufacturerUrl != null)
 | 
					            if (manufacturerUrl != null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                deviceProperties.ManufacturerUrl = manufacturerUrl.Value;
 | 
					                deviceProperties.ManufacturerUrl = manufacturerUrl.Value;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var presentationUrl = document.Descendants(uPnpNamespaces.ud.GetName("presentationURL")).FirstOrDefault();
 | 
					            var presentationUrl = document.Descendants(UPnpNamespaces.Ud.GetName("presentationURL")).FirstOrDefault();
 | 
				
			||||||
            if (presentationUrl != null)
 | 
					            if (presentationUrl != null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                deviceProperties.PresentationUrl = presentationUrl.Value;
 | 
					                deviceProperties.PresentationUrl = presentationUrl.Value;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var modelUrl = document.Descendants(uPnpNamespaces.ud.GetName("modelURL")).FirstOrDefault();
 | 
					            var modelUrl = document.Descendants(UPnpNamespaces.Ud.GetName("modelURL")).FirstOrDefault();
 | 
				
			||||||
            if (modelUrl != null)
 | 
					            if (modelUrl != null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                deviceProperties.ModelUrl = modelUrl.Value;
 | 
					                deviceProperties.ModelUrl = modelUrl.Value;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var serialNumber = document.Descendants(uPnpNamespaces.ud.GetName("serialNumber")).FirstOrDefault();
 | 
					            var serialNumber = document.Descendants(UPnpNamespaces.Ud.GetName("serialNumber")).FirstOrDefault();
 | 
				
			||||||
            if (serialNumber != null)
 | 
					            if (serialNumber != null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                deviceProperties.SerialNumber = serialNumber.Value;
 | 
					                deviceProperties.SerialNumber = serialNumber.Value;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var modelDescription = document.Descendants(uPnpNamespaces.ud.GetName("modelDescription")).FirstOrDefault();
 | 
					            var modelDescription = document.Descendants(UPnpNamespaces.Ud.GetName("modelDescription")).FirstOrDefault();
 | 
				
			||||||
            if (modelDescription != null)
 | 
					            if (modelDescription != null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                deviceProperties.ModelDescription = modelDescription.Value;
 | 
					                deviceProperties.ModelDescription = modelDescription.Value;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var icon = document.Descendants(uPnpNamespaces.ud.GetName("icon")).FirstOrDefault();
 | 
					            var icon = document.Descendants(UPnpNamespaces.Ud.GetName("icon")).FirstOrDefault();
 | 
				
			||||||
            if (icon != null)
 | 
					            if (icon != null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                deviceProperties.Icon = CreateIcon(icon);
 | 
					                deviceProperties.Icon = CreateIcon(icon);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            foreach (var services in document.Descendants(uPnpNamespaces.ud.GetName("serviceList")))
 | 
					            foreach (var services in document.Descendants(UPnpNamespaces.Ud.GetName("serviceList")))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                if (services == null)
 | 
					                if (services == null)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    continue;
 | 
					                    continue;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                var servicesList = services.Descendants(uPnpNamespaces.ud.GetName("service"));
 | 
					                var servicesList = services.Descendants(UPnpNamespaces.Ud.GetName("service"));
 | 
				
			||||||
                if (servicesList == null)
 | 
					                if (servicesList == null)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    continue;
 | 
					                    continue;
 | 
				
			||||||
@ -1068,10 +1079,9 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return new Device(deviceProperties, httpClient, logger, config);
 | 
					            return new Device(deviceProperties, httpClient, logger);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
 | 
					 | 
				
			||||||
        private static DeviceIcon CreateIcon(XElement element)
 | 
					        private static DeviceIcon CreateIcon(XElement element)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (element == null)
 | 
					            if (element == null)
 | 
				
			||||||
@ -1079,11 +1089,11 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
                throw new ArgumentNullException(nameof(element));
 | 
					                throw new ArgumentNullException(nameof(element));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var mimeType = element.GetDescendantValue(uPnpNamespaces.ud.GetName("mimetype"));
 | 
					            var mimeType = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("mimetype"));
 | 
				
			||||||
            var width = element.GetDescendantValue(uPnpNamespaces.ud.GetName("width"));
 | 
					            var width = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("width"));
 | 
				
			||||||
            var height = element.GetDescendantValue(uPnpNamespaces.ud.GetName("height"));
 | 
					            var height = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("height"));
 | 
				
			||||||
            var depth = element.GetDescendantValue(uPnpNamespaces.ud.GetName("depth"));
 | 
					            var depth = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("depth"));
 | 
				
			||||||
            var url = element.GetDescendantValue(uPnpNamespaces.ud.GetName("url"));
 | 
					            var url = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("url"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var widthValue = int.Parse(width, NumberStyles.Integer, UsCulture);
 | 
					            var widthValue = int.Parse(width, NumberStyles.Integer, UsCulture);
 | 
				
			||||||
            var heightValue = int.Parse(height, NumberStyles.Integer, UsCulture);
 | 
					            var heightValue = int.Parse(height, NumberStyles.Integer, UsCulture);
 | 
				
			||||||
@ -1100,11 +1110,11 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        private static DeviceService Create(XElement element)
 | 
					        private static DeviceService Create(XElement element)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var type = element.GetDescendantValue(uPnpNamespaces.ud.GetName("serviceType"));
 | 
					            var type = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("serviceType"));
 | 
				
			||||||
            var id = element.GetDescendantValue(uPnpNamespaces.ud.GetName("serviceId"));
 | 
					            var id = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("serviceId"));
 | 
				
			||||||
            var scpdUrl = element.GetDescendantValue(uPnpNamespaces.ud.GetName("SCPDURL"));
 | 
					            var scpdUrl = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("SCPDURL"));
 | 
				
			||||||
            var controlURL = element.GetDescendantValue(uPnpNamespaces.ud.GetName("controlURL"));
 | 
					            var controlURL = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("controlURL"));
 | 
				
			||||||
            var eventSubURL = element.GetDescendantValue(uPnpNamespaces.ud.GetName("eventSubURL"));
 | 
					            var eventSubURL = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("eventSubURL"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return new DeviceService
 | 
					            return new DeviceService
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@ -1116,14 +1126,7 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
            };
 | 
					            };
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public event EventHandler<PlaybackStartEventArgs> PlaybackStart;
 | 
					        private void UpdateMediaInfo(UBaseObject mediaInfo, TransportState state)
 | 
				
			||||||
        public event EventHandler<PlaybackProgressEventArgs> PlaybackProgress;
 | 
					 | 
				
			||||||
        public event EventHandler<PlaybackStoppedEventArgs> PlaybackStopped;
 | 
					 | 
				
			||||||
        public event EventHandler<MediaChangedEventArgs> MediaChanged;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public uBaseObject CurrentMediaInfo { get; private set; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private void UpdateMediaInfo(uBaseObject mediaInfo, TRANSPORTSTATE state)
 | 
					 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            TransportState = state;
 | 
					            TransportState = state;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1132,7 +1135,7 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            if (previousMediaInfo == null && mediaInfo != null)
 | 
					            if (previousMediaInfo == null && mediaInfo != null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                if (state != TRANSPORTSTATE.STOPPED)
 | 
					                if (state != TransportState.Stopped)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    OnPlaybackStart(mediaInfo);
 | 
					                    OnPlaybackStart(mediaInfo);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@ -1151,7 +1154,7 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void OnPlaybackStart(uBaseObject mediaInfo)
 | 
					        private void OnPlaybackStart(UBaseObject mediaInfo)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (string.IsNullOrWhiteSpace(mediaInfo.Url))
 | 
					            if (string.IsNullOrWhiteSpace(mediaInfo.Url))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@ -1164,7 +1167,7 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
            });
 | 
					            });
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void OnPlaybackProgress(uBaseObject mediaInfo)
 | 
					        private void OnPlaybackProgress(UBaseObject mediaInfo)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (string.IsNullOrWhiteSpace(mediaInfo.Url))
 | 
					            if (string.IsNullOrWhiteSpace(mediaInfo.Url))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@ -1177,7 +1180,7 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
            });
 | 
					            });
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void OnPlaybackStop(uBaseObject mediaInfo)
 | 
					        private void OnPlaybackStop(UBaseObject mediaInfo)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            PlaybackStopped?.Invoke(this, new PlaybackStoppedEventArgs
 | 
					            PlaybackStopped?.Invoke(this, new PlaybackStoppedEventArgs
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@ -1185,7 +1188,7 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
            });
 | 
					            });
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void OnMediaChanged(uBaseObject old, uBaseObject newMedia)
 | 
					        private void OnMediaChanged(UBaseObject old, UBaseObject newMedia)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            MediaChanged?.Invoke(this, new MediaChangedEventArgs
 | 
					            MediaChanged?.Invoke(this, new MediaChangedEventArgs
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@ -1194,14 +1197,17 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
            });
 | 
					            });
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        bool _disposed;
 | 
					        /// <inheritdoc />
 | 
				
			||||||
 | 
					 | 
				
			||||||
        public void Dispose()
 | 
					        public void Dispose()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            Dispose(true);
 | 
					            Dispose(true);
 | 
				
			||||||
            GC.SuppressFinalize(this);
 | 
					            GC.SuppressFinalize(this);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// Releases unmanaged and optionally managed resources.
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
 | 
				
			||||||
        protected virtual void Dispose(bool disposing)
 | 
					        protected virtual void Dispose(bool disposing)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (_disposed)
 | 
					            if (_disposed)
 | 
				
			||||||
@ -1220,9 +1226,10 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
            _disposed = true;
 | 
					            _disposed = true;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <inheritdoc />
 | 
				
			||||||
        public override string ToString()
 | 
					        public override string ToString()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return string.Format("{0} - {1}", Properties.Name, Properties.BaseUrl);
 | 
					            return string.Format(CultureInfo.InvariantCulture, "{0} - {1}", Properties.Name, Properties.BaseUrl);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -8,6 +8,9 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    public class DeviceInfo
 | 
					    public class DeviceInfo
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					        private readonly List<DeviceService> _services = new List<DeviceService>();
 | 
				
			||||||
 | 
					        private string _baseUrl = string.Empty;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public DeviceInfo()
 | 
					        public DeviceInfo()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            Name = "Generic Device";
 | 
					            Name = "Generic Device";
 | 
				
			||||||
@ -33,7 +36,6 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        public string PresentationUrl { get; set; }
 | 
					        public string PresentationUrl { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private string _baseUrl = string.Empty;
 | 
					 | 
				
			||||||
        public string BaseUrl
 | 
					        public string BaseUrl
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            get => _baseUrl;
 | 
					            get => _baseUrl;
 | 
				
			||||||
@ -42,7 +44,6 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        public DeviceIcon Icon { get; set; }
 | 
					        public DeviceIcon Icon { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private readonly List<DeviceService> _services = new List<DeviceService>();
 | 
					 | 
				
			||||||
        public List<DeviceService> Services => _services;
 | 
					        public List<DeviceService> Services => _services;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public DeviceIdentification ToDeviceIdentification()
 | 
					        public DeviceIdentification ToDeviceIdentification()
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										13
									
								
								Emby.Dlna/PlayTo/MediaChangedEventArgs.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								Emby.Dlna/PlayTo/MediaChangedEventArgs.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,13 @@
 | 
				
			|||||||
 | 
					#pragma warning disable CS1591
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Emby.Dlna.PlayTo
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class MediaChangedEventArgs : EventArgs
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public UBaseObject OldMediaInfo { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public UBaseObject NewMediaInfo { get; set; }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -8,6 +8,7 @@ using System.Threading;
 | 
				
			|||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
using Emby.Dlna.Didl;
 | 
					using Emby.Dlna.Didl;
 | 
				
			||||||
using Jellyfin.Data.Entities;
 | 
					using Jellyfin.Data.Entities;
 | 
				
			||||||
 | 
					using Jellyfin.Data.Events;
 | 
				
			||||||
using MediaBrowser.Common.Configuration;
 | 
					using MediaBrowser.Common.Configuration;
 | 
				
			||||||
using MediaBrowser.Controller.Dlna;
 | 
					using MediaBrowser.Controller.Dlna;
 | 
				
			||||||
using MediaBrowser.Controller.Drawing;
 | 
					using MediaBrowser.Controller.Drawing;
 | 
				
			||||||
@ -18,7 +19,6 @@ using MediaBrowser.Controller.Session;
 | 
				
			|||||||
using MediaBrowser.Model.Dlna;
 | 
					using MediaBrowser.Model.Dlna;
 | 
				
			||||||
using MediaBrowser.Model.Dto;
 | 
					using MediaBrowser.Model.Dto;
 | 
				
			||||||
using MediaBrowser.Model.Entities;
 | 
					using MediaBrowser.Model.Entities;
 | 
				
			||||||
using MediaBrowser.Model.Events;
 | 
					 | 
				
			||||||
using MediaBrowser.Model.Globalization;
 | 
					using MediaBrowser.Model.Globalization;
 | 
				
			||||||
using MediaBrowser.Model.Session;
 | 
					using MediaBrowser.Model.Session;
 | 
				
			||||||
using Microsoft.AspNetCore.WebUtilities;
 | 
					using Microsoft.AspNetCore.WebUtilities;
 | 
				
			||||||
@ -31,7 +31,6 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        private static readonly CultureInfo _usCulture = CultureInfo.ReadOnly(new CultureInfo("en-US"));
 | 
					        private static readonly CultureInfo _usCulture = CultureInfo.ReadOnly(new CultureInfo("en-US"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private Device _device;
 | 
					 | 
				
			||||||
        private readonly SessionInfo _session;
 | 
					        private readonly SessionInfo _session;
 | 
				
			||||||
        private readonly ISessionManager _sessionManager;
 | 
					        private readonly ISessionManager _sessionManager;
 | 
				
			||||||
        private readonly ILibraryManager _libraryManager;
 | 
					        private readonly ILibraryManager _libraryManager;
 | 
				
			||||||
@ -50,6 +49,7 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
        private readonly string _accessToken;
 | 
					        private readonly string _accessToken;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private readonly List<PlaylistItem> _playlist = new List<PlaylistItem>();
 | 
					        private readonly List<PlaylistItem> _playlist = new List<PlaylistItem>();
 | 
				
			||||||
 | 
					        private Device _device;
 | 
				
			||||||
        private int _currentPlaylistIndex;
 | 
					        private int _currentPlaylistIndex;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private bool _disposed;
 | 
					        private bool _disposed;
 | 
				
			||||||
@ -372,8 +372,13 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            if (!command.ControllingUserId.Equals(Guid.Empty))
 | 
					            if (!command.ControllingUserId.Equals(Guid.Empty))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                _sessionManager.LogSessionActivity(_session.Client, _session.ApplicationVersion, _session.DeviceId,
 | 
					                _sessionManager.LogSessionActivity(
 | 
				
			||||||
                       _session.DeviceName, _session.RemoteEndPoint, user);
 | 
					                    _session.Client,
 | 
				
			||||||
 | 
					                    _session.ApplicationVersion,
 | 
				
			||||||
 | 
					                    _session.DeviceId,
 | 
				
			||||||
 | 
					                    _session.DeviceName,
 | 
				
			||||||
 | 
					                    _session.RemoteEndPoint,
 | 
				
			||||||
 | 
					                    user);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return PlayItems(playlist, cancellationToken);
 | 
					            return PlayItems(playlist, cancellationToken);
 | 
				
			||||||
@ -498,7 +503,8 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
            if (streamInfo.MediaType == DlnaProfileType.Audio)
 | 
					            if (streamInfo.MediaType == DlnaProfileType.Audio)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                return new ContentFeatureBuilder(profile)
 | 
					                return new ContentFeatureBuilder(profile)
 | 
				
			||||||
                    .BuildAudioHeader(streamInfo.Container,
 | 
					                    .BuildAudioHeader(
 | 
				
			||||||
 | 
					                        streamInfo.Container,
 | 
				
			||||||
                        streamInfo.TargetAudioCodec.FirstOrDefault(),
 | 
					                        streamInfo.TargetAudioCodec.FirstOrDefault(),
 | 
				
			||||||
                        streamInfo.TargetAudioBitrate,
 | 
					                        streamInfo.TargetAudioBitrate,
 | 
				
			||||||
                        streamInfo.TargetAudioSampleRate,
 | 
					                        streamInfo.TargetAudioSampleRate,
 | 
				
			||||||
@ -512,7 +518,8 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
            if (streamInfo.MediaType == DlnaProfileType.Video)
 | 
					            if (streamInfo.MediaType == DlnaProfileType.Video)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var list = new ContentFeatureBuilder(profile)
 | 
					                var list = new ContentFeatureBuilder(profile)
 | 
				
			||||||
                    .BuildVideoHeader(streamInfo.Container,
 | 
					                    .BuildVideoHeader(
 | 
				
			||||||
 | 
					                        streamInfo.Container,
 | 
				
			||||||
                        streamInfo.TargetVideoCodec.FirstOrDefault(),
 | 
					                        streamInfo.TargetVideoCodec.FirstOrDefault(),
 | 
				
			||||||
                        streamInfo.TargetAudioCodec.FirstOrDefault(),
 | 
					                        streamInfo.TargetAudioCodec.FirstOrDefault(),
 | 
				
			||||||
                        streamInfo.TargetWidth,
 | 
					                        streamInfo.TargetWidth,
 | 
				
			||||||
@ -633,6 +640,10 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
            GC.SuppressFinalize(this);
 | 
					            GC.SuppressFinalize(this);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// Releases unmanaged and optionally managed resources.
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
 | 
				
			||||||
        protected virtual void Dispose(bool disposing)
 | 
					        protected virtual void Dispose(bool disposing)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (_disposed)
 | 
					            if (_disposed)
 | 
				
			||||||
@ -673,10 +684,9 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
                    case GeneralCommandType.ToggleMute:
 | 
					                    case GeneralCommandType.ToggleMute:
 | 
				
			||||||
                        return _device.ToggleMute(cancellationToken);
 | 
					                        return _device.ToggleMute(cancellationToken);
 | 
				
			||||||
                    case GeneralCommandType.SetAudioStreamIndex:
 | 
					                    case GeneralCommandType.SetAudioStreamIndex:
 | 
				
			||||||
 | 
					                        if (command.Arguments.TryGetValue("Index", out string index))
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
                            if (command.Arguments.TryGetValue("Index", out string arg))
 | 
					                            if (int.TryParse(index, NumberStyles.Integer, _usCulture, out var val))
 | 
				
			||||||
                            {
 | 
					 | 
				
			||||||
                                if (int.TryParse(arg, NumberStyles.Integer, _usCulture, out var val))
 | 
					 | 
				
			||||||
                            {
 | 
					                            {
 | 
				
			||||||
                                return SetAudioStreamIndex(val);
 | 
					                                return SetAudioStreamIndex(val);
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
@ -685,12 +695,10 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
                        }
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        throw new ArgumentException("SetAudioStreamIndex argument cannot be null");
 | 
					                        throw new ArgumentException("SetAudioStreamIndex argument cannot be null");
 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    case GeneralCommandType.SetSubtitleStreamIndex:
 | 
					                    case GeneralCommandType.SetSubtitleStreamIndex:
 | 
				
			||||||
 | 
					                        if (command.Arguments.TryGetValue("Index", out index))
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
                            if (command.Arguments.TryGetValue("Index", out string arg))
 | 
					                            if (int.TryParse(index, NumberStyles.Integer, _usCulture, out var val))
 | 
				
			||||||
                            {
 | 
					 | 
				
			||||||
                                if (int.TryParse(arg, NumberStyles.Integer, _usCulture, out var val))
 | 
					 | 
				
			||||||
                            {
 | 
					                            {
 | 
				
			||||||
                                return SetSubtitleStreamIndex(val);
 | 
					                                return SetSubtitleStreamIndex(val);
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
@ -699,12 +707,10 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
                        }
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        throw new ArgumentException("SetSubtitleStreamIndex argument cannot be null");
 | 
					                        throw new ArgumentException("SetSubtitleStreamIndex argument cannot be null");
 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    case GeneralCommandType.SetVolume:
 | 
					                    case GeneralCommandType.SetVolume:
 | 
				
			||||||
 | 
					                        if (command.Arguments.TryGetValue("Volume", out string vol))
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
                            if (command.Arguments.TryGetValue("Volume", out string arg))
 | 
					                            if (int.TryParse(vol, NumberStyles.Integer, _usCulture, out var volume))
 | 
				
			||||||
                            {
 | 
					 | 
				
			||||||
                                if (int.TryParse(arg, NumberStyles.Integer, _usCulture, out var volume))
 | 
					 | 
				
			||||||
                            {
 | 
					                            {
 | 
				
			||||||
                                return _device.SetVolume(volume, cancellationToken);
 | 
					                                return _device.SetVolume(volume, cancellationToken);
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
@ -713,8 +719,6 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
                        }
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        throw new ArgumentException("Volume argument cannot be null");
 | 
					                        throw new ArgumentException("Volume argument cannot be null");
 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    default:
 | 
					                    default:
 | 
				
			||||||
                        return Task.CompletedTask;
 | 
					                        return Task.CompletedTask;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@ -778,7 +782,7 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
            const int maxWait = 15000000;
 | 
					            const int maxWait = 15000000;
 | 
				
			||||||
            const int interval = 500;
 | 
					            const int interval = 500;
 | 
				
			||||||
            var currentWait = 0;
 | 
					            var currentWait = 0;
 | 
				
			||||||
            while (_device.TransportState != TRANSPORTSTATE.PLAYING && currentWait < maxWait)
 | 
					            while (_device.TransportState != TransportState.Playing && currentWait < maxWait)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await Task.Delay(interval).ConfigureAwait(false);
 | 
					                await Task.Delay(interval).ConfigureAwait(false);
 | 
				
			||||||
                currentWait += interval;
 | 
					                currentWait += interval;
 | 
				
			||||||
@ -787,8 +791,67 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
            await _device.Seek(TimeSpan.FromTicks(positionTicks), cancellationToken).ConfigureAwait(false);
 | 
					            await _device.Seek(TimeSpan.FromTicks(positionTicks), cancellationToken).ConfigureAwait(false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private static int? GetIntValue(IReadOnlyDictionary<string, string> values, string name)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var value = values.GetValueOrDefault(name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                return result;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private static long GetLongValue(IReadOnlyDictionary<string, string> values, string name)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var value = values.GetValueOrDefault(name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (long.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                return result;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <inheritdoc />
 | 
				
			||||||
 | 
					        public Task SendMessage<T>(string name, Guid messageId, T data, CancellationToken cancellationToken)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (_disposed)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                throw new ObjectDisposedException(GetType().Name);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (_device == null)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                return Task.CompletedTask;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (string.Equals(name, "Play", StringComparison.OrdinalIgnoreCase))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                return SendPlayCommand(data as PlayRequest, cancellationToken);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (string.Equals(name, "PlayState", StringComparison.OrdinalIgnoreCase))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                return SendPlaystateCommand(data as PlaystateRequest, cancellationToken);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (string.Equals(name, "GeneralCommand", StringComparison.OrdinalIgnoreCase))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                return SendGeneralCommand(data as GeneralCommand, cancellationToken);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Not supported or needed right now
 | 
				
			||||||
 | 
					            return Task.CompletedTask;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private class StreamParams
 | 
					        private class StreamParams
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					            private MediaSourceInfo mediaSource;
 | 
				
			||||||
 | 
					            private IMediaSourceManager _mediaSourceManager;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            public Guid ItemId { get; set; }
 | 
					            public Guid ItemId { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            public bool IsDirectStream { get; set; }
 | 
					            public bool IsDirectStream { get; set; }
 | 
				
			||||||
@ -809,15 +872,11 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            public BaseItem Item { get; set; }
 | 
					            public BaseItem Item { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            private MediaSourceInfo MediaSource;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            private IMediaSourceManager _mediaSourceManager;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            public async Task<MediaSourceInfo> GetMediaSource(CancellationToken cancellationToken)
 | 
					            public async Task<MediaSourceInfo> GetMediaSource(CancellationToken cancellationToken)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                if (MediaSource != null)
 | 
					                if (mediaSource != null)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    return MediaSource;
 | 
					                    return mediaSource;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                var hasMediaSources = Item as IHasMediaSources;
 | 
					                var hasMediaSources = Item as IHasMediaSources;
 | 
				
			||||||
@ -827,9 +886,9 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
                    return null;
 | 
					                    return null;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                MediaSource = await _mediaSourceManager.GetMediaSource(Item, MediaSourceId, LiveStreamId, false, cancellationToken).ConfigureAwait(false);
 | 
					                mediaSource = await _mediaSourceManager.GetMediaSource(Item, MediaSourceId, LiveStreamId, false, cancellationToken).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                return MediaSource;
 | 
					                return mediaSource;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            private static Guid GetItemId(string url)
 | 
					            private static Guid GetItemId(string url)
 | 
				
			||||||
@ -901,61 +960,5 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
                return request;
 | 
					                return request;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static int? GetIntValue(IReadOnlyDictionary<string, string> values, string name)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var value = values.GetValueOrDefault(name);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                return result;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return null;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static long GetLongValue(IReadOnlyDictionary<string, string> values, string name)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var value = values.GetValueOrDefault(name);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (long.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                return result;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return 0;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <inheritdoc />
 | 
					 | 
				
			||||||
        public Task SendMessage<T>(string name, Guid messageId, T data, CancellationToken cancellationToken)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (_disposed)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                throw new ObjectDisposedException(GetType().Name);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (_device == null)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                return Task.CompletedTask;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (string.Equals(name, "Play", StringComparison.OrdinalIgnoreCase))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                return SendPlayCommand(data as PlayRequest, cancellationToken);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (string.Equals(name, "PlayState", StringComparison.OrdinalIgnoreCase))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                return SendPlaystateCommand(data as PlaystateRequest, cancellationToken);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (string.Equals(name, "GeneralCommand", StringComparison.OrdinalIgnoreCase))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                return SendGeneralCommand(data as GeneralCommand, cancellationToken);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // Not supported or needed right now
 | 
					 | 
				
			||||||
            return Task.CompletedTask;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -6,6 +6,7 @@ using System.Linq;
 | 
				
			|||||||
using System.Net;
 | 
					using System.Net;
 | 
				
			||||||
using System.Threading;
 | 
					using System.Threading;
 | 
				
			||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using Jellyfin.Data.Events;
 | 
				
			||||||
using MediaBrowser.Common.Extensions;
 | 
					using MediaBrowser.Common.Extensions;
 | 
				
			||||||
using MediaBrowser.Common.Net;
 | 
					using MediaBrowser.Common.Net;
 | 
				
			||||||
using MediaBrowser.Controller;
 | 
					using MediaBrowser.Controller;
 | 
				
			||||||
@ -16,7 +17,6 @@ using MediaBrowser.Controller.Library;
 | 
				
			|||||||
using MediaBrowser.Controller.MediaEncoding;
 | 
					using MediaBrowser.Controller.MediaEncoding;
 | 
				
			||||||
using MediaBrowser.Controller.Session;
 | 
					using MediaBrowser.Controller.Session;
 | 
				
			||||||
using MediaBrowser.Model.Dlna;
 | 
					using MediaBrowser.Model.Dlna;
 | 
				
			||||||
using MediaBrowser.Model.Events;
 | 
					 | 
				
			||||||
using MediaBrowser.Model.Globalization;
 | 
					using MediaBrowser.Model.Globalization;
 | 
				
			||||||
using MediaBrowser.Model.Session;
 | 
					using MediaBrowser.Model.Session;
 | 
				
			||||||
using Microsoft.Extensions.Logging;
 | 
					using Microsoft.Extensions.Logging;
 | 
				
			||||||
@ -174,7 +174,7 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            if (controller == null)
 | 
					            if (controller == null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var device = await Device.CreateuPnpDeviceAsync(uri, _httpClient, _config, _logger, cancellationToken).ConfigureAwait(false);
 | 
					                var device = await Device.CreateuPnpDeviceAsync(uri, _httpClient, _logger, cancellationToken).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                string deviceName = device.Properties.Name;
 | 
					                string deviceName = device.Properties.Name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -218,7 +218,7 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    PlayableMediaTypes = profile.GetSupportedMediaTypes(),
 | 
					                    PlayableMediaTypes = profile.GetSupportedMediaTypes(),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    SupportedCommands = new string[]
 | 
					                    SupportedCommands = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        GeneralCommandType.VolumeDown.ToString(),
 | 
					                        GeneralCommandType.VolumeDown.ToString(),
 | 
				
			||||||
                        GeneralCommandType.VolumeUp.ToString(),
 | 
					                        GeneralCommandType.VolumeUp.ToString(),
 | 
				
			||||||
@ -247,8 +247,9 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
            {
 | 
					            {
 | 
				
			||||||
                _disposeCancellationTokenSource.Cancel();
 | 
					                _disposeCancellationTokenSource.Cancel();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            catch
 | 
					            catch (Exception ex)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
 | 
					                _logger.LogDebug(ex, "Error while disposing PlayToManager");
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            _sessionLock.Dispose();
 | 
					            _sessionLock.Dispose();
 | 
				
			||||||
 | 
				
			|||||||
@ -6,6 +6,6 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    public class PlaybackProgressEventArgs : EventArgs
 | 
					    public class PlaybackProgressEventArgs : EventArgs
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        public uBaseObject MediaInfo { get; set; }
 | 
					        public UBaseObject MediaInfo { get; set; }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -6,6 +6,6 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    public class PlaybackStartEventArgs : EventArgs
 | 
					    public class PlaybackStartEventArgs : EventArgs
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        public uBaseObject MediaInfo { get; set; }
 | 
					        public UBaseObject MediaInfo { get; set; }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -6,13 +6,6 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    public class PlaybackStoppedEventArgs : EventArgs
 | 
					    public class PlaybackStoppedEventArgs : EventArgs
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        public uBaseObject MediaInfo { get; set; }
 | 
					        public UBaseObject MediaInfo { get; set; }
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public class MediaChangedEventArgs : EventArgs
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        public uBaseObject OldMediaInfo { get; set; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public uBaseObject NewMediaInfo { get; set; }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,13 +0,0 @@
 | 
				
			|||||||
#pragma warning disable CS1591
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace Emby.Dlna.PlayTo
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    public enum TRANSPORTSTATE
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        STOPPED,
 | 
					 | 
				
			||||||
        PLAYING,
 | 
					 | 
				
			||||||
        TRANSITIONING,
 | 
					 | 
				
			||||||
        PAUSED_PLAYBACK,
 | 
					 | 
				
			||||||
        PAUSED
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -2,6 +2,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
using System;
 | 
					using System;
 | 
				
			||||||
using System.Collections.Generic;
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Globalization;
 | 
				
			||||||
using System.Linq;
 | 
					using System.Linq;
 | 
				
			||||||
using System.Xml.Linq;
 | 
					using System.Xml.Linq;
 | 
				
			||||||
using Emby.Dlna.Common;
 | 
					using Emby.Dlna.Common;
 | 
				
			||||||
@ -11,36 +12,30 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    public class TransportCommands
 | 
					    public class TransportCommands
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					        private const string CommandBase = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" + "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" + "<SOAP-ENV:Body>" + "<m:{0} xmlns:m=\"{1}\">" + "{2}" + "</m:{0}>" + "</SOAP-ENV:Body></SOAP-ENV:Envelope>";
 | 
				
			||||||
        private List<StateVariable> _stateVariables = new List<StateVariable>();
 | 
					        private List<StateVariable> _stateVariables = new List<StateVariable>();
 | 
				
			||||||
        public List<StateVariable> StateVariables
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            get => _stateVariables;
 | 
					 | 
				
			||||||
            set => _stateVariables = value;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private List<ServiceAction> _serviceActions = new List<ServiceAction>();
 | 
					        private List<ServiceAction> _serviceActions = new List<ServiceAction>();
 | 
				
			||||||
        public List<ServiceAction> ServiceActions
 | 
					
 | 
				
			||||||
        {
 | 
					        public List<StateVariable> StateVariables => _stateVariables;
 | 
				
			||||||
            get => _serviceActions;
 | 
					
 | 
				
			||||||
            set => _serviceActions = value;
 | 
					        public List<ServiceAction> ServiceActions => _serviceActions;
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public static TransportCommands Create(XDocument document)
 | 
					        public static TransportCommands Create(XDocument document)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var command = new TransportCommands();
 | 
					            var command = new TransportCommands();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var actionList = document.Descendants(uPnpNamespaces.svc + "actionList");
 | 
					            var actionList = document.Descendants(UPnpNamespaces.Svc + "actionList");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            foreach (var container in actionList.Descendants(uPnpNamespaces.svc + "action"))
 | 
					            foreach (var container in actionList.Descendants(UPnpNamespaces.Svc + "action"))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                command.ServiceActions.Add(ServiceActionFromXml(container));
 | 
					                command.ServiceActions.Add(ServiceActionFromXml(container));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var stateValues = document.Descendants(uPnpNamespaces.ServiceStateTable).FirstOrDefault();
 | 
					            var stateValues = document.Descendants(UPnpNamespaces.ServiceStateTable).FirstOrDefault();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (stateValues != null)
 | 
					            if (stateValues != null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                foreach (var container in stateValues.Elements(uPnpNamespaces.svc + "stateVariable"))
 | 
					                foreach (var container in stateValues.Elements(UPnpNamespaces.Svc + "stateVariable"))
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    command.StateVariables.Add(FromXml(container));
 | 
					                    command.StateVariables.Add(FromXml(container));
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@ -51,19 +46,19 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        private static ServiceAction ServiceActionFromXml(XElement container)
 | 
					        private static ServiceAction ServiceActionFromXml(XElement container)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var argumentList = new List<Argument>();
 | 
					            var serviceAction = new ServiceAction
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                Name = container.GetValue(UPnpNamespaces.Svc + "name"),
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            foreach (var arg in container.Descendants(uPnpNamespaces.svc + "argument"))
 | 
					            var argumentList = serviceAction.ArgumentList;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            foreach (var arg in container.Descendants(UPnpNamespaces.Svc + "argument"))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                argumentList.Add(ArgumentFromXml(arg));
 | 
					                argumentList.Add(ArgumentFromXml(arg));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return new ServiceAction
 | 
					            return serviceAction;
 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                Name = container.GetValue(uPnpNamespaces.svc + "name"),
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                ArgumentList = argumentList
 | 
					 | 
				
			||||||
            };
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private static Argument ArgumentFromXml(XElement container)
 | 
					        private static Argument ArgumentFromXml(XElement container)
 | 
				
			||||||
@ -75,29 +70,29 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            return new Argument
 | 
					            return new Argument
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                Name = container.GetValue(uPnpNamespaces.svc + "name"),
 | 
					                Name = container.GetValue(UPnpNamespaces.Svc + "name"),
 | 
				
			||||||
                Direction = container.GetValue(uPnpNamespaces.svc + "direction"),
 | 
					                Direction = container.GetValue(UPnpNamespaces.Svc + "direction"),
 | 
				
			||||||
                RelatedStateVariable = container.GetValue(uPnpNamespaces.svc + "relatedStateVariable")
 | 
					                RelatedStateVariable = container.GetValue(UPnpNamespaces.Svc + "relatedStateVariable")
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private static StateVariable FromXml(XElement container)
 | 
					        private static StateVariable FromXml(XElement container)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var allowedValues = new List<string>();
 | 
					            var allowedValues = new List<string>();
 | 
				
			||||||
            var element = container.Descendants(uPnpNamespaces.svc + "allowedValueList")
 | 
					            var element = container.Descendants(UPnpNamespaces.Svc + "allowedValueList")
 | 
				
			||||||
                .FirstOrDefault();
 | 
					                .FirstOrDefault();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (element != null)
 | 
					            if (element != null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var values = element.Descendants(uPnpNamespaces.svc + "allowedValue");
 | 
					                var values = element.Descendants(UPnpNamespaces.Svc + "allowedValue");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                allowedValues.AddRange(values.Select(child => child.Value));
 | 
					                allowedValues.AddRange(values.Select(child => child.Value));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return new StateVariable
 | 
					            return new StateVariable
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                Name = container.GetValue(uPnpNamespaces.svc + "name"),
 | 
					                Name = container.GetValue(UPnpNamespaces.Svc + "name"),
 | 
				
			||||||
                DataType = container.GetValue(uPnpNamespaces.svc + "dataType"),
 | 
					                DataType = container.GetValue(UPnpNamespaces.Svc + "dataType"),
 | 
				
			||||||
                AllowedValues = allowedValues.ToArray()
 | 
					                AllowedValues = allowedValues.ToArray()
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -123,7 +118,7 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return string.Format(CommandBase, action.Name, xmlNamespace, stateString);
 | 
					            return string.Format(CultureInfo.InvariantCulture, CommandBase, action.Name, xmlNamespace, stateString);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public string BuildPost(ServiceAction action, string xmlNamesapce, object value, string commandParameter = "")
 | 
					        public string BuildPost(ServiceAction action, string xmlNamesapce, object value, string commandParameter = "")
 | 
				
			||||||
@ -147,7 +142,7 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return string.Format(CommandBase, action.Name, xmlNamesapce, stateString);
 | 
					            return string.Format(CultureInfo.InvariantCulture, CommandBase, action.Name, xmlNamesapce, stateString);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public string BuildPost(ServiceAction action, string xmlNamesapce, object value, Dictionary<string, string> dictionary)
 | 
					        public string BuildPost(ServiceAction action, string xmlNamesapce, object value, Dictionary<string, string> dictionary)
 | 
				
			||||||
@ -170,7 +165,7 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return string.Format(CommandBase, action.Name, xmlNamesapce, stateString);
 | 
					            return string.Format(CultureInfo.InvariantCulture, CommandBase, action.Name, xmlNamesapce, stateString);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private string BuildArgumentXml(Argument argument, string value, string commandParameter = "")
 | 
					        private string BuildArgumentXml(Argument argument, string value, string commandParameter = "")
 | 
				
			||||||
@ -180,15 +175,12 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
            if (state != null)
 | 
					            if (state != null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var sendValue = state.AllowedValues.FirstOrDefault(a => string.Equals(a, commandParameter, StringComparison.OrdinalIgnoreCase)) ??
 | 
					                var sendValue = state.AllowedValues.FirstOrDefault(a => string.Equals(a, commandParameter, StringComparison.OrdinalIgnoreCase)) ??
 | 
				
			||||||
                                 state.AllowedValues.FirstOrDefault() ??
 | 
					                    (state.AllowedValues.Count > 0 ? state.AllowedValues[0] : value);
 | 
				
			||||||
                                 value;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                return string.Format("<{0} xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"{1}\">{2}</{0}>", argument.Name, state.DataType ?? "string", sendValue);
 | 
					                return string.Format(CultureInfo.InvariantCulture, "<{0} xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"{1}\">{2}</{0}>", argument.Name, state.DataType ?? "string", sendValue);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return string.Format("<{0}>{1}</{0}>", argument.Name, value);
 | 
					            return string.Format(CultureInfo.InvariantCulture, "<{0}>{1}</{0}>", argument.Name, value);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					 | 
				
			||||||
        private const string CommandBase = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" + "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" + "<SOAP-ENV:Body>" + "<m:{0} xmlns:m=\"{1}\">" + "{2}" + "</m:{0}>" + "</SOAP-ENV:Body></SOAP-ENV:Envelope>";
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										14
									
								
								Emby.Dlna/PlayTo/TransportState.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								Emby.Dlna/PlayTo/TransportState.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					#pragma warning disable CS1591
 | 
				
			||||||
 | 
					#pragma warning disable SA1602
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Emby.Dlna.PlayTo
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public enum TransportState
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Stopped,
 | 
				
			||||||
 | 
					        Playing,
 | 
				
			||||||
 | 
					        Transitioning,
 | 
				
			||||||
 | 
					        PausedPlayback,
 | 
				
			||||||
 | 
					        Paused
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -6,22 +6,22 @@ using Emby.Dlna.Ssdp;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace Emby.Dlna.PlayTo
 | 
					namespace Emby.Dlna.PlayTo
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public class UpnpContainer : uBaseObject
 | 
					    public class UpnpContainer : UBaseObject
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        public static uBaseObject Create(XElement container)
 | 
					        public static UBaseObject Create(XElement container)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (container == null)
 | 
					            if (container == null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                throw new ArgumentNullException(nameof(container));
 | 
					                throw new ArgumentNullException(nameof(container));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return new uBaseObject
 | 
					            return new UBaseObject
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                Id = container.GetAttributeValue(uPnpNamespaces.Id),
 | 
					                Id = container.GetAttributeValue(UPnpNamespaces.Id),
 | 
				
			||||||
                ParentId = container.GetAttributeValue(uPnpNamespaces.ParentId),
 | 
					                ParentId = container.GetAttributeValue(UPnpNamespaces.ParentId),
 | 
				
			||||||
                Title = container.GetValue(uPnpNamespaces.title),
 | 
					                Title = container.GetValue(UPnpNamespaces.Title),
 | 
				
			||||||
                IconUrl = container.GetValue(uPnpNamespaces.Artwork),
 | 
					                IconUrl = container.GetValue(UPnpNamespaces.Artwork),
 | 
				
			||||||
                UpnpClass = container.GetValue(uPnpNamespaces.uClass)
 | 
					                UpnpClass = container.GetValue(UPnpNamespaces.Class)
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,10 +1,11 @@
 | 
				
			|||||||
#pragma warning disable CS1591
 | 
					#pragma warning disable CS1591
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using System;
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Emby.Dlna.PlayTo
 | 
					namespace Emby.Dlna.PlayTo
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public class uBaseObject
 | 
					    public class UBaseObject
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        public string Id { get; set; }
 | 
					        public string Id { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -20,20 +21,10 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        public string Url { get; set; }
 | 
					        public string Url { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public string[] ProtocolInfo { get; set; }
 | 
					        public IReadOnlyList<string> ProtocolInfo { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public string UpnpClass { get; set; }
 | 
					        public string UpnpClass { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public bool Equals(uBaseObject obj)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (obj == null)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                throw new ArgumentNullException(nameof(obj));
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return string.Equals(Id, obj.Id);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public string MediaType
 | 
					        public string MediaType
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            get
 | 
					            get
 | 
				
			||||||
@ -58,5 +49,15 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
                return null;
 | 
					                return null;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public bool Equals(UBaseObject obj)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (obj == null)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                throw new ArgumentNullException(nameof(obj));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return string.Equals(Id, obj.Id, StringComparison.Ordinal);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -4,38 +4,64 @@ using System.Xml.Linq;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace Emby.Dlna.PlayTo
 | 
					namespace Emby.Dlna.PlayTo
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public class uPnpNamespaces
 | 
					    public static class UPnpNamespaces
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        public static XNamespace dc = "http://purl.org/dc/elements/1.1/";
 | 
					        public static XNamespace Dc { get; } = "http://purl.org/dc/elements/1.1/";
 | 
				
			||||||
        public static XNamespace ns = "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/";
 | 
					 | 
				
			||||||
        public static XNamespace svc = "urn:schemas-upnp-org:service-1-0";
 | 
					 | 
				
			||||||
        public static XNamespace ud = "urn:schemas-upnp-org:device-1-0";
 | 
					 | 
				
			||||||
        public static XNamespace upnp = "urn:schemas-upnp-org:metadata-1-0/upnp/";
 | 
					 | 
				
			||||||
        public static XNamespace RenderingControl = "urn:schemas-upnp-org:service:RenderingControl:1";
 | 
					 | 
				
			||||||
        public static XNamespace AvTransport = "urn:schemas-upnp-org:service:AVTransport:1";
 | 
					 | 
				
			||||||
        public static XNamespace ContentDirectory = "urn:schemas-upnp-org:service:ContentDirectory:1";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public static XName containers = ns + "container";
 | 
					        public static XNamespace Ns { get; } = "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/";
 | 
				
			||||||
        public static XName items = ns + "item";
 | 
					 | 
				
			||||||
        public static XName title = dc + "title";
 | 
					 | 
				
			||||||
        public static XName creator = dc + "creator";
 | 
					 | 
				
			||||||
        public static XName artist = upnp + "artist";
 | 
					 | 
				
			||||||
        public static XName Id = "id";
 | 
					 | 
				
			||||||
        public static XName ParentId = "parentID";
 | 
					 | 
				
			||||||
        public static XName uClass = upnp + "class";
 | 
					 | 
				
			||||||
        public static XName Artwork = upnp + "albumArtURI";
 | 
					 | 
				
			||||||
        public static XName Description = dc + "description";
 | 
					 | 
				
			||||||
        public static XName LongDescription = upnp + "longDescription";
 | 
					 | 
				
			||||||
        public static XName Album = upnp + "album";
 | 
					 | 
				
			||||||
        public static XName Author = upnp + "author";
 | 
					 | 
				
			||||||
        public static XName Director = upnp + "director";
 | 
					 | 
				
			||||||
        public static XName PlayCount = upnp + "playbackCount";
 | 
					 | 
				
			||||||
        public static XName Tracknumber = upnp + "originalTrackNumber";
 | 
					 | 
				
			||||||
        public static XName Res = ns + "res";
 | 
					 | 
				
			||||||
        public static XName Duration = "duration";
 | 
					 | 
				
			||||||
        public static XName ProtocolInfo = "protocolInfo";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public static XName ServiceStateTable = svc + "serviceStateTable";
 | 
					        public static XNamespace Svc { get; } = "urn:schemas-upnp-org:service-1-0";
 | 
				
			||||||
        public static XName StateVariable = svc + "stateVariable";
 | 
					
 | 
				
			||||||
 | 
					        public static XNamespace Ud { get; } = "urn:schemas-upnp-org:device-1-0";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static XNamespace UPnp { get; } = "urn:schemas-upnp-org:metadata-1-0/upnp/";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static XNamespace RenderingControl { get; } = "urn:schemas-upnp-org:service:RenderingControl:1";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static XNamespace AvTransport { get; } = "urn:schemas-upnp-org:service:AVTransport:1";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static XNamespace ContentDirectory { get; } = "urn:schemas-upnp-org:service:ContentDirectory:1";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static XName Containers { get; } = Ns + "container";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static XName Items { get; } = Ns + "item";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static XName Title { get; } = Dc + "title";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static XName Creator { get; } = Dc + "creator";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static XName Artist { get; } = UPnp + "artist";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static XName Id { get; } = "id";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static XName ParentId { get; } = "parentID";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static XName Class { get; } = UPnp + "class";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static XName Artwork { get; } = UPnp + "albumArtURI";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static XName Description { get; } = Dc + "description";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static XName LongDescription { get; } = UPnp + "longDescription";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static XName Album { get; } = UPnp + "album";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static XName Author { get; } = UPnp + "author";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static XName Director { get; } = UPnp + "director";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static XName PlayCount { get; } = UPnp + "playbackCount";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static XName Tracknumber { get; } = UPnp + "originalTrackNumber";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static XName Res { get; } = Ns + "res";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static XName Duration { get; } = "duration";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static XName ProtocolInfo { get; } = "protocolInfo";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static XName ServiceStateTable { get; } = Svc + "serviceStateTable";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static XName StateVariable { get; } = Svc + "stateVariable";
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -64,14 +64,14 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                new DirectPlayProfile
 | 
					                new DirectPlayProfile
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    // play all
 | 
					                    // play all
 | 
				
			||||||
                    Container = "",
 | 
					                    Container = string.Empty,
 | 
				
			||||||
                    Type = DlnaProfileType.Video
 | 
					                    Type = DlnaProfileType.Video
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                new DirectPlayProfile
 | 
					                new DirectPlayProfile
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    // play all
 | 
					                    // play all
 | 
				
			||||||
                    Container = "",
 | 
					                    Container = string.Empty,
 | 
				
			||||||
                    Type = DlnaProfileType.Audio
 | 
					                    Type = DlnaProfileType.Audio
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
 | 
				
			|||||||
@ -24,7 +24,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                    {
 | 
					                    {
 | 
				
			||||||
                         Match = HeaderMatchType.Substring,
 | 
					                         Match = HeaderMatchType.Substring,
 | 
				
			||||||
                         Name = "User-Agent",
 | 
					                         Name = "User-Agent",
 | 
				
			||||||
                         Value ="Zip_"
 | 
					                         Value = "Zip_"
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
@ -81,7 +81,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = CodecType.Video,
 | 
					                    Type = CodecType.Video,
 | 
				
			||||||
                    Codec = "h264",
 | 
					                    Codec = "h264",
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -124,7 +124,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                new CodecProfile
 | 
					                new CodecProfile
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = CodecType.Video,
 | 
					                    Type = CodecType.Video,
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -161,7 +161,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = CodecType.VideoAudio,
 | 
					                    Type = CodecType.VideoAudio,
 | 
				
			||||||
                    Codec = "ac3,he-aac",
 | 
					                    Codec = "ac3,he-aac",
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -177,7 +177,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = CodecType.VideoAudio,
 | 
					                    Type = CodecType.VideoAudio,
 | 
				
			||||||
                    Codec = "aac",
 | 
					                    Codec = "aac",
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -192,7 +192,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                new CodecProfile
 | 
					                new CodecProfile
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = CodecType.VideoAudio,
 | 
					                    Type = CodecType.VideoAudio,
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        // The device does not have any audio switching capabilities
 | 
					                        // The device does not have any audio switching capabilities
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
 | 
				
			|||||||
@ -84,7 +84,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = DlnaProfileType.Photo,
 | 
					                    Type = DlnaProfileType.Photo,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -191,7 +191,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            ResponseProfiles = new ResponseProfile[]
 | 
					            ResponseProfiles = new[]
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                new ResponseProfile
 | 
					                new ResponseProfile
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
 | 
				
			|||||||
@ -32,7 +32,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            ResponseProfiles = new ResponseProfile[]
 | 
					            ResponseProfiles = new[]
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                new ResponseProfile
 | 
					                new ResponseProfile
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
 | 
				
			|||||||
@ -138,7 +138,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = DlnaProfileType.Photo,
 | 
					                    Type = DlnaProfileType.Photo,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
 | 
				
			|||||||
@ -93,8 +93,8 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                new CodecProfile
 | 
					                new CodecProfile
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = CodecType.Video,
 | 
					                    Type = CodecType.Video,
 | 
				
			||||||
                    Codec="h264",
 | 
					                    Codec = "h264",
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition(ProfileConditionType.EqualsAny, ProfileConditionValue.VideoProfile, "baseline|constrained baseline"),
 | 
					                        new ProfileCondition(ProfileConditionType.EqualsAny, ProfileConditionValue.VideoProfile, "baseline|constrained baseline"),
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
@ -122,7 +122,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                new CodecProfile
 | 
					                new CodecProfile
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = CodecType.Video,
 | 
					                    Type = CodecType.Video,
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -150,7 +150,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = CodecType.VideoAudio,
 | 
					                    Type = CodecType.VideoAudio,
 | 
				
			||||||
                    Codec = "aac",
 | 
					                    Codec = "aac",
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -166,7 +166,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = CodecType.Audio,
 | 
					                    Type = CodecType.Audio,
 | 
				
			||||||
                    Codec = "aac",
 | 
					                    Codec = "aac",
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -182,7 +182,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = CodecType.Audio,
 | 
					                    Type = CodecType.Audio,
 | 
				
			||||||
                    Codec = "mp3",
 | 
					                    Codec = "mp3",
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -202,7 +202,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            ResponseProfiles = new ResponseProfile[]
 | 
					            ResponseProfiles = new[]
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                new ResponseProfile
 | 
					                new ResponseProfile
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
 | 
				
			|||||||
@ -139,7 +139,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = DlnaProfileType.Photo,
 | 
					                    Type = DlnaProfileType.Photo,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
 | 
				
			|||||||
@ -150,7 +150,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = CodecType.Video,
 | 
					                    Type = CodecType.Video,
 | 
				
			||||||
                    Codec = "h264",
 | 
					                    Codec = "h264",
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -178,7 +178,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = CodecType.VideoAudio,
 | 
					                    Type = CodecType.VideoAudio,
 | 
				
			||||||
                    Codec = "ac3",
 | 
					                    Codec = "ac3",
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -197,7 +197,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = DlnaProfileType.Photo,
 | 
					                    Type = DlnaProfileType.Photo,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
 | 
				
			|||||||
@ -150,7 +150,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = CodecType.Video,
 | 
					                    Type = CodecType.Video,
 | 
				
			||||||
                    Codec = "h264",
 | 
					                    Codec = "h264",
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -178,7 +178,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = CodecType.VideoAudio,
 | 
					                    Type = CodecType.VideoAudio,
 | 
				
			||||||
                    Codec = "ac3",
 | 
					                    Codec = "ac3",
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -197,7 +197,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = DlnaProfileType.Photo,
 | 
					                    Type = DlnaProfileType.Photo,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
 | 
				
			|||||||
@ -138,7 +138,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = CodecType.Video,
 | 
					                    Type = CodecType.Video,
 | 
				
			||||||
                    Codec = "h264",
 | 
					                    Codec = "h264",
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -166,7 +166,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = CodecType.VideoAudio,
 | 
					                    Type = CodecType.VideoAudio,
 | 
				
			||||||
                    Codec = "ac3",
 | 
					                    Codec = "ac3",
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -185,7 +185,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = DlnaProfileType.Photo,
 | 
					                    Type = DlnaProfileType.Photo,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
 | 
				
			|||||||
@ -138,7 +138,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = CodecType.Video,
 | 
					                    Type = CodecType.Video,
 | 
				
			||||||
                    Codec = "h264",
 | 
					                    Codec = "h264",
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -166,7 +166,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = CodecType.VideoAudio,
 | 
					                    Type = CodecType.VideoAudio,
 | 
				
			||||||
                    Codec = "ac3",
 | 
					                    Codec = "ac3",
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -185,7 +185,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = DlnaProfileType.Photo,
 | 
					                    Type = DlnaProfileType.Photo,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
 | 
				
			|||||||
@ -114,7 +114,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = CodecType.Video,
 | 
					                    Type = CodecType.Video,
 | 
				
			||||||
                    Codec = "h264",
 | 
					                    Codec = "h264",
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -156,7 +156,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = CodecType.VideoAudio,
 | 
					                    Type = CodecType.VideoAudio,
 | 
				
			||||||
                    Codec = "ac3",
 | 
					                    Codec = "ac3",
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -172,7 +172,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = CodecType.VideoAudio,
 | 
					                    Type = CodecType.VideoAudio,
 | 
				
			||||||
                    Codec = "aac",
 | 
					                    Codec = "aac",
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -191,7 +191,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = DlnaProfileType.Photo,
 | 
					                    Type = DlnaProfileType.Photo,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -217,7 +217,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                    VideoCodec = "h264,mpeg4,vc1",
 | 
					                    VideoCodec = "h264,mpeg4,vc1",
 | 
				
			||||||
                    AudioCodec = "ac3,aac,mp3",
 | 
					                    AudioCodec = "ac3,aac,mp3",
 | 
				
			||||||
                    MimeType = "video/vnd.dlna.mpeg-tts",
 | 
					                    MimeType = "video/vnd.dlna.mpeg-tts",
 | 
				
			||||||
                    OrgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO",
 | 
					                    OrgPn = "MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO",
 | 
				
			||||||
                    Type = DlnaProfileType.Video
 | 
					                    Type = DlnaProfileType.Video
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -102,13 +102,13 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                new ResponseProfile
 | 
					                new ResponseProfile
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Container = "ts,mpegts",
 | 
					                    Container = "ts,mpegts",
 | 
				
			||||||
                    VideoCodec="h264",
 | 
					                    VideoCodec = "h264",
 | 
				
			||||||
                    AudioCodec="ac3,aac,mp3",
 | 
					                    AudioCodec = "ac3,aac,mp3",
 | 
				
			||||||
                    MimeType = "video/vnd.dlna.mpeg-tts",
 | 
					                    MimeType = "video/vnd.dlna.mpeg-tts",
 | 
				
			||||||
                    OrgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T",
 | 
					                    OrgPn = "AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T",
 | 
				
			||||||
                    Type = DlnaProfileType.Video,
 | 
					                    Type = DlnaProfileType.Video,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -128,13 +128,13 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                new ResponseProfile
 | 
					                new ResponseProfile
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Container = "ts,mpegts",
 | 
					                    Container = "ts,mpegts",
 | 
				
			||||||
                    VideoCodec="h264",
 | 
					                    VideoCodec = "h264",
 | 
				
			||||||
                    AudioCodec="ac3,aac,mp3",
 | 
					                    AudioCodec = "ac3,aac,mp3",
 | 
				
			||||||
                    MimeType = "video/mpeg",
 | 
					                    MimeType = "video/mpeg",
 | 
				
			||||||
                    OrgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO",
 | 
					                    OrgPn = "AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO",
 | 
				
			||||||
                    Type = DlnaProfileType.Video,
 | 
					                    Type = DlnaProfileType.Video,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -148,28 +148,28 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                new ResponseProfile
 | 
					                new ResponseProfile
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Container = "ts,mpegts",
 | 
					                    Container = "ts,mpegts",
 | 
				
			||||||
                    VideoCodec="h264",
 | 
					                    VideoCodec = "h264",
 | 
				
			||||||
                    AudioCodec="ac3,aac,mp3",
 | 
					                    AudioCodec = "ac3,aac,mp3",
 | 
				
			||||||
                    MimeType = "video/vnd.dlna.mpeg-tts",
 | 
					                    MimeType = "video/vnd.dlna.mpeg-tts",
 | 
				
			||||||
                    OrgPn="AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU",
 | 
					                    OrgPn = "AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU",
 | 
				
			||||||
                    Type = DlnaProfileType.Video
 | 
					                    Type = DlnaProfileType.Video
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                new ResponseProfile
 | 
					                new ResponseProfile
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Container = "ts,mpegts",
 | 
					                    Container = "ts,mpegts",
 | 
				
			||||||
                    VideoCodec="mpeg2video",
 | 
					                    VideoCodec = "mpeg2video",
 | 
				
			||||||
                    MimeType = "video/vnd.dlna.mpeg-tts",
 | 
					                    MimeType = "video/vnd.dlna.mpeg-tts",
 | 
				
			||||||
                    OrgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO",
 | 
					                    OrgPn = "MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO",
 | 
				
			||||||
                    Type = DlnaProfileType.Video
 | 
					                    Type = DlnaProfileType.Video
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                new ResponseProfile
 | 
					                new ResponseProfile
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Container = "mpeg",
 | 
					                    Container = "mpeg",
 | 
				
			||||||
                    VideoCodec="mpeg1video,mpeg2video",
 | 
					                    VideoCodec = "mpeg1video,mpeg2video",
 | 
				
			||||||
                    MimeType = "video/mpeg",
 | 
					                    MimeType = "video/mpeg",
 | 
				
			||||||
                    OrgPn="MPEG_PS_NTSC,MPEG_PS_PAL",
 | 
					                    OrgPn = "MPEG_PS_NTSC,MPEG_PS_PAL",
 | 
				
			||||||
                    Type = DlnaProfileType.Video
 | 
					                    Type = DlnaProfileType.Video
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
@ -180,7 +180,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = DlnaProfileType.Photo,
 | 
					                    Type = DlnaProfileType.Photo,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -204,7 +204,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = CodecType.Video,
 | 
					                    Type = CodecType.Video,
 | 
				
			||||||
                    Codec = "h264",
 | 
					                    Codec = "h264",
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -243,7 +243,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = CodecType.Video,
 | 
					                    Type = CodecType.Video,
 | 
				
			||||||
                    Codec = "mpeg2video",
 | 
					                    Codec = "mpeg2video",
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -275,7 +275,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                new CodecProfile
 | 
					                new CodecProfile
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = CodecType.Video,
 | 
					                    Type = CodecType.Video,
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -303,7 +303,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                    Type = CodecType.VideoAudio,
 | 
					                    Type = CodecType.VideoAudio,
 | 
				
			||||||
                    Codec = "ac3",
 | 
					                    Codec = "ac3",
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -319,7 +319,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                    Type = CodecType.VideoAudio,
 | 
					                    Type = CodecType.VideoAudio,
 | 
				
			||||||
                    Codec = "aac",
 | 
					                    Codec = "aac",
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -341,7 +341,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                    Type = CodecType.VideoAudio,
 | 
					                    Type = CodecType.VideoAudio,
 | 
				
			||||||
                    Codec = "mp3,mp2",
 | 
					                    Codec = "mp3,mp2",
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
 | 
				
			|||||||
@ -120,7 +120,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = DlnaProfileType.Photo,
 | 
					                    Type = DlnaProfileType.Photo,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -143,13 +143,13 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                new ResponseProfile
 | 
					                new ResponseProfile
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Container = "ts,mpegts",
 | 
					                    Container = "ts,mpegts",
 | 
				
			||||||
                    VideoCodec="h264",
 | 
					                    VideoCodec = "h264",
 | 
				
			||||||
                    AudioCodec="ac3,aac,mp3",
 | 
					                    AudioCodec = "ac3,aac,mp3",
 | 
				
			||||||
                    MimeType = "video/vnd.dlna.mpeg-tts",
 | 
					                    MimeType = "video/vnd.dlna.mpeg-tts",
 | 
				
			||||||
                    OrgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T",
 | 
					                    OrgPn = "AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T",
 | 
				
			||||||
                    Type = DlnaProfileType.Video,
 | 
					                    Type = DlnaProfileType.Video,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -169,13 +169,13 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                new ResponseProfile
 | 
					                new ResponseProfile
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Container = "ts,mpegts",
 | 
					                    Container = "ts,mpegts",
 | 
				
			||||||
                    VideoCodec="h264",
 | 
					                    VideoCodec = "h264",
 | 
				
			||||||
                    AudioCodec="ac3,aac,mp3",
 | 
					                    AudioCodec = "ac3,aac,mp3",
 | 
				
			||||||
                    MimeType = "video/mpeg",
 | 
					                    MimeType = "video/mpeg",
 | 
				
			||||||
                    OrgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO",
 | 
					                    OrgPn = "AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO",
 | 
				
			||||||
                    Type = DlnaProfileType.Video,
 | 
					                    Type = DlnaProfileType.Video,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -189,28 +189,28 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                new ResponseProfile
 | 
					                new ResponseProfile
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Container = "ts,mpegts",
 | 
					                    Container = "ts,mpegts",
 | 
				
			||||||
                    VideoCodec="h264",
 | 
					                    VideoCodec = "h264",
 | 
				
			||||||
                    AudioCodec="ac3,aac,mp3",
 | 
					                    AudioCodec = "ac3,aac,mp3",
 | 
				
			||||||
                    MimeType = "video/vnd.dlna.mpeg-tts",
 | 
					                    MimeType = "video/vnd.dlna.mpeg-tts",
 | 
				
			||||||
                    OrgPn="AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU",
 | 
					                    OrgPn = "AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU",
 | 
				
			||||||
                    Type = DlnaProfileType.Video
 | 
					                    Type = DlnaProfileType.Video
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                new ResponseProfile
 | 
					                new ResponseProfile
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Container = "ts,mpegts",
 | 
					                    Container = "ts,mpegts",
 | 
				
			||||||
                    VideoCodec="mpeg2video",
 | 
					                    VideoCodec = "mpeg2video",
 | 
				
			||||||
                    MimeType = "video/vnd.dlna.mpeg-tts",
 | 
					                    MimeType = "video/vnd.dlna.mpeg-tts",
 | 
				
			||||||
                    OrgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO",
 | 
					                    OrgPn = "MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO",
 | 
				
			||||||
                    Type = DlnaProfileType.Video
 | 
					                    Type = DlnaProfileType.Video
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                new ResponseProfile
 | 
					                new ResponseProfile
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Container = "mpeg",
 | 
					                    Container = "mpeg",
 | 
				
			||||||
                    VideoCodec="mpeg1video,mpeg2video",
 | 
					                    VideoCodec = "mpeg1video,mpeg2video",
 | 
				
			||||||
                    MimeType = "video/mpeg",
 | 
					                    MimeType = "video/mpeg",
 | 
				
			||||||
                    OrgPn="MPEG_PS_NTSC,MPEG_PS_PAL",
 | 
					                    OrgPn = "MPEG_PS_NTSC,MPEG_PS_PAL",
 | 
				
			||||||
                    Type = DlnaProfileType.Video
 | 
					                    Type = DlnaProfileType.Video
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                new ResponseProfile
 | 
					                new ResponseProfile
 | 
				
			||||||
@ -227,7 +227,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = CodecType.Video,
 | 
					                    Type = CodecType.Video,
 | 
				
			||||||
                    Codec = "h264",
 | 
					                    Codec = "h264",
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -266,7 +266,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = CodecType.Video,
 | 
					                    Type = CodecType.Video,
 | 
				
			||||||
                    Codec = "mpeg2video",
 | 
					                    Codec = "mpeg2video",
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -298,7 +298,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                new CodecProfile
 | 
					                new CodecProfile
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = CodecType.Video,
 | 
					                    Type = CodecType.Video,
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -326,7 +326,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                    Type = CodecType.VideoAudio,
 | 
					                    Type = CodecType.VideoAudio,
 | 
				
			||||||
                    Codec = "ac3",
 | 
					                    Codec = "ac3",
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -364,7 +364,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                    Type = CodecType.VideoAudio,
 | 
					                    Type = CodecType.VideoAudio,
 | 
				
			||||||
                    Codec = "mp3,mp2",
 | 
					                    Codec = "mp3,mp2",
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
 | 
				
			|||||||
@ -131,13 +131,13 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                new ResponseProfile
 | 
					                new ResponseProfile
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Container = "ts,mpegts",
 | 
					                    Container = "ts,mpegts",
 | 
				
			||||||
                    VideoCodec="h264",
 | 
					                    VideoCodec = "h264",
 | 
				
			||||||
                    AudioCodec="ac3,aac,mp3",
 | 
					                    AudioCodec = "ac3,aac,mp3",
 | 
				
			||||||
                    MimeType = "video/vnd.dlna.mpeg-tts",
 | 
					                    MimeType = "video/vnd.dlna.mpeg-tts",
 | 
				
			||||||
                    OrgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T",
 | 
					                    OrgPn = "AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T",
 | 
				
			||||||
                    Type = DlnaProfileType.Video,
 | 
					                    Type = DlnaProfileType.Video,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -157,13 +157,13 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                new ResponseProfile
 | 
					                new ResponseProfile
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Container = "ts,mpegts",
 | 
					                    Container = "ts,mpegts",
 | 
				
			||||||
                    VideoCodec="h264",
 | 
					                    VideoCodec = "h264",
 | 
				
			||||||
                    AudioCodec="ac3,aac,mp3",
 | 
					                    AudioCodec = "ac3,aac,mp3",
 | 
				
			||||||
                    MimeType = "video/mpeg",
 | 
					                    MimeType = "video/mpeg",
 | 
				
			||||||
                    OrgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO",
 | 
					                    OrgPn = "AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO",
 | 
				
			||||||
                    Type = DlnaProfileType.Video,
 | 
					                    Type = DlnaProfileType.Video,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -177,28 +177,28 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                new ResponseProfile
 | 
					                new ResponseProfile
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Container = "ts,mpegts",
 | 
					                    Container = "ts,mpegts",
 | 
				
			||||||
                    VideoCodec="h264",
 | 
					                    VideoCodec = "h264",
 | 
				
			||||||
                    AudioCodec="ac3,aac,mp3",
 | 
					                    AudioCodec = "ac3,aac,mp3",
 | 
				
			||||||
                    MimeType = "video/vnd.dlna.mpeg-tts",
 | 
					                    MimeType = "video/vnd.dlna.mpeg-tts",
 | 
				
			||||||
                    OrgPn="AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU",
 | 
					                    OrgPn = "AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU",
 | 
				
			||||||
                    Type = DlnaProfileType.Video
 | 
					                    Type = DlnaProfileType.Video
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                new ResponseProfile
 | 
					                new ResponseProfile
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Container = "ts,mpegts",
 | 
					                    Container = "ts,mpegts",
 | 
				
			||||||
                    VideoCodec="mpeg2video",
 | 
					                    VideoCodec = "mpeg2video",
 | 
				
			||||||
                    MimeType = "video/vnd.dlna.mpeg-tts",
 | 
					                    MimeType = "video/vnd.dlna.mpeg-tts",
 | 
				
			||||||
                    OrgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO",
 | 
					                    OrgPn = "MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO",
 | 
				
			||||||
                    Type = DlnaProfileType.Video
 | 
					                    Type = DlnaProfileType.Video
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                new ResponseProfile
 | 
					                new ResponseProfile
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Container = "mpeg",
 | 
					                    Container = "mpeg",
 | 
				
			||||||
                    VideoCodec="mpeg1video,mpeg2video",
 | 
					                    VideoCodec = "mpeg1video,mpeg2video",
 | 
				
			||||||
                    MimeType = "video/mpeg",
 | 
					                    MimeType = "video/mpeg",
 | 
				
			||||||
                    OrgPn="MPEG_PS_NTSC,MPEG_PS_PAL",
 | 
					                    OrgPn = "MPEG_PS_NTSC,MPEG_PS_PAL",
 | 
				
			||||||
                    Type = DlnaProfileType.Video
 | 
					                    Type = DlnaProfileType.Video
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                new ResponseProfile
 | 
					                new ResponseProfile
 | 
				
			||||||
@ -215,7 +215,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = DlnaProfileType.Photo,
 | 
					                    Type = DlnaProfileType.Photo,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -282,7 +282,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                    Type = CodecType.VideoAudio,
 | 
					                    Type = CodecType.VideoAudio,
 | 
				
			||||||
                    Codec = "mp3,mp2",
 | 
					                    Codec = "mp3,mp2",
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
 | 
				
			|||||||
@ -164,7 +164,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = DlnaProfileType.Photo,
 | 
					                    Type = DlnaProfileType.Photo,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -187,13 +187,13 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                new ResponseProfile
 | 
					                new ResponseProfile
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Container = "ts,mpegts",
 | 
					                    Container = "ts,mpegts",
 | 
				
			||||||
                    VideoCodec="h264",
 | 
					                    VideoCodec = "h264",
 | 
				
			||||||
                    AudioCodec="ac3,aac,mp3",
 | 
					                    AudioCodec = "ac3,aac,mp3",
 | 
				
			||||||
                    MimeType = "video/vnd.dlna.mpeg-tts",
 | 
					                    MimeType = "video/vnd.dlna.mpeg-tts",
 | 
				
			||||||
                    OrgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T",
 | 
					                    OrgPn = "AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T",
 | 
				
			||||||
                    Type = DlnaProfileType.Video,
 | 
					                    Type = DlnaProfileType.Video,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -213,13 +213,13 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                new ResponseProfile
 | 
					                new ResponseProfile
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Container = "ts,mpegts",
 | 
					                    Container = "ts,mpegts",
 | 
				
			||||||
                    VideoCodec="h264",
 | 
					                    VideoCodec = "h264",
 | 
				
			||||||
                    AudioCodec="ac3,aac,mp3",
 | 
					                    AudioCodec = "ac3,aac,mp3",
 | 
				
			||||||
                    MimeType = "video/mpeg",
 | 
					                    MimeType = "video/mpeg",
 | 
				
			||||||
                    OrgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO",
 | 
					                    OrgPn = "AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO",
 | 
				
			||||||
                    Type = DlnaProfileType.Video,
 | 
					                    Type = DlnaProfileType.Video,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -233,28 +233,28 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                new ResponseProfile
 | 
					                new ResponseProfile
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Container = "ts,mpegts",
 | 
					                    Container = "ts,mpegts",
 | 
				
			||||||
                    VideoCodec="h264",
 | 
					                    VideoCodec = "h264",
 | 
				
			||||||
                    AudioCodec="ac3,aac,mp3",
 | 
					                    AudioCodec = "ac3,aac,mp3",
 | 
				
			||||||
                    MimeType = "video/vnd.dlna.mpeg-tts",
 | 
					                    MimeType = "video/vnd.dlna.mpeg-tts",
 | 
				
			||||||
                    OrgPn="AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU",
 | 
					                    OrgPn = "AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU",
 | 
				
			||||||
                    Type = DlnaProfileType.Video
 | 
					                    Type = DlnaProfileType.Video
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                new ResponseProfile
 | 
					                new ResponseProfile
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Container = "ts,mpegts",
 | 
					                    Container = "ts,mpegts",
 | 
				
			||||||
                    VideoCodec="mpeg2video",
 | 
					                    VideoCodec = "mpeg2video",
 | 
				
			||||||
                    MimeType = "video/vnd.dlna.mpeg-tts",
 | 
					                    MimeType = "video/vnd.dlna.mpeg-tts",
 | 
				
			||||||
                    OrgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO",
 | 
					                    OrgPn = "MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO",
 | 
				
			||||||
                    Type = DlnaProfileType.Video
 | 
					                    Type = DlnaProfileType.Video
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                new ResponseProfile
 | 
					                new ResponseProfile
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Container = "mpeg",
 | 
					                    Container = "mpeg",
 | 
				
			||||||
                    VideoCodec="mpeg1video,mpeg2video",
 | 
					                    VideoCodec = "mpeg1video,mpeg2video",
 | 
				
			||||||
                    MimeType = "video/mpeg",
 | 
					                    MimeType = "video/mpeg",
 | 
				
			||||||
                    OrgPn="MPEG_PS_NTSC,MPEG_PS_PAL",
 | 
					                    OrgPn = "MPEG_PS_NTSC,MPEG_PS_PAL",
 | 
				
			||||||
                    Type = DlnaProfileType.Video
 | 
					                    Type = DlnaProfileType.Video
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                new ResponseProfile
 | 
					                new ResponseProfile
 | 
				
			||||||
@ -265,14 +265,13 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
            CodecProfiles = new[]
 | 
					            CodecProfiles = new[]
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                new CodecProfile
 | 
					                new CodecProfile
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = CodecType.Video,
 | 
					                    Type = CodecType.Video,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -300,7 +299,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                    Type = CodecType.VideoAudio,
 | 
					                    Type = CodecType.VideoAudio,
 | 
				
			||||||
                    Codec = "mp3,mp2",
 | 
					                    Codec = "mp3,mp2",
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
 | 
				
			|||||||
@ -164,7 +164,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = DlnaProfileType.Photo,
 | 
					                    Type = DlnaProfileType.Photo,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -187,13 +187,13 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                new ResponseProfile
 | 
					                new ResponseProfile
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Container = "ts,mpegts",
 | 
					                    Container = "ts,mpegts",
 | 
				
			||||||
                    VideoCodec="h264",
 | 
					                    VideoCodec = "h264",
 | 
				
			||||||
                    AudioCodec="ac3,aac,mp3",
 | 
					                    AudioCodec = "ac3,aac,mp3",
 | 
				
			||||||
                    MimeType = "video/vnd.dlna.mpeg-tts",
 | 
					                    MimeType = "video/vnd.dlna.mpeg-tts",
 | 
				
			||||||
                    OrgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T",
 | 
					                    OrgPn = "AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T",
 | 
				
			||||||
                    Type = DlnaProfileType.Video,
 | 
					                    Type = DlnaProfileType.Video,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -213,13 +213,13 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                new ResponseProfile
 | 
					                new ResponseProfile
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Container = "ts,mpegts",
 | 
					                    Container = "ts,mpegts",
 | 
				
			||||||
                    VideoCodec="h264",
 | 
					                    VideoCodec = "h264",
 | 
				
			||||||
                    AudioCodec="ac3,aac,mp3",
 | 
					                    AudioCodec = "ac3,aac,mp3",
 | 
				
			||||||
                    MimeType = "video/mpeg",
 | 
					                    MimeType = "video/mpeg",
 | 
				
			||||||
                    OrgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO",
 | 
					                    OrgPn = "AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO",
 | 
				
			||||||
                    Type = DlnaProfileType.Video,
 | 
					                    Type = DlnaProfileType.Video,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -233,28 +233,28 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                new ResponseProfile
 | 
					                new ResponseProfile
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Container = "ts,mpegts",
 | 
					                    Container = "ts,mpegts",
 | 
				
			||||||
                    VideoCodec="h264",
 | 
					                    VideoCodec = "h264",
 | 
				
			||||||
                    AudioCodec="ac3,aac,mp3",
 | 
					                    AudioCodec = "ac3,aac,mp3",
 | 
				
			||||||
                    MimeType = "video/vnd.dlna.mpeg-tts",
 | 
					                    MimeType = "video/vnd.dlna.mpeg-tts",
 | 
				
			||||||
                    OrgPn="AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU",
 | 
					                    OrgPn = "AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU",
 | 
				
			||||||
                    Type = DlnaProfileType.Video
 | 
					                    Type = DlnaProfileType.Video
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                new ResponseProfile
 | 
					                new ResponseProfile
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Container = "ts,mpegts",
 | 
					                    Container = "ts,mpegts",
 | 
				
			||||||
                    VideoCodec="mpeg2video",
 | 
					                    VideoCodec = "mpeg2video",
 | 
				
			||||||
                    MimeType = "video/vnd.dlna.mpeg-tts",
 | 
					                    MimeType = "video/vnd.dlna.mpeg-tts",
 | 
				
			||||||
                    OrgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO",
 | 
					                    OrgPn = "MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO",
 | 
				
			||||||
                    Type = DlnaProfileType.Video
 | 
					                    Type = DlnaProfileType.Video
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                new ResponseProfile
 | 
					                new ResponseProfile
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Container = "mpeg",
 | 
					                    Container = "mpeg",
 | 
				
			||||||
                    VideoCodec="mpeg1video,mpeg2video",
 | 
					                    VideoCodec = "mpeg1video,mpeg2video",
 | 
				
			||||||
                    MimeType = "video/mpeg",
 | 
					                    MimeType = "video/mpeg",
 | 
				
			||||||
                    OrgPn="MPEG_PS_NTSC,MPEG_PS_PAL",
 | 
					                    OrgPn = "MPEG_PS_NTSC,MPEG_PS_PAL",
 | 
				
			||||||
                    Type = DlnaProfileType.Video
 | 
					                    Type = DlnaProfileType.Video
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                new ResponseProfile
 | 
					                new ResponseProfile
 | 
				
			||||||
@ -265,14 +265,13 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
            CodecProfiles = new[]
 | 
					            CodecProfiles = new[]
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                new CodecProfile
 | 
					                new CodecProfile
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = CodecType.Video,
 | 
					                    Type = CodecType.Video,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -300,7 +299,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                    Type = CodecType.VideoAudio,
 | 
					                    Type = CodecType.VideoAudio,
 | 
				
			||||||
                    Codec = "mp3,mp2",
 | 
					                    Codec = "mp3,mp2",
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
 | 
				
			|||||||
@ -108,7 +108,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = DlnaProfileType.Photo,
 | 
					                    Type = DlnaProfileType.Photo,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -133,7 +133,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                    Type = CodecType.Video,
 | 
					                    Type = CodecType.Video,
 | 
				
			||||||
                    Codec = "h264",
 | 
					                    Codec = "h264",
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -176,7 +176,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                    Type = CodecType.VideoAudio,
 | 
					                    Type = CodecType.VideoAudio,
 | 
				
			||||||
                    Codec = "ac3",
 | 
					                    Codec = "ac3",
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -201,7 +201,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                    Type = CodecType.VideoAudio,
 | 
					                    Type = CodecType.VideoAudio,
 | 
				
			||||||
                    Codec = "wmapro",
 | 
					                    Codec = "wmapro",
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -217,7 +217,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                    Type = CodecType.VideoAudio,
 | 
					                    Type = CodecType.VideoAudio,
 | 
				
			||||||
                    Codec = "aac",
 | 
					                    Codec = "aac",
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -235,7 +235,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                new ResponseProfile
 | 
					                new ResponseProfile
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Container = "mp4,mov",
 | 
					                    Container = "mp4,mov",
 | 
				
			||||||
                    AudioCodec="aac",
 | 
					                    AudioCodec = "aac",
 | 
				
			||||||
                    MimeType = "video/mp4",
 | 
					                    MimeType = "video/mp4",
 | 
				
			||||||
                    Type = DlnaProfileType.Video
 | 
					                    Type = DlnaProfileType.Video
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
@ -244,7 +244,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    Container = "avi",
 | 
					                    Container = "avi",
 | 
				
			||||||
                    MimeType = "video/divx",
 | 
					                    MimeType = "video/divx",
 | 
				
			||||||
                    OrgPn="AVI",
 | 
					                    OrgPn = "AVI",
 | 
				
			||||||
                    Type = DlnaProfileType.Video
 | 
					                    Type = DlnaProfileType.Video
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -110,7 +110,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = DlnaProfileType.Photo,
 | 
					                    Type = DlnaProfileType.Photo,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -135,7 +135,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                    Type = CodecType.Video,
 | 
					                    Type = CodecType.Video,
 | 
				
			||||||
                    Codec = "h264",
 | 
					                    Codec = "h264",
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -178,7 +178,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                    Type = CodecType.VideoAudio,
 | 
					                    Type = CodecType.VideoAudio,
 | 
				
			||||||
                    Codec = "ac3",
 | 
					                    Codec = "ac3",
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -203,7 +203,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                    Type = CodecType.VideoAudio,
 | 
					                    Type = CodecType.VideoAudio,
 | 
				
			||||||
                    Codec = "wmapro",
 | 
					                    Codec = "wmapro",
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -219,7 +219,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                    Type = CodecType.VideoAudio,
 | 
					                    Type = CodecType.VideoAudio,
 | 
				
			||||||
                    Codec = "aac",
 | 
					                    Codec = "aac",
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -237,7 +237,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                new ResponseProfile
 | 
					                new ResponseProfile
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Container = "mp4,mov",
 | 
					                    Container = "mp4,mov",
 | 
				
			||||||
                    AudioCodec="aac",
 | 
					                    AudioCodec = "aac",
 | 
				
			||||||
                    MimeType = "video/mp4",
 | 
					                    MimeType = "video/mp4",
 | 
				
			||||||
                    Type = DlnaProfileType.Video
 | 
					                    Type = DlnaProfileType.Video
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
@ -246,7 +246,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    Container = "avi",
 | 
					                    Container = "avi",
 | 
				
			||||||
                    MimeType = "video/divx",
 | 
					                    MimeType = "video/divx",
 | 
				
			||||||
                    OrgPn="AVI",
 | 
					                    OrgPn = "AVI",
 | 
				
			||||||
                    Type = DlnaProfileType.Video
 | 
					                    Type = DlnaProfileType.Video
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -20,7 +20,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                Headers = new[]
 | 
					                Headers = new[]
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    new HttpHeaderInfo {Name = "User-Agent", Value = "alphanetworks", Match = HeaderMatchType.Substring},
 | 
					                    new HttpHeaderInfo { Name = "User-Agent", Value = "alphanetworks", Match = HeaderMatchType.Substring },
 | 
				
			||||||
                    new HttpHeaderInfo
 | 
					                    new HttpHeaderInfo
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        Name = "User-Agent",
 | 
					                        Name = "User-Agent",
 | 
				
			||||||
@ -168,7 +168,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = DlnaProfileType.Photo,
 | 
					                    Type = DlnaProfileType.Photo,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -193,7 +193,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                    Type = CodecType.Video,
 | 
					                    Type = CodecType.Video,
 | 
				
			||||||
                    Codec = "h264",
 | 
					                    Codec = "h264",
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -221,7 +221,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                    Type = CodecType.VideoAudio,
 | 
					                    Type = CodecType.VideoAudio,
 | 
				
			||||||
                    Codec = "aac",
 | 
					                    Codec = "aac",
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
 | 
				
			|||||||
@ -119,7 +119,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                    Type = DlnaProfileType.Video,
 | 
					                    Type = DlnaProfileType.Video,
 | 
				
			||||||
                    Container = "mp4,mov",
 | 
					                    Container = "mp4,mov",
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -138,7 +138,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = CodecType.Video,
 | 
					                    Type = CodecType.Video,
 | 
				
			||||||
                    Codec = "mpeg4",
 | 
					                    Codec = "mpeg4",
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -187,7 +187,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = CodecType.Video,
 | 
					                    Type = CodecType.Video,
 | 
				
			||||||
                    Codec = "h264",
 | 
					                    Codec = "h264",
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -236,7 +236,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = CodecType.Video,
 | 
					                    Type = CodecType.Video,
 | 
				
			||||||
                    Codec = "wmv2,wmv3,vc1",
 | 
					                    Codec = "wmv2,wmv3,vc1",
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -284,7 +284,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                new CodecProfile
 | 
					                new CodecProfile
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = CodecType.Video,
 | 
					                    Type = CodecType.Video,
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -307,7 +307,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = CodecType.VideoAudio,
 | 
					                    Type = CodecType.VideoAudio,
 | 
				
			||||||
                    Codec = "ac3,wmav2,wmapro",
 | 
					                    Codec = "ac3,wmav2,wmapro",
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -323,7 +323,7 @@ namespace Emby.Dlna.Profiles
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    Type = CodecType.VideoAudio,
 | 
					                    Type = CodecType.VideoAudio,
 | 
				
			||||||
                    Codec = "aac",
 | 
					                    Codec = "aac",
 | 
				
			||||||
                    Conditions = new []
 | 
					                    Conditions = new[]
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        new ProfileCondition
 | 
					                        new ProfileCondition
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
 | 
				
			|||||||
@ -15,11 +15,7 @@ namespace Emby.Dlna.Service
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    public abstract class BaseControlHandler
 | 
					    public abstract class BaseControlHandler
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        private const string NS_SOAPENV = "http://schemas.xmlsoap.org/soap/envelope/";
 | 
					        private const string NsSoapEnv = "http://schemas.xmlsoap.org/soap/envelope/";
 | 
				
			||||||
 | 
					 | 
				
			||||||
        protected IServerConfigurationManager Config { get; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        protected ILogger Logger { get; }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        protected BaseControlHandler(IServerConfigurationManager config, ILogger logger)
 | 
					        protected BaseControlHandler(IServerConfigurationManager config, ILogger logger)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@ -27,6 +23,10 @@ namespace Emby.Dlna.Service
 | 
				
			|||||||
            Logger = logger;
 | 
					            Logger = logger;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        protected IServerConfigurationManager Config { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        protected ILogger Logger { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public async Task<ControlResponse> ProcessControlRequestAsync(ControlRequest request)
 | 
					        public async Task<ControlResponse> ProcessControlRequestAsync(ControlRequest request)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            try
 | 
					            try
 | 
				
			||||||
@ -80,10 +80,10 @@ namespace Emby.Dlna.Service
 | 
				
			|||||||
            {
 | 
					            {
 | 
				
			||||||
                writer.WriteStartDocument(true);
 | 
					                writer.WriteStartDocument(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                writer.WriteStartElement("SOAP-ENV", "Envelope", NS_SOAPENV);
 | 
					                writer.WriteStartElement("SOAP-ENV", "Envelope", NsSoapEnv);
 | 
				
			||||||
                writer.WriteAttributeString(string.Empty, "encodingStyle", NS_SOAPENV, "http://schemas.xmlsoap.org/soap/encoding/");
 | 
					                writer.WriteAttributeString(string.Empty, "encodingStyle", NsSoapEnv, "http://schemas.xmlsoap.org/soap/encoding/");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                writer.WriteStartElement("SOAP-ENV", "Body", NS_SOAPENV);
 | 
					                writer.WriteStartElement("SOAP-ENV", "Body", NsSoapEnv);
 | 
				
			||||||
                writer.WriteStartElement("u", requestInfo.LocalName + "Response", requestInfo.NamespaceURI);
 | 
					                writer.WriteStartElement("u", requestInfo.LocalName + "Response", requestInfo.NamespaceURI);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                WriteResult(requestInfo.LocalName, requestInfo.Headers, writer);
 | 
					                WriteResult(requestInfo.LocalName, requestInfo.Headers, writer);
 | 
				
			||||||
@ -210,15 +210,6 @@ namespace Emby.Dlna.Service
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private class ControlRequestInfo
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            public string LocalName { get; set; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            public string NamespaceURI { get; set; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            public Dictionary<string, string> Headers { get; } = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        protected abstract void WriteResult(string methodName, IDictionary<string, string> methodParams, XmlWriter xmlWriter);
 | 
					        protected abstract void WriteResult(string methodName, IDictionary<string, string> methodParams, XmlWriter xmlWriter);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void LogRequest(ControlRequest request)
 | 
					        private void LogRequest(ControlRequest request)
 | 
				
			||||||
@ -240,5 +231,14 @@ namespace Emby.Dlna.Service
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            Logger.LogDebug("Control response. Headers: {@Headers}\n{Xml}", response.Headers, response.Xml);
 | 
					            Logger.LogDebug("Control response. Headers: {@Headers}\n{Xml}", response.Headers, response.Xml);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private class ControlRequestInfo
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            public string LocalName { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            public string NamespaceURI { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            public Dictionary<string, string> Headers { get; } = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -6,20 +6,22 @@ using Microsoft.Extensions.Logging;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace Emby.Dlna.Service
 | 
					namespace Emby.Dlna.Service
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public class BaseService : IEventManager
 | 
					    public class BaseService : IDlnaEventManager
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        protected IEventManager EventManager;
 | 
					 | 
				
			||||||
        protected IHttpClient HttpClient;
 | 
					 | 
				
			||||||
        protected ILogger Logger;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        protected BaseService(ILogger<BaseService> logger, IHttpClient httpClient)
 | 
					        protected BaseService(ILogger<BaseService> logger, IHttpClient httpClient)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            Logger = logger;
 | 
					            Logger = logger;
 | 
				
			||||||
            HttpClient = httpClient;
 | 
					            HttpClient = httpClient;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            EventManager = new EventManager(logger, HttpClient);
 | 
					            EventManager = new DlnaEventManager(logger, HttpClient);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        protected IDlnaEventManager EventManager { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        protected IHttpClient HttpClient { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        protected ILogger Logger { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public EventSubscriptionResponse CancelEventSubscription(string subscriptionId)
 | 
					        public EventSubscriptionResponse CancelEventSubscription(string subscriptionId)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return EventManager.CancelEventSubscription(subscriptionId);
 | 
					            return EventManager.CancelEventSubscription(subscriptionId);
 | 
				
			||||||
 | 
				
			|||||||
@ -10,7 +10,7 @@ namespace Emby.Dlna.Service
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    public static class ControlErrorHandler
 | 
					    public static class ControlErrorHandler
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        private const string NS_SOAPENV = "http://schemas.xmlsoap.org/soap/envelope/";
 | 
					        private const string NsSoapEnv = "http://schemas.xmlsoap.org/soap/envelope/";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public static ControlResponse GetResponse(Exception ex)
 | 
					        public static ControlResponse GetResponse(Exception ex)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@ -26,11 +26,11 @@ namespace Emby.Dlna.Service
 | 
				
			|||||||
            {
 | 
					            {
 | 
				
			||||||
                writer.WriteStartDocument(true);
 | 
					                writer.WriteStartDocument(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                writer.WriteStartElement("SOAP-ENV", "Envelope", NS_SOAPENV);
 | 
					                writer.WriteStartElement("SOAP-ENV", "Envelope", NsSoapEnv);
 | 
				
			||||||
                writer.WriteAttributeString(string.Empty, "encodingStyle", NS_SOAPENV, "http://schemas.xmlsoap.org/soap/encoding/");
 | 
					                writer.WriteAttributeString(string.Empty, "encodingStyle", NsSoapEnv, "http://schemas.xmlsoap.org/soap/encoding/");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                writer.WriteStartElement("SOAP-ENV", "Body", NS_SOAPENV);
 | 
					                writer.WriteStartElement("SOAP-ENV", "Body", NsSoapEnv);
 | 
				
			||||||
                writer.WriteStartElement("SOAP-ENV", "Fault", NS_SOAPENV);
 | 
					                writer.WriteStartElement("SOAP-ENV", "Fault", NsSoapEnv);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                writer.WriteElementString("faultcode", "500");
 | 
					                writer.WriteElementString("faultcode", "500");
 | 
				
			||||||
                writer.WriteElementString("faultstring", ex.Message);
 | 
					                writer.WriteElementString("faultstring", ex.Message);
 | 
				
			||||||
 | 
				
			|||||||
@ -87,7 +87,7 @@ namespace Emby.Dlna.Service
 | 
				
			|||||||
                    .Append(SecurityElement.Escape(item.DataType ?? string.Empty))
 | 
					                    .Append(SecurityElement.Escape(item.DataType ?? string.Empty))
 | 
				
			||||||
                    .Append("</dataType>");
 | 
					                    .Append("</dataType>");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (item.AllowedValues.Length > 0)
 | 
					                if (item.AllowedValues.Count > 0)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    builder.Append("<allowedValueList>");
 | 
					                    builder.Append("<allowedValueList>");
 | 
				
			||||||
                    foreach (var allowedValue in item.AllowedValues)
 | 
					                    foreach (var allowedValue in item.AllowedValues)
 | 
				
			||||||
 | 
				
			|||||||
@ -3,9 +3,9 @@
 | 
				
			|||||||
using System;
 | 
					using System;
 | 
				
			||||||
using System.Collections.Generic;
 | 
					using System.Collections.Generic;
 | 
				
			||||||
using System.Linq;
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using Jellyfin.Data.Events;
 | 
				
			||||||
using MediaBrowser.Controller.Configuration;
 | 
					using MediaBrowser.Controller.Configuration;
 | 
				
			||||||
using MediaBrowser.Model.Dlna;
 | 
					using MediaBrowser.Model.Dlna;
 | 
				
			||||||
using MediaBrowser.Model.Events;
 | 
					 | 
				
			||||||
using Rssdp;
 | 
					using Rssdp;
 | 
				
			||||||
using Rssdp.Infrastructure;
 | 
					using Rssdp.Infrastructure;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -17,9 +17,17 @@ namespace Emby.Dlna.Ssdp
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        private readonly IServerConfigurationManager _config;
 | 
					        private readonly IServerConfigurationManager _config;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private SsdpDeviceLocator _deviceLocator;
 | 
				
			||||||
 | 
					        private ISsdpCommunicationsServer _commsServer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private int _listenerCount;
 | 
					        private int _listenerCount;
 | 
				
			||||||
        private bool _disposed;
 | 
					        private bool _disposed;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public DeviceDiscovery(IServerConfigurationManager config)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _config = config;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private event EventHandler<GenericEventArgs<UpnpDeviceInfo>> DeviceDiscoveredInternal;
 | 
					        private event EventHandler<GenericEventArgs<UpnpDeviceInfo>> DeviceDiscoveredInternal;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <inheritdoc />
 | 
					        /// <inheritdoc />
 | 
				
			||||||
@ -49,15 +57,6 @@ namespace Emby.Dlna.Ssdp
 | 
				
			|||||||
        /// <inheritdoc />
 | 
					        /// <inheritdoc />
 | 
				
			||||||
        public event EventHandler<GenericEventArgs<UpnpDeviceInfo>> DeviceLeft;
 | 
					        public event EventHandler<GenericEventArgs<UpnpDeviceInfo>> DeviceLeft;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private SsdpDeviceLocator _deviceLocator;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private ISsdpCommunicationsServer _commsServer;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public DeviceDiscovery(IServerConfigurationManager config)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _config = config;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Call this method from somewhere in your code to start the search.
 | 
					        // Call this method from somewhere in your code to start the search.
 | 
				
			||||||
        public void Start(ISsdpCommunicationsServer communicationsServer)
 | 
					        public void Start(ISsdpCommunicationsServer communicationsServer)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
				
			|||||||
@ -5,7 +5,7 @@ using System.Xml.Linq;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace Emby.Dlna.Ssdp
 | 
					namespace Emby.Dlna.Ssdp
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public static class Extensions
 | 
					    public static class SsdpExtensions
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        public static string GetValue(this XElement container, XName name)
 | 
					        public static string GetValue(this XElement container, XName name)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@ -23,8 +23,9 @@
 | 
				
			|||||||
  <PropertyGroup>
 | 
					  <PropertyGroup>
 | 
				
			||||||
    <Authors>Jellyfin Contributors</Authors>
 | 
					    <Authors>Jellyfin Contributors</Authors>
 | 
				
			||||||
    <PackageId>Jellyfin.Naming</PackageId>
 | 
					    <PackageId>Jellyfin.Naming</PackageId>
 | 
				
			||||||
    <PackageLicenseUrl>https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt</PackageLicenseUrl>
 | 
					    <VersionPrefix>10.7.0</VersionPrefix>
 | 
				
			||||||
    <RepositoryUrl>https://github.com/jellyfin/jellyfin</RepositoryUrl>
 | 
					    <RepositoryUrl>https://github.com/jellyfin/jellyfin</RepositoryUrl>
 | 
				
			||||||
 | 
					    <PackageLicenseExpression>GPL-3.0-only</PackageLicenseExpression>
 | 
				
			||||||
  </PropertyGroup>
 | 
					  </PropertyGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <!-- Code Analyzers-->
 | 
					  <!-- Code Analyzers-->
 | 
				
			||||||
 | 
				
			|||||||
@ -4,6 +4,7 @@ using System.Globalization;
 | 
				
			|||||||
using System.Linq;
 | 
					using System.Linq;
 | 
				
			||||||
using System.Threading;
 | 
					using System.Threading;
 | 
				
			||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using Jellyfin.Data.Events;
 | 
				
			||||||
using MediaBrowser.Common.Configuration;
 | 
					using MediaBrowser.Common.Configuration;
 | 
				
			||||||
using MediaBrowser.Controller;
 | 
					using MediaBrowser.Controller;
 | 
				
			||||||
using MediaBrowser.Controller.Entities;
 | 
					using MediaBrowser.Controller.Entities;
 | 
				
			||||||
@ -13,7 +14,6 @@ using MediaBrowser.Controller.Library;
 | 
				
			|||||||
using MediaBrowser.Controller.Notifications;
 | 
					using MediaBrowser.Controller.Notifications;
 | 
				
			||||||
using MediaBrowser.Controller.Plugins;
 | 
					using MediaBrowser.Controller.Plugins;
 | 
				
			||||||
using MediaBrowser.Model.Activity;
 | 
					using MediaBrowser.Model.Activity;
 | 
				
			||||||
using MediaBrowser.Model.Events;
 | 
					 | 
				
			||||||
using MediaBrowser.Model.Globalization;
 | 
					using MediaBrowser.Model.Globalization;
 | 
				
			||||||
using MediaBrowser.Model.Notifications;
 | 
					using MediaBrowser.Model.Notifications;
 | 
				
			||||||
using Microsoft.Extensions.Logging;
 | 
					using Microsoft.Extensions.Logging;
 | 
				
			||||||
 | 
				
			|||||||
@ -1,590 +0,0 @@
 | 
				
			|||||||
using System;
 | 
					 | 
				
			||||||
using System.Collections.Generic;
 | 
					 | 
				
			||||||
using System.Globalization;
 | 
					 | 
				
			||||||
using System.Linq;
 | 
					 | 
				
			||||||
using System.Text;
 | 
					 | 
				
			||||||
using System.Threading.Tasks;
 | 
					 | 
				
			||||||
using Jellyfin.Data.Entities;
 | 
					 | 
				
			||||||
using MediaBrowser.Common.Plugins;
 | 
					 | 
				
			||||||
using MediaBrowser.Common.Updates;
 | 
					 | 
				
			||||||
using MediaBrowser.Controller.Authentication;
 | 
					 | 
				
			||||||
using MediaBrowser.Controller.Library;
 | 
					 | 
				
			||||||
using MediaBrowser.Controller.Plugins;
 | 
					 | 
				
			||||||
using MediaBrowser.Controller.Session;
 | 
					 | 
				
			||||||
using MediaBrowser.Controller.Subtitles;
 | 
					 | 
				
			||||||
using MediaBrowser.Model.Activity;
 | 
					 | 
				
			||||||
using MediaBrowser.Model.Dto;
 | 
					 | 
				
			||||||
using MediaBrowser.Model.Entities;
 | 
					 | 
				
			||||||
using MediaBrowser.Model.Events;
 | 
					 | 
				
			||||||
using MediaBrowser.Model.Globalization;
 | 
					 | 
				
			||||||
using MediaBrowser.Model.Notifications;
 | 
					 | 
				
			||||||
using MediaBrowser.Model.Tasks;
 | 
					 | 
				
			||||||
using MediaBrowser.Model.Updates;
 | 
					 | 
				
			||||||
using Microsoft.Extensions.Logging;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace Emby.Server.Implementations.Activity
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    /// <summary>
 | 
					 | 
				
			||||||
    /// Entry point for the activity logger.
 | 
					 | 
				
			||||||
    /// </summary>
 | 
					 | 
				
			||||||
    public sealed class ActivityLogEntryPoint : IServerEntryPoint
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        private readonly ILogger<ActivityLogEntryPoint> _logger;
 | 
					 | 
				
			||||||
        private readonly IInstallationManager _installationManager;
 | 
					 | 
				
			||||||
        private readonly ISessionManager _sessionManager;
 | 
					 | 
				
			||||||
        private readonly ITaskManager _taskManager;
 | 
					 | 
				
			||||||
        private readonly IActivityManager _activityManager;
 | 
					 | 
				
			||||||
        private readonly ILocalizationManager _localization;
 | 
					 | 
				
			||||||
        private readonly ISubtitleManager _subManager;
 | 
					 | 
				
			||||||
        private readonly IUserManager _userManager;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Initializes a new instance of the <see cref="ActivityLogEntryPoint"/> class.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="logger">The logger.</param>
 | 
					 | 
				
			||||||
        /// <param name="sessionManager">The session manager.</param>
 | 
					 | 
				
			||||||
        /// <param name="taskManager">The task manager.</param>
 | 
					 | 
				
			||||||
        /// <param name="activityManager">The activity manager.</param>
 | 
					 | 
				
			||||||
        /// <param name="localization">The localization manager.</param>
 | 
					 | 
				
			||||||
        /// <param name="installationManager">The installation manager.</param>
 | 
					 | 
				
			||||||
        /// <param name="subManager">The subtitle manager.</param>
 | 
					 | 
				
			||||||
        /// <param name="userManager">The user manager.</param>
 | 
					 | 
				
			||||||
        public ActivityLogEntryPoint(
 | 
					 | 
				
			||||||
            ILogger<ActivityLogEntryPoint> logger,
 | 
					 | 
				
			||||||
            ISessionManager sessionManager,
 | 
					 | 
				
			||||||
            ITaskManager taskManager,
 | 
					 | 
				
			||||||
            IActivityManager activityManager,
 | 
					 | 
				
			||||||
            ILocalizationManager localization,
 | 
					 | 
				
			||||||
            IInstallationManager installationManager,
 | 
					 | 
				
			||||||
            ISubtitleManager subManager,
 | 
					 | 
				
			||||||
            IUserManager userManager)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _logger = logger;
 | 
					 | 
				
			||||||
            _sessionManager = sessionManager;
 | 
					 | 
				
			||||||
            _taskManager = taskManager;
 | 
					 | 
				
			||||||
            _activityManager = activityManager;
 | 
					 | 
				
			||||||
            _localization = localization;
 | 
					 | 
				
			||||||
            _installationManager = installationManager;
 | 
					 | 
				
			||||||
            _subManager = subManager;
 | 
					 | 
				
			||||||
            _userManager = userManager;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <inheritdoc />
 | 
					 | 
				
			||||||
        public Task RunAsync()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _taskManager.TaskCompleted += OnTaskCompleted;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            _installationManager.PluginInstalled += OnPluginInstalled;
 | 
					 | 
				
			||||||
            _installationManager.PluginUninstalled += OnPluginUninstalled;
 | 
					 | 
				
			||||||
            _installationManager.PluginUpdated += OnPluginUpdated;
 | 
					 | 
				
			||||||
            _installationManager.PackageInstallationFailed += OnPackageInstallationFailed;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            _sessionManager.SessionStarted += OnSessionStarted;
 | 
					 | 
				
			||||||
            _sessionManager.AuthenticationFailed += OnAuthenticationFailed;
 | 
					 | 
				
			||||||
            _sessionManager.AuthenticationSucceeded += OnAuthenticationSucceeded;
 | 
					 | 
				
			||||||
            _sessionManager.SessionEnded += OnSessionEnded;
 | 
					 | 
				
			||||||
            _sessionManager.PlaybackStart += OnPlaybackStart;
 | 
					 | 
				
			||||||
            _sessionManager.PlaybackStopped += OnPlaybackStopped;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            _subManager.SubtitleDownloadFailure += OnSubtitleDownloadFailure;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            _userManager.OnUserCreated += OnUserCreated;
 | 
					 | 
				
			||||||
            _userManager.OnUserPasswordChanged += OnUserPasswordChanged;
 | 
					 | 
				
			||||||
            _userManager.OnUserDeleted += OnUserDeleted;
 | 
					 | 
				
			||||||
            _userManager.OnUserLockedOut += OnUserLockedOut;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return Task.CompletedTask;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private async void OnUserLockedOut(object sender, GenericEventArgs<User> e)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            await CreateLogEntry(new ActivityLog(
 | 
					 | 
				
			||||||
                    string.Format(
 | 
					 | 
				
			||||||
                        CultureInfo.InvariantCulture,
 | 
					 | 
				
			||||||
                        _localization.GetLocalizedString("UserLockedOutWithName"),
 | 
					 | 
				
			||||||
                        e.Argument.Username),
 | 
					 | 
				
			||||||
                    NotificationType.UserLockedOut.ToString(),
 | 
					 | 
				
			||||||
                    e.Argument.Id)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                LogSeverity = LogLevel.Error
 | 
					 | 
				
			||||||
            }).ConfigureAwait(false);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private async void OnSubtitleDownloadFailure(object sender, SubtitleDownloadFailureEventArgs e)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            await CreateLogEntry(new ActivityLog(
 | 
					 | 
				
			||||||
                string.Format(
 | 
					 | 
				
			||||||
                    CultureInfo.InvariantCulture,
 | 
					 | 
				
			||||||
                    _localization.GetLocalizedString("SubtitleDownloadFailureFromForItem"),
 | 
					 | 
				
			||||||
                    e.Provider,
 | 
					 | 
				
			||||||
                    Notifications.NotificationEntryPoint.GetItemName(e.Item)),
 | 
					 | 
				
			||||||
                "SubtitleDownloadFailure",
 | 
					 | 
				
			||||||
                Guid.Empty)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                ItemId = e.Item.Id.ToString("N", CultureInfo.InvariantCulture),
 | 
					 | 
				
			||||||
                ShortOverview = e.Exception.Message
 | 
					 | 
				
			||||||
            }).ConfigureAwait(false);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private async void OnPlaybackStopped(object sender, PlaybackStopEventArgs e)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var item = e.MediaInfo;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (item == null)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                _logger.LogWarning("PlaybackStopped reported with null media info.");
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (e.Item != null && e.Item.IsThemeMedia)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                // Don't report theme song or local trailer playback
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (e.Users.Count == 0)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var user = e.Users[0];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            await CreateLogEntry(new ActivityLog(
 | 
					 | 
				
			||||||
                string.Format(
 | 
					 | 
				
			||||||
                    CultureInfo.InvariantCulture,
 | 
					 | 
				
			||||||
                    _localization.GetLocalizedString("UserStoppedPlayingItemWithValues"),
 | 
					 | 
				
			||||||
                    user.Username,
 | 
					 | 
				
			||||||
                    GetItemName(item),
 | 
					 | 
				
			||||||
                    e.DeviceName),
 | 
					 | 
				
			||||||
                GetPlaybackStoppedNotificationType(item.MediaType),
 | 
					 | 
				
			||||||
                user.Id))
 | 
					 | 
				
			||||||
                .ConfigureAwait(false);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private async void OnPlaybackStart(object sender, PlaybackProgressEventArgs e)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var item = e.MediaInfo;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (item == null)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                _logger.LogWarning("PlaybackStart reported with null media info.");
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (e.Item != null && e.Item.IsThemeMedia)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                // Don't report theme song or local trailer playback
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (e.Users.Count == 0)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var user = e.Users.First();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            await CreateLogEntry(new ActivityLog(
 | 
					 | 
				
			||||||
                string.Format(
 | 
					 | 
				
			||||||
                    CultureInfo.InvariantCulture,
 | 
					 | 
				
			||||||
                    _localization.GetLocalizedString("UserStartedPlayingItemWithValues"),
 | 
					 | 
				
			||||||
                    user.Username,
 | 
					 | 
				
			||||||
                    GetItemName(item),
 | 
					 | 
				
			||||||
                    e.DeviceName),
 | 
					 | 
				
			||||||
                GetPlaybackNotificationType(item.MediaType),
 | 
					 | 
				
			||||||
                user.Id))
 | 
					 | 
				
			||||||
                .ConfigureAwait(false);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static string GetItemName(BaseItemDto item)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var name = item.Name;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (!string.IsNullOrEmpty(item.SeriesName))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                name = item.SeriesName + " - " + name;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (item.Artists != null && item.Artists.Count > 0)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                name = item.Artists[0] + " - " + name;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return name;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static string GetPlaybackNotificationType(string mediaType)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (string.Equals(mediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                return NotificationType.AudioPlayback.ToString();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (string.Equals(mediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                return NotificationType.VideoPlayback.ToString();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return null;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static string GetPlaybackStoppedNotificationType(string mediaType)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (string.Equals(mediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                return NotificationType.AudioPlaybackStopped.ToString();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (string.Equals(mediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                return NotificationType.VideoPlaybackStopped.ToString();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return null;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private async void OnSessionEnded(object sender, SessionEventArgs e)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var session = e.SessionInfo;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (string.IsNullOrEmpty(session.UserName))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            await CreateLogEntry(new ActivityLog(
 | 
					 | 
				
			||||||
                string.Format(
 | 
					 | 
				
			||||||
                    CultureInfo.InvariantCulture,
 | 
					 | 
				
			||||||
                    _localization.GetLocalizedString("UserOfflineFromDevice"),
 | 
					 | 
				
			||||||
                    session.UserName,
 | 
					 | 
				
			||||||
                    session.DeviceName),
 | 
					 | 
				
			||||||
                "SessionEnded",
 | 
					 | 
				
			||||||
                session.UserId)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                ShortOverview = string.Format(
 | 
					 | 
				
			||||||
                    CultureInfo.InvariantCulture,
 | 
					 | 
				
			||||||
                    _localization.GetLocalizedString("LabelIpAddressValue"),
 | 
					 | 
				
			||||||
                    session.RemoteEndPoint),
 | 
					 | 
				
			||||||
            }).ConfigureAwait(false);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private async void OnAuthenticationSucceeded(object sender, GenericEventArgs<AuthenticationResult> e)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var user = e.Argument.User;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            await CreateLogEntry(new ActivityLog(
 | 
					 | 
				
			||||||
                string.Format(
 | 
					 | 
				
			||||||
                    CultureInfo.InvariantCulture,
 | 
					 | 
				
			||||||
                    _localization.GetLocalizedString("AuthenticationSucceededWithUserName"),
 | 
					 | 
				
			||||||
                    user.Name),
 | 
					 | 
				
			||||||
                "AuthenticationSucceeded",
 | 
					 | 
				
			||||||
                user.Id)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                ShortOverview = string.Format(
 | 
					 | 
				
			||||||
                    CultureInfo.InvariantCulture,
 | 
					 | 
				
			||||||
                    _localization.GetLocalizedString("LabelIpAddressValue"),
 | 
					 | 
				
			||||||
                    e.Argument.SessionInfo.RemoteEndPoint),
 | 
					 | 
				
			||||||
            }).ConfigureAwait(false);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private async void OnAuthenticationFailed(object sender, GenericEventArgs<AuthenticationRequest> e)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            await CreateLogEntry(new ActivityLog(
 | 
					 | 
				
			||||||
                string.Format(
 | 
					 | 
				
			||||||
                    CultureInfo.InvariantCulture,
 | 
					 | 
				
			||||||
                    _localization.GetLocalizedString("FailedLoginAttemptWithUserName"),
 | 
					 | 
				
			||||||
                    e.Argument.Username),
 | 
					 | 
				
			||||||
                "AuthenticationFailed",
 | 
					 | 
				
			||||||
                Guid.Empty)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                LogSeverity = LogLevel.Error,
 | 
					 | 
				
			||||||
                ShortOverview = string.Format(
 | 
					 | 
				
			||||||
                    CultureInfo.InvariantCulture,
 | 
					 | 
				
			||||||
                    _localization.GetLocalizedString("LabelIpAddressValue"),
 | 
					 | 
				
			||||||
                    e.Argument.RemoteEndPoint),
 | 
					 | 
				
			||||||
            }).ConfigureAwait(false);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private async void OnUserDeleted(object sender, GenericEventArgs<User> e)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            await CreateLogEntry(new ActivityLog(
 | 
					 | 
				
			||||||
                string.Format(
 | 
					 | 
				
			||||||
                    CultureInfo.InvariantCulture,
 | 
					 | 
				
			||||||
                    _localization.GetLocalizedString("UserDeletedWithName"),
 | 
					 | 
				
			||||||
                    e.Argument.Username),
 | 
					 | 
				
			||||||
                "UserDeleted",
 | 
					 | 
				
			||||||
                Guid.Empty))
 | 
					 | 
				
			||||||
                .ConfigureAwait(false);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private async void OnUserPasswordChanged(object sender, GenericEventArgs<User> e)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            await CreateLogEntry(new ActivityLog(
 | 
					 | 
				
			||||||
                string.Format(
 | 
					 | 
				
			||||||
                    CultureInfo.InvariantCulture,
 | 
					 | 
				
			||||||
                    _localization.GetLocalizedString("UserPasswordChangedWithName"),
 | 
					 | 
				
			||||||
                    e.Argument.Username),
 | 
					 | 
				
			||||||
                "UserPasswordChanged",
 | 
					 | 
				
			||||||
                e.Argument.Id))
 | 
					 | 
				
			||||||
                .ConfigureAwait(false);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private async void OnUserCreated(object sender, GenericEventArgs<User> e)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            await CreateLogEntry(new ActivityLog(
 | 
					 | 
				
			||||||
                string.Format(
 | 
					 | 
				
			||||||
                    CultureInfo.InvariantCulture,
 | 
					 | 
				
			||||||
                    _localization.GetLocalizedString("UserCreatedWithName"),
 | 
					 | 
				
			||||||
                    e.Argument.Username),
 | 
					 | 
				
			||||||
                "UserCreated",
 | 
					 | 
				
			||||||
                e.Argument.Id))
 | 
					 | 
				
			||||||
                .ConfigureAwait(false);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private async void OnSessionStarted(object sender, SessionEventArgs e)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var session = e.SessionInfo;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (string.IsNullOrEmpty(session.UserName))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            await CreateLogEntry(new ActivityLog(
 | 
					 | 
				
			||||||
                string.Format(
 | 
					 | 
				
			||||||
                    CultureInfo.InvariantCulture,
 | 
					 | 
				
			||||||
                    _localization.GetLocalizedString("UserOnlineFromDevice"),
 | 
					 | 
				
			||||||
                    session.UserName,
 | 
					 | 
				
			||||||
                    session.DeviceName),
 | 
					 | 
				
			||||||
                "SessionStarted",
 | 
					 | 
				
			||||||
                session.UserId)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                ShortOverview = string.Format(
 | 
					 | 
				
			||||||
                    CultureInfo.InvariantCulture,
 | 
					 | 
				
			||||||
                    _localization.GetLocalizedString("LabelIpAddressValue"),
 | 
					 | 
				
			||||||
                    session.RemoteEndPoint)
 | 
					 | 
				
			||||||
            }).ConfigureAwait(false);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private async void OnPluginUpdated(object sender, InstallationInfo e)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            await CreateLogEntry(new ActivityLog(
 | 
					 | 
				
			||||||
                string.Format(
 | 
					 | 
				
			||||||
                    CultureInfo.InvariantCulture,
 | 
					 | 
				
			||||||
                    _localization.GetLocalizedString("PluginUpdatedWithName"),
 | 
					 | 
				
			||||||
                    e.Name),
 | 
					 | 
				
			||||||
                NotificationType.PluginUpdateInstalled.ToString(),
 | 
					 | 
				
			||||||
                Guid.Empty)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                ShortOverview = string.Format(
 | 
					 | 
				
			||||||
                    CultureInfo.InvariantCulture,
 | 
					 | 
				
			||||||
                    _localization.GetLocalizedString("VersionNumber"),
 | 
					 | 
				
			||||||
                    e.Version),
 | 
					 | 
				
			||||||
                Overview = e.Changelog
 | 
					 | 
				
			||||||
            }).ConfigureAwait(false);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private async void OnPluginUninstalled(object sender, IPlugin e)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            await CreateLogEntry(new ActivityLog(
 | 
					 | 
				
			||||||
                string.Format(
 | 
					 | 
				
			||||||
                    CultureInfo.InvariantCulture,
 | 
					 | 
				
			||||||
                    _localization.GetLocalizedString("PluginUninstalledWithName"),
 | 
					 | 
				
			||||||
                    e.Name),
 | 
					 | 
				
			||||||
                NotificationType.PluginUninstalled.ToString(),
 | 
					 | 
				
			||||||
                Guid.Empty))
 | 
					 | 
				
			||||||
                .ConfigureAwait(false);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private async void OnPluginInstalled(object sender, InstallationInfo e)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            await CreateLogEntry(new ActivityLog(
 | 
					 | 
				
			||||||
                string.Format(
 | 
					 | 
				
			||||||
                    CultureInfo.InvariantCulture,
 | 
					 | 
				
			||||||
                    _localization.GetLocalizedString("PluginInstalledWithName"),
 | 
					 | 
				
			||||||
                    e.Name),
 | 
					 | 
				
			||||||
                NotificationType.PluginInstalled.ToString(),
 | 
					 | 
				
			||||||
                Guid.Empty)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                ShortOverview = string.Format(
 | 
					 | 
				
			||||||
                    CultureInfo.InvariantCulture,
 | 
					 | 
				
			||||||
                    _localization.GetLocalizedString("VersionNumber"),
 | 
					 | 
				
			||||||
                    e.Version)
 | 
					 | 
				
			||||||
            }).ConfigureAwait(false);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private async void OnPackageInstallationFailed(object sender, InstallationFailedEventArgs e)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var installationInfo = e.InstallationInfo;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            await CreateLogEntry(new ActivityLog(
 | 
					 | 
				
			||||||
                string.Format(
 | 
					 | 
				
			||||||
                    CultureInfo.InvariantCulture,
 | 
					 | 
				
			||||||
                    _localization.GetLocalizedString("NameInstallFailed"),
 | 
					 | 
				
			||||||
                    installationInfo.Name),
 | 
					 | 
				
			||||||
                NotificationType.InstallationFailed.ToString(),
 | 
					 | 
				
			||||||
                Guid.Empty)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                ShortOverview = string.Format(
 | 
					 | 
				
			||||||
                    CultureInfo.InvariantCulture,
 | 
					 | 
				
			||||||
                    _localization.GetLocalizedString("VersionNumber"),
 | 
					 | 
				
			||||||
                    installationInfo.Version),
 | 
					 | 
				
			||||||
                Overview = e.Exception.Message
 | 
					 | 
				
			||||||
            }).ConfigureAwait(false);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private async void OnTaskCompleted(object sender, TaskCompletionEventArgs e)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var result = e.Result;
 | 
					 | 
				
			||||||
            var task = e.Task;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (task.ScheduledTask is IConfigurableScheduledTask activityTask
 | 
					 | 
				
			||||||
                && !activityTask.IsLogged)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var time = result.EndTimeUtc - result.StartTimeUtc;
 | 
					 | 
				
			||||||
            var runningTime = string.Format(
 | 
					 | 
				
			||||||
                CultureInfo.InvariantCulture,
 | 
					 | 
				
			||||||
                _localization.GetLocalizedString("LabelRunningTimeValue"),
 | 
					 | 
				
			||||||
                ToUserFriendlyString(time));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (result.Status == TaskCompletionStatus.Failed)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                var vals = new List<string>();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if (!string.IsNullOrEmpty(e.Result.ErrorMessage))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    vals.Add(e.Result.ErrorMessage);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if (!string.IsNullOrEmpty(e.Result.LongErrorMessage))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    vals.Add(e.Result.LongErrorMessage);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                await CreateLogEntry(new ActivityLog(
 | 
					 | 
				
			||||||
                    string.Format(CultureInfo.InvariantCulture, _localization.GetLocalizedString("ScheduledTaskFailedWithName"), task.Name),
 | 
					 | 
				
			||||||
                    NotificationType.TaskFailed.ToString(),
 | 
					 | 
				
			||||||
                    Guid.Empty)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    LogSeverity = LogLevel.Error,
 | 
					 | 
				
			||||||
                    Overview = string.Join(Environment.NewLine, vals),
 | 
					 | 
				
			||||||
                    ShortOverview = runningTime
 | 
					 | 
				
			||||||
                }).ConfigureAwait(false);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private async Task CreateLogEntry(ActivityLog entry)
 | 
					 | 
				
			||||||
            => await _activityManager.CreateAsync(entry).ConfigureAwait(false);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <inheritdoc />
 | 
					 | 
				
			||||||
        public void Dispose()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _taskManager.TaskCompleted -= OnTaskCompleted;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            _installationManager.PluginInstalled -= OnPluginInstalled;
 | 
					 | 
				
			||||||
            _installationManager.PluginUninstalled -= OnPluginUninstalled;
 | 
					 | 
				
			||||||
            _installationManager.PluginUpdated -= OnPluginUpdated;
 | 
					 | 
				
			||||||
            _installationManager.PackageInstallationFailed -= OnPackageInstallationFailed;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            _sessionManager.SessionStarted -= OnSessionStarted;
 | 
					 | 
				
			||||||
            _sessionManager.AuthenticationFailed -= OnAuthenticationFailed;
 | 
					 | 
				
			||||||
            _sessionManager.AuthenticationSucceeded -= OnAuthenticationSucceeded;
 | 
					 | 
				
			||||||
            _sessionManager.SessionEnded -= OnSessionEnded;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            _sessionManager.PlaybackStart -= OnPlaybackStart;
 | 
					 | 
				
			||||||
            _sessionManager.PlaybackStopped -= OnPlaybackStopped;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            _subManager.SubtitleDownloadFailure -= OnSubtitleDownloadFailure;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            _userManager.OnUserCreated -= OnUserCreated;
 | 
					 | 
				
			||||||
            _userManager.OnUserPasswordChanged -= OnUserPasswordChanged;
 | 
					 | 
				
			||||||
            _userManager.OnUserDeleted -= OnUserDeleted;
 | 
					 | 
				
			||||||
            _userManager.OnUserLockedOut -= OnUserLockedOut;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Constructs a user-friendly string for this TimeSpan instance.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        private static string ToUserFriendlyString(TimeSpan span)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            const int DaysInYear = 365;
 | 
					 | 
				
			||||||
            const int DaysInMonth = 30;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // Get each non-zero value from TimeSpan component
 | 
					 | 
				
			||||||
            var values = new List<string>();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // Number of years
 | 
					 | 
				
			||||||
            int days = span.Days;
 | 
					 | 
				
			||||||
            if (days >= DaysInYear)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                int years = days / DaysInYear;
 | 
					 | 
				
			||||||
                values.Add(CreateValueString(years, "year"));
 | 
					 | 
				
			||||||
                days %= DaysInYear;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // Number of months
 | 
					 | 
				
			||||||
            if (days >= DaysInMonth)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                int months = days / DaysInMonth;
 | 
					 | 
				
			||||||
                values.Add(CreateValueString(months, "month"));
 | 
					 | 
				
			||||||
                days = days % DaysInMonth;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // Number of days
 | 
					 | 
				
			||||||
            if (days >= 1)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                values.Add(CreateValueString(days, "day"));
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // Number of hours
 | 
					 | 
				
			||||||
            if (span.Hours >= 1)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                values.Add(CreateValueString(span.Hours, "hour"));
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // Number of minutes
 | 
					 | 
				
			||||||
            if (span.Minutes >= 1)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                values.Add(CreateValueString(span.Minutes, "minute"));
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // Number of seconds (include when 0 if no other components included)
 | 
					 | 
				
			||||||
            if (span.Seconds >= 1 || values.Count == 0)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                values.Add(CreateValueString(span.Seconds, "second"));
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // Combine values into string
 | 
					 | 
				
			||||||
            var builder = new StringBuilder();
 | 
					 | 
				
			||||||
            for (int i = 0; i < values.Count; i++)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                if (builder.Length > 0)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    builder.Append(i == values.Count - 1 ? " and " : ", ");
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                builder.Append(values[i]);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // Return result
 | 
					 | 
				
			||||||
            return builder.ToString();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Constructs a string description of a time-span value.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="value">The value of this item.</param>
 | 
					 | 
				
			||||||
        /// <param name="description">The name of this item (singular form).</param>
 | 
					 | 
				
			||||||
        private static string CreateValueString(int value, string description)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return string.Format(
 | 
					 | 
				
			||||||
                CultureInfo.InvariantCulture,
 | 
					 | 
				
			||||||
                "{0:#,##0} {1}",
 | 
					 | 
				
			||||||
                value,
 | 
					 | 
				
			||||||
                value == 1 ? description : string.Format(CultureInfo.InvariantCulture, "{0}s", description));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -37,6 +37,7 @@ using Emby.Server.Implementations.LiveTv;
 | 
				
			|||||||
using Emby.Server.Implementations.Localization;
 | 
					using Emby.Server.Implementations.Localization;
 | 
				
			||||||
using Emby.Server.Implementations.Net;
 | 
					using Emby.Server.Implementations.Net;
 | 
				
			||||||
using Emby.Server.Implementations.Playlists;
 | 
					using Emby.Server.Implementations.Playlists;
 | 
				
			||||||
 | 
					using Emby.Server.Implementations.QuickConnect;
 | 
				
			||||||
using Emby.Server.Implementations.ScheduledTasks;
 | 
					using Emby.Server.Implementations.ScheduledTasks;
 | 
				
			||||||
using Emby.Server.Implementations.Security;
 | 
					using Emby.Server.Implementations.Security;
 | 
				
			||||||
using Emby.Server.Implementations.Serialization;
 | 
					using Emby.Server.Implementations.Serialization;
 | 
				
			||||||
@ -53,7 +54,6 @@ using MediaBrowser.Common.Net;
 | 
				
			|||||||
using MediaBrowser.Common.Plugins;
 | 
					using MediaBrowser.Common.Plugins;
 | 
				
			||||||
using MediaBrowser.Common.Updates;
 | 
					using MediaBrowser.Common.Updates;
 | 
				
			||||||
using MediaBrowser.Controller;
 | 
					using MediaBrowser.Controller;
 | 
				
			||||||
using MediaBrowser.Controller.Authentication;
 | 
					 | 
				
			||||||
using MediaBrowser.Controller.Channels;
 | 
					using MediaBrowser.Controller.Channels;
 | 
				
			||||||
using MediaBrowser.Controller.Chapters;
 | 
					using MediaBrowser.Controller.Chapters;
 | 
				
			||||||
using MediaBrowser.Controller.Collections;
 | 
					using MediaBrowser.Controller.Collections;
 | 
				
			||||||
@ -72,6 +72,7 @@ using MediaBrowser.Controller.Persistence;
 | 
				
			|||||||
using MediaBrowser.Controller.Playlists;
 | 
					using MediaBrowser.Controller.Playlists;
 | 
				
			||||||
using MediaBrowser.Controller.Plugins;
 | 
					using MediaBrowser.Controller.Plugins;
 | 
				
			||||||
using MediaBrowser.Controller.Providers;
 | 
					using MediaBrowser.Controller.Providers;
 | 
				
			||||||
 | 
					using MediaBrowser.Controller.QuickConnect;
 | 
				
			||||||
using MediaBrowser.Controller.Resolvers;
 | 
					using MediaBrowser.Controller.Resolvers;
 | 
				
			||||||
using MediaBrowser.Controller.Security;
 | 
					using MediaBrowser.Controller.Security;
 | 
				
			||||||
using MediaBrowser.Controller.Session;
 | 
					using MediaBrowser.Controller.Session;
 | 
				
			||||||
@ -98,6 +99,7 @@ using MediaBrowser.Providers.Plugins.TheTvdb;
 | 
				
			|||||||
using MediaBrowser.Providers.Subtitles;
 | 
					using MediaBrowser.Providers.Subtitles;
 | 
				
			||||||
using MediaBrowser.XbmcMetadata.Providers;
 | 
					using MediaBrowser.XbmcMetadata.Providers;
 | 
				
			||||||
using Microsoft.AspNetCore.Http;
 | 
					using Microsoft.AspNetCore.Http;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.Mvc;
 | 
				
			||||||
using Microsoft.Extensions.DependencyInjection;
 | 
					using Microsoft.Extensions.DependencyInjection;
 | 
				
			||||||
using Microsoft.Extensions.Logging;
 | 
					using Microsoft.Extensions.Logging;
 | 
				
			||||||
using Prometheus.DotNetRuntime;
 | 
					using Prometheus.DotNetRuntime;
 | 
				
			||||||
@ -173,6 +175,8 @@ namespace Emby.Server.Implementations
 | 
				
			|||||||
        /// </summary>
 | 
					        /// </summary>
 | 
				
			||||||
        protected ILogger<ApplicationHost> Logger { get; }
 | 
					        protected ILogger<ApplicationHost> Logger { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        protected IServiceCollection ServiceCollection { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private IPlugin[] _plugins;
 | 
					        private IPlugin[] _plugins;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
@ -238,9 +242,11 @@ namespace Emby.Server.Implementations
 | 
				
			|||||||
            ILoggerFactory loggerFactory,
 | 
					            ILoggerFactory loggerFactory,
 | 
				
			||||||
            IStartupOptions options,
 | 
					            IStartupOptions options,
 | 
				
			||||||
            IFileSystem fileSystem,
 | 
					            IFileSystem fileSystem,
 | 
				
			||||||
            INetworkManager networkManager)
 | 
					            INetworkManager networkManager,
 | 
				
			||||||
 | 
					            IServiceCollection serviceCollection)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _xmlSerializer = new MyXmlSerializer();
 | 
					            _xmlSerializer = new MyXmlSerializer();
 | 
				
			||||||
 | 
					            ServiceCollection = serviceCollection;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            _networkManager = networkManager;
 | 
					            _networkManager = networkManager;
 | 
				
			||||||
            networkManager.LocalSubnetsFn = GetConfiguredLocalSubnets;
 | 
					            networkManager.LocalSubnetsFn = GetConfiguredLocalSubnets;
 | 
				
			||||||
@ -464,7 +470,7 @@ namespace Emby.Server.Implementations
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <inheritdoc/>
 | 
					        /// <inheritdoc/>
 | 
				
			||||||
        public void Init(IServiceCollection serviceCollection)
 | 
					        public void Init()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            HttpPort = ServerConfigurationManager.Configuration.HttpServerPortNumber;
 | 
					            HttpPort = ServerConfigurationManager.Configuration.HttpServerPortNumber;
 | 
				
			||||||
            HttpsPort = ServerConfigurationManager.Configuration.HttpsPortNumber;
 | 
					            HttpsPort = ServerConfigurationManager.Configuration.HttpsPortNumber;
 | 
				
			||||||
@ -493,7 +499,7 @@ namespace Emby.Server.Implementations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            DiscoverTypes();
 | 
					            DiscoverTypes();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            RegisterServices(serviceCollection);
 | 
					            RegisterServices();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public Task ExecuteHttpHandlerAsync(HttpContext context, Func<Task> next)
 | 
					        public Task ExecuteHttpHandlerAsync(HttpContext context, Func<Task> next)
 | 
				
			||||||
@ -502,136 +508,140 @@ namespace Emby.Server.Implementations
 | 
				
			|||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
        /// Registers services/resources with the service collection that will be available via DI.
 | 
					        /// Registers services/resources with the service collection that will be available via DI.
 | 
				
			||||||
        /// </summary>
 | 
					        /// </summary>
 | 
				
			||||||
        protected virtual void RegisterServices(IServiceCollection serviceCollection)
 | 
					        protected virtual void RegisterServices()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            serviceCollection.AddSingleton(_startupOptions);
 | 
					            ServiceCollection.AddSingleton(_startupOptions);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddMemoryCache();
 | 
					            ServiceCollection.AddMemoryCache();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddSingleton(ConfigurationManager);
 | 
					            ServiceCollection.AddSingleton(ConfigurationManager);
 | 
				
			||||||
            serviceCollection.AddSingleton<IApplicationHost>(this);
 | 
					            ServiceCollection.AddSingleton<IApplicationHost>(this);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddSingleton<IApplicationPaths>(ApplicationPaths);
 | 
					            ServiceCollection.AddSingleton<IApplicationPaths>(ApplicationPaths);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddSingleton<IJsonSerializer, JsonSerializer>();
 | 
					            ServiceCollection.AddSingleton<IJsonSerializer, JsonSerializer>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddSingleton(_fileSystemManager);
 | 
					            ServiceCollection.AddSingleton(_fileSystemManager);
 | 
				
			||||||
            serviceCollection.AddSingleton<TvdbClientManager>();
 | 
					            ServiceCollection.AddSingleton<TvdbClientManager>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddSingleton<IHttpClient, HttpClientManager.HttpClientManager>();
 | 
					            ServiceCollection.AddSingleton<IHttpClient, HttpClientManager.HttpClientManager>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddSingleton(_networkManager);
 | 
					            ServiceCollection.AddSingleton(_networkManager);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddSingleton<IIsoManager, IsoManager>();
 | 
					            ServiceCollection.AddSingleton<IIsoManager, IsoManager>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddSingleton<ITaskManager, TaskManager>();
 | 
					            ServiceCollection.AddSingleton<ITaskManager, TaskManager>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddSingleton(_xmlSerializer);
 | 
					            ServiceCollection.AddSingleton(_xmlSerializer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddSingleton<IStreamHelper, StreamHelper>();
 | 
					            ServiceCollection.AddSingleton<IStreamHelper, StreamHelper>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddSingleton<ICryptoProvider, CryptographyProvider>();
 | 
					            ServiceCollection.AddSingleton<ICryptoProvider, CryptographyProvider>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddSingleton<ISocketFactory, SocketFactory>();
 | 
					            ServiceCollection.AddSingleton<ISocketFactory, SocketFactory>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddSingleton<IInstallationManager, InstallationManager>();
 | 
					            ServiceCollection.AddSingleton<IInstallationManager, InstallationManager>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddSingleton<IZipClient, ZipClient>();
 | 
					            ServiceCollection.AddSingleton<IZipClient, ZipClient>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddSingleton<IHttpResultFactory, HttpResultFactory>();
 | 
					            ServiceCollection.AddSingleton<IHttpResultFactory, HttpResultFactory>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddSingleton<IServerApplicationHost>(this);
 | 
					            ServiceCollection.AddSingleton<IServerApplicationHost>(this);
 | 
				
			||||||
            serviceCollection.AddSingleton<IServerApplicationPaths>(ApplicationPaths);
 | 
					            ServiceCollection.AddSingleton<IServerApplicationPaths>(ApplicationPaths);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddSingleton(ServerConfigurationManager);
 | 
					            ServiceCollection.AddSingleton(ServerConfigurationManager);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddSingleton<ILocalizationManager, LocalizationManager>();
 | 
					            ServiceCollection.AddSingleton<ILocalizationManager, LocalizationManager>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddSingleton<IBlurayExaminer, BdInfoExaminer>();
 | 
					            ServiceCollection.AddSingleton<IBlurayExaminer, BdInfoExaminer>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddSingleton<IUserDataRepository, SqliteUserDataRepository>();
 | 
					            ServiceCollection.AddSingleton<IUserDataRepository, SqliteUserDataRepository>();
 | 
				
			||||||
            serviceCollection.AddSingleton<IUserDataManager, UserDataManager>();
 | 
					            ServiceCollection.AddSingleton<IUserDataManager, UserDataManager>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddSingleton<IItemRepository, SqliteItemRepository>();
 | 
					            ServiceCollection.AddSingleton<IItemRepository, SqliteItemRepository>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddSingleton<IAuthenticationRepository, AuthenticationRepository>();
 | 
					            ServiceCollection.AddSingleton<IAuthenticationRepository, AuthenticationRepository>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required
 | 
					            // TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required
 | 
				
			||||||
            serviceCollection.AddTransient(provider => new Lazy<IDtoService>(provider.GetRequiredService<IDtoService>));
 | 
					            ServiceCollection.AddTransient(provider => new Lazy<IDtoService>(provider.GetRequiredService<IDtoService>));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required
 | 
					            // TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required
 | 
				
			||||||
            serviceCollection.AddTransient(provider => new Lazy<EncodingHelper>(provider.GetRequiredService<EncodingHelper>));
 | 
					            ServiceCollection.AddTransient(provider => new Lazy<EncodingHelper>(provider.GetRequiredService<EncodingHelper>));
 | 
				
			||||||
            serviceCollection.AddSingleton<IMediaEncoder, MediaBrowser.MediaEncoding.Encoder.MediaEncoder>();
 | 
					            ServiceCollection.AddSingleton<IMediaEncoder, MediaBrowser.MediaEncoding.Encoder.MediaEncoder>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // TODO: Refactor to eliminate the circular dependencies here so that Lazy<T> isn't required
 | 
					            // TODO: Refactor to eliminate the circular dependencies here so that Lazy<T> isn't required
 | 
				
			||||||
            serviceCollection.AddTransient(provider => new Lazy<ILibraryMonitor>(provider.GetRequiredService<ILibraryMonitor>));
 | 
					            ServiceCollection.AddTransient(provider => new Lazy<ILibraryMonitor>(provider.GetRequiredService<ILibraryMonitor>));
 | 
				
			||||||
            serviceCollection.AddTransient(provider => new Lazy<IProviderManager>(provider.GetRequiredService<IProviderManager>));
 | 
					            ServiceCollection.AddTransient(provider => new Lazy<IProviderManager>(provider.GetRequiredService<IProviderManager>));
 | 
				
			||||||
            serviceCollection.AddTransient(provider => new Lazy<IUserViewManager>(provider.GetRequiredService<IUserViewManager>));
 | 
					            ServiceCollection.AddTransient(provider => new Lazy<IUserViewManager>(provider.GetRequiredService<IUserViewManager>));
 | 
				
			||||||
            serviceCollection.AddSingleton<ILibraryManager, LibraryManager>();
 | 
					            ServiceCollection.AddSingleton<ILibraryManager, LibraryManager>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddSingleton<IMusicManager, MusicManager>();
 | 
					            ServiceCollection.AddSingleton<IMusicManager, MusicManager>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddSingleton<ILibraryMonitor, LibraryMonitor>();
 | 
					            ServiceCollection.AddSingleton<ILibraryMonitor, LibraryMonitor>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddSingleton<ISearchEngine, SearchEngine>();
 | 
					            ServiceCollection.AddSingleton<ISearchEngine, SearchEngine>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddSingleton<ServiceController>();
 | 
					            ServiceCollection.AddSingleton<ServiceController>();
 | 
				
			||||||
            serviceCollection.AddSingleton<IHttpServer, HttpListenerHost>();
 | 
					            ServiceCollection.AddSingleton<IHttpServer, HttpListenerHost>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddSingleton<IImageProcessor, ImageProcessor>();
 | 
					            ServiceCollection.AddSingleton<IImageProcessor, ImageProcessor>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddSingleton<ITVSeriesManager, TVSeriesManager>();
 | 
					            ServiceCollection.AddSingleton<ITVSeriesManager, TVSeriesManager>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddSingleton<IDeviceManager, DeviceManager>();
 | 
					            ServiceCollection.AddSingleton<IDeviceManager, DeviceManager>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddSingleton<IMediaSourceManager, MediaSourceManager>();
 | 
					            ServiceCollection.AddSingleton<IMediaSourceManager, MediaSourceManager>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddSingleton<ISubtitleManager, SubtitleManager>();
 | 
					            ServiceCollection.AddSingleton<ISubtitleManager, SubtitleManager>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddSingleton<IProviderManager, ProviderManager>();
 | 
					            ServiceCollection.AddSingleton<IProviderManager, ProviderManager>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required
 | 
					            // TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required
 | 
				
			||||||
            serviceCollection.AddTransient(provider => new Lazy<ILiveTvManager>(provider.GetRequiredService<ILiveTvManager>));
 | 
					            ServiceCollection.AddTransient(provider => new Lazy<ILiveTvManager>(provider.GetRequiredService<ILiveTvManager>));
 | 
				
			||||||
            serviceCollection.AddSingleton<IDtoService, DtoService>();
 | 
					            ServiceCollection.AddSingleton<IDtoService, DtoService>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddSingleton<IChannelManager, ChannelManager>();
 | 
					            ServiceCollection.AddSingleton<IChannelManager, ChannelManager>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddSingleton<ISessionManager, SessionManager>();
 | 
					            ServiceCollection.AddSingleton<ISessionManager, SessionManager>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddSingleton<IDlnaManager, DlnaManager>();
 | 
					            ServiceCollection.AddSingleton<IDlnaManager, DlnaManager>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddSingleton<ICollectionManager, CollectionManager>();
 | 
					            ServiceCollection.AddSingleton<ICollectionManager, CollectionManager>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddSingleton<IPlaylistManager, PlaylistManager>();
 | 
					            ServiceCollection.AddSingleton<IPlaylistManager, PlaylistManager>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddSingleton<ISyncPlayManager, SyncPlayManager>();
 | 
					            ServiceCollection.AddSingleton<ISyncPlayManager, SyncPlayManager>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddSingleton<LiveTvDtoService>();
 | 
					            ServiceCollection.AddSingleton<LiveTvDtoService>();
 | 
				
			||||||
            serviceCollection.AddSingleton<ILiveTvManager, LiveTvManager>();
 | 
					            ServiceCollection.AddSingleton<ILiveTvManager, LiveTvManager>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddSingleton<IUserViewManager, UserViewManager>();
 | 
					            ServiceCollection.AddSingleton<IUserViewManager, UserViewManager>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddSingleton<INotificationManager, NotificationManager>();
 | 
					            ServiceCollection.AddSingleton<INotificationManager, NotificationManager>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddSingleton<IDeviceDiscovery, DeviceDiscovery>();
 | 
					            ServiceCollection.AddSingleton<IDeviceDiscovery, DeviceDiscovery>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddSingleton<IChapterManager, ChapterManager>();
 | 
					            ServiceCollection.AddSingleton<IChapterManager, ChapterManager>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddSingleton<IEncodingManager, MediaEncoder.EncodingManager>();
 | 
					            ServiceCollection.AddSingleton<IEncodingManager, MediaEncoder.EncodingManager>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddSingleton<IAuthorizationContext, AuthorizationContext>();
 | 
					            ServiceCollection.AddSingleton<IAuthorizationContext, AuthorizationContext>();
 | 
				
			||||||
            serviceCollection.AddSingleton<ISessionContext, SessionContext>();
 | 
					            ServiceCollection.AddSingleton<ISessionContext, SessionContext>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddSingleton<IAuthService, AuthService>();
 | 
					            ServiceCollection.AddSingleton<IAuthService, AuthService>();
 | 
				
			||||||
 | 
					            ServiceCollection.AddSingleton<IQuickConnect, QuickConnectManager>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddSingleton<ISubtitleEncoder, MediaBrowser.MediaEncoding.Subtitles.SubtitleEncoder>();
 | 
					            ServiceCollection.AddSingleton<ISubtitleEncoder, MediaBrowser.MediaEncoding.Subtitles.SubtitleEncoder>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddSingleton<IResourceFileManager, ResourceFileManager>();
 | 
					            ServiceCollection.AddSingleton<IResourceFileManager, ResourceFileManager>();
 | 
				
			||||||
            serviceCollection.AddSingleton<EncodingHelper>();
 | 
					            ServiceCollection.AddSingleton<EncodingHelper>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddSingleton<IAttachmentExtractor, MediaBrowser.MediaEncoding.Attachments.AttachmentExtractor>();
 | 
					            ServiceCollection.AddSingleton<IAttachmentExtractor, MediaBrowser.MediaEncoding.Attachments.AttachmentExtractor>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            serviceCollection.AddSingleton<TranscodingJobHelper>();
 | 
					            ServiceCollection.AddSingleton<TranscodingJobHelper>();
 | 
				
			||||||
 | 
					            ServiceCollection.AddScoped<MediaInfoHelper>();
 | 
				
			||||||
 | 
					            ServiceCollection.AddScoped<AudioHelper>();
 | 
				
			||||||
 | 
					            ServiceCollection.AddScoped<DynamicHlsHelper>();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
@ -831,6 +841,8 @@ namespace Emby.Server.Implementations
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    hasPluginConfiguration.SetStartupInfo(s => Directory.CreateDirectory(s));
 | 
					                    hasPluginConfiguration.SetStartupInfo(s => Directory.CreateDirectory(s));
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                plugin.RegisterServices(ServiceCollection);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            catch (Exception ex)
 | 
					            catch (Exception ex)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@ -1385,6 +1397,20 @@ namespace Emby.Server.Implementations
 | 
				
			|||||||
            _plugins = list.ToArray();
 | 
					            _plugins = list.ToArray();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public IEnumerable<Assembly> GetApiPluginAssemblies()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var assemblies = _allConcreteTypes
 | 
				
			||||||
 | 
					                .Where(i => typeof(ControllerBase).IsAssignableFrom(i))
 | 
				
			||||||
 | 
					                .Select(i => i.Assembly)
 | 
				
			||||||
 | 
					                .Distinct();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            foreach (var assembly in assemblies)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                Logger.LogDebug("Found API endpoints in plugin {name}", assembly.FullName);
 | 
				
			||||||
 | 
					                yield return assembly;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public virtual void LaunchUrl(string url)
 | 
					        public virtual void LaunchUrl(string url)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (!CanLaunchWebBrowser)
 | 
					            if (!CanLaunchWebBrowser)
 | 
				
			||||||
 | 
				
			|||||||
@ -746,12 +746,21 @@ namespace Emby.Server.Implementations.Channels
 | 
				
			|||||||
            // null if came from cache
 | 
					            // null if came from cache
 | 
				
			||||||
            if (itemsResult != null)
 | 
					            if (itemsResult != null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var internalItems = itemsResult.Items
 | 
					                var items = itemsResult.Items;
 | 
				
			||||||
                    .Select(i => GetChannelItemEntity(i, channelProvider, channel.Id, parentItem, cancellationToken))
 | 
					                var itemsLen = items.Count;
 | 
				
			||||||
                    .ToArray();
 | 
					                var internalItems = new Guid[itemsLen];
 | 
				
			||||||
 | 
					                for (int i = 0; i < itemsLen; i++)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    internalItems[i] = (await GetChannelItemEntityAsync(
 | 
				
			||||||
 | 
					                        items[i],
 | 
				
			||||||
 | 
					                        channelProvider,
 | 
				
			||||||
 | 
					                        channel.Id,
 | 
				
			||||||
 | 
					                        parentItem,
 | 
				
			||||||
 | 
					                        cancellationToken).ConfigureAwait(false)).Id;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                var existingIds = _libraryManager.GetItemIds(query);
 | 
					                var existingIds = _libraryManager.GetItemIds(query);
 | 
				
			||||||
                var deadIds = existingIds.Except(internalItems.Select(i => i.Id))
 | 
					                var deadIds = existingIds.Except(internalItems)
 | 
				
			||||||
                    .ToArray();
 | 
					                    .ToArray();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                foreach (var deadId in deadIds)
 | 
					                foreach (var deadId in deadIds)
 | 
				
			||||||
@ -963,7 +972,7 @@ namespace Emby.Server.Implementations.Channels
 | 
				
			|||||||
            return item;
 | 
					            return item;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private BaseItem GetChannelItemEntity(ChannelItemInfo info, IChannel channelProvider, Guid internalChannelId, BaseItem parentFolder, CancellationToken cancellationToken)
 | 
					        private async Task<BaseItem> GetChannelItemEntityAsync(ChannelItemInfo info, IChannel channelProvider, Guid internalChannelId, BaseItem parentFolder, CancellationToken cancellationToken)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var parentFolderId = parentFolder.Id;
 | 
					            var parentFolderId = parentFolder.Id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1165,7 +1174,7 @@ namespace Emby.Server.Implementations.Channels
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
            else if (forceUpdate)
 | 
					            else if (forceUpdate)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                item.UpdateToRepository(ItemUpdateType.None, cancellationToken);
 | 
					                await item.UpdateToRepositoryAsync(ItemUpdateType.None, cancellationToken).ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if ((isNew || forceUpdate) && info.Type == ChannelItemType.Media)
 | 
					            if ((isNew || forceUpdate) && info.Type == ChannelItemType.Media)
 | 
				
			||||||
 | 
				
			|||||||
@ -132,7 +132,7 @@ namespace Emby.Server.Implementations.Collections
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <inheritdoc />
 | 
					        /// <inheritdoc />
 | 
				
			||||||
        public BoxSet CreateCollection(CollectionCreationOptions options)
 | 
					        public async Task<BoxSet> CreateCollectionAsync(CollectionCreationOptions options)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var name = options.Name;
 | 
					            var name = options.Name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -141,7 +141,7 @@ namespace Emby.Server.Implementations.Collections
 | 
				
			|||||||
            // This could cause it to get re-resolved as a plain folder
 | 
					            // This could cause it to get re-resolved as a plain folder
 | 
				
			||||||
            var folderName = _fileSystem.GetValidFilename(name) + " [boxset]";
 | 
					            var folderName = _fileSystem.GetValidFilename(name) + " [boxset]";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var parentFolder = GetCollectionsFolder(true).GetAwaiter().GetResult();
 | 
					            var parentFolder = await GetCollectionsFolder(true).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (parentFolder == null)
 | 
					            if (parentFolder == null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@ -169,12 +169,16 @@ namespace Emby.Server.Implementations.Collections
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                if (options.ItemIdList.Length > 0)
 | 
					                if (options.ItemIdList.Length > 0)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    AddToCollection(collection.Id, options.ItemIdList, false, new MetadataRefreshOptions(new DirectoryService(_fileSystem))
 | 
					                    await AddToCollectionAsync(
 | 
				
			||||||
 | 
					                        collection.Id,
 | 
				
			||||||
 | 
					                        options.ItemIdList.Select(x => new Guid(x)),
 | 
				
			||||||
 | 
					                        false,
 | 
				
			||||||
 | 
					                        new MetadataRefreshOptions(new DirectoryService(_fileSystem))
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
                            // The initial adding of items is going to create a local metadata file
 | 
					                            // The initial adding of items is going to create a local metadata file
 | 
				
			||||||
                            // This will cause internet metadata to be skipped as a result
 | 
					                            // This will cause internet metadata to be skipped as a result
 | 
				
			||||||
                            MetadataRefreshMode = MetadataRefreshMode.FullRefresh
 | 
					                            MetadataRefreshMode = MetadataRefreshMode.FullRefresh
 | 
				
			||||||
                    });
 | 
					                        }).ConfigureAwait(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                else
 | 
					                else
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
@ -197,18 +201,10 @@ namespace Emby.Server.Implementations.Collections
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <inheritdoc />
 | 
					        /// <inheritdoc />
 | 
				
			||||||
        public void AddToCollection(Guid collectionId, IEnumerable<string> ids)
 | 
					        public Task AddToCollectionAsync(Guid collectionId, IEnumerable<Guid> ids)
 | 
				
			||||||
        {
 | 
					            => AddToCollectionAsync(collectionId, ids, true, new MetadataRefreshOptions(new DirectoryService(_fileSystem)));
 | 
				
			||||||
            AddToCollection(collectionId, ids, true, new MetadataRefreshOptions(new DirectoryService(_fileSystem)));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <inheritdoc />
 | 
					        private async Task AddToCollectionAsync(Guid collectionId, IEnumerable<Guid> ids, bool fireEvent, MetadataRefreshOptions refreshOptions)
 | 
				
			||||||
        public void AddToCollection(Guid collectionId, IEnumerable<Guid> ids)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            AddToCollection(collectionId, ids.Select(i => i.ToString("N", CultureInfo.InvariantCulture)), true, new MetadataRefreshOptions(new DirectoryService(_fileSystem)));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private void AddToCollection(Guid collectionId, IEnumerable<string> ids, bool fireEvent, MetadataRefreshOptions refreshOptions)
 | 
					 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var collection = _libraryManager.GetItemById(collectionId) as BoxSet;
 | 
					            var collection = _libraryManager.GetItemById(collectionId) as BoxSet;
 | 
				
			||||||
            if (collection == null)
 | 
					            if (collection == null)
 | 
				
			||||||
@ -224,15 +220,14 @@ namespace Emby.Server.Implementations.Collections
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            foreach (var id in ids)
 | 
					            foreach (var id in ids)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var guidId = new Guid(id);
 | 
					                var item = _libraryManager.GetItemById(id);
 | 
				
			||||||
                var item = _libraryManager.GetItemById(guidId);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (item == null)
 | 
					                if (item == null)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    throw new ArgumentException("No item exists with the supplied Id");
 | 
					                    throw new ArgumentException("No item exists with the supplied Id");
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (!currentLinkedChildrenIds.Contains(guidId))
 | 
					                if (!currentLinkedChildrenIds.Contains(id))
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    itemList.Add(item);
 | 
					                    itemList.Add(item);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -249,7 +244,7 @@ namespace Emby.Server.Implementations.Collections
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                collection.UpdateRatingToItems(linkedChildrenList);
 | 
					                collection.UpdateRatingToItems(linkedChildrenList);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                collection.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
 | 
					                await collection.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                refreshOptions.ForceSave = true;
 | 
					                refreshOptions.ForceSave = true;
 | 
				
			||||||
                _providerManager.QueueRefresh(collection.Id, refreshOptions, RefreshPriority.High);
 | 
					                _providerManager.QueueRefresh(collection.Id, refreshOptions, RefreshPriority.High);
 | 
				
			||||||
@ -266,13 +261,7 @@ namespace Emby.Server.Implementations.Collections
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <inheritdoc />
 | 
					        /// <inheritdoc />
 | 
				
			||||||
        public void RemoveFromCollection(Guid collectionId, IEnumerable<string> itemIds)
 | 
					        public async Task RemoveFromCollectionAsync(Guid collectionId, IEnumerable<Guid> itemIds)
 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            RemoveFromCollection(collectionId, itemIds.Select(i => new Guid(i)));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <inheritdoc />
 | 
					 | 
				
			||||||
        public void RemoveFromCollection(Guid collectionId, IEnumerable<Guid> itemIds)
 | 
					 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var collection = _libraryManager.GetItemById(collectionId) as BoxSet;
 | 
					            var collection = _libraryManager.GetItemById(collectionId) as BoxSet;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -309,7 +298,7 @@ namespace Emby.Server.Implementations.Collections
 | 
				
			|||||||
                collection.LinkedChildren = collection.LinkedChildren.Except(list).ToArray();
 | 
					                collection.LinkedChildren = collection.LinkedChildren.Except(list).ToArray();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            collection.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
 | 
					            await collection.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
 | 
				
			||||||
            _providerManager.QueueRefresh(
 | 
					            _providerManager.QueueRefresh(
 | 
				
			||||||
                collection.Id,
 | 
					                collection.Id,
 | 
				
			||||||
                new MetadataRefreshOptions(new DirectoryService(_fileSystem))
 | 
					                new MetadataRefreshOptions(new DirectoryService(_fileSystem))
 | 
				
			||||||
 | 
				
			|||||||
@ -2,11 +2,11 @@ using System;
 | 
				
			|||||||
using System.Globalization;
 | 
					using System.Globalization;
 | 
				
			||||||
using System.IO;
 | 
					using System.IO;
 | 
				
			||||||
using Emby.Server.Implementations.AppBase;
 | 
					using Emby.Server.Implementations.AppBase;
 | 
				
			||||||
 | 
					using Jellyfin.Data.Events;
 | 
				
			||||||
using MediaBrowser.Common.Configuration;
 | 
					using MediaBrowser.Common.Configuration;
 | 
				
			||||||
using MediaBrowser.Controller;
 | 
					using MediaBrowser.Controller;
 | 
				
			||||||
using MediaBrowser.Controller.Configuration;
 | 
					using MediaBrowser.Controller.Configuration;
 | 
				
			||||||
using MediaBrowser.Model.Configuration;
 | 
					using MediaBrowser.Model.Configuration;
 | 
				
			||||||
using MediaBrowser.Model.Events;
 | 
					 | 
				
			||||||
using MediaBrowser.Model.IO;
 | 
					using MediaBrowser.Model.IO;
 | 
				
			||||||
using MediaBrowser.Model.Serialization;
 | 
					using MediaBrowser.Model.Serialization;
 | 
				
			||||||
using Microsoft.Extensions.Logging;
 | 
					using Microsoft.Extensions.Logging;
 | 
				
			||||||
 | 
				
			|||||||
@ -4308,7 +4308,7 @@ namespace Emby.Server.Implementations.Data
 | 
				
			|||||||
                whereClauses.Add("ProductionYear=@Years");
 | 
					                whereClauses.Add("ProductionYear=@Years");
 | 
				
			||||||
                if (statement != null)
 | 
					                if (statement != null)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    statement.TryBind("@Years", query.Years[0].ToString());
 | 
					                    statement.TryBind("@Years", query.Years[0].ToString(CultureInfo.InvariantCulture));
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            else if (query.Years.Length > 1)
 | 
					            else if (query.Years.Length > 1)
 | 
				
			||||||
@ -4560,13 +4560,13 @@ namespace Emby.Server.Implementations.Data
 | 
				
			|||||||
            if (query.AncestorIds.Length > 1)
 | 
					            if (query.AncestorIds.Length > 1)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var inClause = string.Join(",", query.AncestorIds.Select(i => "'" + i.ToString("N", CultureInfo.InvariantCulture) + "'"));
 | 
					                var inClause = string.Join(",", query.AncestorIds.Select(i => "'" + i.ToString("N", CultureInfo.InvariantCulture) + "'"));
 | 
				
			||||||
                whereClauses.Add(string.Format("Guid in (select itemId from AncestorIds where AncestorIdText in ({0}))", inClause));
 | 
					                whereClauses.Add(string.Format(CultureInfo.InvariantCulture, "Guid in (select itemId from AncestorIds where AncestorIdText in ({0}))", inClause));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (!string.IsNullOrWhiteSpace(query.AncestorWithPresentationUniqueKey))
 | 
					            if (!string.IsNullOrWhiteSpace(query.AncestorWithPresentationUniqueKey))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var inClause = "select guid from TypedBaseItems where PresentationUniqueKey=@AncestorWithPresentationUniqueKey";
 | 
					                var inClause = "select guid from TypedBaseItems where PresentationUniqueKey=@AncestorWithPresentationUniqueKey";
 | 
				
			||||||
                whereClauses.Add(string.Format("Guid in (select itemId from AncestorIds where AncestorId in ({0}))", inClause));
 | 
					                whereClauses.Add(string.Format(CultureInfo.InvariantCulture, "Guid in (select itemId from AncestorIds where AncestorId in ({0}))", inClause));
 | 
				
			||||||
                if (statement != null)
 | 
					                if (statement != null)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    statement.TryBind("@AncestorWithPresentationUniqueKey", query.AncestorWithPresentationUniqueKey);
 | 
					                    statement.TryBind("@AncestorWithPresentationUniqueKey", query.AncestorWithPresentationUniqueKey);
 | 
				
			||||||
@ -5170,7 +5170,10 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
 | 
				
			|||||||
                    insertText.Append(',');
 | 
					                    insertText.Append(',');
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                insertText.AppendFormat("(@ItemId, @AncestorId{0}, @AncestorIdText{0})", i.ToString(CultureInfo.InvariantCulture));
 | 
					                insertText.AppendFormat(
 | 
				
			||||||
 | 
					                    CultureInfo.InvariantCulture,
 | 
				
			||||||
 | 
					                    "(@ItemId, @AncestorId{0}, @AncestorIdText{0})",
 | 
				
			||||||
 | 
					                    i.ToString(CultureInfo.InvariantCulture));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            using (var statement = PrepareStatement(db, insertText.ToString()))
 | 
					            using (var statement = PrepareStatement(db, insertText.ToString()))
 | 
				
			||||||
 | 
				
			|||||||
@ -7,13 +7,13 @@ using System.IO;
 | 
				
			|||||||
using System.Linq;
 | 
					using System.Linq;
 | 
				
			||||||
using Jellyfin.Data.Entities;
 | 
					using Jellyfin.Data.Entities;
 | 
				
			||||||
using Jellyfin.Data.Enums;
 | 
					using Jellyfin.Data.Enums;
 | 
				
			||||||
 | 
					using Jellyfin.Data.Events;
 | 
				
			||||||
using MediaBrowser.Common.Extensions;
 | 
					using MediaBrowser.Common.Extensions;
 | 
				
			||||||
using MediaBrowser.Controller.Configuration;
 | 
					using MediaBrowser.Controller.Configuration;
 | 
				
			||||||
using MediaBrowser.Controller.Devices;
 | 
					using MediaBrowser.Controller.Devices;
 | 
				
			||||||
using MediaBrowser.Controller.Library;
 | 
					using MediaBrowser.Controller.Library;
 | 
				
			||||||
using MediaBrowser.Controller.Security;
 | 
					using MediaBrowser.Controller.Security;
 | 
				
			||||||
using MediaBrowser.Model.Devices;
 | 
					using MediaBrowser.Model.Devices;
 | 
				
			||||||
using MediaBrowser.Model.Events;
 | 
					 | 
				
			||||||
using MediaBrowser.Model.Querying;
 | 
					using MediaBrowser.Model.Querying;
 | 
				
			||||||
using MediaBrowser.Model.Serialization;
 | 
					using MediaBrowser.Model.Serialization;
 | 
				
			||||||
using MediaBrowser.Model.Session;
 | 
					using MediaBrowser.Model.Session;
 | 
				
			||||||
 | 
				
			|||||||
@ -22,7 +22,7 @@
 | 
				
			|||||||
  </ItemGroup>
 | 
					  </ItemGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <ItemGroup>
 | 
					  <ItemGroup>
 | 
				
			||||||
    <PackageReference Include="IPNetwork2" Version="2.5.211" />
 | 
					    <PackageReference Include="IPNetwork2" Version="2.5.224" />
 | 
				
			||||||
    <PackageReference Include="Jellyfin.XmlTv" Version="10.6.2" />
 | 
					    <PackageReference Include="Jellyfin.XmlTv" Version="10.6.2" />
 | 
				
			||||||
    <PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.2.7" />
 | 
					    <PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.2.7" />
 | 
				
			||||||
    <PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="2.2.0" />
 | 
					    <PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="2.2.0" />
 | 
				
			||||||
@ -37,11 +37,11 @@
 | 
				
			|||||||
    <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.6" />
 | 
					    <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.6" />
 | 
				
			||||||
    <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.6" />
 | 
					    <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.6" />
 | 
				
			||||||
    <PackageReference Include="Mono.Nat" Version="2.0.2" />
 | 
					    <PackageReference Include="Mono.Nat" Version="2.0.2" />
 | 
				
			||||||
    <PackageReference Include="prometheus-net.DotNetRuntime" Version="3.3.1" />
 | 
					    <PackageReference Include="prometheus-net.DotNetRuntime" Version="3.4.0" />
 | 
				
			||||||
    <PackageReference Include="ServiceStack.Text.Core" Version="5.9.2" />
 | 
					    <PackageReference Include="ServiceStack.Text.Core" Version="5.9.2" />
 | 
				
			||||||
    <PackageReference Include="sharpcompress" Version="0.26.0" />
 | 
					    <PackageReference Include="sharpcompress" Version="0.26.0" />
 | 
				
			||||||
    <PackageReference Include="SQLitePCL.pretty.netstandard" Version="2.1.0" />
 | 
					    <PackageReference Include="SQLitePCL.pretty.netstandard" Version="2.1.0" />
 | 
				
			||||||
    <PackageReference Include="DotNet.Glob" Version="3.0.9" />
 | 
					    <PackageReference Include="DotNet.Glob" Version="3.1.0" />
 | 
				
			||||||
  </ItemGroup>
 | 
					  </ItemGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <ItemGroup>
 | 
					  <ItemGroup>
 | 
				
			||||||
 | 
				
			|||||||
@ -7,11 +7,11 @@ using System.Net;
 | 
				
			|||||||
using System.Text;
 | 
					using System.Text;
 | 
				
			||||||
using System.Threading;
 | 
					using System.Threading;
 | 
				
			||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using Jellyfin.Data.Events;
 | 
				
			||||||
using MediaBrowser.Controller;
 | 
					using MediaBrowser.Controller;
 | 
				
			||||||
using MediaBrowser.Controller.Configuration;
 | 
					using MediaBrowser.Controller.Configuration;
 | 
				
			||||||
using MediaBrowser.Controller.Plugins;
 | 
					using MediaBrowser.Controller.Plugins;
 | 
				
			||||||
using MediaBrowser.Model.Dlna;
 | 
					using MediaBrowser.Model.Dlna;
 | 
				
			||||||
using MediaBrowser.Model.Events;
 | 
					 | 
				
			||||||
using Microsoft.Extensions.Logging;
 | 
					using Microsoft.Extensions.Logging;
 | 
				
			||||||
using Mono.Nat;
 | 
					using Mono.Nat;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -7,6 +7,7 @@ using System.Linq;
 | 
				
			|||||||
using System.Threading;
 | 
					using System.Threading;
 | 
				
			||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
using Jellyfin.Data.Entities;
 | 
					using Jellyfin.Data.Entities;
 | 
				
			||||||
 | 
					using Jellyfin.Data.Events;
 | 
				
			||||||
using MediaBrowser.Controller.Channels;
 | 
					using MediaBrowser.Controller.Channels;
 | 
				
			||||||
using MediaBrowser.Controller.Entities;
 | 
					using MediaBrowser.Controller.Entities;
 | 
				
			||||||
using MediaBrowser.Controller.Entities.Audio;
 | 
					using MediaBrowser.Controller.Entities.Audio;
 | 
				
			||||||
@ -15,7 +16,6 @@ using MediaBrowser.Controller.Plugins;
 | 
				
			|||||||
using MediaBrowser.Controller.Providers;
 | 
					using MediaBrowser.Controller.Providers;
 | 
				
			||||||
using MediaBrowser.Controller.Session;
 | 
					using MediaBrowser.Controller.Session;
 | 
				
			||||||
using MediaBrowser.Model.Entities;
 | 
					using MediaBrowser.Model.Entities;
 | 
				
			||||||
using MediaBrowser.Model.Events;
 | 
					 | 
				
			||||||
using Microsoft.Extensions.Logging;
 | 
					using Microsoft.Extensions.Logging;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Emby.Server.Implementations.EntryPoints
 | 
					namespace Emby.Server.Implementations.EntryPoints
 | 
				
			||||||
 | 
				
			|||||||
@ -5,6 +5,7 @@ using System.Linq;
 | 
				
			|||||||
using System.Threading;
 | 
					using System.Threading;
 | 
				
			||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
using Jellyfin.Data.Enums;
 | 
					using Jellyfin.Data.Enums;
 | 
				
			||||||
 | 
					using Jellyfin.Data.Events;
 | 
				
			||||||
using MediaBrowser.Controller.Library;
 | 
					using MediaBrowser.Controller.Library;
 | 
				
			||||||
using MediaBrowser.Controller.LiveTv;
 | 
					using MediaBrowser.Controller.LiveTv;
 | 
				
			||||||
using MediaBrowser.Controller.Plugins;
 | 
					using MediaBrowser.Controller.Plugins;
 | 
				
			||||||
@ -43,22 +44,22 @@ namespace Emby.Server.Implementations.EntryPoints
 | 
				
			|||||||
            return Task.CompletedTask;
 | 
					            return Task.CompletedTask;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private async void OnLiveTvManagerSeriesTimerCreated(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e)
 | 
					        private async void OnLiveTvManagerSeriesTimerCreated(object sender, GenericEventArgs<TimerEventInfo> e)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            await SendMessage("SeriesTimerCreated", e.Argument).ConfigureAwait(false);
 | 
					            await SendMessage("SeriesTimerCreated", e.Argument).ConfigureAwait(false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private async void OnLiveTvManagerTimerCreated(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e)
 | 
					        private async void OnLiveTvManagerTimerCreated(object sender, GenericEventArgs<TimerEventInfo> e)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            await SendMessage("TimerCreated", e.Argument).ConfigureAwait(false);
 | 
					            await SendMessage("TimerCreated", e.Argument).ConfigureAwait(false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private async void OnLiveTvManagerSeriesTimerCancelled(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e)
 | 
					        private async void OnLiveTvManagerSeriesTimerCancelled(object sender, GenericEventArgs<TimerEventInfo> e)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            await SendMessage("SeriesTimerCancelled", e.Argument).ConfigureAwait(false);
 | 
					            await SendMessage("SeriesTimerCancelled", e.Argument).ConfigureAwait(false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private async void OnLiveTvManagerTimerCancelled(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e)
 | 
					        private async void OnLiveTvManagerTimerCancelled(object sender, GenericEventArgs<TimerEventInfo> e)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            await SendMessage("TimerCancelled", e.Argument).ConfigureAwait(false);
 | 
					            await SendMessage("TimerCancelled", e.Argument).ConfigureAwait(false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,210 +0,0 @@
 | 
				
			|||||||
using System;
 | 
					 | 
				
			||||||
using System.Collections.Generic;
 | 
					 | 
				
			||||||
using System.Globalization;
 | 
					 | 
				
			||||||
using System.Threading;
 | 
					 | 
				
			||||||
using System.Threading.Tasks;
 | 
					 | 
				
			||||||
using Jellyfin.Data.Entities;
 | 
					 | 
				
			||||||
using MediaBrowser.Common.Plugins;
 | 
					 | 
				
			||||||
using MediaBrowser.Common.Updates;
 | 
					 | 
				
			||||||
using MediaBrowser.Controller;
 | 
					 | 
				
			||||||
using MediaBrowser.Controller.Library;
 | 
					 | 
				
			||||||
using MediaBrowser.Controller.Plugins;
 | 
					 | 
				
			||||||
using MediaBrowser.Controller.Session;
 | 
					 | 
				
			||||||
using MediaBrowser.Model.Events;
 | 
					 | 
				
			||||||
using MediaBrowser.Model.Tasks;
 | 
					 | 
				
			||||||
using MediaBrowser.Model.Updates;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace Emby.Server.Implementations.EntryPoints
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    /// <summary>
 | 
					 | 
				
			||||||
    /// Class WebSocketEvents.
 | 
					 | 
				
			||||||
    /// </summary>
 | 
					 | 
				
			||||||
    public class ServerEventNotifier : IServerEntryPoint
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// The user manager.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        private readonly IUserManager _userManager;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// The installation manager.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        private readonly IInstallationManager _installationManager;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// The kernel.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        private readonly IServerApplicationHost _appHost;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// The task manager.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        private readonly ITaskManager _taskManager;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private readonly ISessionManager _sessionManager;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Initializes a new instance of the <see cref="ServerEventNotifier"/> class.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="appHost">The application host.</param>
 | 
					 | 
				
			||||||
        /// <param name="userManager">The user manager.</param>
 | 
					 | 
				
			||||||
        /// <param name="installationManager">The installation manager.</param>
 | 
					 | 
				
			||||||
        /// <param name="taskManager">The task manager.</param>
 | 
					 | 
				
			||||||
        /// <param name="sessionManager">The session manager.</param>
 | 
					 | 
				
			||||||
        public ServerEventNotifier(
 | 
					 | 
				
			||||||
            IServerApplicationHost appHost,
 | 
					 | 
				
			||||||
            IUserManager userManager,
 | 
					 | 
				
			||||||
            IInstallationManager installationManager,
 | 
					 | 
				
			||||||
            ITaskManager taskManager,
 | 
					 | 
				
			||||||
            ISessionManager sessionManager)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _userManager = userManager;
 | 
					 | 
				
			||||||
            _installationManager = installationManager;
 | 
					 | 
				
			||||||
            _appHost = appHost;
 | 
					 | 
				
			||||||
            _taskManager = taskManager;
 | 
					 | 
				
			||||||
            _sessionManager = sessionManager;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <inheritdoc />
 | 
					 | 
				
			||||||
        public Task RunAsync()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _userManager.OnUserDeleted += OnUserDeleted;
 | 
					 | 
				
			||||||
            _userManager.OnUserUpdated += OnUserUpdated;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            _appHost.HasPendingRestartChanged += OnHasPendingRestartChanged;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            _installationManager.PluginUninstalled += OnPluginUninstalled;
 | 
					 | 
				
			||||||
            _installationManager.PackageInstalling += OnPackageInstalling;
 | 
					 | 
				
			||||||
            _installationManager.PackageInstallationCancelled += OnPackageInstallationCancelled;
 | 
					 | 
				
			||||||
            _installationManager.PackageInstallationCompleted += OnPackageInstallationCompleted;
 | 
					 | 
				
			||||||
            _installationManager.PackageInstallationFailed += OnPackageInstallationFailed;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            _taskManager.TaskCompleted += OnTaskCompleted;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return Task.CompletedTask;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private async void OnPackageInstalling(object sender, InstallationInfo e)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            await SendMessageToAdminSessions("PackageInstalling", e).ConfigureAwait(false);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private async void OnPackageInstallationCancelled(object sender, InstallationInfo e)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            await SendMessageToAdminSessions("PackageInstallationCancelled", e).ConfigureAwait(false);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private async void OnPackageInstallationCompleted(object sender, InstallationInfo e)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            await SendMessageToAdminSessions("PackageInstallationCompleted", e).ConfigureAwait(false);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private async void OnPackageInstallationFailed(object sender, InstallationFailedEventArgs e)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            await SendMessageToAdminSessions("PackageInstallationFailed", e.InstallationInfo).ConfigureAwait(false);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private async void OnTaskCompleted(object sender, TaskCompletionEventArgs e)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            await SendMessageToAdminSessions("ScheduledTaskEnded", e.Result).ConfigureAwait(false);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Installations the manager_ plugin uninstalled.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="sender">The sender.</param>
 | 
					 | 
				
			||||||
        /// <param name="e">The e.</param>
 | 
					 | 
				
			||||||
        private async void OnPluginUninstalled(object sender, IPlugin e)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            await SendMessageToAdminSessions("PluginUninstalled", e).ConfigureAwait(false);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Handles the HasPendingRestartChanged event of the kernel control.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="sender">The source of the event.</param>
 | 
					 | 
				
			||||||
        /// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
 | 
					 | 
				
			||||||
        private async void OnHasPendingRestartChanged(object sender, EventArgs e)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            await _sessionManager.SendRestartRequiredNotification(CancellationToken.None).ConfigureAwait(false);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Users the manager_ user updated.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="sender">The sender.</param>
 | 
					 | 
				
			||||||
        /// <param name="e">The e.</param>
 | 
					 | 
				
			||||||
        private async void OnUserUpdated(object sender, GenericEventArgs<User> e)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var dto = _userManager.GetUserDto(e.Argument);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            await SendMessageToUserSession(e.Argument, "UserUpdated", dto).ConfigureAwait(false);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Users the manager_ user deleted.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="sender">The sender.</param>
 | 
					 | 
				
			||||||
        /// <param name="e">The e.</param>
 | 
					 | 
				
			||||||
        private async void OnUserDeleted(object sender, GenericEventArgs<User> e)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            await SendMessageToUserSession(e.Argument, "UserDeleted", e.Argument.Id.ToString("N", CultureInfo.InvariantCulture)).ConfigureAwait(false);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private async Task SendMessageToAdminSessions<T>(string name, T data)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            try
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                await _sessionManager.SendMessageToAdminSessions(name, data, CancellationToken.None).ConfigureAwait(false);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            catch (Exception)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private async Task SendMessageToUserSession<T>(User user, string name, T data)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            try
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                await _sessionManager.SendMessageToUserSessions(
 | 
					 | 
				
			||||||
                    new List<Guid> { user.Id },
 | 
					 | 
				
			||||||
                    name,
 | 
					 | 
				
			||||||
                    data,
 | 
					 | 
				
			||||||
                    CancellationToken.None).ConfigureAwait(false);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            catch (Exception)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <inheritdoc />
 | 
					 | 
				
			||||||
        public void Dispose()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            Dispose(true);
 | 
					 | 
				
			||||||
            GC.SuppressFinalize(this);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Releases unmanaged and - optionally - managed resources.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
 | 
					 | 
				
			||||||
        protected virtual void Dispose(bool dispose)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (dispose)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                _userManager.OnUserDeleted -= OnUserDeleted;
 | 
					 | 
				
			||||||
                _userManager.OnUserUpdated -= OnUserUpdated;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                _installationManager.PluginUninstalled -= OnPluginUninstalled;
 | 
					 | 
				
			||||||
                _installationManager.PackageInstalling -= OnPackageInstalling;
 | 
					 | 
				
			||||||
                _installationManager.PackageInstallationCancelled -= OnPackageInstallationCancelled;
 | 
					 | 
				
			||||||
                _installationManager.PackageInstallationCompleted -= OnPackageInstallationCompleted;
 | 
					 | 
				
			||||||
                _installationManager.PackageInstallationFailed -= OnPackageInstallationFailed;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                _appHost.HasPendingRestartChanged -= OnHasPendingRestartChanged;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                _taskManager.TaskCompleted -= OnTaskCompleted;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -12,13 +12,13 @@ using System.Threading;
 | 
				
			|||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
using Emby.Server.Implementations.Services;
 | 
					using Emby.Server.Implementations.Services;
 | 
				
			||||||
using Emby.Server.Implementations.SocketSharp;
 | 
					using Emby.Server.Implementations.SocketSharp;
 | 
				
			||||||
 | 
					using Jellyfin.Data.Events;
 | 
				
			||||||
using MediaBrowser.Common.Extensions;
 | 
					using MediaBrowser.Common.Extensions;
 | 
				
			||||||
using MediaBrowser.Common.Net;
 | 
					using MediaBrowser.Common.Net;
 | 
				
			||||||
using MediaBrowser.Controller;
 | 
					using MediaBrowser.Controller;
 | 
				
			||||||
using MediaBrowser.Controller.Authentication;
 | 
					using MediaBrowser.Controller.Authentication;
 | 
				
			||||||
using MediaBrowser.Controller.Configuration;
 | 
					using MediaBrowser.Controller.Configuration;
 | 
				
			||||||
using MediaBrowser.Controller.Net;
 | 
					using MediaBrowser.Controller.Net;
 | 
				
			||||||
using MediaBrowser.Model.Events;
 | 
					 | 
				
			||||||
using MediaBrowser.Model.Globalization;
 | 
					using MediaBrowser.Model.Globalization;
 | 
				
			||||||
using MediaBrowser.Model.Serialization;
 | 
					using MediaBrowser.Model.Serialization;
 | 
				
			||||||
using MediaBrowser.Model.Services;
 | 
					using MediaBrowser.Model.Services;
 | 
				
			||||||
 | 
				
			|||||||
@ -179,7 +179,7 @@ namespace Emby.Server.Implementations.HttpServer
 | 
				
			|||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            WebSocketMessage<object> stub;
 | 
					            WebSocketMessage<object>? stub;
 | 
				
			||||||
            try
 | 
					            try
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -209,6 +209,12 @@ namespace Emby.Server.Implementations.HttpServer
 | 
				
			|||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (stub == null)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                _logger.LogError("Error processing web socket message");
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Tell the PipeReader how much of the buffer we have consumed
 | 
					            // Tell the PipeReader how much of the buffer we have consumed
 | 
				
			||||||
            reader.AdvanceTo(buffer.End);
 | 
					            reader.AdvanceTo(buffer.End);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -6,12 +6,11 @@ using System.Collections.Generic;
 | 
				
			|||||||
using System.IO;
 | 
					using System.IO;
 | 
				
			||||||
using System.Linq;
 | 
					using System.Linq;
 | 
				
			||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using Emby.Server.Implementations.Library;
 | 
				
			||||||
using MediaBrowser.Controller.Configuration;
 | 
					using MediaBrowser.Controller.Configuration;
 | 
				
			||||||
using MediaBrowser.Controller.Entities;
 | 
					using MediaBrowser.Controller.Entities;
 | 
				
			||||||
using MediaBrowser.Controller.Library;
 | 
					using MediaBrowser.Controller.Library;
 | 
				
			||||||
using MediaBrowser.Controller.Plugins;
 | 
					 | 
				
			||||||
using MediaBrowser.Model.IO;
 | 
					using MediaBrowser.Model.IO;
 | 
				
			||||||
using Emby.Server.Implementations.Library;
 | 
					 | 
				
			||||||
using Microsoft.Extensions.Logging;
 | 
					using Microsoft.Extensions.Logging;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Emby.Server.Implementations.IO
 | 
					namespace Emby.Server.Implementations.IO
 | 
				
			||||||
@ -38,6 +37,8 @@ namespace Emby.Server.Implementations.IO
 | 
				
			|||||||
        /// </summary>
 | 
					        /// </summary>
 | 
				
			||||||
        private readonly ConcurrentDictionary<string, string> _tempIgnoredPaths = new ConcurrentDictionary<string, string>(StringComparer.OrdinalIgnoreCase);
 | 
					        private readonly ConcurrentDictionary<string, string> _tempIgnoredPaths = new ConcurrentDictionary<string, string>(StringComparer.OrdinalIgnoreCase);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private bool _disposed = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
        /// Add the path to our temporary ignore list.  Use when writing to a path within our listening scope.
 | 
					        /// Add the path to our temporary ignore list.  Use when writing to a path within our listening scope.
 | 
				
			||||||
        /// </summary>
 | 
					        /// </summary>
 | 
				
			||||||
@ -492,8 +493,6 @@ namespace Emby.Server.Implementations.IO
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private bool _disposed = false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
 | 
					        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
 | 
				
			||||||
        /// </summary>
 | 
					        /// </summary>
 | 
				
			||||||
@ -522,24 +521,4 @@ namespace Emby.Server.Implementations.IO
 | 
				
			|||||||
            _disposed = true;
 | 
					            _disposed = true;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    public class LibraryMonitorStartup : IServerEntryPoint
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        private readonly ILibraryMonitor _monitor;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public LibraryMonitorStartup(ILibraryMonitor monitor)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _monitor = monitor;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public Task RunAsync()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _monitor.Start();
 | 
					 | 
				
			||||||
            return Task.CompletedTask;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public void Dispose()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										35
									
								
								Emby.Server.Implementations/IO/LibraryMonitorStartup.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								Emby.Server.Implementations/IO/LibraryMonitorStartup.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,35 @@
 | 
				
			|||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using MediaBrowser.Controller.Library;
 | 
				
			||||||
 | 
					using MediaBrowser.Controller.Plugins;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Emby.Server.Implementations.IO
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /// <summary>
 | 
				
			||||||
 | 
					    /// <see cref="IServerEntryPoint" /> which is responsible for starting the library monitor.
 | 
				
			||||||
 | 
					    /// </summary>
 | 
				
			||||||
 | 
					    public sealed class LibraryMonitorStartup : IServerEntryPoint
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private readonly ILibraryMonitor _monitor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// Initializes a new instance of the <see cref="LibraryMonitorStartup"/> class.
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        /// <param name="monitor">The library monitor.</param>
 | 
				
			||||||
 | 
					        public LibraryMonitorStartup(ILibraryMonitor monitor)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _monitor = monitor;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <inheritdoc />
 | 
				
			||||||
 | 
					        public Task RunAsync()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _monitor.Start();
 | 
				
			||||||
 | 
					            return Task.CompletedTask;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <inheritdoc />
 | 
				
			||||||
 | 
					        public void Dispose()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -729,7 +729,7 @@ namespace Emby.Server.Implementations.Library
 | 
				
			|||||||
            Directory.CreateDirectory(rootFolderPath);
 | 
					            Directory.CreateDirectory(rootFolderPath);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var rootFolder = GetItemById(GetNewItemId(rootFolderPath, typeof(AggregateFolder))) as AggregateFolder ??
 | 
					            var rootFolder = GetItemById(GetNewItemId(rootFolderPath, typeof(AggregateFolder))) as AggregateFolder ??
 | 
				
			||||||
                             ((Folder) ResolvePath(_fileSystem.GetDirectoryInfo(rootFolderPath)))
 | 
					                             ((Folder)ResolvePath(_fileSystem.GetDirectoryInfo(rootFolderPath)))
 | 
				
			||||||
                             .DeepCopy<Folder, AggregateFolder>();
 | 
					                             .DeepCopy<Folder, AggregateFolder>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // In case program data folder was moved
 | 
					            // In case program data folder was moved
 | 
				
			||||||
@ -771,7 +771,7 @@ namespace Emby.Server.Implementations.Library
 | 
				
			|||||||
            if (folder.ParentId != rootFolder.Id)
 | 
					            if (folder.ParentId != rootFolder.Id)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                folder.ParentId = rootFolder.Id;
 | 
					                folder.ParentId = rootFolder.Id;
 | 
				
			||||||
                folder.UpdateToRepository(ItemUpdateType.MetadataImport, CancellationToken.None);
 | 
					                folder.UpdateToRepositoryAsync(ItemUpdateType.MetadataImport, CancellationToken.None).GetAwaiter().GetResult();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            rootFolder.AddVirtualChild(folder);
 | 
					            rootFolder.AddVirtualChild(folder);
 | 
				
			||||||
@ -1868,7 +1868,8 @@ namespace Emby.Server.Implementations.Library
 | 
				
			|||||||
            return image.Path != null && !image.IsLocalFile;
 | 
					            return image.Path != null && !image.IsLocalFile;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public void UpdateImages(BaseItem item, bool forceUpdate = false)
 | 
					        /// <inheritdoc />
 | 
				
			||||||
 | 
					        public async Task UpdateImagesAsync(BaseItem item, bool forceUpdate = false)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (item == null)
 | 
					            if (item == null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@ -1891,7 +1892,7 @@ namespace Emby.Server.Implementations.Library
 | 
				
			|||||||
                    try
 | 
					                    try
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        var index = item.GetImageIndex(img);
 | 
					                        var index = item.GetImageIndex(img);
 | 
				
			||||||
                        image = ConvertImageToLocal(item, img, index).ConfigureAwait(false).GetAwaiter().GetResult();
 | 
					                        image = await ConvertImageToLocal(item, img, index).ConfigureAwait(false);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    catch (ArgumentException)
 | 
					                    catch (ArgumentException)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
@ -1913,7 +1914,7 @@ namespace Emby.Server.Implementations.Library
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
                catch (Exception ex)
 | 
					                catch (Exception ex)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    _logger.LogError(ex, "Cannnot get image dimensions for {0}", image.Path);
 | 
					                    _logger.LogError(ex, "Cannot get image dimensions for {0}", image.Path);
 | 
				
			||||||
                    image.Width = 0;
 | 
					                    image.Width = 0;
 | 
				
			||||||
                    image.Height = 0;
 | 
					                    image.Height = 0;
 | 
				
			||||||
                    continue;
 | 
					                    continue;
 | 
				
			||||||
@ -1943,10 +1944,8 @@ namespace Emby.Server.Implementations.Library
 | 
				
			|||||||
            RegisterItem(item);
 | 
					            RegisterItem(item);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <summary>
 | 
					        /// <inheritdoc />
 | 
				
			||||||
        /// Updates the item.
 | 
					        public async Task UpdateItemsAsync(IReadOnlyList<BaseItem> items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken)
 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        public void UpdateItems(IReadOnlyList<BaseItem> items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken)
 | 
					 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            foreach (var item in items)
 | 
					            foreach (var item in items)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@ -1957,7 +1956,7 @@ namespace Emby.Server.Implementations.Library
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                item.DateLastSaved = DateTime.UtcNow;
 | 
					                item.DateLastSaved = DateTime.UtcNow;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                UpdateImages(item, updateReason >= ItemUpdateType.ImageUpdate);
 | 
					                await UpdateImagesAsync(item, updateReason >= ItemUpdateType.ImageUpdate).ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            _itemRepository.SaveItems(items, cancellationToken);
 | 
					            _itemRepository.SaveItems(items, cancellationToken);
 | 
				
			||||||
@ -1991,17 +1990,9 @@ namespace Emby.Server.Implementations.Library
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <summary>
 | 
					        /// <inheritdoc />
 | 
				
			||||||
        /// Updates the item.
 | 
					        public Task UpdateItemAsync(BaseItem item, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken)
 | 
				
			||||||
        /// </summary>
 | 
					            => UpdateItemsAsync(new[] { item }, parent, updateReason, cancellationToken);
 | 
				
			||||||
        /// <param name="item">The item.</param>
 | 
					 | 
				
			||||||
        /// <param name="parent">The parent item.</param>
 | 
					 | 
				
			||||||
        /// <param name="updateReason">The update reason.</param>
 | 
					 | 
				
			||||||
        /// <param name="cancellationToken">The cancellation token.</param>
 | 
					 | 
				
			||||||
        public void UpdateItem(BaseItem item, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            UpdateItems(new[] { item }, parent, updateReason, cancellationToken);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
        /// Reports the item removed.
 | 
					        /// Reports the item removed.
 | 
				
			||||||
@ -2233,7 +2224,7 @@ namespace Emby.Server.Implementations.Library
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            if (refresh)
 | 
					            if (refresh)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                item.UpdateToRepository(ItemUpdateType.MetadataImport, CancellationToken.None);
 | 
					                item.UpdateToRepositoryAsync(ItemUpdateType.MetadataImport, CancellationToken.None).GetAwaiter().GetResult();
 | 
				
			||||||
                ProviderManager.QueueRefresh(item.Id, new MetadataRefreshOptions(new DirectoryService(_fileSystem)), RefreshPriority.Normal);
 | 
					                ProviderManager.QueueRefresh(item.Id, new MetadataRefreshOptions(new DirectoryService(_fileSystem)), RefreshPriority.Normal);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -2420,7 +2411,7 @@ namespace Emby.Server.Implementations.Library
 | 
				
			|||||||
            if (!string.Equals(viewType, item.ViewType, StringComparison.OrdinalIgnoreCase))
 | 
					            if (!string.Equals(viewType, item.ViewType, StringComparison.OrdinalIgnoreCase))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                item.ViewType = viewType;
 | 
					                item.ViewType = viewType;
 | 
				
			||||||
                item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
 | 
					                item.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).GetAwaiter().GetResult();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var refresh = isNew || DateTime.UtcNow - item.DateLastRefreshed >= _viewRefreshInterval;
 | 
					            var refresh = isNew || DateTime.UtcNow - item.DateLastRefreshed >= _viewRefreshInterval;
 | 
				
			||||||
@ -2902,7 +2893,7 @@ namespace Emby.Server.Implementations.Library
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    await ProviderManager.SaveImage(item, url, image.Type, imageIndex, CancellationToken.None).ConfigureAwait(false);
 | 
					                    await ProviderManager.SaveImage(item, url, image.Type, imageIndex, CancellationToken.None).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    item.UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None);
 | 
					                    await item.UpdateToRepositoryAsync(ItemUpdateType.ImageUpdate, CancellationToken.None).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    return item.GetImageInfo(image.Type, imageIndex);
 | 
					                    return item.GetImageInfo(image.Type, imageIndex);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@ -2920,7 +2911,7 @@ namespace Emby.Server.Implementations.Library
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            // Remove this image to prevent it from retrying over and over
 | 
					            // Remove this image to prevent it from retrying over and over
 | 
				
			||||||
            item.RemoveImage(image);
 | 
					            item.RemoveImage(image);
 | 
				
			||||||
            item.UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None);
 | 
					            await item.UpdateToRepositoryAsync(ItemUpdateType.ImageUpdate, CancellationToken.None).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            throw new InvalidOperationException();
 | 
					            throw new InvalidOperationException();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
				
			|||||||
@ -13,6 +13,7 @@ using System.Threading.Tasks;
 | 
				
			|||||||
using System.Xml;
 | 
					using System.Xml;
 | 
				
			||||||
using Emby.Server.Implementations.Library;
 | 
					using Emby.Server.Implementations.Library;
 | 
				
			||||||
using Jellyfin.Data.Enums;
 | 
					using Jellyfin.Data.Enums;
 | 
				
			||||||
 | 
					using Jellyfin.Data.Events;
 | 
				
			||||||
using MediaBrowser.Common.Configuration;
 | 
					using MediaBrowser.Common.Configuration;
 | 
				
			||||||
using MediaBrowser.Common.Extensions;
 | 
					using MediaBrowser.Common.Extensions;
 | 
				
			||||||
using MediaBrowser.Common.Net;
 | 
					using MediaBrowser.Common.Net;
 | 
				
			||||||
@ -29,7 +30,6 @@ using MediaBrowser.Controller.Providers;
 | 
				
			|||||||
using MediaBrowser.Model.Configuration;
 | 
					using MediaBrowser.Model.Configuration;
 | 
				
			||||||
using MediaBrowser.Model.Dto;
 | 
					using MediaBrowser.Model.Dto;
 | 
				
			||||||
using MediaBrowser.Model.Entities;
 | 
					using MediaBrowser.Model.Entities;
 | 
				
			||||||
using MediaBrowser.Model.Events;
 | 
					 | 
				
			||||||
using MediaBrowser.Model.IO;
 | 
					using MediaBrowser.Model.IO;
 | 
				
			||||||
using MediaBrowser.Model.LiveTv;
 | 
					using MediaBrowser.Model.LiveTv;
 | 
				
			||||||
using MediaBrowser.Model.MediaInfo;
 | 
					using MediaBrowser.Model.MediaInfo;
 | 
				
			||||||
 | 
				
			|||||||
@ -230,7 +230,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            if (filters.Count > 0)
 | 
					            if (filters.Count > 0)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                output += string.Format(" -vf \"{0}\"", string.Join(",", filters.ToArray()));
 | 
					                output += string.Format(CultureInfo.InvariantCulture, " -vf \"{0}\"", string.Join(",", filters.ToArray()));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return output;
 | 
					            return output;
 | 
				
			||||||
 | 
				
			|||||||
@ -5,7 +5,7 @@ using MediaBrowser.Controller.Plugins;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace Emby.Server.Implementations.LiveTv.EmbyTV
 | 
					namespace Emby.Server.Implementations.LiveTv.EmbyTV
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public class EntryPoint : IServerEntryPoint
 | 
					    public sealed class EntryPoint : IServerEntryPoint
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        /// <inheritdoc />
 | 
					        /// <inheritdoc />
 | 
				
			||||||
        public Task RunAsync()
 | 
					        public Task RunAsync()
 | 
				
			||||||
 | 
				
			|||||||
@ -5,8 +5,8 @@ using System.Collections.Concurrent;
 | 
				
			|||||||
using System.Globalization;
 | 
					using System.Globalization;
 | 
				
			||||||
using System.Linq;
 | 
					using System.Linq;
 | 
				
			||||||
using System.Threading;
 | 
					using System.Threading;
 | 
				
			||||||
 | 
					using Jellyfin.Data.Events;
 | 
				
			||||||
using MediaBrowser.Controller.LiveTv;
 | 
					using MediaBrowser.Controller.LiveTv;
 | 
				
			||||||
using MediaBrowser.Model.Events;
 | 
					 | 
				
			||||||
using MediaBrowser.Model.LiveTv;
 | 
					using MediaBrowser.Model.LiveTv;
 | 
				
			||||||
using MediaBrowser.Model.Serialization;
 | 
					using MediaBrowser.Model.Serialization;
 | 
				
			||||||
using Microsoft.Extensions.Logging;
 | 
					using Microsoft.Extensions.Logging;
 | 
				
			||||||
 | 
				
			|||||||
@ -929,7 +929,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        private static string NormalizeName(string value)
 | 
					        private static string NormalizeName(string value)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return value.Replace(" ", string.Empty).Replace("-", string.Empty);
 | 
					            return value.Replace(" ", string.Empty, StringComparison.Ordinal).Replace("-", string.Empty, StringComparison.Ordinal);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public class ScheduleDirect
 | 
					        public class ScheduleDirect
 | 
				
			||||||
 | 
				
			|||||||
@ -237,7 +237,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
 | 
				
			|||||||
                    && !programInfo.IsRepeat
 | 
					                    && !programInfo.IsRepeat
 | 
				
			||||||
                    && (programInfo.EpisodeNumber ?? 0) == 0)
 | 
					                    && (programInfo.EpisodeNumber ?? 0) == 0)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    programInfo.ShowId = programInfo.ShowId + programInfo.StartDate.Ticks.ToString(CultureInfo.InvariantCulture);
 | 
					                    programInfo.ShowId += programInfo.StartDate.Ticks.ToString(CultureInfo.InvariantCulture);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
@ -246,7 +246,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Construct an id from the channel and start date
 | 
					            // Construct an id from the channel and start date
 | 
				
			||||||
            programInfo.Id = string.Format("{0}_{1:O}", program.ChannelId, program.StartDate);
 | 
					            programInfo.Id = string.Format(CultureInfo.InvariantCulture, "{0}_{1:O}", program.ChannelId, program.StartDate);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (programInfo.IsMovie)
 | 
					            if (programInfo.IsMovie)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@ -296,7 +296,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings
 | 
				
			|||||||
                Name = c.DisplayName,
 | 
					                Name = c.DisplayName,
 | 
				
			||||||
                ImageUrl = c.Icon != null && !string.IsNullOrEmpty(c.Icon.Source) ? c.Icon.Source : null,
 | 
					                ImageUrl = c.Icon != null && !string.IsNullOrEmpty(c.Icon.Source) ? c.Icon.Source : null,
 | 
				
			||||||
                Number = string.IsNullOrWhiteSpace(c.Number) ? c.Id : c.Number
 | 
					                Number = string.IsNullOrWhiteSpace(c.Number) ? c.Id : c.Number
 | 
				
			||||||
 | 
					 | 
				
			||||||
            }).ToList();
 | 
					            }).ToList();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -10,6 +10,7 @@ using System.Threading.Tasks;
 | 
				
			|||||||
using Emby.Server.Implementations.Library;
 | 
					using Emby.Server.Implementations.Library;
 | 
				
			||||||
using Jellyfin.Data.Entities;
 | 
					using Jellyfin.Data.Entities;
 | 
				
			||||||
using Jellyfin.Data.Enums;
 | 
					using Jellyfin.Data.Enums;
 | 
				
			||||||
 | 
					using Jellyfin.Data.Events;
 | 
				
			||||||
using MediaBrowser.Common.Configuration;
 | 
					using MediaBrowser.Common.Configuration;
 | 
				
			||||||
using MediaBrowser.Common.Extensions;
 | 
					using MediaBrowser.Common.Extensions;
 | 
				
			||||||
using MediaBrowser.Common.Progress;
 | 
					using MediaBrowser.Common.Progress;
 | 
				
			||||||
@ -24,7 +25,6 @@ using MediaBrowser.Controller.Providers;
 | 
				
			|||||||
using MediaBrowser.Controller.Sorting;
 | 
					using MediaBrowser.Controller.Sorting;
 | 
				
			||||||
using MediaBrowser.Model.Dto;
 | 
					using MediaBrowser.Model.Dto;
 | 
				
			||||||
using MediaBrowser.Model.Entities;
 | 
					using MediaBrowser.Model.Entities;
 | 
				
			||||||
using MediaBrowser.Model.Events;
 | 
					 | 
				
			||||||
using MediaBrowser.Model.Globalization;
 | 
					using MediaBrowser.Model.Globalization;
 | 
				
			||||||
using MediaBrowser.Model.IO;
 | 
					using MediaBrowser.Model.IO;
 | 
				
			||||||
using MediaBrowser.Model.LiveTv;
 | 
					using MediaBrowser.Model.LiveTv;
 | 
				
			||||||
@ -41,6 +41,7 @@ namespace Emby.Server.Implementations.LiveTv
 | 
				
			|||||||
    /// </summary>
 | 
					    /// </summary>
 | 
				
			||||||
    public class LiveTvManager : ILiveTvManager, IDisposable
 | 
					    public class LiveTvManager : ILiveTvManager, IDisposable
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					        private const int MaxGuideDays = 14;
 | 
				
			||||||
        private const string ExternalServiceTag = "ExternalServiceId";
 | 
					        private const string ExternalServiceTag = "ExternalServiceId";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private const string EtagKey = "ProgramEtag";
 | 
					        private const string EtagKey = "ProgramEtag";
 | 
				
			||||||
@ -421,7 +422,7 @@ namespace Emby.Server.Implementations.LiveTv
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private LiveTvChannel GetChannel(ChannelInfo channelInfo, string serviceName, BaseItem parentFolder, CancellationToken cancellationToken)
 | 
					        private async Task<LiveTvChannel> GetChannelAsync(ChannelInfo channelInfo, string serviceName, BaseItem parentFolder, CancellationToken cancellationToken)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var parentFolderId = parentFolder.Id;
 | 
					            var parentFolderId = parentFolder.Id;
 | 
				
			||||||
            var isNew = false;
 | 
					            var isNew = false;
 | 
				
			||||||
@ -511,7 +512,7 @@ namespace Emby.Server.Implementations.LiveTv
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
            else if (forceUpdate)
 | 
					            else if (forceUpdate)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                _libraryManager.UpdateItem(item, parentFolder, ItemUpdateType.MetadataImport, cancellationToken);
 | 
					                await _libraryManager.UpdateItemAsync(item, parentFolder, ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return item;
 | 
					            return item;
 | 
				
			||||||
@ -560,7 +561,7 @@ namespace Emby.Server.Implementations.LiveTv
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            item.Audio = info.Audio;
 | 
					            item.Audio = info.Audio;
 | 
				
			||||||
            item.ChannelId = channel.Id;
 | 
					            item.ChannelId = channel.Id;
 | 
				
			||||||
            item.CommunityRating = item.CommunityRating ?? info.CommunityRating;
 | 
					            item.CommunityRating ??= info.CommunityRating;
 | 
				
			||||||
            if ((item.CommunityRating ?? 0).Equals(0))
 | 
					            if ((item.CommunityRating ?? 0).Equals(0))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                item.CommunityRating = null;
 | 
					                item.CommunityRating = null;
 | 
				
			||||||
@ -645,8 +646,8 @@ namespace Emby.Server.Implementations.LiveTv
 | 
				
			|||||||
            item.IsSeries = isSeries;
 | 
					            item.IsSeries = isSeries;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            item.Name = info.Name;
 | 
					            item.Name = info.Name;
 | 
				
			||||||
            item.OfficialRating = item.OfficialRating ?? info.OfficialRating;
 | 
					            item.OfficialRating ??= info.OfficialRating;
 | 
				
			||||||
            item.Overview = item.Overview ?? info.Overview;
 | 
					            item.Overview ??= info.Overview;
 | 
				
			||||||
            item.RunTimeTicks = (info.EndDate - info.StartDate).Ticks;
 | 
					            item.RunTimeTicks = (info.EndDate - info.StartDate).Ticks;
 | 
				
			||||||
            item.ProviderIds = info.ProviderIds;
 | 
					            item.ProviderIds = info.ProviderIds;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -683,19 +684,23 @@ namespace Emby.Server.Implementations.LiveTv
 | 
				
			|||||||
            {
 | 
					            {
 | 
				
			||||||
                if (!string.IsNullOrWhiteSpace(info.ImagePath))
 | 
					                if (!string.IsNullOrWhiteSpace(info.ImagePath))
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    item.SetImage(new ItemImageInfo
 | 
					                    item.SetImage(
 | 
				
			||||||
 | 
					                        new ItemImageInfo
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
                            Path = info.ImagePath,
 | 
					                            Path = info.ImagePath,
 | 
				
			||||||
                            Type = ImageType.Primary
 | 
					                            Type = ImageType.Primary
 | 
				
			||||||
                    }, 0);
 | 
					                        },
 | 
				
			||||||
 | 
					                        0);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                else if (!string.IsNullOrWhiteSpace(info.ImageUrl))
 | 
					                else if (!string.IsNullOrWhiteSpace(info.ImageUrl))
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    item.SetImage(new ItemImageInfo
 | 
					                    item.SetImage(
 | 
				
			||||||
 | 
					                        new ItemImageInfo
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
                            Path = info.ImageUrl,
 | 
					                            Path = info.ImageUrl,
 | 
				
			||||||
                            Type = ImageType.Primary
 | 
					                            Type = ImageType.Primary
 | 
				
			||||||
                    }, 0);
 | 
					                        },
 | 
				
			||||||
 | 
					                        0);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -703,11 +708,13 @@ namespace Emby.Server.Implementations.LiveTv
 | 
				
			|||||||
            {
 | 
					            {
 | 
				
			||||||
                if (!string.IsNullOrWhiteSpace(info.ThumbImageUrl))
 | 
					                if (!string.IsNullOrWhiteSpace(info.ThumbImageUrl))
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    item.SetImage(new ItemImageInfo
 | 
					                    item.SetImage(
 | 
				
			||||||
 | 
					                        new ItemImageInfo
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
                            Path = info.ThumbImageUrl,
 | 
					                            Path = info.ThumbImageUrl,
 | 
				
			||||||
                            Type = ImageType.Thumb
 | 
					                            Type = ImageType.Thumb
 | 
				
			||||||
                    }, 0);
 | 
					                        },
 | 
				
			||||||
 | 
					                        0);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -715,11 +722,13 @@ namespace Emby.Server.Implementations.LiveTv
 | 
				
			|||||||
            {
 | 
					            {
 | 
				
			||||||
                if (!string.IsNullOrWhiteSpace(info.LogoImageUrl))
 | 
					                if (!string.IsNullOrWhiteSpace(info.LogoImageUrl))
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    item.SetImage(new ItemImageInfo
 | 
					                    item.SetImage(
 | 
				
			||||||
 | 
					                        new ItemImageInfo
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
                            Path = info.LogoImageUrl,
 | 
					                            Path = info.LogoImageUrl,
 | 
				
			||||||
                            Type = ImageType.Logo
 | 
					                            Type = ImageType.Logo
 | 
				
			||||||
                    }, 0);
 | 
					                        },
 | 
				
			||||||
 | 
					                        0);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -727,11 +736,13 @@ namespace Emby.Server.Implementations.LiveTv
 | 
				
			|||||||
            {
 | 
					            {
 | 
				
			||||||
                if (!string.IsNullOrWhiteSpace(info.BackdropImageUrl))
 | 
					                if (!string.IsNullOrWhiteSpace(info.BackdropImageUrl))
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    item.SetImage(new ItemImageInfo
 | 
					                    item.SetImage(
 | 
				
			||||||
 | 
					                        new ItemImageInfo
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
                            Path = info.BackdropImageUrl,
 | 
					                            Path = info.BackdropImageUrl,
 | 
				
			||||||
                            Type = ImageType.Backdrop
 | 
					                            Type = ImageType.Backdrop
 | 
				
			||||||
                    }, 0);
 | 
					                        },
 | 
				
			||||||
 | 
					                        0);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -786,7 +797,6 @@ namespace Emby.Server.Implementations.LiveTv
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            if (query.OrderBy.Count == 0)
 | 
					            if (query.OrderBy.Count == 0)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
 | 
					 | 
				
			||||||
                // Unless something else was specified, order by start date to take advantage of a specialized index
 | 
					                // Unless something else was specified, order by start date to take advantage of a specialized index
 | 
				
			||||||
                query.OrderBy = new[]
 | 
					                query.OrderBy = new[]
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
@ -824,7 +834,7 @@ namespace Emby.Server.Implementations.LiveTv
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            if (!string.IsNullOrWhiteSpace(query.SeriesTimerId))
 | 
					            if (!string.IsNullOrWhiteSpace(query.SeriesTimerId))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var seriesTimers = await GetSeriesTimersInternal(new SeriesTimerQuery { }, cancellationToken).ConfigureAwait(false);
 | 
					                var seriesTimers = await GetSeriesTimersInternal(new SeriesTimerQuery(), cancellationToken).ConfigureAwait(false);
 | 
				
			||||||
                var seriesTimer = seriesTimers.Items.FirstOrDefault(i => string.Equals(_tvDtoService.GetInternalSeriesTimerId(i.Id).ToString("N", CultureInfo.InvariantCulture), query.SeriesTimerId, StringComparison.OrdinalIgnoreCase));
 | 
					                var seriesTimer = seriesTimers.Items.FirstOrDefault(i => string.Equals(_tvDtoService.GetInternalSeriesTimerId(i.Id).ToString("N", CultureInfo.InvariantCulture), query.SeriesTimerId, StringComparison.OrdinalIgnoreCase));
 | 
				
			||||||
                if (seriesTimer != null)
 | 
					                if (seriesTimer != null)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
@ -847,13 +857,11 @@ namespace Emby.Server.Implementations.LiveTv
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            var returnArray = _dtoService.GetBaseItemDtos(queryResult.Items, options, user);
 | 
					            var returnArray = _dtoService.GetBaseItemDtos(queryResult.Items, options, user);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var result = new QueryResult<BaseItemDto>
 | 
					            return new QueryResult<BaseItemDto>
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                Items = returnArray,
 | 
					                Items = returnArray,
 | 
				
			||||||
                TotalRecordCount = queryResult.TotalRecordCount
 | 
					                TotalRecordCount = queryResult.TotalRecordCount
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
 | 
					 | 
				
			||||||
            return result;
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public QueryResult<BaseItem> GetRecommendedProgramsInternal(InternalItemsQuery query, DtoOptions options, CancellationToken cancellationToken)
 | 
					        public QueryResult<BaseItem> GetRecommendedProgramsInternal(InternalItemsQuery query, DtoOptions options, CancellationToken cancellationToken)
 | 
				
			||||||
@ -1121,7 +1129,7 @@ namespace Emby.Server.Implementations.LiveTv
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                try
 | 
					                try
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    var item = GetChannel(channelInfo.Item2, channelInfo.Item1, parentFolder, cancellationToken);
 | 
					                    var item = await GetChannelAsync(channelInfo.Item2, channelInfo.Item1, parentFolder, cancellationToken).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    list.Add(item);
 | 
					                    list.Add(item);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@ -1138,7 +1146,7 @@ namespace Emby.Server.Implementations.LiveTv
 | 
				
			|||||||
                double percent = numComplete;
 | 
					                double percent = numComplete;
 | 
				
			||||||
                percent /= allChannelsList.Count;
 | 
					                percent /= allChannelsList.Count;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                progress.Report(5 * percent + 10);
 | 
					                progress.Report((5 * percent) + 10);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            progress.Report(15);
 | 
					            progress.Report(15);
 | 
				
			||||||
@ -1173,7 +1181,6 @@ namespace Emby.Server.Implementations.LiveTv
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    var existingPrograms = _libraryManager.GetItemList(new InternalItemsQuery
 | 
					                    var existingPrograms = _libraryManager.GetItemList(new InternalItemsQuery
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
 | 
					 | 
				
			||||||
                        IncludeItemTypes = new string[] { typeof(LiveTvProgram).Name },
 | 
					                        IncludeItemTypes = new string[] { typeof(LiveTvProgram).Name },
 | 
				
			||||||
                        ChannelIds = new Guid[] { currentChannel.Id },
 | 
					                        ChannelIds = new Guid[] { currentChannel.Id },
 | 
				
			||||||
                        DtoOptions = new DtoOptions(true)
 | 
					                        DtoOptions = new DtoOptions(true)
 | 
				
			||||||
@ -1214,7 +1221,11 @@ namespace Emby.Server.Implementations.LiveTv
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    if (updatedPrograms.Count > 0)
 | 
					                    if (updatedPrograms.Count > 0)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        _libraryManager.UpdateItems(updatedPrograms, currentChannel, ItemUpdateType.MetadataImport, cancellationToken);
 | 
					                        await _libraryManager.UpdateItemsAsync(
 | 
				
			||||||
 | 
					                            updatedPrograms,
 | 
				
			||||||
 | 
					                            currentChannel,
 | 
				
			||||||
 | 
					                            ItemUpdateType.MetadataImport,
 | 
				
			||||||
 | 
					                            cancellationToken).ConfigureAwait(false);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    currentChannel.IsMovie = isMovie;
 | 
					                    currentChannel.IsMovie = isMovie;
 | 
				
			||||||
@ -1227,7 +1238,7 @@ namespace Emby.Server.Implementations.LiveTv
 | 
				
			|||||||
                        currentChannel.AddTag("Kids");
 | 
					                        currentChannel.AddTag("Kids");
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    currentChannel.UpdateToRepository(ItemUpdateType.MetadataImport, cancellationToken);
 | 
					                    await currentChannel.UpdateToRepositoryAsync(ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false);
 | 
				
			||||||
                    await currentChannel.RefreshMetadata(
 | 
					                    await currentChannel.RefreshMetadata(
 | 
				
			||||||
                        new MetadataRefreshOptions(new DirectoryService(_fileSystem))
 | 
					                        new MetadataRefreshOptions(new DirectoryService(_fileSystem))
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@ -1298,8 +1309,6 @@ namespace Emby.Server.Implementations.LiveTv
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private const int MaxGuideDays = 14;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private double GetGuideDays()
 | 
					        private double GetGuideDays()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var config = GetConfiguration();
 | 
					            var config = GetConfiguration();
 | 
				
			||||||
@ -1712,7 +1721,7 @@ namespace Emby.Server.Implementations.LiveTv
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            if (timer == null)
 | 
					            if (timer == null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                throw new ResourceNotFoundException(string.Format("Timer with Id {0} not found", id));
 | 
					                throw new ResourceNotFoundException(string.Format(CultureInfo.InvariantCulture, "Timer with Id {0} not found", id));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var service = GetService(timer.ServiceName);
 | 
					            var service = GetService(timer.ServiceName);
 | 
				
			||||||
@ -1731,7 +1740,7 @@ namespace Emby.Server.Implementations.LiveTv
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            if (timer == null)
 | 
					            if (timer == null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                throw new ResourceNotFoundException(string.Format("SeriesTimer with Id {0} not found", id));
 | 
					                throw new ResourceNotFoundException(string.Format(CultureInfo.InvariantCulture, "SeriesTimer with Id {0} not found", id));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var service = GetService(timer.ServiceName);
 | 
					            var service = GetService(timer.ServiceName);
 | 
				
			||||||
@ -1743,10 +1752,12 @@ namespace Emby.Server.Implementations.LiveTv
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        public async Task<TimerInfoDto> GetTimer(string id, CancellationToken cancellationToken)
 | 
					        public async Task<TimerInfoDto> GetTimer(string id, CancellationToken cancellationToken)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var results = await GetTimers(new TimerQuery
 | 
					            var results = await GetTimers(
 | 
				
			||||||
 | 
					                new TimerQuery
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Id = id
 | 
					                    Id = id
 | 
				
			||||||
            }, cancellationToken).ConfigureAwait(false);
 | 
					                },
 | 
				
			||||||
 | 
					                cancellationToken).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return results.Items.FirstOrDefault(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase));
 | 
					            return results.Items.FirstOrDefault(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -1794,10 +1805,7 @@ namespace Emby.Server.Implementations.LiveTv
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var returnArray = timers
 | 
					            var returnArray = timers
 | 
				
			||||||
                .Select(i =>
 | 
					                .Select(i => i.Item1)
 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    return i.Item1;
 | 
					 | 
				
			||||||
                })
 | 
					 | 
				
			||||||
                .ToArray();
 | 
					                .ToArray();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return new QueryResult<SeriesTimerInfo>
 | 
					            return new QueryResult<SeriesTimerInfo>
 | 
				
			||||||
@ -1968,7 +1976,7 @@ namespace Emby.Server.Implementations.LiveTv
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            if (service == null)
 | 
					            if (service == null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                service = _services.First();
 | 
					                service = _services[0];
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var info = await service.GetNewTimerDefaultsAsync(cancellationToken, programInfo).ConfigureAwait(false);
 | 
					            var info = await service.GetNewTimerDefaultsAsync(cancellationToken, programInfo).ConfigureAwait(false);
 | 
				
			||||||
@ -1994,9 +2002,7 @@ namespace Emby.Server.Implementations.LiveTv
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            var info = await GetNewTimerDefaultsInternal(cancellationToken).ConfigureAwait(false);
 | 
					            var info = await GetNewTimerDefaultsInternal(cancellationToken).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var obj = _tvDtoService.GetSeriesTimerInfoDto(info.Item1, info.Item2, null);
 | 
					            return _tvDtoService.GetSeriesTimerInfoDto(info.Item1, info.Item2, null);
 | 
				
			||||||
 | 
					 | 
				
			||||||
            return obj;
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public async Task<SeriesTimerInfoDto> GetNewTimerDefaults(string programId, CancellationToken cancellationToken)
 | 
					        public async Task<SeriesTimerInfoDto> GetNewTimerDefaults(string programId, CancellationToken cancellationToken)
 | 
				
			||||||
@ -2125,6 +2131,7 @@ namespace Emby.Server.Implementations.LiveTv
 | 
				
			|||||||
        public void Dispose()
 | 
					        public void Dispose()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            Dispose(true);
 | 
					            Dispose(true);
 | 
				
			||||||
 | 
					            GC.SuppressFinalize(this);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private bool _disposed = false;
 | 
					        private bool _disposed = false;
 | 
				
			||||||
@ -2447,8 +2454,7 @@ namespace Emby.Server.Implementations.LiveTv
 | 
				
			|||||||
                .SelectMany(i => i.Locations)
 | 
					                .SelectMany(i => i.Locations)
 | 
				
			||||||
                .Distinct(StringComparer.OrdinalIgnoreCase)
 | 
					                .Distinct(StringComparer.OrdinalIgnoreCase)
 | 
				
			||||||
                .Select(i => _libraryManager.FindByPath(i, true))
 | 
					                .Select(i => _libraryManager.FindByPath(i, true))
 | 
				
			||||||
                .Where(i => i != null)
 | 
					                .Where(i => i != null && i.IsVisibleStandalone(user))
 | 
				
			||||||
                .Where(i => i.IsVisibleStandalone(user))
 | 
					 | 
				
			||||||
                .SelectMany(i => _libraryManager.GetCollectionFolders(i))
 | 
					                .SelectMany(i => _libraryManager.GetCollectionFolders(i))
 | 
				
			||||||
                .GroupBy(x => x.Id)
 | 
					                .GroupBy(x => x.Id)
 | 
				
			||||||
                .Select(x => x.First())
 | 
					                .Select(x => x.First())
 | 
				
			||||||
 | 
				
			|||||||
@ -37,6 +37,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
 | 
				
			|||||||
        private readonly INetworkManager _networkManager;
 | 
					        private readonly INetworkManager _networkManager;
 | 
				
			||||||
        private readonly IStreamHelper _streamHelper;
 | 
					        private readonly IStreamHelper _streamHelper;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private readonly Dictionary<string, DiscoverResponse> _modelCache = new Dictionary<string, DiscoverResponse>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public HdHomerunHost(
 | 
					        public HdHomerunHost(
 | 
				
			||||||
            IServerConfigurationManager config,
 | 
					            IServerConfigurationManager config,
 | 
				
			||||||
            ILogger<HdHomerunHost> logger,
 | 
					            ILogger<HdHomerunHost> logger,
 | 
				
			||||||
@ -114,7 +116,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
 | 
				
			|||||||
            }).Cast<ChannelInfo>().ToList();
 | 
					            }).Cast<ChannelInfo>().ToList();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private readonly Dictionary<string, DiscoverResponse> _modelCache = new Dictionary<string, DiscoverResponse>();
 | 
					 | 
				
			||||||
        private async Task<DiscoverResponse> GetModelInfo(TunerHostInfo info, bool throwAllExceptions, CancellationToken cancellationToken)
 | 
					        private async Task<DiscoverResponse> GetModelInfo(TunerHostInfo info, bool throwAllExceptions, CancellationToken cancellationToken)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var cacheKey = info.Id;
 | 
					            var cacheKey = info.Id;
 | 
				
			||||||
@ -157,10 +158,10 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
 | 
				
			|||||||
            {
 | 
					            {
 | 
				
			||||||
                if (!throwAllExceptions && ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.NotFound)
 | 
					                if (!throwAllExceptions && ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.NotFound)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    var defaultValue = "HDHR";
 | 
					                    const string DefaultValue = "HDHR";
 | 
				
			||||||
                    var response = new DiscoverResponse
 | 
					                    var response = new DiscoverResponse
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        ModelNumber = defaultValue
 | 
					                        ModelNumber = DefaultValue
 | 
				
			||||||
                    };
 | 
					                    };
 | 
				
			||||||
                    if (!string.IsNullOrEmpty(cacheKey))
 | 
					                    if (!string.IsNullOrEmpty(cacheKey))
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
@ -182,12 +183,14 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            var model = await GetModelInfo(info, false, cancellationToken).ConfigureAwait(false);
 | 
					            var model = await GetModelInfo(info, false, cancellationToken).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            using (var response = await _httpClient.SendAsync(new HttpRequestOptions()
 | 
					            using (var response = await _httpClient.SendAsync(
 | 
				
			||||||
 | 
					                new HttpRequestOptions()
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                Url = string.Format("{0}/tuners.html", GetApiUrl(info)),
 | 
					                    Url = string.Format(CultureInfo.InvariantCulture, "{0}/tuners.html", GetApiUrl(info)),
 | 
				
			||||||
                    CancellationToken = cancellationToken,
 | 
					                    CancellationToken = cancellationToken,
 | 
				
			||||||
                    BufferContent = false
 | 
					                    BufferContent = false
 | 
				
			||||||
            }, HttpMethod.Get).ConfigureAwait(false))
 | 
					                },
 | 
				
			||||||
 | 
					                HttpMethod.Get).ConfigureAwait(false))
 | 
				
			||||||
            using (var stream = response.Content)
 | 
					            using (var stream = response.Content)
 | 
				
			||||||
            using (var sr = new StreamReader(stream, System.Text.Encoding.UTF8))
 | 
					            using (var sr = new StreamReader(stream, System.Text.Encoding.UTF8))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@ -730,7 +733,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
 | 
				
			|||||||
                // Need a way to set the Receive timeout on the socket otherwise this might never timeout?
 | 
					                // Need a way to set the Receive timeout on the socket otherwise this might never timeout?
 | 
				
			||||||
                try
 | 
					                try
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    await udpClient.SendToAsync(discBytes, 0, discBytes.Length, new IPEndPoint(IPAddress.Parse("255.255.255.255"), 65001), cancellationToken);
 | 
					                    await udpClient.SendToAsync(discBytes, 0, discBytes.Length, new IPEndPoint(IPAddress.Parse("255.255.255.255"), 65001), cancellationToken).ConfigureAwait(false);
 | 
				
			||||||
                    var receiveBuffer = new byte[8192];
 | 
					                    var receiveBuffer = new byte[8192];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    while (!cancellationToken.IsCancellationRequested)
 | 
					                    while (!cancellationToken.IsCancellationRequested)
 | 
				
			||||||
 | 
				
			|||||||
@ -17,5 +17,8 @@
 | 
				
			|||||||
    "Genres": "Géneros",
 | 
					    "Genres": "Géneros",
 | 
				
			||||||
    "Folders": "Carpetas",
 | 
					    "Folders": "Carpetas",
 | 
				
			||||||
    "Favorites": "Favoritos",
 | 
					    "Favorites": "Favoritos",
 | 
				
			||||||
    "FailedLoginAttemptWithUserName": "Intento de inicio de sesión fallido de {0}"
 | 
					    "FailedLoginAttemptWithUserName": "Intento de inicio de sesión fallido de {0}",
 | 
				
			||||||
 | 
					    "HeaderFavoriteSongs": "Canciones Favoritas",
 | 
				
			||||||
 | 
					    "HeaderFavoriteEpisodes": "Episodios Favoritos",
 | 
				
			||||||
 | 
					    "HeaderFavoriteArtists": "Artistas Favoritos"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    "Albums": "Album",
 | 
					    "Albums": "Album",
 | 
				
			||||||
    "AuthenticationSucceededWithUserName": "{0} berhasil diautentikasi",
 | 
					    "AuthenticationSucceededWithUserName": "{0} berhasil diautentikasi",
 | 
				
			||||||
    "AppDeviceValues": "Aplikasi: {0}, Alat: {1}",
 | 
					    "AppDeviceValues": "Aplikasi : {0}, Alat : {1}",
 | 
				
			||||||
    "LabelRunningTimeValue": "Waktu berjalan: {0}",
 | 
					    "LabelRunningTimeValue": "Waktu berjalan: {0}",
 | 
				
			||||||
    "MessageApplicationUpdatedTo": "Jellyfin Server sudah diperbarui ke {0}",
 | 
					    "MessageApplicationUpdatedTo": "Jellyfin Server sudah diperbarui ke {0}",
 | 
				
			||||||
    "MessageApplicationUpdated": "Jellyfin Server sudah diperbarui",
 | 
					    "MessageApplicationUpdated": "Jellyfin Server sudah diperbarui",
 | 
				
			||||||
@ -22,7 +22,7 @@
 | 
				
			|||||||
    "HeaderContinueWatching": "Lanjutkan Menonton",
 | 
					    "HeaderContinueWatching": "Lanjutkan Menonton",
 | 
				
			||||||
    "HeaderCameraUploads": "Unggahan Kamera",
 | 
					    "HeaderCameraUploads": "Unggahan Kamera",
 | 
				
			||||||
    "HeaderAlbumArtists": "Album Artis",
 | 
					    "HeaderAlbumArtists": "Album Artis",
 | 
				
			||||||
    "Genres": "Genre",
 | 
					    "Genres": "Aliran",
 | 
				
			||||||
    "Folders": "Folder",
 | 
					    "Folders": "Folder",
 | 
				
			||||||
    "Favorites": "Favorit",
 | 
					    "Favorites": "Favorit",
 | 
				
			||||||
    "Collections": "Koleksi",
 | 
					    "Collections": "Koleksi",
 | 
				
			||||||
 | 
				
			|||||||
@ -21,7 +21,7 @@
 | 
				
			|||||||
    "HeaderFavoriteAlbums": "Избранные альбомы",
 | 
					    "HeaderFavoriteAlbums": "Избранные альбомы",
 | 
				
			||||||
    "HeaderFavoriteArtists": "Избранные исполнители",
 | 
					    "HeaderFavoriteArtists": "Избранные исполнители",
 | 
				
			||||||
    "HeaderFavoriteEpisodes": "Избранные эпизоды",
 | 
					    "HeaderFavoriteEpisodes": "Избранные эпизоды",
 | 
				
			||||||
    "HeaderFavoriteShows": "Избранные передачи",
 | 
					    "HeaderFavoriteShows": "Избранные сериалы",
 | 
				
			||||||
    "HeaderFavoriteSongs": "Избранные композиции",
 | 
					    "HeaderFavoriteSongs": "Избранные композиции",
 | 
				
			||||||
    "HeaderLiveTV": "Эфир",
 | 
					    "HeaderLiveTV": "Эфир",
 | 
				
			||||||
    "HeaderNextUp": "Очередное",
 | 
					    "HeaderNextUp": "Очередное",
 | 
				
			||||||
 | 
				
			|||||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user