From da9a59de1e0837d2a4b030d59fa8d009b4457439 Mon Sep 17 00:00:00 2001 From: Steve Hayles Date: Thu, 31 Oct 2019 18:48:34 +0000 Subject: [PATCH 001/270] Allow valid https requests in .NET Core ServerCertificateValidationCallback on the ServicePointManager is not supported in .NET Core and outgoing https requests will fail if the certificate is not trusted. This adds the equivalent functionality --- .../HttpClientManager/HttpClientManager.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs index 0e6083773d..c465030906 100644 --- a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs +++ b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs @@ -59,7 +59,17 @@ namespace Emby.Server.Implementations.HttpClientManager if (!_httpClients.TryGetValue(key, out var client)) { - client = new HttpClient() + var httpClientHandler = new HttpClientHandler() + { + ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => + { + var success = errors == System.Net.Security.SslPolicyErrors.None; + _logger.LogDebug("Validating certificate {Cert}. Success {1}", cert, success); + return success; + } + }; + + client = new HttpClient(httpClientHandler) { BaseAddress = new Uri(url) }; From be956dfd0270c010ef6ed6904b4bada628717155 Mon Sep 17 00:00:00 2001 From: Artiume Date: Sun, 15 Dec 2019 23:52:07 -0500 Subject: [PATCH 002/270] Update MediaInfoService.cs --- MediaBrowser.Api/Playback/MediaInfoService.cs | 143 ++++++++++++------ 1 file changed, 93 insertions(+), 50 deletions(-) diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index c3032416b8..1730db89a7 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -407,8 +407,13 @@ namespace MediaBrowser.Api.Playback if (mediaSource.SupportsDirectPlay) { - if (mediaSource.IsRemote && forceDirectPlayRemoteMediaSource) + if (mediaSource.IsRemote && forceDirectPlayRemoteMediaSource && user.Policy.ForceRemoteSourceTranscoding) { + mediaSource.SupportsDirectPlay = false; + } + else if (mediaSource.IsRemote && user.Policy.ForceRemoteSourceTranscoding) + { + mediaSource.SupportsDirectPlay = false; } else { @@ -455,74 +460,112 @@ namespace MediaBrowser.Api.Playback if (mediaSource.SupportsDirectStream) { - options.MaxBitrate = GetMaxBitrate(maxBitrate, user); - - if (item is Audio) - { - if (!user.Policy.EnableAudioPlaybackTranscoding) - { - options.ForceDirectStream = true; - } - } - else if (item is Video) - { - if (!user.Policy.EnableAudioPlaybackTranscoding && !user.Policy.EnableVideoPlaybackTranscoding && !user.Policy.EnablePlaybackRemuxing) - { - options.ForceDirectStream = true; - } - } - - // The MediaSource supports direct stream, now test to see if the client supports it - var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ? - streamBuilder.BuildAudioItem(options) : - streamBuilder.BuildVideoItem(options); - - if (streamInfo == null || !streamInfo.IsDirectStream) + if (mediaSource.IsRemote && forceDirectPlayRemoteMediaSource && user.Policy.ForceRemoteSourceTranscoding) { mediaSource.SupportsDirectStream = false; } - - if (streamInfo != null) + else if (mediaSource.IsRemote && user.Policy.ForceRemoteSourceTranscoding) { - SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token); + mediaSource.SupportsDirectStream = false; } - } + } + else + { + options.MaxBitrate = GetMaxBitrate(maxBitrate, user); + { + if (item is Audio) + if (!user.Policy.EnableAudioPlaybackTranscoding) + { + options.ForceDirectStream = true; + } + } + else if (item is Video) + { + if (!user.Policy.EnableAudioPlaybackTranscoding && !user.Policy.EnableVideoPlaybackTranscoding && !user.Policy.EnablePlaybackRemuxing) + { + options.ForceDirectStream = true; + } + } + // The MediaSource supports direct stream, now test to see if the client supports it + var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ? + streamBuilder.BuildAudioItem(options) : + streamBuilder.BuildVideoItem(options); + if (streamInfo == null || !streamInfo.IsDirectStream) + { + mediaSource.SupportsDirectStream = false; + } + if (streamInfo != null) + { + SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token); + } + } + } if (mediaSource.SupportsTranscoding) { - options.MaxBitrate = GetMaxBitrate(maxBitrate, user); - + if (mediaSource.IsRemote && user.Policy.ForceRemoteSourceTranscoding) + { + if (GetMaxBitrate(maxBitrate, user) < mediaSource.Bitrate) + { + options.MaxBitrate = GetMaxBitrate(maxBitrate, user); + } + else + { + options.MaxBitrate = mediaSource.Bitrate; + } + } + else + { + options.MaxBitrate = GetMaxBitrate(maxBitrate, user); + } + // The MediaSource supports direct stream, now test to see if the client supports it var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ? streamBuilder.BuildAudioItem(options) : streamBuilder.BuildVideoItem(options); - if (streamInfo != null) - { - streamInfo.PlaySessionId = playSessionId; - - if (streamInfo.PlayMethod == PlayMethod.Transcode) + if (mediaSource.IsRemote && user.Policy.ForceRemoteSourceTranscoding) + { + if (streamInfo != null) { + streamInfo.PlaySessionId = playSessionId; streamInfo.StartPositionTicks = startTimeTicks; mediaSource.TranscodingUrl = streamInfo.ToUrl("-", auth.Token).TrimStart('-'); - - if (!allowVideoStreamCopy) - { - mediaSource.TranscodingUrl += "&allowVideoStreamCopy=false"; - } - if (!allowAudioStreamCopy) - { - mediaSource.TranscodingUrl += "&allowAudioStreamCopy=false"; - } + mediaSource.TranscodingUrl += "&allowVideoStreamCopy=false"; + mediaSource.TranscodingUrl += "&allowAudioStreamCopy=false"; mediaSource.TranscodingContainer = streamInfo.Container; mediaSource.TranscodingSubProtocol = streamInfo.SubProtocol; - } + + // Do this after the above so that StartPositionTicks is set + SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token); + } + } + else + { + if (streamInfo != null) + { + streamInfo.PlaySessionId = playSessionId; + if (streamInfo.PlayMethod == PlayMethod.Transcode) + { + streamInfo.StartPositionTicks = startTimeTicks; + mediaSource.TranscodingUrl = streamInfo.ToUrl("-", auth.Token).TrimStart('-'); - // Do this after the above so that StartPositionTicks is set - SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token); - } - } - } + if (!allowVideoStreamCopy) + { + mediaSource.TranscodingUrl += "&allowVideoStreamCopy=false"; + } + if (!allowAudioStreamCopy) + { + mediaSource.TranscodingUrl += "&allowAudioStreamCopy=false"; + } + mediaSource.TranscodingContainer = streamInfo.Container; + mediaSource.TranscodingSubProtocol = streamInfo.SubProtocol; + } + // Do this after the above so that StartPositionTicks is set + SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token); + } + } + } private long? GetMaxBitrate(long? clientMaxBitrate, User user) { From 77fc77eb823da523a9d37695adf00561781c0c8e Mon Sep 17 00:00:00 2001 From: Artiume Date: Sun, 15 Dec 2019 23:59:46 -0500 Subject: [PATCH 003/270] Update UserPolicy.cs --- MediaBrowser.Model/Users/UserPolicy.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.Model/Users/UserPolicy.cs b/MediaBrowser.Model/Users/UserPolicy.cs index 9c3e1f9808..8a6b49d8b2 100644 --- a/MediaBrowser.Model/Users/UserPolicy.cs +++ b/MediaBrowser.Model/Users/UserPolicy.cs @@ -44,6 +44,7 @@ namespace MediaBrowser.Model.Users public bool EnableAudioPlaybackTranscoding { get; set; } public bool EnableVideoPlaybackTranscoding { get; set; } public bool EnablePlaybackRemuxing { get; set; } + public bool ForceRemoteSourceTranscoding { get; set; } public bool EnableContentDeletion { get; set; } public string[] EnableContentDeletionFromFolders { get; set; } @@ -91,7 +92,8 @@ namespace MediaBrowser.Model.Users EnableAudioPlaybackTranscoding = true; EnableVideoPlaybackTranscoding = true; EnablePlaybackRemuxing = true; - + ForceRemoteSourceTranscoding = false; + EnableLiveTvManagement = true; EnableLiveTvAccess = true; From 64c313a8fb007b7002d0de4e67eb90941e06fd06 Mon Sep 17 00:00:00 2001 From: Artiume Date: Mon, 16 Dec 2019 00:27:48 -0500 Subject: [PATCH 004/270] Update MediaInfoService.cs --- MediaBrowser.Api/Playback/MediaInfoService.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index 1730db89a7..78e7bf454c 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -564,9 +564,10 @@ namespace MediaBrowser.Api.Playback // Do this after the above so that StartPositionTicks is set SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token); } - } - } - + } + } + } + private long? GetMaxBitrate(long? clientMaxBitrate, User user) { var maxBitrate = clientMaxBitrate; From 3fb7aabfde0e3c67d6abe8345de8ef92279b038e Mon Sep 17 00:00:00 2001 From: Artiume Date: Mon, 16 Dec 2019 00:40:25 -0500 Subject: [PATCH 005/270] Update MediaInfoService.cs --- MediaBrowser.Api/Playback/MediaInfoService.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index 78e7bf454c..4cf29d72c4 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -671,5 +671,4 @@ namespace MediaBrowser.Api.Playback }).ThenBy(originalList.IndexOf) .ToArray(); } - } } From 46442e24f8e2fadc1e0e762631fd1f75da009ba5 Mon Sep 17 00:00:00 2001 From: Artiume Date: Mon, 16 Dec 2019 00:43:03 -0500 Subject: [PATCH 006/270] Update MediaInfoService.cs --- MediaBrowser.Api/Playback/MediaInfoService.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index 4cf29d72c4..78e7bf454c 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -671,4 +671,5 @@ namespace MediaBrowser.Api.Playback }).ThenBy(originalList.IndexOf) .ToArray(); } + } } From 2457e43decdc17758a2822227611d7f06bb4b7b8 Mon Sep 17 00:00:00 2001 From: Artiume Date: Mon, 16 Dec 2019 00:44:10 -0500 Subject: [PATCH 007/270] Update Dockerfile --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 2a60bf1845..b633e56893 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,7 @@ ARG FFMPEG_VERSION=latest FROM node:alpine as web-builder ARG JELLYFIN_WEB_VERSION=master RUN apk add curl \ - && curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \ + && curl -L https://github.com/artiume/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \ && cd jellyfin-web-* \ && yarn install \ && yarn build \ From 0d571b3ad1506a5dd40bdcb2bf73f85ae7415a24 Mon Sep 17 00:00:00 2001 From: Artiume Date: Mon, 16 Dec 2019 00:59:38 -0500 Subject: [PATCH 008/270] Update Dockerfile --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index b633e56893..f100c27a34 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,8 +4,8 @@ ARG FFMPEG_VERSION=latest FROM node:alpine as web-builder ARG JELLYFIN_WEB_VERSION=master RUN apk add curl \ - && curl -L https://github.com/artiume/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \ - && cd jellyfin-web-* \ + && curl -L https://github.com/artiume/jellyfin-web \ + && cd jellyfin-web \ && yarn install \ && yarn build \ && mv dist /dist From c96d3c35a0fc6f919554156473466eef5a7faebf Mon Sep 17 00:00:00 2001 From: Artiume Date: Mon, 16 Dec 2019 01:09:45 -0500 Subject: [PATCH 009/270] Update Dockerfile --- Dockerfile | 1 - 1 file changed, 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index f100c27a34..e7d36316de 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,6 @@ ARG FFMPEG_VERSION=latest FROM node:alpine as web-builder ARG JELLYFIN_WEB_VERSION=master RUN apk add curl \ - && curl -L https://github.com/artiume/jellyfin-web \ && cd jellyfin-web \ && yarn install \ && yarn build \ From 4e43c70439605b6d92991ddb69387b7d17b3f501 Mon Sep 17 00:00:00 2001 From: Artiume Date: Mon, 16 Dec 2019 01:12:50 -0500 Subject: [PATCH 010/270] Update Dockerfile --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index e7d36316de..ea5669eaac 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,6 +3,7 @@ ARG FFMPEG_VERSION=latest FROM node:alpine as web-builder ARG JELLYFIN_WEB_VERSION=master +COPY --from=builder /jellyfin/jellyfin-web /jellyfin/jellyfin-web RUN apk add curl \ && cd jellyfin-web \ && yarn install \ From 5a9e08e0de3b088f6a7b4067cfee92f7d20fb64b Mon Sep 17 00:00:00 2001 From: Artiume Date: Mon, 16 Dec 2019 01:26:36 -0500 Subject: [PATCH 011/270] Update Dockerfile --- Dockerfile | 1 - 1 file changed, 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index ea5669eaac..e7d36316de 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,6 @@ ARG FFMPEG_VERSION=latest FROM node:alpine as web-builder ARG JELLYFIN_WEB_VERSION=master -COPY --from=builder /jellyfin/jellyfin-web /jellyfin/jellyfin-web RUN apk add curl \ && cd jellyfin-web \ && yarn install \ From b31f4ccbc2299b8b327b3ffa3ffc0d40f8b08bf5 Mon Sep 17 00:00:00 2001 From: Artiume Date: Mon, 16 Dec 2019 14:53:42 -0500 Subject: [PATCH 012/270] Update MediaInfoService.cs --- MediaBrowser.Api/Playback/MediaInfoService.cs | 156 +++++++----------- 1 file changed, 56 insertions(+), 100 deletions(-) diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index 78e7bf454c..c3032416b8 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -407,13 +407,8 @@ namespace MediaBrowser.Api.Playback if (mediaSource.SupportsDirectPlay) { - if (mediaSource.IsRemote && forceDirectPlayRemoteMediaSource && user.Policy.ForceRemoteSourceTranscoding) + if (mediaSource.IsRemote && forceDirectPlayRemoteMediaSource) { - mediaSource.SupportsDirectPlay = false; - } - else if (mediaSource.IsRemote && user.Policy.ForceRemoteSourceTranscoding) - { - mediaSource.SupportsDirectPlay = false; } else { @@ -460,114 +455,75 @@ namespace MediaBrowser.Api.Playback if (mediaSource.SupportsDirectStream) { - if (mediaSource.IsRemote && forceDirectPlayRemoteMediaSource && user.Policy.ForceRemoteSourceTranscoding) - { - mediaSource.SupportsDirectStream = false; - } - else if (mediaSource.IsRemote && user.Policy.ForceRemoteSourceTranscoding) - { - mediaSource.SupportsDirectStream = false; - } - } - else - { - options.MaxBitrate = GetMaxBitrate(maxBitrate, user); - { - if (item is Audio) - if (!user.Policy.EnableAudioPlaybackTranscoding) - { - options.ForceDirectStream = true; - } - } - else if (item is Video) - { - if (!user.Policy.EnableAudioPlaybackTranscoding && !user.Policy.EnableVideoPlaybackTranscoding && !user.Policy.EnablePlaybackRemuxing) - { - options.ForceDirectStream = true; - } - } - // The MediaSource supports direct stream, now test to see if the client supports it - var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ? - streamBuilder.BuildAudioItem(options) : - streamBuilder.BuildVideoItem(options); - if (streamInfo == null || !streamInfo.IsDirectStream) - { - mediaSource.SupportsDirectStream = false; - } - if (streamInfo != null) - { - SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token); - } - } - } + options.MaxBitrate = GetMaxBitrate(maxBitrate, user); + + if (item is Audio) + { + if (!user.Policy.EnableAudioPlaybackTranscoding) + { + options.ForceDirectStream = true; + } + } + else if (item is Video) + { + if (!user.Policy.EnableAudioPlaybackTranscoding && !user.Policy.EnableVideoPlaybackTranscoding && !user.Policy.EnablePlaybackRemuxing) + { + options.ForceDirectStream = true; + } + } - if (mediaSource.SupportsTranscoding) - { - if (mediaSource.IsRemote && user.Policy.ForceRemoteSourceTranscoding) - { - if (GetMaxBitrate(maxBitrate, user) < mediaSource.Bitrate) - { - options.MaxBitrate = GetMaxBitrate(maxBitrate, user); - } - else - { - options.MaxBitrate = mediaSource.Bitrate; - } - } - else - { - options.MaxBitrate = GetMaxBitrate(maxBitrate, user); - } - // The MediaSource supports direct stream, now test to see if the client supports it var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ? streamBuilder.BuildAudioItem(options) : streamBuilder.BuildVideoItem(options); - if (mediaSource.IsRemote && user.Policy.ForceRemoteSourceTranscoding) - { - if (streamInfo != null) + if (streamInfo == null || !streamInfo.IsDirectStream) + { + mediaSource.SupportsDirectStream = false; + } + + if (streamInfo != null) + { + SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token); + } + } + + if (mediaSource.SupportsTranscoding) + { + options.MaxBitrate = GetMaxBitrate(maxBitrate, user); + + // The MediaSource supports direct stream, now test to see if the client supports it + var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ? + streamBuilder.BuildAudioItem(options) : + streamBuilder.BuildVideoItem(options); + + if (streamInfo != null) + { + streamInfo.PlaySessionId = playSessionId; + + if (streamInfo.PlayMethod == PlayMethod.Transcode) { - streamInfo.PlaySessionId = playSessionId; streamInfo.StartPositionTicks = startTimeTicks; mediaSource.TranscodingUrl = streamInfo.ToUrl("-", auth.Token).TrimStart('-'); - mediaSource.TranscodingUrl += "&allowVideoStreamCopy=false"; - mediaSource.TranscodingUrl += "&allowAudioStreamCopy=false"; + + if (!allowVideoStreamCopy) + { + mediaSource.TranscodingUrl += "&allowVideoStreamCopy=false"; + } + if (!allowAudioStreamCopy) + { + mediaSource.TranscodingUrl += "&allowAudioStreamCopy=false"; + } mediaSource.TranscodingContainer = streamInfo.Container; mediaSource.TranscodingSubProtocol = streamInfo.SubProtocol; - - // Do this after the above so that StartPositionTicks is set - SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token); - } - } - else - { - if (streamInfo != null) - { - streamInfo.PlaySessionId = playSessionId; - if (streamInfo.PlayMethod == PlayMethod.Transcode) - { - streamInfo.StartPositionTicks = startTimeTicks; - mediaSource.TranscodingUrl = streamInfo.ToUrl("-", auth.Token).TrimStart('-'); - - if (!allowVideoStreamCopy) - { - mediaSource.TranscodingUrl += "&allowVideoStreamCopy=false"; - } - if (!allowAudioStreamCopy) - { - mediaSource.TranscodingUrl += "&allowAudioStreamCopy=false"; - } - mediaSource.TranscodingContainer = streamInfo.Container; - mediaSource.TranscodingSubProtocol = streamInfo.SubProtocol; - } - // Do this after the above so that StartPositionTicks is set - SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token); } - } + + // Do this after the above so that StartPositionTicks is set + SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token); + } } } - + private long? GetMaxBitrate(long? clientMaxBitrate, User user) { var maxBitrate = clientMaxBitrate; From f3e7c72bacb0c5682064b9cb3492b33a96dcc697 Mon Sep 17 00:00:00 2001 From: Artiume Date: Mon, 16 Dec 2019 15:22:18 -0500 Subject: [PATCH 013/270] Update MediaInfoService.cs --- MediaBrowser.Api/Playback/MediaInfoService.cs | 144 ++++++++++++------ 1 file changed, 97 insertions(+), 47 deletions(-) diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index c3032416b8..09805b8054 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -407,8 +407,13 @@ namespace MediaBrowser.Api.Playback if (mediaSource.SupportsDirectPlay) { - if (mediaSource.IsRemote && forceDirectPlayRemoteMediaSource) + if (mediaSource.IsRemote && forceDirectPlayRemoteMediaSource && user.Policy.ForceRemoteSourceTranscoding) { + mediaSource.SupportsDirectPlay = false; + } + else if (mediaSource.IsRemote && user.Policy.ForceRemoteSourceTranscoding) + { + mediaSource.SupportsDirectPlay = false; } else { @@ -455,72 +460,117 @@ namespace MediaBrowser.Api.Playback if (mediaSource.SupportsDirectStream) { - options.MaxBitrate = GetMaxBitrate(maxBitrate, user); - - if (item is Audio) - { - if (!user.Policy.EnableAudioPlaybackTranscoding) - { - options.ForceDirectStream = true; - } - } - else if (item is Video) - { - if (!user.Policy.EnableAudioPlaybackTranscoding && !user.Policy.EnableVideoPlaybackTranscoding && !user.Policy.EnablePlaybackRemuxing) - { - options.ForceDirectStream = true; - } - } - - // The MediaSource supports direct stream, now test to see if the client supports it - var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ? - streamBuilder.BuildAudioItem(options) : - streamBuilder.BuildVideoItem(options); - - if (streamInfo == null || !streamInfo.IsDirectStream) + if (mediaSource.IsRemote && forceDirectPlayRemoteMediaSource && user.Policy.ForceRemoteSourceTranscoding) { mediaSource.SupportsDirectStream = false; } - - if (streamInfo != null) + else if (mediaSource.IsRemote && user.Policy.ForceRemoteSourceTranscoding) { - SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token); + mediaSource.SupportsDirectStream = false; } + else + { + options.MaxBitrate = GetMaxBitrate(maxBitrate, user); + + if (item is Audio) + { + if (!user.Policy.EnableAudioPlaybackTranscoding) + { + options.ForceDirectStream = true; + } + } + else if (item is Video) + { + if (!user.Policy.EnableAudioPlaybackTranscoding && !user.Policy.EnableVideoPlaybackTranscoding && !user.Policy.EnablePlaybackRemuxing) + { + options.ForceDirectStream = true; + } + } + + // The MediaSource supports direct stream, now test to see if the client supports it + var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ? + streamBuilder.BuildAudioItem(options) : + streamBuilder.BuildVideoItem(options); + + if (streamInfo == null || !streamInfo.IsDirectStream) + { + mediaSource.SupportsDirectStream = false; + } + + if (streamInfo != null) + { + SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token); + } + } } if (mediaSource.SupportsTranscoding) { - options.MaxBitrate = GetMaxBitrate(maxBitrate, user); - + if (mediaSource.IsRemote && user.Policy.ForceRemoteSourceTranscoding) + { + if (GetMaxBitrate(maxBitrate, user) < mediaSource.Bitrate) + { + options.MaxBitrate = GetMaxBitrate(maxBitrate, user); + } + else + { + options.MaxBitrate = mediaSource.Bitrate; + } + } + else + { + options.MaxBitrate = GetMaxBitrate(maxBitrate, user); + } + // The MediaSource supports direct stream, now test to see if the client supports it var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ? streamBuilder.BuildAudioItem(options) : streamBuilder.BuildVideoItem(options); + - if (streamInfo != null) - { - streamInfo.PlaySessionId = playSessionId; - - if (streamInfo.PlayMethod == PlayMethod.Transcode) + if (mediaSource.IsRemote && user.Policy.ForceRemoteSourceTranscoding) + { + if (streamInfo != null) { + streamInfo.PlaySessionId = playSessionId; streamInfo.StartPositionTicks = startTimeTicks; mediaSource.TranscodingUrl = streamInfo.ToUrl("-", auth.Token).TrimStart('-'); - - if (!allowVideoStreamCopy) - { - mediaSource.TranscodingUrl += "&allowVideoStreamCopy=false"; - } - if (!allowAudioStreamCopy) - { - mediaSource.TranscodingUrl += "&allowAudioStreamCopy=false"; - } + mediaSource.TranscodingUrl += "&allowVideoStreamCopy=false"; + mediaSource.TranscodingUrl += "&allowAudioStreamCopy=false"; mediaSource.TranscodingContainer = streamInfo.Container; mediaSource.TranscodingSubProtocol = streamInfo.SubProtocol; - } + + // Do this after the above so that StartPositionTicks is set + SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token); + } + } + else + { + if (streamInfo != null) + { + streamInfo.PlaySessionId = playSessionId; - // Do this after the above so that StartPositionTicks is set - SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token); - } + if (streamInfo.PlayMethod == PlayMethod.Transcode) + { + streamInfo.StartPositionTicks = startTimeTicks; + mediaSource.TranscodingUrl = streamInfo.ToUrl("-", auth.Token).TrimStart('-'); + + if (!allowVideoStreamCopy) + { + mediaSource.TranscodingUrl += "&allowVideoStreamCopy=false"; + } + if (!allowAudioStreamCopy) + { + mediaSource.TranscodingUrl += "&allowAudioStreamCopy=false"; + } + mediaSource.TranscodingContainer = streamInfo.Container; + mediaSource.TranscodingSubProtocol = streamInfo.SubProtocol; + } + + // Do this after the above so that StartPositionTicks is set + SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token); + } + } } } From b626508bc8f51e1a6e6ac385dad0cfe4dc6a21b2 Mon Sep 17 00:00:00 2001 From: Artiume Date: Mon, 16 Dec 2019 15:23:43 -0500 Subject: [PATCH 014/270] Update Dockerfile --- Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index e7d36316de..2a60bf1845 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,8 @@ ARG FFMPEG_VERSION=latest FROM node:alpine as web-builder ARG JELLYFIN_WEB_VERSION=master RUN apk add curl \ - && cd jellyfin-web \ + && curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \ + && cd jellyfin-web-* \ && yarn install \ && yarn build \ && mv dist /dist From 5dc3874ebdeac26d7be885b9a44ca1321b4b25cf Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 20 Dec 2019 21:30:51 +0100 Subject: [PATCH 015/270] Enable nullable reference types for Emby.Photos and Emby.Notifications * Enable TreatWarningsAsErrors for Emby.Notifications * Add analyzers to Emby.Notifications --- .../Api/NotificationsService.cs | 115 ++--------------- Emby.Notifications/CoreNotificationTypes.cs | 4 +- Emby.Notifications/Emby.Notifications.csproj | 14 ++ .../NotificationConfigurationFactory.cs | 5 +- ...fications.cs => NotificationEntryPoint.cs} | 121 +++++++++++------- Emby.Notifications/NotificationManager.cs | 37 ++++-- Emby.Photos/Emby.Photos.csproj | 1 + 7 files changed, 138 insertions(+), 159 deletions(-) rename Emby.Notifications/{Notifications.cs => NotificationEntryPoint.cs} (69%) diff --git a/Emby.Notifications/Api/NotificationsService.cs b/Emby.Notifications/Api/NotificationsService.cs index 83845558ad..5591809712 100644 --- a/Emby.Notifications/Api/NotificationsService.cs +++ b/Emby.Notifications/Api/NotificationsService.cs @@ -1,3 +1,8 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1402 +#pragma warning disable SA1600 +#pragma warning disable SA1649 + using System; using System.Collections.Generic; using System.Linq; @@ -12,60 +17,6 @@ using MediaBrowser.Model.Services; namespace Emby.Notifications.Api { - [Route("/Notifications/{UserId}", "GET", Summary = "Gets notifications")] - public class GetNotifications : IReturn - { - [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string UserId { get; set; } - - [ApiMember(Name = "IsRead", Description = "An optional filter by IsRead", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] - public bool? IsRead { get; set; } - - [ApiMember(Name = "StartIndex", Description = "Optional. The record index to start at. All items with a lower index will be dropped from the results.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] - public int? StartIndex { get; set; } - - [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] - public int? Limit { get; set; } - } - - public class Notification - { - public string Id { get; set; } - - public string UserId { get; set; } - - public DateTime Date { get; set; } - - public bool IsRead { get; set; } - - public string Name { get; set; } - - public string Description { get; set; } - - public string Url { get; set; } - - public NotificationLevel Level { get; set; } - } - - public class NotificationResult - { - public Notification[] Notifications { get; set; } - public int TotalRecordCount { get; set; } - } - - public class NotificationsSummary - { - public int UnreadCount { get; set; } - public NotificationLevel MaxUnreadNotificationLevel { get; set; } - } - - [Route("/Notifications/{UserId}/Summary", "GET", Summary = "Gets a notification summary for a user")] - public class GetNotificationsSummary : IReturn - { - [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string UserId { get; set; } - } - [Route("/Notifications/Types", "GET", Summary = "Gets notification types")] public class GetNotificationTypes : IReturn> { @@ -80,41 +31,21 @@ namespace Emby.Notifications.Api public class AddAdminNotification : IReturnVoid { [ApiMember(Name = "Name", Description = "The notification's name", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] - public string Name { get; set; } + public string Name { get; set; } = string.Empty; [ApiMember(Name = "Description", Description = "The notification's description", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] - public string Description { get; set; } + public string Description { get; set; } = string.Empty; [ApiMember(Name = "ImageUrl", Description = "The notification's image url", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] - public string ImageUrl { get; set; } + public string? ImageUrl { get; set; } [ApiMember(Name = "Url", Description = "The notification's info url", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] - public string Url { get; set; } + public string? Url { get; set; } [ApiMember(Name = "Level", Description = "The notification level", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] public NotificationLevel Level { get; set; } } - [Route("/Notifications/{UserId}/Read", "POST", Summary = "Marks notifications as read")] - public class MarkRead : IReturnVoid - { - [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string UserId { get; set; } - - [ApiMember(Name = "Ids", Description = "A list of notification ids, comma delimited", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST", AllowMultiple = true)] - public string Ids { get; set; } - } - - [Route("/Notifications/{UserId}/Unread", "POST", Summary = "Marks notifications as unread")] - public class MarkUnread : IReturnVoid - { - [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string UserId { get; set; } - - [ApiMember(Name = "Ids", Description = "A list of notification ids, comma delimited", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST", AllowMultiple = true)] - public string Ids { get; set; } - } - [Authenticated] public class NotificationsService : IService { @@ -129,30 +60,19 @@ namespace Emby.Notifications.Api public object Get(GetNotificationTypes request) { + _ = request; // Silence unused variable warning return _notificationManager.GetNotificationTypes(); } public object Get(GetNotificationServices request) { + _ = request; // Silence unused variable warning return _notificationManager.GetNotificationServices().ToList(); } - public object Get(GetNotificationsSummary request) - { - return new NotificationsSummary - { - - }; - } - public Task Post(AddAdminNotification request) { // This endpoint really just exists as post of a real with sickbeard - return AddNotification(request); - } - - private Task AddNotification(AddAdminNotification request) - { var notification = new NotificationRequest { Date = DateTime.UtcNow, @@ -165,18 +85,5 @@ namespace Emby.Notifications.Api return _notificationManager.SendNotification(notification, CancellationToken.None); } - - public void Post(MarkRead request) - { - } - - public void Post(MarkUnread request) - { - } - - public object Get(GetNotifications request) - { - return new NotificationResult(); - } } } diff --git a/Emby.Notifications/CoreNotificationTypes.cs b/Emby.Notifications/CoreNotificationTypes.cs index 0f9fc08d99..73e0b0256a 100644 --- a/Emby.Notifications/CoreNotificationTypes.cs +++ b/Emby.Notifications/CoreNotificationTypes.cs @@ -1,7 +1,9 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System; using System.Collections.Generic; using System.Linq; -using MediaBrowser.Controller; using MediaBrowser.Controller.Notifications; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Notifications; diff --git a/Emby.Notifications/Emby.Notifications.csproj b/Emby.Notifications/Emby.Notifications.csproj index 004ded77b1..e6bf785bff 100644 --- a/Emby.Notifications/Emby.Notifications.csproj +++ b/Emby.Notifications/Emby.Notifications.csproj @@ -4,6 +4,8 @@ netstandard2.1 false true + true + enable @@ -16,4 +18,16 @@ + + + + + + + + + + ../jellyfin.ruleset + + diff --git a/Emby.Notifications/NotificationConfigurationFactory.cs b/Emby.Notifications/NotificationConfigurationFactory.cs index d08475f7d2..b168ed221b 100644 --- a/Emby.Notifications/NotificationConfigurationFactory.cs +++ b/Emby.Notifications/NotificationConfigurationFactory.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System.Collections.Generic; using MediaBrowser.Common.Configuration; using MediaBrowser.Model.Notifications; @@ -13,7 +16,7 @@ namespace Emby.Notifications new ConfigurationStore { Key = "notifications", - ConfigurationType = typeof (NotificationOptions) + ConfigurationType = typeof(NotificationOptions) } }; } diff --git a/Emby.Notifications/Notifications.cs b/Emby.Notifications/NotificationEntryPoint.cs similarity index 69% rename from Emby.Notifications/Notifications.cs rename to Emby.Notifications/NotificationEntryPoint.cs index 7aa1e7ae89..ab92822fa2 100644 --- a/Emby.Notifications/Notifications.cs +++ b/Emby.Notifications/NotificationEntryPoint.cs @@ -21,70 +21,85 @@ using Microsoft.Extensions.Logging; namespace Emby.Notifications { /// - /// Creates notifications for various system events + /// Creates notifications for various system events. /// - public class Notifications : IServerEntryPoint + public class NotificationEntryPoint : IServerEntryPoint { private readonly ILogger _logger; - + private readonly IActivityManager _activityManager; + private readonly ILocalizationManager _localization; private readonly INotificationManager _notificationManager; - private readonly ILibraryManager _libraryManager; private readonly IServerApplicationHost _appHost; - - private Timer LibraryUpdateTimer { get; set; } - private readonly object _libraryChangedSyncLock = new object(); - private readonly IConfigurationManager _config; - private readonly ILocalizationManager _localization; - private readonly IActivityManager _activityManager; + + private readonly object _libraryChangedSyncLock = new object(); + private readonly List _itemsAdded = new List(); + + private Timer? _libraryUpdateTimer; private string[] _coreNotificationTypes; - public Notifications( + private bool _disposed = false; + + /// + /// Initializes a new instance of the class. + /// + /// The logger. + /// The activity manager. + /// The localization manager. + /// The notification manager. + /// The library manager. + /// The application host. + /// The configuration manager. + public NotificationEntryPoint( + ILogger logger, IActivityManager activityManager, ILocalizationManager localization, - ILogger logger, INotificationManager notificationManager, ILibraryManager libraryManager, IServerApplicationHost appHost, IConfigurationManager config) { _logger = logger; + _activityManager = activityManager; + _localization = localization; _notificationManager = notificationManager; _libraryManager = libraryManager; _appHost = appHost; _config = config; - _localization = localization; - _activityManager = activityManager; _coreNotificationTypes = new CoreNotificationTypes(localization).GetNotificationTypes().Select(i => i.Type).ToArray(); } + /// public Task RunAsync() { - _libraryManager.ItemAdded += _libraryManager_ItemAdded; - _appHost.HasPendingRestartChanged += _appHost_HasPendingRestartChanged; - _appHost.HasUpdateAvailableChanged += _appHost_HasUpdateAvailableChanged; - _activityManager.EntryCreated += _activityManager_EntryCreated; + _libraryManager.ItemAdded += OnLibraryManagerItemAdded; + _appHost.HasPendingRestartChanged += OnAppHostHasPendingRestartChanged; + _appHost.HasUpdateAvailableChanged += OnAppHostHasUpdateAvailableChanged; + _activityManager.EntryCreated += OnActivityManagerEntryCreated; return Task.CompletedTask; } - private async void _appHost_HasPendingRestartChanged(object sender, EventArgs e) + private async void OnAppHostHasPendingRestartChanged(object sender, EventArgs e) { var type = NotificationType.ServerRestartRequired.ToString(); var notification = new NotificationRequest { NotificationType = type, - Name = string.Format(_localization.GetLocalizedString("ServerNameNeedsToBeRestarted"), _appHost.Name) + Name = string.Format( + CultureInfo.InvariantCulture, + _localization.GetLocalizedString("ServerNameNeedsToBeRestarted"), + _appHost.Name) }; await SendNotification(notification, null).ConfigureAwait(false); } - private async void _activityManager_EntryCreated(object sender, GenericEventArgs e) + private async void OnActivityManagerEntryCreated(object sender, GenericEventArgs e) { var entry = e.Argument; @@ -117,7 +132,7 @@ namespace Emby.Notifications return _config.GetConfiguration("notifications"); } - private async void _appHost_HasUpdateAvailableChanged(object sender, EventArgs e) + private async void OnAppHostHasUpdateAvailableChanged(object sender, EventArgs e) { if (!_appHost.HasUpdateAvailable) { @@ -136,8 +151,7 @@ namespace Emby.Notifications await SendNotification(notification, null).ConfigureAwait(false); } - private readonly List _itemsAdded = new List(); - private void _libraryManager_ItemAdded(object sender, ItemChangeEventArgs e) + private void OnLibraryManagerItemAdded(object sender, ItemChangeEventArgs e) { if (!FilterItem(e.Item)) { @@ -146,14 +160,17 @@ namespace Emby.Notifications lock (_libraryChangedSyncLock) { - if (LibraryUpdateTimer == null) + if (_libraryUpdateTimer == null) { - LibraryUpdateTimer = new Timer(LibraryUpdateTimerCallback, null, 5000, - Timeout.Infinite); + _libraryUpdateTimer = new Timer( + LibraryUpdateTimerCallback, + null, + 5000, + Timeout.Infinite); } else { - LibraryUpdateTimer.Change(5000, Timeout.Infinite); + _libraryUpdateTimer.Change(5000, Timeout.Infinite); } _itemsAdded.Add(e.Item); @@ -188,7 +205,8 @@ namespace Emby.Notifications { items = _itemsAdded.ToList(); _itemsAdded.Clear(); - DisposeLibraryUpdateTimer(); + _libraryUpdateTimer!.Dispose(); // Shouldn't be null as it just set off this callback + _libraryUpdateTimer = null; } items = items.Take(10).ToList(); @@ -198,7 +216,10 @@ namespace Emby.Notifications var notification = new NotificationRequest { NotificationType = NotificationType.NewLibraryContent.ToString(), - Name = string.Format(_localization.GetLocalizedString("ValueHasBeenAddedToLibrary"), GetItemName(item)), + Name = string.Format( + CultureInfo.InvariantCulture, + _localization.GetLocalizedString("ValueHasBeenAddedToLibrary"), + GetItemName(item)), Description = item.Overview }; @@ -206,7 +227,7 @@ namespace Emby.Notifications } } - public static string GetItemName(BaseItem item) + private static string GetItemName(BaseItem item) { var name = item.Name; if (item is Episode episode) @@ -219,6 +240,7 @@ namespace Emby.Notifications episode.IndexNumber.Value, name); } + if (episode.ParentIndexNumber.HasValue) { name = string.Format( @@ -229,7 +251,6 @@ namespace Emby.Notifications } } - if (item is IHasSeries hasSeries) { name = hasSeries.SeriesName + " - " + name; @@ -257,7 +278,7 @@ namespace Emby.Notifications return name; } - private async Task SendNotification(NotificationRequest notification, BaseItem relatedItem) + private async Task SendNotification(NotificationRequest notification, BaseItem? relatedItem) { try { @@ -269,23 +290,37 @@ namespace Emby.Notifications } } + /// public void Dispose() { - DisposeLibraryUpdateTimer(); - - _libraryManager.ItemAdded -= _libraryManager_ItemAdded; - _appHost.HasPendingRestartChanged -= _appHost_HasPendingRestartChanged; - _appHost.HasUpdateAvailableChanged -= _appHost_HasUpdateAvailableChanged; - _activityManager.EntryCreated -= _activityManager_EntryCreated; + Dispose(true); + GC.SuppressFinalize(this); } - private void DisposeLibraryUpdateTimer() + /// + /// Releases unmanaged and - optionally - managed resources. + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool disposing) { - if (LibraryUpdateTimer != null) + if (_disposed) { - LibraryUpdateTimer.Dispose(); - LibraryUpdateTimer = null; + return; } + + if (disposing) + { + _libraryUpdateTimer?.Dispose(); + } + + _libraryUpdateTimer = null; + + _libraryManager.ItemAdded -= OnLibraryManagerItemAdded; + _appHost.HasPendingRestartChanged -= OnAppHostHasPendingRestartChanged; + _appHost.HasUpdateAvailableChanged -= OnAppHostHasUpdateAvailableChanged; + _activityManager.EntryCreated -= OnActivityManagerEntryCreated; + + _disposed = true; } } } diff --git a/Emby.Notifications/NotificationManager.cs b/Emby.Notifications/NotificationManager.cs index eecbbea071..836aa3e729 100644 --- a/Emby.Notifications/NotificationManager.cs +++ b/Emby.Notifications/NotificationManager.cs @@ -16,20 +16,32 @@ using Microsoft.Extensions.Logging; namespace Emby.Notifications { + /// + /// NotificationManager class. + /// public class NotificationManager : INotificationManager { private readonly ILogger _logger; private readonly IUserManager _userManager; private readonly IServerConfigurationManager _config; - private INotificationService[] _services; - private INotificationTypeFactory[] _typeFactories; + private INotificationService[] _services = Array.Empty(); + private INotificationTypeFactory[] _typeFactories = Array.Empty(); - public NotificationManager(ILoggerFactory loggerFactory, IUserManager userManager, IServerConfigurationManager config) + /// + /// Initializes a new instance of the class. + /// + /// The logger. + /// the user manager. + /// The server configuration manager. + public NotificationManager( + ILogger logger, + IUserManager userManager, + IServerConfigurationManager config) { + _logger = logger; _userManager = userManager; _config = config; - _logger = loggerFactory.CreateLogger(GetType().Name); } private NotificationOptions GetConfiguration() @@ -37,12 +49,14 @@ namespace Emby.Notifications return _config.GetConfiguration("notifications"); } + /// public Task SendNotification(NotificationRequest request, CancellationToken cancellationToken) { return SendNotification(request, null, cancellationToken); } - public Task SendNotification(NotificationRequest request, BaseItem relatedItem, CancellationToken cancellationToken) + /// + public Task SendNotification(NotificationRequest request, BaseItem? relatedItem, CancellationToken cancellationToken) { var notificationType = request.NotificationType; @@ -64,7 +78,8 @@ namespace Emby.Notifications return Task.WhenAll(tasks); } - private Task SendNotification(NotificationRequest request, + private Task SendNotification( + NotificationRequest request, INotificationService service, IEnumerable users, string title, @@ -79,7 +94,7 @@ namespace Emby.Notifications return Task.WhenAll(tasks); } - private IEnumerable GetUserIds(NotificationRequest request, NotificationOption options) + private IEnumerable GetUserIds(NotificationRequest request, NotificationOption? options) { if (request.SendToUserMode.HasValue) { @@ -109,7 +124,8 @@ namespace Emby.Notifications return request.UserIds; } - private async Task SendNotification(NotificationRequest request, + private async Task SendNotification( + NotificationRequest request, INotificationService service, string title, string description, @@ -161,12 +177,14 @@ namespace Emby.Notifications return GetConfiguration().IsServiceEnabled(service.Name, notificationType); } + /// public void AddParts(IEnumerable services, IEnumerable notificationTypeFactories) { _services = services.ToArray(); _typeFactories = notificationTypeFactories.ToArray(); } + /// public List GetNotificationTypes() { var list = _typeFactories.Select(i => @@ -180,7 +198,6 @@ namespace Emby.Notifications _logger.LogError(ex, "Error in GetNotificationTypes"); return new List(); } - }).SelectMany(i => i).ToList(); var config = GetConfiguration(); @@ -193,13 +210,13 @@ namespace Emby.Notifications return list; } + /// public IEnumerable GetNotificationServices() { return _services.Select(i => new NameIdPair { Name = i.Name, Id = i.Name.GetMD5().ToString("N", CultureInfo.InvariantCulture) - }).OrderBy(i => i.Name); } } diff --git a/Emby.Photos/Emby.Photos.csproj b/Emby.Photos/Emby.Photos.csproj index 64692c3703..8fd18466a1 100644 --- a/Emby.Photos/Emby.Photos.csproj +++ b/Emby.Photos/Emby.Photos.csproj @@ -18,6 +18,7 @@ false true true + enable From 963b69c7b2dd2682b95dcab25602a151e6a6f48a Mon Sep 17 00:00:00 2001 From: Artiume Date: Fri, 20 Dec 2019 23:17:01 -0500 Subject: [PATCH 016/270] Update MediaInfoService.cs --- MediaBrowser.Api/Playback/MediaInfoService.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index 09805b8054..df6dd82390 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -405,6 +405,7 @@ namespace MediaBrowser.Api.Playback user.Policy.EnableAudioPlaybackTranscoding); } + // Beginning of Playback Determination: Attempt DirectPlay first if (mediaSource.SupportsDirectPlay) { if (mediaSource.IsRemote && forceDirectPlayRemoteMediaSource && user.Policy.ForceRemoteSourceTranscoding) @@ -460,13 +461,13 @@ namespace MediaBrowser.Api.Playback if (mediaSource.SupportsDirectStream) { - if (mediaSource.IsRemote && forceDirectPlayRemoteMediaSource && user.Policy.ForceRemoteSourceTranscoding) + if (mediaSource.IsRemote && forceDirectPlayRemoteMediaSource)// && user.Policy.ForceRemoteSourceTranscoding) { - mediaSource.SupportsDirectStream = false; - } - else if (mediaSource.IsRemote && user.Policy.ForceRemoteSourceTranscoding) - { - mediaSource.SupportsDirectStream = false; + mediaSource.SupportsDirectStream = true; //false + // } + // else if (mediaSource.IsRemote && user.Policy.ForceRemoteSourceTranscoding) + // { + // mediaSource.SupportsDirectStream = false; } else { From 0e920a6d5f18dc6a012908444ce9766acba2dd63 Mon Sep 17 00:00:00 2001 From: Artiume Date: Fri, 20 Dec 2019 23:40:36 -0500 Subject: [PATCH 017/270] Update MediaInfoService.cs --- MediaBrowser.Api/Playback/MediaInfoService.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index df6dd82390..187e664756 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -461,13 +461,13 @@ namespace MediaBrowser.Api.Playback if (mediaSource.SupportsDirectStream) { - if (mediaSource.IsRemote && forceDirectPlayRemoteMediaSource)// && user.Policy.ForceRemoteSourceTranscoding) + if (mediaSource.IsRemote && forceDirectPlayRemoteMediaSource && user.Policy.ForceRemoteSourceTranscoding) { - mediaSource.SupportsDirectStream = true; //false - // } - // else if (mediaSource.IsRemote && user.Policy.ForceRemoteSourceTranscoding) - // { - // mediaSource.SupportsDirectStream = false; + mediaSource.SupportsDirectStream = false; + } + else if (mediaSource.IsRemote && user.Policy.ForceRemoteSourceTranscoding) + { + mediaSource.SupportsDirectStream = false; } else { @@ -527,7 +527,6 @@ namespace MediaBrowser.Api.Playback var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ? streamBuilder.BuildAudioItem(options) : streamBuilder.BuildVideoItem(options); - if (mediaSource.IsRemote && user.Policy.ForceRemoteSourceTranscoding) { From 1bab76d80c24bc090322eea3389fda98a7a99d69 Mon Sep 17 00:00:00 2001 From: Artiume Date: Sat, 21 Dec 2019 00:01:43 -0500 Subject: [PATCH 018/270] Revert "Allow valid https requests in .NET Core" --- .../HttpClientManager/HttpClientManager.cs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs index ff51820ca1..50233ea485 100644 --- a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs +++ b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs @@ -59,17 +59,7 @@ namespace Emby.Server.Implementations.HttpClientManager if (!_httpClients.TryGetValue(key, out var client)) { - var httpClientHandler = new HttpClientHandler() - { - ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => - { - var success = errors == System.Net.Security.SslPolicyErrors.None; - _logger.LogDebug("Validating certificate {Cert}. Success {1}", cert, success); - return success; - } - }; - - client = new HttpClient(httpClientHandler) + client = new HttpClient() { BaseAddress = new Uri(url) }; From 081d942d0361a4ad8aa918edcbb2f20c4c3f8471 Mon Sep 17 00:00:00 2001 From: Maximilian Marschall Date: Sun, 12 Jan 2020 00:31:17 +0100 Subject: [PATCH 019/270] Enable Throttling when transcoding without Hardware-Acceleration --- CONTRIBUTORS.md | 1 + MediaBrowser.Api/Playback/BaseStreamingService.cs | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 458944778e..2d2e5712a5 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -32,6 +32,7 @@ - [nevado](https://github.com/nevado) - [mark-monteiro](https://github.com/mark-monteiro) - [ullmie02](https://github.com/ullmie02) + - [geilername](https://github.com/geilername) # Emby Contributors diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 5881e22a7c..023d373d5b 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -327,6 +327,18 @@ namespace MediaBrowser.Api.Playback private bool EnableThrottling(StreamState state) { + var encodingOptions = ServerConfigurationManager.GetEncodingOptions(); + + // enable throttling when not using hardware acceleration + if (encodingOptions.HardwareAccelerationType == string.Empty) + { + return state.InputProtocol == MediaProtocol.File && + state.RunTimeTicks.HasValue && + state.RunTimeTicks.Value >= TimeSpan.FromMinutes(5).Ticks && + state.IsInputVideo && + state.VideoType == VideoType.VideoFile && + !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase); + } return false; //// do not use throttling with hardware encoders //return state.InputProtocol == MediaProtocol.File && From 0cbae4a06d49acccfd7a757039f7f6725cdc53a5 Mon Sep 17 00:00:00 2001 From: artiume Date: Thu, 16 Jan 2020 12:51:34 -0500 Subject: [PATCH 020/270] Tcoding https://github.com/jellyfin/jellyfin/pull/2184 --- .../MediaEncoding/EncodingHelper.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 020f0553ed..765932023b 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -2750,6 +2750,8 @@ namespace MediaBrowser.Controller.MediaEncoding args += " -mpegts_m2ts_mode 1"; } + var supportsGlobalHeaderFlag = state.OutputContainer != "mkv"; + if (string.Equals(videoCodec, "copy", StringComparison.OrdinalIgnoreCase)) { if (state.VideoStream != null @@ -2770,7 +2772,12 @@ namespace MediaBrowser.Controller.MediaEncoding if (!state.RunTimeTicks.HasValue) { - args += " -flags -global_header -fflags +genpts"; + if(supportsGlobalHeaderFlag) + { + args += " -flags -global_header"; + } + + args += " -fflags +genpts"; } } else @@ -2816,7 +2823,7 @@ namespace MediaBrowser.Controller.MediaEncoding args += " " + qualityParam.Trim(); } - if (!state.RunTimeTicks.HasValue) + if (supportsGlobalHeaderFlag && !state.RunTimeTicks.HasValue) { args += " -flags -global_header"; } From c601def4847cb0ada323a9da8d152c8c8d7b834b Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 17 Jan 2020 00:19:58 +0100 Subject: [PATCH 021/270] Fix warnings in SessionManager --- .../ApplicationHost.cs | 13 +- .../Session/SessionManager.cs | 274 +++++++++--------- 2 files changed, 156 insertions(+), 131 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 0bb1d832fb..e93337c2fc 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -808,7 +808,18 @@ namespace Emby.Server.Implementations ChannelManager = new ChannelManager(UserManager, DtoService, LibraryManager, LoggerFactory, ServerConfigurationManager, FileSystemManager, UserDataManager, JsonSerializer, ProviderManager); serviceCollection.AddSingleton(ChannelManager); - SessionManager = new SessionManager(UserDataManager, LoggerFactory, LibraryManager, UserManager, musicManager, DtoService, ImageProcessor, this, AuthenticationRepository, DeviceManager, MediaSourceManager); + SessionManager = new SessionManager( + LoggerFactory.CreateLogger(), + UserDataManager, + LibraryManager, + UserManager, + musicManager, + DtoService, + ImageProcessor, + this, + AuthenticationRepository, + DeviceManager, + MediaSourceManager); serviceCollection.AddSingleton(SessionManager); serviceCollection.AddSingleton( diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index b1d513dd4f..bb0836614e 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -30,17 +30,17 @@ using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.Session { /// - /// Class SessionManager + /// Class SessionManager. /// public class SessionManager : ISessionManager, IDisposable { /// - /// The _user data repository + /// The _user data repository. /// private readonly IUserDataManager _userDataManager; /// - /// The _logger + /// The _logger. /// private readonly ILogger _logger; @@ -57,36 +57,19 @@ namespace Emby.Server.Implementations.Session private readonly IDeviceManager _deviceManager; /// - /// The _active connections + /// The _active connections. /// private readonly ConcurrentDictionary _activeConnections = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); - public event EventHandler> AuthenticationFailed; + private Timer _idleTimer; - public event EventHandler> AuthenticationSucceeded; - - /// - /// Occurs when [playback start]. - /// - public event EventHandler PlaybackStart; - /// - /// Occurs when [playback progress]. - /// - public event EventHandler PlaybackProgress; - /// - /// Occurs when [playback stopped]. - /// - public event EventHandler PlaybackStopped; - - public event EventHandler SessionStarted; - public event EventHandler CapabilitiesChanged; - public event EventHandler SessionEnded; - public event EventHandler SessionActivity; + private DtoOptions _itemInfoDtoOptions; + private bool _disposed = false; public SessionManager( + ILogger logger, IUserDataManager userDataManager, - ILoggerFactory loggerFactory, ILibraryManager libraryManager, IUserManager userManager, IMusicManager musicManager, @@ -97,8 +80,8 @@ namespace Emby.Server.Implementations.Session IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager) { + _logger = logger; _userDataManager = userDataManager; - _logger = loggerFactory.CreateLogger(nameof(SessionManager)); _libraryManager = libraryManager; _userManager = userManager; _musicManager = musicManager; @@ -108,9 +91,49 @@ namespace Emby.Server.Implementations.Session _authRepo = authRepo; _deviceManager = deviceManager; _mediaSourceManager = mediaSourceManager; + _deviceManager.DeviceOptionsUpdated += OnDeviceManagerDeviceOptionsUpdated; } + /// + public event EventHandler> AuthenticationFailed; + + /// + public event EventHandler> AuthenticationSucceeded; + + /// + /// Occurs when [playback start]. + /// + public event EventHandler PlaybackStart; + + /// + /// Occurs when [playback progress]. + /// + public event EventHandler PlaybackProgress; + + /// + /// Occurs when [playback stopped]. + /// + public event EventHandler PlaybackStopped; + + /// + public event EventHandler SessionStarted; + + /// + public event EventHandler CapabilitiesChanged; + + /// + public event EventHandler SessionEnded; + + /// + public event EventHandler SessionActivity; + + /// + /// Gets all connections. + /// + /// All connections. + public IEnumerable Sessions => _activeConnections.Values.OrderByDescending(c => c.LastActivityDate); + private void OnDeviceManagerDeviceOptionsUpdated(object sender, GenericEventArgs> e) { foreach (var session in Sessions) @@ -130,14 +153,17 @@ namespace Emby.Server.Implementations.Session } } - private bool _disposed = false; - + /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } + /// + /// Releases unmanaged and - optionally - managed resources. + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool disposing) { if (_disposed) @@ -147,15 +173,17 @@ namespace Emby.Server.Implementations.Session if (disposing) { - // TODO: dispose stuff + _idleTimer?.Dispose(); } + _idleTimer = null; + _deviceManager.DeviceOptionsUpdated -= OnDeviceManagerDeviceOptionsUpdated; _disposed = true; } - public void CheckDisposed() + private void CheckDisposed() { if (_disposed) { @@ -163,12 +191,6 @@ namespace Emby.Server.Implementations.Session } } - /// - /// Gets all connections. - /// - /// All connections. - public IEnumerable Sessions => _activeConnections.Values.OrderByDescending(c => c.LastActivityDate); - private void OnSessionStarted(SessionInfo info) { if (!string.IsNullOrEmpty(info.DeviceId)) @@ -199,13 +221,13 @@ namespace Emby.Server.Implementations.Session new SessionEventArgs { SessionInfo = info - }, _logger); info.Dispose(); } + /// public void UpdateDeviceName(string sessionId, string deviceName) { var session = GetSession(sessionId); @@ -225,7 +247,6 @@ namespace Emby.Server.Implementations.Session /// The remote end point. /// The user. /// SessionInfo. - /// user public SessionInfo LogSessionActivity( string appName, string appVersion, @@ -263,14 +284,7 @@ namespace Emby.Server.Implementations.Session if ((activityDate - userLastActivityDate).TotalSeconds > 60) { - try - { - _userManager.UpdateUser(user); - } - catch (Exception ex) - { - _logger.LogError("Error updating user", ex); - } + _userManager.UpdateUser(user); } } @@ -287,18 +301,20 @@ namespace Emby.Server.Implementations.Session return session; } + /// public void CloseIfNeeded(SessionInfo session) { if (!session.SessionControllers.Any(i => i.IsSessionActive)) { var key = GetSessionKey(session.Client, session.DeviceId); - _activeConnections.TryRemove(key, out var removed); + _activeConnections.TryRemove(key, out _); OnSessionEnded(session); } } + /// public void ReportSessionEnded(string sessionId) { CheckDisposed(); @@ -308,7 +324,7 @@ namespace Emby.Server.Implementations.Session { var key = GetSessionKey(session.Client, session.DeviceId); - _activeConnections.TryRemove(key, out var removed); + _activeConnections.TryRemove(key, out _); OnSessionEnded(session); } @@ -339,7 +355,7 @@ namespace Emby.Server.Implementations.Session var runtimeTicks = libraryItem.RunTimeTicks; MediaSourceInfo mediaSource = null; - if (libraryItem is IHasMediaSources hasMediaSources) + if (libraryItem is IHasMediaSources) { mediaSource = await GetMediaSource(libraryItem, info.MediaSourceId, info.LiveStreamId).ConfigureAwait(false); @@ -391,7 +407,6 @@ namespace Emby.Server.Implementations.Session /// Removes the now playing item id. /// /// The session. - /// item private void RemoveNowPlayingItem(SessionInfo session) { session.NowPlayingItem = null; @@ -404,9 +419,7 @@ namespace Emby.Server.Implementations.Session } private static string GetSessionKey(string appName, string deviceId) - { - return appName + deviceId; - } + => appName + deviceId; /// /// Gets the connection. @@ -426,6 +439,7 @@ namespace Emby.Server.Implementations.Session { throw new ArgumentNullException(nameof(deviceId)); } + var key = GetSessionKey(appName, deviceId); CheckDisposed(); @@ -498,7 +512,7 @@ namespace Emby.Server.Implementations.Session { var users = new List(); - if (!session.UserId.Equals(Guid.Empty)) + if (session.UserId != Guid.Empty) { var user = _userManager.GetUserById(session.UserId); @@ -517,8 +531,6 @@ namespace Emby.Server.Implementations.Session return users; } - private Timer _idleTimer; - private void StartIdleCheckTimer() { if (_idleTimer == null) @@ -594,11 +606,11 @@ namespace Emby.Server.Implementations.Session } /// - /// Used to report that playback has started for an item + /// Used to report that playback has started for an item. /// /// The info. /// Task. - /// info + /// info is null. public async Task OnPlaybackStart(PlaybackStartInfo info) { CheckDisposed(); @@ -610,7 +622,7 @@ namespace Emby.Server.Implementations.Session var session = GetSession(info.SessionId); - var libraryItem = info.ItemId.Equals(Guid.Empty) + var libraryItem = info.ItemId == Guid.Empty ? null : GetNowPlayingItem(session, info.ItemId); @@ -648,7 +660,6 @@ namespace Emby.Server.Implementations.Session ClientName = session.Client, DeviceId = session.DeviceId, Session = session - }, _logger); @@ -679,13 +690,14 @@ namespace Emby.Server.Implementations.Session _userDataManager.SaveUserData(user, item, data, UserDataSaveReason.PlaybackStart, CancellationToken.None); } + /// public Task OnPlaybackProgress(PlaybackProgressInfo info) { return OnPlaybackProgress(info, false); } /// - /// Used to report playback progress for an item + /// Used to report playback progress for an item. /// /// Task. public async Task OnPlaybackProgress(PlaybackProgressInfo info, bool isAutomated) @@ -852,7 +864,7 @@ namespace Emby.Server.Implementations.Session { MediaSourceInfo mediaSource = null; - if (libraryItem is IHasMediaSources hasMediaSources) + if (libraryItem is IHasMediaSources) { mediaSource = await GetMediaSource(libraryItem, info.MediaSourceId, info.LiveStreamId).ConfigureAwait(false); } @@ -924,7 +936,6 @@ namespace Emby.Server.Implementations.Session ClientName = session.Client, DeviceId = session.DeviceId, Session = session - }, _logger); } @@ -962,13 +973,17 @@ namespace Emby.Server.Implementations.Session /// The session identifier. /// if set to true [throw on missing]. /// SessionInfo. - /// sessionId + /// + /// No session with an Id equal to sessionId was found + /// and throwOnMissing is true. + /// private SessionInfo GetSession(string sessionId, bool throwOnMissing = true) { var session = Sessions.FirstOrDefault(i => string.Equals(i.Id, sessionId, StringComparison.Ordinal)); if (session == null && throwOnMissing) { - throw new ResourceNotFoundException(string.Format("Session {0} not found.", sessionId)); + throw new ResourceNotFoundException( + string.Format(CultureInfo.InvariantCulture, "Session {0} not found.", sessionId)); } return session; @@ -981,12 +996,14 @@ namespace Emby.Server.Implementations.Session if (session == null) { - throw new ResourceNotFoundException(string.Format("Session {0} not found.", sessionId)); + throw new ResourceNotFoundException( + string.Format(CultureInfo.InvariantCulture, "Session {0} not found.", sessionId)); } return session; } + /// public Task SendMessageCommand(string controllingSessionId, string sessionId, MessageCommand command, CancellationToken cancellationToken) { CheckDisposed(); @@ -1007,6 +1024,7 @@ namespace Emby.Server.Implementations.Session return SendGeneralCommand(controllingSessionId, sessionId, generalCommand, cancellationToken); } + /// public Task SendGeneralCommand(string controllingSessionId, string sessionId, GeneralCommand command, CancellationToken cancellationToken) { CheckDisposed(); @@ -1051,6 +1069,7 @@ namespace Emby.Server.Implementations.Session return Task.WhenAll(GetTasks()); } + /// public async Task SendPlayCommand(string controllingSessionId, string sessionId, PlayRequest command, CancellationToken cancellationToken) { CheckDisposed(); @@ -1092,7 +1111,8 @@ namespace Emby.Server.Implementations.Session { if (items.Any(i => i.GetPlayAccess(user) != PlayAccess.Full)) { - throw new ArgumentException(string.Format("{0} is not allowed to play media.", user.Name)); + throw new ArgumentException( + string.Format(CultureInfo.InvariantCulture, "{0} is not allowed to play media.", user.Name)); } } @@ -1200,6 +1220,7 @@ namespace Emby.Server.Implementations.Session return _musicManager.GetInstantMixFromItem(item, user, new DtoOptions(false) { EnableImages = false }); } + /// public Task SendBrowseCommand(string controllingSessionId, string sessionId, BrowseRequest command, CancellationToken cancellationToken) { var generalCommand = new GeneralCommand @@ -1216,6 +1237,7 @@ namespace Emby.Server.Implementations.Session return SendGeneralCommand(controllingSessionId, sessionId, generalCommand, cancellationToken); } + /// public Task SendPlaystateCommand(string controllingSessionId, string sessionId, PlaystateRequest command, CancellationToken cancellationToken) { CheckDisposed(); @@ -1299,12 +1321,12 @@ namespace Emby.Server.Implementations.Session var session = GetSession(sessionId); - if (session.UserId.Equals(userId)) + if (session.UserId == userId) { throw new ArgumentException("The requested user is already the primary user of the session."); } - if (session.AdditionalUsers.All(i => !i.UserId.Equals(userId))) + if (session.AdditionalUsers.All(i => i.UserId != userId)) { var user = _userManager.GetUserById(userId); @@ -1430,19 +1452,19 @@ namespace Emby.Server.Implementations.Session private string GetAuthorizationToken(User user, string deviceId, string app, string appVersion, string deviceName) { - var existing = _authRepo.Get(new AuthenticationInfoQuery - { - DeviceId = deviceId, - UserId = user.Id, - Limit = 1 + var existing = _authRepo.Get( + new AuthenticationInfoQuery + { + DeviceId = deviceId, + UserId = user.Id, + Limit = 1 + }).Items.FirstOrDefault(); - }).Items.FirstOrDefault(); - - var allExistingForDevice = _authRepo.Get(new AuthenticationInfoQuery - { - DeviceId = deviceId - - }).Items; + var allExistingForDevice = _authRepo.Get( + new AuthenticationInfoQuery + { + DeviceId = deviceId + }).Items; foreach (var auth in allExistingForDevice) { @@ -1461,7 +1483,7 @@ namespace Emby.Server.Implementations.Session if (existing != null) { - _logger.LogInformation("Reissuing access token: " + existing.AccessToken); + _logger.LogInformation("Reissuing access token: {Token}", existing.AccessToken); return existing.AccessToken; } @@ -1486,6 +1508,7 @@ namespace Emby.Server.Implementations.Session return newToken.AccessToken; } + /// public void Logout(string accessToken) { CheckDisposed(); @@ -1495,19 +1518,20 @@ namespace Emby.Server.Implementations.Session throw new ArgumentNullException(nameof(accessToken)); } - var existing = _authRepo.Get(new AuthenticationInfoQuery - { - Limit = 1, - AccessToken = accessToken + var existing = _authRepo.Get( + new AuthenticationInfoQuery + { + Limit = 1, + AccessToken = accessToken + }).Items; - }).Items.FirstOrDefault(); - - if (existing != null) + if (existing.Count > 0) { - Logout(existing); + Logout(existing[0]); } } + /// public void Logout(AuthenticationInfo existing) { CheckDisposed(); @@ -1533,6 +1557,7 @@ namespace Emby.Server.Implementations.Session } } + /// public void RevokeUserTokens(Guid userId, string currentAccessToken) { CheckDisposed(); @@ -1551,6 +1576,7 @@ namespace Emby.Server.Implementations.Session } } + /// public void RevokeToken(string token) { Logout(token); @@ -1607,10 +1633,8 @@ namespace Emby.Server.Implementations.Session _deviceManager.SaveCapabilities(deviceId, capabilities); } - private DtoOptions _itemInfoDtoOptions; - /// - /// Converts a BaseItem to a BaseItemInfo + /// Converts a BaseItem to a BaseItemInfo. /// private BaseItemDto GetItemInfo(BaseItem item, MediaSourceInfo mediaSource) { @@ -1685,6 +1709,7 @@ namespace Emby.Server.Implementations.Session } } + /// public void ReportNowViewingItem(string sessionId, string itemId) { if (string.IsNullOrEmpty(itemId)) @@ -1692,23 +1717,26 @@ namespace Emby.Server.Implementations.Session throw new ArgumentNullException(nameof(itemId)); } - //var item = _libraryManager.GetItemById(new Guid(itemId)); + var item = _libraryManager.GetItemById(new Guid(itemId)); - //var info = GetItemInfo(item, null, null); + var info = GetItemInfo(item, null); - //ReportNowViewingItem(sessionId, info); + ReportNowViewingItem(sessionId, info); } + /// public void ReportNowViewingItem(string sessionId, BaseItemDto item) { - //var session = GetSession(sessionId); + throw new NotImplementedException(); - //session.NowViewingItem = item; + // var session = GetSession(sessionId); + // session.NowViewingItem = item; } + /// public void ReportTranscodingInfo(string deviceId, TranscodingInfo info) { - var session = Sessions.FirstOrDefault(i => string.Equals(i.DeviceId, deviceId)); + var session = Sessions.FirstOrDefault(i => string.Equals(i.DeviceId, deviceId, StringComparison.Ordinal)); if (session != null) { @@ -1716,17 +1744,18 @@ namespace Emby.Server.Implementations.Session } } + /// public void ClearTranscodingInfo(string deviceId) { ReportTranscodingInfo(deviceId, null); } + /// public SessionInfo GetSession(string deviceId, string client, string version) - { - return Sessions.FirstOrDefault(i => string.Equals(i.DeviceId, deviceId) && - string.Equals(i.Client, client)); - } + => Sessions.FirstOrDefault( + i => string.Equals(i.DeviceId, deviceId, StringComparison.Ordinal) && string.Equals(i.Client, client, StringComparison.Ordinal)); + /// public SessionInfo GetSessionByAuthenticationToken(AuthenticationInfo info, string deviceId, string remoteEndpoint, string appVersion) { if (info == null) @@ -1759,23 +1788,24 @@ namespace Emby.Server.Implementations.Session return LogSessionActivity(appName, appVersion, deviceId, deviceName, remoteEndpoint, user); } + /// public SessionInfo GetSessionByAuthenticationToken(string token, string deviceId, string remoteEndpoint) { - var result = _authRepo.Get(new AuthenticationInfoQuery + var items = _authRepo.Get(new AuthenticationInfoQuery { - AccessToken = token - }); + AccessToken = token, + Limit = 1 + }).Items; - var info = result.Items.FirstOrDefault(); - - if (info == null) + if (items.Count == 0) { return null; } - return GetSessionByAuthenticationToken(info, deviceId, remoteEndpoint, null); + return GetSessionByAuthenticationToken(items[0], deviceId, remoteEndpoint, null); } + /// public Task SendMessageToAdminSessions(string name, T data, CancellationToken cancellationToken) { CheckDisposed(); @@ -1785,6 +1815,7 @@ namespace Emby.Server.Implementations.Session return SendMessageToUserSessions(adminUserIds, name, data, cancellationToken); } + /// public Task SendMessageToUserSessions(List userIds, string name, Func dataFn, CancellationToken cancellationToken) { CheckDisposed(); @@ -1796,11 +1827,10 @@ namespace Emby.Server.Implementations.Session return Task.CompletedTask; } - var data = dataFn(); - - return SendMessageToSessions(sessions, name, data, cancellationToken); + return SendMessageToSessions(sessions, name, dataFn(), cancellationToken); } + /// public Task SendMessageToUserSessions(List userIds, string name, T data, CancellationToken cancellationToken) { CheckDisposed(); @@ -1809,6 +1839,7 @@ namespace Emby.Server.Implementations.Session return SendMessageToSessions(sessions, name, data, cancellationToken); } + /// public Task SendMessageToUserDeviceSessions(string deviceId, string name, T data, CancellationToken cancellationToken) { CheckDisposed(); @@ -1817,22 +1848,5 @@ namespace Emby.Server.Implementations.Session return SendMessageToSessions(sessions, name, data, cancellationToken); } - - public Task SendMessageToUserDeviceAndAdminSessions(string deviceId, string name, T data, CancellationToken cancellationToken) - { - CheckDisposed(); - - var sessions = Sessions - .Where(i => string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase) || IsAdminSession(i)); - - return SendMessageToSessions(sessions, name, data, cancellationToken); - } - - private bool IsAdminSession(SessionInfo s) - { - var user = _userManager.GetUserById(s.UserId); - - return user != null && user.Policy.IsAdministrator; - } } } From ddf9b38799fa4a0e227ae3c2516a72ec48f13854 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Tue, 21 Jan 2020 20:26:30 +0100 Subject: [PATCH 022/270] Simplify image processing by removing image enhancers --- Emby.Drawing/Emby.Drawing.csproj | 12 + Emby.Drawing/ImageProcessor.cs | 353 ++---------------- .../ApplicationHost.cs | 2 - Emby.Server.Implementations/Dto/DtoService.cs | 51 +-- Jellyfin.Drawing.Skia/SkiaEncoder.cs | 20 +- Jellyfin.Server/Program.cs | 11 +- MediaBrowser.Api/Images/ImageService.cs | 26 +- .../Drawing/IImageProcessor.cs | 35 -- .../Drawing/ImageHelper.cs | 2 - .../Drawing/ImageProcessingOptions.cs | 3 - .../MediaEncoding/EncodingJobInfo.cs | 12 +- .../Providers/IImageEnhancer.cs | 61 --- MediaBrowser.Model/Drawing/ImageDimensions.cs | 37 +- 13 files changed, 103 insertions(+), 522 deletions(-) delete mode 100644 MediaBrowser.Controller/Providers/IImageEnhancer.cs diff --git a/Emby.Drawing/Emby.Drawing.csproj b/Emby.Drawing/Emby.Drawing.csproj index 85cecdc44e..b7090b2629 100644 --- a/Emby.Drawing/Emby.Drawing.csproj +++ b/Emby.Drawing/Emby.Drawing.csproj @@ -17,4 +17,16 @@ + + + + + + + + + + ../jellyfin.ruleset + + diff --git a/Emby.Drawing/ImageProcessor.cs b/Emby.Drawing/ImageProcessor.cs index ce8089e59c..8f68509364 100644 --- a/Emby.Drawing/ImageProcessor.cs +++ b/Emby.Drawing/ImageProcessor.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; -using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Extensions; using MediaBrowser.Controller; @@ -11,10 +10,8 @@ using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; -using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Drawing; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Extensions; using MediaBrowser.Model.IO; using MediaBrowser.Model.Net; using Microsoft.Extensions.Logging; @@ -24,7 +21,7 @@ namespace Emby.Drawing /// /// Class ImageProcessor. /// - public class ImageProcessor : IImageProcessor, IDisposable + public sealed class ImageProcessor : IImageProcessor, IDisposable { // Increment this when there's a change requiring caches to be invalidated private const string Version = "3"; @@ -32,28 +29,24 @@ namespace Emby.Drawing private static readonly HashSet _transparentImageTypes = new HashSet(StringComparer.OrdinalIgnoreCase) { ".png", ".webp", ".gif" }; - /// - /// The _logger - /// private readonly ILogger _logger; private readonly IFileSystem _fileSystem; private readonly IServerApplicationPaths _appPaths; - private IImageEncoder _imageEncoder; + private readonly IImageEncoder _imageEncoder; private readonly Func _libraryManager; private readonly Func _mediaEncoder; - private readonly Dictionary _locks = new Dictionary(); private bool _disposed = false; /// - /// + /// Initializes a new instance of the class. /// - /// - /// - /// - /// - /// - /// + /// The logger. + /// The server application paths. + /// The filesystem. + /// The image encoder. + /// The library manager. + /// The media encoder. public ImageProcessor( ILogger logger, IServerApplicationPaths appPaths, @@ -68,16 +61,10 @@ namespace Emby.Drawing _libraryManager = libraryManager; _mediaEncoder = mediaEncoder; _appPaths = appPaths; - - ImageEnhancers = Array.Empty(); - - ImageHelper.ImageProcessor = this; } private string ResizedImageCachePath => Path.Combine(_appPaths.ImageCachePath, "resized-images"); - private string EnhancedImageCachePath => Path.Combine(_appPaths.ImageCachePath, "enhanced-images"); - /// public IReadOnlyCollection SupportedInputFormats => new HashSet(StringComparer.OrdinalIgnoreCase) @@ -90,9 +77,7 @@ namespace Emby.Drawing "aiff", "cr2", "crw", - - // Remove until supported - //"nef", + "nef", "orf", "pef", "arw", @@ -111,19 +96,9 @@ namespace Emby.Drawing "wbmp" }; - /// - public IReadOnlyCollection ImageEnhancers { get; set; } - /// public bool SupportsImageCollageCreation => _imageEncoder.SupportsImageCollageCreation; - /// - public IImageEncoder ImageEncoder - { - get => _imageEncoder; - set => _imageEncoder = value ?? throw new ArgumentNullException(nameof(value)); - } - /// public async Task ProcessImage(ImageProcessingOptions options, Stream toStream) { @@ -151,6 +126,8 @@ namespace Emby.Drawing throw new ArgumentNullException(nameof(options)); } + var libraryManager = _libraryManager(); + ItemImageInfo originalImage = options.Image; BaseItem item = options.Item; @@ -158,9 +135,10 @@ namespace Emby.Drawing { if (item == null) { - item = _libraryManager().GetItemById(options.ItemId); + item = libraryManager.GetItemById(options.ItemId); } - originalImage = await _libraryManager().ConvertImageToLocal(item, originalImage, options.ImageIndex).ConfigureAwait(false); + + originalImage = await libraryManager.ConvertImageToLocal(item, originalImage, options.ImageIndex).ConfigureAwait(false); } string originalImagePath = originalImage.Path; @@ -187,27 +165,6 @@ namespace Emby.Drawing dateModified = supportedImageInfo.dateModified; bool requiresTransparency = _transparentImageTypes.Contains(Path.GetExtension(originalImagePath)); - if (options.Enhancers.Count > 0) - { - if (item == null) - { - item = _libraryManager().GetItemById(options.ItemId); - } - - var tuple = await GetEnhancedImage(new ItemImageInfo - { - DateModified = dateModified, - Type = originalImage.Type, - Path = originalImagePath - }, requiresTransparency, item, options.ImageIndex, options.Enhancers, CancellationToken.None).ConfigureAwait(false); - - originalImagePath = tuple.path; - dateModified = tuple.dateModified; - requiresTransparency = tuple.transparent; - // TODO: Get this info - originalImageSize = null; - } - bool autoOrient = false; ImageOrientation? orientation = null; if (item is Photo photo) @@ -240,12 +197,6 @@ namespace Emby.Drawing ImageFormat outputFormat = GetOutputFormat(options.SupportedOutputFormats, requiresTransparency); string cacheFilePath = GetCacheFilePath(originalImagePath, newSize, quality, dateModified, outputFormat, options.AddPlayedIndicator, options.PercentPlayed, options.UnplayedCount, options.Blur, options.BackgroundColor, options.ForegroundLayer); - CheckDisposed(); - - LockInfo lockInfo = GetLock(cacheFilePath); - - await lockInfo.Lock.WaitAsync().ConfigureAwait(false); - try { if (!File.Exists(cacheFilePath)) @@ -271,10 +222,6 @@ namespace Emby.Drawing _logger.LogError(ex, "Error encoding image"); return (originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified); } - finally - { - ReleaseLock(cacheFilePath, lockInfo); - } } private ImageFormat GetOutputFormat(IReadOnlyCollection clientSupportedFormats, bool requiresTransparency) @@ -306,20 +253,18 @@ namespace Emby.Drawing } private string GetMimeType(ImageFormat format, string path) - { - switch(format) + => format switch { - case ImageFormat.Bmp: return MimeTypes.GetMimeType("i.bmp"); - case ImageFormat.Gif: return MimeTypes.GetMimeType("i.gif"); - case ImageFormat.Jpg: return MimeTypes.GetMimeType("i.jpg"); - case ImageFormat.Png: return MimeTypes.GetMimeType("i.png"); - case ImageFormat.Webp: return MimeTypes.GetMimeType("i.webp"); - default: return MimeTypes.GetMimeType(path); - } - } + ImageFormat.Bmp => MimeTypes.GetMimeType("i.bmp"), + ImageFormat.Gif => MimeTypes.GetMimeType("i.gif"), + ImageFormat.Jpg => MimeTypes.GetMimeType("i.jpg"), + ImageFormat.Png => MimeTypes.GetMimeType("i.png"), + ImageFormat.Webp => MimeTypes.GetMimeType("i.webp"), + _ => MimeTypes.GetMimeType(path) + }; /// - /// Gets the cache file path based on a set of parameters + /// Gets the cache file path based on a set of parameters. /// private string GetCacheFilePath(string originalPath, ImageDimensions outputSize, int quality, DateTime dateModified, ImageFormat format, bool addPlayedIndicator, double percentPlayed, int? unwatchedCount, int? blur, string backgroundColor, string foregroundLayer) { @@ -401,11 +346,7 @@ namespace Emby.Drawing /// public string GetImageCacheTag(BaseItem item, ItemImageInfo image) - { - var supportedEnhancers = GetSupportedEnhancers(item, image.Type).ToArray(); - - return GetImageCacheTag(item, image, supportedEnhancers); - } + => (item.Path + image.DateModified.Ticks).GetMD5().ToString("N", CultureInfo.InvariantCulture); /// public string GetImageCacheTag(BaseItem item, ChapterInfo chapter) @@ -425,26 +366,6 @@ namespace Emby.Drawing } } - /// - public string GetImageCacheTag(BaseItem item, ItemImageInfo image, IReadOnlyCollection imageEnhancers) - { - string originalImagePath = image.Path; - DateTime dateModified = image.DateModified; - ImageType imageType = image.Type; - - // Optimization - if (imageEnhancers.Count == 0) - { - return (originalImagePath + dateModified.Ticks).GetMD5().ToString("N", CultureInfo.InvariantCulture); - } - - // Cache name is created with supported enhancers combined with the last config change so we pick up new config changes - var cacheKeys = imageEnhancers.Select(i => i.GetConfigurationCacheKey(item, imageType)).ToList(); - cacheKeys.Add(originalImagePath + dateModified.Ticks); - - return string.Join("|", cacheKeys).GetMD5().ToString("N", CultureInfo.InvariantCulture); - } - private async Task<(string path, DateTime dateModified)> GetSupportedImage(string originalImagePath, DateTime dateModified) { var inputFormat = Path.GetExtension(originalImagePath) @@ -488,154 +409,6 @@ namespace Emby.Drawing return (originalImagePath, dateModified); } - /// - public async Task GetEnhancedImage(BaseItem item, ImageType imageType, int imageIndex) - { - var enhancers = GetSupportedEnhancers(item, imageType).ToArray(); - - ItemImageInfo imageInfo = item.GetImageInfo(imageType, imageIndex); - - bool inputImageSupportsTransparency = SupportsTransparency(imageInfo.Path); - - var result = await GetEnhancedImage(imageInfo, inputImageSupportsTransparency, item, imageIndex, enhancers, CancellationToken.None); - - return result.path; - } - - private async Task<(string path, DateTime dateModified, bool transparent)> GetEnhancedImage( - ItemImageInfo image, - bool inputImageSupportsTransparency, - BaseItem item, - int imageIndex, - IReadOnlyCollection enhancers, - CancellationToken cancellationToken) - { - var originalImagePath = image.Path; - var dateModified = image.DateModified; - var imageType = image.Type; - - try - { - var cacheGuid = GetImageCacheTag(item, image, enhancers); - - // Enhance if we have enhancers - var enhancedImageInfo = await GetEnhancedImageInternal(originalImagePath, item, imageType, imageIndex, enhancers, cacheGuid, cancellationToken).ConfigureAwait(false); - - string enhancedImagePath = enhancedImageInfo.path; - - // If the path changed update dateModified - if (!string.Equals(enhancedImagePath, originalImagePath, StringComparison.OrdinalIgnoreCase)) - { - var treatmentRequiresTransparency = enhancedImageInfo.transparent; - - return (enhancedImagePath, _fileSystem.GetLastWriteTimeUtc(enhancedImagePath), treatmentRequiresTransparency); - } - } - catch (Exception ex) - { - _logger.LogError(ex, "Error enhancing image"); - } - - return (originalImagePath, dateModified, inputImageSupportsTransparency); - } - - /// - /// Gets the enhanced image internal. - /// - /// The original image path. - /// The item. - /// Type of the image. - /// Index of the image. - /// The supported enhancers. - /// The cache unique identifier. - /// The cancellation token. - /// Task<System.String>. - /// - /// originalImagePath - /// or - /// item - /// - private async Task<(string path, bool transparent)> GetEnhancedImageInternal( - string originalImagePath, - BaseItem item, - ImageType imageType, - int imageIndex, - IReadOnlyCollection supportedEnhancers, - string cacheGuid, - CancellationToken cancellationToken = default) - { - if (string.IsNullOrEmpty(originalImagePath)) - { - throw new ArgumentNullException(nameof(originalImagePath)); - } - - if (item == null) - { - throw new ArgumentNullException(nameof(item)); - } - - var treatmentRequiresTransparency = false; - foreach (var enhancer in supportedEnhancers) - { - if (!treatmentRequiresTransparency) - { - treatmentRequiresTransparency = enhancer.GetEnhancedImageInfo(item, originalImagePath, imageType, imageIndex).RequiresTransparency; - } - } - - // All enhanced images are saved as png to allow transparency - string cacheExtension = _imageEncoder.SupportedOutputFormats.Contains(ImageFormat.Webp) ? - ".webp" : - (treatmentRequiresTransparency ? ".png" : ".jpg"); - - string enhancedImagePath = GetCachePath(EnhancedImageCachePath, cacheGuid + cacheExtension); - - LockInfo lockInfo = GetLock(enhancedImagePath); - - await lockInfo.Lock.WaitAsync(cancellationToken).ConfigureAwait(false); - - try - { - // Check again in case of contention - if (File.Exists(enhancedImagePath)) - { - return (enhancedImagePath, treatmentRequiresTransparency); - } - - Directory.CreateDirectory(Path.GetDirectoryName(enhancedImagePath)); - - await ExecuteImageEnhancers(supportedEnhancers, originalImagePath, enhancedImagePath, item, imageType, imageIndex).ConfigureAwait(false); - - return (enhancedImagePath, treatmentRequiresTransparency); - } - finally - { - ReleaseLock(enhancedImagePath, lockInfo); - } - } - - /// - /// Executes the image enhancers. - /// - /// The image enhancers. - /// The input path. - /// The output path. - /// The item. - /// Type of the image. - /// Index of the image. - /// Task{EnhancedImage}. - private static async Task ExecuteImageEnhancers(IEnumerable imageEnhancers, string inputPath, string outputPath, BaseItem item, ImageType imageType, int imageIndex) - { - // Run the enhancers sequentially in order of priority - foreach (var enhancer in imageEnhancers) - { - await enhancer.EnhanceImageAsync(item, inputPath, outputPath, imageType, imageIndex).ConfigureAwait(false); - - // Feed the output into the next enhancer as input - inputPath = outputPath; - } - } - /// /// Gets the cache path. /// @@ -648,7 +421,7 @@ namespace Emby.Drawing /// or /// uniqueName /// or - /// fileExtension + /// fileExtension. /// public string GetCachePath(string path, string uniqueName, string fileExtension) { @@ -681,7 +454,7 @@ namespace Emby.Drawing /// /// path /// or - /// filename + /// filename. /// public string GetCachePath(string path, string filename) { @@ -689,6 +462,7 @@ namespace Emby.Drawing { throw new ArgumentNullException(nameof(path)); } + if (string.IsNullOrEmpty(filename)) { throw new ArgumentNullException(nameof(filename)); @@ -709,75 +483,20 @@ namespace Emby.Drawing _logger.LogInformation("Completed creation of image collage and saved to {Path}", options.OutputPath); } - /// - public IEnumerable GetSupportedEnhancers(BaseItem item, ImageType imageType) - { - foreach (var i in ImageEnhancers) - { - if (i.Supports(item, imageType)) - { - yield return i; - } - } - } - - - private class LockInfo - { - public SemaphoreSlim Lock = new SemaphoreSlim(1, 1); - public int Count = 1; - } - - private LockInfo GetLock(string key) - { - lock (_locks) - { - if (_locks.TryGetValue(key, out LockInfo info)) - { - info.Count++; - } - else - { - info = new LockInfo(); - _locks[key] = info; - } - return info; - } - } - - private void ReleaseLock(string key, LockInfo info) - { - info.Lock.Release(); - - lock (_locks) - { - info.Count--; - if (info.Count <= 0) - { - _locks.Remove(key); - info.Lock.Dispose(); - } - } - } - /// public void Dispose() - { - _disposed = true; - - var disposable = _imageEncoder as IDisposable; - if (disposable != null) - { - disposable.Dispose(); - } - } - - private void CheckDisposed() { if (_disposed) { - throw new ObjectDisposedException(GetType().Name); + return; } + + if (_imageEncoder is IDisposable disposable) + { + disposable.Dispose(); + } + + _disposed = true; } } } diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 0bb1d832fb..4f47874439 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -1074,8 +1074,6 @@ namespace Emby.Server.Implementations GetExports(), GetExports()); - ImageProcessor.ImageEnhancers = GetExports(); - LiveTvManager.AddParts(GetExports(), GetExports(), GetExports()); SubtitleManager.AddParts(GetExports()); diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index fcf0360c79..960f3f2d67 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -1362,56 +1362,33 @@ namespace Emby.Server.Implementations.Dto return null; } - var supportedEnhancers = _imageProcessor.GetSupportedEnhancers(item, ImageType.Primary).ToArray(); - ImageDimensions size; var defaultAspectRatio = item.GetDefaultPrimaryImageAspectRatio(); if (defaultAspectRatio > 0) { - if (supportedEnhancers.Length == 0) - { - return defaultAspectRatio; - } - - int dummyWidth = 200; - int dummyHeight = Convert.ToInt32(dummyWidth / defaultAspectRatio); - size = new ImageDimensions(dummyWidth, dummyHeight); + return defaultAspectRatio; } - else + + if (!imageInfo.IsLocalFile) { - if (!imageInfo.IsLocalFile) + return null; + } + + try + { + size = _imageProcessor.GetImageDimensions(item, imageInfo); + + if (size.Width <= 0 || size.Height <= 0) { return null; } - - try - { - size = _imageProcessor.GetImageDimensions(item, imageInfo); - - if (size.Width <= 0 || size.Height <= 0) - { - return null; - } - } - catch (Exception ex) - { - _logger.LogError(ex, "Failed to determine primary image aspect ratio for {0}", imageInfo.Path); - return null; - } } - - foreach (var enhancer in supportedEnhancers) + catch (Exception ex) { - try - { - size = enhancer.GetEnhancedImageSize(item, ImageType.Primary, 0, size); - } - catch (Exception ex) - { - _logger.LogError(ex, "Error in image enhancer: {0}", enhancer.GetType().Name); - } + _logger.LogError(ex, "Failed to determine primary image aspect ratio for {0}", imageInfo.Path); + return null; } var width = size.Width; diff --git a/Jellyfin.Drawing.Skia/SkiaEncoder.cs b/Jellyfin.Drawing.Skia/SkiaEncoder.cs index b080b3e6a5..2ea690650b 100644 --- a/Jellyfin.Drawing.Skia/SkiaEncoder.cs +++ b/Jellyfin.Drawing.Skia/SkiaEncoder.cs @@ -6,7 +6,6 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Extensions; using MediaBrowser.Model.Drawing; -using MediaBrowser.Model.Globalization; using Microsoft.Extensions.Logging; using SkiaSharp; using static Jellyfin.Drawing.Skia.SkiaHelper; @@ -18,27 +17,23 @@ namespace Jellyfin.Drawing.Skia /// public class SkiaEncoder : IImageEncoder { - private readonly ILogger _logger; - private readonly IApplicationPaths _appPaths; - private readonly ILocalizationManager _localizationManager; - private static readonly HashSet _transparentImageTypes = new HashSet(StringComparer.OrdinalIgnoreCase) { ".png", ".gif", ".webp" }; + private readonly ILogger _logger; + private readonly IApplicationPaths _appPaths; + /// /// Initializes a new instance of the class. /// /// The application logger. /// The application paths. - /// The application localization manager. public SkiaEncoder( ILogger logger, - IApplicationPaths appPaths, - ILocalizationManager localizationManager) + IApplicationPaths appPaths) { _logger = logger; _appPaths = appPaths; - _localizationManager = localizationManager; } /// @@ -235,9 +230,12 @@ namespace Jellyfin.Drawing.Skia private bool RequiresSpecialCharacterHack(string path) { - if (_localizationManager.HasUnicodeCategory(path, UnicodeCategory.OtherLetter)) + for (int i = 0; i < path.Length; i++) { - return true; + if (char.GetUnicodeCategory(path[i]) == UnicodeCategory.OtherLetter) + { + return true; + } } if (HasDiacritics(path)) diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index fa9b626744..c0621ba144 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -169,7 +169,7 @@ namespace Jellyfin.Server _loggerFactory, options, new ManagedFileSystem(_loggerFactory.CreateLogger(), appPaths), - new NullImageEncoder(), + GetImageEncoder(appPaths), new NetworkManager(_loggerFactory.CreateLogger()), appConfig); try @@ -193,8 +193,6 @@ namespace Jellyfin.Server throw; } - appHost.ImageProcessor.ImageEncoder = GetImageEncoder(appPaths, appHost.LocalizationManager); - await appHost.RunStartupTasksAsync().ConfigureAwait(false); stopWatch.Stop(); @@ -494,9 +492,7 @@ namespace Jellyfin.Server } } - private static IImageEncoder GetImageEncoder( - IApplicationPaths appPaths, - ILocalizationManager localizationManager) + private static IImageEncoder GetImageEncoder(IApplicationPaths appPaths) { try { @@ -505,8 +501,7 @@ namespace Jellyfin.Server return new SkiaEncoder( _loggerFactory.CreateLogger(), - appPaths, - localizationManager); + appPaths); } catch (Exception ex) { diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs index e94c1321f4..4060bf4cce 100644 --- a/MediaBrowser.Api/Images/ImageService.cs +++ b/MediaBrowser.Api/Images/ImageService.cs @@ -24,7 +24,7 @@ using Microsoft.Net.Http.Headers; namespace MediaBrowser.Api.Images { /// - /// Class GetItemImage + /// Class GetItemImage. /// [Route("/Items/{Id}/Images", "GET", Summary = "Gets information about an item's images")] [Authenticated] @@ -558,21 +558,6 @@ namespace MediaBrowser.Api.Images throw new ResourceNotFoundException(string.Format("{0} does not have an image of type {1}", displayText, request.Type)); } - IImageEnhancer[] supportedImageEnhancers; - if (_imageProcessor.ImageEnhancers.Count > 0) - { - if (item == null) - { - item = _libraryManager.GetItemById(itemId); - } - - supportedImageEnhancers = request.EnableImageEnhancers ? _imageProcessor.GetSupportedEnhancers(item, request.Type).ToArray() : Array.Empty(); - } - else - { - supportedImageEnhancers = Array.Empty(); - } - bool cropwhitespace; if (request.CropWhitespace.HasValue) { @@ -598,25 +583,25 @@ namespace MediaBrowser.Api.Images {"realTimeInfo.dlna.org", "DLNA.ORG_TLAG=*"} }; - return GetImageResult(item, + return GetImageResult( + item, itemId, request, imageInfo, cropwhitespace, outputFormats, - supportedImageEnhancers, cacheDuration, responseHeaders, isHeadRequest); } - private async Task GetImageResult(BaseItem item, + private async Task GetImageResult( + BaseItem item, Guid itemId, ImageRequest request, ItemImageInfo image, bool cropwhitespace, IReadOnlyCollection supportedFormats, - IReadOnlyCollection enhancers, TimeSpan? cacheDuration, IDictionary headers, bool isHeadRequest) @@ -624,7 +609,6 @@ namespace MediaBrowser.Api.Images var options = new ImageProcessingOptions { CropWhiteSpace = cropwhitespace, - Enhancers = enhancers, Height = request.Height, ImageIndex = request.Index ?? 0, Image = image, diff --git a/MediaBrowser.Controller/Drawing/IImageProcessor.cs b/MediaBrowser.Controller/Drawing/IImageProcessor.cs index a58a11bd1f..79399807fd 100644 --- a/MediaBrowser.Controller/Drawing/IImageProcessor.cs +++ b/MediaBrowser.Controller/Drawing/IImageProcessor.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.IO; using System.Threading.Tasks; using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Drawing; using MediaBrowser.Model.Entities; @@ -20,20 +19,12 @@ namespace MediaBrowser.Controller.Drawing /// The supported input formats. IReadOnlyCollection SupportedInputFormats { get; } - /// - /// Gets the image enhancers. - /// - /// The image enhancers. - IReadOnlyCollection ImageEnhancers { get; set; } - /// /// Gets a value indicating whether [supports image collage creation]. /// /// true if [supports image collage creation]; otherwise, false. bool SupportsImageCollageCreation { get; } - IImageEncoder ImageEncoder { get; set; } - /// /// Gets the dimensions of the image. /// @@ -58,14 +49,6 @@ namespace MediaBrowser.Controller.Drawing /// ImageDimensions ImageDimensions GetImageDimensions(BaseItem item, ItemImageInfo info, bool updateItem); - /// - /// Gets the supported enhancers. - /// - /// The item. - /// Type of the image. - /// IEnumerable{IImageEnhancer}. - IEnumerable GetSupportedEnhancers(BaseItem item, ImageType imageType); - /// /// Gets the image cache tag. /// @@ -75,15 +58,6 @@ namespace MediaBrowser.Controller.Drawing string GetImageCacheTag(BaseItem item, ItemImageInfo image); string GetImageCacheTag(BaseItem item, ChapterInfo info); - /// - /// Gets the image cache tag. - /// - /// The item. - /// The image. - /// The image enhancers. - /// Guid. - string GetImageCacheTag(BaseItem item, ItemImageInfo image, IReadOnlyCollection imageEnhancers); - /// /// Processes the image. /// @@ -99,15 +73,6 @@ namespace MediaBrowser.Controller.Drawing /// Task. Task<(string path, string mimeType, DateTime dateModified)> ProcessImage(ImageProcessingOptions options); - /// - /// Gets the enhanced image. - /// - /// The item. - /// Type of the image. - /// Index of the image. - /// Task{System.String}. - Task GetEnhancedImage(BaseItem item, ImageType imageType, int imageIndex); - /// /// Gets the supported image output formats. /// diff --git a/MediaBrowser.Controller/Drawing/ImageHelper.cs b/MediaBrowser.Controller/Drawing/ImageHelper.cs index 432cf80423..d5a5f547ea 100644 --- a/MediaBrowser.Controller/Drawing/ImageHelper.cs +++ b/MediaBrowser.Controller/Drawing/ImageHelper.cs @@ -19,8 +19,6 @@ namespace MediaBrowser.Controller.Drawing return GetSizeEstimate(options); } - public static IImageProcessor ImageProcessor { get; set; } - private static ImageDimensions GetSizeEstimate(ImageProcessingOptions options) { if (options.Width.HasValue && options.Height.HasValue) diff --git a/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs b/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs index 29addf6e65..870e0278e4 100644 --- a/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs +++ b/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Drawing; namespace MediaBrowser.Controller.Drawing @@ -34,8 +33,6 @@ namespace MediaBrowser.Controller.Drawing public int Quality { get; set; } - public IReadOnlyCollection Enhancers { get; set; } - public IReadOnlyCollection SupportedOutputFormats { get; set; } public bool AddPlayedIndicator { get; set; } diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs index 34af3b1568..6b3fb4144e 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs @@ -316,11 +316,7 @@ namespace MediaBrowser.Controller.MediaEncoding { if (VideoStream != null && VideoStream.Width.HasValue && VideoStream.Height.HasValue) { - var size = new ImageDimensions - { - Width = VideoStream.Width.Value, - Height = VideoStream.Height.Value - }; + var size = new ImageDimensions(VideoStream.Width.Value, VideoStream.Height.Value); var newSize = DrawingUtils.Resize(size, BaseRequest.Width ?? 0, @@ -346,11 +342,7 @@ namespace MediaBrowser.Controller.MediaEncoding { if (VideoStream != null && VideoStream.Width.HasValue && VideoStream.Height.HasValue) { - var size = new ImageDimensions - { - Width = VideoStream.Width.Value, - Height = VideoStream.Height.Value - }; + var size = new ImageDimensions(VideoStream.Width.Value, VideoStream.Height.Value); var newSize = DrawingUtils.Resize(size, BaseRequest.Width ?? 0, diff --git a/MediaBrowser.Controller/Providers/IImageEnhancer.cs b/MediaBrowser.Controller/Providers/IImageEnhancer.cs deleted file mode 100644 index c27c00ca26..0000000000 --- a/MediaBrowser.Controller/Providers/IImageEnhancer.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System.Threading.Tasks; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Model.Drawing; -using MediaBrowser.Model.Entities; - -namespace MediaBrowser.Controller.Providers -{ - public interface IImageEnhancer - { - /// - /// Return true only if the given image for the given item will be enhanced by this enhancer. - /// - /// The item. - /// Type of the image. - /// true if this enhancer will enhance the supplied image for the supplied item, false otherwise - bool Supports(BaseItem item, ImageType imageType); - - /// - /// Gets the priority or order in which this enhancer should be run. - /// - /// The priority. - MetadataProviderPriority Priority { get; } - - /// - /// Return a key incorporating all configuration information related to this item - /// - /// The item. - /// Type of the image. - /// Cache key relating to the current state of this item and configuration - string GetConfigurationCacheKey(BaseItem item, ImageType imageType); - - /// - /// Gets the size of the enhanced image. - /// - /// The item. - /// Type of the image. - /// Index of the image. - /// Size of the original image. - /// ImageSize. - ImageDimensions GetEnhancedImageSize(BaseItem item, ImageType imageType, int imageIndex, ImageDimensions originalImageSize); - - EnhancedImageInfo GetEnhancedImageInfo(BaseItem item, string inputFile, ImageType imageType, int imageIndex); - - /// - /// Enhances the image async. - /// - /// The item. - /// The input file. - /// The output file. - /// Type of the image. - /// Index of the image. - /// Task{Image}. - /// - Task EnhanceImageAsync(BaseItem item, string inputFile, string outputFile, ImageType imageType, int imageIndex); - } - - public class EnhancedImageInfo - { - public bool RequiresTransparency { get; set; } - } -} diff --git a/MediaBrowser.Model/Drawing/ImageDimensions.cs b/MediaBrowser.Model/Drawing/ImageDimensions.cs index e7805ac494..4bd8d85d4f 100644 --- a/MediaBrowser.Model/Drawing/ImageDimensions.cs +++ b/MediaBrowser.Model/Drawing/ImageDimensions.cs @@ -1,36 +1,43 @@ +using System.Globalization; + namespace MediaBrowser.Model.Drawing { /// /// Struct ImageDimensions /// - public struct ImageDimensions + public readonly struct ImageDimensions { - /// - /// Gets or sets the height. - /// - /// The height. - public int Height { get; set; } + public ImageDimensions(int width, int height) + { + Width = width; + Height = height; + } /// - /// Gets or sets the width. + /// Gets the height. + /// + /// The height. + public int Height { get; } + + /// + /// Gets the width. /// /// The width. - public int Width { get; set; } + public int Width { get; } public bool Equals(ImageDimensions size) { return Width.Equals(size.Width) && Height.Equals(size.Height); } + /// public override string ToString() { - return string.Format("{0}-{1}", Width, Height); - } - - public ImageDimensions(int width, int height) - { - Width = width; - Height = height; + return string.Format( + CultureInfo.InvariantCulture, + "{0}-{1}", + Width, + Height); } } } From 82112b67880265c34f2dcca1ee7f0acf52a156df Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 22 Jan 2020 21:00:07 +0100 Subject: [PATCH 023/270] Improvements to dlna server * Improve response writer * Add analyzers * Error on warnings in release mode * Disable doc warnings --- Emby.Dlna/Api/DlnaServerService.cs | 3 + Emby.Dlna/Api/DlnaService.cs | 3 + Emby.Dlna/Common/Argument.cs | 2 + Emby.Dlna/Common/DeviceIcon.cs | 11 +- Emby.Dlna/Common/DeviceService.cs | 7 +- Emby.Dlna/Common/ServiceAction.cs | 22 +- Emby.Dlna/Common/StateVariable.cs | 18 +- Emby.Dlna/Configuration/DlnaOptions.cs | 27 +- Emby.Dlna/ConfigurationExtension.cs | 3 + .../ConnectionManager/ConnectionManager.cs | 3 + .../ConnectionManagerXmlBuilder.cs | 3 + Emby.Dlna/ConnectionManager/ControlHandler.cs | 40 +- .../ServiceActionListBuilder.cs | 3 + .../ContentDirectory/ContentDirectory.cs | 3 + .../ContentDirectoryXmlBuilder.cs | 3 + Emby.Dlna/ContentDirectory/ControlHandler.cs | 368 ++++++++---------- .../ServiceActionListBuilder.cs | 3 + Emby.Dlna/ControlRequest.cs | 3 + Emby.Dlna/ControlResponse.cs | 13 +- Emby.Dlna/Didl/DidlBuilder.cs | 4 +- Emby.Dlna/Didl/Filter.cs | 3 + Emby.Dlna/Didl/StringWriterWithEncoding.cs | 3 + Emby.Dlna/DlnaManager.cs | 3 + Emby.Dlna/Emby.Dlna.csproj | 13 + Emby.Dlna/EventSubscriptionResponse.cs | 14 +- Emby.Dlna/Eventing/EventManager.cs | 6 +- Emby.Dlna/Eventing/EventSubscription.cs | 7 +- Emby.Dlna/IConnectionManager.cs | 2 + Emby.Dlna/IContentDirectory.cs | 2 + Emby.Dlna/IEventManager.cs | 2 + Emby.Dlna/IMediaReceiverRegistrar.cs | 2 + Emby.Dlna/IUpnpService.cs | 3 + Emby.Dlna/Main/DlnaEntryPoint.cs | 3 + .../MediaReceiverRegistrar/ControlHandler.cs | 54 +-- .../MediaReceiverRegistrar.cs | 3 + .../MediaReceiverRegistrarXmlBuilder.cs | 3 + .../ServiceActionListBuilder.cs | 3 + Emby.Dlna/PlayTo/Device.cs | 33 +- Emby.Dlna/PlayTo/DeviceInfo.cs | 3 + Emby.Dlna/PlayTo/PlayToController.cs | 3 + Emby.Dlna/PlayTo/PlayToManager.cs | 11 +- Emby.Dlna/PlayTo/PlaybackProgressEventArgs.cs | 3 + Emby.Dlna/PlayTo/PlaybackStartEventArgs.cs | 3 + Emby.Dlna/PlayTo/PlaybackStoppedEventArgs.cs | 3 + Emby.Dlna/PlayTo/PlaylistItem.cs | 3 + Emby.Dlna/PlayTo/PlaylistItemFactory.cs | 3 + Emby.Dlna/PlayTo/SsdpHttpClient.cs | 15 +- Emby.Dlna/PlayTo/TRANSPORTSTATE.cs | 3 + Emby.Dlna/PlayTo/TransportCommands.cs | 3 + Emby.Dlna/PlayTo/UpnpContainer.cs | 3 + Emby.Dlna/PlayTo/uBaseObject.cs | 3 + Emby.Dlna/PlayTo/uPnpNamespaces.cs | 3 + Emby.Dlna/Profiles/DefaultProfile.cs | 3 + Emby.Dlna/Profiles/DenonAvrProfile.cs | 3 + Emby.Dlna/Profiles/DirectTvProfile.cs | 3 + Emby.Dlna/Profiles/DishHopperJoeyProfile.cs | 3 + Emby.Dlna/Profiles/Foobar2000Profile.cs | 3 + Emby.Dlna/Profiles/LgTvProfile.cs | 3 + Emby.Dlna/Profiles/LinksysDMA2100Profile.cs | 3 + Emby.Dlna/Profiles/MarantzProfile.cs | 3 + Emby.Dlna/Profiles/MediaMonkeyProfile.cs | 3 + Emby.Dlna/Profiles/PanasonicVieraProfile.cs | 3 + Emby.Dlna/Profiles/PopcornHourProfile.cs | 3 + Emby.Dlna/Profiles/SamsungSmartTvProfile.cs | 3 + Emby.Dlna/Profiles/SharpSmartTvProfile.cs | 3 + Emby.Dlna/Profiles/SonyBlurayPlayer2013.cs | 3 + Emby.Dlna/Profiles/SonyBlurayPlayer2014.cs | 3 + Emby.Dlna/Profiles/SonyBlurayPlayer2015.cs | 3 + Emby.Dlna/Profiles/SonyBlurayPlayer2016.cs | 3 + Emby.Dlna/Profiles/SonyBlurayPlayerProfile.cs | 3 + Emby.Dlna/Profiles/SonyBravia2010Profile.cs | 3 + Emby.Dlna/Profiles/SonyBravia2011Profile.cs | 3 + Emby.Dlna/Profiles/SonyBravia2012Profile.cs | 3 + Emby.Dlna/Profiles/SonyBravia2013Profile.cs | 3 + Emby.Dlna/Profiles/SonyBravia2014Profile.cs | 3 + Emby.Dlna/Profiles/SonyPs3Profile.cs | 3 + Emby.Dlna/Profiles/SonyPs4Profile.cs | 3 + Emby.Dlna/Profiles/WdtvLiveProfile.cs | 3 + Emby.Dlna/Profiles/XboxOneProfile.cs | 3 + Emby.Dlna/Server/DescriptionXmlBuilder.cs | 3 + Emby.Dlna/Service/BaseControlHandler.cs | 17 +- Emby.Dlna/Service/BaseService.cs | 3 + Emby.Dlna/Service/ControlErrorHandler.cs | 3 + Emby.Dlna/Service/ServiceXmlBuilder.cs | 3 + Emby.Dlna/Ssdp/DeviceDiscovery.cs | 15 +- Emby.Dlna/Ssdp/Extensions.cs | 3 + 86 files changed, 564 insertions(+), 327 deletions(-) diff --git a/Emby.Dlna/Api/DlnaServerService.cs b/Emby.Dlna/Api/DlnaServerService.cs index a7ff6e9cfd..4d9933a0cb 100644 --- a/Emby.Dlna/Api/DlnaServerService.cs +++ b/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; diff --git a/Emby.Dlna/Api/DlnaService.cs b/Emby.Dlna/Api/DlnaService.cs index 7f51f477a7..f10695541d 100644 --- a/Emby.Dlna/Api/DlnaService.cs +++ b/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; diff --git a/Emby.Dlna/Common/Argument.cs b/Emby.Dlna/Common/Argument.cs index 3e325c41ce..c6ab9959e0 100644 --- a/Emby.Dlna/Common/Argument.cs +++ b/Emby.Dlna/Common/Argument.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 namespace Emby.Dlna.Common { diff --git a/Emby.Dlna/Common/DeviceIcon.cs b/Emby.Dlna/Common/DeviceIcon.cs index 3a91b952ec..49d19992d8 100644 --- a/Emby.Dlna/Common/DeviceIcon.cs +++ b/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; } + /// public override string ToString() { - return string.Format("{0}x{1}", Height, Width); + return string.Format( + CultureInfo.InvariantCulture, + "{0}x{1}", + Height, + Width); } } } diff --git a/Emby.Dlna/Common/DeviceService.cs b/Emby.Dlna/Common/DeviceService.cs index c60d652913..9947ec6b9a 100644 --- a/Emby.Dlna/Common/DeviceService.cs +++ b/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; } + /// public override string ToString() - { - return string.Format("{0}", ServiceId); - } + => ServiceId; } } diff --git a/Emby.Dlna/Common/ServiceAction.cs b/Emby.Dlna/Common/ServiceAction.cs index 5e030d396a..15c4be8092 100644 --- a/Emby.Dlna/Common/ServiceAction.cs +++ b/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 string Name { get; set; } - - public List ArgumentList { get; set; } - - public override string ToString() - { - return Name; - } - public ServiceAction() { ArgumentList = new List(); } + + public string Name { get; set; } + + public List ArgumentList { get; set; } + + /// + public override string ToString() + { + return Name; + } } } diff --git a/Emby.Dlna/Common/StateVariable.cs b/Emby.Dlna/Common/StateVariable.cs index 4ca84bf51c..bade28e4bc 100644 --- a/Emby.Dlna/Common/StateVariable.cs +++ b/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(); + } + public string Name { get; set; } public string DataType { get; set; } @@ -12,14 +20,8 @@ namespace Emby.Dlna.Common public string[] AllowedValues { get; set; } + /// public override string ToString() - { - return Name; - } - - public StateVariable() - { - AllowedValues = Array.Empty(); - } + => Name; } } diff --git a/Emby.Dlna/Configuration/DlnaOptions.cs b/Emby.Dlna/Configuration/DlnaOptions.cs index c7cb364a83..84587a7cee 100644 --- a/Emby.Dlna/Configuration/DlnaOptions.cs +++ b/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; } } } diff --git a/Emby.Dlna/ConfigurationExtension.cs b/Emby.Dlna/ConfigurationExtension.cs index 82d726e018..f8125c12c3 100644 --- a/Emby.Dlna/ConfigurationExtension.cs +++ b/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; diff --git a/Emby.Dlna/ConnectionManager/ConnectionManager.cs b/Emby.Dlna/ConnectionManager/ConnectionManager.cs index 934b353c28..365249c54a 100644 --- a/Emby.Dlna/ConnectionManager/ConnectionManager.cs +++ b/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; diff --git a/Emby.Dlna/ConnectionManager/ConnectionManagerXmlBuilder.cs b/Emby.Dlna/ConnectionManager/ConnectionManagerXmlBuilder.cs index f5873455a6..c8c97c79c0 100644 --- a/Emby.Dlna/ConnectionManager/ConnectionManagerXmlBuilder.cs +++ b/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; diff --git a/Emby.Dlna/ConnectionManager/ControlHandler.cs b/Emby.Dlna/ConnectionManager/ControlHandler.cs index 2e11047487..7e6c7eb228 100644 --- a/Emby.Dlna/ConnectionManager/ControlHandler.cs +++ b/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,27 @@ namespace Emby.Dlna.ConnectionManager { private readonly DeviceProfile _profile; - protected override IEnumerable> GetResult(string methodName, IDictionary methodParams) - { - if (string.Equals(methodName, "GetProtocolInfo", StringComparison.OrdinalIgnoreCase)) - { - return HandleGetProtocolInfo(); - } - - throw new ResourceNotFoundException("Unexpected control request name: " + methodName); - } - - private IEnumerable> HandleGetProtocolInfo() - { - return new Dictionary(StringComparer.OrdinalIgnoreCase) - { - { "Source", _profile.ProtocolInfo }, - { "Sink", "" } - }; - } - public ControlHandler(IServerConfigurationManager config, ILogger logger, DeviceProfile profile) : base(config, logger) { _profile = profile; } + + /// + protected override void WriteResult(string methodName, IDictionary methodParams, XmlWriter xmlWriter) + { + if (string.Equals(methodName, "GetProtocolInfo", StringComparison.OrdinalIgnoreCase)) + { + HandleGetProtocolInfo(xmlWriter); + } + + throw new ResourceNotFoundException("Unexpected control request name: " + methodName); + } + + private void HandleGetProtocolInfo(XmlWriter xmlWriter) + { + xmlWriter.WriteElementString("Source", _profile.ProtocolInfo); + xmlWriter.WriteElementString("Sink", string.Empty); + } } } diff --git a/Emby.Dlna/ConnectionManager/ServiceActionListBuilder.cs b/Emby.Dlna/ConnectionManager/ServiceActionListBuilder.cs index b7727b5580..019a0f80f4 100644 --- a/Emby.Dlna/ConnectionManager/ServiceActionListBuilder.cs +++ b/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; diff --git a/Emby.Dlna/ContentDirectory/ContentDirectory.cs b/Emby.Dlna/ContentDirectory/ContentDirectory.cs index a3062b18fd..523430e430 100644 --- a/Emby.Dlna/ContentDirectory/ContentDirectory.cs +++ b/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; diff --git a/Emby.Dlna/ContentDirectory/ContentDirectoryXmlBuilder.cs b/Emby.Dlna/ContentDirectory/ContentDirectoryXmlBuilder.cs index 15fdb36c4c..282a47c738 100644 --- a/Emby.Dlna/ContentDirectory/ContentDirectoryXmlBuilder.cs +++ b/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; diff --git a/Emby.Dlna/ContentDirectory/ControlHandler.cs b/Emby.Dlna/ContentDirectory/ControlHandler.cs index 396649c5ea..e5acb92db5 100644 --- a/Emby.Dlna/ContentDirectory/ControlHandler.cs +++ b/Emby.Dlna/ContentDirectory/ControlHandler.cs @@ -1,7 +1,9 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System; using System.Collections.Generic; using System.Globalization; -using System.IO; using System.Linq; using System.Text; using System.Threading; @@ -79,114 +81,129 @@ namespace Emby.Dlna.ContentDirectory _didlBuilder = new DidlBuilder(profile, user, imageProcessor, serverAddress, accessToken, userDataManager, localization, mediaSourceManager, Logger, mediaEncoder); } - protected override IEnumerable> GetResult(string methodName, IDictionary methodParams) + /// + protected override void WriteResult(string methodName, IDictionary 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> HandleXSetBookmark(IDictionary sparams, User user) + private void HandleXSetBookmark(IDictionary 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 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(StringComparer.OrdinalIgnoreCase); } - private IEnumerable> HandleGetSearchCapabilities() + private void HandleGetSearchCapabilities(XmlWriter xmlWriter) { - return new Dictionary(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> HandleGetSortCapabilities() + private void HandleGetSortCapabilities(XmlWriter xmlWriter) { - return new Dictionary(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> HandleGetSortExtensionCapabilities() + private void HandleGetSortExtensionCapabilities(XmlWriter xmlWriter) { - return new Dictionary(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> HandleGetSystemUpdateID() + private void HandleGetSystemUpdateID(XmlWriter xmlWriter) { - var headers = new Dictionary(StringComparer.OrdinalIgnoreCase); - headers.Add("Id", _systemUpdateId.ToString(_usCulture)); - return headers; + xmlWriter.WriteElementString("Id", _systemUpdateId.ToString(CultureInfo.InvariantCulture)); } - private IEnumerable> HandleGetFeatureList() + private void HandleGetFeatureList(XmlWriter xmlWriter) { - return new Dictionary(StringComparer.OrdinalIgnoreCase) - { - { "FeatureList", GetFeatureListXml() } - }; + xmlWriter.WriteElementString("FeatureList", WriteFeatureListXml()); } - private IEnumerable> HandleXGetFeatureList() - { - return new Dictionary(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(""); @@ -213,7 +230,7 @@ namespace Emby.Dlna.ContentDirectory return defaultValue; } - private IEnumerable> HandleBrowse(IDictionary sparams, User user, string deviceId) + private void HandleBrowse(XmlWriter xmlWriter, IDictionary sparams, string deviceId) { var id = sparams["ObjectID"]; var flag = sparams["BrowseFlag"]; @@ -237,101 +254,81 @@ namespace Emby.Dlna.ContentDirectory start = startVal; } - var settings = new XmlWriterSettings - { - Encoding = Encoding.UTF8, - CloseOutput = false, - OmitXmlDeclaration = true, - ConformanceLevel = ConformanceLevel.Fragment - }; + xmlWriter.WriteStartElement("Result"); - StringWriter builder = new StringWriterWithEncoding(Encoding.UTF8); + xmlWriter.WriteStartElement(string.Empty, "DIDL-Lite", NS_DIDL); + + xmlWriter.WriteAttributeString("xmlns", "dc", null, NS_DC); + xmlWriter.WriteAttributeString("xmlns", "dlna", null, NS_DLNA); + xmlWriter.WriteAttributeString("xmlns", "upnp", null, NS_UPNP); + //didl.SetAttribute("xmlns:sec", NS_SEC); + + DidlBuilder.WriteXmlRootAttributes(_profile, xmlWriter); + + var serverItem = GetItemFromObjectId(id, _user); + var item = serverItem.Item; int totalCount; - - var dlnaOptions = _config.GetDlnaConfiguration(); - - using (var writer = XmlWriter.Create(builder, settings)) + if (string.Equals(flag, "BrowseMetadata")) { - //writer.WriteStartDocument(); + totalCount = 1; - 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; - - if (string.Equals(flag, "BrowseMetadata")) + if (item.IsDisplayedAsFolder || serverItem.StubType.HasValue) { - totalCount = 1; + var childrenResult = GetUserItems(item, serverItem.StubType, _user, sortCriteria, start, requestedCount); - if (item.IsDisplayedAsFolder || serverItem.StubType.HasValue) - { - var childrenResult = GetUserItems(item, serverItem.StubType, user, sortCriteria, start, requestedCount); - - _didlBuilder.WriteFolderElement(writer, item, serverItem.StubType, null, childrenResult.TotalRecordCount, filter, id); - } - else - { - _didlBuilder.WriteItemElement(dlnaOptions, writer, item, user, null, null, deviceId, filter); - } - - provided++; + _didlBuilder.WriteFolderElement(xmlWriter, item, serverItem.StubType, null, childrenResult.TotalRecordCount, filter, id); } else { - var childrenResult = GetUserItems(item, serverItem.StubType, user, sortCriteria, start, requestedCount); - totalCount = childrenResult.TotalRecordCount; - - provided = childrenResult.Items.Count; - - 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); - } - } + var dlnaOptions = _config.GetDlnaConfiguration(); + _didlBuilder.WriteItemElement(dlnaOptions, xmlWriter, item, _user, null, null, deviceId, filter); } - writer.WriteFullEndElement(); - //writer.WriteEndDocument(); + 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(xmlWriter, childItem, displayStubType, item, childCount, filter); + } + else + { + _didlBuilder.WriteItemElement(dlnaOptions, xmlWriter, childItem, _user, item, serverItem.StubType, deviceId, filter); + } + } } - var resXML = builder.ToString(); + xmlWriter.WriteFullEndElement(); + xmlWriter.WriteFullEndElement(); - return new[] - { - new KeyValuePair("Result", resXML), - new KeyValuePair("NumberReturned", provided.ToString(_usCulture)), - new KeyValuePair("TotalMatches", totalCount.ToString(_usCulture)), - new KeyValuePair("UpdateID", _systemUpdateId.ToString(_usCulture)) - }; + xmlWriter.WriteElementString("NumberReturned", provided.ToString(_usCulture)); + xmlWriter.WriteElementString("TotalMatches", totalCount.ToString(_usCulture)); + xmlWriter.WriteElementString("UpdateID", _systemUpdateId.ToString(_usCulture)); } - private IEnumerable> HandleX_BrowseByLetter(IDictionary sparams, User user, string deviceId) + private void HandleXBrowseByLetter(XmlWriter xmlWriter, IDictionary sparams, string deviceId) { // TODO: Implement this method - return HandleSearch(sparams, user, deviceId); + HandleSearch(xmlWriter, sparams, deviceId); } - private IEnumerable> HandleSearch(IDictionary sparams, User user, string deviceId) + private void HandleSearch(XmlWriter xmlWriter, IDictionary sparams, string deviceId) { var searchCriteria = new SearchCriteria(GetValueOrDefault(sparams, "SearchCriteria", "")); var sortCriteria = new SortCriteria(GetValueOrDefault(sparams, "SortCriteria", "")); @@ -354,99 +351,74 @@ namespace Emby.Dlna.ContentDirectory start = startVal; } - var settings = new XmlWriterSettings + xmlWriter.WriteStartElement("Result"); + + xmlWriter.WriteStartElement(string.Empty, "DIDL-Lite", NS_DIDL); + + xmlWriter.WriteAttributeString("xmlns", "dc", null, NS_DC); + xmlWriter.WriteAttributeString("xmlns", "dlna", null, NS_DLNA); + xmlWriter.WriteAttributeString("xmlns", "upnp", null, NS_UPNP); + //didl.SetAttribute("xmlns:sec", NS_SEC); + + DidlBuilder.WriteXmlRootAttributes(_profile, xmlWriter); + + var serverItem = GetItemFromObjectId(sparams["ContainerID"], _user); + + var item = serverItem.Item; + + var childrenResult = GetChildrenSorted(item, _user, searchCriteria, sortCriteria, start, requestedCount); + + var dlnaOptions = _config.GetDlnaConfiguration(); + + foreach (var i in childrenResult.Items) { - Encoding = Encoding.UTF8, - CloseOutput = false, - OmitXmlDeclaration = true, - ConformanceLevel = ConformanceLevel.Fragment - }; - - StringWriter builder = new StringWriterWithEncoding(Encoding.UTF8); - int totalCount = 0; - int provided = 0; - - using (var writer = XmlWriter.Create(builder, settings)) - { - //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(sparams["ContainerID"], user); - - var item = serverItem.Item; - - var childrenResult = (GetChildrenSorted(item, user, searchCriteria, sortCriteria, start, requestedCount)); - - totalCount = childrenResult.TotalRecordCount; - - provided = childrenResult.Items.Count; - - var dlnaOptions = _config.GetDlnaConfiguration(); - - foreach (var i in childrenResult.Items) + if (i.IsDisplayedAsFolder) { - if (i.IsDisplayedAsFolder) - { - var childCount = (GetChildrenSorted(i, user, searchCriteria, sortCriteria, null, 0)) - .TotalRecordCount; + 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(xmlWriter, i, null, item, childCount, filter); + } + else + { + _didlBuilder.WriteItemElement(dlnaOptions, xmlWriter, i, _user, item, serverItem.StubType, deviceId, filter); } - - writer.WriteFullEndElement(); - //writer.WriteEndDocument(); } - var resXML = builder.ToString(); + xmlWriter.WriteFullEndElement(); + xmlWriter.WriteFullEndElement(); - return new List> - { - new KeyValuePair("Result", resXML), - new KeyValuePair("NumberReturned", provided.ToString(_usCulture)), - new KeyValuePair("TotalMatches", totalCount.ToString(_usCulture)), - new KeyValuePair("UpdateID", _systemUpdateId.ToString(_usCulture)) - }; + xmlWriter.WriteElementString("NumberReturned", childrenResult.Items.Count.ToString(_usCulture)); + xmlWriter.WriteElementString("TotalMatches", childrenResult.TotalRecordCount.ToString(_usCulture)); + xmlWriter.WriteElementString("UpdateID", _systemUpdateId.ToString(_usCulture)); } private QueryResult 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) + var sortOrders = folder.IsPreSorted switch { - sortOrders.Add((ItemSortBy.SortName, sort.SortOrder)); - } + true => Array.Empty<(string, SortOrder)>(), + false => new[] { (ItemSortBy.SortName, sort.SortOrder) } + }; - var mediaTypes = new List(); + string[] mediaTypes = Array.Empty(); 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 +442,7 @@ namespace Emby.Dlna.ContentDirectory IsMissing = false, ExcludeItemTypes = new[] { typeof(Book).Name }, IsFolder = isFolder, - MediaTypes = mediaTypes.ToArray(), + MediaTypes = mediaTypes, DtoOptions = GetDtoOptions() }); } @@ -1304,11 +1276,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]; diff --git a/Emby.Dlna/ContentDirectory/ServiceActionListBuilder.cs b/Emby.Dlna/ContentDirectory/ServiceActionListBuilder.cs index e999314fa6..a385a74cfb 100644 --- a/Emby.Dlna/ContentDirectory/ServiceActionListBuilder.cs +++ b/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; diff --git a/Emby.Dlna/ControlRequest.cs b/Emby.Dlna/ControlRequest.cs index 8c227159c4..97ad41c834 100644 --- a/Emby.Dlna/ControlRequest.cs +++ b/Emby.Dlna/ControlRequest.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System.IO; using Microsoft.AspNetCore.Http; diff --git a/Emby.Dlna/ControlResponse.cs b/Emby.Dlna/ControlResponse.cs index d2b79fc585..0215a5e387 100644 --- a/Emby.Dlna/ControlResponse.cs +++ b/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(); + } + public IDictionary Headers { get; set; } public string Xml { get; set; } public bool IsSuccessful { get; set; } - - public ControlResponse() - { - Headers = new Dictionary(); - } } } diff --git a/Emby.Dlna/Didl/DidlBuilder.cs b/Emby.Dlna/Didl/DidlBuilder.cs index 85ef9d4829..f2128d9c6d 100644 --- a/Emby.Dlna/Didl/DidlBuilder.cs +++ b/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; @@ -18,7 +21,6 @@ using MediaBrowser.Controller.Playlists; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Drawing; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Extensions; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Net; using Microsoft.Extensions.Logging; diff --git a/Emby.Dlna/Didl/Filter.cs b/Emby.Dlna/Didl/Filter.cs index a0e67870e9..88cf7cb5a6 100644 --- a/Emby.Dlna/Didl/Filter.cs +++ b/Emby.Dlna/Didl/Filter.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System; using MediaBrowser.Model.Extensions; diff --git a/Emby.Dlna/Didl/StringWriterWithEncoding.cs b/Emby.Dlna/Didl/StringWriterWithEncoding.cs index c3c4bd393b..edc2588995 100644 --- a/Emby.Dlna/Didl/StringWriterWithEncoding.cs +++ b/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; diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs index d5d788021d..14602485af 100644 --- a/Emby.Dlna/DlnaManager.cs +++ b/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; diff --git a/Emby.Dlna/Emby.Dlna.csproj b/Emby.Dlna/Emby.Dlna.csproj index 8d6fabdb44..0cabe43d51 100644 --- a/Emby.Dlna/Emby.Dlna.csproj +++ b/Emby.Dlna/Emby.Dlna.csproj @@ -15,6 +15,19 @@ netstandard2.1 false true + true + + + + + + + + + + + + ../jellyfin.ruleset diff --git a/Emby.Dlna/EventSubscriptionResponse.cs b/Emby.Dlna/EventSubscriptionResponse.cs index 6dc1aacf4f..f90d273c4b 100644 --- a/Emby.Dlna/EventSubscriptionResponse.cs +++ b/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 Headers { get; set; } - public EventSubscriptionResponse() { Headers = new Dictionary(); } + + public string Content { get; set; } + + public string ContentType { get; set; } + + public Dictionary Headers { get; set; } } } diff --git a/Emby.Dlna/Eventing/EventManager.cs b/Emby.Dlna/Eventing/EventManager.cs index b76a0066d1..7881898803 100644 --- a/Emby.Dlna/Eventing/EventManager.cs +++ b/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)) { } diff --git a/Emby.Dlna/Eventing/EventSubscription.cs b/Emby.Dlna/Eventing/EventSubscription.cs index eb8781e0cd..108ab48303 100644 --- a/Emby.Dlna/Eventing/EventSubscription.cs +++ b/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; } } diff --git a/Emby.Dlna/IConnectionManager.cs b/Emby.Dlna/IConnectionManager.cs index 855c4454d9..01fb869f57 100644 --- a/Emby.Dlna/IConnectionManager.cs +++ b/Emby.Dlna/IConnectionManager.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 namespace Emby.Dlna { diff --git a/Emby.Dlna/IContentDirectory.cs b/Emby.Dlna/IContentDirectory.cs index b54a17c009..a28ad2b9c8 100644 --- a/Emby.Dlna/IContentDirectory.cs +++ b/Emby.Dlna/IContentDirectory.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 namespace Emby.Dlna { diff --git a/Emby.Dlna/IEventManager.cs b/Emby.Dlna/IEventManager.cs index 4f67a1b9b6..d0960aa16c 100644 --- a/Emby.Dlna/IEventManager.cs +++ b/Emby.Dlna/IEventManager.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 namespace Emby.Dlna { diff --git a/Emby.Dlna/IMediaReceiverRegistrar.cs b/Emby.Dlna/IMediaReceiverRegistrar.cs index 5dde01f58b..d2aaa8f553 100644 --- a/Emby.Dlna/IMediaReceiverRegistrar.cs +++ b/Emby.Dlna/IMediaReceiverRegistrar.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 namespace Emby.Dlna { diff --git a/Emby.Dlna/IUpnpService.cs b/Emby.Dlna/IUpnpService.cs index 0f3d327ed2..289e2df78c 100644 --- a/Emby.Dlna/IUpnpService.cs +++ b/Emby.Dlna/IUpnpService.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System.Threading.Tasks; namespace Emby.Dlna diff --git a/Emby.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs index 77bde0ca24..1ee4151e4b 100644 --- a/Emby.Dlna/Main/DlnaEntryPoint.cs +++ b/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; diff --git a/Emby.Dlna/MediaReceiverRegistrar/ControlHandler.cs b/Emby.Dlna/MediaReceiverRegistrar/ControlHandler.cs index 7381e52582..815aac5c7c 100644 --- a/Emby.Dlna/MediaReceiverRegistrar/ControlHandler.cs +++ b/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> GetResult(string methodName, IDictionary methodParams) - { - 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> HandleIsAuthorized() - { - return new Dictionary(StringComparer.OrdinalIgnoreCase) - { - { "Result", "1" } - }; - } - - private static IEnumerable> HandleIsValidated() - { - return new Dictionary(StringComparer.OrdinalIgnoreCase) - { - { "Result", "1" } - }; - } - public ControlHandler(IServerConfigurationManager config, ILogger logger) : base(config, logger) { } + + /// + protected override void WriteResult(string methodName, IDictionary methodParams, XmlWriter xmlWriter) + { + if (string.Equals(methodName, "IsAuthorized", StringComparison.OrdinalIgnoreCase)) + { + HandleIsAuthorized(xmlWriter); + return; + } + + if (string.Equals(methodName, "IsValidated", StringComparison.OrdinalIgnoreCase)) + { + HandleIsValidated(xmlWriter); + return; + } + + 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"); } } diff --git a/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrar.cs b/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrar.cs index 5eab6aee0c..e2d48bc01f 100644 --- a/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrar.cs +++ b/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; diff --git a/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarXmlBuilder.cs b/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarXmlBuilder.cs index 6413411851..465b08f58e 100644 --- a/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarXmlBuilder.cs +++ b/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; diff --git a/Emby.Dlna/MediaReceiverRegistrar/ServiceActionListBuilder.cs b/Emby.Dlna/MediaReceiverRegistrar/ServiceActionListBuilder.cs index 86429f6051..3e8b2dbd88 100644 --- a/Emby.Dlna/MediaReceiverRegistrar/ServiceActionListBuilder.cs +++ b/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; diff --git a/Emby.Dlna/PlayTo/Device.cs b/Emby.Dlna/PlayTo/Device.cs index 0c5ddee654..61db264a2f 100644 --- a/Emby.Dlna/PlayTo/Device.cs +++ b/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 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); diff --git a/Emby.Dlna/PlayTo/DeviceInfo.cs b/Emby.Dlna/PlayTo/DeviceInfo.cs index 9e7c04bdb3..c36f890966 100644 --- a/Emby.Dlna/PlayTo/DeviceInfo.cs +++ b/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; diff --git a/Emby.Dlna/PlayTo/PlayToController.cs b/Emby.Dlna/PlayTo/PlayToController.cs index c58f16438b..ad01f0576d 100644 --- a/Emby.Dlna/PlayTo/PlayToController.cs +++ b/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; diff --git a/Emby.Dlna/PlayTo/PlayToManager.cs b/Emby.Dlna/PlayTo/PlayToManager.cs index 2ca44b7ea2..5d75e33600 100644 --- a/Emby.Dlna/PlayTo/PlayToManager.cs +++ b/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 e) + private async void OnDeviceDiscoveryDeviceDiscovered(object sender, GenericEventArgs e) { if (_disposed) { @@ -231,7 +234,7 @@ namespace Emby.Dlna.PlayTo public void Dispose() { - _deviceDiscovery.DeviceDiscovered -= _deviceDiscovery_DeviceDiscovered; + _deviceDiscovery.DeviceDiscovered -= OnDeviceDiscoveryDeviceDiscovered; try { diff --git a/Emby.Dlna/PlayTo/PlaybackProgressEventArgs.cs b/Emby.Dlna/PlayTo/PlaybackProgressEventArgs.cs index ffa56419bb..bdd2a6c3e4 100644 --- a/Emby.Dlna/PlayTo/PlaybackProgressEventArgs.cs +++ b/Emby.Dlna/PlayTo/PlaybackProgressEventArgs.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System; namespace Emby.Dlna.PlayTo diff --git a/Emby.Dlna/PlayTo/PlaybackStartEventArgs.cs b/Emby.Dlna/PlayTo/PlaybackStartEventArgs.cs index 8cd8b47acf..485f7ec103 100644 --- a/Emby.Dlna/PlayTo/PlaybackStartEventArgs.cs +++ b/Emby.Dlna/PlayTo/PlaybackStartEventArgs.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System; namespace Emby.Dlna.PlayTo diff --git a/Emby.Dlna/PlayTo/PlaybackStoppedEventArgs.cs b/Emby.Dlna/PlayTo/PlaybackStoppedEventArgs.cs index 2afdc324d9..2eddb125d4 100644 --- a/Emby.Dlna/PlayTo/PlaybackStoppedEventArgs.cs +++ b/Emby.Dlna/PlayTo/PlaybackStoppedEventArgs.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System; namespace Emby.Dlna.PlayTo diff --git a/Emby.Dlna/PlayTo/PlaylistItem.cs b/Emby.Dlna/PlayTo/PlaylistItem.cs index 1e62b61e93..42d73c38ca 100644 --- a/Emby.Dlna/PlayTo/PlaylistItem.cs +++ b/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 diff --git a/Emby.Dlna/PlayTo/PlaylistItemFactory.cs b/Emby.Dlna/PlayTo/PlaylistItemFactory.cs index 446d8e1e6e..8e279da6fc 100644 --- a/Emby.Dlna/PlayTo/PlaylistItemFactory.cs +++ b/Emby.Dlna/PlayTo/PlaylistItemFactory.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System.Globalization; using System.IO; using System.Linq; diff --git a/Emby.Dlna/PlayTo/SsdpHttpClient.cs b/Emby.Dlna/PlayTo/SsdpHttpClient.cs index 66c634150a..757e713e1c 100644 --- a/Emby.Dlna/PlayTo/SsdpHttpClient.cs +++ b/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 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)) { diff --git a/Emby.Dlna/PlayTo/TRANSPORTSTATE.cs b/Emby.Dlna/PlayTo/TRANSPORTSTATE.cs index 9f1690b04f..b312c8b6ec 100644 --- a/Emby.Dlna/PlayTo/TRANSPORTSTATE.cs +++ b/Emby.Dlna/PlayTo/TRANSPORTSTATE.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + namespace Emby.Dlna.PlayTo { public enum TRANSPORTSTATE diff --git a/Emby.Dlna/PlayTo/TransportCommands.cs b/Emby.Dlna/PlayTo/TransportCommands.cs index 4f9e398e90..a00d154f78 100644 --- a/Emby.Dlna/PlayTo/TransportCommands.cs +++ b/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; diff --git a/Emby.Dlna/PlayTo/UpnpContainer.cs b/Emby.Dlna/PlayTo/UpnpContainer.cs index 943e0347b2..9700d8a5d1 100644 --- a/Emby.Dlna/PlayTo/UpnpContainer.cs +++ b/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; diff --git a/Emby.Dlna/PlayTo/uBaseObject.cs b/Emby.Dlna/PlayTo/uBaseObject.cs index f29a126df7..6e2e31dc47 100644 --- a/Emby.Dlna/PlayTo/uBaseObject.cs +++ b/Emby.Dlna/PlayTo/uBaseObject.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System; namespace Emby.Dlna.PlayTo diff --git a/Emby.Dlna/PlayTo/uPnpNamespaces.cs b/Emby.Dlna/PlayTo/uPnpNamespaces.cs index 7132ecd15c..fc0f0f704f 100644 --- a/Emby.Dlna/PlayTo/uPnpNamespaces.cs +++ b/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 diff --git a/Emby.Dlna/Profiles/DefaultProfile.cs b/Emby.Dlna/Profiles/DefaultProfile.cs index ea50bd4a75..97286e3472 100644 --- a/Emby.Dlna/Profiles/DefaultProfile.cs +++ b/Emby.Dlna/Profiles/DefaultProfile.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System.Linq; using MediaBrowser.Model.Dlna; diff --git a/Emby.Dlna/Profiles/DenonAvrProfile.cs b/Emby.Dlna/Profiles/DenonAvrProfile.cs index a738851915..3be9805288 100644 --- a/Emby.Dlna/Profiles/DenonAvrProfile.cs +++ b/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 diff --git a/Emby.Dlna/Profiles/DirectTvProfile.cs b/Emby.Dlna/Profiles/DirectTvProfile.cs index 317c0976a2..33bcae6044 100644 --- a/Emby.Dlna/Profiles/DirectTvProfile.cs +++ b/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 diff --git a/Emby.Dlna/Profiles/DishHopperJoeyProfile.cs b/Emby.Dlna/Profiles/DishHopperJoeyProfile.cs index 8d8ab41cad..26654b803c 100644 --- a/Emby.Dlna/Profiles/DishHopperJoeyProfile.cs +++ b/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 diff --git a/Emby.Dlna/Profiles/Foobar2000Profile.cs b/Emby.Dlna/Profiles/Foobar2000Profile.cs index 947194bce4..c1aece8c8a 100644 --- a/Emby.Dlna/Profiles/Foobar2000Profile.cs +++ b/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 diff --git a/Emby.Dlna/Profiles/LgTvProfile.cs b/Emby.Dlna/Profiles/LgTvProfile.cs index 145685ab12..63b5b6f311 100644 --- a/Emby.Dlna/Profiles/LgTvProfile.cs +++ b/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 diff --git a/Emby.Dlna/Profiles/LinksysDMA2100Profile.cs b/Emby.Dlna/Profiles/LinksysDMA2100Profile.cs index 3f0bb4263d..3a9744e381 100644 --- a/Emby.Dlna/Profiles/LinksysDMA2100Profile.cs +++ b/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 diff --git a/Emby.Dlna/Profiles/MarantzProfile.cs b/Emby.Dlna/Profiles/MarantzProfile.cs index 162e284be6..05f94a2063 100644 --- a/Emby.Dlna/Profiles/MarantzProfile.cs +++ b/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 diff --git a/Emby.Dlna/Profiles/MediaMonkeyProfile.cs b/Emby.Dlna/Profiles/MediaMonkeyProfile.cs index 53cae4e2ff..10218fa563 100644 --- a/Emby.Dlna/Profiles/MediaMonkeyProfile.cs +++ b/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 diff --git a/Emby.Dlna/Profiles/PanasonicVieraProfile.cs b/Emby.Dlna/Profiles/PanasonicVieraProfile.cs index 5f31ec484e..945ec45187 100644 --- a/Emby.Dlna/Profiles/PanasonicVieraProfile.cs +++ b/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 diff --git a/Emby.Dlna/Profiles/PopcornHourProfile.cs b/Emby.Dlna/Profiles/PopcornHourProfile.cs index aefe8c44f3..3765d01dc4 100644 --- a/Emby.Dlna/Profiles/PopcornHourProfile.cs +++ b/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 diff --git a/Emby.Dlna/Profiles/SamsungSmartTvProfile.cs b/Emby.Dlna/Profiles/SamsungSmartTvProfile.cs index 51a1c81735..61c5f4dce9 100644 --- a/Emby.Dlna/Profiles/SamsungSmartTvProfile.cs +++ b/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 diff --git a/Emby.Dlna/Profiles/SharpSmartTvProfile.cs b/Emby.Dlna/Profiles/SharpSmartTvProfile.cs index f840cfb346..8967dc16a9 100644 --- a/Emby.Dlna/Profiles/SharpSmartTvProfile.cs +++ b/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 diff --git a/Emby.Dlna/Profiles/SonyBlurayPlayer2013.cs b/Emby.Dlna/Profiles/SonyBlurayPlayer2013.cs index 2af1d3b501..308d74aa84 100644 --- a/Emby.Dlna/Profiles/SonyBlurayPlayer2013.cs +++ b/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 diff --git a/Emby.Dlna/Profiles/SonyBlurayPlayer2014.cs b/Emby.Dlna/Profiles/SonyBlurayPlayer2014.cs index 3de0b51922..496c243160 100644 --- a/Emby.Dlna/Profiles/SonyBlurayPlayer2014.cs +++ b/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 diff --git a/Emby.Dlna/Profiles/SonyBlurayPlayer2015.cs b/Emby.Dlna/Profiles/SonyBlurayPlayer2015.cs index 889484bea5..987a9af4b1 100644 --- a/Emby.Dlna/Profiles/SonyBlurayPlayer2015.cs +++ b/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 diff --git a/Emby.Dlna/Profiles/SonyBlurayPlayer2016.cs b/Emby.Dlna/Profiles/SonyBlurayPlayer2016.cs index acb90bd01b..560193dedd 100644 --- a/Emby.Dlna/Profiles/SonyBlurayPlayer2016.cs +++ b/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 diff --git a/Emby.Dlna/Profiles/SonyBlurayPlayerProfile.cs b/Emby.Dlna/Profiles/SonyBlurayPlayerProfile.cs index e1808b2052..c983d98baf 100644 --- a/Emby.Dlna/Profiles/SonyBlurayPlayerProfile.cs +++ b/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 diff --git a/Emby.Dlna/Profiles/SonyBravia2010Profile.cs b/Emby.Dlna/Profiles/SonyBravia2010Profile.cs index f8e8faa76d..186c894736 100644 --- a/Emby.Dlna/Profiles/SonyBravia2010Profile.cs +++ b/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 diff --git a/Emby.Dlna/Profiles/SonyBravia2011Profile.cs b/Emby.Dlna/Profiles/SonyBravia2011Profile.cs index 111f36e9b5..a29d143f68 100644 --- a/Emby.Dlna/Profiles/SonyBravia2011Profile.cs +++ b/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 diff --git a/Emby.Dlna/Profiles/SonyBravia2012Profile.cs b/Emby.Dlna/Profiles/SonyBravia2012Profile.cs index d5efe42707..9bcdd21b8f 100644 --- a/Emby.Dlna/Profiles/SonyBravia2012Profile.cs +++ b/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 diff --git a/Emby.Dlna/Profiles/SonyBravia2013Profile.cs b/Emby.Dlna/Profiles/SonyBravia2013Profile.cs index 3b0228694b..900e4ff06e 100644 --- a/Emby.Dlna/Profiles/SonyBravia2013Profile.cs +++ b/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 diff --git a/Emby.Dlna/Profiles/SonyBravia2014Profile.cs b/Emby.Dlna/Profiles/SonyBravia2014Profile.cs index e860eae346..963e7993e1 100644 --- a/Emby.Dlna/Profiles/SonyBravia2014Profile.cs +++ b/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 diff --git a/Emby.Dlna/Profiles/SonyPs3Profile.cs b/Emby.Dlna/Profiles/SonyPs3Profile.cs index 88d0646955..31a764d8d3 100644 --- a/Emby.Dlna/Profiles/SonyPs3Profile.cs +++ b/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 diff --git a/Emby.Dlna/Profiles/SonyPs4Profile.cs b/Emby.Dlna/Profiles/SonyPs4Profile.cs index 499cf88038..9376a564ba 100644 --- a/Emby.Dlna/Profiles/SonyPs4Profile.cs +++ b/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 diff --git a/Emby.Dlna/Profiles/WdtvLiveProfile.cs b/Emby.Dlna/Profiles/WdtvLiveProfile.cs index bf7b1ab47e..8e056792a6 100644 --- a/Emby.Dlna/Profiles/WdtvLiveProfile.cs +++ b/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 diff --git a/Emby.Dlna/Profiles/XboxOneProfile.cs b/Emby.Dlna/Profiles/XboxOneProfile.cs index 710b891e3d..364c433549 100644 --- a/Emby.Dlna/Profiles/XboxOneProfile.cs +++ b/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 diff --git a/Emby.Dlna/Server/DescriptionXmlBuilder.cs b/Emby.Dlna/Server/DescriptionXmlBuilder.cs index 03d8f80abb..df48246377 100644 --- a/Emby.Dlna/Server/DescriptionXmlBuilder.cs +++ b/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; diff --git a/Emby.Dlna/Service/BaseControlHandler.cs b/Emby.Dlna/Service/BaseControlHandler.cs index 49129f6ffd..0125c0528a 100644 --- a/Emby.Dlna/Service/BaseControlHandler.cs +++ b/Emby.Dlna/Service/BaseControlHandler.cs @@ -1,7 +1,9 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Text; using System.Threading.Tasks; using System.Xml; @@ -66,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, @@ -85,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(); @@ -219,7 +216,7 @@ namespace Emby.Dlna.Service public Dictionary Headers { get; } = new Dictionary(StringComparer.OrdinalIgnoreCase); } - protected abstract IEnumerable> GetResult(string methodName, IDictionary methodParams); + protected abstract void WriteResult(string methodName, IDictionary methodParams, XmlWriter xmlWriter); private void LogRequest(ControlRequest request) { diff --git a/Emby.Dlna/Service/BaseService.cs b/Emby.Dlna/Service/BaseService.cs index 5359e94c44..d7e5c541d9 100644 --- a/Emby.Dlna/Service/BaseService.cs +++ b/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; diff --git a/Emby.Dlna/Service/ControlErrorHandler.cs b/Emby.Dlna/Service/ControlErrorHandler.cs index bbf975f384..a2f5057fb0 100644 --- a/Emby.Dlna/Service/ControlErrorHandler.cs +++ b/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; diff --git a/Emby.Dlna/Service/ServiceXmlBuilder.cs b/Emby.Dlna/Service/ServiceXmlBuilder.cs index bd1f0bf05c..0787b8df94 100644 --- a/Emby.Dlna/Service/ServiceXmlBuilder.cs +++ b/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; diff --git a/Emby.Dlna/Ssdp/DeviceDiscovery.cs b/Emby.Dlna/Ssdp/DeviceDiscovery.cs index c5f3593da8..c5e57d0ff0 100644 --- a/Emby.Dlna/Ssdp/DeviceDiscovery.cs +++ b/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> DeviceDiscoveredInternal; - private int _listenerCount; - private object _syncLock = new object(); + private bool _disposed; + + private event EventHandler> DeviceDiscoveredInternal; /// public event EventHandler> 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); } + /// public void Dispose() { if (!_disposed) diff --git a/Emby.Dlna/Ssdp/Extensions.cs b/Emby.Dlna/Ssdp/Extensions.cs index c680c123ed..836d4abfd2 100644 --- a/Emby.Dlna/Ssdp/Extensions.cs +++ b/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 From dc62e436c43362f2193415f4c81280c6c1a8560c Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 22 Jan 2020 22:18:56 +0100 Subject: [PATCH 024/270] Clean up Emby.Naming --- Emby.Naming/Audio/AlbumParser.cs | 11 +- Emby.Naming/Audio/MultiPartResult.cs | 26 ---- Emby.Naming/AudioBook/AudioBookFileInfo.cs | 2 +- .../AudioBook/AudioBookListResolver.cs | 19 +-- Emby.Naming/Common/EpisodeExpression.cs | 36 ++--- Emby.Naming/Common/NamingOptions.cs | 83 +++++------ Emby.Naming/Emby.Naming.csproj | 5 +- Emby.Naming/TV/EpisodePathParser.cs | 5 +- Emby.Naming/TV/EpisodeResolver.cs | 20 +-- Emby.Naming/TV/SeasonPathParser.cs | 39 +++--- Emby.Naming/Video/StackResolver.cs | 21 +-- Emby.Naming/Video/StackResult.cs | 17 --- Emby.Naming/Video/StubResolver.cs | 20 ++- Emby.Naming/Video/VideoFileInfo.cs | 2 +- Emby.Naming/Video/VideoInfo.cs | 18 ++- Emby.Naming/Video/VideoListResolver.cs | 43 +++--- Emby.Naming/Video/VideoResolver.cs | 21 ++- .../Library/LibraryManager.cs | 2 +- .../Resolvers/Audio/MusicAlbumResolver.cs | 20 +-- .../Library/Resolvers/Movies/MovieResolver.cs | 73 +++++----- .../Library/Resolvers/TV/SeasonResolver.cs | 21 ++- .../Library/Resolvers/TV/SeriesResolver.cs | 2 +- Jellyfin.Server/Jellyfin.Server.csproj | 3 - .../Music/MultiDiscAlbumTests.cs | 2 +- .../TV/SeasonFolderTests.cs | 3 +- .../Jellyfin.Naming.Tests/Video/StackTests.cs | 130 +++++++++--------- .../Jellyfin.Naming.Tests/Video/StubTests.cs | 10 +- 27 files changed, 287 insertions(+), 367 deletions(-) delete mode 100644 Emby.Naming/Audio/MultiPartResult.cs delete mode 100644 Emby.Naming/Video/StackResult.cs diff --git a/Emby.Naming/Audio/AlbumParser.cs b/Emby.Naming/Audio/AlbumParser.cs index 4975b8e19d..b807816eba 100644 --- a/Emby.Naming/Audio/AlbumParser.cs +++ b/Emby.Naming/Audio/AlbumParser.cs @@ -19,15 +19,13 @@ namespace Emby.Naming.Audio _options = options; } - public MultiPartResult ParseMultiPart(string path) + public bool IsMultiPart(string path) { - var result = new MultiPartResult(); - var filename = Path.GetFileName(path); if (string.IsNullOrEmpty(filename)) { - return result; + return false; } // TODO: Move this logic into options object @@ -57,12 +55,11 @@ namespace Emby.Naming.Audio if (int.TryParse(tmp, NumberStyles.Integer, CultureInfo.InvariantCulture, out _)) { - result.IsMultiPart = true; - break; + return true; } } - return result; + return false; } } } diff --git a/Emby.Naming/Audio/MultiPartResult.cs b/Emby.Naming/Audio/MultiPartResult.cs deleted file mode 100644 index 8f68d97fa8..0000000000 --- a/Emby.Naming/Audio/MultiPartResult.cs +++ /dev/null @@ -1,26 +0,0 @@ -#pragma warning disable CS1591 -#pragma warning disable SA1600 - -namespace Emby.Naming.Audio -{ - public class MultiPartResult - { - /// - /// Gets or sets the name. - /// - /// The name. - public string Name { get; set; } - - /// - /// Gets or sets the part. - /// - /// The part. - public string Part { get; set; } - - /// - /// Gets or sets a value indicating whether this instance is multi part. - /// - /// true if this instance is multi part; otherwise, false. - public bool IsMultiPart { get; set; } - } -} diff --git a/Emby.Naming/AudioBook/AudioBookFileInfo.cs b/Emby.Naming/AudioBook/AudioBookFileInfo.cs index 769e3d7fac..0bc6ec7e40 100644 --- a/Emby.Naming/AudioBook/AudioBookFileInfo.cs +++ b/Emby.Naming/AudioBook/AudioBookFileInfo.cs @@ -32,7 +32,7 @@ namespace Emby.Naming.AudioBook public int? ChapterNumber { get; set; } /// - /// Gets or sets the type. + /// Gets or sets a value indicating whether this instance is a directory. /// /// The type. public bool IsDirectory { get; set; } diff --git a/Emby.Naming/AudioBook/AudioBookListResolver.cs b/Emby.Naming/AudioBook/AudioBookListResolver.cs index 97f3592857..835e83a081 100644 --- a/Emby.Naming/AudioBook/AudioBookListResolver.cs +++ b/Emby.Naming/AudioBook/AudioBookListResolver.cs @@ -39,9 +39,7 @@ namespace Emby.Naming.AudioBook var stackResult = new StackResolver(_options) .ResolveAudioBooks(metadata); - var list = new List(); - - foreach (var stack in stackResult.Stacks) + foreach (var stack in stackResult) { var stackFiles = stack.Files.Select(i => audioBookResolver.Resolve(i, stack.IsDirectoryStack)).ToList(); stackFiles.Sort(); @@ -50,20 +48,9 @@ namespace Emby.Naming.AudioBook Files = stackFiles, Name = stack.Name }; - list.Add(info); + + yield return info; } - - // Whatever files are left, just add them - /*list.AddRange(remainingFiles.Select(i => new AudioBookInfo - { - Files = new List { i }, - Name = i., - Year = i.Year - }));*/ - - var orderedList = list.OrderBy(i => i.Name); - - return orderedList; } } } diff --git a/Emby.Naming/Common/EpisodeExpression.cs b/Emby.Naming/Common/EpisodeExpression.cs index 30a74fb657..f60f7e84b1 100644 --- a/Emby.Naming/Common/EpisodeExpression.cs +++ b/Emby.Naming/Common/EpisodeExpression.cs @@ -11,6 +11,24 @@ namespace Emby.Naming.Common private string _expression; private Regex _regex; + public EpisodeExpression(string expression, bool byDate) + { + Expression = expression; + IsByDate = byDate; + DateTimeFormats = Array.Empty(); + SupportsAbsoluteEpisodeNumbers = true; + } + + public EpisodeExpression(string expression) + : this(expression, false) + { + } + + public EpisodeExpression() + : this(null) + { + } + public string Expression { get => _expression; @@ -32,23 +50,5 @@ namespace Emby.Naming.Common public string[] DateTimeFormats { get; set; } public Regex Regex => _regex ?? (_regex = new Regex(Expression, RegexOptions.IgnoreCase | RegexOptions.Compiled)); - - public EpisodeExpression(string expression, bool byDate) - { - Expression = expression; - IsByDate = byDate; - DateTimeFormats = Array.Empty(); - SupportsAbsoluteEpisodeNumbers = true; - } - - public EpisodeExpression(string expression) - : this(expression, false) - { - } - - public EpisodeExpression() - : this(null) - { - } } } diff --git a/Emby.Naming/Common/NamingOptions.cs b/Emby.Naming/Common/NamingOptions.cs index 9ce503b8e4..1554e61d83 100644 --- a/Emby.Naming/Common/NamingOptions.cs +++ b/Emby.Naming/Common/NamingOptions.cs @@ -11,46 +11,6 @@ namespace Emby.Naming.Common { public class NamingOptions { - public string[] AudioFileExtensions { get; set; } - - public string[] AlbumStackingPrefixes { get; set; } - - public string[] SubtitleFileExtensions { get; set; } - - public char[] SubtitleFlagDelimiters { get; set; } - - public string[] SubtitleForcedFlags { get; set; } - - public string[] SubtitleDefaultFlags { get; set; } - - public EpisodeExpression[] EpisodeExpressions { get; set; } - - public string[] EpisodeWithoutSeasonExpressions { get; set; } - - public string[] EpisodeMultiPartExpressions { get; set; } - - public string[] VideoFileExtensions { get; set; } - - public string[] StubFileExtensions { get; set; } - - public string[] AudioBookPartsExpressions { get; set; } - - public StubTypeRule[] StubTypes { get; set; } - - public char[] VideoFlagDelimiters { get; set; } - - public Format3DRule[] Format3DRules { get; set; } - - public string[] VideoFileStackingExpressions { get; set; } - - public string[] CleanDateTimes { get; set; } - - public string[] CleanStrings { get; set; } - - public EpisodeExpression[] MultipleEpisodeExpressions { get; set; } - - public ExtraRule[] VideoExtraRules { get; set; } - public NamingOptions() { VideoFileExtensions = new[] @@ -681,11 +641,54 @@ namespace Emby.Naming.Common Compile(); } + public string[] AudioFileExtensions { get; set; } + + public string[] AlbumStackingPrefixes { get; set; } + + public string[] SubtitleFileExtensions { get; set; } + + public char[] SubtitleFlagDelimiters { get; set; } + + public string[] SubtitleForcedFlags { get; set; } + + public string[] SubtitleDefaultFlags { get; set; } + + public EpisodeExpression[] EpisodeExpressions { get; set; } + + public string[] EpisodeWithoutSeasonExpressions { get; set; } + + public string[] EpisodeMultiPartExpressions { get; set; } + + public string[] VideoFileExtensions { get; set; } + + public string[] StubFileExtensions { get; set; } + + public string[] AudioBookPartsExpressions { get; set; } + + public StubTypeRule[] StubTypes { get; set; } + + public char[] VideoFlagDelimiters { get; set; } + + public Format3DRule[] Format3DRules { get; set; } + + public string[] VideoFileStackingExpressions { get; set; } + + public string[] CleanDateTimes { get; set; } + + public string[] CleanStrings { get; set; } + + public EpisodeExpression[] MultipleEpisodeExpressions { get; set; } + + public ExtraRule[] VideoExtraRules { get; set; } + public Regex[] VideoFileStackingRegexes { get; private set; } + public Regex[] CleanDateTimeRegexes { get; private set; } + public Regex[] CleanStringRegexes { get; private set; } public Regex[] EpisodeWithoutSeasonRegexes { get; private set; } + public Regex[] EpisodeMultiPartRegexes { get; private set; } public void Compile() diff --git a/Emby.Naming/Emby.Naming.csproj b/Emby.Naming/Emby.Naming.csproj index 900b9694c2..4e08170a47 100644 --- a/Emby.Naming/Emby.Naming.csproj +++ b/Emby.Naming/Emby.Naming.csproj @@ -4,9 +4,6 @@ netstandard2.1 false true - - - true @@ -27,7 +24,7 @@ - + diff --git a/Emby.Naming/TV/EpisodePathParser.cs b/Emby.Naming/TV/EpisodePathParser.cs index 6b557d2e19..b97b3137bc 100644 --- a/Emby.Naming/TV/EpisodePathParser.cs +++ b/Emby.Naming/TV/EpisodePathParser.cs @@ -1,5 +1,6 @@ #pragma warning disable CS1591 #pragma warning disable SA1600 +#nullable enable using System; using System.Collections.Generic; @@ -28,7 +29,7 @@ namespace Emby.Naming.TV path += ".mp4"; } - EpisodePathParserResult result = null; + EpisodePathParserResult? result = null; foreach (var expression in _options.EpisodeExpressions) { @@ -136,7 +137,7 @@ namespace Emby.Naming.TV // It avoids erroneous parsing of something like "series-s09e14-1080p.mkv" as a multi-episode from E14 to E108 int nextIndex = endingNumberGroup.Index + endingNumberGroup.Length; if (nextIndex >= name.Length - || "0123456789iIpP".IndexOf(name[nextIndex]) == -1) + || !"0123456789iIpP".Contains(name[nextIndex], StringComparison.Ordinal)) { if (int.TryParse(endingNumberGroup.Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out num)) { diff --git a/Emby.Naming/TV/EpisodeResolver.cs b/Emby.Naming/TV/EpisodeResolver.cs index 5e115fc75d..57659ee131 100644 --- a/Emby.Naming/TV/EpisodeResolver.cs +++ b/Emby.Naming/TV/EpisodeResolver.cs @@ -1,5 +1,6 @@ #pragma warning disable CS1591 #pragma warning disable SA1600 +#nullable enable using System; using System.IO; @@ -18,7 +19,7 @@ namespace Emby.Naming.TV _options = options; } - public EpisodeInfo Resolve( + public EpisodeInfo? Resolve( string path, bool isDirectory, bool? isNamed = null, @@ -26,14 +27,9 @@ namespace Emby.Naming.TV bool? supportsAbsoluteNumbers = null, bool fillExtendedInfo = true) { - if (string.IsNullOrEmpty(path)) - { - throw new ArgumentNullException(nameof(path)); - } - bool isStub = false; - string container = null; - string stubType = null; + string? container = null; + string? stubType = null; if (!isDirectory) { @@ -41,17 +37,13 @@ namespace Emby.Naming.TV // Check supported extensions if (!_options.VideoFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)) { - var stubResult = StubResolver.ResolveFile(path, _options); - - isStub = stubResult.IsStub; - // It's not supported. Check stub extensions - if (!isStub) + if (!StubResolver.TryResolveFile(path, _options, out stubType)) { return null; } - stubType = stubResult.StubType; + isStub = true; } container = extension.TrimStart('.'); diff --git a/Emby.Naming/TV/SeasonPathParser.cs b/Emby.Naming/TV/SeasonPathParser.cs index e5f90e9660..7715a16a45 100644 --- a/Emby.Naming/TV/SeasonPathParser.cs +++ b/Emby.Naming/TV/SeasonPathParser.cs @@ -8,9 +8,24 @@ using System.Linq; namespace Emby.Naming.TV { - public class SeasonPathParser + public static class SeasonPathParser { - public SeasonPathParserResult Parse(string path, bool supportSpecialAliases, bool supportNumericSeasonFolders) + /// + /// A season folder must contain one of these somewhere in the name. + /// + private static readonly string[] _seasonFolderNames = + { + "season", + "sæson", + "temporada", + "saison", + "staffel", + "series", + "сезон", + "stagione" + }; + + public static SeasonPathParserResult Parse(string path, bool supportSpecialAliases, bool supportNumericSeasonFolders) { var result = new SeasonPathParserResult(); @@ -27,21 +42,6 @@ namespace Emby.Naming.TV return result; } - /// - /// A season folder must contain one of these somewhere in the name. - /// - private static readonly string[] _seasonFolderNames = - { - "season", - "sæson", - "temporada", - "saison", - "staffel", - "series", - "сезон", - "stagione" - }; - /// /// Gets the season number from path. /// @@ -150,6 +150,7 @@ namespace Emby.Naming.TV { numericStart = i; } + length++; } } @@ -161,11 +162,11 @@ namespace Emby.Naming.TV } var currentChar = path[i]; - if (currentChar.Equals('(')) + if (currentChar == '(') { hasOpenParenth = true; } - else if (currentChar.Equals(')')) + else if (currentChar == ')') { hasOpenParenth = false; } diff --git a/Emby.Naming/Video/StackResolver.cs b/Emby.Naming/Video/StackResolver.cs index e7a769ae6b..8f210fa453 100644 --- a/Emby.Naming/Video/StackResolver.cs +++ b/Emby.Naming/Video/StackResolver.cs @@ -20,7 +20,7 @@ namespace Emby.Naming.Video _options = options; } - public StackResult ResolveDirectories(IEnumerable files) + public IEnumerable ResolveDirectories(IEnumerable files) { return Resolve(files.Select(i => new FileSystemMetadata { @@ -29,7 +29,7 @@ namespace Emby.Naming.Video })); } - public StackResult ResolveFiles(IEnumerable files) + public IEnumerable ResolveFiles(IEnumerable files) { return Resolve(files.Select(i => new FileSystemMetadata { @@ -38,9 +38,8 @@ namespace Emby.Naming.Video })); } - public StackResult ResolveAudioBooks(IEnumerable files) + public IEnumerable ResolveAudioBooks(IEnumerable files) { - var result = new StackResult(); foreach (var directory in files.GroupBy(file => file.IsDirectory ? file.FullName : Path.GetDirectoryName(file.FullName))) { var stack = new FileStack() @@ -58,20 +57,16 @@ namespace Emby.Naming.Video stack.Files.Add(file.FullName); } - result.Stacks.Add(stack); + yield return stack; } - - return result; } - public StackResult Resolve(IEnumerable files) + public IEnumerable Resolve(IEnumerable files) { - var result = new StackResult(); - var resolver = new VideoResolver(_options); var list = files - .Where(i => i.IsDirectory || (resolver.IsVideoFile(i.FullName) || resolver.IsStubFile(i.FullName))) + .Where(i => i.IsDirectory || resolver.IsVideoFile(i.FullName) || resolver.IsStubFile(i.FullName)) .OrderBy(i => i.FullName) .ToList(); @@ -191,14 +186,12 @@ namespace Emby.Naming.Video if (stack.Files.Count > 1) { - result.Stacks.Add(stack); + yield return stack; i += stack.Files.Count - 1; break; } } } - - return result; } private string GetRegexInput(FileSystemMetadata file) diff --git a/Emby.Naming/Video/StackResult.cs b/Emby.Naming/Video/StackResult.cs deleted file mode 100644 index 31ef2d69c5..0000000000 --- a/Emby.Naming/Video/StackResult.cs +++ /dev/null @@ -1,17 +0,0 @@ -#pragma warning disable CS1591 -#pragma warning disable SA1600 - -using System.Collections.Generic; - -namespace Emby.Naming.Video -{ - public class StackResult - { - public List Stacks { get; set; } - - public StackResult() - { - Stacks = new List(); - } - } -} diff --git a/Emby.Naming/Video/StubResolver.cs b/Emby.Naming/Video/StubResolver.cs index 95868e89d2..4024d6d593 100644 --- a/Emby.Naming/Video/StubResolver.cs +++ b/Emby.Naming/Video/StubResolver.cs @@ -1,5 +1,6 @@ #pragma warning disable CS1591 #pragma warning disable SA1600 +#nullable enable using System; using System.IO; @@ -10,25 +11,22 @@ namespace Emby.Naming.Video { public static class StubResolver { - public static StubResult ResolveFile(string path, NamingOptions options) + public static bool TryResolveFile(string path, NamingOptions options, out string? stubType) { + stubType = default; + if (path == null) { - return default; + return false; } var extension = Path.GetExtension(path); if (!options.StubFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)) { - return default; + return false; } - var result = new StubResult() - { - IsStub = true - }; - path = Path.GetFileNameWithoutExtension(path); var token = Path.GetExtension(path).TrimStart('.'); @@ -36,12 +34,12 @@ namespace Emby.Naming.Video { if (string.Equals(rule.Token, token, StringComparison.OrdinalIgnoreCase)) { - result.StubType = rule.StubType; - break; + stubType = rule.StubType; + return true; } } - return result; + return true; } } } diff --git a/Emby.Naming/Video/VideoFileInfo.cs b/Emby.Naming/Video/VideoFileInfo.cs index 90c798da10..aa4f3a35c3 100644 --- a/Emby.Naming/Video/VideoFileInfo.cs +++ b/Emby.Naming/Video/VideoFileInfo.cs @@ -68,7 +68,7 @@ namespace Emby.Naming.Video public string StubType { get; set; } /// - /// Gets or sets the type. + /// Gets or sets a value indicating whether this instance is a directory. /// /// The type. public bool IsDirectory { get; set; } diff --git a/Emby.Naming/Video/VideoInfo.cs b/Emby.Naming/Video/VideoInfo.cs index a585bb99a2..ea74c40e2a 100644 --- a/Emby.Naming/Video/VideoInfo.cs +++ b/Emby.Naming/Video/VideoInfo.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; namespace Emby.Naming.Video @@ -10,11 +11,14 @@ namespace Emby.Naming.Video /// /// Initializes a new instance of the class. /// - public VideoInfo() + /// The name. + public VideoInfo(string name) { - Files = new List(); - Extras = new List(); - AlternateVersions = new List(); + Name = name; + + Files = Array.Empty(); + Extras = Array.Empty(); + AlternateVersions = Array.Empty(); } /// @@ -33,18 +37,18 @@ namespace Emby.Naming.Video /// Gets or sets the files. /// /// The files. - public List Files { get; set; } + public IReadOnlyList Files { get; set; } /// /// Gets or sets the extras. /// /// The extras. - public List Extras { get; set; } + public IReadOnlyList Extras { get; set; } /// /// Gets or sets the alternate versions. /// /// The alternate versions. - public List AlternateVersions { get; set; } + public IReadOnlyList AlternateVersions { get; set; } } } diff --git a/Emby.Naming/Video/VideoListResolver.cs b/Emby.Naming/Video/VideoListResolver.cs index 87498000ce..1366583534 100644 --- a/Emby.Naming/Video/VideoListResolver.cs +++ b/Emby.Naming/Video/VideoListResolver.cs @@ -41,20 +41,19 @@ namespace Emby.Naming.Video }); var stackResult = new StackResolver(_options) - .Resolve(nonExtras); + .Resolve(nonExtras).ToList(); var remainingFiles = videoInfos - .Where(i => !stackResult.Stacks.Any(s => s.ContainsFile(i.Path, i.IsDirectory))) + .Where(i => !stackResult.Any(s => s.ContainsFile(i.Path, i.IsDirectory))) .ToList(); var list = new List(); - foreach (var stack in stackResult.Stacks) + foreach (var stack in stackResult) { - var info = new VideoInfo + var info = new VideoInfo(stack.Name) { - Files = stack.Files.Select(i => videoResolver.Resolve(i, stack.IsDirectoryStack)).ToList(), - Name = stack.Name + Files = stack.Files.Select(i => videoResolver.Resolve(i, stack.IsDirectoryStack)).ToList() }; info.Year = info.Files[0].Year; @@ -85,10 +84,9 @@ namespace Emby.Naming.Video foreach (var media in standaloneMedia) { - var info = new VideoInfo + var info = new VideoInfo(media.Name) { - Files = new List { media }, - Name = media.Name + Files = new List { media } }; info.Year = info.Files[0].Year; @@ -128,7 +126,8 @@ namespace Emby.Naming.Video .Except(extras) .ToList(); - info.Extras.AddRange(extras); + extras.AddRange(info.Extras); + info.Extras = extras; } } @@ -141,7 +140,8 @@ namespace Emby.Naming.Video .Except(extrasByFileName) .ToList(); - info.Extras.AddRange(extrasByFileName); + extrasByFileName.AddRange(info.Extras); + info.Extras = extrasByFileName; } // If there's only one video, accept all trailers @@ -152,7 +152,8 @@ namespace Emby.Naming.Video .Where(i => i.ExtraType == ExtraType.Trailer) .ToList(); - list[0].Extras.AddRange(trailers); + trailers.AddRange(list[0].Extras); + list[0].Extras = trailers; remainingFiles = remainingFiles .Except(trailers) @@ -160,14 +161,13 @@ namespace Emby.Naming.Video } // Whatever files are left, just add them - list.AddRange(remainingFiles.Select(i => new VideoInfo + list.AddRange(remainingFiles.Select(i => new VideoInfo(i.Name) { Files = new List { i }, - Name = i.Name, Year = i.Year })); - return list.OrderBy(i => i.Name); + return list; } private IEnumerable GetVideosGroupedByVersion(List videos) @@ -191,9 +191,18 @@ namespace Emby.Naming.Video list.Add(ordered[0]); - list[0].AlternateVersions = ordered.Skip(1).Select(i => i.Files[0]).ToList(); + var alternateVersionsLen = ordered.Count - 1; + var alternateVersions = new VideoFileInfo[alternateVersionsLen]; + for (int i = 0; i < alternateVersionsLen; i++) + { + alternateVersions[i] = ordered[i + 1].Files[0]; + } + + list[0].AlternateVersions = alternateVersions; list[0].Name = folderName; - list[0].Extras.AddRange(ordered.Skip(1).SelectMany(i => i.Extras)); + var extras = ordered.Skip(1).SelectMany(i => i.Extras).ToList(); + extras.AddRange(list[0].Extras); + list[0].Extras = extras; return list; } diff --git a/Emby.Naming/Video/VideoResolver.cs b/Emby.Naming/Video/VideoResolver.cs index f93db24866..699bbe40a1 100644 --- a/Emby.Naming/Video/VideoResolver.cs +++ b/Emby.Naming/Video/VideoResolver.cs @@ -1,5 +1,6 @@ #pragma warning disable CS1591 #pragma warning disable SA1600 +#nullable enable using System; using System.IO; @@ -22,7 +23,7 @@ namespace Emby.Naming.Video /// /// The path. /// VideoFileInfo. - public VideoFileInfo ResolveDirectory(string path) + public VideoFileInfo? ResolveDirectory(string path) { return Resolve(path, true); } @@ -32,7 +33,7 @@ namespace Emby.Naming.Video /// /// The path. /// VideoFileInfo. - public VideoFileInfo ResolveFile(string path) + public VideoFileInfo? ResolveFile(string path) { return Resolve(path, false); } @@ -42,10 +43,10 @@ namespace Emby.Naming.Video /// /// The path. /// if set to true [is folder]. - /// Whether or not the name should be parsed for info + /// Whether or not the name should be parsed for info. /// VideoFileInfo. /// path is null. - public VideoFileInfo Resolve(string path, bool isDirectory, bool parseName = true) + public VideoFileInfo? Resolve(string path, bool isDirectory, bool parseName = true) { if (string.IsNullOrEmpty(path)) { @@ -53,8 +54,8 @@ namespace Emby.Naming.Video } bool isStub = false; - string container = null; - string stubType = null; + string? container = null; + string? stubType = null; if (!isDirectory) { @@ -63,17 +64,13 @@ namespace Emby.Naming.Video // Check supported extensions if (!_options.VideoFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)) { - var stubResult = StubResolver.ResolveFile(path, _options); - - isStub = stubResult.IsStub; - // It's not supported. Check stub extensions - if (!isStub) + if (!StubResolver.TryResolveFile(path, _options, out stubType)) { return null; } - stubType = stubResult.StubType; + isStub = true; } container = extension.TrimStart('.'); diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 6fb6235541..5c04631da9 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -2384,7 +2384,7 @@ namespace Emby.Server.Implementations.Library public int? GetSeasonNumberFromPath(string path) { - return new SeasonPathParser().Parse(path, true, true).SeasonNumber; + return SeasonPathParser.Parse(path, true, true).SeasonNumber; } public bool FillMissingEpisodeNumbersFromPath(Episode episode, bool forceRefresh) diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs index 4a2d210d5d..9f858f98dd 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs @@ -76,7 +76,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio } /// - /// Determine if the supplied file data points to a music album + /// Determine if the supplied file data points to a music album. /// public bool IsMusicAlbum(string path, IDirectoryService directoryService, LibraryOptions libraryOptions) { @@ -84,7 +84,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio } /// - /// Determine if the supplied resolve args should be considered a music album + /// Determine if the supplied resolve args should be considered a music album. /// /// The args. /// true if [is music album] [the specified args]; otherwise, false. @@ -104,7 +104,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio } /// - /// Determine if the supplied list contains what we should consider music + /// Determine if the supplied list contains what we should consider music. /// private bool ContainsMusic( IEnumerable list, @@ -118,6 +118,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio var discSubfolderCount = 0; var notMultiDisc = false; + var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions(); + var parser = new AlbumParser(namingOptions); foreach (var fileSystemInfo in list) { if (fileSystemInfo.IsDirectory) @@ -134,7 +136,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio if (hasMusic) { - if (IsMultiDiscFolder(path, libraryOptions)) + if (parser.IsMultiPart(path)) { logger.LogDebug("Found multi-disc folder: " + path); discSubfolderCount++; @@ -165,15 +167,5 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio return discSubfolderCount > 0; } - - private bool IsMultiDiscFolder(string path, LibraryOptions libraryOptions) - { - var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions(); - - var parser = new AlbumParser(namingOptions); - var result = parser.ParseMultiPart(path); - - return result.IsMultiPart; - } } } diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs index 6c76900557..08db168bc3 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs @@ -21,6 +21,28 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies /// public class MovieResolver : BaseVideoResolver