ソースを参照

Merge branch 'master' into sessionmanager

Bond-009 5 年 前
コミット
0f173e1778
100 ファイル変更787 行追加350 行削除
  1. 13 7
      .github/ISSUE_TEMPLATE/bug_report.md
  2. 2 0
      CONTRIBUTORS.md
  3. 1 1
      Dockerfile
  4. 8 2
      Dockerfile.arm
  5. 8 3
      Dockerfile.arm64
  6. 3 0
      Emby.Dlna/Api/DlnaServerService.cs
  7. 3 0
      Emby.Dlna/Api/DlnaService.cs
  8. 2 0
      Emby.Dlna/Common/Argument.cs
  9. 10 1
      Emby.Dlna/Common/DeviceIcon.cs
  10. 4 3
      Emby.Dlna/Common/DeviceService.cs
  11. 9 5
      Emby.Dlna/Common/ServiceAction.cs
  12. 10 8
      Emby.Dlna/Common/StateVariable.cs
  13. 18 9
      Emby.Dlna/Configuration/DlnaOptions.cs
  14. 3 0
      Emby.Dlna/ConfigurationExtension.cs
  15. 3 0
      Emby.Dlna/ConnectionManager/ConnectionManager.cs
  16. 3 0
      Emby.Dlna/ConnectionManager/ConnectionManagerXmlBuilder.cs
  17. 17 14
      Emby.Dlna/ConnectionManager/ControlHandler.cs
  18. 3 0
      Emby.Dlna/ConnectionManager/ServiceActionListBuilder.cs
  19. 3 0
      Emby.Dlna/ContentDirectory/ContentDirectory.cs
  20. 3 0
      Emby.Dlna/ContentDirectory/ContentDirectoryXmlBuilder.cs
  21. 182 183
      Emby.Dlna/ContentDirectory/ControlHandler.cs
  22. 3 0
      Emby.Dlna/ContentDirectory/ServiceActionListBuilder.cs
  23. 3 0
      Emby.Dlna/ControlRequest.cs
  24. 8 5
      Emby.Dlna/ControlResponse.cs
  25. 4 1
      Emby.Dlna/Didl/DidlBuilder.cs
  26. 3 0
      Emby.Dlna/Didl/Filter.cs
  27. 3 0
      Emby.Dlna/Didl/StringWriterWithEncoding.cs
  28. 3 0
      Emby.Dlna/DlnaManager.cs
  29. 13 0
      Emby.Dlna/Emby.Dlna.csproj
  30. 9 5
      Emby.Dlna/EventSubscriptionResponse.cs
  31. 5 1
      Emby.Dlna/Eventing/EventManager.cs
  32. 5 2
      Emby.Dlna/Eventing/EventSubscription.cs
  33. 2 0
      Emby.Dlna/IConnectionManager.cs
  34. 2 0
      Emby.Dlna/IContentDirectory.cs
  35. 2 0
      Emby.Dlna/IEventManager.cs
  36. 2 0
      Emby.Dlna/IMediaReceiverRegistrar.cs
  37. 3 0
      Emby.Dlna/IUpnpService.cs
  38. 3 0
      Emby.Dlna/Main/DlnaEntryPoint.cs
  39. 23 21
      Emby.Dlna/MediaReceiverRegistrar/ControlHandler.cs
  40. 3 0
      Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrar.cs
  41. 3 0
      Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarXmlBuilder.cs
  42. 3 0
      Emby.Dlna/MediaReceiverRegistrar/ServiceActionListBuilder.cs
  43. 18 15
      Emby.Dlna/PlayTo/Device.cs
  44. 3 0
      Emby.Dlna/PlayTo/DeviceInfo.cs
  45. 3 0
      Emby.Dlna/PlayTo/PlayToController.cs
  46. 7 4
      Emby.Dlna/PlayTo/PlayToManager.cs
  47. 3 0
      Emby.Dlna/PlayTo/PlaybackProgressEventArgs.cs
  48. 3 0
      Emby.Dlna/PlayTo/PlaybackStartEventArgs.cs
  49. 3 0
      Emby.Dlna/PlayTo/PlaybackStoppedEventArgs.cs
  50. 3 0
      Emby.Dlna/PlayTo/PlaylistItem.cs
  51. 3 0
      Emby.Dlna/PlayTo/PlaylistItemFactory.cs
  52. 9 6
      Emby.Dlna/PlayTo/SsdpHttpClient.cs
  53. 3 0
      Emby.Dlna/PlayTo/TRANSPORTSTATE.cs
  54. 3 0
      Emby.Dlna/PlayTo/TransportCommands.cs
  55. 3 0
      Emby.Dlna/PlayTo/UpnpContainer.cs
  56. 3 0
      Emby.Dlna/PlayTo/uBaseObject.cs
  57. 3 0
      Emby.Dlna/PlayTo/uPnpNamespaces.cs
  58. 3 0
      Emby.Dlna/Profiles/DefaultProfile.cs
  59. 3 0
      Emby.Dlna/Profiles/DenonAvrProfile.cs
  60. 3 0
      Emby.Dlna/Profiles/DirectTvProfile.cs
  61. 3 0
      Emby.Dlna/Profiles/DishHopperJoeyProfile.cs
  62. 3 0
      Emby.Dlna/Profiles/Foobar2000Profile.cs
  63. 3 0
      Emby.Dlna/Profiles/LgTvProfile.cs
  64. 3 0
      Emby.Dlna/Profiles/LinksysDMA2100Profile.cs
  65. 3 0
      Emby.Dlna/Profiles/MarantzProfile.cs
  66. 3 0
      Emby.Dlna/Profiles/MediaMonkeyProfile.cs
  67. 3 0
      Emby.Dlna/Profiles/PanasonicVieraProfile.cs
  68. 3 0
      Emby.Dlna/Profiles/PopcornHourProfile.cs
  69. 3 0
      Emby.Dlna/Profiles/SamsungSmartTvProfile.cs
  70. 3 0
      Emby.Dlna/Profiles/SharpSmartTvProfile.cs
  71. 3 0
      Emby.Dlna/Profiles/SonyBlurayPlayer2013.cs
  72. 3 0
      Emby.Dlna/Profiles/SonyBlurayPlayer2014.cs
  73. 3 0
      Emby.Dlna/Profiles/SonyBlurayPlayer2015.cs
  74. 3 0
      Emby.Dlna/Profiles/SonyBlurayPlayer2016.cs
  75. 3 0
      Emby.Dlna/Profiles/SonyBlurayPlayerProfile.cs
  76. 3 0
      Emby.Dlna/Profiles/SonyBravia2010Profile.cs
  77. 3 0
      Emby.Dlna/Profiles/SonyBravia2011Profile.cs
  78. 3 0
      Emby.Dlna/Profiles/SonyBravia2012Profile.cs
  79. 3 0
      Emby.Dlna/Profiles/SonyBravia2013Profile.cs
  80. 3 0
      Emby.Dlna/Profiles/SonyBravia2014Profile.cs
  81. 3 0
      Emby.Dlna/Profiles/SonyPs3Profile.cs
  82. 3 0
      Emby.Dlna/Profiles/SonyPs4Profile.cs
  83. 3 0
      Emby.Dlna/Profiles/WdtvLiveProfile.cs
  84. 3 0
      Emby.Dlna/Profiles/XboxOneProfile.cs
  85. 3 0
      Emby.Dlna/Server/DescriptionXmlBuilder.cs
  86. 8 10
      Emby.Dlna/Service/BaseControlHandler.cs
  87. 3 0
      Emby.Dlna/Service/BaseService.cs
  88. 3 0
      Emby.Dlna/Service/ControlErrorHandler.cs
  89. 3 0
      Emby.Dlna/Service/ServiceXmlBuilder.cs
  90. 10 5
      Emby.Dlna/Ssdp/DeviceDiscovery.cs
  91. 3 0
      Emby.Dlna/Ssdp/Extensions.cs
  92. 1 1
      Emby.Server.Implementations/ApplicationHost.cs
  93. 96 0
      Emby.Server.Implementations/Localization/Core/bn.json
  94. 1 0
      Emby.Server.Implementations/Localization/Core/es_DO.json
  95. 60 12
      Emby.Server.Implementations/Localization/Core/fi.json
  96. 3 1
      Emby.Server.Implementations/Localization/Core/gl.json
  97. 4 4
      Emby.Server.Implementations/Localization/Core/hu.json
  98. 6 1
      Emby.Server.Implementations/Localization/Core/id.json
  99. 4 4
      Emby.Server.Implementations/Localization/Core/zh-TW.json
  100. 15 16
      Emby.Server.Implementations/Session/SessionManager.cs

+ 13 - 7
.github/ISSUE_TEMPLATE/bug_report.md

@@ -10,6 +10,19 @@ assignees: ''
 **Describe the bug**
 <!-- A clear and concise description of what the bug is. -->
 
+**System (please complete the following information):**
+ - OS: [e.g. Debian, Windows]
+ - Virtualization: [e.g. Docker, KVM, LXC]
+ - Clients: [Browser, Android, Fire Stick, etc.]
+ - Browser: [e.g. Firefox 72, Chrome 80, Safari 13]
+ - Jellyfin Version: [e.g. 10.4.3, nightly 20191231]
+ - Playback: [Direct Play, Remux, Direct Stream, Transcode] 
+ - Installed Plugins: [e.g. none, Fanart, Anime, etc.]
+ - Reverse Proxy: [e.g. none, nginx, apache, etc.]
+ - Base URL: [e.g. none, yes: /example]
+ - Networking: [e.g. Host, Bridge/NAT]
+ - Storage: [e.g. local, NFS, cloud]
+
 **To Reproduce**
 <!-- Steps to reproduce the behavior: -->
 1. Go to '...'
@@ -26,12 +39,5 @@ assignees: ''
 **Screenshots**
 <!-- If applicable, add screenshots to help explain your problem. -->
 
-**System (please complete the following information):**
- - OS: [e.g. Docker, Debian, Windows]
- - Browser: [e.g. Firefox, Chrome, Safari]
- - Jellyfin Version: [e.g. 10.0.1]
- - Installed Plugins: [e.g. none, Fanart, Anime, etc.]
- - Reverse proxy: [e.g. no, nginx, apache, etc.]
-
 **Additional context**
 <!-- Add any other context about the problem here. -->

+ 2 - 0
CONTRIBUTORS.md

@@ -32,8 +32,10 @@
  - [nevado](https://github.com/nevado)
  - [mark-monteiro](https://github.com/mark-monteiro)
  - [ullmie02](https://github.com/ullmie02)
+ - [geilername](https://github.com/geilername)
  - [pR0Ps](https://github.com/pR0Ps)
 
+
 # Emby Contributors
 
  - [LukePulverenti](https://github.com/LukePulverenti)

+ 1 - 1
Dockerfile

@@ -3,7 +3,7 @@ ARG FFMPEG_VERSION=latest
 
 FROM node:alpine as web-builder
 ARG JELLYFIN_WEB_VERSION=master
-RUN apk add curl \
+RUN apk add curl git \
  && curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
  && cd jellyfin-web-* \
  && yarn install \

+ 8 - 2
Dockerfile.arm

@@ -1,9 +1,13 @@
+# DESIGNED FOR BUILDING ON AMD64 ONLY
+#####################################
+# Requires binfm_misc registration
+# https://github.com/multiarch/qemu-user-static#binfmt_misc-register
 ARG DOTNET_VERSION=3.1
 
 
 FROM node:alpine as web-builder
 ARG JELLYFIN_WEB_VERSION=master
-RUN apk add curl \
+RUN apk add curl git \
  && curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
  && cd jellyfin-web-* \
  && yarn install \
@@ -21,7 +25,9 @@ RUN find . -type d -name obj | xargs -r rm -r
 RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" --self-contained --runtime linux-arm "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none"
 
 
-FROM debian:buster-slim
+FROM multiarch/qemu-user-static:x86_64-arm as qemu
+FROM arm32v7/debian:buster-slim
+COPY --from=qemu /usr/bin/qemu-arm-static /usr/bin
 RUN apt-get update \
  && apt-get install --no-install-recommends --no-install-suggests -y ffmpeg \
  libssl-dev ca-certificates \

+ 8 - 3
Dockerfile.arm64

@@ -1,9 +1,13 @@
+# DESIGNED FOR BUILDING ON AMD64 ONLY
+#####################################
+# Requires binfm_misc registration
+# https://github.com/multiarch/qemu-user-static#binfmt_misc-register
 ARG DOTNET_VERSION=3.1
 
 
 FROM node:alpine as web-builder
 ARG JELLYFIN_WEB_VERSION=master
-RUN apk add curl \
+RUN apk add curl git \
  && curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
  && cd jellyfin-web-* \
  && yarn install \
@@ -20,8 +24,9 @@ RUN find . -type d -name obj | xargs -r rm -r
 # Build
 RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" --self-contained --runtime linux-arm64 "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none"
 
-
-FROM debian:buster-slim
+FROM multiarch/qemu-user-static:x86_64-aarch64 as qemu
+FROM arm64v8/debian:buster-slim
+COPY --from=qemu /usr/bin/qemu-aarch64-static /usr/bin
 RUN apt-get update \
  && apt-get install --no-install-recommends --no-install-suggests -y ffmpeg \
  libssl-dev ca-certificates \

+ 3 - 0
Emby.Dlna/Api/DlnaServerService.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using System;
 using System.IO;
 using System.Text;

+ 3 - 0
Emby.Dlna/Api/DlnaService.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using System.Linq;
 using MediaBrowser.Controller.Dlna;
 using MediaBrowser.Controller.Net;

+ 2 - 0
Emby.Dlna/Common/Argument.cs

@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
 
 namespace Emby.Dlna.Common
 {

+ 10 - 1
Emby.Dlna/Common/DeviceIcon.cs

@@ -1,3 +1,7 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
+using System.Globalization;
 
 namespace Emby.Dlna.Common
 {
@@ -13,9 +17,14 @@ namespace Emby.Dlna.Common
 
         public string Depth { get; set; }
 
+        /// <inheritdoc />
         public override string ToString()
         {
-            return string.Format("{0}x{1}", Height, Width);
+            return string.Format(
+                CultureInfo.InvariantCulture,
+                "{0}x{1}",
+                Height,
+                Width);
         }
     }
 }

+ 4 - 3
Emby.Dlna/Common/DeviceService.cs

@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
 
 namespace Emby.Dlna.Common
 {
@@ -13,9 +15,8 @@ namespace Emby.Dlna.Common
 
         public string EventSubUrl { get; set; }
 
+        /// <inheritdoc />
         public override string ToString()
-        {
-            return string.Format("{0}", ServiceId);
-        }
+            => ServiceId;
     }
 }

+ 9 - 5
Emby.Dlna/Common/ServiceAction.cs

@@ -1,21 +1,25 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using System.Collections.Generic;
 
 namespace Emby.Dlna.Common
 {
     public class ServiceAction
     {
+        public ServiceAction()
+        {
+            ArgumentList = new List<Argument>();
+        }
+
         public string Name { get; set; }
 
         public List<Argument> ArgumentList { get; set; }
 
+        /// <inheritdoc />
         public override string ToString()
         {
             return Name;
         }
-
-        public ServiceAction()
-        {
-            ArgumentList = new List<Argument>();
-        }
     }
 }

+ 10 - 8
Emby.Dlna/Common/StateVariable.cs

@@ -1,9 +1,17 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using System;
 
 namespace Emby.Dlna.Common
 {
     public class StateVariable
     {
+        public StateVariable()
+        {
+            AllowedValues = Array.Empty<string>();
+        }
+
         public string Name { get; set; }
 
         public string DataType { get; set; }
@@ -12,14 +20,8 @@ namespace Emby.Dlna.Common
 
         public string[] AllowedValues { get; set; }
 
+        /// <inheritdoc />
         public override string ToString()
-        {
-            return Name;
-        }
-
-        public StateVariable()
-        {
-            AllowedValues = Array.Empty<string>();
-        }
+            => Name;
     }
 }

+ 18 - 9
Emby.Dlna/Configuration/DlnaOptions.cs

@@ -1,17 +1,10 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
 
 namespace Emby.Dlna.Configuration
 {
     public class DlnaOptions
     {
-        public bool EnablePlayTo { get; set; }
-        public bool EnableServer { get; set; }
-        public bool EnableDebugLog { get; set; }
-        public bool BlastAliveMessages { get; set; }
-        public bool SendOnlyMatchedHost { get; set; }
-        public int ClientDiscoveryIntervalSeconds { get; set; }
-        public int BlastAliveMessageIntervalSeconds { get; set; }
-        public string DefaultUserId { get; set; }
-
         public DlnaOptions()
         {
             EnablePlayTo = true;
@@ -21,5 +14,21 @@ namespace Emby.Dlna.Configuration
             ClientDiscoveryIntervalSeconds = 60;
             BlastAliveMessageIntervalSeconds = 1800;
         }
+
+        public bool EnablePlayTo { get; set; }
+
+        public bool EnableServer { get; set; }
+
+        public bool EnableDebugLog { get; set; }
+
+        public bool BlastAliveMessages { get; set; }
+
+        public bool SendOnlyMatchedHost { get; set; }
+
+        public int ClientDiscoveryIntervalSeconds { get; set; }
+
+        public int BlastAliveMessageIntervalSeconds { get; set; }
+
+        public string DefaultUserId { get; set; }
     }
 }

+ 3 - 0
Emby.Dlna/ConfigurationExtension.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using System.Collections.Generic;
 using Emby.Dlna.Configuration;
 using MediaBrowser.Common.Configuration;

+ 3 - 0
Emby.Dlna/ConnectionManager/ConnectionManager.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using System.Threading.Tasks;
 using Emby.Dlna.Service;
 using MediaBrowser.Common.Net;

+ 3 - 0
Emby.Dlna/ConnectionManager/ConnectionManagerXmlBuilder.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using System.Collections.Generic;
 using Emby.Dlna.Common;
 using Emby.Dlna.Service;

+ 17 - 14
Emby.Dlna/ConnectionManager/ControlHandler.cs

@@ -1,5 +1,9 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using System;
 using System.Collections.Generic;
+using System.Xml;
 using Emby.Dlna.Service;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Controller.Configuration;
@@ -12,29 +16,28 @@ namespace Emby.Dlna.ConnectionManager
     {
         private readonly DeviceProfile _profile;
 
-        protected override IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, IDictionary<string, string> methodParams)
+        public ControlHandler(IServerConfigurationManager config, ILogger logger, DeviceProfile profile)
+            : base(config, logger)
+        {
+            _profile = profile;
+        }
+
+        /// <inheritdoc />
+        protected override void WriteResult(string methodName, IDictionary<string, string> methodParams, XmlWriter xmlWriter)
         {
             if (string.Equals(methodName, "GetProtocolInfo", StringComparison.OrdinalIgnoreCase))
             {
-                return HandleGetProtocolInfo();
+                HandleGetProtocolInfo(xmlWriter);
+                return;
             }
 
             throw new ResourceNotFoundException("Unexpected control request name: " + methodName);
         }
 
-        private IEnumerable<KeyValuePair<string, string>> HandleGetProtocolInfo()
+        private void HandleGetProtocolInfo(XmlWriter xmlWriter)
         {
-            return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
-            {
-                { "Source", _profile.ProtocolInfo },
-                { "Sink", "" }
-            };
-        }
-
-        public ControlHandler(IServerConfigurationManager config, ILogger logger, DeviceProfile profile)
-            : base(config, logger)
-        {
-            _profile = profile;
+            xmlWriter.WriteElementString("Source", _profile.ProtocolInfo);
+            xmlWriter.WriteElementString("Sink", string.Empty);
         }
     }
 }

+ 3 - 0
Emby.Dlna/ConnectionManager/ServiceActionListBuilder.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using System.Collections.Generic;
 using Emby.Dlna.Common;
 

+ 3 - 0
Emby.Dlna/ContentDirectory/ContentDirectory.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using System;
 using System.Threading.Tasks;
 using Emby.Dlna.Service;

+ 3 - 0
Emby.Dlna/ContentDirectory/ContentDirectoryXmlBuilder.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using System.Collections.Generic;
 using Emby.Dlna.Common;
 using Emby.Dlna.Service;

+ 182 - 183
Emby.Dlna/ContentDirectory/ControlHandler.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using System;
 using System.Collections.Generic;
 using System.Globalization;
@@ -44,7 +47,6 @@ namespace Emby.Dlna.ContentDirectory
         private const string NS_UPNP = "urn:schemas-upnp-org:metadata-1-0/upnp/";
 
         private readonly int _systemUpdateId;
-        private readonly CultureInfo _usCulture = new CultureInfo("en-US");
 
         private readonly DidlBuilder _didlBuilder;
 
@@ -58,7 +60,8 @@ namespace Emby.Dlna.ContentDirectory
             string accessToken,
             IImageProcessor imageProcessor,
             IUserDataManager userDataManager,
-            User user, int systemUpdateId,
+            User user,
+            int systemUpdateId,
             IServerConfigurationManager config,
             ILocalizationManager localization,
             IMediaSourceManager mediaSourceManager,
@@ -79,114 +82,129 @@ namespace Emby.Dlna.ContentDirectory
             _didlBuilder = new DidlBuilder(profile, user, imageProcessor, serverAddress, accessToken, userDataManager, localization, mediaSourceManager, Logger, mediaEncoder);
         }
 
-        protected override IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, IDictionary<string, string> methodParams)
+        /// <inheritdoc />
+        protected override void WriteResult(string methodName, IDictionary<string, string> methodParams, XmlWriter xmlWriter)
         {
-            var deviceId = "test";
-
-            var user = _user;
+            const string DeviceId = "test";
 
             if (string.Equals(methodName, "GetSearchCapabilities", StringComparison.OrdinalIgnoreCase))
-                return HandleGetSearchCapabilities();
+            {
+                HandleGetSearchCapabilities(xmlWriter);
+                return;
+            }
 
             if (string.Equals(methodName, "GetSortCapabilities", StringComparison.OrdinalIgnoreCase))
-                return HandleGetSortCapabilities();
+            {
+                HandleGetSortCapabilities(xmlWriter);
+                return;
+            }
 
             if (string.Equals(methodName, "GetSortExtensionCapabilities", StringComparison.OrdinalIgnoreCase))
-                return HandleGetSortExtensionCapabilities();
+            {
+                HandleGetSortExtensionCapabilities(xmlWriter);
+                return;
+            }
 
             if (string.Equals(methodName, "GetSystemUpdateID", StringComparison.OrdinalIgnoreCase))
-                return HandleGetSystemUpdateID();
+            {
+                HandleGetSystemUpdateID(xmlWriter);
+                return;
+            }
 
             if (string.Equals(methodName, "Browse", StringComparison.OrdinalIgnoreCase))
-                return HandleBrowse(methodParams, user, deviceId);
+            {
+                HandleBrowse(xmlWriter, methodParams, DeviceId);
+                return;
+            }
 
             if (string.Equals(methodName, "X_GetFeatureList", StringComparison.OrdinalIgnoreCase))
-                return HandleXGetFeatureList();
+            {
+                HandleXGetFeatureList(xmlWriter);
+                return;
+            }
 
             if (string.Equals(methodName, "GetFeatureList", StringComparison.OrdinalIgnoreCase))
-                return HandleGetFeatureList();
+            {
+                HandleGetFeatureList(xmlWriter);
+                return;
+            }
 
             if (string.Equals(methodName, "X_SetBookmark", StringComparison.OrdinalIgnoreCase))
-                return HandleXSetBookmark(methodParams, user);
+            {
+                HandleXSetBookmark(methodParams);
+                return;
+            }
 
             if (string.Equals(methodName, "Search", StringComparison.OrdinalIgnoreCase))
-                return HandleSearch(methodParams, user, deviceId);
+            {
+                HandleSearch(xmlWriter, methodParams, DeviceId);
+                return;
+            }
 
             if (string.Equals(methodName, "X_BrowseByLetter", StringComparison.OrdinalIgnoreCase))
-                return HandleX_BrowseByLetter(methodParams, user, deviceId);
+            {
+                HandleXBrowseByLetter(xmlWriter, methodParams, DeviceId);
+                return;
+            }
 
             throw new ResourceNotFoundException("Unexpected control request name: " + methodName);
         }
 
-        private IEnumerable<KeyValuePair<string, string>> HandleXSetBookmark(IDictionary<string, string> sparams, User user)
+        private void HandleXSetBookmark(IDictionary<string, string> sparams)
         {
             var id = sparams["ObjectID"];
 
-            var serverItem = GetItemFromObjectId(id, user);
+            var serverItem = GetItemFromObjectId(id, _user);
 
             var item = serverItem.Item;
 
-            var newbookmark = int.Parse(sparams["PosSecond"], _usCulture);
+            var newbookmark = int.Parse(sparams["PosSecond"], CultureInfo.InvariantCulture);
 
-            var userdata = _userDataManager.GetUserData(user, item);
+            var userdata = _userDataManager.GetUserData(_user, item);
 
             userdata.PlaybackPositionTicks = TimeSpan.FromSeconds(newbookmark).Ticks;
 
-            _userDataManager.SaveUserData(user, item, userdata, UserDataSaveReason.TogglePlayed,
+            _userDataManager.SaveUserData(_user, item, userdata, UserDataSaveReason.TogglePlayed,
                 CancellationToken.None);
-
-            return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
         }
 
-        private IEnumerable<KeyValuePair<string, string>> HandleGetSearchCapabilities()
+        private void HandleGetSearchCapabilities(XmlWriter xmlWriter)
         {
-            return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
-            {
-                { "SearchCaps", "res@resolution,res@size,res@duration,dc:title,dc:creator,upnp:actor,upnp:artist,upnp:genre,upnp:album,dc:date,upnp:class,@id,@refID,@protocolInfo,upnp:author,dc:description,pv:avKeywords" }
-            };
+            xmlWriter.WriteElementString(
+                "SearchCaps",
+                "res@resolution,res@size,res@duration,dc:title,dc:creator,upnp:actor,upnp:artist,upnp:genre,upnp:album,dc:date,upnp:class,@id,@refID,@protocolInfo,upnp:author,dc:description,pv:avKeywords");
         }
 
-        private IEnumerable<KeyValuePair<string, string>> HandleGetSortCapabilities()
+        private void HandleGetSortCapabilities(XmlWriter xmlWriter)
         {
-            return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
-            {
-                { "SortCaps", "res@duration,res@size,res@bitrate,dc:date,dc:title,dc:size,upnp:album,upnp:artist,upnp:albumArtist,upnp:episodeNumber,upnp:genre,upnp:originalTrackNumber,upnp:rating" }
-            };
+            xmlWriter.WriteElementString(
+                "SortCaps",
+                "res@duration,res@size,res@bitrate,dc:date,dc:title,dc:size,upnp:album,upnp:artist,upnp:albumArtist,upnp:episodeNumber,upnp:genre,upnp:originalTrackNumber,upnp:rating");
         }
 
-        private IEnumerable<KeyValuePair<string, string>> HandleGetSortExtensionCapabilities()
+        private void HandleGetSortExtensionCapabilities(XmlWriter xmlWriter)
         {
-            return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
-            {
-                { "SortExtensionCaps", "res@duration,res@size,res@bitrate,dc:date,dc:title,dc:size,upnp:album,upnp:artist,upnp:albumArtist,upnp:episodeNumber,upnp:genre,upnp:originalTrackNumber,upnp:rating" }
-            };
+            xmlWriter.WriteElementString(
+                "SortExtensionCaps",
+                "res@duration,res@size,res@bitrate,dc:date,dc:title,dc:size,upnp:album,upnp:artist,upnp:albumArtist,upnp:episodeNumber,upnp:genre,upnp:originalTrackNumber,upnp:rating");
         }
 
-        private IEnumerable<KeyValuePair<string, string>> HandleGetSystemUpdateID()
+        private void HandleGetSystemUpdateID(XmlWriter xmlWriter)
         {
-            var headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
-            headers.Add("Id", _systemUpdateId.ToString(_usCulture));
-            return headers;
+            xmlWriter.WriteElementString("Id", _systemUpdateId.ToString(CultureInfo.InvariantCulture));
         }
 
-        private IEnumerable<KeyValuePair<string, string>> HandleGetFeatureList()
+        private void HandleGetFeatureList(XmlWriter xmlWriter)
         {
-            return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
-            {
-                { "FeatureList", GetFeatureListXml() }
-            };
+            xmlWriter.WriteElementString("FeatureList", WriteFeatureListXml());
         }
 
-        private IEnumerable<KeyValuePair<string, string>> HandleXGetFeatureList()
-        {
-            return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
-            {
-                { "FeatureList", GetFeatureListXml() }
-            };
-        }
+        private void HandleXGetFeatureList(XmlWriter xmlWriter)
+            => HandleGetFeatureList(xmlWriter);
 
-        private string GetFeatureListXml()
+        private string WriteFeatureListXml()
         {
+            // TODO: clean this up
             var builder = new StringBuilder();
 
             builder.Append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
@@ -213,7 +231,7 @@ namespace Emby.Dlna.ContentDirectory
             return defaultValue;
         }
 
-        private IEnumerable<KeyValuePair<string, string>> HandleBrowse(IDictionary<string, string> sparams, User user, string deviceId)
+        private void HandleBrowse(XmlWriter xmlWriter, IDictionary<string, string> sparams, string deviceId)
         {
             var id = sparams["ObjectID"];
             var flag = sparams["BrowseFlag"];
@@ -237,101 +255,95 @@ namespace Emby.Dlna.ContentDirectory
                 start = startVal;
             }
 
-            var settings = new XmlWriterSettings
-            {
-                Encoding = Encoding.UTF8,
-                CloseOutput = false,
-                OmitXmlDeclaration = true,
-                ConformanceLevel = ConformanceLevel.Fragment
-            };
-
-            StringWriter builder = new StringWriterWithEncoding(Encoding.UTF8);
-
             int totalCount;
 
-            var dlnaOptions = _config.GetDlnaConfiguration();
-
-            using (var writer = XmlWriter.Create(builder, settings))
+            using (StringWriter builder = new StringWriterWithEncoding(Encoding.UTF8))
             {
-                //writer.WriteStartDocument();
-
-                writer.WriteStartElement(string.Empty, "DIDL-Lite", NS_DIDL);
-
-                writer.WriteAttributeString("xmlns", "dc", null, NS_DC);
-                writer.WriteAttributeString("xmlns", "dlna", null, NS_DLNA);
-                writer.WriteAttributeString("xmlns", "upnp", null, NS_UPNP);
-                //didl.SetAttribute("xmlns:sec", NS_SEC);
-
-                DidlBuilder.WriteXmlRootAttributes(_profile, writer);
-
-                var serverItem = GetItemFromObjectId(id, user);
-                var item = serverItem.Item;
+                var settings = new XmlWriterSettings()
+                {
+                    Encoding = Encoding.UTF8,
+                    CloseOutput = false,
+                    OmitXmlDeclaration = true,
+                    ConformanceLevel = ConformanceLevel.Fragment
+                };
 
-                if (string.Equals(flag, "BrowseMetadata"))
+                using (var writer = XmlWriter.Create(builder, settings))
                 {
-                    totalCount = 1;
+                    writer.WriteStartElement(string.Empty, "DIDL-Lite", NS_DIDL);
 
-                    if (item.IsDisplayedAsFolder || serverItem.StubType.HasValue)
-                    {
-                        var childrenResult = GetUserItems(item, serverItem.StubType, user, sortCriteria, start, requestedCount);
+                    writer.WriteAttributeString("xmlns", "dc", null, NS_DC);
+                    writer.WriteAttributeString("xmlns", "dlna", null, NS_DLNA);
+                    writer.WriteAttributeString("xmlns", "upnp", null, NS_UPNP);
 
-                        _didlBuilder.WriteFolderElement(writer, item, serverItem.StubType, null, childrenResult.TotalRecordCount, filter, id);
-                    }
-                    else
-                    {
-                        _didlBuilder.WriteItemElement(dlnaOptions, writer, item, user, null, null, deviceId, filter);
-                    }
+                    DidlBuilder.WriteXmlRootAttributes(_profile, writer);
 
-                    provided++;
-                }
-                else
-                {
-                    var childrenResult = GetUserItems(item, serverItem.StubType, user, sortCriteria, start, requestedCount);
-                    totalCount = childrenResult.TotalRecordCount;
+                    var serverItem = GetItemFromObjectId(id, _user);
+                    var item = serverItem.Item;
 
-                    provided = childrenResult.Items.Count;
 
-                    foreach (var i in childrenResult.Items)
+                    if (string.Equals(flag, "BrowseMetadata", StringComparison.Ordinal))
                     {
-                        var childItem = i.Item;
-                        var displayStubType = i.StubType;
+                        totalCount = 1;
 
-                        if (childItem.IsDisplayedAsFolder || displayStubType.HasValue)
+                        if (item.IsDisplayedAsFolder || serverItem.StubType.HasValue)
                         {
-                            var childCount = (GetUserItems(childItem, displayStubType, user, sortCriteria, null, 0))
-                                .TotalRecordCount;
+                            var childrenResult = GetUserItems(item, serverItem.StubType, _user, sortCriteria, start, requestedCount);
 
-                            _didlBuilder.WriteFolderElement(writer, childItem, displayStubType, item, childCount, filter);
+                            _didlBuilder.WriteFolderElement(writer, item, serverItem.StubType, null, childrenResult.TotalRecordCount, filter, id);
                         }
                         else
                         {
-                            _didlBuilder.WriteItemElement(dlnaOptions, writer, childItem, user, item, serverItem.StubType, deviceId, filter);
+                            var dlnaOptions = _config.GetDlnaConfiguration();
+                            _didlBuilder.WriteItemElement(dlnaOptions, writer, item, _user, null, null, deviceId, filter);
                         }
+
+                        provided++;
                     }
+                    else
+                    {
+                        var childrenResult = GetUserItems(item, serverItem.StubType, _user, sortCriteria, start, requestedCount);
+                        totalCount = childrenResult.TotalRecordCount;
+
+                        provided = childrenResult.Items.Count;
+
+                        var dlnaOptions = _config.GetDlnaConfiguration();
+                        foreach (var i in childrenResult.Items)
+                        {
+                            var childItem = i.Item;
+                            var displayStubType = i.StubType;
+
+                            if (childItem.IsDisplayedAsFolder || displayStubType.HasValue)
+                            {
+                                var childCount = GetUserItems(childItem, displayStubType, _user, sortCriteria, null, 0)
+                                    .TotalRecordCount;
+
+                                _didlBuilder.WriteFolderElement(writer, childItem, displayStubType, item, childCount, filter);
+                            }
+                            else
+                            {
+                                _didlBuilder.WriteItemElement(dlnaOptions, writer, childItem, _user, item, serverItem.StubType, deviceId, filter);
+                            }
+                        }
+                    }
+
+                    writer.WriteFullEndElement();
                 }
 
-                writer.WriteFullEndElement();
-                //writer.WriteEndDocument();
+                xmlWriter.WriteElementString("Result", builder.ToString());
             }
 
-            var resXML = builder.ToString();
-
-            return new[]
-                {
-                    new KeyValuePair<string,string>("Result", resXML),
-                    new KeyValuePair<string,string>("NumberReturned", provided.ToString(_usCulture)),
-                    new KeyValuePair<string,string>("TotalMatches", totalCount.ToString(_usCulture)),
-                    new KeyValuePair<string,string>("UpdateID", _systemUpdateId.ToString(_usCulture))
-                };
+            xmlWriter.WriteElementString("NumberReturned", provided.ToString(CultureInfo.InvariantCulture));
+            xmlWriter.WriteElementString("TotalMatches", totalCount.ToString(CultureInfo.InvariantCulture));
+            xmlWriter.WriteElementString("UpdateID", _systemUpdateId.ToString(CultureInfo.InvariantCulture));
         }
 
-        private IEnumerable<KeyValuePair<string, string>> HandleX_BrowseByLetter(IDictionary<string, string> sparams, User user, string deviceId)
+        private void HandleXBrowseByLetter(XmlWriter xmlWriter, IDictionary<string, string> sparams, string deviceId)
         {
             // TODO: Implement this method
-            return HandleSearch(sparams, user, deviceId);
+            HandleSearch(xmlWriter, sparams, deviceId);
         }
 
-        private IEnumerable<KeyValuePair<string, string>> HandleSearch(IDictionary<string, string> sparams, User user, string deviceId)
+        private void HandleSearch(XmlWriter xmlWriter, IDictionary<string, string> sparams, string deviceId)
         {
             var searchCriteria = new SearchCriteria(GetValueOrDefault(sparams, "SearchCriteria", ""));
             var sortCriteria = new SortCriteria(GetValueOrDefault(sparams, "SortCriteria", ""));
@@ -354,99 +366,86 @@ namespace Emby.Dlna.ContentDirectory
                 start = startVal;
             }
 
-            var settings = new XmlWriterSettings
-            {
-                Encoding = Encoding.UTF8,
-                CloseOutput = false,
-                OmitXmlDeclaration = true,
-                ConformanceLevel = ConformanceLevel.Fragment
-            };
-
-            StringWriter builder = new StringWriterWithEncoding(Encoding.UTF8);
-            int totalCount = 0;
-            int provided = 0;
+            QueryResult<BaseItem> childrenResult;
 
-            using (var writer = XmlWriter.Create(builder, settings))
+            using (StringWriter builder = new StringWriterWithEncoding(Encoding.UTF8))
             {
-                //writer.WriteStartDocument();
-
-                writer.WriteStartElement(string.Empty, "DIDL-Lite", NS_DIDL);
-
-                writer.WriteAttributeString("xmlns", "dc", null, NS_DC);
-                writer.WriteAttributeString("xmlns", "dlna", null, NS_DLNA);
-                writer.WriteAttributeString("xmlns", "upnp", null, NS_UPNP);
-                //didl.SetAttribute("xmlns:sec", NS_SEC);
+                var settings = new XmlWriterSettings()
+                {
+                    Encoding = Encoding.UTF8,
+                    CloseOutput = false,
+                    OmitXmlDeclaration = true,
+                    ConformanceLevel = ConformanceLevel.Fragment
+                };
 
-                DidlBuilder.WriteXmlRootAttributes(_profile, writer);
+                using (var writer = XmlWriter.Create(builder, settings))
+                {
+                    writer.WriteStartElement(string.Empty, "DIDL-Lite", NS_DIDL);
 
-                var serverItem = GetItemFromObjectId(sparams["ContainerID"], user);
+                    writer.WriteAttributeString("xmlns", "dc", null, NS_DC);
+                    writer.WriteAttributeString("xmlns", "dlna", null, NS_DLNA);
+                    writer.WriteAttributeString("xmlns", "upnp", null, NS_UPNP);
 
-                var item = serverItem.Item;
+                    DidlBuilder.WriteXmlRootAttributes(_profile, writer);
 
-                var childrenResult = (GetChildrenSorted(item, user, searchCriteria, sortCriteria, start, requestedCount));
+                    var serverItem = GetItemFromObjectId(sparams["ContainerID"], _user);
 
-                totalCount = childrenResult.TotalRecordCount;
+                    var item = serverItem.Item;
 
-                provided = childrenResult.Items.Count;
+                    childrenResult = GetChildrenSorted(item, _user, searchCriteria, sortCriteria, start, requestedCount);
 
-                var dlnaOptions = _config.GetDlnaConfiguration();
+                    var dlnaOptions = _config.GetDlnaConfiguration();
 
-                foreach (var i in childrenResult.Items)
-                {
-                    if (i.IsDisplayedAsFolder)
+                    foreach (var i in childrenResult.Items)
                     {
-                        var childCount = (GetChildrenSorted(i, user, searchCriteria, sortCriteria, null, 0))
-                            .TotalRecordCount;
+                        if (i.IsDisplayedAsFolder)
+                        {
+                            var childCount = GetChildrenSorted(i, _user, searchCriteria, sortCriteria, null, 0)
+                                .TotalRecordCount;
 
-                        _didlBuilder.WriteFolderElement(writer, i, null, item, childCount, filter);
-                    }
-                    else
-                    {
-                        _didlBuilder.WriteItemElement(dlnaOptions, writer, i, user, item, serverItem.StubType, deviceId, filter);
+                            _didlBuilder.WriteFolderElement(writer, i, null, item, childCount, filter);
+                        }
+                        else
+                        {
+                            _didlBuilder.WriteItemElement(dlnaOptions, writer, i, _user, item, serverItem.StubType, deviceId, filter);
+                        }
                     }
+
+                    writer.WriteFullEndElement();
                 }
 
-                writer.WriteFullEndElement();
-                //writer.WriteEndDocument();
+                xmlWriter.WriteElementString("Result", builder.ToString());
             }
 
-            var resXML = builder.ToString();
-
-            return new List<KeyValuePair<string, string>>
-                {
-                    new KeyValuePair<string,string>("Result", resXML),
-                    new KeyValuePair<string,string>("NumberReturned", provided.ToString(_usCulture)),
-                    new KeyValuePair<string,string>("TotalMatches", totalCount.ToString(_usCulture)),
-                    new KeyValuePair<string,string>("UpdateID", _systemUpdateId.ToString(_usCulture))
-                };
+            xmlWriter.WriteElementString("NumberReturned", childrenResult.Items.Count.ToString(CultureInfo.InvariantCulture));
+            xmlWriter.WriteElementString("TotalMatches", childrenResult.TotalRecordCount.ToString(CultureInfo.InvariantCulture));
+            xmlWriter.WriteElementString("UpdateID", _systemUpdateId.ToString(CultureInfo.InvariantCulture));
         }
 
         private QueryResult<BaseItem> GetChildrenSorted(BaseItem item, User user, SearchCriteria search, SortCriteria sort, int? startIndex, int? limit)
         {
             var folder = (Folder)item;
 
-            var sortOrders = new List<(string, SortOrder)>();
-            if (!folder.IsPreSorted)
-            {
-                sortOrders.Add((ItemSortBy.SortName, sort.SortOrder));
-            }
+            var sortOrders = folder.IsPreSorted
+                ? Array.Empty<(string, SortOrder)>()
+                : new[] { (ItemSortBy.SortName, sort.SortOrder) };
 
-            var mediaTypes = new List<string>();
+            string[] mediaTypes = Array.Empty<string>();
             bool? isFolder = null;
 
             if (search.SearchType == SearchType.Audio)
             {
-                mediaTypes.Add(MediaType.Audio);
+                mediaTypes = new[] { MediaType.Audio };
                 isFolder = false;
             }
             else if (search.SearchType == SearchType.Video)
             {
-                mediaTypes.Add(MediaType.Video);
+                mediaTypes = new[] { MediaType.Video };
                 isFolder = false;
             }
             else if (search.SearchType == SearchType.Image)
             {
-                mediaTypes.Add(MediaType.Photo);
+                mediaTypes = new[] { MediaType.Photo };
                 isFolder = false;
             }
             else if (search.SearchType == SearchType.Playlist)
@@ -470,7 +469,7 @@ namespace Emby.Dlna.ContentDirectory
                 IsMissing = false,
                 ExcludeItemTypes = new[] { typeof(Book).Name },
                 IsFolder = isFolder,
-                MediaTypes = mediaTypes.ToArray(),
+                MediaTypes = mediaTypes,
                 DtoOptions = GetDtoOptions()
             });
         }
@@ -1304,11 +1303,11 @@ namespace Emby.Dlna.ContentDirectory
             StubType? stubType = null;
 
             // After using PlayTo, MediaMonkey sends a request to the server trying to get item info
-            const string paramsSrch = "Params=";
-            var paramsIndex = id.IndexOf(paramsSrch, StringComparison.OrdinalIgnoreCase);
+            const string ParamsSrch = "Params=";
+            var paramsIndex = id.IndexOf(ParamsSrch, StringComparison.OrdinalIgnoreCase);
             if (paramsIndex != -1)
             {
-                id = id.Substring(paramsIndex + paramsSrch.Length);
+                id = id.Substring(paramsIndex + ParamsSrch.Length);
 
                 var parts = id.Split(';');
                 id = parts[23];

+ 3 - 0
Emby.Dlna/ContentDirectory/ServiceActionListBuilder.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using System.Collections.Generic;
 using Emby.Dlna.Common;
 

+ 3 - 0
Emby.Dlna/ControlRequest.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using System.IO;
 using Microsoft.AspNetCore.Http;
 

+ 8 - 5
Emby.Dlna/ControlResponse.cs

@@ -1,18 +1,21 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using System.Collections.Generic;
 
 namespace Emby.Dlna
 {
     public class ControlResponse
     {
+        public ControlResponse()
+        {
+            Headers = new Dictionary<string, string>();
+        }
+
         public IDictionary<string, string> Headers { get; set; }
 
         public string Xml { get; set; }
 
         public bool IsSuccessful { get; set; }
-
-        public ControlResponse()
-        {
-            Headers = new Dictionary<string, string>();
-        }
     }
 }

+ 4 - 1
Emby.Dlna/Didl/DidlBuilder.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using System;
 using System.Globalization;
 using System.IO;
@@ -631,7 +634,7 @@ namespace Emby.Dlna.Didl
             {
                 if (item.PremiereDate.HasValue)
                 {
-                    AddValue(writer, "dc", "date", item.PremiereDate.Value.ToString("o"), NS_DC);
+                    AddValue(writer, "dc", "date", item.PremiereDate.Value.ToString("o", CultureInfo.InvariantCulture), NS_DC);
                 }
             }
 

+ 3 - 0
Emby.Dlna/Didl/Filter.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using System;
 using MediaBrowser.Model.Extensions;
 

+ 3 - 0
Emby.Dlna/Didl/StringWriterWithEncoding.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using System;
 using System.IO;
 using System.Text;

+ 3 - 0
Emby.Dlna/DlnaManager.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using System;
 using System.Collections.Generic;
 using System.Globalization;

+ 13 - 0
Emby.Dlna/Emby.Dlna.csproj

@@ -15,6 +15,19 @@
     <TargetFramework>netstandard2.1</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
+    <TreatWarningsAsErrors Condition=" '$(Configuration)' == 'Release'" >true</TreatWarningsAsErrors>
+  </PropertyGroup>
+
+  <!-- Code Analyzers-->
+  <ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
+    <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8" PrivateAssets="All" />
+    <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />
+    <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" />
+    <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
+  </ItemGroup>
+
+  <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
+    <CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet>
   </PropertyGroup>
 
   <ItemGroup>

+ 9 - 5
Emby.Dlna/EventSubscriptionResponse.cs

@@ -1,17 +1,21 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using System.Collections.Generic;
 
 namespace Emby.Dlna
 {
     public class EventSubscriptionResponse
     {
-        public string Content { get; set; }
-        public string ContentType { get; set; }
-
-        public Dictionary<string, string> Headers { get; set; }
-
         public EventSubscriptionResponse()
         {
             Headers = new Dictionary<string, string>();
         }
+
+        public string Content { get; set; }
+
+        public string ContentType { get; set; }
+
+        public Dictionary<string, string> Headers { get; set; }
     }
 }

+ 5 - 1
Emby.Dlna/Eventing/EventManager.cs

@@ -1,8 +1,12 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using System;
 using System.Collections.Concurrent;
 using System.Collections.Generic;
 using System.Globalization;
 using System.Linq;
+using System.Net.Http;
 using System.Text;
 using System.Threading.Tasks;
 using MediaBrowser.Common.Extensions;
@@ -164,7 +168,7 @@ namespace Emby.Dlna.Eventing
 
             try
             {
-                using (await _httpClient.SendAsync(options, "NOTIFY").ConfigureAwait(false))
+                using (await _httpClient.SendAsync(options, new HttpMethod("NOTIFY")).ConfigureAwait(false))
                 {
 
                 }

+ 5 - 2
Emby.Dlna/Eventing/EventSubscription.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using System;
 
 namespace Emby.Dlna.Eventing
@@ -13,6 +16,8 @@ namespace Emby.Dlna.Eventing
 
         public long TriggerCount { get; set; }
 
+        public bool IsExpired => SubscriptionTime.AddSeconds(TimeoutSeconds) >= DateTime.UtcNow;
+
         public void IncrementTriggerCount()
         {
             if (TriggerCount == long.MaxValue)
@@ -22,7 +27,5 @@ namespace Emby.Dlna.Eventing
 
             TriggerCount++;
         }
-
-        public bool IsExpired => SubscriptionTime.AddSeconds(TimeoutSeconds) >= DateTime.UtcNow;
     }
 }

+ 2 - 0
Emby.Dlna/IConnectionManager.cs

@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
 
 namespace Emby.Dlna
 {

+ 2 - 0
Emby.Dlna/IContentDirectory.cs

@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
 
 namespace Emby.Dlna
 {

+ 2 - 0
Emby.Dlna/IEventManager.cs

@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
 
 namespace Emby.Dlna
 {

+ 2 - 0
Emby.Dlna/IMediaReceiverRegistrar.cs

@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
 
 namespace Emby.Dlna
 {

+ 3 - 0
Emby.Dlna/IUpnpService.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using System.Threading.Tasks;
 
 namespace Emby.Dlna

+ 3 - 0
Emby.Dlna/Main/DlnaEntryPoint.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using System;
 using System.Net.Sockets;
 using System.Globalization;

+ 23 - 21
Emby.Dlna/MediaReceiverRegistrar/ControlHandler.cs

@@ -1,5 +1,9 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using System;
 using System.Collections.Generic;
+using System.Xml;
 using Emby.Dlna.Service;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Controller.Configuration;
@@ -9,35 +13,33 @@ namespace Emby.Dlna.MediaReceiverRegistrar
 {
     public class ControlHandler : BaseControlHandler
     {
-        protected override IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, IDictionary<string, string> methodParams)
+        public ControlHandler(IServerConfigurationManager config, ILogger logger)
+            : base(config, logger)
         {
-            if (string.Equals(methodName, "IsAuthorized", StringComparison.OrdinalIgnoreCase))
-                return HandleIsAuthorized();
-            if (string.Equals(methodName, "IsValidated", StringComparison.OrdinalIgnoreCase))
-                return HandleIsValidated();
-
-            throw new ResourceNotFoundException("Unexpected control request name: " + methodName);
         }
 
-        private static IEnumerable<KeyValuePair<string, string>> HandleIsAuthorized()
+        /// <inheritdoc />
+        protected override void WriteResult(string methodName, IDictionary<string, string> methodParams, XmlWriter xmlWriter)
         {
-            return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
+            if (string.Equals(methodName, "IsAuthorized", StringComparison.OrdinalIgnoreCase))
             {
-                { "Result", "1" }
-            };
-        }
+                HandleIsAuthorized(xmlWriter);
+                return;
+            }
 
-        private static IEnumerable<KeyValuePair<string, string>> HandleIsValidated()
-        {
-            return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
+            if (string.Equals(methodName, "IsValidated", StringComparison.OrdinalIgnoreCase))
             {
-                { "Result", "1" }
-            };
-        }
+                HandleIsValidated(xmlWriter);
+                return;
+            }
 
-        public ControlHandler(IServerConfigurationManager config, ILogger logger)
-            : base(config, logger)
-        {
+            throw new ResourceNotFoundException("Unexpected control request name: " + methodName);
         }
+
+        private static void HandleIsAuthorized(XmlWriter xmlWriter)
+            => xmlWriter.WriteElementString("Result", "1");
+
+        private static void HandleIsValidated(XmlWriter xmlWriter)
+            => xmlWriter.WriteElementString("Result", "1");
     }
 }

+ 3 - 0
Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrar.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using System.Threading.Tasks;
 using Emby.Dlna.Service;
 using MediaBrowser.Common.Net;

+ 3 - 0
Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarXmlBuilder.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using System.Collections.Generic;
 using Emby.Dlna.Common;
 using Emby.Dlna.Service;

+ 3 - 0
Emby.Dlna/MediaReceiverRegistrar/ServiceActionListBuilder.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using System.Collections.Generic;
 using Emby.Dlna.Common;
 

+ 18 - 15
Emby.Dlna/PlayTo/Device.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using System;
 using System.Collections.Generic;
 using System.Globalization;
@@ -221,7 +224,7 @@ namespace Emby.Dlna.PlayTo
             _logger.LogDebug("Setting mute");
             var value = mute ? 1 : 0;
 
-            await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType, value))
+            await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType, value))
                 .ConfigureAwait(false);
 
             IsMuted = mute;
@@ -251,7 +254,7 @@ namespace Emby.Dlna.PlayTo
             // Remote control will perform better
             Volume = value;
 
-            await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType, value))
+            await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType, value))
                 .ConfigureAwait(false);
         }
 
@@ -270,7 +273,7 @@ namespace Emby.Dlna.PlayTo
                 throw new InvalidOperationException("Unable to find service");
             }
 
-            await new SsdpHttpClient(_httpClient, _config).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("{0:hh}:{0:mm}:{0:ss}", value), "REL_TIME"))
                 .ConfigureAwait(false);
 
             RestartTimer(true);
@@ -302,7 +305,7 @@ namespace Emby.Dlna.PlayTo
             }
 
             var post = avCommands.BuildPost(command, service.ServiceType, url, dictionary);
-            await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, post, header: header)
+            await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, post, header: header)
                 .ConfigureAwait(false);
 
             await Task.Delay(50).ConfigureAwait(false);
@@ -344,7 +347,7 @@ namespace Emby.Dlna.PlayTo
                 throw new InvalidOperationException("Unable to find service");
             }
 
-            return new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, 1));
+            return new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, 1));
         }
 
         public async Task SetPlay(CancellationToken cancellationToken)
@@ -368,7 +371,7 @@ namespace Emby.Dlna.PlayTo
 
             var service = GetAvTransportService();
 
-            await new SsdpHttpClient(_httpClient, _config).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);
 
             RestartTimer(true);
@@ -386,7 +389,7 @@ namespace Emby.Dlna.PlayTo
 
             var service = GetAvTransportService();
 
-            await new SsdpHttpClient(_httpClient, _config).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);
 
             TransportState = TRANSPORTSTATE.PAUSED;
@@ -513,7 +516,7 @@ namespace Emby.Dlna.PlayTo
                 return;
             }
 
-            var result = await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType), true)
+            var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType), true)
                 .ConfigureAwait(false);
 
             if (result == null || result.Document == null)
@@ -559,7 +562,7 @@ namespace Emby.Dlna.PlayTo
                 return;
             }
 
-            var result = await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType), true)
+            var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType), true)
                 .ConfigureAwait(false);
 
             if (result == null || result.Document == null)
@@ -586,7 +589,7 @@ namespace Emby.Dlna.PlayTo
                 return null;
             }
 
-            var result = await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType), false)
+            var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType), false)
                 .ConfigureAwait(false);
 
             if (result == null || result.Document == null)
@@ -624,7 +627,7 @@ namespace Emby.Dlna.PlayTo
 
             var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false);
 
-            var result = await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType), false)
+            var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType), false)
                 .ConfigureAwait(false);
 
             if (result == null || result.Document == null)
@@ -687,7 +690,7 @@ namespace Emby.Dlna.PlayTo
 
             var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false);
 
-            var result = await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType), false)
+            var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType), false)
                 .ConfigureAwait(false);
 
             if (result == null || result.Document == null)
@@ -868,7 +871,7 @@ namespace Emby.Dlna.PlayTo
 
             string url = NormalizeUrl(Properties.BaseUrl, avService.ScpdUrl);
 
-            var httpClient = new SsdpHttpClient(_httpClient, _config);
+            var httpClient = new SsdpHttpClient(_httpClient);
 
             var document = await httpClient.GetDataAsync(url, cancellationToken).ConfigureAwait(false);
 
@@ -896,7 +899,7 @@ namespace Emby.Dlna.PlayTo
 
             string url = NormalizeUrl(Properties.BaseUrl, avService.ScpdUrl);
 
-            var httpClient = new SsdpHttpClient(_httpClient, _config);
+            var httpClient = new SsdpHttpClient(_httpClient);
             _logger.LogDebug("Dlna Device.GetRenderingProtocolAsync");
             var document = await httpClient.GetDataAsync(url, cancellationToken).ConfigureAwait(false);
 
@@ -931,7 +934,7 @@ namespace Emby.Dlna.PlayTo
 
         public static async Task<Device> CreateuPnpDeviceAsync(Uri url, IHttpClient httpClient, IServerConfigurationManager config, ILogger logger, CancellationToken cancellationToken)
         {
-            var ssdpHttpClient = new SsdpHttpClient(httpClient, config);
+            var ssdpHttpClient = new SsdpHttpClient(httpClient);
 
             var document = await ssdpHttpClient.GetDataAsync(url.ToString(), cancellationToken).ConfigureAwait(false);
 

+ 3 - 0
Emby.Dlna/PlayTo/DeviceInfo.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using System.Collections.Generic;
 using Emby.Dlna.Common;
 using MediaBrowser.Model.Dlna;

+ 3 - 0
Emby.Dlna/PlayTo/PlayToController.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using System;
 using System.Collections.Generic;
 using System.Globalization;

+ 7 - 4
Emby.Dlna/PlayTo/PlayToManager.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using System;
 using System.Globalization;
 using System.Linq;
@@ -21,7 +24,7 @@ using Microsoft.Extensions.Logging;
 
 namespace Emby.Dlna.PlayTo
 {
-    class PlayToManager : IDisposable
+    public class PlayToManager : IDisposable
     {
         private readonly ILogger _logger;
         private readonly ISessionManager _sessionManager;
@@ -64,10 +67,10 @@ namespace Emby.Dlna.PlayTo
 
         public void Start()
         {
-            _deviceDiscovery.DeviceDiscovered += _deviceDiscovery_DeviceDiscovered;
+            _deviceDiscovery.DeviceDiscovered += OnDeviceDiscoveryDeviceDiscovered;
         }
 
-        async void _deviceDiscovery_DeviceDiscovered(object sender, GenericEventArgs<UpnpDeviceInfo> e)
+        private async void OnDeviceDiscoveryDeviceDiscovered(object sender, GenericEventArgs<UpnpDeviceInfo> e)
         {
             if (_disposed)
             {
@@ -231,7 +234,7 @@ namespace Emby.Dlna.PlayTo
 
         public void Dispose()
         {
-            _deviceDiscovery.DeviceDiscovered -= _deviceDiscovery_DeviceDiscovered;
+            _deviceDiscovery.DeviceDiscovered -= OnDeviceDiscoveryDeviceDiscovered;
 
             try
             {

+ 3 - 0
Emby.Dlna/PlayTo/PlaybackProgressEventArgs.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using System;
 
 namespace Emby.Dlna.PlayTo

+ 3 - 0
Emby.Dlna/PlayTo/PlaybackStartEventArgs.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using System;
 
 namespace Emby.Dlna.PlayTo

+ 3 - 0
Emby.Dlna/PlayTo/PlaybackStoppedEventArgs.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using System;
 
 namespace Emby.Dlna.PlayTo

+ 3 - 0
Emby.Dlna/PlayTo/PlaylistItem.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using MediaBrowser.Model.Dlna;
 
 namespace Emby.Dlna.PlayTo

+ 3 - 0
Emby.Dlna/PlayTo/PlaylistItemFactory.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using System.IO;
 using System.Linq;
 using MediaBrowser.Controller.Entities;

+ 9 - 6
Emby.Dlna/PlayTo/SsdpHttpClient.cs

@@ -1,13 +1,16 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using System;
 using System.Globalization;
 using System.IO;
+using System.Net.Http;
 using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Xml.Linq;
 using Emby.Dlna.Common;
 using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Configuration;
 
 namespace Emby.Dlna.PlayTo
 {
@@ -19,12 +22,10 @@ namespace Emby.Dlna.PlayTo
         private readonly CultureInfo _usCulture = new CultureInfo("en-US");
 
         private readonly IHttpClient _httpClient;
-        private readonly IServerConfigurationManager _config;
 
-        public SsdpHttpClient(IHttpClient httpClient, IServerConfigurationManager config)
+        public SsdpHttpClient(IHttpClient httpClient)
         {
             _httpClient = httpClient;
-            _config = config;
         }
 
         public async Task<XDocument> SendCommandAsync(
@@ -64,7 +65,9 @@ namespace Emby.Dlna.PlayTo
             }
 
             if (!serviceUrl.StartsWith("/"))
+            {
                 serviceUrl = "/" + serviceUrl;
+            }
 
             return baseUrl + serviceUrl;
         }
@@ -90,7 +93,7 @@ namespace Emby.Dlna.PlayTo
             options.RequestHeaders["NT"] = "upnp:event";
             options.RequestHeaders["TIMEOUT"] = "Second-" + timeOut.ToString(_usCulture);
 
-            using (await _httpClient.SendAsync(options, "SUBSCRIBE").ConfigureAwait(false))
+            using (await _httpClient.SendAsync(options, new HttpMethod("SUBSCRIBE")).ConfigureAwait(false))
             {
 
             }
@@ -110,7 +113,7 @@ namespace Emby.Dlna.PlayTo
 
             options.RequestHeaders["FriendlyName.DLNA.ORG"] = FriendlyName;
 
-            using (var response = await _httpClient.SendAsync(options, "GET").ConfigureAwait(false))
+            using (var response = await _httpClient.SendAsync(options, HttpMethod.Get).ConfigureAwait(false))
             using (var stream = response.Content)
             using (var reader = new StreamReader(stream, Encoding.UTF8))
             {

+ 3 - 0
Emby.Dlna/PlayTo/TRANSPORTSTATE.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 namespace Emby.Dlna.PlayTo
 {
     public enum TRANSPORTSTATE

+ 3 - 0
Emby.Dlna/PlayTo/TransportCommands.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using System;
 using System.Collections.Generic;
 using System.Linq;

+ 3 - 0
Emby.Dlna/PlayTo/UpnpContainer.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using System;
 using System.Xml.Linq;
 using Emby.Dlna.Ssdp;

+ 3 - 0
Emby.Dlna/PlayTo/uBaseObject.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using System;
 
 namespace Emby.Dlna.PlayTo

+ 3 - 0
Emby.Dlna/PlayTo/uPnpNamespaces.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using System.Xml.Linq;
 
 namespace Emby.Dlna.PlayTo

+ 3 - 0
Emby.Dlna/Profiles/DefaultProfile.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using System.Linq;
 using MediaBrowser.Model.Dlna;
 

+ 3 - 0
Emby.Dlna/Profiles/DenonAvrProfile.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using MediaBrowser.Model.Dlna;
 
 namespace Emby.Dlna.Profiles

+ 3 - 0
Emby.Dlna/Profiles/DirectTvProfile.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using MediaBrowser.Model.Dlna;
 
 namespace Emby.Dlna.Profiles

+ 3 - 0
Emby.Dlna/Profiles/DishHopperJoeyProfile.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using MediaBrowser.Model.Dlna;
 
 namespace Emby.Dlna.Profiles

+ 3 - 0
Emby.Dlna/Profiles/Foobar2000Profile.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using MediaBrowser.Model.Dlna;
 
 namespace Emby.Dlna.Profiles

+ 3 - 0
Emby.Dlna/Profiles/LgTvProfile.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using MediaBrowser.Model.Dlna;
 
 namespace Emby.Dlna.Profiles

+ 3 - 0
Emby.Dlna/Profiles/LinksysDMA2100Profile.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using MediaBrowser.Model.Dlna;
 
 namespace Emby.Dlna.Profiles

+ 3 - 0
Emby.Dlna/Profiles/MarantzProfile.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using MediaBrowser.Model.Dlna;
 
 namespace Emby.Dlna.Profiles

+ 3 - 0
Emby.Dlna/Profiles/MediaMonkeyProfile.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using MediaBrowser.Model.Dlna;
 
 namespace Emby.Dlna.Profiles

+ 3 - 0
Emby.Dlna/Profiles/PanasonicVieraProfile.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using MediaBrowser.Model.Dlna;
 
 namespace Emby.Dlna.Profiles

+ 3 - 0
Emby.Dlna/Profiles/PopcornHourProfile.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using MediaBrowser.Model.Dlna;
 
 namespace Emby.Dlna.Profiles

+ 3 - 0
Emby.Dlna/Profiles/SamsungSmartTvProfile.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using MediaBrowser.Model.Dlna;
 
 namespace Emby.Dlna.Profiles

+ 3 - 0
Emby.Dlna/Profiles/SharpSmartTvProfile.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using MediaBrowser.Model.Dlna;
 
 namespace Emby.Dlna.Profiles

+ 3 - 0
Emby.Dlna/Profiles/SonyBlurayPlayer2013.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using MediaBrowser.Model.Dlna;
 
 namespace Emby.Dlna.Profiles

+ 3 - 0
Emby.Dlna/Profiles/SonyBlurayPlayer2014.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using MediaBrowser.Model.Dlna;
 
 namespace Emby.Dlna.Profiles

+ 3 - 0
Emby.Dlna/Profiles/SonyBlurayPlayer2015.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using MediaBrowser.Model.Dlna;
 
 namespace Emby.Dlna.Profiles

+ 3 - 0
Emby.Dlna/Profiles/SonyBlurayPlayer2016.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using MediaBrowser.Model.Dlna;
 
 namespace Emby.Dlna.Profiles

+ 3 - 0
Emby.Dlna/Profiles/SonyBlurayPlayerProfile.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using MediaBrowser.Model.Dlna;
 
 namespace Emby.Dlna.Profiles

+ 3 - 0
Emby.Dlna/Profiles/SonyBravia2010Profile.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using MediaBrowser.Model.Dlna;
 
 namespace Emby.Dlna.Profiles

+ 3 - 0
Emby.Dlna/Profiles/SonyBravia2011Profile.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using MediaBrowser.Model.Dlna;
 
 namespace Emby.Dlna.Profiles

+ 3 - 0
Emby.Dlna/Profiles/SonyBravia2012Profile.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using MediaBrowser.Model.Dlna;
 
 namespace Emby.Dlna.Profiles

+ 3 - 0
Emby.Dlna/Profiles/SonyBravia2013Profile.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using MediaBrowser.Model.Dlna;
 
 namespace Emby.Dlna.Profiles

+ 3 - 0
Emby.Dlna/Profiles/SonyBravia2014Profile.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using MediaBrowser.Model.Dlna;
 
 namespace Emby.Dlna.Profiles

+ 3 - 0
Emby.Dlna/Profiles/SonyPs3Profile.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using MediaBrowser.Model.Dlna;
 
 namespace Emby.Dlna.Profiles

+ 3 - 0
Emby.Dlna/Profiles/SonyPs4Profile.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using MediaBrowser.Model.Dlna;
 
 namespace Emby.Dlna.Profiles

+ 3 - 0
Emby.Dlna/Profiles/WdtvLiveProfile.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using MediaBrowser.Model.Dlna;
 
 namespace Emby.Dlna.Profiles

+ 3 - 0
Emby.Dlna/Profiles/XboxOneProfile.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using MediaBrowser.Model.Dlna;
 
 namespace Emby.Dlna.Profiles

+ 3 - 0
Emby.Dlna/Server/DescriptionXmlBuilder.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using System;
 using System.Collections.Generic;
 using System.Globalization;

+ 8 - 10
Emby.Dlna/Service/BaseControlHandler.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using System;
 using System.Collections.Generic;
 using System.IO;
@@ -65,8 +68,6 @@ namespace Emby.Dlna.Service
 
             Logger.LogDebug("Received control request {0}", requestInfo.LocalName);
 
-            var result = GetResult(requestInfo.LocalName, requestInfo.Headers);
-
             var settings = new XmlWriterSettings
             {
                 Encoding = Encoding.UTF8,
@@ -84,12 +85,9 @@ namespace Emby.Dlna.Service
 
                 writer.WriteStartElement("SOAP-ENV", "Body", NS_SOAPENV);
                 writer.WriteStartElement("u", requestInfo.LocalName + "Response", requestInfo.NamespaceURI);
-                foreach (var i in result)
-                {
-                    writer.WriteStartElement(i.Key);
-                    writer.WriteString(i.Value);
-                    writer.WriteFullEndElement();
-                }
+
+                WriteResult(requestInfo.LocalName, requestInfo.Headers, writer);
+
                 writer.WriteFullEndElement();
                 writer.WriteFullEndElement();
 
@@ -97,7 +95,7 @@ namespace Emby.Dlna.Service
                 writer.WriteEndDocument();
             }
 
-            var xml = builder.ToString().Replace("xmlns:m=", "xmlns:u=");
+            var xml = builder.ToString().Replace("xmlns:m=", "xmlns:u=", StringComparison.Ordinal);
 
             var controlResponse = new ControlResponse
             {
@@ -218,7 +216,7 @@ namespace Emby.Dlna.Service
             public Dictionary<string, string> Headers { get; } = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
         }
 
-        protected abstract IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, IDictionary<string, string> methodParams);
+        protected abstract void WriteResult(string methodName, IDictionary<string, string> methodParams, XmlWriter xmlWriter);
 
         private void LogRequest(ControlRequest request)
         {

+ 3 - 0
Emby.Dlna/Service/BaseService.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using Emby.Dlna.Eventing;
 using MediaBrowser.Common.Net;
 using Microsoft.Extensions.Logging;

+ 3 - 0
Emby.Dlna/Service/ControlErrorHandler.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using System;
 using System.IO;
 using System.Text;

+ 3 - 0
Emby.Dlna/Service/ServiceXmlBuilder.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using System.Collections.Generic;
 using System.Text;
 using Emby.Dlna.Common;

+ 10 - 5
Emby.Dlna/Ssdp/DeviceDiscovery.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using System;
 using System.Collections.Generic;
 using System.Linq;
@@ -9,16 +12,16 @@ using Rssdp.Infrastructure;
 
 namespace Emby.Dlna.Ssdp
 {
-    public class DeviceDiscovery : IDeviceDiscovery
+    public sealed class DeviceDiscovery : IDeviceDiscovery, IDisposable
     {
-        private bool _disposed;
+        private readonly object _syncLock = new object();
 
         private readonly IServerConfigurationManager _config;
 
-        private event EventHandler<GenericEventArgs<UpnpDeviceInfo>> DeviceDiscoveredInternal;
-
         private int _listenerCount;
-        private object _syncLock = new object();
+        private bool _disposed;
+
+        private event EventHandler<GenericEventArgs<UpnpDeviceInfo>> DeviceDiscoveredInternal;
 
         /// <inheritdoc />
         public event EventHandler<GenericEventArgs<UpnpDeviceInfo>> DeviceDiscovered
@@ -33,6 +36,7 @@ namespace Emby.Dlna.Ssdp
 
                 StartInternal();
             }
+
             remove
             {
                 lock (_syncLock)
@@ -130,6 +134,7 @@ namespace Emby.Dlna.Ssdp
             DeviceLeft?.Invoke(this, args);
         }
 
+        /// <inheritdoc />
         public void Dispose()
         {
             if (!_disposed)

+ 3 - 0
Emby.Dlna/Ssdp/Extensions.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using System.Xml.Linq;
 
 namespace Emby.Dlna.Ssdp

+ 1 - 1
Emby.Server.Implementations/ApplicationHost.cs

@@ -1533,7 +1533,7 @@ namespace Emby.Server.Implementations
             string baseUrl = ServerConfigurationManager.Configuration.BaseUrl;
             if (baseUrl.Length != 0)
             {
-                url.Append('/').Append(baseUrl);
+                url.Append(baseUrl);
             }
 
             return url.ToString();

+ 96 - 0
Emby.Server.Implementations/Localization/Core/bn.json

@@ -0,0 +1,96 @@
+{
+    "DeviceOnlineWithName": "{0}-এর সাথে সংযুক্ত হয়েছে",
+    "DeviceOfflineWithName": "{0}-এর সাথে সংযোগ বিচ্ছিন্ন হয়েছে",
+    "Collections": "সংকলন",
+    "ChapterNameValue": "অধ্যায় {0}",
+    "Channels": "চ্যানেল",
+    "CameraImageUploadedFrom": "একটি নতুন ক্যামেরার চিত্র আপলোড করা হয়েছে {0} থেকে",
+    "Books": "বই",
+    "AuthenticationSucceededWithUserName": "{0} যাচাই সফল",
+    "Artists": "শিল্পী",
+    "Application": "অ্যাপ্লিকেশন",
+    "Albums": "অ্যালবামগুলো",
+    "HeaderFavoriteEpisodes": "প্রিব পর্বগুলো",
+    "HeaderFavoriteArtists": "প্রিয় শিল্পীরা",
+    "HeaderFavoriteAlbums": "প্রিয় এলবামগুলো",
+    "HeaderContinueWatching": "দেখতে থাকুন",
+    "HeaderCameraUploads": "ক্যামেরার আপলোডগুলো",
+    "HeaderAlbumArtists": "এলবামের শিল্পী",
+    "Genres": "ঘরানা",
+    "Folders": "ফোল্ডারগুলো",
+    "Favorites": "ফেভারিটগুলো",
+    "FailedLoginAttemptWithUserName": "{0} থেকে লগিন করতে ব্যর্থ",
+    "AppDeviceValues": "এপ: {0}, ডিভাইস: {0}",
+    "VersionNumber": "সংস্করণ {0}",
+    "ValueSpecialEpisodeName": "বিশেষ - {0}",
+    "ValueHasBeenAddedToLibrary": "আপনার লাইব্রেরিতে {0} যোগ করা হয়েছে",
+    "UserStoppedPlayingItemWithValues": "{2}তে {1} বাজানো শেষ করেছেন {0}",
+    "UserStartedPlayingItemWithValues": "{2}তে {1} বাজাচ্ছেন {0}",
+    "UserPolicyUpdatedWithName": "{0} এর জন্য ব্যবহার নীতি আপডেট করা হয়েছে",
+    "UserPasswordChangedWithName": "ব্যবহারকারী {0} এর পাসওয়ার্ড পরিবর্তিত হয়েছে",
+    "UserOnlineFromDevice": "{0}, {1} থেকে অনলাইন",
+    "UserOfflineFromDevice": "{0} {1} থেকে বিযুক্ত হয়ে গেছে",
+    "UserLockedOutWithName": "ব্যবহারকারী {0} ঢুকতে পারছে না",
+    "UserDownloadingItemWithValues": "{0}, {1} ডাউনলোড করছে",
+    "UserDeletedWithName": "ব্যবহারকারী {0}কে বাদ দেয়া হয়েছে",
+    "UserCreatedWithName": "ব্যবহারকারী {0} সৃষ্টি করা হয়েছে",
+    "User": "ব্যবহারকারী",
+    "TvShows": "টিভি শোগুলো",
+    "System": "সিস্টেম",
+    "Sync": "সিংক",
+    "SubtitlesDownloadedForItem": "{0} এর জন্য সাবটাইটেল ডাউনলোড করা হয়েছে",
+    "SubtitleDownloadFailureFromForItem": "{2} থেকে {1} এর জন্য সাবটাইটেল ডাউনলোড ব্যর্থ",
+    "StartupEmbyServerIsLoading": "জেলিফিন সার্ভার লোড হচ্ছে। দয়া করে একটু পরে আবার চেষ্টা করুন।",
+    "Songs": "গানগুলো",
+    "Shows": "টিভি পর্ব",
+    "ServerNameNeedsToBeRestarted": "{0} রিস্টার্ট করা প্রয়োজন",
+    "ScheduledTaskStartedWithName": "{0} শুরু হয়েছে",
+    "ScheduledTaskFailedWithName": "{0} ব্যর্থ",
+    "ProviderValue": "প্রদানকারী: {0}",
+    "PluginUpdatedWithName": "{0} আপডেট করা হয়েছে",
+    "PluginUninstalledWithName": "{0} বাদ দেয়া হয়েছে",
+    "PluginInstalledWithName": "{0} ইন্সটল করা হয়েছে",
+    "Plugin": "প্লাগিন",
+    "Playlists": "প্লেলিস্ট",
+    "Photos": "ছবিগুলো",
+    "NotificationOptionVideoPlaybackStopped": "ভিডিও চলা বন্ধ",
+    "NotificationOptionVideoPlayback": "ভিডিও চলা শুরু হয়েছে",
+    "NotificationOptionUserLockedOut": "ব্যবহারকারী ঢুকতে পারছে না",
+    "NotificationOptionTaskFailed": "পরিকল্পিত কাজটি ব্যর্থ",
+    "NotificationOptionServerRestartRequired": "সার্ভার রিস্টার্ট বাধ্যতামূলক",
+    "NotificationOptionPluginUpdateInstalled": "প্লাগিন আপডেট ইন্সটল করা হয়েছে",
+    "NotificationOptionPluginUninstalled": "প্লাগিন বাদ দেয়া হয়েছে",
+    "NotificationOptionPluginInstalled": "প্লাগিন ইন্সটল করা হয়েছে",
+    "NotificationOptionPluginError": "প্লাগিন ব্যর্থ",
+    "NotificationOptionNewLibraryContent": "নতুন কন্টেন্ট যোগ করা হয়েছে",
+    "NotificationOptionInstallationFailed": "ইন্সটল ব্যর্থ",
+    "NotificationOptionCameraImageUploaded": "ক্যামেরার ছবি আপলোড হয়েছে",
+    "NotificationOptionAudioPlaybackStopped": "গান বাজা বন্ধ হয়েছে",
+    "NotificationOptionAudioPlayback": "গান বাজা শুরু হয়েছে",
+    "NotificationOptionApplicationUpdateInstalled": "এপ্লিকেশনের আপডেট ইনস্টল করা হয়েছে",
+    "NotificationOptionApplicationUpdateAvailable": "এপ্লিকেশনের আপডেট রয়েছে",
+    "NewVersionIsAvailable": "জেলিফিন সার্ভারের একটি নতুন ভার্শন ডাউনলোডের জন্য তৈরী",
+    "NameSeasonUnknown": "সিজন অজানা",
+    "NameSeasonNumber": "সিজন {0}",
+    "NameInstallFailed": "{0} ইন্সটল ব্যর্থ",
+    "MusicVideos": "গানের ভিডিও",
+    "Music": "গান",
+    "Movies": "সিনেমা",
+    "MixedContent": "মিশ্র কন্টেন্ট",
+    "MessageServerConfigurationUpdated": "সার্ভারের কনফিগারেশন হালনাগাদ করা হয়েছে",
+    "HeaderRecordingGroups": "রেকর্ডিং গ্রুপ",
+    "MessageNamedServerConfigurationUpdatedWithValue": "সার্ভারের {0} কনফিগারেসন অংশ আপডেট করা হয়েছে",
+    "MessageApplicationUpdatedTo": "জেলিফিন সার্ভার {0} তে হালনাগাদ করা হয়েছে",
+    "MessageApplicationUpdated": "জেলিফিন সার্ভার হালনাগাদ করা হয়েছে",
+    "Latest": "একদম নতুন",
+    "LabelRunningTimeValue": "চলার সময়: {0}",
+    "LabelIpAddressValue": "আইপি ঠিকানা: {0}",
+    "ItemRemovedWithName": "{0} লাইব্রেরি থেকে বাদ দেয়া হয়েছে",
+    "ItemAddedWithName": "{0} লাইব্রেরিতে যোগ করা হয়েছে",
+    "Inherit": "থেকে পাওয়া",
+    "HomeVideos": "বাসার ভিডিও",
+    "HeaderNextUp": "এরপরে আসছে",
+    "HeaderLiveTV": "লাইভ টিভি",
+    "HeaderFavoriteSongs": "প্রিয় গানগুলো",
+    "HeaderFavoriteShows": "প্রিয় শোগুলো"
+}

+ 1 - 0
Emby.Server.Implementations/Localization/Core/es_DO.json

@@ -0,0 +1 @@
+{}

+ 60 - 12
Emby.Server.Implementations/Localization/Core/fi.json

@@ -1,6 +1,6 @@
 {
-    "HeaderLiveTV": "Netti-TV",
-    "NewVersionIsAvailable": "Uusi versio Jellyfin palvelimesta on ladattavissa",
+    "HeaderLiveTV": "TV-lähetykset",
+    "NewVersionIsAvailable": "Uusi versio Jellyfin palvelimesta on ladattavissa.",
     "NameSeasonUnknown": "Tuntematon Kausi",
     "NameSeasonNumber": "Kausi {0}",
     "NameInstallFailed": "{0} asennus epäonnistui",
@@ -8,28 +8,28 @@
     "Music": "Musiikki",
     "Movies": "Elokuvat",
     "MixedContent": "Sekoitettu sisältö",
-    "MessageServerConfigurationUpdated": "Palvelimen konfiguraatio on päivitetty",
-    "MessageNamedServerConfigurationUpdatedWithValue": "Palvelimen konfiguraatio-osa {0} on päivitetty",
-    "MessageApplicationUpdatedTo": "Jellyfin palvelin on päivitetty {0}",
+    "MessageServerConfigurationUpdated": "Palvelimen asetukset on päivitetty",
+    "MessageNamedServerConfigurationUpdatedWithValue": "Palvelimen asetusryhmä {0} on päivitetty",
+    "MessageApplicationUpdatedTo": "Jellyfin palvelin on päivitetty versioon {0}",
     "MessageApplicationUpdated": "Jellyfin palvelin on päivitetty",
     "Latest": "Viimeisin",
-    "LabelRunningTimeValue": "Kesto: {0}",
+    "LabelRunningTimeValue": "Toiston kesto: {0}",
     "LabelIpAddressValue": "IP-osoite: {0}",
     "ItemRemovedWithName": "{0} poistettiin kirjastosta",
     "ItemAddedWithName": "{0} lisättiin kirjastoon",
     "Inherit": "Periä",
     "HomeVideos": "Kotivideot",
-    "HeaderRecordingGroups": "Äänitysryhmä",
+    "HeaderRecordingGroups": "Äänitysryhmät",
     "HeaderNextUp": "Seuraavaksi",
     "HeaderFavoriteSongs": "Lempikappaleet",
     "HeaderFavoriteShows": "Lempisarjat",
     "HeaderFavoriteEpisodes": "Lempijaksot",
-    "HeaderCameraUploads": "Kamerasta Ladatut",
+    "HeaderCameraUploads": "Kamerasta lähetetyt",
     "HeaderFavoriteArtists": "Lempiartistit",
     "HeaderFavoriteAlbums": "Lempialbumit",
-    "HeaderContinueWatching": "Jatka Katsomista",
-    "HeaderAlbumArtists": "Albumiartistit",
-    "Genres": "Genret",
+    "HeaderContinueWatching": "Jatka katsomista",
+    "HeaderAlbumArtists": "Albumin artistit",
+    "Genres": "Tyylilaji",
     "Folders": "Kansiot",
     "Favorites": "Suosikit",
     "FailedLoginAttemptWithUserName": "Epäonnistunut kirjautumisyritys kohteesta {0}",
@@ -44,5 +44,53 @@
     "Artists": "Artistit",
     "Application": "Sovellus",
     "AppDeviceValues": "Sovellus: {0}, Laite: {1}",
-    "Albums": "Albumit"
+    "Albums": "Albumit",
+    "User": "Käyttäjä",
+    "System": "Järjestelmä",
+    "ScheduledTaskFailedWithName": "{0} epäonnistui",
+    "PluginUpdatedWithName": "{0} päivitetty",
+    "PluginInstalledWithName": "{0} asennettu",
+    "Photos": "Kuvat",
+    "ScheduledTaskStartedWithName": "{0} aloitettu",
+    "PluginUninstalledWithName": "{0} poistettu",
+    "Playlists": "Soittolistat",
+    "VersionNumber": "Versio {0}",
+    "ValueSpecialEpisodeName": "Erikois - {0}",
+    "ValueHasBeenAddedToLibrary": "{0} lisättiin mediakirjastoon",
+    "UserStoppedPlayingItemWithValues": "{0} toistaminen valmistui {1} laitteella {2}",
+    "UserStartedPlayingItemWithValues": "{0} toistaa {1} laitteella {2}",
+    "UserPolicyUpdatedWithName": "Käyttöoikeudet päivitetty käyttäjälle {0}",
+    "UserPasswordChangedWithName": "Salasana vaihdettu käyttäjälle {0}",
+    "UserOnlineFromDevice": "{0} on paikalla osoitteesta {1}",
+    "UserOfflineFromDevice": "{0} yhteys katkaistu {1}",
+    "UserLockedOutWithName": "Käyttäjä {0} kirjautui ulos",
+    "UserDownloadingItemWithValues": "{0} latautumassa {1}",
+    "UserDeletedWithName": "Poistettiin käyttäjä {0}",
+    "UserCreatedWithName": "Luotiin käyttäjä {0}",
+    "TvShows": "TV-Ohjelmat",
+    "Sync": "Synkronoi",
+    "SubtitlesDownloadedForItem": "Tekstitys ladattu {0}",
+    "SubtitleDownloadFailureFromForItem": "Tekstityksen lataaminen epäonnistui {0} - {1}",
+    "StartupEmbyServerIsLoading": "Jellyfin palvelin latautuu. Kokeile hetken kuluttua uudelleen.",
+    "Songs": "Kappaleet",
+    "Shows": "Ohjelmat",
+    "ServerNameNeedsToBeRestarted": "{0} vaatii uudelleenkäynnistyksen",
+    "ProviderValue": "Palveluntarjoaja: {0}",
+    "Plugin": "Laajennus",
+    "NotificationOptionVideoPlaybackStopped": "Videon toistaminen pysäytetty",
+    "NotificationOptionVideoPlayback": "Videon toistaminen aloitettu",
+    "NotificationOptionUserLockedOut": "Käyttäjä kirjautui ulos",
+    "NotificationOptionTaskFailed": "Ajastetun tehtävän ongelma",
+    "NotificationOptionServerRestartRequired": "Palvelimen uudelleenkäynnistys vaaditaan",
+    "NotificationOptionPluginUpdateInstalled": "Laajennuksen päivitys asennettu",
+    "NotificationOptionPluginUninstalled": "Laajennus poistettu",
+    "NotificationOptionPluginInstalled": "Laajennus asennettu",
+    "NotificationOptionPluginError": "Ongelma laajennuksessa",
+    "NotificationOptionNewLibraryContent": "Uusi sisältö lisätty",
+    "NotificationOptionInstallationFailed": "Asennusvirhe",
+    "NotificationOptionCameraImageUploaded": "Kameran kuva lisätty",
+    "NotificationOptionAudioPlaybackStopped": "Äänen toistaminen pysäytetty",
+    "NotificationOptionAudioPlayback": "Äänen toistaminen aloitettu",
+    "NotificationOptionApplicationUpdateInstalled": "Ohjelmistopäivitys asennettu",
+    "NotificationOptionApplicationUpdateAvailable": "Ohjelmistopäivitys saatavilla"
 }

+ 3 - 1
Emby.Server.Implementations/Localization/Core/gl.json

@@ -1 +1,3 @@
-{}
+{
+    "Albums": "Álbumes"
+}

+ 4 - 4
Emby.Server.Implementations/Localization/Core/hu.json

@@ -1,7 +1,7 @@
 {
     "Albums": "Albumok",
     "AppDeviceValues": "Program: {0}, Eszköz: {1}",
-    "Application": "Program",
+    "Application": "Alkalmazás",
     "Artists": "Előadók",
     "AuthenticationSucceededWithUserName": "{0} sikeresen azonosítva",
     "Books": "Könyvek",
@@ -17,7 +17,7 @@
     "Genres": "Műfajok",
     "HeaderAlbumArtists": "Album előadók",
     "HeaderCameraUploads": "Kamera feltöltések",
-    "HeaderContinueWatching": "Folyamatban lévő filmek",
+    "HeaderContinueWatching": "Megtekintés folytatása",
     "HeaderFavoriteAlbums": "Kedvenc albumok",
     "HeaderFavoriteArtists": "Kedvenc előadók",
     "HeaderFavoriteEpisodes": "Kedvenc epizódok",
@@ -27,7 +27,7 @@
     "HeaderNextUp": "Következik",
     "HeaderRecordingGroups": "Felvételi csoportok",
     "HomeVideos": "Házi videók",
-    "Inherit": "Inherit",
+    "Inherit": "Öröklés",
     "ItemAddedWithName": "{0} hozzáadva a könyvtárhoz",
     "ItemRemovedWithName": "{0} eltávolítva a könyvtárból",
     "LabelIpAddressValue": "IP cím: {0}",
@@ -42,7 +42,7 @@
     "Music": "Zene",
     "MusicVideos": "Zenei videók",
     "NameInstallFailed": "{0} sikertelen telepítés",
-    "NameSeasonNumber": "Évad {0}",
+    "NameSeasonNumber": "{0}. évad",
     "NameSeasonUnknown": "Ismeretlen évad",
     "NewVersionIsAvailable": "Letölthető a Jellyfin Szerver új verziója.",
     "NotificationOptionApplicationUpdateAvailable": "Frissítés érhető el az alkalmazáshoz",

+ 6 - 1
Emby.Server.Implementations/Localization/Core/id.json

@@ -86,5 +86,10 @@
     "FailedLoginAttemptWithUserName": "Percobaan login gagal dari {0}",
     "CameraImageUploadedFrom": "Sebuah gambar baru telah diunggah dari {0}",
     "DeviceOfflineWithName": "{0} telah terputus",
-    "DeviceOnlineWithName": "{0} telah terhubung"
+    "DeviceOnlineWithName": "{0} telah terhubung",
+    "NotificationOptionVideoPlaybackStopped": "Pemutaran video berhenti",
+    "NotificationOptionVideoPlayback": "Pemutaran video dimulai",
+    "NotificationOptionAudioPlaybackStopped": "Pemutaran audio berhenti",
+    "NotificationOptionAudioPlayback": "Pemutaran audio dimulai",
+    "MixedContent": "Konten campur"
 }

+ 4 - 4
Emby.Server.Implementations/Localization/Core/zh-TW.json

@@ -50,10 +50,10 @@
     "NotificationOptionCameraImageUploaded": "相機相片已上傳",
     "NotificationOptionInstallationFailed": "安裝失敗",
     "NotificationOptionNewLibraryContent": "已新增新內容",
-    "NotificationOptionPluginError": "外掛失敗",
-    "NotificationOptionPluginInstalled": "外掛已安裝",
-    "NotificationOptionPluginUninstalled": "外掛已移除",
-    "NotificationOptionPluginUpdateInstalled": "已更新外掛",
+    "NotificationOptionPluginError": "擴充元件安裝失敗",
+    "NotificationOptionPluginInstalled": "擴充元件已安裝",
+    "NotificationOptionPluginUninstalled": "擴充元件已移除",
+    "NotificationOptionPluginUpdateInstalled": "已更新擴充元件",
     "NotificationOptionServerRestartRequired": "伺服器需要重新啟動",
     "NotificationOptionTaskFailed": "排程任務失敗",
     "NotificationOptionUserLockedOut": "使用者已鎖定",

+ 15 - 16
Emby.Server.Implementations/Session/SessionManager.cs

@@ -1401,20 +1401,16 @@ namespace Emby.Server.Implementations.Session
                 user = _userManager.GetUserByName(request.Username);
             }
 
-            if (user != null)
+            if (user == null)
             {
-                // TODO: Move this to userManager?
-                if (!string.IsNullOrEmpty(request.DeviceId)
-                    && !_deviceManager.CanAccessDevice(user, request.DeviceId))
-                {
-                    throw new SecurityException("User is not allowed access from this device.");
-                }
+                AuthenticationFailed?.Invoke(this, new GenericEventArgs<AuthenticationRequest>(request));
+                throw new SecurityException("Invalid username or password entered.");
             }
 
-            if (user == null)
+            if (!string.IsNullOrEmpty(request.DeviceId)
+                && !_deviceManager.CanAccessDevice(user, request.DeviceId))
             {
-                AuthenticationFailed?.Invoke(this, new GenericEventArgs<AuthenticationRequest>(request));
-                throw new SecurityException("Invalid user or password entered.");
+                throw new SecurityException("User is not allowed access from this device.");
             }
 
             if (enforcePassword)
@@ -1727,16 +1723,16 @@ namespace Emby.Server.Implementations.Session
         /// <inheritdoc />
         public void ReportNowViewingItem(string sessionId, BaseItemDto item)
         {
-            throw new NotImplementedException();
+            var session = GetSession(sessionId);
 
-            // var session = GetSession(sessionId);
-            // session.NowViewingItem = item;
+            session.NowViewingItem = item;
         }
 
         /// <inheritdoc />
         public void ReportTranscodingInfo(string deviceId, TranscodingInfo info)
         {
-            var session = Sessions.FirstOrDefault(i => string.Equals(i.DeviceId, deviceId, StringComparison.Ordinal));
+            var session = Sessions.FirstOrDefault(i =>
+                string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase));
 
             if (session != null)
             {
@@ -1752,8 +1748,11 @@ namespace Emby.Server.Implementations.Session
 
         /// <inheritdoc />
         public SessionInfo GetSession(string deviceId, string client, string version)
-            => Sessions.FirstOrDefault(
-                i => string.Equals(i.DeviceId, deviceId, StringComparison.Ordinal) && string.Equals(i.Client, client, StringComparison.Ordinal));
+        {
+            return Sessions.FirstOrDefault(i =>
+                string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase)
+                    && string.Equals(i.Client, client, StringComparison.OrdinalIgnoreCase));
+        }
 
         /// <inheritdoc />
         public SessionInfo GetSessionByAuthenticationToken(AuthenticationInfo info, string deviceId, string remoteEndpoint, string appVersion)

この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません