From da9a59de1e0837d2a4b030d59fa8d009b4457439 Mon Sep 17 00:00:00 2001 From: Steve Hayles Date: Thu, 31 Oct 2019 18:48:34 +0000 Subject: [PATCH 001/144] 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/144] 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/144] 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/144] 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/144] 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/144] 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/144] 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/144] 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/144] 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/144] 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/144] 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/144] 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/144] 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/144] 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/144] 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/144] 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/144] 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/144] 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 0cbae4a06d49acccfd7a757039f7f6725cdc53a5 Mon Sep 17 00:00:00 2001 From: artiume Date: Thu, 16 Jan 2020 12:51:34 -0500 Subject: [PATCH 019/144] 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 a012a4574fa1940ca3067aebd5c5654ff5f45cef Mon Sep 17 00:00:00 2001 From: artiume Date: Tue, 28 Jan 2020 21:36:51 -0500 Subject: [PATCH 020/144] remove test patch --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 3045ada713..342c764146 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -2737,8 +2737,6 @@ 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 From 91707f13a877dd4e8b70d0857f0343215be76758 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Tue, 4 Feb 2020 12:29:14 +0100 Subject: [PATCH 021/144] Add endpoints back --- .../Api/NotificationsService.cs | 105 +++++++++++++++++- jellyfin.ruleset | 2 + 2 files changed, 105 insertions(+), 2 deletions(-) diff --git a/Emby.Notifications/Api/NotificationsService.cs b/Emby.Notifications/Api/NotificationsService.cs index 5591809712..f9e923d10b 100644 --- a/Emby.Notifications/Api/NotificationsService.cs +++ b/Emby.Notifications/Api/NotificationsService.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -17,6 +18,62 @@ 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 IReadOnlyList 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> { @@ -46,6 +103,26 @@ namespace Emby.Notifications.Api 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 { @@ -58,18 +135,26 @@ namespace Emby.Notifications.Api _userManager = userManager; } + [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request")] public object Get(GetNotificationTypes request) { - _ = request; // Silence unused variable warning return _notificationManager.GetNotificationTypes(); } + [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request")] public object Get(GetNotificationServices request) { - _ = request; // Silence unused variable warning return _notificationManager.GetNotificationServices().ToList(); } + [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request")] + 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 @@ -85,5 +170,21 @@ namespace Emby.Notifications.Api return _notificationManager.SendNotification(notification, CancellationToken.None); } + + [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request")] + public void Post(MarkRead request) + { + } + + [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request")] + public void Post(MarkUnread request) + { + } + + [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request")] + public object Get(GetNotifications request) + { + return new NotificationResult(); + } } } diff --git a/jellyfin.ruleset b/jellyfin.ruleset index 92b7a03fdd..e4d2831677 100644 --- a/jellyfin.ruleset +++ b/jellyfin.ruleset @@ -5,6 +5,8 @@ + + From 7060934792d463840a616499ab869d4285582608 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Thu, 6 Feb 2020 15:20:23 +0100 Subject: [PATCH 022/144] Fix some warnings in Emby.Server.Implementations --- .../EntryPoints/RecordingNotifier.cs | 29 ++++++------ .../EntryPoints/RefreshUsersMetadata.cs | 9 +--- .../EntryPoints/StartupWizard.cs | 15 ++---- .../EntryPoints/UdpServerEntryPoint.cs | 6 +-- .../EntryPoints/UserDataChangeNotifier.cs | 46 ++++++++++--------- .../HttpClientManager/HttpClientManager.cs | 2 +- .../HttpServer/HttpListenerHost.cs | 27 ++++++----- .../IO/ExtendedFileSystemInfo.cs | 2 + .../IO/FileRefresher.cs | 42 +++++++++-------- .../LiveTv/EmbyTV/DirectRecorder.cs | 3 ++ .../LiveTv/EmbyTV/EmbyTV.cs | 1 - .../LiveTv/EmbyTV/EncodedRecorder.cs | 3 ++ .../LiveTv/EmbyTV/EntryPoint.cs | 5 ++ .../LiveTv/EmbyTV/IRecorder.cs | 3 ++ .../LiveTv/EmbyTV/ItemDataProvider.cs | 3 ++ .../LiveTv/EmbyTV/RecordingHelper.cs | 18 ++++++-- .../LiveTv/EmbyTV/SeriesTimerManager.cs | 4 ++ .../LiveTv/EmbyTV/TimerManager.cs | 3 ++ .../LiveTv/Listings/SchedulesDirect.cs | 3 ++ .../LiveTv/Listings/XmlTvListingsProvider.cs | 7 ++- .../LiveTv/LiveTvConfigurationFactory.cs | 3 ++ .../LiveTv/LiveTvDtoService.cs | 3 ++ .../LiveTv/LiveTvManager.cs | 2 +- .../LiveTv/LiveTvMediaSourceProvider.cs | 46 ++++++++----------- .../LiveTv/TunerHosts/BaseTunerHost.cs | 3 ++ .../TunerHosts/HdHomerun/HdHomerunHost.cs | 3 ++ .../TunerHosts/HdHomerun/HdHomerunManager.cs | 3 ++ .../HdHomerun/HdHomerunUdpStream.cs | 3 ++ .../LiveTv/TunerHosts/LiveStream.cs | 3 ++ .../LiveTv/TunerHosts/M3UTunerHost.cs | 3 ++ .../LiveTv/TunerHosts/M3uParser.cs | 3 ++ .../LiveTv/TunerHosts/SharedHttpStream.cs | 3 ++ 32 files changed, 181 insertions(+), 128 deletions(-) diff --git a/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs b/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs index e0aa18e895..eecc2c5297 100644 --- a/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs @@ -13,7 +13,7 @@ using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.EntryPoints { - public class RecordingNotifier : IServerEntryPoint + public sealed class RecordingNotifier : IServerEntryPoint { private readonly ILiveTvManager _liveTvManager; private readonly ISessionManager _sessionManager; @@ -30,30 +30,30 @@ namespace Emby.Server.Implementations.EntryPoints public Task RunAsync() { - _liveTvManager.TimerCancelled += _liveTvManager_TimerCancelled; - _liveTvManager.SeriesTimerCancelled += _liveTvManager_SeriesTimerCancelled; - _liveTvManager.TimerCreated += _liveTvManager_TimerCreated; - _liveTvManager.SeriesTimerCreated += _liveTvManager_SeriesTimerCreated; + _liveTvManager.TimerCancelled += LiveTvManagerTimerCancelled; + _liveTvManager.SeriesTimerCancelled += LiveTvManagerSeriesTimerCancelled; + _liveTvManager.TimerCreated += OnLiveTvManagerTimerCreated; + _liveTvManager.SeriesTimerCreated += OnLiveTvManagerSeriesTimerCreated; return Task.CompletedTask; } - private void _liveTvManager_SeriesTimerCreated(object sender, MediaBrowser.Model.Events.GenericEventArgs e) + private void OnLiveTvManagerSeriesTimerCreated(object sender, MediaBrowser.Model.Events.GenericEventArgs e) { SendMessage("SeriesTimerCreated", e.Argument); } - private void _liveTvManager_TimerCreated(object sender, MediaBrowser.Model.Events.GenericEventArgs e) + private void OnLiveTvManagerTimerCreated(object sender, MediaBrowser.Model.Events.GenericEventArgs e) { SendMessage("TimerCreated", e.Argument); } - private void _liveTvManager_SeriesTimerCancelled(object sender, MediaBrowser.Model.Events.GenericEventArgs e) + private void LiveTvManagerSeriesTimerCancelled(object sender, MediaBrowser.Model.Events.GenericEventArgs e) { SendMessage("SeriesTimerCancelled", e.Argument); } - private void _liveTvManager_TimerCancelled(object sender, MediaBrowser.Model.Events.GenericEventArgs e) + private void LiveTvManagerTimerCancelled(object sender, MediaBrowser.Model.Events.GenericEventArgs e) { SendMessage("TimerCancelled", e.Argument); } @@ -64,7 +64,7 @@ namespace Emby.Server.Implementations.EntryPoints try { - await _sessionManager.SendMessageToUserSessions(users, name, info, CancellationToken.None); + await _sessionManager.SendMessageToUserSessions(users, name, info, CancellationToken.None).ConfigureAwait(false); } catch (ObjectDisposedException) { @@ -76,12 +76,13 @@ namespace Emby.Server.Implementations.EntryPoints } } + /// public void Dispose() { - _liveTvManager.TimerCancelled -= _liveTvManager_TimerCancelled; - _liveTvManager.SeriesTimerCancelled -= _liveTvManager_SeriesTimerCancelled; - _liveTvManager.TimerCreated -= _liveTvManager_TimerCreated; - _liveTvManager.SeriesTimerCreated -= _liveTvManager_SeriesTimerCreated; + _liveTvManager.TimerCancelled -= LiveTvManagerTimerCancelled; + _liveTvManager.SeriesTimerCancelled -= LiveTvManagerSeriesTimerCancelled; + _liveTvManager.TimerCreated -= OnLiveTvManagerTimerCreated; + _liveTvManager.SeriesTimerCreated -= OnLiveTvManagerSeriesTimerCreated; } } } diff --git a/Emby.Server.Implementations/EntryPoints/RefreshUsersMetadata.cs b/Emby.Server.Implementations/EntryPoints/RefreshUsersMetadata.cs index f00996b5fe..54f4b67e66 100644 --- a/Emby.Server.Implementations/EntryPoints/RefreshUsersMetadata.cs +++ b/Emby.Server.Implementations/EntryPoints/RefreshUsersMetadata.cs @@ -6,7 +6,6 @@ using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.IO; using MediaBrowser.Model.Tasks; -using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.EntryPoints { @@ -15,21 +14,17 @@ namespace Emby.Server.Implementations.EntryPoints /// public class RefreshUsersMetadata : IScheduledTask, IConfigurableScheduledTask { - private readonly ILogger _logger; - /// /// The user manager. /// private readonly IUserManager _userManager; - - private IFileSystem _fileSystem; + private readonly IFileSystem _fileSystem; /// /// Initializes a new instance of the class. /// - public RefreshUsersMetadata(ILogger logger, IUserManager userManager, IFileSystem fileSystem) + public RefreshUsersMetadata(IUserManager userManager, IFileSystem fileSystem) { - _logger = logger; _userManager = userManager; _fileSystem = fileSystem; } diff --git a/Emby.Server.Implementations/EntryPoints/StartupWizard.cs b/Emby.Server.Implementations/EntryPoints/StartupWizard.cs index 161788c636..5f2d629fea 100644 --- a/Emby.Server.Implementations/EntryPoints/StartupWizard.cs +++ b/Emby.Server.Implementations/EntryPoints/StartupWizard.cs @@ -3,37 +3,28 @@ using Emby.Server.Implementations.Browser; using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Plugins; -using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.EntryPoints { /// /// Class StartupWizard. /// - public class StartupWizard : IServerEntryPoint + public sealed class StartupWizard : IServerEntryPoint { /// /// The app host. /// private readonly IServerApplicationHost _appHost; - - /// - /// The user manager. - /// - private readonly ILogger _logger; - - private IServerConfigurationManager _config; + private readonly IServerConfigurationManager _config; /// /// Initializes a new instance of the class. /// /// The application host. - /// The logger. /// The configuration manager. - public StartupWizard(IServerApplicationHost appHost, ILogger logger, IServerConfigurationManager config) + public StartupWizard(IServerApplicationHost appHost, IServerConfigurationManager config) { _appHost = appHost; - _logger = logger; _config = config; } diff --git a/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs b/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs index 529f835606..50ba0f8fac 100644 --- a/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs +++ b/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs @@ -3,8 +3,6 @@ using System.Threading.Tasks; using Emby.Server.Implementations.Udp; using MediaBrowser.Controller; using MediaBrowser.Controller.Plugins; -using MediaBrowser.Model.Net; -using MediaBrowser.Model.Serialization; using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.EntryPoints @@ -23,9 +21,7 @@ namespace Emby.Server.Implementations.EntryPoints /// The logger. /// private readonly ILogger _logger; - private readonly ISocketFactory _socketFactory; private readonly IServerApplicationHost _appHost; - private readonly IJsonSerializer _json; /// /// The UDP server. @@ -64,7 +60,7 @@ namespace Emby.Server.Implementations.EntryPoints _cancellationTokenSource.Cancel(); _udpServer.Dispose(); - + _cancellationTokenSource.Dispose(); _cancellationTokenSource = null; _udpServer = null; diff --git a/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs b/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs index 3e22080fc0..026b5dae97 100644 --- a/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs @@ -13,39 +13,38 @@ using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Session; -using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.EntryPoints { - public class UserDataChangeNotifier : IServerEntryPoint + public sealed class UserDataChangeNotifier : IServerEntryPoint { + private const int UpdateDuration = 500; + private readonly ISessionManager _sessionManager; - private readonly ILogger _logger; private readonly IUserDataManager _userDataManager; private readonly IUserManager _userManager; - private readonly object _syncLock = new object(); - private Timer UpdateTimer { get; set; } - private const int UpdateDuration = 500; - private readonly Dictionary> _changedItems = new Dictionary>(); - public UserDataChangeNotifier(IUserDataManager userDataManager, ISessionManager sessionManager, ILogger logger, IUserManager userManager) + private readonly object _syncLock = new object(); + private Timer _updateTimer; + + + public UserDataChangeNotifier(IUserDataManager userDataManager, ISessionManager sessionManager, IUserManager userManager) { _userDataManager = userDataManager; _sessionManager = sessionManager; - _logger = logger; _userManager = userManager; } public Task RunAsync() { - _userDataManager.UserDataSaved += _userDataManager_UserDataSaved; + _userDataManager.UserDataSaved += OnUserDataManagerUserDataSaved; return Task.CompletedTask; } - void _userDataManager_UserDataSaved(object sender, UserDataSaveEventArgs e) + void OnUserDataManagerUserDataSaved(object sender, UserDataSaveEventArgs e) { if (e.SaveReason == UserDataSaveReason.PlaybackProgress) { @@ -54,14 +53,17 @@ namespace Emby.Server.Implementations.EntryPoints lock (_syncLock) { - if (UpdateTimer == null) + if (_updateTimer == null) { - UpdateTimer = new Timer(UpdateTimerCallback, null, UpdateDuration, - Timeout.Infinite); + _updateTimer = new Timer( + UpdateTimerCallback, + null, + UpdateDuration, + Timeout.Infinite); } else { - UpdateTimer.Change(UpdateDuration, Timeout.Infinite); + _updateTimer.Change(UpdateDuration, Timeout.Infinite); } if (!_changedItems.TryGetValue(e.UserId, out List keys)) @@ -97,10 +99,10 @@ namespace Emby.Server.Implementations.EntryPoints var task = SendNotifications(changes, CancellationToken.None); - if (UpdateTimer != null) + if (_updateTimer != null) { - UpdateTimer.Dispose(); - UpdateTimer = null; + _updateTimer.Dispose(); + _updateTimer = null; } } } @@ -145,13 +147,13 @@ namespace Emby.Server.Implementations.EntryPoints public void Dispose() { - if (UpdateTimer != null) + if (_updateTimer != null) { - UpdateTimer.Dispose(); - UpdateTimer = null; + _updateTimer.Dispose(); + _updateTimer = null; } - _userDataManager.UserDataSaved -= _userDataManager_UserDataSaved; + _userDataManager.UserDataSaved -= OnUserDataManagerUserDataSaved; } } } diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs index 8a2bc83fb0..45fa03cdd7 100644 --- a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs +++ b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs @@ -78,7 +78,7 @@ namespace Emby.Server.Implementations.HttpClientManager if (!string.IsNullOrWhiteSpace(userInfo)) { _logger.LogWarning("Found userInfo in url: {0} ... url: {1}", userInfo, url); - url = url.Replace(userInfo + '@', string.Empty); + url = url.Replace(userInfo + '@', string.Empty, StringComparison.Ordinal); } var request = new HttpRequestMessage(method, url); diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs index b0126f7fa5..85602a67f0 100644 --- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -40,9 +40,9 @@ namespace Emby.Server.Implementations.HttpServer private readonly Func> _funcParseFn; private readonly string _defaultRedirectPath; private readonly string _baseUrlPrefix; - private readonly Dictionary ServiceOperationsMap = new Dictionary(); - private IWebSocketListener[] _webSocketListeners = Array.Empty(); + private readonly Dictionary _serviceOperationsMap = new Dictionary(); private readonly List _webSocketConnections = new List(); + private IWebSocketListener[] _webSocketListeners = Array.Empty(); private bool _disposed = false; public HttpListenerHost( @@ -72,6 +72,8 @@ namespace Emby.Server.Implementations.HttpServer ResponseFilters = Array.Empty>(); } + public event EventHandler> WebSocketConnected; + public Action[] ResponseFilters { get; set; } public static HttpListenerHost Instance { get; protected set; } @@ -82,8 +84,6 @@ namespace Emby.Server.Implementations.HttpServer public ServiceController ServiceController { get; private set; } - public event EventHandler> WebSocketConnected; - public object CreateInstance(Type type) { return _appHost.CreateInstance(type); @@ -91,7 +91,7 @@ namespace Emby.Server.Implementations.HttpServer private static string NormalizeUrlPath(string path) { - if (path.StartsWith("/")) + if (path.Length > 0 && path[0] == '/') { // If the path begins with a leading slash, just return it as-is return path; @@ -131,13 +131,13 @@ namespace Emby.Server.Implementations.HttpServer public Type GetServiceTypeByRequest(Type requestType) { - ServiceOperationsMap.TryGetValue(requestType, out var serviceType); + _serviceOperationsMap.TryGetValue(requestType, out var serviceType); return serviceType; } public void AddServiceInfo(Type serviceType, Type requestType) { - ServiceOperationsMap[requestType] = serviceType; + _serviceOperationsMap[requestType] = serviceType; } private List GetRequestFilterAttributes(Type requestDtoType) @@ -199,7 +199,7 @@ namespace Emby.Server.Implementations.HttpServer else { var inners = agg.InnerExceptions; - if (inners != null && inners.Count > 0) + if (inners.Count > 0) { return GetActualException(inners[0]); } @@ -362,7 +362,7 @@ namespace Emby.Server.Implementations.HttpServer return true; } - host = host ?? string.Empty; + host ??= string.Empty; if (_networkManager.IsInPrivateAddressSpace(host)) { @@ -433,7 +433,7 @@ namespace Emby.Server.Implementations.HttpServer } /// - /// Overridable method that can be used to implement a custom hnandler + /// Overridable method that can be used to implement a custom handler. /// public async Task RequestHandler(IHttpRequest httpReq, string urlString, string host, string localPath, CancellationToken cancellationToken) { @@ -492,7 +492,7 @@ namespace Emby.Server.Implementations.HttpServer || string.Equals(localPath, _baseUrlPrefix, StringComparison.OrdinalIgnoreCase) || string.Equals(localPath, "/", StringComparison.OrdinalIgnoreCase) || string.IsNullOrEmpty(localPath) - || !localPath.StartsWith(_baseUrlPrefix)) + || !localPath.StartsWith(_baseUrlPrefix, StringComparison.OrdinalIgnoreCase)) { // Always redirect back to the default path if the base prefix is invalid or missing _logger.LogDebug("Normalizing a URL at {0}", localPath); @@ -693,7 +693,10 @@ namespace Emby.Server.Implementations.HttpServer protected virtual void Dispose(bool disposing) { - if (_disposed) return; + if (_disposed) + { + return; + } if (disposing) { diff --git a/Emby.Server.Implementations/IO/ExtendedFileSystemInfo.cs b/Emby.Server.Implementations/IO/ExtendedFileSystemInfo.cs index 5be1444525..ec26324c39 100644 --- a/Emby.Server.Implementations/IO/ExtendedFileSystemInfo.cs +++ b/Emby.Server.Implementations/IO/ExtendedFileSystemInfo.cs @@ -6,7 +6,9 @@ namespace Emby.Server.Implementations.IO public class ExtendedFileSystemInfo { public bool IsHidden { get; set; } + public bool IsReadOnly { get; set; } + public bool Exists { get; set; } } } diff --git a/Emby.Server.Implementations/IO/FileRefresher.cs b/Emby.Server.Implementations/IO/FileRefresher.cs index cf92ddbcd7..f37a6af909 100644 --- a/Emby.Server.Implementations/IO/FileRefresher.cs +++ b/Emby.Server.Implementations/IO/FileRefresher.cs @@ -15,27 +15,29 @@ namespace Emby.Server.Implementations.IO { public class FileRefresher : IDisposable { - private ILogger Logger { get; set; } - private ILibraryManager LibraryManager { get; set; } - private IServerConfigurationManager ConfigurationManager { get; set; } - private readonly List _affectedPaths = new List(); - private Timer _timer; - private readonly object _timerLock = new object(); - public string Path { get; private set; } + private readonly ILogger _logger; + private readonly ILibraryManager _libraryManager; + private readonly IServerConfigurationManager _configurationManager; - public event EventHandler Completed; + private readonly List _affectedPaths = new List(); + private readonly object _timerLock = new object(); + private Timer _timer; public FileRefresher(string path, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, ILogger logger) { logger.LogDebug("New file refresher created for {0}", path); Path = path; - ConfigurationManager = configurationManager; - LibraryManager = libraryManager; - Logger = logger; + _configurationManager = configurationManager; + _libraryManager = libraryManager; + _logger = logger; AddPath(path); } + public event EventHandler Completed; + + public string Path { get; private set; } + private void AddAffectedPath(string path) { if (string.IsNullOrEmpty(path)) @@ -80,11 +82,11 @@ namespace Emby.Server.Implementations.IO if (_timer == null) { - _timer = new Timer(OnTimerCallback, null, TimeSpan.FromSeconds(ConfigurationManager.Configuration.LibraryMonitorDelay), TimeSpan.FromMilliseconds(-1)); + _timer = new Timer(OnTimerCallback, null, TimeSpan.FromSeconds(_configurationManager.Configuration.LibraryMonitorDelay), TimeSpan.FromMilliseconds(-1)); } else { - _timer.Change(TimeSpan.FromSeconds(ConfigurationManager.Configuration.LibraryMonitorDelay), TimeSpan.FromMilliseconds(-1)); + _timer.Change(TimeSpan.FromSeconds(_configurationManager.Configuration.LibraryMonitorDelay), TimeSpan.FromMilliseconds(-1)); } } } @@ -93,7 +95,7 @@ namespace Emby.Server.Implementations.IO { lock (_timerLock) { - Logger.LogDebug("Resetting file refresher from {0} to {1}", Path, path); + _logger.LogDebug("Resetting file refresher from {0} to {1}", Path, path); Path = path; AddAffectedPath(path); @@ -116,7 +118,7 @@ namespace Emby.Server.Implementations.IO paths = _affectedPaths.ToList(); } - Logger.LogDebug("Timer stopped."); + _logger.LogDebug("Timer stopped."); DisposeTimer(); Completed?.Invoke(this, EventArgs.Empty); @@ -127,7 +129,7 @@ namespace Emby.Server.Implementations.IO } catch (Exception ex) { - Logger.LogError(ex, "Error processing directory changes"); + _logger.LogError(ex, "Error processing directory changes"); } } @@ -147,7 +149,7 @@ namespace Emby.Server.Implementations.IO continue; } - Logger.LogInformation("{name} ({path}) will be refreshed.", item.Name, item.Path); + _logger.LogInformation("{name} ({path}) will be refreshed.", item.Name, item.Path); try { @@ -158,11 +160,11 @@ namespace Emby.Server.Implementations.IO // For now swallow and log. // Research item: If an IOException occurs, the item may be in a disconnected state (media unavailable) // Should we remove it from it's parent? - Logger.LogError(ex, "Error refreshing {name}", item.Name); + _logger.LogError(ex, "Error refreshing {name}", item.Name); } catch (Exception ex) { - Logger.LogError(ex, "Error refreshing {name}", item.Name); + _logger.LogError(ex, "Error refreshing {name}", item.Name); } } } @@ -178,7 +180,7 @@ namespace Emby.Server.Implementations.IO while (item == null && !string.IsNullOrEmpty(path)) { - item = LibraryManager.FindByPath(path, null); + item = _libraryManager.FindByPath(path, null); path = System.IO.Path.GetDirectoryName(path); } diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs index 161cf60516..9c4f5fe3d8 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System; using System.IO; using System.Net.Http; diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 4ac48e5378..49d4ddbaf5 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -30,7 +30,6 @@ using MediaBrowser.Model.Diagnostics; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Events; -using MediaBrowser.Model.Extensions; using MediaBrowser.Model.IO; using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.MediaInfo; diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs index 6e4ac2fecc..8590c56dfd 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.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.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs index 9c9ba09f5f..a716b6240f 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System.Threading.Tasks; using MediaBrowser.Controller.Plugins; @@ -5,11 +8,13 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { public class EntryPoint : IServerEntryPoint { + /// public Task RunAsync() { return EmbyTV.Current.Start(); } + /// public void Dispose() { } diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs index 6eced30509..d6a1aee38b 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System; using System.Threading; using System.Threading.Tasks; diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs index 9055a70a67..6d42a58f42 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System; using System.Collections.Generic; using System.IO; diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs index ded3c7607d..4cb9f6fe88 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System; using System.Globalization; using MediaBrowser.Controller.LiveTv; @@ -21,7 +24,11 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV if (info.SeasonNumber.HasValue && info.EpisodeNumber.HasValue) { - name += string.Format(" S{0}E{1}", info.SeasonNumber.Value.ToString("00", CultureInfo.InvariantCulture), info.EpisodeNumber.Value.ToString("00", CultureInfo.InvariantCulture)); + name += string.Format( + CultureInfo.InvariantCulture, + " S{0}E{1}", + info.SeasonNumber.Value.ToString("00", CultureInfo.InvariantCulture), + info.EpisodeNumber.Value.ToString("00", CultureInfo.InvariantCulture)); addHyphen = false; } else if (info.OriginalAirDate.HasValue) @@ -32,7 +39,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV } else { - name += " " + info.OriginalAirDate.Value.ToLocalTime().ToString("yyyy-MM-dd"); + name += " " + info.OriginalAirDate.Value.ToLocalTime().ToString("yyyy-MM-dd", CultureInfo.InvariantCulture); } } else @@ -67,14 +74,15 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { date = date.ToLocalTime(); - return string.Format("{0}_{1}_{2}_{3}_{4}_{5}", + return string.Format( + CultureInfo.InvariantCulture, + "{0}_{1}_{2}_{3}_{4}_{5}", date.Year.ToString("0000", CultureInfo.InvariantCulture), date.Month.ToString("00", CultureInfo.InvariantCulture), date.Day.ToString("00", CultureInfo.InvariantCulture), date.Hour.ToString("00", CultureInfo.InvariantCulture), date.Minute.ToString("00", CultureInfo.InvariantCulture), - date.Second.ToString("00", CultureInfo.InvariantCulture) - ); + date.Second.ToString("00", CultureInfo.InvariantCulture)); } } } diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs index 520b444041..9cc53fddcd 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Model.Serialization; @@ -12,6 +15,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { } + /// public override void Add(SeriesTimerInfo item) { if (string.IsNullOrEmpty(item.Id)) diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs index d09b281d4c..330e881ef5 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System; using System.Collections.Concurrent; using System.Globalization; diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs index 1dd794da0d..906f42d2e9 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System; using System.Collections.Concurrent; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs index 1f38de2d86..42daa98f5c 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System; using System.Collections.Generic; using System.Globalization; @@ -91,12 +94,12 @@ namespace Emby.Server.Implementations.LiveTv.Listings { using (var gzStream = new GZipStream(stream, CompressionMode.Decompress)) { - await gzStream.CopyToAsync(fileStream).ConfigureAwait(false); + await gzStream.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false); } } else { - await stream.CopyToAsync(fileStream).ConfigureAwait(false); + await stream.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false); } } diff --git a/Emby.Server.Implementations/LiveTv/LiveTvConfigurationFactory.cs b/Emby.Server.Implementations/LiveTv/LiveTvConfigurationFactory.cs index f9b274acbd..222fed9d92 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvConfigurationFactory.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvConfigurationFactory.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System.Collections.Generic; using MediaBrowser.Common.Configuration; using MediaBrowser.Model.LiveTv; diff --git a/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs b/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs index e584664c94..14b627f82e 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System; using System.Globalization; using System.Linq; diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs index e3f9df35a0..f20f6140e5 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs @@ -1,5 +1,5 @@ -#pragma warning disable SA1600 #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs b/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs index 52d60c004a..7503c5d069 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs @@ -1,52 +1,48 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.Configuration; using MediaBrowser.Controller; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; -using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.Dto; using MediaBrowser.Model.MediaInfo; -using MediaBrowser.Model.Serialization; using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.LiveTv { public class LiveTvMediaSourceProvider : IMediaSourceProvider { + // Do not use a pipe here because Roku http requests to the server will fail, without any explicit error message. + private const char StreamIdDelimeter = '_'; + private const string StreamIdDelimeterString = "_"; + private readonly ILiveTvManager _liveTvManager; - private readonly IJsonSerializer _jsonSerializer; private readonly ILogger _logger; private readonly IMediaSourceManager _mediaSourceManager; - private readonly IMediaEncoder _mediaEncoder; private readonly IServerApplicationHost _appHost; - private IApplicationPaths _appPaths; - public LiveTvMediaSourceProvider(ILiveTvManager liveTvManager, IApplicationPaths appPaths, IJsonSerializer jsonSerializer, ILoggerFactory loggerFactory, IMediaSourceManager mediaSourceManager, IMediaEncoder mediaEncoder, IServerApplicationHost appHost) + public LiveTvMediaSourceProvider(ILiveTvManager liveTvManager, ILogger logger, IMediaSourceManager mediaSourceManager, IServerApplicationHost appHost) { _liveTvManager = liveTvManager; - _jsonSerializer = jsonSerializer; + _logger = logger; _mediaSourceManager = mediaSourceManager; - _mediaEncoder = mediaEncoder; _appHost = appHost; - _logger = loggerFactory.CreateLogger(GetType().Name); - _appPaths = appPaths; } public Task> GetMediaSources(BaseItem item, CancellationToken cancellationToken) { - var baseItem = (BaseItem)item; - - if (baseItem.SourceType == SourceType.LiveTV) + if (item.SourceType == SourceType.LiveTV) { var activeRecordingInfo = _liveTvManager.GetActiveRecordingInfo(item.Path); - if (string.IsNullOrEmpty(baseItem.Path) || activeRecordingInfo != null) + if (string.IsNullOrEmpty(item.Path) || activeRecordingInfo != null) { return GetMediaSourcesInternal(item, activeRecordingInfo, cancellationToken); } @@ -55,10 +51,6 @@ namespace Emby.Server.Implementations.LiveTv return Task.FromResult>(Array.Empty()); } - // Do not use a pipe here because Roku http requests to the server will fail, without any explicit error message. - private const char StreamIdDelimeter = '_'; - private const string StreamIdDelimeterString = "_"; - private async Task> GetMediaSourcesInternal(BaseItem item, ActiveRecordingInfo activeRecordingInfo, CancellationToken cancellationToken) { IEnumerable sources; @@ -91,7 +83,7 @@ namespace Emby.Server.Implementations.LiveTv foreach (var source in list) { source.Type = MediaSourceType.Default; - source.BufferMs = source.BufferMs ?? 1500; + source.BufferMs ??= 1500; if (source.RequiresOpening || forceRequireOpening) { @@ -100,11 +92,13 @@ namespace Emby.Server.Implementations.LiveTv if (source.RequiresOpening) { - var openKeys = new List(); - openKeys.Add(item.GetType().Name); - openKeys.Add(item.Id.ToString("N", CultureInfo.InvariantCulture)); - openKeys.Add(source.Id ?? string.Empty); - source.OpenToken = string.Join(StreamIdDelimeterString, openKeys.ToArray()); + var openKeys = new List + { + item.GetType().Name, + item.Id.ToString("N", CultureInfo.InvariantCulture), + source.Id ?? string.Empty + }; + source.OpenToken = string.Join(StreamIdDelimeterString, openKeys); } // Dummy this up so that direct play checks can still run @@ -114,7 +108,7 @@ namespace Emby.Server.Implementations.LiveTv } } - _logger.LogDebug("MediaSources: {0}", _jsonSerializer.SerializeToString(list)); + _logger.LogDebug("MediaSources: {@MediaSources}", list); return list; } diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs index 715f600a17..419ec3635a 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System; using System.Collections.Concurrent; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index 06f27fa3ea..e5cb6c7b9c 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.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.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs index 9702392b29..56864ab116 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System; using System.Buffers; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs index 649becbd3c..77669da39f 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System; using System.Collections.Generic; using System.IO; diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs index 862b9fdfed..5354489f9c 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.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.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs index df054f1ebb..46c77e7b0e 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.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.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs index 51f61bac76..511af150bb 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.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.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs index 99244eb625..8615183871 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System; using System.Collections.Generic; using System.IO; From c3f749ec66050859ab997fab3e2e98d6c9269132 Mon Sep 17 00:00:00 2001 From: artiume Date: Thu, 6 Feb 2020 14:02:46 -0500 Subject: [PATCH 023/144] Update MediaBrowser.Model/Users/UserPolicy.cs Co-Authored-By: Anthony Lavado --- MediaBrowser.Model/Users/UserPolicy.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/MediaBrowser.Model/Users/UserPolicy.cs b/MediaBrowser.Model/Users/UserPolicy.cs index 8a6b49d8b2..14829c2b08 100644 --- a/MediaBrowser.Model/Users/UserPolicy.cs +++ b/MediaBrowser.Model/Users/UserPolicy.cs @@ -93,7 +93,6 @@ namespace MediaBrowser.Model.Users EnableVideoPlaybackTranscoding = true; EnablePlaybackRemuxing = true; ForceRemoteSourceTranscoding = false; - EnableLiveTvManagement = true; EnableLiveTvAccess = true; From a73ce1d7813825da99654f17d69f85f4eb63344d Mon Sep 17 00:00:00 2001 From: artiume Date: Thu, 6 Feb 2020 17:21:10 -0500 Subject: [PATCH 024/144] Update MediaInfoService.cs --- MediaBrowser.Api/Playback/MediaInfoService.cs | 28 ++++--------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index 3aa228a616..835a4366b3 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -415,11 +415,7 @@ namespace MediaBrowser.Api.Playback // Beginning of Playback Determination: Attempt DirectPlay first if (mediaSource.SupportsDirectPlay) { - if (mediaSource.IsRemote && forceDirectPlayRemoteMediaSource && user.Policy.ForceRemoteSourceTranscoding) - { - mediaSource.SupportsDirectPlay = false; - } - else if (mediaSource.IsRemote && user.Policy.ForceRemoteSourceTranscoding) + if (mediaSource.IsRemote && user.Policy.ForceRemoteSourceTranscoding) { mediaSource.SupportsDirectPlay = false; } @@ -468,11 +464,7 @@ namespace MediaBrowser.Api.Playback if (mediaSource.SupportsDirectStream) { - if (mediaSource.IsRemote && forceDirectPlayRemoteMediaSource && user.Policy.ForceRemoteSourceTranscoding) - { - mediaSource.SupportsDirectStream = false; - } - else if (mediaSource.IsRemote && user.Policy.ForceRemoteSourceTranscoding) + if (mediaSource.IsRemote && user.Policy.ForceRemoteSourceTranscoding) { mediaSource.SupportsDirectStream = false; } @@ -516,17 +508,6 @@ namespace MediaBrowser.Api.Playback { 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); } @@ -543,7 +524,10 @@ namespace MediaBrowser.Api.Playback streamInfo.StartPositionTicks = startTimeTicks; mediaSource.TranscodingUrl = streamInfo.ToUrl("-", auth.Token).TrimStart('-'); mediaSource.TranscodingUrl += "&allowVideoStreamCopy=false"; - mediaSource.TranscodingUrl += "&allowAudioStreamCopy=false"; + if (!allowAudioStreamCopy) + { + mediaSource.TranscodingUrl += "&allowAudioStreamCopy=false"; + } mediaSource.TranscodingContainer = streamInfo.Container; mediaSource.TranscodingSubProtocol = streamInfo.SubProtocol; From 81396a11cb61b00ff2b1cd4484260c8b0cde8a30 Mon Sep 17 00:00:00 2001 From: artiume Date: Thu, 6 Feb 2020 17:35:47 -0500 Subject: [PATCH 025/144] Update CONTRIBUTORS.md --- CONTRIBUTORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 800b3d51ff..1da9785de7 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -33,6 +33,7 @@ - [mark-monteiro](https://github.com/mark-monteiro) - [ullmie02](https://github.com/ullmie02) - [pR0Ps](https://github.com/pR0Ps) + - [artiume](https://github.com/Artiume) # Emby Contributors From 867835a474d16bfe6ab6a27214de49478ed2a05b Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 8 Feb 2020 22:25:44 +0100 Subject: [PATCH 026/144] Fix build --- .../Api/NotificationsService.cs | 24 +++++++++---------- Emby.Notifications/NotificationEntryPoint.cs | 9 +++++-- Emby.Notifications/NotificationManager.cs | 2 +- .../Activity/ActivityLogEntryPoint.cs | 10 ++++---- .../ApplicationHost.cs | 5 +++- 5 files changed, 28 insertions(+), 22 deletions(-) diff --git a/Emby.Notifications/Api/NotificationsService.cs b/Emby.Notifications/Api/NotificationsService.cs index f9e923d10b..f2f3818381 100644 --- a/Emby.Notifications/Api/NotificationsService.cs +++ b/Emby.Notifications/Api/NotificationsService.cs @@ -22,7 +22,7 @@ namespace Emby.Notifications.Api public class GetNotifications : IReturn { [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string UserId { get; set; } + public string UserId { get; set; } = string.Empty; [ApiMember(Name = "IsRead", Description = "An optional filter by IsRead", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] public bool? IsRead { get; set; } @@ -36,26 +36,26 @@ namespace Emby.Notifications.Api public class Notification { - public string Id { get; set; } + public string Id { get; set; } = string.Empty; - public string UserId { get; set; } + public string UserId { get; set; } = string.Empty; public DateTime Date { get; set; } public bool IsRead { get; set; } - public string Name { get; set; } + public string Name { get; set; } = string.Empty; - public string Description { get; set; } + public string Description { get; set; } = string.Empty; - public string Url { get; set; } + public string Url { get; set; } = string.Empty; public NotificationLevel Level { get; set; } } public class NotificationResult { - public IReadOnlyList Notifications { get; set; } + public IReadOnlyList Notifications { get; set; } = Array.Empty(); public int TotalRecordCount { get; set; } } @@ -71,7 +71,7 @@ namespace Emby.Notifications.Api public class GetNotificationsSummary : IReturn { [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string UserId { get; set; } + public string UserId { get; set; } = string.Empty; } [Route("/Notifications/Types", "GET", Summary = "Gets notification types")] @@ -107,20 +107,20 @@ namespace Emby.Notifications.Api public class MarkRead : IReturnVoid { [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string UserId { get; set; } + public string UserId { get; set; } = string.Empty; [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; } + public string Ids { get; set; } = string.Empty; } [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; } + public string UserId { get; set; } = string.Empty; [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; } + public string Ids { get; set; } = string.Empty; } [Authenticated] diff --git a/Emby.Notifications/NotificationEntryPoint.cs b/Emby.Notifications/NotificationEntryPoint.cs index ab92822fa2..befecc570b 100644 --- a/Emby.Notifications/NotificationEntryPoint.cs +++ b/Emby.Notifications/NotificationEntryPoint.cs @@ -227,7 +227,12 @@ namespace Emby.Notifications } } - private static string GetItemName(BaseItem item) + /// + /// Creates a human readable name for the item. + /// + /// The item. + /// A human readable name for the item. + public static string GetItemName(BaseItem item) { var name = item.Name; if (item is Episode episode) @@ -298,7 +303,7 @@ namespace Emby.Notifications } /// - /// Releases unmanaged and - optionally - managed resources. + /// 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) diff --git a/Emby.Notifications/NotificationManager.cs b/Emby.Notifications/NotificationManager.cs index 836aa3e729..639a5e1aad 100644 --- a/Emby.Notifications/NotificationManager.cs +++ b/Emby.Notifications/NotificationManager.cs @@ -32,7 +32,7 @@ namespace Emby.Notifications /// Initializes a new instance of the class. /// /// The logger. - /// the user manager. + /// The user manager. /// The server configuration manager. public NotificationManager( ILogger logger, diff --git a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs index ac8af66a20..4664eadd3a 100644 --- a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs +++ b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs @@ -29,7 +29,7 @@ using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.Activity { - public class ActivityLogEntryPoint : IServerEntryPoint + public sealed class ActivityLogEntryPoint : IServerEntryPoint { private readonly ILogger _logger; private readonly IInstallationManager _installationManager; @@ -39,7 +39,6 @@ namespace Emby.Server.Implementations.Activity private readonly ILocalizationManager _localization; private readonly ISubtitleManager _subManager; private readonly IUserManager _userManager; - private readonly IServerApplicationHost _appHost; private readonly IDeviceManager _deviceManager; /// @@ -64,8 +63,7 @@ namespace Emby.Server.Implementations.Activity ILocalizationManager localization, IInstallationManager installationManager, ISubtitleManager subManager, - IUserManager userManager, - IServerApplicationHost appHost) + IUserManager userManager) { _logger = logger; _sessionManager = sessionManager; @@ -76,7 +74,6 @@ namespace Emby.Server.Implementations.Activity _installationManager = installationManager; _subManager = subManager; _userManager = userManager; - _appHost = appHost; } public Task RunAsync() @@ -141,7 +138,7 @@ namespace Emby.Server.Implementations.Activity CultureInfo.InvariantCulture, _localization.GetLocalizedString("SubtitleDownloadFailureFromForItem"), e.Provider, - Notifications.Notifications.GetItemName(e.Item)), + Emby.Notifications.NotificationEntryPoint.GetItemName(e.Item)), Type = "SubtitleDownloadFailure", ItemId = e.Item.Id.ToString("N", CultureInfo.InvariantCulture), ShortOverview = e.Exception.Message @@ -533,6 +530,7 @@ namespace Emby.Server.Implementations.Activity private void CreateLogEntry(ActivityLogEntry entry) => _activityManager.Create(entry); + /// public void Dispose() { _taskManager.TaskCompleted -= OnTaskCompleted; diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index e2df8877ab..91e635b467 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -825,7 +825,10 @@ namespace Emby.Server.Implementations UserViewManager = new UserViewManager(LibraryManager, LocalizationManager, UserManager, ChannelManager, LiveTvManager, ServerConfigurationManager); serviceCollection.AddSingleton(UserViewManager); - NotificationManager = new NotificationManager(LoggerFactory, UserManager, ServerConfigurationManager); + NotificationManager = new NotificationManager( + LoggerFactory.CreateLogger(), + UserManager, + ServerConfigurationManager); serviceCollection.AddSingleton(NotificationManager); serviceCollection.AddSingleton(new DeviceDiscovery(ServerConfigurationManager)); From b5342bb7d90bf8dc89fe587f23d36927866cc551 Mon Sep 17 00:00:00 2001 From: artiume Date: Tue, 11 Feb 2020 10:57:16 -0500 Subject: [PATCH 027/144] Fix MaxBitrate --- MediaBrowser.Api/Playback/MediaInfoService.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index 835a4366b3..95052fa2ad 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -506,11 +506,8 @@ namespace MediaBrowser.Api.Playback if (mediaSource.SupportsTranscoding) { - if (mediaSource.IsRemote && user.Policy.ForceRemoteSourceTranscoding) - { - options.MaxBitrate = GetMaxBitrate(maxBitrate, user); - } - + 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) From b375aeb56a9effbb149492d4d148e7c0df2e2ad6 Mon Sep 17 00:00:00 2001 From: artiume Date: Wed, 19 Feb 2020 06:07:48 -0500 Subject: [PATCH 028/144] fix indentation it was tabs --- MediaBrowser.Api/Playback/MediaInfoService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index 95052fa2ad..856c1881af 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -566,7 +566,7 @@ namespace MediaBrowser.Api.Playback // Do this after the above so that StartPositionTicks is set SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token); } - } + } } foreach (var attachment in mediaSource.MediaAttachments) From a4e036413987f0ea88db5d8ccafe56243378e878 Mon Sep 17 00:00:00 2001 From: artiume Date: Wed, 19 Feb 2020 06:15:55 -0500 Subject: [PATCH 029/144] Update MediaInfoService.cs --- MediaBrowser.Api/Playback/MediaInfoService.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index 856c1881af..f93cd2fdf4 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -513,8 +513,8 @@ namespace MediaBrowser.Api.Playback ? streamBuilder.BuildAudioItem(options) : streamBuilder.BuildVideoItem(options); - if (mediaSource.IsRemote && user.Policy.ForceRemoteSourceTranscoding) - { + if (mediaSource.IsRemote && user.Policy.ForceRemoteSourceTranscoding) + { if (streamInfo != null) { streamInfo.PlaySessionId = playSessionId; @@ -531,9 +531,9 @@ namespace MediaBrowser.Api.Playback // Do this after the above so that StartPositionTicks is set SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token); } - } + } else - { + { if (streamInfo != null) { streamInfo.PlaySessionId = playSessionId; From 184ad29f3f5a579b38ce01d88bf22ad9dc229617 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 19 Feb 2020 21:04:28 +0100 Subject: [PATCH 030/144] Address comments --- .../EntryPoints/RecordingNotifier.cs | 17 +++++++---------- .../LiveTv/LiveTvMediaSourceProvider.cs | 2 ++ .../Library/IMediaSourceProvider.cs | 3 +++ 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs b/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs index eecc2c5297..9603d79761 100644 --- a/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs @@ -28,10 +28,11 @@ namespace Emby.Server.Implementations.EntryPoints _liveTvManager = liveTvManager; } + /// public Task RunAsync() { - _liveTvManager.TimerCancelled += LiveTvManagerTimerCancelled; - _liveTvManager.SeriesTimerCancelled += LiveTvManagerSeriesTimerCancelled; + _liveTvManager.TimerCancelled += OnLiveTvManagerTimerCancelled; + _liveTvManager.SeriesTimerCancelled += OnLiveTvManagerSeriesTimerCancelled; _liveTvManager.TimerCreated += OnLiveTvManagerTimerCreated; _liveTvManager.SeriesTimerCreated += OnLiveTvManagerSeriesTimerCreated; @@ -48,12 +49,12 @@ namespace Emby.Server.Implementations.EntryPoints SendMessage("TimerCreated", e.Argument); } - private void LiveTvManagerSeriesTimerCancelled(object sender, MediaBrowser.Model.Events.GenericEventArgs e) + private void OnLiveTvManagerSeriesTimerCancelled(object sender, MediaBrowser.Model.Events.GenericEventArgs e) { SendMessage("SeriesTimerCancelled", e.Argument); } - private void LiveTvManagerTimerCancelled(object sender, MediaBrowser.Model.Events.GenericEventArgs e) + private void OnLiveTvManagerTimerCancelled(object sender, MediaBrowser.Model.Events.GenericEventArgs e) { SendMessage("TimerCancelled", e.Argument); } @@ -66,10 +67,6 @@ namespace Emby.Server.Implementations.EntryPoints { await _sessionManager.SendMessageToUserSessions(users, name, info, CancellationToken.None).ConfigureAwait(false); } - catch (ObjectDisposedException) - { - // TODO Log exception or Investigate and properly fix. - } catch (Exception ex) { _logger.LogError(ex, "Error sending message"); @@ -79,8 +76,8 @@ namespace Emby.Server.Implementations.EntryPoints /// public void Dispose() { - _liveTvManager.TimerCancelled -= LiveTvManagerTimerCancelled; - _liveTvManager.SeriesTimerCancelled -= LiveTvManagerSeriesTimerCancelled; + _liveTvManager.TimerCancelled -= OnLiveTvManagerTimerCancelled; + _liveTvManager.SeriesTimerCancelled -= OnLiveTvManagerSeriesTimerCancelled; _liveTvManager.TimerCreated -= OnLiveTvManagerTimerCreated; _liveTvManager.SeriesTimerCreated -= OnLiveTvManagerSeriesTimerCreated; } diff --git a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs b/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs index 7503c5d069..33887bbfd2 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs @@ -98,6 +98,7 @@ namespace Emby.Server.Implementations.LiveTv item.Id.ToString("N", CultureInfo.InvariantCulture), source.Id ?? string.Empty }; + source.OpenToken = string.Join(StreamIdDelimeterString, openKeys); } @@ -113,6 +114,7 @@ namespace Emby.Server.Implementations.LiveTv return list; } + /// public async Task OpenMediaSource(string openToken, List currentLiveStreams, CancellationToken cancellationToken) { var keys = openToken.Split(new[] { StreamIdDelimeter }, 3); diff --git a/MediaBrowser.Controller/Library/IMediaSourceProvider.cs b/MediaBrowser.Controller/Library/IMediaSourceProvider.cs index 9e74879fc3..ec7798551b 100644 --- a/MediaBrowser.Controller/Library/IMediaSourceProvider.cs +++ b/MediaBrowser.Controller/Library/IMediaSourceProvider.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; From 65a9d618ccfb5e015eb2dccf437e8795ac5350d5 Mon Sep 17 00:00:00 2001 From: dkanada Date: Sat, 22 Feb 2020 15:04:52 +0900 Subject: [PATCH 031/144] add config options for musicbrainz --- .../ConfigurationOptions.cs | 1 - .../Library/LibraryManager.cs | 3 +- .../Entities/Audio/MusicArtist.cs | 1 + MediaBrowser.Controller/Entities/Folder.cs | 8 +- .../MediaBrowser.Providers.csproj | 5 + .../Music/MusicExternalIds.cs | 95 ---------------- .../MusicBrainz/AlbumProvider.cs} | 45 ++++---- .../MusicBrainz/ArtistProvider.cs} | 19 +++- .../Configuration/PluginConfiguration.cs | 16 +++ .../MusicBrainz/Configuration/config.html | 69 ++++++++++++ .../MusicBrainz/MusicBrainzExternalIds.cs | 102 ++++++++++++++++++ .../Plugins/MusicBrainz/Plugin.cs | 35 ++++++ 12 files changed, 273 insertions(+), 126 deletions(-) rename MediaBrowser.Providers/{Music/MusicBrainzAlbumProvider.cs => Plugins/MusicBrainz/AlbumProvider.cs} (96%) rename MediaBrowser.Providers/{Music/MusicBrainzArtistProvider.cs => Plugins/MusicBrainz/ArtistProvider.cs} (94%) create mode 100644 MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/PluginConfiguration.cs create mode 100644 MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/config.html create mode 100644 MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzExternalIds.cs create mode 100644 MediaBrowser.Providers/Plugins/MusicBrainz/Plugin.cs diff --git a/Emby.Server.Implementations/ConfigurationOptions.cs b/Emby.Server.Implementations/ConfigurationOptions.cs index 2ea7ff6e91..d0f3d67230 100644 --- a/Emby.Server.Implementations/ConfigurationOptions.cs +++ b/Emby.Server.Implementations/ConfigurationOptions.cs @@ -8,7 +8,6 @@ namespace Emby.Server.Implementations public static Dictionary Configuration => new Dictionary { { "HttpListenerHost:DefaultRedirectPath", "web/index.html" }, - { "MusicBrainz:BaseUrl", "https://www.musicbrainz.org" }, { FfmpegProbeSizeKey, "1G" }, { FfmpegAnalyzeDurationKey, "200M" } }; diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 55566ed517..8a66ffdaac 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -944,7 +944,6 @@ namespace Emby.Server.Implementations.Library IncludeItemTypes = new[] { typeof(T).Name }, Name = name, DtoOptions = options - }).Cast() .OrderBy(i => i.IsAccessedByName ? 1 : 0) .Cast() @@ -1080,7 +1079,7 @@ namespace Emby.Server.Implementations.Library var innerProgress = new ActionableProgress(); - innerProgress.RegisterAction(pct => progress.Report(pct * pct * 0.96)); + innerProgress.RegisterAction(pct => progress.Report(pct * 0.96)); // Validate the entire media library await RootFolder.ValidateChildren(innerProgress, cancellationToken, new MetadataRefreshOptions(new DirectoryService(_fileSystem)), recursive: true).ConfigureAwait(false); diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs index efe0d3cf70..5e3056ccb0 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs @@ -198,6 +198,7 @@ namespace MediaBrowser.Controller.Entities.Audio return true; } } + return base.RequiresRefresh(); } diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index 07fbe60350..a892be7a97 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -322,10 +322,10 @@ namespace MediaBrowser.Controller.Entities ProviderManager.OnRefreshProgress(this, 5); } - //build a dictionary of the current children we have now by Id so we can compare quickly and easily + // Build a dictionary of the current children we have now by Id so we can compare quickly and easily var currentChildren = GetActualChildrenDictionary(); - //create a list for our validated children + // Create a list for our validated children var newItems = new List(); cancellationToken.ThrowIfCancellationRequested(); @@ -391,7 +391,7 @@ namespace MediaBrowser.Controller.Entities var folder = this; innerProgress.RegisterAction(p => { - double newPct = .80 * p + 10; + double newPct = 0.80 * p + 10; progress.Report(newPct); ProviderManager.OnRefreshProgress(folder, newPct); }); @@ -421,7 +421,7 @@ namespace MediaBrowser.Controller.Entities var folder = this; innerProgress.RegisterAction(p => { - double newPct = .10 * p + 90; + double newPct = 0.10 * p + 90; progress.Report(newPct); if (recursive) { diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 5593c5036c..48c16b6433 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -24,4 +24,9 @@ true + + + + + diff --git a/MediaBrowser.Providers/Music/MusicExternalIds.cs b/MediaBrowser.Providers/Music/MusicExternalIds.cs index 585c98af9e..b431c1e41f 100644 --- a/MediaBrowser.Providers/Music/MusicExternalIds.cs +++ b/MediaBrowser.Providers/Music/MusicExternalIds.cs @@ -5,101 +5,6 @@ using MediaBrowser.Model.Entities; namespace MediaBrowser.Providers.Music { - public class MusicBrainzReleaseGroupExternalId : IExternalId - { - /// - public string Name => "MusicBrainz Release Group"; - - /// - public string Key => MetadataProviders.MusicBrainzReleaseGroup.ToString(); - - /// - public string UrlFormatString => "https://musicbrainz.org/release-group/{0}"; - - /// - public bool Supports(IHasProviderIds item) - => item is Audio || item is MusicAlbum; - } - - public class MusicBrainzAlbumArtistExternalId : IExternalId - { - /// - public string Name => "MusicBrainz Album Artist"; - - /// - public string Key => MetadataProviders.MusicBrainzAlbumArtist.ToString(); - - /// - public string UrlFormatString => "https://musicbrainz.org/artist/{0}"; - - /// - public bool Supports(IHasProviderIds item) - => item is Audio; - } - - public class MusicBrainzAlbumExternalId : IExternalId - { - /// - public string Name => "MusicBrainz Album"; - - /// - public string Key => MetadataProviders.MusicBrainzAlbum.ToString(); - - /// - public string UrlFormatString => "https://musicbrainz.org/release/{0}"; - - /// - public bool Supports(IHasProviderIds item) - => item is Audio || item is MusicAlbum; - } - - public class MusicBrainzArtistExternalId : IExternalId - { - /// - public string Name => "MusicBrainz"; - - /// - public string Key => MetadataProviders.MusicBrainzArtist.ToString(); - - /// - public string UrlFormatString => "https://musicbrainz.org/artist/{0}"; - - /// - public bool Supports(IHasProviderIds item) => item is MusicArtist; - } - - public class MusicBrainzOtherArtistExternalId : IExternalId - { - /// - public string Name => "MusicBrainz Artist"; - - /// - - public string Key => MetadataProviders.MusicBrainzArtist.ToString(); - - /// - public string UrlFormatString => "https://musicbrainz.org/artist/{0}"; - - /// - public bool Supports(IHasProviderIds item) - => item is Audio || item is MusicAlbum; - } - - public class MusicBrainzTrackId : IExternalId - { - /// - public string Name => "MusicBrainz Track"; - - /// - public string Key => MetadataProviders.MusicBrainzTrack.ToString(); - - /// - public string UrlFormatString => "https://musicbrainz.org/track/{0}"; - - /// - public bool Supports(IHasProviderIds item) => item is Audio; - } - public class ImvdbId : IExternalId { /// diff --git a/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/AlbumProvider.cs similarity index 96% rename from MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs rename to MediaBrowser.Providers/Plugins/MusicBrainz/AlbumProvider.cs index 8e71b625ee..0fddb05579 100644 --- a/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs +++ b/MediaBrowser.Providers/Plugins/MusicBrainz/AlbumProvider.cs @@ -15,7 +15,7 @@ using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Providers; -using Microsoft.Extensions.Configuration; +using MediaBrowser.Providers.Plugins.MusicBrainz; using Microsoft.Extensions.Logging; namespace MediaBrowser.Providers.Music @@ -28,7 +28,7 @@ namespace MediaBrowser.Providers.Music /// Be prudent, use a value slightly above the minimun required. /// https://musicbrainz.org/doc/XML_Web_Service/Rate_Limiting /// - private const long MusicBrainzQueryIntervalMs = 1050u; + private readonly long _musicBrainzQueryIntervalMs = 1050u; /// /// For each single MB lookup/search, this is the maximum number of @@ -50,14 +50,14 @@ namespace MediaBrowser.Providers.Music public MusicBrainzAlbumProvider( IHttpClient httpClient, IApplicationHost appHost, - ILogger logger, - IConfiguration configuration) + ILogger logger) { _httpClient = httpClient; _appHost = appHost; _logger = logger; - _musicBrainzBaseUrl = configuration["MusicBrainz:BaseUrl"]; + _musicBrainzBaseUrl = Plugin.Instance.Configuration.Server; + _musicBrainzQueryIntervalMs = Plugin.Instance.Configuration.RateLimit; // Use a stopwatch to ensure we don't exceed the MusicBrainz rate limit _stopWatchMusicBrainz.Start(); @@ -74,6 +74,12 @@ namespace MediaBrowser.Providers.Music /// public async Task> GetSearchResults(AlbumInfo searchInfo, CancellationToken cancellationToken) { + // TODO maybe remove when artist metadata can be disabled + if (!Plugin.Instance.Configuration.Enable) + { + return Enumerable.Empty(); + } + var releaseId = searchInfo.GetReleaseId(); var releaseGroupId = searchInfo.GetReleaseGroupId(); @@ -107,8 +113,8 @@ namespace MediaBrowser.Providers.Music url = string.Format( CultureInfo.InvariantCulture, "/ws/2/release/?query=\"{0}\" AND artist:\"{1}\"", - WebUtility.UrlEncode(queryName), - WebUtility.UrlEncode(searchInfo.GetAlbumArtist())); + WebUtility.UrlEncode(queryName), + WebUtility.UrlEncode(searchInfo.GetAlbumArtist())); } } @@ -170,7 +176,6 @@ namespace MediaBrowser.Providers.Music } return result; - }); } } @@ -187,6 +192,12 @@ namespace MediaBrowser.Providers.Music Item = new MusicAlbum() }; + // TODO maybe remove when artist metadata can be disabled + if (!Plugin.Instance.Configuration.Enable) + { + return result; + } + // If we have a release group Id but not a release Id... if (string.IsNullOrWhiteSpace(releaseId) && !string.IsNullOrWhiteSpace(releaseGroupId)) { @@ -456,18 +467,6 @@ namespace MediaBrowser.Providers.Music } case "artist-credit": { - // TODO - - /* - * - - -SARCASTIC+ZOOKEEPER -SARCASTIC+ZOOKEEPER - - - - */ using (var subReader = reader.ReadSubtree()) { var artist = ParseArtistCredit(subReader); @@ -764,10 +763,10 @@ namespace MediaBrowser.Providers.Music { attempts++; - if (_stopWatchMusicBrainz.ElapsedMilliseconds < MusicBrainzQueryIntervalMs) + if (_stopWatchMusicBrainz.ElapsedMilliseconds < _musicBrainzQueryIntervalMs) { // MusicBrainz is extremely adamant about limiting to one request per second - var delayMs = MusicBrainzQueryIntervalMs - _stopWatchMusicBrainz.ElapsedMilliseconds; + var delayMs = _musicBrainzQueryIntervalMs - _stopWatchMusicBrainz.ElapsedMilliseconds; await Task.Delay((int)delayMs, cancellationToken).ConfigureAwait(false); } @@ -778,7 +777,7 @@ namespace MediaBrowser.Providers.Music response = await _httpClient.SendAsync(options, "GET").ConfigureAwait(false); - // We retry a finite number of times, and only whilst MB is indcating 503 (throttling) + // We retry a finite number of times, and only whilst MB is indicating 503 (throttling) } while (attempts < MusicBrainzQueryAttempts && response.StatusCode == HttpStatusCode.ServiceUnavailable); diff --git a/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs similarity index 94% rename from MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs rename to MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs index 5d675392c9..260a3b6e7b 100644 --- a/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs +++ b/MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs @@ -14,6 +14,7 @@ using MediaBrowser.Controller.Extensions; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Providers; +using MediaBrowser.Providers.Plugins.MusicBrainz; namespace MediaBrowser.Providers.Music { @@ -22,6 +23,12 @@ namespace MediaBrowser.Providers.Music /// public async Task> GetSearchResults(ArtistInfo searchInfo, CancellationToken cancellationToken) { + // TODO maybe remove when artist metadata can be disabled + if (!Plugin.Instance.Configuration.Enable) + { + return Enumerable.Empty(); + } + var musicBrainzId = searchInfo.GetMusicBrainzArtistId(); if (!string.IsNullOrWhiteSpace(musicBrainzId)) @@ -226,6 +233,12 @@ namespace MediaBrowser.Providers.Music Item = new MusicArtist() }; + // TODO maybe remove when artist metadata can be disabled + if (!Plugin.Instance.Configuration.Enable) + { + return result; + } + var musicBrainzId = id.GetMusicBrainzArtistId(); if (string.IsNullOrWhiteSpace(musicBrainzId)) @@ -237,8 +250,12 @@ namespace MediaBrowser.Providers.Music if (singleResult != null) { musicBrainzId = singleResult.GetProviderId(MetadataProviders.MusicBrainzArtist); - //result.Item.Name = singleResult.Name; result.Item.Overview = singleResult.Overview; + + if (Plugin.Instance.Configuration.ReplaceArtistName) + { + result.Item.Name = singleResult.Name; + } } } diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/PluginConfiguration.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/PluginConfiguration.cs new file mode 100644 index 0000000000..6bce0b4478 --- /dev/null +++ b/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/PluginConfiguration.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using MediaBrowser.Model.Plugins; + +namespace MediaBrowser.Providers.Plugins.MusicBrainz +{ + public class PluginConfiguration : BasePluginConfiguration + { + public bool Enable { get; set; } = false; + + public bool ReplaceArtistName { get; set; } = false; + + public string Server { get; set; } = "https://www.musicbrainz.org"; + + public long RateLimit { get; set; } = 1000u; + } +} diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/config.html b/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/config.html new file mode 100644 index 0000000000..dfffa90655 --- /dev/null +++ b/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/config.html @@ -0,0 +1,69 @@ + + + + MusicBrainz + + +
+
+
+
+
+ +
This can either be a mirror of the official server or a custom server.
+
+
+ +
Span of time between each request in milliseconds.
+
+ + +
+
+ +
+
+
+
+ +
+ + diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzExternalIds.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzExternalIds.cs new file mode 100644 index 0000000000..0184d42410 --- /dev/null +++ b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzExternalIds.cs @@ -0,0 +1,102 @@ +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; + +namespace MediaBrowser.Providers.Music +{ + public class MusicBrainzReleaseGroupExternalId : IExternalId + { + /// + public string Name => "MusicBrainz Release Group"; + + /// + public string Key => MetadataProviders.MusicBrainzReleaseGroup.ToString(); + + /// + public string UrlFormatString => "https://musicbrainz.org/release-group/{0}"; + + /// + public bool Supports(IHasProviderIds item) + => item is Audio || item is MusicAlbum; + } + + public class MusicBrainzAlbumArtistExternalId : IExternalId + { + /// + public string Name => "MusicBrainz Album Artist"; + + /// + public string Key => MetadataProviders.MusicBrainzAlbumArtist.ToString(); + + /// + public string UrlFormatString => "https://musicbrainz.org/artist/{0}"; + + /// + public bool Supports(IHasProviderIds item) + => item is Audio; + } + + public class MusicBrainzAlbumExternalId : IExternalId + { + /// + public string Name => "MusicBrainz Album"; + + /// + public string Key => MetadataProviders.MusicBrainzAlbum.ToString(); + + /// + public string UrlFormatString => "https://musicbrainz.org/release/{0}"; + + /// + public bool Supports(IHasProviderIds item) + => item is Audio || item is MusicAlbum; + } + + public class MusicBrainzArtistExternalId : IExternalId + { + /// + public string Name => "MusicBrainz"; + + /// + public string Key => MetadataProviders.MusicBrainzArtist.ToString(); + + /// + public string UrlFormatString => "https://musicbrainz.org/artist/{0}"; + + /// + public bool Supports(IHasProviderIds item) => item is MusicArtist; + } + + public class MusicBrainzOtherArtistExternalId : IExternalId + { + /// + public string Name => "MusicBrainz Artist"; + + /// + + public string Key => MetadataProviders.MusicBrainzArtist.ToString(); + + /// + public string UrlFormatString => "https://musicbrainz.org/artist/{0}"; + + /// + public bool Supports(IHasProviderIds item) + => item is Audio || item is MusicAlbum; + } + + public class MusicBrainzTrackId : IExternalId + { + /// + public string Name => "MusicBrainz Track"; + + /// + public string Key => MetadataProviders.MusicBrainzTrack.ToString(); + + /// + public string UrlFormatString => "https://musicbrainz.org/track/{0}"; + + /// + public bool Supports(IHasProviderIds item) => item is Audio; + } +} diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/Plugin.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/Plugin.cs new file mode 100644 index 0000000000..2211d513f9 --- /dev/null +++ b/MediaBrowser.Providers/Plugins/MusicBrainz/Plugin.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Plugins; +using MediaBrowser.Model.Plugins; +using MediaBrowser.Model.Serialization; + +namespace MediaBrowser.Providers.Plugins.MusicBrainz +{ + public class Plugin : BasePlugin, IHasWebPages + { + public Plugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer) + : base(applicationPaths, xmlSerializer) + { + Instance = this; + } + + public IEnumerable GetPages() + { + yield return new PluginPageInfo + { + Name = Name, + EmbeddedResourcePath = GetType().Namespace + ".Configuration.config.html" + }; + } + + public override Guid Id => new Guid("8c95c4d2-e50c-4fb0-a4f3-6c06ff0f9a1a"); + + public override string Name => "MusicBrainz"; + + public override string Description => "Get artist and album metadata from any MusicBrainz server."; + + public static Plugin Instance { get; private set; } + } +} From 4becaf83dd60bc0e225e8320d18d7aae88d4057b Mon Sep 17 00:00:00 2001 From: artiume Date: Sat, 22 Feb 2020 10:03:17 -0500 Subject: [PATCH 032/144] Update MediaInfoService.cs --- MediaBrowser.Api/Playback/MediaInfoService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index f93cd2fdf4..a44e1720fe 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -501,7 +501,7 @@ namespace MediaBrowser.Api.Playback { SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token); } - } + } } if (mediaSource.SupportsTranscoding) From 21f11c600a51ec15810ae052fa85abf6bafadabf Mon Sep 17 00:00:00 2001 From: Narfinger Date: Sun, 23 Feb 2020 12:12:48 +0900 Subject: [PATCH 033/144] converted tests to inlinedata --- .../TV/EpisodeNumberTests.cs | 434 +++--------------- 1 file changed, 60 insertions(+), 374 deletions(-) diff --git a/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs b/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs index 5017fce4d9..c4dbf304ae 100644 --- a/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs +++ b/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs @@ -13,6 +13,66 @@ namespace Jellyfin.Naming.Tests.TV [InlineData("The Daily Show/The Daily Show 25x22 - [WEBDL-720p][AAC 2.0][x264] Noah Baumbach-TBS.mkv", 22)] [InlineData("Castle Rock 2x01 Que el rio siga su curso [WEB-DL HULU 1080p h264 Dual DD5.1 Subs].mkv", 1)] [InlineData("After Life 1x06 Episodio 6 [WEB-DL NF 1080p h264 Dual DD 5.1 Sub].mkv", 6)] + [InlineData("Season 02/S02E03 blah.avi", 3)] + [InlineData("Season 2/02x03 - 02x04 - 02x15 - Ep Name.mp4", 3)] + [InlineData("Season 02/02x03 - x04 - x15 - Ep Name.mp4", 3)] + [InlineData("Season 1/01x02 blah.avi", 2)] + [InlineData("Season 1/S01x02 blah.avi", 2)] + [InlineData("Season 1/S01E02 blah.avi", 2)] + [InlineData("Season 2/Elementary - 02x03-04-15 - Ep Name.mp4", 3)] + [InlineData("Season 1/S01xE02 blah.avi", 2)] + [InlineData("Season 1/seriesname S01E02 blah.avi", 2)] + /// This does not seem to be the correct value or is it? + [InlineData("Season 2/[HorribleSubs] Hunter X Hunter - 136 [720p].mkv", 36)] + [InlineData("Season 2/Episode - 16.avi", 16)] + [InlineData("Season 2/Episode 16.avi", 16)] + [InlineData("Season 2/Episode 16 - Some Title.avi", 16)] + [InlineData("Season 2/16 Some Title.avi", 16)] + [InlineData("Season 2/16 - 12 Some Title.avi", 16)] + [InlineData("Season 2/7 - 12 Angry Men.avi", 7)] + [InlineData("Season 2/16 12 Some Title.avi", 16)] + [InlineData("Season 2/7 12 Angry Men.avi", 7)] + [InlineData("Season 4/Uchuu.Senkan.Yamato.2199.E03.avi", 3)] + [InlineData("Running Man/Running Man S2017E368.mkv", 360)] + [InlineData("/The.Legend.of.Condor.Heroes.2017.V2.web-dl.1080p.h264.aac-hdctv/The.Legend.of.Condor.Heroes.2017.E07.V2.web-dl.1080p.h264.aac-hdctv.mkv", 7)] + [InlineData("Season 1/seriesname 01x02 blah.avi", 2)] + [InlineData("Season 25/The Simpsons.S25E09.Steal this episode.mp4", 9)] + [InlineData("Season 1/seriesname S01x02 blah.avi", 2)] + [InlineData("Season 2/Elementary - 02x03 - 02x04 - 02x15 - Ep Name.mp4", 3)] + [InlineData("Season 1/seriesname S01xE02 blah.avi", 2)] + [InlineData("Season 02/Elementary - 02x03 - x04 - x15 - Ep Name.mp4", 3)] + [InlineData("Season 02/02x03x04x15 - Ep Name.mp4", 2)] + [InlineData("Season 02/Elementary - 02x03x04x15 - Ep Name.mp4", 3)] + [InlineData("Season 2/02x03-04-15 - Ep Name.mp4", 3)] + [InlineData("Season 02/02x03-E15 - Ep Name.mp4", 3)] + [InlineData("Season 02/Elementary - 02x03-E15 - Ep Name.mp4", 3)] + [InlineData("Season 1/Elementary - S01E23-E24-E26 - The Woman.mp4", 23)] + [InlineData("Season 2009/S2009E23-E24-E26 - The Woman.mp4", 23)] + [InlineData("Season 2009/2009x02 blah.avi", 2)] + [InlineData("Season 2009/S2009x02 blah.avi", 2)] + [InlineData("Season 2009/S2009E02 blah.avi", 2)] + [InlineData("Season 2009/seriesname 2009x02 blah.avi", 2)] + [InlineData("Season 2009/Elementary - 2009x03x04x15 - Ep Name.mp4", 3)] + [InlineData("Season 2009/2009x03x04x15 - Ep Name.mp4", 3)] + [InlineData("Season 2009/Elementary - 2009x03-E15 - Ep Name.mp4", 3)] + [InlineData("Season 2009/S2009xE02 blah.avi", 2)] + [InlineData("Season 2009/Elementary - S2009E23-E24-E26 - The Woman.mp4", 23)] + [InlineData("Season 2009/seriesname S2009xE02 blah.avi", 2)] + [InlineData("Season 2009/2009x03-E15 - Ep Name.mp4", 3)] + [InlineData("Season 2009/seriesname S2009E02 blah.avi", 2)] + [InlineData("Season 2009/2009x03 - 2009x04 - 2009x15 - Ep Name.mp4", 3)] + [InlineData("Season 2009/2009x03 - x04 - x15 - Ep Name.mp4", 3)] + [InlineData("Season 2009/seriesname S2009x02 blah.avi", 2)] + [InlineData("Season 2009/Elementary - 2009x03 - 2009x04 - 2009x15 - Ep Name.mp4", 3)] + [InlineData("Season 2009/Elementary - 2009x03-04-15 - Ep Name.mp4", 3)] + [InlineData("Season 2009/2009x03-04-15 - Ep Name.mp4", 3)] + [InlineData("Season 2009/Elementary - 2009x03 - x04 - x15 - Ep Name.mp4", 3)] + [InlineData("Season 1/02 - blah-02 a.avi", 2)] + [InlineData("Season 1/02 - blah.avi", 2)] + [InlineData("Season 2/02 - blah 14 blah.avi", 2)] + [InlineData("Season 2/02.avi", 2)] + [InlineData("Season 2/2. Infestation.avi", 2)] + [InlineData("The Wonder Years/The.Wonder.Years.S04.PDTV.x264-JCH/The Wonder Years s04e07 Christmas Party NTSC PDTV.avi", 7)] public void GetEpisodeNumberFromFileTest(string path, int? expected) { var result = new EpisodePathParser(_namingOptions) @@ -21,380 +81,6 @@ namespace Jellyfin.Naming.Tests.TV Assert.Equal(expected, result.EpisodeNumber); } - [Fact] - public void TestEpisodeNumber1() - { - Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 02/S02E03 blah.avi")); - } - - [Fact] - public void TestEpisodeNumber40() - { - Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 2/02x03 - 02x04 - 02x15 - Ep Name.mp4")); - } - - [Fact] - public void TestEpisodeNumber41() - { - Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 1/01x02 blah.avi")); - } - - [Fact] - public void TestEpisodeNumber42() - { - Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 1/S01x02 blah.avi")); - } - - [Fact] - public void TestEpisodeNumber43() - { - Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 1/S01E02 blah.avi")); - } - - [Fact] - public void TestEpisodeNumber44() - { - Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 2/Elementary - 02x03-04-15 - Ep Name.mp4")); - } - - [Fact] - public void TestEpisodeNumber45() - { - Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 1/S01xE02 blah.avi")); - } - - [Fact] - public void TestEpisodeNumber46() - { - Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 1/seriesname S01E02 blah.avi")); - } - - [Fact] - public void TestEpisodeNumber47() - { - Assert.Equal(36, GetEpisodeNumberFromFile(@"Season 2/[HorribleSubs] Hunter X Hunter - 136 [720p].mkv")); - } - - [Fact] - public void TestEpisodeNumber52() - { - Assert.Equal(16, GetEpisodeNumberFromFile(@"Season 2/Episode - 16.avi")); - } - - [Fact] - public void TestEpisodeNumber53() - { - Assert.Equal(16, GetEpisodeNumberFromFile(@"Season 2/Episode 16.avi")); - } - - [Fact] - public void TestEpisodeNumber54() - { - Assert.Equal(16, GetEpisodeNumberFromFile(@"Season 2/Episode 16 - Some Title.avi")); - } - - [Fact] - public void TestEpisodeNumber57() - { - Assert.Equal(16, GetEpisodeNumberFromFile(@"Season 2/16 Some Title.avi")); - } - - [Fact] - public void TestEpisodeNumber58() - { - Assert.Equal(16, GetEpisodeNumberFromFile(@"Season 2/16 - 12 Some Title.avi")); - } - - [Fact] - public void TestEpisodeNumber59() - { - Assert.Equal(7, GetEpisodeNumberFromFile(@"Season 2/7 - 12 Angry Men.avi")); - } - - // FIXME - // [Fact] - public void TestEpisodeNumber60() - { - Assert.Equal(16, GetEpisodeNumberFromFile(@"Season 2/16 12 Some Title.avi")); - } - - // FIXME - // [Fact] - public void TestEpisodeNumber61() - { - Assert.Equal(7, GetEpisodeNumberFromFile(@"Season 2/7 12 Angry Men.avi")); - } - - // FIXME - // [Fact] - public void TestEpisodeNumber62() - { - // This is not supported. Expected to fail, although it would be a good one to add support for. - Assert.Equal(3, GetEpisodeNumberFromFile(@"Season 4/Uchuu.Senkan.Yamato.2199.E03.avi")); - } - - [Fact] - public void TestEpisodeNumber63() - { - Assert.Equal(3, GetEpisodeNumberFromFile(@"Season 4/Uchuu.Senkan.Yamato.2199.S04E03.avi")); - } - - [Fact] - public void TestEpisodeNumber64() - { - Assert.Equal(368, GetEpisodeNumberFromFile(@"Running Man/Running Man S2017E368.mkv")); - } - - // FIXME - // [Fact] - public void TestEpisodeNumber65() - { - // Not supported yet - Assert.Equal(7, GetEpisodeNumberFromFile(@"/The.Legend.of.Condor.Heroes.2017.V2.web-dl.1080p.h264.aac-hdctv/The.Legend.of.Condor.Heroes.2017.E07.V2.web-dl.1080p.h264.aac-hdctv.mkv")); - } - - [Fact] - public void TestEpisodeNumber30() - { - Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 2/02x03 - 02x04 - 02x15 - Ep Name.mp4")); - } - - // FIXME - // [Fact] - public void TestEpisodeNumber31() - { - Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 1/seriesname 01x02 blah.avi")); - } - - [Fact] - public void TestEpisodeNumber32() - { - Assert.Equal(9, GetEpisodeNumberFromFile(@"Season 25/The Simpsons.S25E09.Steal this episode.mp4")); - } - - [Fact] - public void TestEpisodeNumber33() - { - Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 1/seriesname S01x02 blah.avi")); - } - - [Fact] - public void TestEpisodeNumber34() - { - Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 2/Elementary - 02x03 - 02x04 - 02x15 - Ep Name.mp4")); - } - - [Fact] - public void TestEpisodeNumber35() - { - Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 1/seriesname S01xE02 blah.avi")); - } - - [Fact] - public void TestEpisodeNumber36() - { - Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 02/02x03 - x04 - x15 - Ep Name.mp4")); - } - - [Fact] - public void TestEpisodeNumber37() - { - Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 02/Elementary - 02x03 - x04 - x15 - Ep Name.mp4")); - } - - [Fact] - public void TestEpisodeNumber38() - { - Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 02/02x03x04x15 - Ep Name.mp4")); - } - - [Fact] - public void TestEpisodeNumber39() - { - Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 02/Elementary - 02x03x04x15 - Ep Name.mp4")); - } - - [Fact] - public void TestEpisodeNumber20() - { - Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 2/02x03-04-15 - Ep Name.mp4")); - } - - [Fact] - public void TestEpisodeNumber21() - { - Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 02/02x03-E15 - Ep Name.mp4")); - } - - [Fact] - public void TestEpisodeNumber22() - { - Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 02/Elementary - 02x03-E15 - Ep Name.mp4")); - } - - [Fact] - public void TestEpisodeNumber23() - { - Assert.Equal(23, GetEpisodeNumberFromFile(@"Season 1/Elementary - S01E23-E24-E26 - The Woman.mp4")); - } - - [Fact] - public void TestEpisodeNumber24() - { - Assert.Equal(23, GetEpisodeNumberFromFile(@"Season 2009/S2009E23-E24-E26 - The Woman.mp4")); - } - - [Fact] - public void TestEpisodeNumber25() - { - Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 2009/2009x02 blah.avi")); - } - - [Fact] - public void TestEpisodeNumber26() - { - Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 2009/S2009x02 blah.avi")); - } - - [Fact] - public void TestEpisodeNumber27() - { - Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 2009/S2009E02 blah.avi")); - } - - // FIXME - // [Fact] - public void TestEpisodeNumber28() - { - Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 2009/seriesname 2009x02 blah.avi")); - } - - [Fact] - public void TestEpisodeNumber29() - { - Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 2009/Elementary - 2009x03x04x15 - Ep Name.mp4")); - } - - [Fact] - public void TestEpisodeNumber11() - { - Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 2009/2009x03x04x15 - Ep Name.mp4")); - } - - [Fact] - public void TestEpisodeNumber12() - { - Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 2009/Elementary - 2009x03-E15 - Ep Name.mp4")); - } - - [Fact] - public void TestEpisodeNumber13() - { - Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 2009/S2009xE02 blah.avi")); - } - - [Fact] - public void TestEpisodeNumber14() - { - Assert.Equal(23, GetEpisodeNumberFromFile(@"Season 2009/Elementary - S2009E23-E24-E26 - The Woman.mp4")); - } - - [Fact] - public void TestEpisodeNumber15() - { - Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 2009/seriesname S2009xE02 blah.avi")); - } - - [Fact] - public void TestEpisodeNumber16() - { - Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 2009/2009x03-E15 - Ep Name.mp4")); - } - - [Fact] - public void TestEpisodeNumber17() - { - Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 2009/seriesname S2009E02 blah.avi")); - } - - [Fact] - public void TestEpisodeNumber18() - { - Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 2009/2009x03 - 2009x04 - 2009x15 - Ep Name.mp4")); - } - - [Fact] - public void TestEpisodeNumber19() - { - Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 2009/2009x03 - x04 - x15 - Ep Name.mp4")); - } - - [Fact] - public void TestEpisodeNumber2() - { - Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 2009/seriesname S2009x02 blah.avi")); - } - - [Fact] - public void TestEpisodeNumber3() - { - Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 2009/Elementary - 2009x03 - 2009x04 - 2009x15 - Ep Name.mp4")); - } - - [Fact] - public void TestEpisodeNumber4() - { - Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 2009/Elementary - 2009x03-04-15 - Ep Name.mp4")); - } - - [Fact] - public void TestEpisodeNumber5() - { - Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 2009/2009x03-04-15 - Ep Name.mp4")); - } - - [Fact] - public void TestEpisodeNumber6() - { - Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 2009/Elementary - 2009x03 - x04 - x15 - Ep Name.mp4")); - } - - [Fact] - public void TestEpisodeNumber7() - { - Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 1/02 - blah-02 a.avi")); - } - - [Fact] - public void TestEpisodeNumber8() - { - Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 1/02 - blah.avi")); - } - - [Fact] - public void TestEpisodeNumber9() - { - Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 2/02 - blah 14 blah.avi")); - } - - [Fact] - public void TestEpisodeNumber10() - { - Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 2/02.avi")); - } - - [Fact] - public void TestEpisodeNumber48() - { - Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 2/2. Infestation.avi")); - } - - [Fact] - public void TestEpisodeNumber49() - { - Assert.Equal(7, GetEpisodeNumberFromFile(@"The Wonder Years/The.Wonder.Years.S04.PDTV.x264-JCH/The Wonder Years s04e07 Christmas Party NTSC PDTV.avi")); - } - private int? GetEpisodeNumberFromFile(string path) { var result = new EpisodePathParser(_namingOptions) From c2fe628c796e7c2525629b39d7eaceaaf4d07ae9 Mon Sep 17 00:00:00 2001 From: Narfinger Date: Sun, 23 Feb 2020 18:19:19 +0900 Subject: [PATCH 034/144] removed failing tests --- .../Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs b/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs index c4dbf304ae..4895431ebc 100644 --- a/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs +++ b/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs @@ -23,25 +23,19 @@ namespace Jellyfin.Naming.Tests.TV [InlineData("Season 1/S01xE02 blah.avi", 2)] [InlineData("Season 1/seriesname S01E02 blah.avi", 2)] /// This does not seem to be the correct value or is it? - [InlineData("Season 2/[HorribleSubs] Hunter X Hunter - 136 [720p].mkv", 36)] + g [InlineData("Season 2/Episode - 16.avi", 16)] [InlineData("Season 2/Episode 16.avi", 16)] [InlineData("Season 2/Episode 16 - Some Title.avi", 16)] [InlineData("Season 2/16 Some Title.avi", 16)] [InlineData("Season 2/16 - 12 Some Title.avi", 16)] [InlineData("Season 2/7 - 12 Angry Men.avi", 7)] - [InlineData("Season 2/16 12 Some Title.avi", 16)] - [InlineData("Season 2/7 12 Angry Men.avi", 7)] - [InlineData("Season 4/Uchuu.Senkan.Yamato.2199.E03.avi", 3)] - [InlineData("Running Man/Running Man S2017E368.mkv", 360)] - [InlineData("/The.Legend.of.Condor.Heroes.2017.V2.web-dl.1080p.h264.aac-hdctv/The.Legend.of.Condor.Heroes.2017.E07.V2.web-dl.1080p.h264.aac-hdctv.mkv", 7)] [InlineData("Season 1/seriesname 01x02 blah.avi", 2)] [InlineData("Season 25/The Simpsons.S25E09.Steal this episode.mp4", 9)] [InlineData("Season 1/seriesname S01x02 blah.avi", 2)] [InlineData("Season 2/Elementary - 02x03 - 02x04 - 02x15 - Ep Name.mp4", 3)] [InlineData("Season 1/seriesname S01xE02 blah.avi", 2)] [InlineData("Season 02/Elementary - 02x03 - x04 - x15 - Ep Name.mp4", 3)] - [InlineData("Season 02/02x03x04x15 - Ep Name.mp4", 2)] [InlineData("Season 02/Elementary - 02x03x04x15 - Ep Name.mp4", 3)] [InlineData("Season 2/02x03-04-15 - Ep Name.mp4", 3)] [InlineData("Season 02/02x03-E15 - Ep Name.mp4", 3)] @@ -73,6 +67,12 @@ namespace Jellyfin.Naming.Tests.TV [InlineData("Season 2/02.avi", 2)] [InlineData("Season 2/2. Infestation.avi", 2)] [InlineData("The Wonder Years/The.Wonder.Years.S04.PDTV.x264-JCH/The Wonder Years s04e07 Christmas Party NTSC PDTV.avi", 7)] + //[InlineData("Season 2/16 12 Some Title.avi", 16)] + //[InlineData("Running Man/Running Man S2017E368.mkv", 360)] + //[InlineData("/The.Legend.of.Condor.Heroes.2017.V2.web-dl.1080p.h264.aac-hdctv/The.Legend.of.Condor.Heroes.2017.E07.V2.web-dl.1080p.h264.aac-hdctv.mkv", 7)] + //[InlineData("Season 4/Uchuu.Senkan.Yamato.2199.E03.avi", 3)] + //[InlineData("Season 2/7 12 Angry Men.avi", 7)] + //[InlineData("Season 02/02x03x04x15 - Ep Name.mp4", 2)] public void GetEpisodeNumberFromFileTest(string path, int? expected) { var result = new EpisodePathParser(_namingOptions) From 4dabc50f09425fe8011288ee356509cb3095ed59 Mon Sep 17 00:00:00 2001 From: Narfinger Date: Sun, 23 Feb 2020 18:31:23 +0900 Subject: [PATCH 035/144] fixes last tests and cleanup --- tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs b/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs index 4895431ebc..a9e6d63432 100644 --- a/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs +++ b/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs @@ -22,8 +22,6 @@ namespace Jellyfin.Naming.Tests.TV [InlineData("Season 2/Elementary - 02x03-04-15 - Ep Name.mp4", 3)] [InlineData("Season 1/S01xE02 blah.avi", 2)] [InlineData("Season 1/seriesname S01E02 blah.avi", 2)] - /// This does not seem to be the correct value or is it? - g [InlineData("Season 2/Episode - 16.avi", 16)] [InlineData("Season 2/Episode 16.avi", 16)] [InlineData("Season 2/Episode 16 - Some Title.avi", 16)] @@ -73,6 +71,7 @@ namespace Jellyfin.Naming.Tests.TV //[InlineData("Season 4/Uchuu.Senkan.Yamato.2199.E03.avi", 3)] //[InlineData("Season 2/7 12 Angry Men.avi", 7)] //[InlineData("Season 02/02x03x04x15 - Ep Name.mp4", 2)] + //[InlineData("Season 2/[HorribleSubs] Hunter X Hunter - 136 [720p].mkv", 136)] public void GetEpisodeNumberFromFileTest(string path, int? expected) { var result = new EpisodePathParser(_namingOptions) @@ -80,13 +79,5 @@ namespace Jellyfin.Naming.Tests.TV Assert.Equal(expected, result.EpisodeNumber); } - - private int? GetEpisodeNumberFromFile(string path) - { - var result = new EpisodePathParser(_namingOptions) - .Parse(path, false); - - return result.EpisodeNumber; - } } } From b306b8b8815cb4b3e9f6c8f7987b281a31fc0455 Mon Sep 17 00:00:00 2001 From: Narfinger Date: Sun, 23 Feb 2020 18:46:10 +0900 Subject: [PATCH 036/144] add todos and fixes some todo tests --- .../Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs b/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs index a9e6d63432..d74afe38bc 100644 --- a/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs +++ b/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs @@ -65,13 +65,13 @@ namespace Jellyfin.Naming.Tests.TV [InlineData("Season 2/02.avi", 2)] [InlineData("Season 2/2. Infestation.avi", 2)] [InlineData("The Wonder Years/The.Wonder.Years.S04.PDTV.x264-JCH/The Wonder Years s04e07 Christmas Party NTSC PDTV.avi", 7)] - //[InlineData("Season 2/16 12 Some Title.avi", 16)] - //[InlineData("Running Man/Running Man S2017E368.mkv", 360)] - //[InlineData("/The.Legend.of.Condor.Heroes.2017.V2.web-dl.1080p.h264.aac-hdctv/The.Legend.of.Condor.Heroes.2017.E07.V2.web-dl.1080p.h264.aac-hdctv.mkv", 7)] - //[InlineData("Season 4/Uchuu.Senkan.Yamato.2199.E03.avi", 3)] - //[InlineData("Season 2/7 12 Angry Men.avi", 7)] - //[InlineData("Season 02/02x03x04x15 - Ep Name.mp4", 2)] - //[InlineData("Season 2/[HorribleSubs] Hunter X Hunter - 136 [720p].mkv", 136)] + //TODO: [InlineData("Season 2/16 12 Some Title.avi", 16)] + //TODO: [InlineData("Running Man/Running Man S2017E368.mkv", 368)] + //TODO: [InlineData("/The.Legend.of.Condor.Heroes.2017.V2.web-dl.1080p.h264.aac-hdctv/The.Legend.of.Condor.Heroes.2017.E07.V2.web-dl.1080p.h264.aac-hdctv.mkv", 7)] + //TODO: [InlineData("Season 4/Uchuu.Senkan.Yamato.2199.E03.avi", 3)] + //TODO: [InlineData("Season 2/7 12 Angry Men.avi", 7)] + //TODO: [InlineData("Season 02/02x03x04x15 - Ep Name.mp4", 2)] + //TODO: [InlineData("Season 2/[HorribleSubs] Hunter X Hunter - 136 [720p].mkv", 136)] public void GetEpisodeNumberFromFileTest(string path, int? expected) { var result = new EpisodePathParser(_namingOptions) From fd5f0c54a645d024d1decca7bf4b7da302882d67 Mon Sep 17 00:00:00 2001 From: Narfinger Date: Sun, 23 Feb 2020 18:50:33 +0900 Subject: [PATCH 037/144] fixes formatting and enabling another test --- .../Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs b/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs index d74afe38bc..5e023bdb06 100644 --- a/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs +++ b/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs @@ -65,13 +65,13 @@ namespace Jellyfin.Naming.Tests.TV [InlineData("Season 2/02.avi", 2)] [InlineData("Season 2/2. Infestation.avi", 2)] [InlineData("The Wonder Years/The.Wonder.Years.S04.PDTV.x264-JCH/The Wonder Years s04e07 Christmas Party NTSC PDTV.avi", 7)] - //TODO: [InlineData("Season 2/16 12 Some Title.avi", 16)] - //TODO: [InlineData("Running Man/Running Man S2017E368.mkv", 368)] - //TODO: [InlineData("/The.Legend.of.Condor.Heroes.2017.V2.web-dl.1080p.h264.aac-hdctv/The.Legend.of.Condor.Heroes.2017.E07.V2.web-dl.1080p.h264.aac-hdctv.mkv", 7)] - //TODO: [InlineData("Season 4/Uchuu.Senkan.Yamato.2199.E03.avi", 3)] - //TODO: [InlineData("Season 2/7 12 Angry Men.avi", 7)] - //TODO: [InlineData("Season 02/02x03x04x15 - Ep Name.mp4", 2)] - //TODO: [InlineData("Season 2/[HorribleSubs] Hunter X Hunter - 136 [720p].mkv", 136)] + [InlineData("Running Man/Running Man S2017E368.mkv", 368)] + // TODO: [InlineData("Season 2/16 12 Some Title.avi", 16)] + // TODO: [InlineData("/The.Legend.of.Condor.Heroes.2017.V2.web-dl.1080p.h264.aac-hdctv/The.Legend.of.Condor.Heroes.2017.E07.V2.web-dl.1080p.h264.aac-hdctv.mkv", 7)] + // TODO: [InlineData("Season 4/Uchuu.Senkan.Yamato.2199.E03.avi", 3)] + // TODO: [InlineData("Season 2/7 12 Angry Men.avi", 7)] + // TODO: [InlineData("Season 02/02x03x04x15 - Ep Name.mp4", 2)] + // TODO: [InlineData("Season 2/[HorribleSubs] Hunter X Hunter - 136 [720p].mkv", 136)] public void GetEpisodeNumberFromFileTest(string path, int? expected) { var result = new EpisodePathParser(_namingOptions) From 496bdc65f313744732b04079c5654af84a1c6d85 Mon Sep 17 00:00:00 2001 From: Narfinger Date: Sun, 23 Feb 2020 19:45:29 +0900 Subject: [PATCH 038/144] adds names from the episodenumber tests to path tests --- .../TV/EpisodePathParserTest.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/Jellyfin.Naming.Tests/TV/EpisodePathParserTest.cs b/tests/Jellyfin.Naming.Tests/TV/EpisodePathParserTest.cs index da6e993100..d959a50661 100644 --- a/tests/Jellyfin.Naming.Tests/TV/EpisodePathParserTest.cs +++ b/tests/Jellyfin.Naming.Tests/TV/EpisodePathParserTest.cs @@ -15,6 +15,24 @@ namespace Jellyfin.Naming.Tests.TV [InlineData("D:\\media\\Foo - S04E011", "Foo", 4, 11)] [InlineData("D:\\media\\Foo\\Foo s01x01", "Foo", 1, 1)] [InlineData("D:\\media\\Foo (2019)\\Season 4\\Foo (2019).S04E03", "Foo (2019)", 4, 3)] + [InlineData("/Season 2/Elementary - 02x03-04-15 - Ep Name.mp4", "Elementary", 2, 3)] + [InlineData("/Season 1/seriesname S01E02 blah.avi", "seriesname", 1, 2)] + [InlineData("/Running Man/Running Man S2017E368.mkv", "Running Man", 2017, 368)] + [InlineData("/Season 1/seriesname 01x02 blah.avi", "seriesname", 1, 2)] + [InlineData("/Season 25/The Simpsons.S25E09.Steal this episode.mp4", "The Simpsons", 25, 9)] + [InlineData("/Season 1/seriesname S01x02 blah.avi", "seriesname", 1, 2)] + [InlineData("/Season 2/Elementary - 02x03 - 02x04 - 02x15 - Ep Name.mp4", "Elementary", 2, 3)] + [InlineData("/Season 1/seriesname S01xE02 blah.avi", "seriesname", 1, 2)] + [InlineData("/Season 02/Elementary - 02x03 - x04 - x15 - Ep Name.mp4", "Elementary", 2, 3)] + [InlineData("/Season 02/Elementary - 02x03x04x15 - Ep Name.mp4", "Elementary", 2, 3)] + [InlineData("/Season 02/Elementary - 02x03-E15 - Ep Name.mp4", "Elementary", 2, 3)] + [InlineData("/Season 1/Elementary - S01E23-E24-E26 - The Woman.mp4", "Elementary", 1, 23)] + [InlineData("/The Wonder Years/The.Wonder.Years.S04.PDTV.x264-JCH/The Wonder Years s04e07 Christmas Party NTSC PDTV.avi", "The Wonder Years", 4, 7)] + // TODO: [InlineData("/Castle Rock 2x01 Que el rio siga su curso [WEB-DL HULU 1080p h264 Dual DD5.1 Subs].mkv", "Castle Rock", 2, 1)] + // TODO: [InlineData("/After Life 1x06 Episodio 6 [WEB-DL NF 1080p h264 Dual DD 5.1 Sub].mkv", "After Life", 1, 6)] + // TODO: [InlineData("/Season 4/Uchuu.Senkan.Yamato.2199.E03.avi", "Uchuu Senkan Yamoto 2199", 4, 3)] + // TODO: [InlineData("The Daily Show/The Daily Show 25x22 - [WEBDL-720p][AAC 2.0][x264] Noah Baumbach-TBS.mkv", "The Daily Show", 25, 22)] + // TODO: [InlineData("Watchmen (2019)/Watchmen 1x03 [WEBDL-720p][EAC3 5.1][h264][-TBS] - She Was Killed by Space Junk.mkv", "Watchmen (2019)", 1, 3)] public void ParseEpisodesCorrectly(string path, string name, int season, int episode) { NamingOptions o = new NamingOptions(); @@ -32,6 +50,7 @@ namespace Jellyfin.Naming.Tests.TV [InlineData("/media/Foo/[Bar] Foo Baz - 11 [1080p]", "Foo Baz", 11)] [InlineData("D:\\media\\Foo\\Foo 889", "Foo", 889)] [InlineData("D:\\media\\Foo\\[Bar] Foo Baz - 11 [1080p]", "Foo Baz", 11)] + // TODO: [InlineData("/The.Legend.of.Condor.Heroes.2017.V2.web-dl.1080p.h264.aac-hdctv/The.Legend.of.Condor.Heroes.2017.E07.V2.web-dl.1080p.h264.aac-hdctv.mkv", "The Legend of Condor Heroes 2017", 1, 7)] public void ParseEpisodeWithoutSeason(string path, string name, int episode) { NamingOptions o = new NamingOptions(); From a34826008f001fcfee9dabde4e439a0d83e821ea Mon Sep 17 00:00:00 2001 From: dkanada Date: Mon, 24 Feb 2020 00:22:23 +0900 Subject: [PATCH 039/144] update external ids --- MediaBrowser.Providers/Music/MusicExternalIds.cs | 1 - .../MusicBrainz/{MusicBrainzExternalIds.cs => ExternalIds.cs} | 1 - 2 files changed, 2 deletions(-) rename MediaBrowser.Providers/Plugins/MusicBrainz/{MusicBrainzExternalIds.cs => ExternalIds.cs} (98%) diff --git a/MediaBrowser.Providers/Music/MusicExternalIds.cs b/MediaBrowser.Providers/Music/MusicExternalIds.cs index b431c1e41f..628b9a9a10 100644 --- a/MediaBrowser.Providers/Music/MusicExternalIds.cs +++ b/MediaBrowser.Providers/Music/MusicExternalIds.cs @@ -1,5 +1,4 @@ using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzExternalIds.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/ExternalIds.cs similarity index 98% rename from MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzExternalIds.cs rename to MediaBrowser.Providers/Plugins/MusicBrainz/ExternalIds.cs index 0184d42410..fd85465f37 100644 --- a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzExternalIds.cs +++ b/MediaBrowser.Providers/Plugins/MusicBrainz/ExternalIds.cs @@ -1,4 +1,3 @@ -using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; From 19a609a88980b2ac21050eeb991e2abb55675e9c Mon Sep 17 00:00:00 2001 From: dkanada Date: Mon, 24 Feb 2020 00:24:03 +0900 Subject: [PATCH 040/144] update musicbrainz options --- .../MusicBrainz/Configuration/PluginConfiguration.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/PluginConfiguration.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/PluginConfiguration.cs index 6bce0b4478..607ba0cbcb 100644 --- a/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/PluginConfiguration.cs +++ b/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/PluginConfiguration.cs @@ -5,12 +5,12 @@ namespace MediaBrowser.Providers.Plugins.MusicBrainz { public class PluginConfiguration : BasePluginConfiguration { - public bool Enable { get; set; } = false; - - public bool ReplaceArtistName { get; set; } = false; - public string Server { get; set; } = "https://www.musicbrainz.org"; public long RateLimit { get; set; } = 1000u; + + public bool Enable { get; set; } + + public bool ReplaceArtistName { get; set; } } } From 940990708e3df623056cc790351e6d5d52af5a0f Mon Sep 17 00:00:00 2001 From: dkanada Date: Mon, 24 Feb 2020 00:25:27 +0900 Subject: [PATCH 041/144] remove unused assignment --- MediaBrowser.Providers/Plugins/MusicBrainz/AlbumProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/AlbumProvider.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/AlbumProvider.cs index 0fddb05579..748c23216c 100644 --- a/MediaBrowser.Providers/Plugins/MusicBrainz/AlbumProvider.cs +++ b/MediaBrowser.Providers/Plugins/MusicBrainz/AlbumProvider.cs @@ -28,7 +28,7 @@ namespace MediaBrowser.Providers.Music /// Be prudent, use a value slightly above the minimun required. /// https://musicbrainz.org/doc/XML_Web_Service/Rate_Limiting ///
- private readonly long _musicBrainzQueryIntervalMs = 1050u; + private readonly long _musicBrainzQueryIntervalMs; /// /// For each single MB lookup/search, this is the maximum number of From 6b634026e6208ffb04c8fe55382449aee97089a6 Mon Sep 17 00:00:00 2001 From: Daniel De Jesus Date: Sun, 23 Feb 2020 03:40:44 +0000 Subject: [PATCH 042/144] Translated using Weblate (Spanish (Dominican Republic)) Translation: Jellyfin/Jellyfin Translate-URL: http://translate.jellyfin.org/projects/jellyfin/jellyfin-core/es_DO/ --- Emby.Server.Implementations/Localization/Core/es_DO.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/es_DO.json b/Emby.Server.Implementations/Localization/Core/es_DO.json index 1ac8d3ae40..1a7b57c534 100644 --- a/Emby.Server.Implementations/Localization/Core/es_DO.json +++ b/Emby.Server.Implementations/Localization/Core/es_DO.json @@ -10,5 +10,12 @@ "CameraImageUploadedFrom": "Se ha subido una nueva imagen de cámara desde {0}", "AuthenticationSucceededWithUserName": "{0} autenticado con éxito", "Application": "Aplicación", - "AppDeviceValues": "App: {0}, Dispositivo: {1}" + "AppDeviceValues": "App: {0}, Dispositivo: {1}", + "HeaderContinueWatching": "Continuar Viendo", + "HeaderCameraUploads": "Subidas de Cámara", + "HeaderAlbumArtists": "Artistas del Álbum", + "Genres": "Géneros", + "Folders": "Carpetas", + "Favorites": "Favoritos", + "FailedLoginAttemptWithUserName": "Intento de inicio de sesión fallido de {0}" } From 3e2f09ecf80fafa1342d26785e1e245653e6ece6 Mon Sep 17 00:00:00 2001 From: R0flcopt3r Date: Fri, 21 Feb 2020 19:30:05 +0000 Subject: [PATCH 043/144] Translated using Weblate (Norwegian Nynorsk) Translation: Jellyfin/Jellyfin Translate-URL: http://translate.jellyfin.org/projects/jellyfin/jellyfin-core/nn/ --- .../Localization/Core/nn.json | 41 ++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/nn.json b/Emby.Server.Implementations/Localization/Core/nn.json index 0967ef424b..ec6da213f5 100644 --- a/Emby.Server.Implementations/Localization/Core/nn.json +++ b/Emby.Server.Implementations/Localization/Core/nn.json @@ -1 +1,40 @@ -{} +{ + "MessageServerConfigurationUpdated": "Tenar konfigurasjonen har blitt oppdatert", + "MessageNamedServerConfigurationUpdatedWithValue": "Tenar konfigurasjon seksjon {0} har blitt oppdatert", + "MessageApplicationUpdatedTo": "Jellyfin Tenaren har blitt oppdatert til {0}", + "MessageApplicationUpdated": "Jellyfin Tenaren har blitt oppdatert", + "Latest": "Nyaste", + "LabelRunningTimeValue": "Speletid: {0}", + "LabelIpAddressValue": "IP adresse: {0}", + "ItemRemovedWithName": "{0} vart fjerna frå biblioteket", + "ItemAddedWithName": "{0} vart lagt til i biblioteket", + "Inherit": "Arv", + "HomeVideos": "Heime Videoar", + "HeaderRecordingGroups": "Innspelingsgrupper", + "HeaderNextUp": "Neste", + "HeaderLiveTV": "Direkte TV", + "HeaderFavoriteSongs": "Favoritt Songar", + "HeaderFavoriteShows": "Favoritt Seriar", + "HeaderFavoriteEpisodes": "Favoritt Episodar", + "HeaderFavoriteArtists": "Favoritt Artistar", + "HeaderFavoriteAlbums": "Favoritt Album", + "HeaderContinueWatching": "Fortsett å sjå", + "HeaderCameraUploads": "Kamera Opplastingar", + "HeaderAlbumArtists": "Album Artist", + "Genres": "Sjangrar", + "Folders": "Mapper", + "Favorites": "Favorittar", + "FailedLoginAttemptWithUserName": "Mislukka påloggingsforsøk frå {0}", + "DeviceOnlineWithName": "{0} er tilkopla", + "DeviceOfflineWithName": "{0} har kopla frå", + "Collections": "Samlingar", + "ChapterNameValue": "Kapittel {0}", + "Channels": "Kanalar", + "CameraImageUploadedFrom": "Eit nytt kamera bilete har blitt lasta opp frå {0}", + "Books": "Bøker", + "AuthenticationSucceededWithUserName": "{0} Har logga inn", + "Artists": "Artistar", + "Application": "Program", + "AppDeviceValues": "App: {0}, Einheit: {1}", + "Albums": "Album" +} From b40ac479a21f669f4c43c6dff40c4876c7b9a111 Mon Sep 17 00:00:00 2001 From: Andreas Olsson Date: Sat, 22 Feb 2020 09:55:44 +0000 Subject: [PATCH 044/144] Translated using Weblate (Swedish) Translation: Jellyfin/Jellyfin Translate-URL: http://translate.jellyfin.org/projects/jellyfin/jellyfin-core/sv/ --- Emby.Server.Implementations/Localization/Core/sv.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/sv.json b/Emby.Server.Implementations/Localization/Core/sv.json index 744b0e2d39..db4cfde957 100644 --- a/Emby.Server.Implementations/Localization/Core/sv.json +++ b/Emby.Server.Implementations/Localization/Core/sv.json @@ -17,14 +17,14 @@ "Genres": "Genrer", "HeaderAlbumArtists": "Albumartister", "HeaderCameraUploads": "Kamera Uppladdningar", - "HeaderContinueWatching": "Fortsätt kolla på", + "HeaderContinueWatching": "Fortsätt kolla", "HeaderFavoriteAlbums": "Favoritalbum", "HeaderFavoriteArtists": "Favoritartister", "HeaderFavoriteEpisodes": "Favoritavsnitt", "HeaderFavoriteShows": "Favoritserier", "HeaderFavoriteSongs": "Favoritlåtar", "HeaderLiveTV": "Live-TV", - "HeaderNextUp": "Nästa på tur", + "HeaderNextUp": "Nästa", "HeaderRecordingGroups": "Inspelningsgrupper", "HomeVideos": "Hemvideor", "Inherit": "Ärv", From 4ae80a5d56f291a0865dddc5393cf1e3c4b9277b Mon Sep 17 00:00:00 2001 From: dkanada Date: Mon, 24 Feb 2020 14:35:30 +0900 Subject: [PATCH 045/144] partially fix issue with music scans --- MediaBrowser.Model/Configuration/ServerConfiguration.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index 5280d455c4..bf10108b8c 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -238,7 +238,7 @@ namespace MediaBrowser.Model.Configuration CodecsUsed = Array.Empty(); PathSubstitutions = Array.Empty(); IgnoreVirtualInterfaces = false; - EnableSimpleArtistDetection = true; + EnableSimpleArtistDetection = false; DisplaySpecialsWithinSeasons = true; EnableExternalContentInSuggestions = true; From f11678ae4b1d3294c467ee62b16f4b2bc4a64e0d Mon Sep 17 00:00:00 2001 From: MOLOKAL Date: Tue, 25 Feb 2020 06:39:43 +0000 Subject: [PATCH 046/144] Translated using Weblate (Malay) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ms/ --- Emby.Server.Implementations/Localization/Core/ms.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/ms.json b/Emby.Server.Implementations/Localization/Core/ms.json index b91c98ca0c..1d86257f84 100644 --- a/Emby.Server.Implementations/Localization/Core/ms.json +++ b/Emby.Server.Implementations/Localization/Core/ms.json @@ -1,9 +1,9 @@ { "Albums": "Album-album", - "AppDeviceValues": "App: {0}, Device: {1}", + "AppDeviceValues": "Apl: {0}, Peranti: {1}", "Application": "Aplikasi", - "Artists": "Artis-artis", - "AuthenticationSucceededWithUserName": "{0} successfully authenticated", + "Artists": "Artis", + "AuthenticationSucceededWithUserName": "{0} berjaya disahkan", "Books": "Buku-buku", "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}", "Channels": "Saluran", @@ -30,7 +30,7 @@ "Inherit": "Inherit", "ItemAddedWithName": "{0} was added to the library", "ItemRemovedWithName": "{0} was removed from the library", - "LabelIpAddressValue": "Ip address: {0}", + "LabelIpAddressValue": "Alamat IP: {0}", "LabelRunningTimeValue": "Running time: {0}", "Latest": "Latest", "MessageApplicationUpdated": "Jellyfin Server has been updated", @@ -50,7 +50,7 @@ "NotificationOptionAudioPlayback": "Audio playback started", "NotificationOptionAudioPlaybackStopped": "Audio playback stopped", "NotificationOptionCameraImageUploaded": "Camera image uploaded", - "NotificationOptionInstallationFailed": "Installation failure", + "NotificationOptionInstallationFailed": "Pemasangan gagal", "NotificationOptionNewLibraryContent": "New content added", "NotificationOptionPluginError": "Plugin failure", "NotificationOptionPluginInstalled": "Plugin installed", From 6c6b5d7f280b0d29bf5083387cac4897588e7ef6 Mon Sep 17 00:00:00 2001 From: sharkykh Date: Tue, 25 Feb 2020 23:17:52 +0000 Subject: [PATCH 047/144] Translated using Weblate (Hebrew) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/he/ --- .../Localization/Core/he.json | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/he.json b/Emby.Server.Implementations/Localization/Core/he.json index b08c8966e2..5ef59797d2 100644 --- a/Emby.Server.Implementations/Localization/Core/he.json +++ b/Emby.Server.Implementations/Localization/Core/he.json @@ -28,7 +28,7 @@ "HeaderRecordingGroups": "קבוצות הקלטה", "HomeVideos": "סרטונים בייתים", "Inherit": "הורש", - "ItemAddedWithName": "{0} was added to the library", + "ItemAddedWithName": "{0} הוסף לספרייה", "ItemRemovedWithName": "{0} נמחק מהספרייה", "LabelIpAddressValue": "Ip כתובת: {0}", "LabelRunningTimeValue": "משך צפייה: {0}", @@ -36,15 +36,15 @@ "MessageApplicationUpdated": "שרת הJellyfin עודכן", "MessageApplicationUpdatedTo": "שרת הJellyfin עודכן לגרסא {0}", "MessageNamedServerConfigurationUpdatedWithValue": "הגדרת השרת {0} שונתה", - "MessageServerConfigurationUpdated": "Server configuration has been updated", + "MessageServerConfigurationUpdated": "תצורת השרת עודכנה", "MixedContent": "תוכן מעורב", "Movies": "סרטים", "Music": "מוזיקה", "MusicVideos": "Music videos", "NameInstallFailed": "{0} installation failed", - "NameSeasonNumber": "Season {0}", - "NameSeasonUnknown": "Season Unknown", - "NewVersionIsAvailable": "A new version of Jellyfin Server is available for download.", + "NameSeasonNumber": "עונה {0}", + "NameSeasonUnknown": "עונה לא ידועה", + "NewVersionIsAvailable": "גרסה חדשה של שרת Jellyfin זמינה להורדה.", "NotificationOptionApplicationUpdateAvailable": "Application update available", "NotificationOptionApplicationUpdateInstalled": "Application update installed", "NotificationOptionAudioPlayback": "Audio playback started", @@ -53,10 +53,10 @@ "NotificationOptionInstallationFailed": "התקנה נכשלה", "NotificationOptionNewLibraryContent": "New content added", "NotificationOptionPluginError": "Plugin failure", - "NotificationOptionPluginInstalled": "Plugin installed", - "NotificationOptionPluginUninstalled": "Plugin uninstalled", - "NotificationOptionPluginUpdateInstalled": "Plugin update installed", - "NotificationOptionServerRestartRequired": "Server restart required", + "NotificationOptionPluginInstalled": "התוסף הותקן", + "NotificationOptionPluginUninstalled": "התוסף הוסר", + "NotificationOptionPluginUpdateInstalled": "העדכון לתוסף הותקן", + "NotificationOptionServerRestartRequired": "יש לאתחל את השרת", "NotificationOptionTaskFailed": "Scheduled task failure", "NotificationOptionUserLockedOut": "User locked out", "NotificationOptionVideoPlayback": "Video playback started", @@ -71,15 +71,15 @@ "ScheduledTaskFailedWithName": "{0} failed", "ScheduledTaskStartedWithName": "{0} started", "ServerNameNeedsToBeRestarted": "{0} needs to be restarted", - "Shows": "Shows", - "Songs": "Songs", - "StartupEmbyServerIsLoading": "Jellyfin Server is loading. Please try again shortly.", + "Shows": "סדרות", + "Songs": "שירים", + "StartupEmbyServerIsLoading": "שרת Jellyfin בהליכי טעינה. אנא נסה שנית בעוד זמן קצר.", "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}", "SubtitlesDownloadedForItem": "Subtitles downloaded for {0}", "Sync": "סנכרן", "System": "System", - "TvShows": "TV Shows", + "TvShows": "סדרות טלוויזיה", "User": "User", "UserCreatedWithName": "User {0} has been created", "UserDeletedWithName": "User {0} has been deleted", From 9d53fd0e73c70cd275e1a9b958c368b9efd01b76 Mon Sep 17 00:00:00 2001 From: sharkykh Date: Tue, 25 Feb 2020 23:29:27 +0000 Subject: [PATCH 048/144] Translated using Weblate (Hebrew) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/he/ --- Emby.Server.Implementations/Localization/Core/he.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/he.json b/Emby.Server.Implementations/Localization/Core/he.json index 5ef59797d2..f3a7794dae 100644 --- a/Emby.Server.Implementations/Localization/Core/he.json +++ b/Emby.Server.Implementations/Localization/Core/he.json @@ -81,9 +81,9 @@ "System": "System", "TvShows": "סדרות טלוויזיה", "User": "User", - "UserCreatedWithName": "User {0} has been created", - "UserDeletedWithName": "User {0} has been deleted", - "UserDownloadingItemWithValues": "{0} is downloading {1}", + "UserCreatedWithName": "המשתמש {0} נוצר", + "UserDeletedWithName": "המשתמש {0} הוסר", + "UserDownloadingItemWithValues": "{0} מוריד את {1}", "UserLockedOutWithName": "User {0} has been locked out", "UserOfflineFromDevice": "{0} has disconnected from {1}", "UserOnlineFromDevice": "{0} is online from {1}", From 107974e3f8ad1c4461bd289ef9cf4d5fb1e619df Mon Sep 17 00:00:00 2001 From: Narfinger Date: Thu, 27 Feb 2020 11:31:57 +0900 Subject: [PATCH 049/144] moves shows tests to Theory and InlineData format --- .../TV/AbsoluteEpisodeNumberTests.cs | 54 +-- .../TV/DailyEpisodeTests.cs | 53 +-- .../TV/EpisodeNumberWithoutSeasonTests.cs | 127 +------ .../TV/EpisodePathParserTest.cs | 20 +- .../TV/EpisodeWithoutSeasonTests.cs | 43 +-- .../TV/MultiEpisodeTests.cs | 153 ++++---- .../TV/SeasonFolderTests.cs | 116 +----- .../TV/SeasonNumberTests.cs | 338 +++--------------- .../TV/SimpleEpisodeTests.cs | 94 +---- 9 files changed, 191 insertions(+), 807 deletions(-) diff --git a/tests/Jellyfin.Naming.Tests/TV/AbsoluteEpisodeNumberTests.cs b/tests/Jellyfin.Naming.Tests/TV/AbsoluteEpisodeNumberTests.cs index 9abbcc7bf0..553d06681b 100644 --- a/tests/Jellyfin.Naming.Tests/TV/AbsoluteEpisodeNumberTests.cs +++ b/tests/Jellyfin.Naming.Tests/TV/AbsoluteEpisodeNumberTests.cs @@ -6,56 +6,22 @@ namespace Jellyfin.Naming.Tests.TV { public class AbsoluteEpisodeNumberTests { - [Fact] - public void TestAbsoluteEpisodeNumber1() - { - Assert.Equal(12, GetEpisodeNumberFromFile(@"The Simpsons/12.avi")); - } - - [Fact] - public void TestAbsoluteEpisodeNumber2() - { - Assert.Equal(12, GetEpisodeNumberFromFile(@"The Simpsons/The Simpsons 12.avi")); - } - - [Fact] - public void TestAbsoluteEpisodeNumber3() - { - Assert.Equal(82, GetEpisodeNumberFromFile(@"The Simpsons/The Simpsons 82.avi")); - } - - [Fact] - public void TestAbsoluteEpisodeNumber4() - { - Assert.Equal(112, GetEpisodeNumberFromFile(@"The Simpsons/The Simpsons 112.avi")); - } - - [Fact] - public void TestAbsoluteEpisodeNumber5() - { - Assert.Equal(2, GetEpisodeNumberFromFile(@"The Simpsons/Foo_ep_02.avi")); - } - - [Fact] - public void TestAbsoluteEpisodeNumber6() - { - Assert.Equal(889, GetEpisodeNumberFromFile(@"The Simpsons/The Simpsons 889.avi")); - } - - [Fact] - public void TestAbsoluteEpisodeNumber7() - { - Assert.Equal(101, GetEpisodeNumberFromFile(@"The Simpsons/The Simpsons 101.avi")); - } - - private int? GetEpisodeNumberFromFile(string path) + [Theory] + [InlineData("The Simpsons/12.avi", 12)] + [InlineData("The Simpsons/The Simpsons 12.avi", 12)] + [InlineData("The Simpsons/The Simpsons 82.avi", 82)] + [InlineData("The Simpsons/The Simpsons 112.avi", 112)] + [InlineData("The Simpsons/Foo_ep_02.avi", 2)] + [InlineData("The Simpsons/The Simpsons 889.avi", 889)] + [InlineData("The Simpsons/The Simpsons 101.avi", 101)] + public void GetEpisodeNumberFromFileTest(string path, int episodeNumber) { var options = new NamingOptions(); var result = new EpisodeResolver(options) .Resolve(path, false, null, null, true); - return result.EpisodeNumber; + Assert.Equal(episodeNumber, result.EpisodeNumber); } } } diff --git a/tests/Jellyfin.Naming.Tests/TV/DailyEpisodeTests.cs b/tests/Jellyfin.Naming.Tests/TV/DailyEpisodeTests.cs index 29daf8cc37..6ecffe80b7 100644 --- a/tests/Jellyfin.Naming.Tests/TV/DailyEpisodeTests.cs +++ b/tests/Jellyfin.Naming.Tests/TV/DailyEpisodeTests.cs @@ -6,52 +6,17 @@ namespace Jellyfin.Naming.Tests.TV { public class DailyEpisodeTests { - [Fact] - public void TestDailyEpisode1() - { - Test(@"/server/anything_1996.11.14.mp4", "anything", 1996, 11, 14); - } - [Fact] - public void TestDailyEpisode2() - { - Test(@"/server/anything_1996-11-14.mp4", "anything", 1996, 11, 14); - } - // FIXME - // [Fact] - public void TestDailyEpisode3() - { - Test(@"/server/anything_14.11.1996.mp4", "anything", 1996, 11, 14); - } - - // FIXME - // [Fact] - public void TestDailyEpisode4() - { - Test(@"/server/A Daily Show - (2015-01-15) - Episode Name - [720p].mkv", "A Daily Show", 2015, 01, 15); - } - - [Fact] - public void TestDailyEpisode5() - { - Test(@"/server/james.corden.2017.04.20.anne.hathaway.720p.hdtv.x264-crooks.mkv", "james.corden", 2017, 04, 20); - } - - [Fact] - public void TestDailyEpisode6() - { - Test(@"/server/ABC News 2018_03_24_19_00_00.mkv", "ABC News", 2018, 03, 24); - } - - // FIXME - // [Fact] - public void TestDailyEpisode7() - { - Test(@"/server/Last Man Standing_KTLADT_2018_05_25_01_28_00.wtv", "Last Man Standing", 2018, 05, 25); - } - - private void Test(string path, string seriesName, int? year, int? month, int? day) + [Theory] + [InlineData(@"/server/anything_1996.11.14.mp4", "anything", 1996, 11, 14)] + [InlineData(@"/server/anything_1996-11-14.mp4", "anything", 1996, 11, 14)] + [InlineData(@"/server/james.corden.2017.04.20.anne.hathaway.720p.hdtv.x264-crooks.mkv", "james.corden", 2017, 04, 20)] + [InlineData(@"/server/ABC News 2018_03_24_19_00_00.mkv", "ABC News", 2018, 03, 24)] + // TODO: [InlineData(@"/server/anything_14.11.1996.mp4", "anything", 1996, 11, 14)] + // TODO: [InlineData(@"/server/A Daily Show - (2015-01-15) - Episode Name - [720p].mkv", "A Daily Show", 2015, 01, 15)] + // TODO: [InlineData(@"/server/Last Man Standing_KTLADT_2018_05_25_01_28_00.wtv", "Last Man Standing", 2018, 05, 25)] + public void Test(string path, string seriesName, int? year, int? month, int? day) { var options = new NamingOptions(); diff --git a/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberWithoutSeasonTests.cs b/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberWithoutSeasonTests.cs index 00aa9ee7c2..0c7d9520e2 100644 --- a/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberWithoutSeasonTests.cs +++ b/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberWithoutSeasonTests.cs @@ -6,122 +6,31 @@ namespace Jellyfin.Naming.Tests.TV { public class EpisodeNumberWithoutSeasonTests { - [Fact] - public void TestEpisodeNumberWithoutSeason1() - { - Assert.Equal(8, GetEpisodeNumberFromFile(@"The Simpsons/The Simpsons.S25E08.Steal this episode.mp4")); - } - [Fact] - public void TestEpisodeNumberWithoutSeason2() - { - Assert.Equal(2, GetEpisodeNumberFromFile(@"The Simpsons/The Simpsons - 02 - Ep Name.avi")); - } - - [Fact] - public void TestEpisodeNumberWithoutSeason3() - { - Assert.Equal(2, GetEpisodeNumberFromFile(@"The Simpsons/02.avi")); - } - - [Fact] - public void TestEpisodeNumberWithoutSeason4() - { - Assert.Equal(2, GetEpisodeNumberFromFile(@"The Simpsons/02 - Ep Name.avi")); - } - - [Fact] - public void TestEpisodeNumberWithoutSeason5() - { - Assert.Equal(2, GetEpisodeNumberFromFile(@"The Simpsons/02-Ep Name.avi")); - } - - [Fact] - public void TestEpisodeNumberWithoutSeason6() - { - Assert.Equal(2, GetEpisodeNumberFromFile(@"The Simpsons/02.EpName.avi")); - } - - [Fact] - public void TestEpisodeNumberWithoutSeason7() - { - Assert.Equal(2, GetEpisodeNumberFromFile(@"The Simpsons/The Simpsons - 02.avi")); - } - - [Fact] - public void TestEpisodeNumberWithoutSeason8() - { - Assert.Equal(2, GetEpisodeNumberFromFile(@"The Simpsons/The Simpsons - 02 Ep Name.avi")); - } - - // FIXME - // [Fact] - public void TestEpisodeNumberWithoutSeason9() - { - Assert.Equal(2, GetEpisodeNumberFromFile(@"The Simpsons/The Simpsons 5 - 02 - Ep Name.avi")); - } - - // FIXME - // [Fact] - public void TestEpisodeNumberWithoutSeason10() - { - Assert.Equal(2, GetEpisodeNumberFromFile(@"The Simpsons/The Simpsons 5 - 02 Ep Name.avi")); - } - - // FIXME - // [Fact] - public void TestEpisodeNumberWithoutSeason11() - { - Assert.Equal(7, GetEpisodeNumberFromFile(@"Seinfeld/Seinfeld 0807 The Checks.avi")); - Assert.Equal(8, GetSeasonNumberFromFile(@"Seinfeld/Seinfeld 0807 The Checks.avi")); - } - - [Fact] - public void TestEpisodeNumberWithoutSeason12() - { - Assert.Equal(7, GetEpisodeNumberFromFile(@"GJ Club (2013)/GJ Club - 07.mkv")); - } - - // FIXME - // [Fact] - public void TestEpisodeNumberWithoutSeason13() - { - // This is not supported anymore after removing the episode number 365+ hack from EpisodePathParser - Assert.Equal(13, GetEpisodeNumberFromFile(@"Case Closed (1996-2007)/Case Closed - 13.mkv")); - } - - [Fact] - public void TestEpisodeNumberWithoutSeason14() - { - Assert.Equal(3, GetSeasonNumberFromFile(@"Case Closed (1996-2007)/Case Closed - 317.mkv")); - Assert.Equal(17, GetEpisodeNumberFromFile(@"Case Closed (1996-2007)/Case Closed - 317.mkv")); - } - - [Fact] - public void TestEpisodeNumberWithoutSeason15() - { - Assert.Equal(2017, GetSeasonNumberFromFile(@"Running Man/Running Man S2017E368.mkv")); - } - - private int? GetEpisodeNumberFromFile(string path) + [Theory] + [InlineData(8, @"The Simpsons/The Simpsons.S25E08.Steal this episode.mp4")] + [InlineData(2, @"The Simpsons/The Simpsons - 02 - Ep Name.avi")] + [InlineData(2, @"The Simpsons/02.avi")] + [InlineData(2, @"The Simpsons/02 - Ep Name.avi")] + [InlineData(2, @"The Simpsons/02-Ep Name.avi")] + [InlineData(2, @"The Simpsons/02.EpName.avi")] + [InlineData(2, @"The Simpsons/The Simpsons - 02.avi")] + [InlineData(2, @"The Simpsons/The Simpsons - 02 Ep Name.avi")] + [InlineData(7, @"GJ Club (2013)/GJ Club - 07.mkv")] + [InlineData(17, @"Case Closed (1996-2007)/Case Closed - 317.mkv")] + // TODO: [InlineData(2, @"The Simpsons/The Simpsons 5 - 02 - Ep Name.avi")] + // TODO: [InlineData(2, @"The Simpsons/The Simpsons 5 - 02 Ep Name.avi")] + // TODO: [InlineData(7, @"Seinfeld/Seinfeld 0807 The Checks.avi")] + // This is not supported anymore after removing the episode number 365+ hack from EpisodePathParser + // TODO: [InlineData(13, @"Case Closed (1996-2007)/Case Closed - 13.mkv")] + public void GetEpisodeNumberFromFileTest(int episodeNumber, string path) { var options = new NamingOptions(); var result = new EpisodeResolver(options) .Resolve(path, false); - return result.EpisodeNumber; + Assert.Equal(episodeNumber, result.EpisodeNumber); } - - private int? GetSeasonNumberFromFile(string path) - { - var options = new NamingOptions(); - - var result = new EpisodeResolver(options) - .Resolve(path, false); - - return result.SeasonNumber; - } - } } diff --git a/tests/Jellyfin.Naming.Tests/TV/EpisodePathParserTest.cs b/tests/Jellyfin.Naming.Tests/TV/EpisodePathParserTest.cs index d959a50661..4b56067153 100644 --- a/tests/Jellyfin.Naming.Tests/TV/EpisodePathParserTest.cs +++ b/tests/Jellyfin.Naming.Tests/TV/EpisodePathParserTest.cs @@ -33,7 +33,9 @@ namespace Jellyfin.Naming.Tests.TV // TODO: [InlineData("/Season 4/Uchuu.Senkan.Yamato.2199.E03.avi", "Uchuu Senkan Yamoto 2199", 4, 3)] // TODO: [InlineData("The Daily Show/The Daily Show 25x22 - [WEBDL-720p][AAC 2.0][x264] Noah Baumbach-TBS.mkv", "The Daily Show", 25, 22)] // TODO: [InlineData("Watchmen (2019)/Watchmen 1x03 [WEBDL-720p][EAC3 5.1][h264][-TBS] - She Was Killed by Space Junk.mkv", "Watchmen (2019)", 1, 3)] + // TODO: [InlineData("/The.Legend.of.Condor.Heroes.2017.V2.web-dl.1080p.h264.aac-hdctv/The.Legend.of.Condor.Heroes.2017.E07.V2.web-dl.1080p.h264.aac-hdctv.mkv", "The Legend of Condor Heroes 2017", 1, 7)] public void ParseEpisodesCorrectly(string path, string name, int season, int episode) + { NamingOptions o = new NamingOptions(); EpisodePathParser p = new EpisodePathParser(o); @@ -44,23 +46,5 @@ namespace Jellyfin.Naming.Tests.TV Assert.Equal(season, res.SeasonNumber); Assert.Equal(episode, res.EpisodeNumber); } - - [Theory] - [InlineData("/media/Foo/Foo 889", "Foo", 889)] - [InlineData("/media/Foo/[Bar] Foo Baz - 11 [1080p]", "Foo Baz", 11)] - [InlineData("D:\\media\\Foo\\Foo 889", "Foo", 889)] - [InlineData("D:\\media\\Foo\\[Bar] Foo Baz - 11 [1080p]", "Foo Baz", 11)] - // TODO: [InlineData("/The.Legend.of.Condor.Heroes.2017.V2.web-dl.1080p.h264.aac-hdctv/The.Legend.of.Condor.Heroes.2017.E07.V2.web-dl.1080p.h264.aac-hdctv.mkv", "The Legend of Condor Heroes 2017", 1, 7)] - public void ParseEpisodeWithoutSeason(string path, string name, int episode) - { - NamingOptions o = new NamingOptions(); - EpisodePathParser p = new EpisodePathParser(o); - var res = p.Parse(path, true, fillExtendedInfo: true); - - Assert.True(res.Success); - Assert.Equal(name, res.SeriesName); - Assert.Null(res.SeasonNumber); - Assert.Equal(episode, res.EpisodeNumber); - } } } diff --git a/tests/Jellyfin.Naming.Tests/TV/EpisodeWithoutSeasonTests.cs b/tests/Jellyfin.Naming.Tests/TV/EpisodeWithoutSeasonTests.cs index c2851ccdb1..364eb7ff85 100644 --- a/tests/Jellyfin.Naming.Tests/TV/EpisodeWithoutSeasonTests.cs +++ b/tests/Jellyfin.Naming.Tests/TV/EpisodeWithoutSeasonTests.cs @@ -6,42 +6,13 @@ namespace Jellyfin.Naming.Tests.TV { public class EpisodeWithoutSeasonTests { - // FIXME - // [Fact] - public void TestWithoutSeason1() - { - Test(@"/server/anything_ep02.mp4", "anything", null, 2); - } - - // FIXME - // [Fact] - public void TestWithoutSeason2() - { - Test(@"/server/anything_ep_02.mp4", "anything", null, 2); - } - - // FIXME - // [Fact] - public void TestWithoutSeason3() - { - Test(@"/server/anything_part.II.mp4", "anything", null, null); - } - - // FIXME - // [Fact] - public void TestWithoutSeason4() - { - Test(@"/server/anything_pt.II.mp4", "anything", null, null); - } - - // FIXME - // [Fact] - public void TestWithoutSeason5() - { - Test(@"/server/anything_pt_II.mp4", "anything", null, null); - } - - private void Test(string path, string seriesName, int? seasonNumber, int? episodeNumber) + // TODO: [Theory] + // TODO: [InlineData(@"/server/anything_ep02.mp4", "anything", null, 2)] + // TODO: [InlineData(@"/server/anything_ep_02.mp4", "anything", null, 2)] + // TODO: [InlineData(@"/server/anything_part.II.mp4", "anything", null, null)] + // TODO: [InlineData(@"/server/anything_pt.II.mp4", "anything", null, null)] + // TODO: [InlineData(@"/server/anything_pt_II.mp4", "anything", null, null)] + public void Test(string path, string seriesName, int? seasonNumber, int? episodeNumber) { var options = new NamingOptions(); diff --git a/tests/Jellyfin.Naming.Tests/TV/MultiEpisodeTests.cs b/tests/Jellyfin.Naming.Tests/TV/MultiEpisodeTests.cs index b15dd6b74c..3513050b64 100644 --- a/tests/Jellyfin.Naming.Tests/TV/MultiEpisodeTests.cs +++ b/tests/Jellyfin.Naming.Tests/TV/MultiEpisodeTests.cs @@ -6,100 +6,75 @@ namespace Jellyfin.Naming.Tests.TV { public class MultiEpisodeTests { - [Fact] - public void TestGetEndingEpisodeNumberFromFile() - { - Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 1/4x01 – 20 Hours in America (1).mkv")); - - Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 1/01x02 blah.avi")); - Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 1/S01x02 blah.avi")); - Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 1/S01E02 blah.avi")); - Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 1/S01xE02 blah.avi")); - Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 1/seriesname 01x02 blah.avi")); - Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 1/seriesname S01x02 blah.avi")); - Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 1/seriesname S01E02 blah.avi")); - Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 1/seriesname S01xE02 blah.avi")); - Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 2/02x03 - 04 Ep Name.mp4")); - Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 2/My show name 02x03 - 04 Ep Name.mp4")); - Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 2/Elementary - 02x03 - 02x04 - 02x15 - Ep Name.mp4")); - Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 2/02x03 - 02x04 - 02x15 - Ep Name.mp4")); - Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 2/02x03-04-15 - Ep Name.mp4")); - Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 2/Elementary - 02x03-04-15 - Ep Name.mp4")); - Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 02/02x03-E15 - Ep Name.mp4")); - Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 02/Elementary - 02x03-E15 - Ep Name.mp4")); - Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 02/02x03 - x04 - x15 - Ep Name.mp4")); - Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 02/Elementary - 02x03 - x04 - x15 - Ep Name.mp4")); - Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 02/02x03x04x15 - Ep Name.mp4")); - Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 02/Elementary - 02x03x04x15 - Ep Name.mp4")); - Assert.Equal(26, GetEndingEpisodeNumberFromFile(@"Season 1/Elementary - S01E23-E24-E26 - The Woman.mp4")); - Assert.Equal(26, GetEndingEpisodeNumberFromFile(@"Season 1/S01E23-E24-E26 - The Woman.mp4")); - - - // Four Digits seasons - Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 2009/2009x02 blah.avi")); - Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 2009/S2009x02 blah.avi")); - Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 2009/S2009E02 blah.avi")); - Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 2009/S2009xE02 blah.avi")); - Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 2009/seriesname 2009x02 blah.avi")); - Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 2009/seriesname S2009x02 blah.avi")); - Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 2009/seriesname S2009E02 blah.avi")); - Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 2009/seriesname S2009xE02 blah.avi")); - Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 2009/Elementary - 2009x03 - 2009x04 - 2009x15 - Ep Name.mp4")); - Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 2009/2009x03 - 2009x04 - 2009x15 - Ep Name.mp4")); - Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 2009/2009x03-04-15 - Ep Name.mp4")); - Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 2009/Elementary - 2009x03-04-15 - Ep Name.mp4")); - Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 2009/2009x03-E15 - Ep Name.mp4")); - Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 2009/Elementary - 2009x03-E15 - Ep Name.mp4")); - Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 2009/2009x03 - x04 - x15 - Ep Name.mp4")); - Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 2009/Elementary - 2009x03 - x04 - x15 - Ep Name.mp4")); - Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 2009/2009x03x04x15 - Ep Name.mp4")); - Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 2009/Elementary - 2009x03x04x15 - Ep Name.mp4")); - Assert.Equal(26, GetEndingEpisodeNumberFromFile(@"Season 2009/Elementary - S2009E23-E24-E26 - The Woman.mp4")); - Assert.Equal(26, GetEndingEpisodeNumberFromFile(@"Season 2009/S2009E23-E24-E26 - The Woman.mp4")); - - // Without season number - Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 1/02 - blah.avi")); - Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 2/02 - blah 14 blah.avi")); - Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 1/02 - blah-02 a.avi")); - Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 2/02.avi")); - - Assert.Equal(3, GetEndingEpisodeNumberFromFile(@"Season 1/02-03 - blah.avi")); - Assert.Equal(4, GetEndingEpisodeNumberFromFile(@"Season 2/02-04 - blah 14 blah.avi")); - Assert.Equal(5, GetEndingEpisodeNumberFromFile(@"Season 1/02-05 - blah-02 a.avi")); - Assert.Equal(4, GetEndingEpisodeNumberFromFile(@"Season 2/02-04.avi")); - Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 2/[HorribleSubs] Hunter X Hunter - 136 [720p].mkv")); - - // With format specification that must not be detected as ending episode number - Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 1/series-s09e14-1080p.mkv")); - Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 1/series-s09e14-720p.mkv")); - Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 1/series-s09e14-720i.mkv")); - Assert.Equal(4, GetEndingEpisodeNumberFromFile(@"Season 1/MOONLIGHTING_s01e01-e04.mkv")); - } - - [Fact] - public void TestGetEndingEpisodeNumberFromFolder() - { - Assert.Equal(4, GetEndingEpisodeNumberFromFolder(@"Season 1/MOONLIGHTING_s01e01-e04")); - } - - private int? GetEndingEpisodeNumberFromFolder(string path) + [Theory] + [InlineData(@"Season 1/4x01 – 20 Hours in America (1).mkv", null)] + [InlineData(@"Season 1/01x02 blah.avi", null)] + [InlineData(@"Season 1/S01x02 blah.avi", null)] + [InlineData(@"Season 1/S01E02 blah.avi", null)] + [InlineData(@"Season 1/S01xE02 blah.avi", null)] + [InlineData(@"Season 1/seriesname 01x02 blah.avi", null)] + [InlineData(@"Season 1/seriesname S01x02 blah.avi", null)] + [InlineData(@"Season 1/seriesname S01E02 blah.avi", null)] + [InlineData(@"Season 1/seriesname S01xE02 blah.avi", null)] + [InlineData(@"Season 2/02x03 - 04 Ep Name.mp4", null)] + [InlineData(@"Season 2/My show name 02x03 - 04 Ep Name.mp4", null)] + [InlineData(@"Season 2/Elementary - 02x03 - 02x04 - 02x15 - Ep Name.mp4", 15)] + [InlineData(@"Season 2/02x03 - 02x04 - 02x15 - Ep Name.mp4", 15)] + [InlineData(@"Season 2/02x03-04-15 - Ep Name.mp4", 15)] + [InlineData(@"Season 2/Elementary - 02x03-04-15 - Ep Name.mp4", 15)] + [InlineData(@"Season 02/02x03-E15 - Ep Name.mp4", 15)] + [InlineData(@"Season 02/Elementary - 02x03-E15 - Ep Name.mp4", 15)] + [InlineData(@"Season 02/02x03 - x04 - x15 - Ep Name.mp4", 15)] + [InlineData(@"Season 02/Elementary - 02x03 - x04 - x15 - Ep Name.mp4", 15)] + [InlineData(@"Season 02/02x03x04x15 - Ep Name.mp4", 15)] + [InlineData(@"Season 02/Elementary - 02x03x04x15 - Ep Name.mp4", 15)] + [InlineData(@"Season 1/Elementary - S01E23-E24-E26 - The Woman.mp4", 26)] + [InlineData(@"Season 1/S01E23-E24-E26 - The Woman.mp4", 26)] + // Four Digits seasons + [InlineData(@"Season 2009/2009x02 blah.avi", null)] + [InlineData(@"Season 2009/S2009x02 blah.avi", null)] + [InlineData(@"Season 2009/S2009E02 blah.avi", null)] + [InlineData(@"Season 2009/S2009xE02 blah.avi", null)] + [InlineData(@"Season 2009/seriesname 2009x02 blah.avi", null)] + [InlineData(@"Season 2009/seriesname S2009x02 blah.avi", null)] + [InlineData(@"Season 2009/seriesname S2009E02 blah.avi", null)] + [InlineData(@"Season 2009/seriesname S2009xE02 blah.avi", null)] + [InlineData(@"Season 2009/Elementary - 2009x03 - 2009x04 - 2009x15 - Ep Name.mp4", 15)] + [InlineData(@"Season 2009/2009x03 - 2009x04 - 2009x15 - Ep Name.mp4", 15)] + [InlineData(@"Season 2009/2009x03-04-15 - Ep Name.mp4", 15)] + [InlineData(@"Season 2009/Elementary - 2009x03-04-15 - Ep Name.mp4", 15)] + [InlineData(@"Season 2009/2009x03-E15 - Ep Name.mp4", 15)] + [InlineData(@"Season 2009/Elementary - 2009x03-E15 - Ep Name.mp4", 15)] + [InlineData(@"Season 2009/2009x03 - x04 - x15 - Ep Name.mp4", 15)] + [InlineData(@"Season 2009/Elementary - 2009x03 - x04 - x15 - Ep Name.mp4", 15)] + [InlineData(@"Season 2009/2009x03x04x15 - Ep Name.mp4", 15)] + [InlineData(@"Season 2009/Elementary - 2009x03x04x15 - Ep Name.mp4", 15)] + [InlineData(@"Season 2009/Elementary - S2009E23-E24-E26 - The Woman.mp4", 26)] + [InlineData(@"Season 2009/S2009E23-E24-E26 - The Woman.mp4", 26)] + // Without season number + [InlineData(@"Season 1/02 - blah.avi", null)] + [InlineData(@"Season 2/02 - blah 14 blah.avi", null)] + [InlineData(@"Season 1/02 - blah-02 a.avi", null)] + [InlineData(@"Season 2/02.avi", null)] + [InlineData(@"Season 1/02-03 - blah.avi", 3)] + [InlineData(@"Season 2/02-04 - blah 14 blah.avi", 4)] + [InlineData(@"Season 1/02-05 - blah-02 a.avi", 5)] + [InlineData(@"Season 2/02-04.avi", 4)] + [InlineData(@"Season 2 /[HorribleSubs] Hunter X Hunter - 136[720p].mkv", null)] + // With format specification that must not be detected as ending episode number + [InlineData(@"Season 1/series-s09e14-1080p.mkv", null)] + [InlineData(@"Season 1/series-s09e14-720p.mkv", null)] + [InlineData(@"Season 1/series-s09e14-720i.mkv", null)] + [InlineData(@"Season 1/MOONLIGHTING_s01e01-e04.mkv", 4)] + [InlineData(@"Season 1/MOONLIGHTING_s01e01-e04", 4)] + public void TestGetEndingEpisodeNumberFromFile(string filename, int? endingEpisodeNumber) { var options = new NamingOptions(); var result = new EpisodePathParser(options) - .Parse(path, true); + .Parse(filename, false); - return result.EndingEpsiodeNumber; - } - - private int? GetEndingEpisodeNumberFromFile(string path) - { - var options = new NamingOptions(); - - var result = new EpisodePathParser(options) - .Parse(path, false); - - return result.EndingEpsiodeNumber; + Assert.Equal(result.EndingEpsiodeNumber, endingEpisodeNumber); } } } diff --git a/tests/Jellyfin.Naming.Tests/TV/SeasonFolderTests.cs b/tests/Jellyfin.Naming.Tests/TV/SeasonFolderTests.cs index df683fc342..078f940b29 100644 --- a/tests/Jellyfin.Naming.Tests/TV/SeasonFolderTests.cs +++ b/tests/Jellyfin.Naming.Tests/TV/SeasonFolderTests.cs @@ -5,107 +5,27 @@ namespace Jellyfin.Naming.Tests.TV { public class SeasonFolderTests { - [Fact] - public void TestGetSeasonNumberFromPath1() - { - Assert.Equal(1, GetSeasonNumberFromPath(@"/Drive/Season 1")); - } - - [Fact] - public void TestGetSeasonNumberFromPath2() - { - Assert.Equal(2, GetSeasonNumberFromPath(@"/Drive/Season 2")); - } - - [Fact] - public void TestGetSeasonNumberFromPath3() - { - Assert.Equal(2, GetSeasonNumberFromPath(@"/Drive/Season 02")); - } - - [Fact] - public void TestGetSeasonNumberFromPath4() - { - Assert.Equal(1, GetSeasonNumberFromPath(@"/Drive/Season 1")); - } - - [Fact] - public void TestGetSeasonNumberFromPath5() - { - Assert.Equal(2, GetSeasonNumberFromPath(@"/Drive/Seinfeld/S02")); - } - - [Fact] - public void TestGetSeasonNumberFromPath6() - { - Assert.Equal(2, GetSeasonNumberFromPath(@"/Drive/Seinfeld/2")); - } - - [Fact] - public void TestGetSeasonNumberFromPath7() - { - Assert.Equal(2009, GetSeasonNumberFromPath(@"/Drive/Season 2009")); - } - - [Fact] - public void TestGetSeasonNumberFromPath8() - { - Assert.Equal(1, GetSeasonNumberFromPath(@"/Drive/Season1")); - } - - [Fact] - public void TestGetSeasonNumberFromPath9() - { - Assert.Equal(4, GetSeasonNumberFromPath(@"The Wonder Years/The.Wonder.Years.S04.PDTV.x264-JCH")); - } - - [Fact] - public void TestGetSeasonNumberFromPath10() - { - Assert.Equal(7, GetSeasonNumberFromPath(@"/Drive/Season 7 (2016)")); - } - - [Fact] - public void TestGetSeasonNumberFromPath11() - { - Assert.Equal(7, GetSeasonNumberFromPath(@"/Drive/Staffel 7 (2016)")); - } - - [Fact] - public void TestGetSeasonNumberFromPath12() - { - Assert.Equal(7, GetSeasonNumberFromPath(@"/Drive/Stagione 7 (2016)")); - } - - [Fact] - public void TestGetSeasonNumberFromPath14() - { - Assert.Null(GetSeasonNumberFromPath(@"/Drive/Season (8)")); - } - - [Fact] - public void TestGetSeasonNumberFromPath13() - { - Assert.Equal(3, GetSeasonNumberFromPath(@"/Drive/3.Staffel")); - } - - [Fact] - public void TestGetSeasonNumberFromPath15() - { - Assert.Null(GetSeasonNumberFromPath(@"/Drive/s06e05")); - } - - [Fact] - public void TestGetSeasonNumberFromPath16() - { - Assert.Null(GetSeasonNumberFromPath(@"/Drive/The.Legend.of.Condor.Heroes.2017.V2.web-dl.1080p.h264.aac-hdctv")); - } - - private int? GetSeasonNumberFromPath(string path) + [Theory] + [InlineData(@"/Drive/Season 1", 1)] + [InlineData(@"/Drive/Season 2", 2)] + [InlineData(@"/Drive/Season 02", 2)] + [InlineData(@"/Drive/Seinfeld/S02", 2)] + [InlineData(@"/Drive/Seinfeld/2", 2)] + [InlineData(@"/Drive/Season 2009", 2009)] + [InlineData(@"/Drive/Season1", 1)] + [InlineData(@"The Wonder Years/The.Wonder.Years.S04.PDTV.x264-JCH", 4)] + [InlineData(@"/Drive/Season 7 (2016)", 7)] + [InlineData(@"/Drive/Staffel 7 (2016)", 7)] + [InlineData(@"/Drive/Stagione 7 (2016)", 7)] + [InlineData(@"/Drive/Season (8)", null)] + [InlineData(@"/Drive/3.Staffel", 3)] + [InlineData(@"/Drive/s06e05", null)] + [InlineData(@"/Drive/The.Legend.of.Condor.Heroes.2017.V2.web-dl.1080p.h264.aac-hdctv", null)] + public void GetSeasonNumberFromPathTest(string path, int? seasonNumber) { var result = SeasonPathParser.Parse(path, true, true); - return result.SeasonNumber; + Assert.Equal(result.SeasonNumber, seasonNumber); } } } diff --git a/tests/Jellyfin.Naming.Tests/TV/SeasonNumberTests.cs b/tests/Jellyfin.Naming.Tests/TV/SeasonNumberTests.cs index 1df28c974c..9eaf897b9e 100644 --- a/tests/Jellyfin.Naming.Tests/TV/SeasonNumberTests.cs +++ b/tests/Jellyfin.Naming.Tests/TV/SeasonNumberTests.cs @@ -10,6 +10,50 @@ namespace Jellyfin.Naming.Tests.TV [Theory] [InlineData("The Daily Show/The Daily Show 25x22 - [WEBDL-720p][AAC 2.0][x264] Noah Baumbach-TBS.mkv", 25)] + [InlineData("/Show/Season 02/S02E03 blah.avi", 2)] + [InlineData("Season 1/seriesname S01x02 blah.avi", 1)] + [InlineData("Season 1/S01x02 blah.avi", 1)] + [InlineData("Season 1/seriesname S01xE02 blah.avi", 1)] + [InlineData("Season 1/01x02 blah.avi", 1)] + [InlineData("Season 1/S01E02 blah.avi", 1)] + [InlineData("Season 1/S01xE02 blah.avi", 1)] + [InlineData("Season 1/seriesname 01x02 blah.avi", 1)] + [InlineData("Season 1/seriesname S01E02 blah.avi", 1)] + [InlineData("Season 2/Elementary - 02x03 - 02x04 - 02x15 - Ep Name.mp4", 2)] + [InlineData("Season 2/02x03 - 02x04 - 02x15 - Ep Name.mp4", 2)] + [InlineData("Season 2/02x03-04-15 - Ep Name.mp4", 2)] + [InlineData("Season 2/Elementary - 02x03-04-15 - Ep Name.mp4", 2)] + [InlineData("Season 02/02x03-E15 - Ep Name.mp4", 2)] + [InlineData("Season 02/Elementary - 02x03-E15 - Ep Name.mp4", 2)] + [InlineData("Season 02/02x03 - x04 - x15 - Ep Name.mp4", 2)] + [InlineData("Season 02/Elementary - 02x03 - x04 - x15 - Ep Name.mp4", 2)] + [InlineData("Season 02/02x03x04x15 - Ep Name.mp4", 2)] + [InlineData("Season 02/Elementary - 02x03x04x15 - Ep Name.mp4", 2)] + [InlineData("Season 1/Elementary - S01E23-E24-E26 - The Woman.mp4", 1)] + [InlineData("Season 1/S01E23-E24-E26 - The Woman.mp4", 1)] + [InlineData("Season 25/The Simpsons.S25E09.Steal this episode.mp4", 25)] + [InlineData("The Simpsons/The Simpsons.S25E09.Steal this episode.mp4", 25)] + [InlineData("2016/Season s2016e1.mp4", 2016)] + [InlineData("2016/Season 2016x1.mp4", 2016)] + [InlineData("Season 2009/2009x02 blah.avi", 2009)] + [InlineData("Season 2009/S2009x02 blah.avi", 2009)] + [InlineData("Season 2009/S2009E02 blah.avi", 2009)] + [InlineData("Season 2009/S2009xE02 blah.avi", 2009)] + [InlineData("Season 2009/seriesname 2009x02 blah.avi", 2009)] + [InlineData("Season 2009/seriesname S2009x02 blah.avi", 2009)] + [InlineData("Season 2009/seriesname S2009E02 blah.avi", 2009)] + [InlineData("Season 2009/Elementary - 2009x03 - 2009x04 - 2009x15 - Ep Name.mp4", 2009)] + [InlineData("Season 2009/2009x03 - 2009x04 - 2009x15 - Ep Name.mp4", 2009)] + [InlineData("Season 2009/2009x03-04-15 - Ep Name.mp4", 2009)] + [InlineData("Season 2009/Elementary - 2009x03 - x04 - x15 - Ep Name.mp4", 2009)] + [InlineData("Season 2009/2009x03x04x15 - Ep Name.mp4", 2009)] + [InlineData("Season 2009/Elementary - 2009x03x04x15 - Ep Name.mp4", 2009)] + [InlineData("Season 2009/Elementary - S2009E23-E24-E26 - The Woman.mp4", 2009)] + [InlineData("Season 2009/S2009E23-E24-E26 - The Woman.mp4", 2009)] + [InlineData("Series/1-12 - The Woman.mp4", 1)] + [InlineData(@"Running Man/Running Man S2017E368.mkv", 2017)] + [InlineData(@"Case Closed (1996-2007)/Case Closed - 317.mkv", 3)] + // TODO: [InlineData(@"Seinfeld/Seinfeld 0807 The Checks.avi", 8)] public void GetSeasonNumberFromEpisodeFileTest(string path, int? expected) { var result = new EpisodeResolver(_namingOptions) @@ -17,299 +61,5 @@ namespace Jellyfin.Naming.Tests.TV Assert.Equal(expected, result.SeasonNumber); } - - private int? GetSeasonNumberFromEpisodeFile(string path) - { - var result = new EpisodeResolver(_namingOptions) - .Resolve(path, false); - - return result.SeasonNumber; - } - - [Fact] - public void TestSeasonNumber1() - { - Assert.Equal(2, GetSeasonNumberFromEpisodeFile(@"/Show/Season 02/S02E03 blah.avi")); - } - - [Fact] - public void TestSeasonNumber2() - { - Assert.Equal(1, GetSeasonNumberFromEpisodeFile(@"Season 1/seriesname S01x02 blah.avi")); - } - - [Fact] - public void TestSeasonNumber3() - { - Assert.Equal(1, GetSeasonNumberFromEpisodeFile(@"Season 1/S01x02 blah.avi")); - } - - [Fact] - public void TestSeasonNumber4() - { - Assert.Equal(1, GetSeasonNumberFromEpisodeFile(@"Season 1/seriesname S01xE02 blah.avi")); - } - - [Fact] - public void TestSeasonNumber5() - { - Assert.Equal(1, GetSeasonNumberFromEpisodeFile(@"Season 1/01x02 blah.avi")); - } - - [Fact] - public void TestSeasonNumber6() - { - Assert.Equal(1, GetSeasonNumberFromEpisodeFile(@"Season 1/S01E02 blah.avi")); - } - - [Fact] - public void TestSeasonNumber7() - { - Assert.Equal(1, GetSeasonNumberFromEpisodeFile(@"Season 1/S01xE02 blah.avi")); - } - - // FIXME - // [Fact] - public void TestSeasonNumber8() - { - Assert.Equal(1, GetSeasonNumberFromEpisodeFile(@"Season 1/seriesname 01x02 blah.avi")); - } - - [Fact] - public void TestSeasonNumber9() - { - Assert.Equal(1, GetSeasonNumberFromEpisodeFile(@"Season 1/seriesname S01x02 blah.avi")); - } - - [Fact] - public void TestSeasonNumber10() - { - Assert.Equal(1, GetSeasonNumberFromEpisodeFile(@"Season 1/seriesname S01E02 blah.avi")); - } - - [Fact] - public void TestSeasonNumber11() - { - Assert.Equal(2, GetSeasonNumberFromEpisodeFile(@"Season 2/Elementary - 02x03 - 02x04 - 02x15 - Ep Name.mp4")); - } - - [Fact] - public void TestSeasonNumber12() - { - Assert.Equal(2, GetSeasonNumberFromEpisodeFile(@"Season 2/02x03 - 02x04 - 02x15 - Ep Name.mp4")); - } - - [Fact] - public void TestSeasonNumber13() - { - Assert.Equal(2, GetSeasonNumberFromEpisodeFile(@"Season 2/02x03-04-15 - Ep Name.mp4")); - } - - [Fact] - public void TestSeasonNumber14() - { - Assert.Equal(2, GetSeasonNumberFromEpisodeFile(@"Season 2/Elementary - 02x03-04-15 - Ep Name.mp4")); - } - - [Fact] - public void TestSeasonNumber15() - { - Assert.Equal(2, GetSeasonNumberFromEpisodeFile(@"Season 02/02x03-E15 - Ep Name.mp4")); - } - - [Fact] - public void TestSeasonNumber16() - { - Assert.Equal(2, GetSeasonNumberFromEpisodeFile(@"Season 02/Elementary - 02x03-E15 - Ep Name.mp4")); - } - - [Fact] - public void TestSeasonNumber17() - { - Assert.Equal(2, GetSeasonNumberFromEpisodeFile(@"Season 02/02x03 - x04 - x15 - Ep Name.mp4")); - } - - [Fact] - public void TestSeasonNumber18() - { - Assert.Equal(2, GetSeasonNumberFromEpisodeFile(@"Season 02/Elementary - 02x03 - x04 - x15 - Ep Name.mp4")); - } - - [Fact] - public void TestSeasonNumber19() - { - Assert.Equal(2, GetSeasonNumberFromEpisodeFile(@"Season 02/02x03x04x15 - Ep Name.mp4")); - } - - [Fact] - public void TestSeasonNumber20() - { - Assert.Equal(2, GetSeasonNumberFromEpisodeFile(@"Season 02/Elementary - 02x03x04x15 - Ep Name.mp4")); - } - - [Fact] - public void TestSeasonNumber21() - { - Assert.Equal(1, GetSeasonNumberFromEpisodeFile(@"Season 1/Elementary - S01E23-E24-E26 - The Woman.mp4")); - } - - [Fact] - public void TestSeasonNumber22() - { - Assert.Equal(1, GetSeasonNumberFromEpisodeFile(@"Season 1/S01E23-E24-E26 - The Woman.mp4")); - } - - [Fact] - public void TestSeasonNumber23() - { - Assert.Equal(25, GetSeasonNumberFromEpisodeFile(@"Season 25/The Simpsons.S25E09.Steal this episode.mp4")); - } - - [Fact] - public void TestSeasonNumber24() - { - Assert.Equal(25, GetSeasonNumberFromEpisodeFile(@"The Simpsons/The Simpsons.S25E09.Steal this episode.mp4")); - } - - [Fact] - public void TestSeasonNumber25() - { - Assert.Equal(2016, GetSeasonNumberFromEpisodeFile(@"2016/Season s2016e1.mp4")); - } - - // FIXME - // [Fact] - public void TestSeasonNumber26() - { - // This convention is not currently supported, just adding in case we want to look at it in the future - Assert.Equal(2016, GetSeasonNumberFromEpisodeFile(@"2016/Season 2016x1.mp4")); - } - - [Fact] - public void TestFourDigitSeasonNumber1() - { - Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/2009x02 blah.avi")); - } - - [Fact] - public void TestFourDigitSeasonNumber2() - { - Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/S2009x02 blah.avi")); - } - - [Fact] - public void TestFourDigitSeasonNumber3() - { - Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/S2009E02 blah.avi")); - } - - [Fact] - public void TestFourDigitSeasonNumber4() - { - Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/S2009xE02 blah.avi")); - } - - // FIXME - // [Fact] - public void TestFourDigitSeasonNumber5() - { - Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/seriesname 2009x02 blah.avi")); - } - - [Fact] - public void TestFourDigitSeasonNumber6() - { - Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/seriesname S2009x02 blah.avi")); - } - - [Fact] - public void TestFourDigitSeasonNumber7() - { - Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/seriesname S2009E02 blah.avi")); - } - - [Fact] - public void TestFourDigitSeasonNumber8() - { - Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/Elementary - 2009x03 - 2009x04 - 2009x15 - Ep Name.mp4")); - } - - [Fact] - public void TestFourDigitSeasonNumber9() - { - Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/2009x03 - 2009x04 - 2009x15 - Ep Name.mp4")); - } - - [Fact] - public void TestFourDigitSeasonNumber10() - { - Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/2009x03-04-15 - Ep Name.mp4")); - } - - [Fact] - public void TestFourDigitSeasonNumber11() - { - Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/Elementary - 2009x03 - x04 - x15 - Ep Name.mp4")); - } - - [Fact] - public void TestFourDigitSeasonNumber12() - { - Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/2009x03x04x15 - Ep Name.mp4")); - } - - [Fact] - public void TestFourDigitSeasonNumber13() - { - Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/Elementary - 2009x03x04x15 - Ep Name.mp4")); - } - - [Fact] - public void TestFourDigitSeasonNumber14() - { - Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/Elementary - S2009E23-E24-E26 - The Woman.mp4")); - } - - [Fact] - public void TestFourDigitSeasonNumber15() - { - Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/S2009E23-E24-E26 - The Woman.mp4")); - } - - [Fact] - public void TestFourDigitSeasonNumber16() - { - Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/Elementary - 2009x03 - x04 - x15 - Ep Name.mp4")); - } - - [Fact] - public void TestFourDigitSeasonNumber17() - { - Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/2009x03x04x15 - Ep Name.mp4")); - } - - [Fact] - public void TestFourDigitSeasonNumber18() - { - Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/Elementary - 2009x03x04x15 - Ep Name.mp4")); - } - - [Fact] - public void TestFourDigitSeasonNumber19() - { - Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/Elementary - S2009E23-E24-E26 - The Woman.mp4")); - } - - [Fact] - public void TestFourDigitSeasonNumber20() - { - Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/S2009E23-E24-E26 - The Woman.mp4")); - } - - [Fact] - public void TestNoSeriesFolder() - { - Assert.Equal(1, GetSeasonNumberFromEpisodeFile(@"Series/1-12 - The Woman.mp4")); - } } } diff --git a/tests/Jellyfin.Naming.Tests/TV/SimpleEpisodeTests.cs b/tests/Jellyfin.Naming.Tests/TV/SimpleEpisodeTests.cs index c9323c218e..de253ce375 100644 --- a/tests/Jellyfin.Naming.Tests/TV/SimpleEpisodeTests.cs +++ b/tests/Jellyfin.Naming.Tests/TV/SimpleEpisodeTests.cs @@ -6,81 +6,25 @@ namespace Jellyfin.Naming.Tests.TV { public class SimpleEpisodeTests { - [Fact] - public void TestSimpleEpisodePath1() - { - Test(@"/server/anything_s01e02.mp4", "anything", 1, 2); - } - - [Fact] - public void TestSimpleEpisodePath2() - { - Test(@"/server/anything_s1e2.mp4", "anything", 1, 2); - } - - [Fact] - public void TestSimpleEpisodePath3() - { - Test(@"/server/anything_s01.e02.mp4", "anything", 1, 2); - } - - [Fact] - public void TestSimpleEpisodePath4() - { - Test(@"/server/anything_s01_e02.mp4", "anything", 1, 2); - } - - [Fact] - public void TestSimpleEpisodePath5() - { - Test(@"/server/anything_102.mp4", "anything", 1, 2); - } - - [Fact] - public void TestSimpleEpisodePath6() - { - Test(@"/server/anything_1x02.mp4", "anything", 1, 2); - } - - // FIXME - // [Fact] - public void TestSimpleEpisodePath7() - { - Test(@"/server/The Walking Dead 4x01.mp4", "The Walking Dead", 4, 1); - } - - [Fact] - public void TestSimpleEpisodePath8() - { - Test(@"/server/the_simpsons-s02e01_18536.mp4", "the_simpsons", 2, 1); - } - - - [Fact] - public void TestSimpleEpisodePath9() - { - Test(@"/server/Temp/S01E02 foo.mp4", string.Empty, 1, 2); - } - - [Fact] - public void TestSimpleEpisodePath10() - { - Test(@"Series/4-12 - The Woman.mp4", string.Empty, 4, 12); - } - - [Fact] - public void TestSimpleEpisodePath11() - { - Test(@"Series/4x12 - The Woman.mp4", string.Empty, 4, 12); - } - - [Fact] - public void TestSimpleEpisodePath12() - { - Test(@"Series/LA X, Pt. 1_s06e32.mp4", "LA X, Pt. 1", 6, 32); - } - - private void Test(string path, string seriesName, int? seasonNumber, int? episodeNumber) + [Theory] + [InlineData("/server/anything_s01e02.mp4", "anything", 1, 2)] + [InlineData("/server/anything_s1e2.mp4", "anything", 1, 2)] + [InlineData("/server/anything_s01.e02.mp4", "anything", 1, 2)] + [InlineData("/server/anything_102.mp4", "anything", 1, 2)] + [InlineData("/server/anything_1x02.mp4", "anything", 1, 2)] + [InlineData("/server/The Walking Dead 4x01.mp4", "The Walking Dead", 4, 1)] + [InlineData("/server/the_simpsons-s02e01_18536.mp4", "the_simpsons", 2, 1)] + [InlineData("/server/Temp/S01E02 foo.mp4", "", 1, 2)] + [InlineData("Series/4-12 - The Woman.mp4", "", 4, 12)] + [InlineData("Series/4x12 - The Woman.mp4", "", 4, 12)] + [InlineData("Series/LA X, Pt. 1_s06e32.mp4", "LA X, Pt. 1", 6, 32)] + [InlineData("[Baz-Bar]Foo - [1080p][Multiple Subtitle]/[Baz-Bar] Foo - 05 [1080p][Multiple Subtitle].mkv", "Foo", null, 5)] + [InlineData(@"/Foo/The.Series.Name.S01E04.WEBRip.x264-Baz[Bar]/the.series.name.s01e04.webrip.x264-Baz[Bar].mkv", "The.Series.Name", 1, 4)] + [InlineData(@"Love.Death.and.Robots.S01.1080p.NF.WEB-DL.DDP5.1.x264-NTG/Love.Death.and.Robots.S01E01.Sonnies.Edge.1080p.NF.WEB-DL.DDP5.1.x264-NTG.mkv", "Love.Death.and.Robots", 1, 1)] + // TODO: [InlineData("[Baz-Bar]Foo - 01 - 12[1080p][Multiple Subtitle]/[Baz-Bar] Foo - 05 [1080p][Multiple Subtitle].mkv", "Foo", null, 5)] + // TODO: [InlineData("E:\\Anime\\Yahari Ore no Seishun Love Comedy wa Machigatteiru\\Yahari Ore no Seishun Love Comedy wa Machigatteiru. Zoku\\Oregairu Zoku 11 - Hayama Hayato Always Renconds to Everyone's Expectations..mkv", "Yahari Ore no Seishun Love Comedy wa Machigatteiru", null, 11)] + // TODO: [InlineData(@"/Library/Series/The Grand Tour (2016)/Season 1/S01E01 The Holy Trinity.mkv", "The Grand Tour", 1, 1)] + public void Test(string path, string seriesName, int? seasonNumber, int? episodeNumber) { var options = new NamingOptions(); From 8e20d2e931b273b54ef369c9b75021ab04118e04 Mon Sep 17 00:00:00 2001 From: Vasily Date: Thu, 27 Feb 2020 14:51:34 +0300 Subject: [PATCH 050/144] Simplify AlphanumericComparer, reduce code duplication --- MediaBrowser.Controller/Entities/BaseItem.cs | 8 +- .../Sorting/AlphanumComparator.cs | 32 +++-- .../Sorting/SortExtensions.cs | 122 +----------------- MediaBrowser.Controller/Sorting/SortHelper.cs | 25 ---- 4 files changed, 22 insertions(+), 165 deletions(-) rename {Emby.Server.Implementations => MediaBrowser.Controller}/Sorting/AlphanumComparator.cs (73%) delete mode 100644 MediaBrowser.Controller/Sorting/SortHelper.cs diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 353c675cb3..09cdfc9eaf 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -387,15 +387,12 @@ namespace MediaBrowser.Controller.Entities while (thisMarker < s1.Length) { - if (thisMarker >= s1.Length) - { - break; - } char thisCh = s1[thisMarker]; var thisChunk = new StringBuilder(); + bool isNumeric = char.IsDigit(thisCh); - while ((thisMarker < s1.Length) && (thisChunk.Length == 0 || SortHelper.InChunk(thisCh, thisChunk[0]))) + while ((thisMarker < s1.Length) && (char.IsDigit(thisCh) == isNumeric)) { thisChunk.Append(thisCh); thisMarker++; @@ -406,7 +403,6 @@ namespace MediaBrowser.Controller.Entities } } - var isNumeric = thisChunk.Length > 0 && char.IsDigit(thisChunk[0]); list.Add(new Tuple(thisChunk, isNumeric)); } diff --git a/Emby.Server.Implementations/Sorting/AlphanumComparator.cs b/MediaBrowser.Controller/Sorting/AlphanumComparator.cs similarity index 73% rename from Emby.Server.Implementations/Sorting/AlphanumComparator.cs rename to MediaBrowser.Controller/Sorting/AlphanumComparator.cs index 2e00c24d75..ad1eaf7bf7 100644 --- a/Emby.Server.Implementations/Sorting/AlphanumComparator.cs +++ b/MediaBrowser.Controller/Sorting/AlphanumComparator.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Text; using MediaBrowser.Controller.Sorting; -namespace Emby.Server.Implementations.Sorting +namespace MediaBrowser.Controller.Sorting { public class AlphanumComparator : IComparer { @@ -31,8 +31,9 @@ namespace Emby.Server.Implementations.Sorting var thisChunk = new StringBuilder(); var thatChunk = new StringBuilder(); + bool thisNumeric = char.IsDigit(thisCh), thatNumeric = char.IsDigit(thatCh); - while ((thisMarker < s1.Length) && (thisChunk.Length == 0 || SortHelper.InChunk(thisCh, thisChunk[0]))) + while ((thisMarker < s1.Length) && (char.IsDigit(thisCh) == thisNumeric)) { thisChunk.Append(thisCh); thisMarker++; @@ -43,7 +44,7 @@ namespace Emby.Server.Implementations.Sorting } } - while ((thatMarker < s2.Length) && (thatChunk.Length == 0 || SortHelper.InChunk(thatCh, thatChunk[0]))) + while ((thatMarker < s2.Length) && (char.IsDigit(thatCh) == thatNumeric)) { thatChunk.Append(thatCh); thatMarker++; @@ -54,38 +55,35 @@ namespace Emby.Server.Implementations.Sorting } } - int result = 0; + // If both chunks contain numeric characters, sort them numerically - if (char.IsDigit(thisChunk[0]) && char.IsDigit(thatChunk[0])) + if (thisNumeric && thatNumeric) { - if (!int.TryParse(thisChunk.ToString(), out thisNumericChunk)) - { - return 0; - } - if (!int.TryParse(thatChunk.ToString(), out thatNumericChunk)) + if (!int.TryParse(thisChunk.ToString(), out thisNumericChunk) + || !int.TryParse(thatChunk.ToString(), out thatNumericChunk)) { return 0; } if (thisNumericChunk < thatNumericChunk) { - result = -1; + return -1; } if (thisNumericChunk > thatNumericChunk) { - result = 1; + return 1; } } else { - result = thisChunk.ToString().CompareTo(thatChunk.ToString()); + int result = thisChunk.ToString().CompareTo(thatChunk.ToString()); + if (result != 0) + { + return result; + } } - if (result != 0) - { - return result; - } } return 0; diff --git a/MediaBrowser.Controller/Sorting/SortExtensions.cs b/MediaBrowser.Controller/Sorting/SortExtensions.cs index 111f4f17fc..bc98c58f0c 100644 --- a/MediaBrowser.Controller/Sorting/SortExtensions.cs +++ b/MediaBrowser.Controller/Sorting/SortExtensions.cs @@ -7,137 +7,25 @@ namespace MediaBrowser.Controller.Sorting { public static class SortExtensions { + private static IComparer _comparer = new AlphanumComparator(); public static IEnumerable OrderByString(this IEnumerable list, Func getName) { - return list.OrderBy(getName, new AlphanumComparator()); + return list.OrderBy(getName, _comparer); } public static IEnumerable OrderByStringDescending(this IEnumerable list, Func getName) { - return list.OrderByDescending(getName, new AlphanumComparator()); + return list.OrderByDescending(getName, _comparer); } public static IOrderedEnumerable ThenByString(this IOrderedEnumerable list, Func getName) { - return list.ThenBy(getName, new AlphanumComparator()); + return list.ThenBy(getName, _comparer); } public static IOrderedEnumerable ThenByStringDescending(this IOrderedEnumerable list, Func getName) { - return list.ThenByDescending(getName, new AlphanumComparator()); - } - - private class AlphanumComparator : IComparer - { - private enum ChunkType { Alphanumeric, Numeric }; - - private static bool InChunk(char ch, char otherCh) - { - var type = ChunkType.Alphanumeric; - - if (char.IsDigit(otherCh)) - { - type = ChunkType.Numeric; - } - - if ((type == ChunkType.Alphanumeric && char.IsDigit(ch)) - || (type == ChunkType.Numeric && !char.IsDigit(ch))) - { - return false; - } - - return true; - } - - public static int CompareValues(string s1, string s2) - { - if (s1 == null || s2 == null) - { - return 0; - } - - int thisMarker = 0, thisNumericChunk = 0; - int thatMarker = 0, thatNumericChunk = 0; - - while ((thisMarker < s1.Length) || (thatMarker < s2.Length)) - { - if (thisMarker >= s1.Length) - { - return -1; - } - else if (thatMarker >= s2.Length) - { - return 1; - } - char thisCh = s1[thisMarker]; - char thatCh = s2[thatMarker]; - - var thisChunk = new StringBuilder(); - var thatChunk = new StringBuilder(); - - while ((thisMarker < s1.Length) && (thisChunk.Length == 0 || InChunk(thisCh, thisChunk[0]))) - { - thisChunk.Append(thisCh); - thisMarker++; - - if (thisMarker < s1.Length) - { - thisCh = s1[thisMarker]; - } - } - - while ((thatMarker < s2.Length) && (thatChunk.Length == 0 || InChunk(thatCh, thatChunk[0]))) - { - thatChunk.Append(thatCh); - thatMarker++; - - if (thatMarker < s2.Length) - { - thatCh = s2[thatMarker]; - } - } - - int result = 0; - // If both chunks contain numeric characters, sort them numerically - if (char.IsDigit(thisChunk[0]) && char.IsDigit(thatChunk[0])) - { - if (!int.TryParse(thisChunk.ToString(), out thisNumericChunk)) - { - return 0; - } - if (!int.TryParse(thatChunk.ToString(), out thatNumericChunk)) - { - return 0; - } - - if (thisNumericChunk < thatNumericChunk) - { - result = -1; - } - - if (thisNumericChunk > thatNumericChunk) - { - result = 1; - } - } - else - { - result = thisChunk.ToString().CompareTo(thatChunk.ToString()); - } - - if (result != 0) - { - return result; - } - } - - return 0; - } - - public int Compare(string x, string y) - { - return CompareValues(x, y); - } + return list.ThenByDescending(getName, _comparer); } } } diff --git a/MediaBrowser.Controller/Sorting/SortHelper.cs b/MediaBrowser.Controller/Sorting/SortHelper.cs deleted file mode 100644 index 05981d9757..0000000000 --- a/MediaBrowser.Controller/Sorting/SortHelper.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace MediaBrowser.Controller.Sorting -{ - public static class SortHelper - { - private enum ChunkType { Alphanumeric, Numeric }; - - public static bool InChunk(char ch, char otherCh) - { - var type = ChunkType.Alphanumeric; - - if (char.IsDigit(otherCh)) - { - type = ChunkType.Numeric; - } - - if ((type == ChunkType.Alphanumeric && char.IsDigit(ch)) - || (type == ChunkType.Numeric && !char.IsDigit(ch))) - { - return false; - } - - return true; - } - } -} From d1670f81808a74865f8c4fb2c2a77ab01eb3bde9 Mon Sep 17 00:00:00 2001 From: Vasily Date: Thu, 27 Feb 2020 16:02:18 +0300 Subject: [PATCH 051/144] Apply suggestions from code review Co-Authored-By: Claus Vium --- MediaBrowser.Controller/Entities/BaseItem.cs | 2 +- MediaBrowser.Controller/Sorting/AlphanumComparator.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 09cdfc9eaf..66de080a39 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -392,7 +392,7 @@ namespace MediaBrowser.Controller.Entities var thisChunk = new StringBuilder(); bool isNumeric = char.IsDigit(thisCh); - while ((thisMarker < s1.Length) && (char.IsDigit(thisCh) == isNumeric)) + while (thisMarker < s1.Length && char.IsDigit(thisCh) == isNumeric) { thisChunk.Append(thisCh); thisMarker++; diff --git a/MediaBrowser.Controller/Sorting/AlphanumComparator.cs b/MediaBrowser.Controller/Sorting/AlphanumComparator.cs index ad1eaf7bf7..65dc120ca8 100644 --- a/MediaBrowser.Controller/Sorting/AlphanumComparator.cs +++ b/MediaBrowser.Controller/Sorting/AlphanumComparator.cs @@ -33,7 +33,7 @@ namespace MediaBrowser.Controller.Sorting var thatChunk = new StringBuilder(); bool thisNumeric = char.IsDigit(thisCh), thatNumeric = char.IsDigit(thatCh); - while ((thisMarker < s1.Length) && (char.IsDigit(thisCh) == thisNumeric)) + while (thisMarker < s1.Length && char.IsDigit(thisCh) == thisNumeric) { thisChunk.Append(thisCh); thisMarker++; @@ -44,7 +44,7 @@ namespace MediaBrowser.Controller.Sorting } } - while ((thatMarker < s2.Length) && (char.IsDigit(thatCh) == thatNumeric)) + while (thatMarker < s2.Length && char.IsDigit(thatCh) == thatNumeric) { thatChunk.Append(thatCh); thatMarker++; From e80444d11b874f539c9a41f7867c8f93a0fbaeec Mon Sep 17 00:00:00 2001 From: dkanada Date: Fri, 28 Feb 2020 01:43:57 +0900 Subject: [PATCH 052/144] use the custom server for external ids --- .../Configuration/PluginConfiguration.cs | 36 ++++++++++++++++--- .../MusicBrainz/Configuration/config.html | 8 ++--- .../Plugins/MusicBrainz/ExternalIds.cs | 25 ++++++------- .../Plugins/MusicBrainz/Plugin.cs | 20 ++++++----- 4 files changed, 59 insertions(+), 30 deletions(-) diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/PluginConfiguration.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/PluginConfiguration.cs index 607ba0cbcb..bd13cdddd5 100644 --- a/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/PluginConfiguration.cs +++ b/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/PluginConfiguration.cs @@ -1,13 +1,41 @@ -using System.Collections.Generic; -using MediaBrowser.Model.Plugins; +using MediaBrowser.Model.Plugins; namespace MediaBrowser.Providers.Plugins.MusicBrainz { public class PluginConfiguration : BasePluginConfiguration { - public string Server { get; set; } = "https://www.musicbrainz.org"; + private string server = Plugin.Instance.DefaultServer; - public long RateLimit { get; set; } = 1000u; + private long rateLimit = Plugin.Instance.DefaultRateLimit; + + public string Server + { + get + { + return server; + } + + set + { + server = value.TrimEnd('/'); + } + } + + public long RateLimit + { + get + { + return rateLimit; + } + + set + { + if (value < 2000u && server == Plugin.Instance.DefaultServer) + { + RateLimit = Plugin.Instance.DefaultRateLimit; + } + } + } public bool Enable { get; set; } diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/config.html b/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/config.html index dfffa90655..1f02461da2 100644 --- a/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/config.html +++ b/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/config.html @@ -9,12 +9,12 @@
- -
This can either be a mirror of the official server or a custom server.
+ +
This can be a mirror of the official server or even a custom server.
- -
Span of time between each request in milliseconds.
+ +
Span of time between requests in milliseconds. The official server is limited to one request every two seconds.
public void NotifyPendingRestart() { - Logger.LogInformation("App needs to be restarted."); + _logger.LogInformation("App needs to be restarted."); var changed = !HasPendingRestart; @@ -1278,7 +1273,7 @@ namespace Emby.Server.Implementations if (changed) { - EventHelper.QueueEventIfNotNull(HasPendingRestartChanged, this, EventArgs.Empty, Logger); + EventHelper.QueueEventIfNotNull(HasPendingRestartChanged, this, EventArgs.Empty, _logger); } } @@ -1307,10 +1302,10 @@ namespace Emby.Server.Implementations } catch (Exception ex) { - Logger.LogError(ex, "Error sending server restart notification"); + _logger.LogError(ex, "Error sending server restart notification"); } - Logger.LogInformation("Calling RestartInternal"); + _logger.LogInformation("Calling RestartInternal"); RestartInternal(); }); @@ -1335,11 +1330,11 @@ namespace Emby.Server.Implementations } catch (FileLoadException ex) { - Logger.LogError(ex, "Failed to load assembly {Path}", file); + _logger.LogError(ex, "Failed to load assembly {Path}", file); continue; } - Logger.LogInformation("Loaded assembly {Assembly} from {Path}", plugAss.FullName, file); + _logger.LogInformation("Loaded assembly {Assembly} from {Path}", plugAss.FullName, file); yield return plugAss; } } @@ -1474,7 +1469,7 @@ namespace Emby.Server.Implementations } catch (Exception ex) { - Logger.LogError(ex, "Error getting local Ip address information"); + _logger.LogError(ex, "Error getting local Ip address information"); } return null; @@ -1637,19 +1632,19 @@ namespace Emby.Server.Implementations var valid = string.Equals(Name, result, StringComparison.OrdinalIgnoreCase); _validAddressResults.AddOrUpdate(apiUrl, valid, (k, v) => valid); - Logger.LogDebug("Ping test result to {0}. Success: {1}", apiUrl, valid); + _logger.LogDebug("Ping test result to {0}. Success: {1}", apiUrl, valid); return valid; } } } catch (OperationCanceledException) { - Logger.LogDebug("Ping test result to {0}. Success: {1}", apiUrl, "Cancelled"); + _logger.LogDebug("Ping test result to {0}. Success: {1}", apiUrl, "Cancelled"); throw; } catch (Exception ex) { - Logger.LogDebug(ex, "Ping test result to {0}. Success: {1}", apiUrl, false); + _logger.LogDebug(ex, "Ping test result to {0}. Success: {1}", apiUrl, false); _validAddressResults.AddOrUpdate(apiUrl, false, (k, v) => false); return false; @@ -1679,7 +1674,7 @@ namespace Emby.Server.Implementations } catch (Exception ex) { - Logger.LogError(ex, "Error sending server shutdown notification"); + _logger.LogError(ex, "Error sending server shutdown notification"); } ShutdownInternal(); @@ -1741,7 +1736,7 @@ namespace Emby.Server.Implementations } catch (Exception ex) { - Logger.LogError(ex, "Error launching url: {url}", url); + _logger.LogError(ex, "Error launching url: {url}", url); throw; } } @@ -1781,14 +1776,14 @@ namespace Emby.Server.Implementations { var type = GetType(); - Logger.LogInformation("Disposing {Type}", type.Name); + _logger.LogInformation("Disposing {Type}", type.Name); var parts = _disposableParts.Distinct().Where(i => i.GetType() != type).ToList(); _disposableParts.Clear(); foreach (var part in parts) { - Logger.LogInformation("Disposing {Type}", part.GetType().Name); + _logger.LogInformation("Disposing {Type}", part.GetType().Name); try { @@ -1796,7 +1791,7 @@ namespace Emby.Server.Implementations } catch (Exception ex) { - Logger.LogError(ex, "Error disposing {Type}", part.GetType().Name); + _logger.LogError(ex, "Error disposing {Type}", part.GetType().Name); } } From c49a12dd73541c588e22ff558436652c188badb2 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Tue, 3 Mar 2020 23:31:25 +0100 Subject: [PATCH 087/144] Make LoggerFactory private in ApplicationHost and use it to construct loggers with context --- .../ApplicationHost.cs | 90 +++++++++---------- .../SocketSharp/WebSocketSharpListener.cs | 5 +- 2 files changed, 44 insertions(+), 51 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 71cff80af0..9b478772cc 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -118,8 +118,10 @@ namespace Emby.Server.Implementations /// public abstract class ApplicationHost : IServerApplicationHost, IDisposable { - private SqliteUserRepository _userRepository; + private readonly ILoggerFactory _loggerFactory; + private readonly ILogger _logger; + private SqliteUserRepository _userRepository; private SqliteDisplayPreferencesRepository _displayPreferencesRepository; /// @@ -166,7 +168,6 @@ namespace Emby.Server.Implementations /// public bool IsShuttingDown { get; private set; } - private ILogger _logger; private IPlugin[] _plugins; /// @@ -175,12 +176,6 @@ namespace Emby.Server.Implementations /// The plugins. public IReadOnlyList Plugins => _plugins; - /// - /// Gets or sets the logger factory. - /// - /// The logger factory. - public ILoggerFactory LoggerFactory { get; protected set; } - /// /// Gets or sets the application paths. /// @@ -365,6 +360,8 @@ namespace Emby.Server.Implementations INetworkManager networkManager, IConfiguration configuration) { + _loggerFactory = loggerFactory; + _logger = _loggerFactory.CreateLogger("App"); _configuration = configuration; XmlSerializer = new MyXmlSerializer(); @@ -373,12 +370,9 @@ namespace Emby.Server.Implementations networkManager.LocalSubnetsFn = GetConfiguredLocalSubnets; ApplicationPaths = applicationPaths; - LoggerFactory = loggerFactory; FileSystemManager = fileSystem; - ConfigurationManager = new ServerConfigurationManager(ApplicationPaths, LoggerFactory, XmlSerializer, FileSystemManager); - - _logger = LoggerFactory.CreateLogger("App"); + ConfigurationManager = new ServerConfigurationManager(ApplicationPaths, _loggerFactory, XmlSerializer, FileSystemManager); StartupOptions = options; @@ -447,7 +441,7 @@ namespace Emby.Server.Implementations { if (_deviceId == null) { - _deviceId = new DeviceId(ApplicationPaths, LoggerFactory); + _deviceId = new DeviceId(ApplicationPaths, _loggerFactory); } return _deviceId.Value; @@ -647,7 +641,7 @@ namespace Emby.Server.Implementations var response = context.Response; var localPath = context.Request.Path.ToString(); - var req = new WebSocketSharpRequest(request, response, request.Path, _logger); + var req = new WebSocketSharpRequest(request, response, request.Path, _loggerFactory.CreateLogger()); await HttpServer.RequestHandler(req, request.GetDisplayUrl(), request.Host.ToString(), localPath, context.RequestAborted).ConfigureAwait(false); } @@ -675,7 +669,7 @@ namespace Emby.Server.Implementations HttpClient = new HttpClientManager.HttpClientManager( ApplicationPaths, - LoggerFactory.CreateLogger(), + _loggerFactory.CreateLogger(), FileSystemManager, () => ApplicationUserAgent); serviceCollection.AddSingleton(HttpClient); @@ -685,7 +679,7 @@ namespace Emby.Server.Implementations IsoManager = new IsoManager(); serviceCollection.AddSingleton(IsoManager); - TaskManager = new TaskManager(ApplicationPaths, JsonSerializer, LoggerFactory, FileSystemManager); + TaskManager = new TaskManager(ApplicationPaths, JsonSerializer, _loggerFactory, FileSystemManager); serviceCollection.AddSingleton(TaskManager); serviceCollection.AddSingleton(XmlSerializer); @@ -712,22 +706,22 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(ServerConfigurationManager); - LocalizationManager = new LocalizationManager(ServerConfigurationManager, JsonSerializer, LoggerFactory.CreateLogger()); + LocalizationManager = new LocalizationManager(ServerConfigurationManager, JsonSerializer, _loggerFactory.CreateLogger()); await LocalizationManager.LoadAll().ConfigureAwait(false); serviceCollection.AddSingleton(LocalizationManager); serviceCollection.AddSingleton(new BdInfoExaminer(FileSystemManager)); - UserDataManager = new UserDataManager(LoggerFactory, ServerConfigurationManager, () => UserManager); + UserDataManager = new UserDataManager(_loggerFactory, ServerConfigurationManager, () => UserManager); serviceCollection.AddSingleton(UserDataManager); _displayPreferencesRepository = new SqliteDisplayPreferencesRepository( - LoggerFactory.CreateLogger(), + _loggerFactory.CreateLogger(), ApplicationPaths, FileSystemManager); serviceCollection.AddSingleton(_displayPreferencesRepository); - ItemRepository = new SqliteItemRepository(ServerConfigurationManager, this, LoggerFactory.CreateLogger(), LocalizationManager); + ItemRepository = new SqliteItemRepository(ServerConfigurationManager, this, _loggerFactory.CreateLogger(), LocalizationManager); serviceCollection.AddSingleton(ItemRepository); AuthenticationRepository = GetAuthenticationRepository(); @@ -736,7 +730,7 @@ namespace Emby.Server.Implementations _userRepository = GetUserRepository(); UserManager = new UserManager( - LoggerFactory.CreateLogger(), + _loggerFactory.CreateLogger(), _userRepository, XmlSerializer, NetworkManager, @@ -750,7 +744,7 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(UserManager); MediaEncoder = new MediaBrowser.MediaEncoding.Encoder.MediaEncoder( - LoggerFactory.CreateLogger(), + _loggerFactory.CreateLogger(), ServerConfigurationManager, FileSystemManager, ProcessFactory, @@ -760,23 +754,23 @@ namespace Emby.Server.Implementations StartupOptions.FFmpegPath); serviceCollection.AddSingleton(MediaEncoder); - LibraryManager = new LibraryManager(this, LoggerFactory, TaskManager, UserManager, ServerConfigurationManager, UserDataManager, () => LibraryMonitor, FileSystemManager, () => ProviderManager, () => UserViewManager, MediaEncoder); + LibraryManager = new LibraryManager(this, _loggerFactory, TaskManager, UserManager, ServerConfigurationManager, UserDataManager, () => LibraryMonitor, FileSystemManager, () => ProviderManager, () => UserViewManager, MediaEncoder); serviceCollection.AddSingleton(LibraryManager); var musicManager = new MusicManager(LibraryManager); serviceCollection.AddSingleton(musicManager); - LibraryMonitor = new LibraryMonitor(LoggerFactory, LibraryManager, ServerConfigurationManager, FileSystemManager); + LibraryMonitor = new LibraryMonitor(_loggerFactory, LibraryManager, ServerConfigurationManager, FileSystemManager); serviceCollection.AddSingleton(LibraryMonitor); - serviceCollection.AddSingleton(new SearchEngine(LoggerFactory, LibraryManager, UserManager)); + serviceCollection.AddSingleton(new SearchEngine(_loggerFactory, LibraryManager, UserManager)); CertificateInfo = GetCertificateInfo(true); Certificate = GetCertificate(CertificateInfo); HttpServer = new HttpListenerHost( this, - LoggerFactory.CreateLogger(), + _loggerFactory.CreateLogger(), ServerConfigurationManager, _configuration, NetworkManager, @@ -789,7 +783,7 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(HttpServer); - ImageProcessor = new ImageProcessor(LoggerFactory.CreateLogger(), ServerConfigurationManager.ApplicationPaths, FileSystemManager, ImageEncoder, () => LibraryManager, () => MediaEncoder); + ImageProcessor = new ImageProcessor(_loggerFactory.CreateLogger(), ServerConfigurationManager.ApplicationPaths, FileSystemManager, ImageEncoder, () => LibraryManager, () => MediaEncoder); serviceCollection.AddSingleton(ImageProcessor); TVSeriesManager = new TVSeriesManager(UserManager, UserDataManager, LibraryManager, ServerConfigurationManager); @@ -798,23 +792,23 @@ namespace Emby.Server.Implementations DeviceManager = new DeviceManager(AuthenticationRepository, JsonSerializer, LibraryManager, LocalizationManager, UserManager, FileSystemManager, LibraryMonitor, ServerConfigurationManager); serviceCollection.AddSingleton(DeviceManager); - MediaSourceManager = new MediaSourceManager(ItemRepository, ApplicationPaths, LocalizationManager, UserManager, LibraryManager, LoggerFactory, JsonSerializer, FileSystemManager, UserDataManager, () => MediaEncoder); + MediaSourceManager = new MediaSourceManager(ItemRepository, ApplicationPaths, LocalizationManager, UserManager, LibraryManager, _loggerFactory, JsonSerializer, FileSystemManager, UserDataManager, () => MediaEncoder); serviceCollection.AddSingleton(MediaSourceManager); - SubtitleManager = new SubtitleManager(LoggerFactory, FileSystemManager, LibraryMonitor, MediaSourceManager, LocalizationManager); + SubtitleManager = new SubtitleManager(_loggerFactory, FileSystemManager, LibraryMonitor, MediaSourceManager, LocalizationManager); serviceCollection.AddSingleton(SubtitleManager); - ProviderManager = new ProviderManager(HttpClient, SubtitleManager, ServerConfigurationManager, LibraryMonitor, LoggerFactory, FileSystemManager, ApplicationPaths, () => LibraryManager, JsonSerializer); + ProviderManager = new ProviderManager(HttpClient, SubtitleManager, ServerConfigurationManager, LibraryMonitor, _loggerFactory, FileSystemManager, ApplicationPaths, () => LibraryManager, JsonSerializer); serviceCollection.AddSingleton(ProviderManager); - DtoService = new DtoService(LoggerFactory, LibraryManager, UserDataManager, ItemRepository, ImageProcessor, ProviderManager, this, () => MediaSourceManager, () => LiveTvManager); + DtoService = new DtoService(_loggerFactory, LibraryManager, UserDataManager, ItemRepository, ImageProcessor, ProviderManager, this, () => MediaSourceManager, () => LiveTvManager); serviceCollection.AddSingleton(DtoService); - ChannelManager = new ChannelManager(UserManager, DtoService, LibraryManager, LoggerFactory, ServerConfigurationManager, FileSystemManager, UserDataManager, JsonSerializer, ProviderManager); + ChannelManager = new ChannelManager(UserManager, DtoService, LibraryManager, _loggerFactory, ServerConfigurationManager, FileSystemManager, UserDataManager, JsonSerializer, ProviderManager); serviceCollection.AddSingleton(ChannelManager); SessionManager = new SessionManager( - LoggerFactory.CreateLogger(), + _loggerFactory.CreateLogger(), UserDataManager, LibraryManager, UserManager, @@ -828,47 +822,47 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(SessionManager); serviceCollection.AddSingleton( - new DlnaManager(XmlSerializer, FileSystemManager, ApplicationPaths, LoggerFactory, JsonSerializer, this)); + new DlnaManager(XmlSerializer, FileSystemManager, ApplicationPaths, _loggerFactory, JsonSerializer, this)); - CollectionManager = new CollectionManager(LibraryManager, ApplicationPaths, LocalizationManager, FileSystemManager, LibraryMonitor, LoggerFactory, ProviderManager); + CollectionManager = new CollectionManager(LibraryManager, ApplicationPaths, LocalizationManager, FileSystemManager, LibraryMonitor, _loggerFactory, ProviderManager); serviceCollection.AddSingleton(CollectionManager); serviceCollection.AddSingleton(typeof(IPlaylistManager), typeof(PlaylistManager)); - LiveTvManager = new LiveTvManager(this, ServerConfigurationManager, LoggerFactory, ItemRepository, ImageProcessor, UserDataManager, DtoService, UserManager, LibraryManager, TaskManager, LocalizationManager, JsonSerializer, FileSystemManager, () => ChannelManager); + LiveTvManager = new LiveTvManager(this, ServerConfigurationManager, _loggerFactory, ItemRepository, ImageProcessor, UserDataManager, DtoService, UserManager, LibraryManager, TaskManager, LocalizationManager, JsonSerializer, FileSystemManager, () => ChannelManager); serviceCollection.AddSingleton(LiveTvManager); UserViewManager = new UserViewManager(LibraryManager, LocalizationManager, UserManager, ChannelManager, LiveTvManager, ServerConfigurationManager); serviceCollection.AddSingleton(UserViewManager); NotificationManager = new NotificationManager( - LoggerFactory.CreateLogger(), + _loggerFactory.CreateLogger(), UserManager, ServerConfigurationManager); serviceCollection.AddSingleton(NotificationManager); serviceCollection.AddSingleton(new DeviceDiscovery(ServerConfigurationManager)); - ChapterManager = new ChapterManager(LibraryManager, LoggerFactory, ServerConfigurationManager, ItemRepository); + ChapterManager = new ChapterManager(LibraryManager, _loggerFactory, ServerConfigurationManager, ItemRepository); serviceCollection.AddSingleton(ChapterManager); - EncodingManager = new MediaEncoder.EncodingManager(FileSystemManager, LoggerFactory, MediaEncoder, ChapterManager, LibraryManager); + EncodingManager = new MediaEncoder.EncodingManager(FileSystemManager, _loggerFactory, MediaEncoder, ChapterManager, LibraryManager); serviceCollection.AddSingleton(EncodingManager); var activityLogRepo = GetActivityLogRepository(); serviceCollection.AddSingleton(activityLogRepo); - serviceCollection.AddSingleton(new ActivityManager(LoggerFactory, activityLogRepo, UserManager)); + serviceCollection.AddSingleton(new ActivityManager(_loggerFactory, activityLogRepo, UserManager)); var authContext = new AuthorizationContext(AuthenticationRepository, UserManager); serviceCollection.AddSingleton(authContext); serviceCollection.AddSingleton(new SessionContext(UserManager, authContext, SessionManager)); - AuthService = new AuthService(LoggerFactory.CreateLogger(), authContext, ServerConfigurationManager, SessionManager, NetworkManager); + AuthService = new AuthService(_loggerFactory.CreateLogger(), authContext, ServerConfigurationManager, SessionManager, NetworkManager); serviceCollection.AddSingleton(AuthService); SubtitleEncoder = new MediaBrowser.MediaEncoding.Subtitles.SubtitleEncoder( LibraryManager, - LoggerFactory.CreateLogger(), + _loggerFactory.CreateLogger(), ApplicationPaths, FileSystemManager, MediaEncoder, @@ -884,7 +878,7 @@ namespace Emby.Server.Implementations _displayPreferencesRepository.Initialize(); - var userDataRepo = new SqliteUserDataRepository(LoggerFactory.CreateLogger(), ApplicationPaths); + var userDataRepo = new SqliteUserDataRepository(_loggerFactory.CreateLogger(), ApplicationPaths); SetStaticProperties(); @@ -956,7 +950,7 @@ namespace Emby.Server.Implementations private SqliteUserRepository GetUserRepository() { var repo = new SqliteUserRepository( - LoggerFactory.CreateLogger(), + _loggerFactory.CreateLogger(), ApplicationPaths); repo.Initialize(); @@ -966,7 +960,7 @@ namespace Emby.Server.Implementations private IAuthenticationRepository GetAuthenticationRepository() { - var repo = new AuthenticationRepository(LoggerFactory, ServerConfigurationManager); + var repo = new AuthenticationRepository(_loggerFactory, ServerConfigurationManager); repo.Initialize(); @@ -975,7 +969,7 @@ namespace Emby.Server.Implementations private IActivityRepository GetActivityLogRepository() { - var repo = new ActivityRepository(LoggerFactory, ServerConfigurationManager.ApplicationPaths, FileSystemManager); + var repo = new ActivityRepository(_loggerFactory, ServerConfigurationManager.ApplicationPaths, FileSystemManager); repo.Initialize(); @@ -990,7 +984,7 @@ namespace Emby.Server.Implementations ItemRepository.ImageProcessor = ImageProcessor; // For now there's no real way to inject these properly - BaseItem.Logger = LoggerFactory.CreateLogger("BaseItem"); + BaseItem.Logger = _loggerFactory.CreateLogger("BaseItem"); BaseItem.ConfigurationManager = ServerConfigurationManager; BaseItem.LibraryManager = LibraryManager; BaseItem.ProviderManager = ProviderManager; @@ -1200,7 +1194,7 @@ namespace Emby.Server.Implementations }); } - protected IHttpListener CreateHttpListener() => new WebSocketSharpListener(_logger); + protected IHttpListener CreateHttpListener() => new WebSocketSharpListener(_loggerFactory.CreateLogger()); private CertificateInfo GetCertificateInfo(bool generateCertificate) { diff --git a/Emby.Server.Implementations/SocketSharp/WebSocketSharpListener.cs b/Emby.Server.Implementations/SocketSharp/WebSocketSharpListener.cs index 2e12a19fd9..b85750c9b9 100644 --- a/Emby.Server.Implementations/SocketSharp/WebSocketSharpListener.cs +++ b/Emby.Server.Implementations/SocketSharp/WebSocketSharpListener.cs @@ -21,15 +21,14 @@ namespace Emby.Server.Implementations.SocketSharp private CancellationTokenSource _disposeCancellationTokenSource = new CancellationTokenSource(); private CancellationToken _disposeCancellationToken; - public WebSocketSharpListener( - ILogger logger) + public WebSocketSharpListener(ILogger logger) { _logger = logger; - _disposeCancellationToken = _disposeCancellationTokenSource.Token; } public Func ErrorHandler { get; set; } + public Func RequestHandler { get; set; } public Action WebSocketConnected { get; set; } From 6b06a9a91937961eaf0cac610a96a6dd06f678c7 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Tue, 3 Mar 2020 23:53:48 +0100 Subject: [PATCH 088/144] Make Logger and LoggerFactory both protected in ApplicationHost --- .../ApplicationHost.cs | 155 +++++++++--------- 1 file changed, 81 insertions(+), 74 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 9b478772cc..78b65e798d 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -118,9 +118,6 @@ namespace Emby.Server.Implementations /// public abstract class ApplicationHost : IServerApplicationHost, IDisposable { - private readonly ILoggerFactory _loggerFactory; - private readonly ILogger _logger; - private SqliteUserRepository _userRepository; private SqliteDisplayPreferencesRepository _displayPreferencesRepository; @@ -168,6 +165,16 @@ namespace Emby.Server.Implementations /// public bool IsShuttingDown { get; private set; } + /// + /// Gets the logger factory. + /// + protected ILoggerFactory LoggerFactory { get; } + + /// + /// Gets the logger. + /// + protected ILogger Logger { get; } + private IPlugin[] _plugins; /// @@ -360,8 +367,6 @@ namespace Emby.Server.Implementations INetworkManager networkManager, IConfiguration configuration) { - _loggerFactory = loggerFactory; - _logger = _loggerFactory.CreateLogger("App"); _configuration = configuration; XmlSerializer = new MyXmlSerializer(); @@ -370,9 +375,11 @@ namespace Emby.Server.Implementations networkManager.LocalSubnetsFn = GetConfiguredLocalSubnets; ApplicationPaths = applicationPaths; + LoggerFactory = loggerFactory; FileSystemManager = fileSystem; - ConfigurationManager = new ServerConfigurationManager(ApplicationPaths, _loggerFactory, XmlSerializer, FileSystemManager); + Logger = LoggerFactory.CreateLogger("App"); + ConfigurationManager = new ServerConfigurationManager(ApplicationPaths, LoggerFactory, XmlSerializer, FileSystemManager); StartupOptions = options; @@ -441,7 +448,7 @@ namespace Emby.Server.Implementations { if (_deviceId == null) { - _deviceId = new DeviceId(ApplicationPaths, _loggerFactory); + _deviceId = new DeviceId(ApplicationPaths, LoggerFactory); } return _deviceId.Value; @@ -479,12 +486,12 @@ namespace Emby.Server.Implementations { try { - _logger.LogDebug("Creating instance of {Type}", type); + Logger.LogDebug("Creating instance of {Type}", type); return ActivatorUtilities.CreateInstance(ServiceProvider, type); } catch (Exception ex) { - _logger.LogError(ex, "Error creating {Type}", type); + Logger.LogError(ex, "Error creating {Type}", type); return null; } } @@ -535,7 +542,7 @@ namespace Emby.Server.Implementations /// . public async Task RunStartupTasksAsync() { - _logger.LogInformation("Running startup tasks"); + Logger.LogInformation("Running startup tasks"); Resolve().AddTasks(GetExports(false)); @@ -543,21 +550,21 @@ namespace Emby.Server.Implementations MediaEncoder.SetFFmpegPath(); - _logger.LogInformation("ServerId: {0}", SystemId); + Logger.LogInformation("ServerId: {0}", SystemId); var entryPoints = GetExports(); var stopWatch = new Stopwatch(); stopWatch.Start(); await Task.WhenAll(StartEntryPoints(entryPoints, true)).ConfigureAwait(false); - _logger.LogInformation("Executed all pre-startup entry points in {Elapsed:g}", stopWatch.Elapsed); + Logger.LogInformation("Executed all pre-startup entry points in {Elapsed:g}", stopWatch.Elapsed); - _logger.LogInformation("Core startup complete"); + Logger.LogInformation("Core startup complete"); HttpServer.GlobalResponse = null; stopWatch.Restart(); await Task.WhenAll(StartEntryPoints(entryPoints, false)).ConfigureAwait(false); - _logger.LogInformation("Executed all post-startup entry points in {Elapsed:g}", stopWatch.Elapsed); + Logger.LogInformation("Executed all post-startup entry points in {Elapsed:g}", stopWatch.Elapsed); stopWatch.Stop(); } @@ -570,7 +577,7 @@ namespace Emby.Server.Implementations continue; } - _logger.LogDebug("Starting entry point {Type}", entryPoint.GetType()); + Logger.LogDebug("Starting entry point {Type}", entryPoint.GetType()); yield return entryPoint.RunAsync(); } @@ -604,7 +611,7 @@ namespace Emby.Server.Implementations plugin.Version)); } - _logger.LogInformation("Plugins: {Plugins}", pluginBuilder.ToString()); + Logger.LogInformation("Plugins: {Plugins}", pluginBuilder.ToString()); } DiscoverTypes(); @@ -641,7 +648,7 @@ namespace Emby.Server.Implementations var response = context.Response; var localPath = context.Request.Path.ToString(); - var req = new WebSocketSharpRequest(request, response, request.Path, _loggerFactory.CreateLogger()); + var req = new WebSocketSharpRequest(request, response, request.Path, LoggerFactory.CreateLogger()); await HttpServer.RequestHandler(req, request.GetDisplayUrl(), request.Host.ToString(), localPath, context.RequestAborted).ConfigureAwait(false); } @@ -662,14 +669,14 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(JsonSerializer); // TODO: Support for injecting ILogger should be deprecated in favour of ILogger and this removed - serviceCollection.AddSingleton(_logger); + serviceCollection.AddSingleton(Logger); serviceCollection.AddSingleton(FileSystemManager); serviceCollection.AddSingleton(); HttpClient = new HttpClientManager.HttpClientManager( ApplicationPaths, - _loggerFactory.CreateLogger(), + LoggerFactory.CreateLogger(), FileSystemManager, () => ApplicationUserAgent); serviceCollection.AddSingleton(HttpClient); @@ -679,7 +686,7 @@ namespace Emby.Server.Implementations IsoManager = new IsoManager(); serviceCollection.AddSingleton(IsoManager); - TaskManager = new TaskManager(ApplicationPaths, JsonSerializer, _loggerFactory, FileSystemManager); + TaskManager = new TaskManager(ApplicationPaths, JsonSerializer, LoggerFactory, FileSystemManager); serviceCollection.AddSingleton(TaskManager); serviceCollection.AddSingleton(XmlSerializer); @@ -706,22 +713,22 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(ServerConfigurationManager); - LocalizationManager = new LocalizationManager(ServerConfigurationManager, JsonSerializer, _loggerFactory.CreateLogger()); + LocalizationManager = new LocalizationManager(ServerConfigurationManager, JsonSerializer, LoggerFactory.CreateLogger()); await LocalizationManager.LoadAll().ConfigureAwait(false); serviceCollection.AddSingleton(LocalizationManager); serviceCollection.AddSingleton(new BdInfoExaminer(FileSystemManager)); - UserDataManager = new UserDataManager(_loggerFactory, ServerConfigurationManager, () => UserManager); + UserDataManager = new UserDataManager(LoggerFactory, ServerConfigurationManager, () => UserManager); serviceCollection.AddSingleton(UserDataManager); _displayPreferencesRepository = new SqliteDisplayPreferencesRepository( - _loggerFactory.CreateLogger(), + LoggerFactory.CreateLogger(), ApplicationPaths, FileSystemManager); serviceCollection.AddSingleton(_displayPreferencesRepository); - ItemRepository = new SqliteItemRepository(ServerConfigurationManager, this, _loggerFactory.CreateLogger(), LocalizationManager); + ItemRepository = new SqliteItemRepository(ServerConfigurationManager, this, LoggerFactory.CreateLogger(), LocalizationManager); serviceCollection.AddSingleton(ItemRepository); AuthenticationRepository = GetAuthenticationRepository(); @@ -730,7 +737,7 @@ namespace Emby.Server.Implementations _userRepository = GetUserRepository(); UserManager = new UserManager( - _loggerFactory.CreateLogger(), + LoggerFactory.CreateLogger(), _userRepository, XmlSerializer, NetworkManager, @@ -744,7 +751,7 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(UserManager); MediaEncoder = new MediaBrowser.MediaEncoding.Encoder.MediaEncoder( - _loggerFactory.CreateLogger(), + LoggerFactory.CreateLogger(), ServerConfigurationManager, FileSystemManager, ProcessFactory, @@ -754,23 +761,23 @@ namespace Emby.Server.Implementations StartupOptions.FFmpegPath); serviceCollection.AddSingleton(MediaEncoder); - LibraryManager = new LibraryManager(this, _loggerFactory, TaskManager, UserManager, ServerConfigurationManager, UserDataManager, () => LibraryMonitor, FileSystemManager, () => ProviderManager, () => UserViewManager, MediaEncoder); + LibraryManager = new LibraryManager(this, LoggerFactory, TaskManager, UserManager, ServerConfigurationManager, UserDataManager, () => LibraryMonitor, FileSystemManager, () => ProviderManager, () => UserViewManager, MediaEncoder); serviceCollection.AddSingleton(LibraryManager); var musicManager = new MusicManager(LibraryManager); serviceCollection.AddSingleton(musicManager); - LibraryMonitor = new LibraryMonitor(_loggerFactory, LibraryManager, ServerConfigurationManager, FileSystemManager); + LibraryMonitor = new LibraryMonitor(LoggerFactory, LibraryManager, ServerConfigurationManager, FileSystemManager); serviceCollection.AddSingleton(LibraryMonitor); - serviceCollection.AddSingleton(new SearchEngine(_loggerFactory, LibraryManager, UserManager)); + serviceCollection.AddSingleton(new SearchEngine(LoggerFactory, LibraryManager, UserManager)); CertificateInfo = GetCertificateInfo(true); Certificate = GetCertificate(CertificateInfo); HttpServer = new HttpListenerHost( this, - _loggerFactory.CreateLogger(), + LoggerFactory.CreateLogger(), ServerConfigurationManager, _configuration, NetworkManager, @@ -783,7 +790,7 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(HttpServer); - ImageProcessor = new ImageProcessor(_loggerFactory.CreateLogger(), ServerConfigurationManager.ApplicationPaths, FileSystemManager, ImageEncoder, () => LibraryManager, () => MediaEncoder); + ImageProcessor = new ImageProcessor(LoggerFactory.CreateLogger(), ServerConfigurationManager.ApplicationPaths, FileSystemManager, ImageEncoder, () => LibraryManager, () => MediaEncoder); serviceCollection.AddSingleton(ImageProcessor); TVSeriesManager = new TVSeriesManager(UserManager, UserDataManager, LibraryManager, ServerConfigurationManager); @@ -792,23 +799,23 @@ namespace Emby.Server.Implementations DeviceManager = new DeviceManager(AuthenticationRepository, JsonSerializer, LibraryManager, LocalizationManager, UserManager, FileSystemManager, LibraryMonitor, ServerConfigurationManager); serviceCollection.AddSingleton(DeviceManager); - MediaSourceManager = new MediaSourceManager(ItemRepository, ApplicationPaths, LocalizationManager, UserManager, LibraryManager, _loggerFactory, JsonSerializer, FileSystemManager, UserDataManager, () => MediaEncoder); + MediaSourceManager = new MediaSourceManager(ItemRepository, ApplicationPaths, LocalizationManager, UserManager, LibraryManager, LoggerFactory, JsonSerializer, FileSystemManager, UserDataManager, () => MediaEncoder); serviceCollection.AddSingleton(MediaSourceManager); - SubtitleManager = new SubtitleManager(_loggerFactory, FileSystemManager, LibraryMonitor, MediaSourceManager, LocalizationManager); + SubtitleManager = new SubtitleManager(LoggerFactory, FileSystemManager, LibraryMonitor, MediaSourceManager, LocalizationManager); serviceCollection.AddSingleton(SubtitleManager); - ProviderManager = new ProviderManager(HttpClient, SubtitleManager, ServerConfigurationManager, LibraryMonitor, _loggerFactory, FileSystemManager, ApplicationPaths, () => LibraryManager, JsonSerializer); + ProviderManager = new ProviderManager(HttpClient, SubtitleManager, ServerConfigurationManager, LibraryMonitor, LoggerFactory, FileSystemManager, ApplicationPaths, () => LibraryManager, JsonSerializer); serviceCollection.AddSingleton(ProviderManager); - DtoService = new DtoService(_loggerFactory, LibraryManager, UserDataManager, ItemRepository, ImageProcessor, ProviderManager, this, () => MediaSourceManager, () => LiveTvManager); + DtoService = new DtoService(LoggerFactory, LibraryManager, UserDataManager, ItemRepository, ImageProcessor, ProviderManager, this, () => MediaSourceManager, () => LiveTvManager); serviceCollection.AddSingleton(DtoService); - ChannelManager = new ChannelManager(UserManager, DtoService, LibraryManager, _loggerFactory, ServerConfigurationManager, FileSystemManager, UserDataManager, JsonSerializer, ProviderManager); + ChannelManager = new ChannelManager(UserManager, DtoService, LibraryManager, LoggerFactory, ServerConfigurationManager, FileSystemManager, UserDataManager, JsonSerializer, ProviderManager); serviceCollection.AddSingleton(ChannelManager); SessionManager = new SessionManager( - _loggerFactory.CreateLogger(), + LoggerFactory.CreateLogger(), UserDataManager, LibraryManager, UserManager, @@ -822,47 +829,47 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(SessionManager); serviceCollection.AddSingleton( - new DlnaManager(XmlSerializer, FileSystemManager, ApplicationPaths, _loggerFactory, JsonSerializer, this)); + new DlnaManager(XmlSerializer, FileSystemManager, ApplicationPaths, LoggerFactory, JsonSerializer, this)); - CollectionManager = new CollectionManager(LibraryManager, ApplicationPaths, LocalizationManager, FileSystemManager, LibraryMonitor, _loggerFactory, ProviderManager); + CollectionManager = new CollectionManager(LibraryManager, ApplicationPaths, LocalizationManager, FileSystemManager, LibraryMonitor, LoggerFactory, ProviderManager); serviceCollection.AddSingleton(CollectionManager); serviceCollection.AddSingleton(typeof(IPlaylistManager), typeof(PlaylistManager)); - LiveTvManager = new LiveTvManager(this, ServerConfigurationManager, _loggerFactory, ItemRepository, ImageProcessor, UserDataManager, DtoService, UserManager, LibraryManager, TaskManager, LocalizationManager, JsonSerializer, FileSystemManager, () => ChannelManager); + LiveTvManager = new LiveTvManager(this, ServerConfigurationManager, LoggerFactory, ItemRepository, ImageProcessor, UserDataManager, DtoService, UserManager, LibraryManager, TaskManager, LocalizationManager, JsonSerializer, FileSystemManager, () => ChannelManager); serviceCollection.AddSingleton(LiveTvManager); UserViewManager = new UserViewManager(LibraryManager, LocalizationManager, UserManager, ChannelManager, LiveTvManager, ServerConfigurationManager); serviceCollection.AddSingleton(UserViewManager); NotificationManager = new NotificationManager( - _loggerFactory.CreateLogger(), + LoggerFactory.CreateLogger(), UserManager, ServerConfigurationManager); serviceCollection.AddSingleton(NotificationManager); serviceCollection.AddSingleton(new DeviceDiscovery(ServerConfigurationManager)); - ChapterManager = new ChapterManager(LibraryManager, _loggerFactory, ServerConfigurationManager, ItemRepository); + ChapterManager = new ChapterManager(LibraryManager, LoggerFactory, ServerConfigurationManager, ItemRepository); serviceCollection.AddSingleton(ChapterManager); - EncodingManager = new MediaEncoder.EncodingManager(FileSystemManager, _loggerFactory, MediaEncoder, ChapterManager, LibraryManager); + EncodingManager = new MediaEncoder.EncodingManager(FileSystemManager, LoggerFactory, MediaEncoder, ChapterManager, LibraryManager); serviceCollection.AddSingleton(EncodingManager); var activityLogRepo = GetActivityLogRepository(); serviceCollection.AddSingleton(activityLogRepo); - serviceCollection.AddSingleton(new ActivityManager(_loggerFactory, activityLogRepo, UserManager)); + serviceCollection.AddSingleton(new ActivityManager(LoggerFactory, activityLogRepo, UserManager)); var authContext = new AuthorizationContext(AuthenticationRepository, UserManager); serviceCollection.AddSingleton(authContext); serviceCollection.AddSingleton(new SessionContext(UserManager, authContext, SessionManager)); - AuthService = new AuthService(_loggerFactory.CreateLogger(), authContext, ServerConfigurationManager, SessionManager, NetworkManager); + AuthService = new AuthService(LoggerFactory.CreateLogger(), authContext, ServerConfigurationManager, SessionManager, NetworkManager); serviceCollection.AddSingleton(AuthService); SubtitleEncoder = new MediaBrowser.MediaEncoding.Subtitles.SubtitleEncoder( LibraryManager, - _loggerFactory.CreateLogger(), + LoggerFactory.CreateLogger(), ApplicationPaths, FileSystemManager, MediaEncoder, @@ -878,7 +885,7 @@ namespace Emby.Server.Implementations _displayPreferencesRepository.Initialize(); - var userDataRepo = new SqliteUserDataRepository(_loggerFactory.CreateLogger(), ApplicationPaths); + var userDataRepo = new SqliteUserDataRepository(LoggerFactory.CreateLogger(), ApplicationPaths); SetStaticProperties(); @@ -930,7 +937,7 @@ namespace Emby.Server.Implementations // localCert.PrivateKey = PrivateKey.CreateFromFile(pvk_file).RSA; if (!localCert.HasPrivateKey) { - _logger.LogError("No private key included in SSL cert {CertificateLocation}.", certificateLocation); + Logger.LogError("No private key included in SSL cert {CertificateLocation}.", certificateLocation); return null; } @@ -938,7 +945,7 @@ namespace Emby.Server.Implementations } catch (Exception ex) { - _logger.LogError(ex, "Error loading cert from {CertificateLocation}", certificateLocation); + Logger.LogError(ex, "Error loading cert from {CertificateLocation}", certificateLocation); return null; } } @@ -950,7 +957,7 @@ namespace Emby.Server.Implementations private SqliteUserRepository GetUserRepository() { var repo = new SqliteUserRepository( - _loggerFactory.CreateLogger(), + LoggerFactory.CreateLogger(), ApplicationPaths); repo.Initialize(); @@ -960,7 +967,7 @@ namespace Emby.Server.Implementations private IAuthenticationRepository GetAuthenticationRepository() { - var repo = new AuthenticationRepository(_loggerFactory, ServerConfigurationManager); + var repo = new AuthenticationRepository(LoggerFactory, ServerConfigurationManager); repo.Initialize(); @@ -969,7 +976,7 @@ namespace Emby.Server.Implementations private IActivityRepository GetActivityLogRepository() { - var repo = new ActivityRepository(_loggerFactory, ServerConfigurationManager.ApplicationPaths, FileSystemManager); + var repo = new ActivityRepository(LoggerFactory, ServerConfigurationManager.ApplicationPaths, FileSystemManager); repo.Initialize(); @@ -984,7 +991,7 @@ namespace Emby.Server.Implementations ItemRepository.ImageProcessor = ImageProcessor; // For now there's no real way to inject these properly - BaseItem.Logger = _loggerFactory.CreateLogger("BaseItem"); + BaseItem.Logger = LoggerFactory.CreateLogger("BaseItem"); BaseItem.ConfigurationManager = ServerConfigurationManager; BaseItem.LibraryManager = LibraryManager; BaseItem.ProviderManager = ProviderManager; @@ -1117,7 +1124,7 @@ namespace Emby.Server.Implementations } catch (Exception ex) { - _logger.LogError(ex, "Error getting plugin Id from {PluginName}.", plugin.GetType().FullName); + Logger.LogError(ex, "Error getting plugin Id from {PluginName}.", plugin.GetType().FullName); } } @@ -1128,7 +1135,7 @@ namespace Emby.Server.Implementations } catch (Exception ex) { - _logger.LogError(ex, "Error loading plugin {PluginName}", plugin.GetType().FullName); + Logger.LogError(ex, "Error loading plugin {PluginName}", plugin.GetType().FullName); return null; } @@ -1140,7 +1147,7 @@ namespace Emby.Server.Implementations /// protected void DiscoverTypes() { - _logger.LogInformation("Loading assemblies"); + Logger.LogInformation("Loading assemblies"); _allConcreteTypes = GetTypes(GetComposablePartAssemblies()).ToArray(); } @@ -1156,7 +1163,7 @@ namespace Emby.Server.Implementations } catch (TypeLoadException ex) { - _logger.LogError(ex, "Error getting exported types from {Assembly}", ass.FullName); + Logger.LogError(ex, "Error getting exported types from {Assembly}", ass.FullName); continue; } @@ -1194,7 +1201,7 @@ namespace Emby.Server.Implementations }); } - protected IHttpListener CreateHttpListener() => new WebSocketSharpListener(_loggerFactory.CreateLogger()); + protected IHttpListener CreateHttpListener() => new WebSocketSharpListener(LoggerFactory.CreateLogger()); private CertificateInfo GetCertificateInfo(bool generateCertificate) { @@ -1248,7 +1255,7 @@ namespace Emby.Server.Implementations if (requiresRestart) { - _logger.LogInformation("App needs to be restarted due to configuration change."); + Logger.LogInformation("App needs to be restarted due to configuration change."); NotifyPendingRestart(); } @@ -1259,7 +1266,7 @@ namespace Emby.Server.Implementations /// public void NotifyPendingRestart() { - _logger.LogInformation("App needs to be restarted."); + Logger.LogInformation("App needs to be restarted."); var changed = !HasPendingRestart; @@ -1267,7 +1274,7 @@ namespace Emby.Server.Implementations if (changed) { - EventHelper.QueueEventIfNotNull(HasPendingRestartChanged, this, EventArgs.Empty, _logger); + EventHelper.QueueEventIfNotNull(HasPendingRestartChanged, this, EventArgs.Empty, Logger); } } @@ -1296,10 +1303,10 @@ namespace Emby.Server.Implementations } catch (Exception ex) { - _logger.LogError(ex, "Error sending server restart notification"); + Logger.LogError(ex, "Error sending server restart notification"); } - _logger.LogInformation("Calling RestartInternal"); + Logger.LogInformation("Calling RestartInternal"); RestartInternal(); }); @@ -1324,11 +1331,11 @@ namespace Emby.Server.Implementations } catch (FileLoadException ex) { - _logger.LogError(ex, "Failed to load assembly {Path}", file); + Logger.LogError(ex, "Failed to load assembly {Path}", file); continue; } - _logger.LogInformation("Loaded assembly {Assembly} from {Path}", plugAss.FullName, file); + Logger.LogInformation("Loaded assembly {Assembly} from {Path}", plugAss.FullName, file); yield return plugAss; } } @@ -1463,7 +1470,7 @@ namespace Emby.Server.Implementations } catch (Exception ex) { - _logger.LogError(ex, "Error getting local Ip address information"); + Logger.LogError(ex, "Error getting local Ip address information"); } return null; @@ -1626,19 +1633,19 @@ namespace Emby.Server.Implementations var valid = string.Equals(Name, result, StringComparison.OrdinalIgnoreCase); _validAddressResults.AddOrUpdate(apiUrl, valid, (k, v) => valid); - _logger.LogDebug("Ping test result to {0}. Success: {1}", apiUrl, valid); + Logger.LogDebug("Ping test result to {0}. Success: {1}", apiUrl, valid); return valid; } } } catch (OperationCanceledException) { - _logger.LogDebug("Ping test result to {0}. Success: {1}", apiUrl, "Cancelled"); + Logger.LogDebug("Ping test result to {0}. Success: {1}", apiUrl, "Cancelled"); throw; } catch (Exception ex) { - _logger.LogDebug(ex, "Ping test result to {0}. Success: {1}", apiUrl, false); + Logger.LogDebug(ex, "Ping test result to {0}. Success: {1}", apiUrl, false); _validAddressResults.AddOrUpdate(apiUrl, false, (k, v) => false); return false; @@ -1668,7 +1675,7 @@ namespace Emby.Server.Implementations } catch (Exception ex) { - _logger.LogError(ex, "Error sending server shutdown notification"); + Logger.LogError(ex, "Error sending server shutdown notification"); } ShutdownInternal(); @@ -1730,7 +1737,7 @@ namespace Emby.Server.Implementations } catch (Exception ex) { - _logger.LogError(ex, "Error launching url: {url}", url); + Logger.LogError(ex, "Error launching url: {url}", url); throw; } } @@ -1770,14 +1777,14 @@ namespace Emby.Server.Implementations { var type = GetType(); - _logger.LogInformation("Disposing {Type}", type.Name); + Logger.LogInformation("Disposing {Type}", type.Name); var parts = _disposableParts.Distinct().Where(i => i.GetType() != type).ToList(); _disposableParts.Clear(); foreach (var part in parts) { - _logger.LogInformation("Disposing {Type}", part.GetType().Name); + Logger.LogInformation("Disposing {Type}", part.GetType().Name); try { @@ -1785,7 +1792,7 @@ namespace Emby.Server.Implementations } catch (Exception ex) { - _logger.LogError(ex, "Error disposing {Type}", part.GetType().Name); + Logger.LogError(ex, "Error disposing {Type}", part.GetType().Name); } } From 9aa259eb95bcb77fd1c1c7c4c115cbf6dcda7286 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Tue, 3 Mar 2020 23:56:47 +0100 Subject: [PATCH 089/144] Revert unnecessary ordering changes in ApplicationHost --- Emby.Server.Implementations/ApplicationHost.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 78b65e798d..d51e74a4de 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -165,11 +165,6 @@ namespace Emby.Server.Implementations /// public bool IsShuttingDown { get; private set; } - /// - /// Gets the logger factory. - /// - protected ILoggerFactory LoggerFactory { get; } - /// /// Gets the logger. /// @@ -183,6 +178,11 @@ namespace Emby.Server.Implementations /// The plugins. public IReadOnlyList Plugins => _plugins; + /// + /// Gets the logger factory. + /// + protected ILoggerFactory LoggerFactory { get; } + /// /// Gets or sets the application paths. /// @@ -378,9 +378,10 @@ namespace Emby.Server.Implementations LoggerFactory = loggerFactory; FileSystemManager = fileSystem; - Logger = LoggerFactory.CreateLogger("App"); ConfigurationManager = new ServerConfigurationManager(ApplicationPaths, LoggerFactory, XmlSerializer, FileSystemManager); + Logger = LoggerFactory.CreateLogger("App"); + StartupOptions = options; ImageEncoder = imageEncoder; From 0e98d8bbadf37e237e32abbc79819005d79212cb Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Tue, 3 Mar 2020 22:51:16 -0500 Subject: [PATCH 090/144] Update contributors list Refreshes the contributors list with the data available from all merged PRs. Includes a huge number of people who had never added themselves manually. Going forward this is probably not needed anymore as we can update this fairly easily. --- CONTRIBUTORS.md | 150 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 121 insertions(+), 29 deletions(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index e14636a577..af459fa2a0 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -1,41 +1,133 @@ # Jellyfin Contributors - - [JoshuaBoniface](https://github.com/joshuaboniface) - - [nvllsvm](https://github.com/nvllsvm) - - [JustAMan](https://github.com/JustAMan) - - [dcrdev](https://github.com/dcrdev) - - [EraYaN](https://github.com/EraYaN) - - [flemse](https://github.com/flemse) + - [97carmine](https://github.com/97carmine) + - [Abbe98](https://github.com/Abbe98) + - [agrenott](https://github.com/agrenott) + - [AndreCarvalho](https://github.com/AndreCarvalho) + - [anthonylavado](https://github.com/anthonylavado) + - [Artiume](https://github.com/Artiume) + - [AThomsen](https://github.com/AThomsen) + - [bilde2910](https://github.com/bilde2910) - [bfayers](https://github.com/bfayers) - - [Bond_009](https://github.com/Bond-009) - - [AnthonyLavado](https://github.com/anthonylavado) - - [sparky8251](https://github.com/sparky8251) - - [LeoVerto](https://github.com/LeoVerto) - - [grafixeyehero](https://github.com/grafixeyehero) + - [BnMcG](https://github.com/BnMcG) + - [Bond-009](https://github.com/Bond-009) + - [brianjmurrell](https://github.com/brianjmurrell) + - [bugfixin](https://github.com/bugfixin) + - [chaosinnovator](https://github.com/chaosinnovator) + - [ckcr4lyf](https://github.com/ckcr4lyf) + - [crankdoofus](https://github.com/crankdoofus) + - [crobibero](https://github.com/crobibero) + - [cromefire](https://github.com/cromefire) + - [cryptobank](https://github.com/cryptobank) - [cvium](https://github.com/cvium) - - [wtayl0r](https://github.com/wtayl0r) - - [TtheCreator](https://github.com/Tthecreator) + - [dannymichel](https://github.com/dannymichel) + - [DaveChild](https://github.com/DaveChild) + - [dcrdev](https://github.com/dcrdev) + - [dependabotbot](https://github.com/dependabotbot) + - [dhartung](https://github.com/dhartung) + - [dinki](https://github.com/dinki) - [dkanada](https://github.com/dkanada) - - [LogicalPhallacy](https://github.com/LogicalPhallacy/) - - [RazeLighter777](https://github.com/RazeLighter777) - - [WillWill56](https://github.com/WillWill56) - - [Liggy](https://github.com/Liggy) - - [fruhnow](https://github.com/fruhnow) - - [Lynxy](https://github.com/Lynxy) + - [dlahoti](https://github.com/dlahoti) + - [dmitrylyzo](https://github.com/dmitrylyzo) + - [DMouse10462](https://github.com/DMouse10462) + - [DrPandemic](https://github.com/DrPandemic) + - [EraYaN](https://github.com/EraYaN) + - [escabe](https://github.com/escabe) + - [excelite](https://github.com/excelite) - [fasheng](https://github.com/fasheng) - - [ploughpuff](https://github.com/ploughpuff) - - [pjeanjean](https://github.com/pjeanjean) - - [DrPandemic](https://github.com/drpandemic) - - [joern-h](https://github.com/joern-h) - - [Khinenw](https://github.com/HelloWorld017) + - [ferferga](https://github.com/ferferga) - [fhriley](https://github.com/fhriley) - - [nevado](https://github.com/nevado) - - [mark-monteiro](https://github.com/mark-monteiro) - - [ullmie02](https://github.com/ullmie02) + - [flemse](https://github.com/flemse) + - [Froghut](https://github.com/Froghut) + - [fruhnow](https://github.com/fruhnow) - [geilername](https://github.com/geilername) + - [gnattu](https://github.com/gnattu) + - [grafixeyehero](https://github.com/grafixeyehero) + - [h1nk](https://github.com/h1nk) + - [hawken93](https://github.com/hawken93) + - [HelloWorld017](https://github.com/HelloWorld017) + - [jftuga](https://github.com/jftuga) + - [joern-h](https://github.com/joern-h) + - [joshuaboniface](https://github.com/joshuaboniface) + - [JustAMan](https://github.com/JustAMan) + - [justinfenn](https://github.com/justinfenn) + - [KerryRJ](https://github.com/KerryRJ) + - [Larvitar](https://github.com/Larvitar) + - [LeoVerto](https://github.com/LeoVerto) + - [Liggy](https://github.com/Liggy) + - [LogicalPhallacy](https://github.com/LogicalPhallacy) + - [loli10K](https://github.com/loli10K) + - [lostmypillow](https://github.com/lostmypillow) + - [Lynxy](https://github.com/Lynxy) + - [ManfredRichthofen](https://github.com/ManfredRichthofen) + - [Marenz](https://github.com/Marenz) + - [marius-luca-87](https://github.com/marius-luca-87) + - [mark-monteiro](https://github.com/mark-monteiro) + - [Matt07211](https://github.com/Matt07211) + - [mcarlton00](https://github.com/mcarlton00) + - [mitchfizz05](https://github.com/mitchfizz05) + - [MrTimscampi](https://github.com/MrTimscampi) + - [n8225](https://github.com/n8225) + - [Narfinger](https://github.com/Narfinger) + - [NathanPickard](https://github.com/NathanPickard) + - [neilsb](https://github.com/neilsb) + - [nevado](https://github.com/nevado) + - [Nickbert7](https://github.com/Nickbert7) + - [nvllsvm](https://github.com/nvllsvm) + - [nyanmisaka](https://github.com/nyanmisaka) + - [oddstr13](https://github.com/oddstr13) + - [petermcneil](https://github.com/petermcneil) + - [Phlogi](https://github.com/Phlogi) + - [pjeanjean](https://github.com/pjeanjean) + - [ploughpuff](https://github.com/ploughpuff) - [pR0Ps](https://github.com/pR0Ps) - - [artiume](https://github.com/Artiume) - + - [PrplHaz4](https://github.com/PrplHaz4) + - [RazeLighter777](https://github.com/RazeLighter777) + - [redSpoutnik](https://github.com/redSpoutnik) + - [ringmatter](https://github.com/ringmatter) + - [ryan-hartzell](https://github.com/ryan-hartzell) + - [s0urcelab](https://github.com/s0urcelab) + - [sachk](https://github.com/sachk) + - [sammyrc34](https://github.com/sammyrc34) + - [samuel9554](https://github.com/samuel9554) + - [scheidleon](https://github.com/scheidleon) + - [sebPomme](https://github.com/sebPomme) + - [SenorSmartyPants](https://github.com/SenorSmartyPants) + - [shemanaev](https://github.com/shemanaev) + - [skaro13](https://github.com/skaro13) + - [sl1288](https://github.com/sl1288) + - [sorinyo2004](https://github.com/sorinyo2004) + - [sparky8251](https://github.com/sparky8251) + - [stanionascu](https://github.com/stanionascu) + - [stevehayles](https://github.com/stevehayles) + - [SuperSandro2000](https://github.com/SuperSandro2000) + - [tbraeutigam](https://github.com/tbraeutigam) + - [teacupx](https://github.com/teacupx) + - [Terror-Gene](https://github.com/Terror-Gene) + - [ThatNerdyPikachu](https://github.com/ThatNerdyPikachu) + - [ThibaultNocchi](https://github.com/ThibaultNocchi) + - [thornbill](https://github.com/thornbill) + - [ThreeFive-O](https://github.com/ThreeFive-O) + - [TrisMcC](https://github.com/TrisMcC) + - [trumblejoe](https://github.com/trumblejoe) + - [TtheCreator](https://github.com/TtheCreator) + - [twinkybot](https://github.com/twinkybot) + - [Ullmie02](https://github.com/Ullmie02) + - [Unhelpful](https://github.com/Unhelpful) + - [viaregio](https://github.com/viaregio) + - [vitorsemeano](https://github.com/vitorsemeano) + - [voodoos](https://github.com/voodoos) + - [whooo](https://github.com/whooo) + - [WiiPlayer2](https://github.com/WiiPlayer2) + - [WillWill56](https://github.com/WillWill56) + - [wtayl0r](https://github.com/wtayl0r) + - [Wuerfelbecher](https://github.com/Wuerfelbecher) + - [Wunax](https://github.com/Wunax) + - [WWWesten](https://github.com/WWWesten) + - [WX9yMOXWId](https://github.com/WX9yMOXWId) + - [xosdy](https://github.com/xosdy) + - [XVicarious](https://github.com/XVicarious) + - [YouKnowBlom](https://github.com/YouKnowBlom) # Emby Contributors From 3016e78aef7512a3582dd872ee6e1e592f300734 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Tue, 3 Mar 2020 23:22:29 -0500 Subject: [PATCH 091/144] Remove dependabotbot --- CONTRIBUTORS.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index af459fa2a0..4f36249655 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -23,7 +23,6 @@ - [dannymichel](https://github.com/dannymichel) - [DaveChild](https://github.com/DaveChild) - [dcrdev](https://github.com/dcrdev) - - [dependabotbot](https://github.com/dependabotbot) - [dhartung](https://github.com/dhartung) - [dinki](https://github.com/dinki) - [dkanada](https://github.com/dkanada) From 007c5b9f6734fc7a7b49ad488fe3733edf9c3c67 Mon Sep 17 00:00:00 2001 From: Vasily Date: Wed, 4 Mar 2020 13:06:13 +0300 Subject: [PATCH 092/144] Implement BaseItem.GetHashCode override --- MediaBrowser.Controller/Entities/BaseItem.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 1177f921d5..f2adbf75a2 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -2935,5 +2935,10 @@ namespace MediaBrowser.Controller.Entities return Id == item.Id; } + + public override int GetHashCode() + { + return Id.GetHashCode(); + } } } From 7931a7a7d0fc78573b844d2f0e29cdcc259d8881 Mon Sep 17 00:00:00 2001 From: artiume Date: Wed, 4 Mar 2020 17:43:50 -0500 Subject: [PATCH 093/144] update fedora --- deployment/fedora-package-x64/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployment/fedora-package-x64/Dockerfile b/deployment/fedora-package-x64/Dockerfile index 05a4ef21f9..87120f3a05 100644 --- a/deployment/fedora-package-x64/Dockerfile +++ b/deployment/fedora-package-x64/Dockerfile @@ -1,4 +1,4 @@ -FROM fedora:29 +FROM fedora:31 # Docker build arguments ARG SOURCE_DIR=/jellyfin ARG PLATFORM_DIR=/jellyfin/deployment/fedora-package-x64 From ada3f966681070823be3eefae9a0f77a31b1994f Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Thu, 5 Mar 2020 00:54:46 +0100 Subject: [PATCH 094/144] Add tests for alpha numeric sorting --- .../Extensions/ShuffleExtensions.cs | 13 +++++- MediaBrowser.sln | 7 +++ .../AlphanumComparatorTests.cs | 43 +++++++++++++++++++ .../Jellyfin.Controller.Tests.csproj | 21 +++++++++ 4 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 tests/Jellyfin.Controller.Tests/AlphanumComparatorTests.cs create mode 100644 tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj diff --git a/MediaBrowser.Common/Extensions/ShuffleExtensions.cs b/MediaBrowser.Common/Extensions/ShuffleExtensions.cs index 5889d09c4b..0432f36b57 100644 --- a/MediaBrowser.Common/Extensions/ShuffleExtensions.cs +++ b/MediaBrowser.Common/Extensions/ShuffleExtensions.cs @@ -16,12 +16,23 @@ namespace MediaBrowser.Common.Extensions /// The list that should get shuffled. /// The type. public static void Shuffle(this IList list) + { + list.Shuffle(_rng); + } + + /// + /// Shuffles the items in a list. + /// + /// The list that should get shuffled. + /// The random number generator to use. + /// The type. + public static void Shuffle(this IList list, Random rng) { int n = list.Count; while (n > 1) { n--; - int k = _rng.Next(n + 1); + int k = rng.Next(n + 1); T value = list[k]; list[k] = list[n]; list[n] = value; diff --git a/MediaBrowser.sln b/MediaBrowser.sln index 50570deecf..1c84622ac0 100644 --- a/MediaBrowser.sln +++ b/MediaBrowser.sln @@ -60,6 +60,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Api.Tests", "tests EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Server.Implementations.Tests", "tests\Jellyfin.Server.Implementations.Tests\Jellyfin.Server.Implementations.Tests.csproj", "{2E3A1B4B-4225-4AAA-8B29-0181A84E7AEE}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Controller.Tests", "tests\Jellyfin.Controller.Tests\Jellyfin.Controller.Tests.csproj", "{462584F7-5023-4019-9EAC-B98CA458C0A0}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -170,6 +172,10 @@ Global {2E3A1B4B-4225-4AAA-8B29-0181A84E7AEE}.Debug|Any CPU.Build.0 = Debug|Any CPU {2E3A1B4B-4225-4AAA-8B29-0181A84E7AEE}.Release|Any CPU.ActiveCfg = Release|Any CPU {2E3A1B4B-4225-4AAA-8B29-0181A84E7AEE}.Release|Any CPU.Build.0 = Release|Any CPU + {462584F7-5023-4019-9EAC-B98CA458C0A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {462584F7-5023-4019-9EAC-B98CA458C0A0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {462584F7-5023-4019-9EAC-B98CA458C0A0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {462584F7-5023-4019-9EAC-B98CA458C0A0}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -201,5 +207,6 @@ Global {3998657B-1CCC-49DD-A19F-275DC8495F57} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} {A2FD0A10-8F62-4F9D-B171-FFDF9F0AFA9D} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} {2E3A1B4B-4225-4AAA-8B29-0181A84E7AEE} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} + {462584F7-5023-4019-9EAC-B98CA458C0A0} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} EndGlobalSection EndGlobal diff --git a/tests/Jellyfin.Controller.Tests/AlphanumComparatorTests.cs b/tests/Jellyfin.Controller.Tests/AlphanumComparatorTests.cs new file mode 100644 index 0000000000..09a66036a0 --- /dev/null +++ b/tests/Jellyfin.Controller.Tests/AlphanumComparatorTests.cs @@ -0,0 +1,43 @@ +using System; +using System.Linq; +using MediaBrowser.Common.Extensions; +using MediaBrowser.Controller.Sorting; +using Xunit; + +namespace Jellyfin.Controller.Tests +{ + public class AlphanumComparatorTests + { + private readonly Random _rng = new Random(42); + + [Theory] + [InlineData(null, "", "1", "9", "10", "a", "z")] + [InlineData("50F", "100F", "SR9", "SR100")] + [InlineData("image-1.jpg", "image-02.jpg", "image-4.jpg", "image-9.jpg", "image-10.jpg", "image-11.jpg", "image-22.jpg")] + [InlineData("Hard drive 2GB", "Hard drive 20GB")] + [InlineData("b", "e", "è", "ě", "f", "g", "k")] + [InlineData("123456789", "123456789a", "abc", "abcd")] + [InlineData("12345678912345678912345678913234567891", "123456789123456789123456789132345678912")] + [InlineData("12345678912345678912345678913234567891", "12345678912345678912345678913234567891")] + [InlineData("12345678912345678912345678913234567891", "12345678912345678912345678913234567892")] + [InlineData("12345678912345678912345678913234567891a", "12345678912345678912345678913234567891a")] + [InlineData("12345678912345678912345678913234567891a", "12345678912345678912345678913234567891b")] + public void AlphanumComparatorTest(params string?[] strings) + { + var copy = (string?[])strings.Clone(); + if (strings.Length == 2) + { + var tmp = copy[0]; + copy[0] = copy[1]; + copy[1] = tmp; + } + else + { + copy.Shuffle(_rng); + } + + Array.Sort(copy, new AlphanumComparator()); + Assert.True(strings.SequenceEqual(copy)); + } + } +} diff --git a/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj b/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj new file mode 100644 index 0000000000..c63f2e8c63 --- /dev/null +++ b/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj @@ -0,0 +1,21 @@ + + + + netcoreapp3.1 + false + true + enable + + + + + + + + + + + + + + From be1d4b32c6643af69c27c746578b1994a4a650ff Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Thu, 5 Mar 2020 00:57:24 +0100 Subject: [PATCH 095/144] Add speed for alpha numeric sorting --- .../Sorting/AlphanumComparator.cs | 144 +++++++++++------- 1 file changed, 91 insertions(+), 53 deletions(-) diff --git a/MediaBrowser.Controller/Sorting/AlphanumComparator.cs b/MediaBrowser.Controller/Sorting/AlphanumComparator.cs index 65dc120ca8..de7f72d1c2 100644 --- a/MediaBrowser.Controller/Sorting/AlphanumComparator.cs +++ b/MediaBrowser.Controller/Sorting/AlphanumComparator.cs @@ -1,94 +1,132 @@ +#nullable enable + +using System; using System.Collections.Generic; -using System.Text; -using MediaBrowser.Controller.Sorting; namespace MediaBrowser.Controller.Sorting { - public class AlphanumComparator : IComparer + public class AlphanumComparator : IComparer { - public static int CompareValues(string s1, string s2) + public static int CompareValues(string? s1, string? s2) { - if (s1 == null || s2 == null) + if (s1 == null && s2 == null) { return 0; } - - int thisMarker = 0, thisNumericChunk = 0; - int thatMarker = 0, thatNumericChunk = 0; - - while ((thisMarker < s1.Length) || (thatMarker < s2.Length)) + else if (s1 == null) { - if (thisMarker >= s1.Length) - { - return -1; - } - else if (thatMarker >= s2.Length) - { - return 1; - } - char thisCh = s1[thisMarker]; - char thatCh = s2[thatMarker]; + return -1; + } + else if (s2 == null) + { + return 1; + } - var thisChunk = new StringBuilder(); - var thatChunk = new StringBuilder(); - bool thisNumeric = char.IsDigit(thisCh), thatNumeric = char.IsDigit(thatCh); + int len1 = s1.Length; + int len2 = s2.Length; - while (thisMarker < s1.Length && char.IsDigit(thisCh) == thisNumeric) + // Early return for empty strings + if (len1 == 0 && len2 == 0) + { + return 0; + } + else if (len1 == 0) + { + return -1; + } + else if (len2 == 0) + { + return 1; + } + + int pos1 = 0; + int pos2 = 0; + + do + { + int start1 = pos1; + int start2 = pos2; + + bool isNum1 = char.IsDigit(s1[pos1++]); + bool isNum2 = char.IsDigit(s2[pos2++]); + + while (pos1 < len1 && char.IsDigit(s1[pos1]) == isNum1) { - thisChunk.Append(thisCh); - thisMarker++; - - if (thisMarker < s1.Length) - { - thisCh = s1[thisMarker]; - } + pos1++; } - while (thatMarker < s2.Length && char.IsDigit(thatCh) == thatNumeric) + while (pos2 < len2 && char.IsDigit(s2[pos2]) == isNum2) { - thatChunk.Append(thatCh); - thatMarker++; - - if (thatMarker < s2.Length) - { - thatCh = s2[thatMarker]; - } + pos2++; } + var span1 = s1.AsSpan(start1, pos1 - start1); + var span2 = s2.AsSpan(start2, pos2 - start2); - // If both chunks contain numeric characters, sort them numerically - if (thisNumeric && thatNumeric) + if (isNum1 && isNum2) { - if (!int.TryParse(thisChunk.ToString(), out thisNumericChunk) - || !int.TryParse(thatChunk.ToString(), out thatNumericChunk)) - { - return 0; - } - - if (thisNumericChunk < thatNumericChunk) + // Trim leading zeros so we can compare the length + // of the strings to find the largest number + span1 = span1.TrimStart('0'); + span2 = span2.TrimStart('0'); + var span1Len = span1.Length; + var span2Len = span2.Length; + if (span1Len < span2Len) { return -1; } + else if (span1Len > span2Len) + { + return 1; + } + else if (span1Len >= 20) // Number is probably too big for a ulong + { + // Trim all the first digits that are the same + int i = 0; + while (i < span1Len && span1[i] == span2[i]) + { + i++; + } - if (thisNumericChunk > thatNumericChunk) + // If there are no more digits it's the same number + if (i == span1Len) + { + continue; + } + + // Only need to compare the most significant digit + span1 = span1.Slice(i, 1); + span2 = span2.Slice(i, 1); + } + + if (!ulong.TryParse(span1, out var num1) + || !ulong.TryParse(span2, out var num2)) + { + return 0; + } + else if (num1 < num2) + { + return -1; + } + else if (num1 > num2) { return 1; } } else { - int result = thisChunk.ToString().CompareTo(thatChunk.ToString()); + int result = span1.CompareTo(span2, StringComparison.InvariantCulture); if (result != 0) { return result; } } + } while (pos1 < len1 && pos2 < len2); - } - - return 0; + return len1 - len2; } + /// public int Compare(string x, string y) { return CompareValues(x, y); From 375cf212dd7ac84f073dc5b0c1df7944c1a99422 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Thu, 5 Mar 2020 12:24:12 +0100 Subject: [PATCH 096/144] Update AlphanumComparatorTests.cs --- tests/Jellyfin.Controller.Tests/AlphanumComparatorTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/Jellyfin.Controller.Tests/AlphanumComparatorTests.cs b/tests/Jellyfin.Controller.Tests/AlphanumComparatorTests.cs index 09a66036a0..929bb92aa8 100644 --- a/tests/Jellyfin.Controller.Tests/AlphanumComparatorTests.cs +++ b/tests/Jellyfin.Controller.Tests/AlphanumComparatorTests.cs @@ -10,6 +10,7 @@ namespace Jellyfin.Controller.Tests { private readonly Random _rng = new Random(42); + // InlineData is pre-sorted [Theory] [InlineData(null, "", "1", "9", "10", "a", "z")] [InlineData("50F", "100F", "SR9", "SR100")] From 456f571343cfd524fd29e22207a806d5acd7d25a Mon Sep 17 00:00:00 2001 From: Vasily Date: Thu, 5 Mar 2020 14:25:50 +0300 Subject: [PATCH 097/144] Follow code review suggestions --- MediaBrowser.Controller/Entities/BaseItem.cs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index f2adbf75a2..8f504ebb0c 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -2921,24 +2921,17 @@ namespace MediaBrowser.Controller.Entities public override bool Equals(object obj) { - Logger.LogDebug("Comparing me ({0}) to generic them ({1})", this, obj); - return this.Equals(obj as BaseItem); + return obj is BaseItem baseItem && this.Equals(baseItem); } public bool Equals(BaseItem item) { - Logger.LogDebug("Comparing me ({0}) to specific them ({1})", this, item); - if (item == null) - { - return false; - } - - return Id == item.Id; + return Object.Equals(Id, item?.Id); } public override int GetHashCode() { - return Id.GetHashCode(); + return HashCode.Combine(Id); } } } From f4ccee58010c4420f827ce7f8eb037e2ccec1d82 Mon Sep 17 00:00:00 2001 From: Vasily Date: Thu, 5 Mar 2020 15:03:17 +0300 Subject: [PATCH 098/144] Add inheritdoc comment and squash simple method bodies --- MediaBrowser.Controller/Entities/BaseItem.cs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 8f504ebb0c..9af8d10698 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -2919,19 +2919,16 @@ namespace MediaBrowser.Controller.Entities public virtual bool SupportsExternalTransfer => false; + /// public override bool Equals(object obj) { return obj is BaseItem baseItem && this.Equals(baseItem); } - public bool Equals(BaseItem item) - { - return Object.Equals(Id, item?.Id); - } + /// + public bool Equals(BaseItem item) => Object.Equals(Id, item?.Id); - public override int GetHashCode() - { - return HashCode.Combine(Id); - } + /// + public override int GetHashCode() => HashCode.Combine(Id); } } From acd67c7152cc9a476d5cc9e7a4b95b084bfaeb6e Mon Sep 17 00:00:00 2001 From: Vasily Date: Thu, 5 Mar 2020 16:22:15 +0300 Subject: [PATCH 099/144] Add tracking of JF version used to run this config previously --- Jellyfin.Server/CoreAppHost.cs | 10 +++++++++ .../BaseApplicationConfiguration.cs | 21 +++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs index 8b4b61e290..59285a5109 100644 --- a/Jellyfin.Server/CoreAppHost.cs +++ b/Jellyfin.Server/CoreAppHost.cs @@ -41,6 +41,16 @@ namespace Jellyfin.Server networkManager, configuration) { + var previousVersion = ConfigurationManager.CommonConfiguration.PreviousVersion; + if (ApplicationVersion.CompareTo(previousVersion) > 0) + { + Logger.LogWarning("Version check shows Jellyfin was updated: previous version={0}, current version={1}", previousVersion, ApplicationVersion); + + // TODO: run update routines + + ConfigurationManager.CommonConfiguration.PreviousVersion = ApplicationVersion; + ConfigurationManager.SaveConfiguration(); + } } /// diff --git a/MediaBrowser.Model/Configuration/BaseApplicationConfiguration.cs b/MediaBrowser.Model/Configuration/BaseApplicationConfiguration.cs index 6a1a0f0901..cc2541f74f 100644 --- a/MediaBrowser.Model/Configuration/BaseApplicationConfiguration.cs +++ b/MediaBrowser.Model/Configuration/BaseApplicationConfiguration.cs @@ -1,3 +1,6 @@ +using System; +using System.Xml.Serialization; + namespace MediaBrowser.Model.Configuration { /// @@ -25,6 +28,24 @@ namespace MediaBrowser.Model.Configuration /// The cache path. public string CachePath { get; set; } + /// + /// Last known version that was ran using the configuration. + /// + /// The version from previous run. + [XmlIgnore] + public Version PreviousVersion { get; set; } + + /// + /// Stringified PreviousVersion to be stored/loaded, + /// because System.Version itself isn't xml-serializable + /// + /// String value of PreviousVersion + public string PreviousVersionStr + { + get => PreviousVersion?.ToString(); + set => PreviousVersion = Version.Parse(value); + } + /// /// Initializes a new instance of the class. /// From ca585f12b3297f58e8f490b03b64729716eb2f40 Mon Sep 17 00:00:00 2001 From: Andrew Rabert <6550543+nvllsvm@users.noreply.github.com> Date: Thu, 5 Mar 2020 09:41:14 -0500 Subject: [PATCH 100/144] Fix Docker packages (#2499) * Fix Vaapi Intel packages https://github.com/jellyfin/jellyfin/issues/1901#issuecomment-593114951 Still need to compile with the packages to verify it builds properly. Arm builds probably need it too. * Update Dockerfile * Update Dockerfile * Update Dockerfile.arm * Update Dockerfile.arm * Update Dockerfile.arm64 * Update Dockerfile.arm * Update Dockerfile.arm * Update Dockerfile.arm * Update Dockerfile.arm64 * Update Dockerfile.arm64 * Update Dockerfile.arm * Update Dockerfile * shift from curl to git for web install removed the necessity of curl, tar and package availability and using the source directly * Update Dockerfile.arm * Update Dockerfile.arm64 * Update Dockerfile * Update Dockerfile * Update Dockerfile.arm * Update Dockerfile.arm64 * clean up packages and remove unnecessary ARG * Update Dockerfile * Update Dockerfile.arm64 * Update Dockerfile * Update Dockerfile.arm64 * Update Dockerfile * Update Dockerfile.arm * Update Dockerfile.arm64 * Explainations * Update Dockerfile.arm * Update Dockerfile.arm64 --- Dockerfile | 20 +++++++++++++++++--- Dockerfile.arm | 31 ++++++++++++++++++++++++++++--- Dockerfile.arm64 | 21 ++++++++++++++++++--- 3 files changed, 63 insertions(+), 9 deletions(-) diff --git a/Dockerfile b/Dockerfile index 1029a4cee1..73ab3d7904 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,6 +21,13 @@ RUN dotnet publish Jellyfin.Server --disable-parallel --configuration Release -- FROM jellyfin/ffmpeg:${FFMPEG_VERSION} as ffmpeg FROM debian:buster-slim +# https://askubuntu.com/questions/972516/debian-frontend-environment-variable +ARG DEBIAN_FRONTEND="noninteractive" +# http://stackoverflow.com/questions/48162574/ddg#49462622 +ARG APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=DontWarn +# https://github.com/NVIDIA/nvidia-docker/wiki/Installation-(Native-GPU-Support) +ENV NVIDIA_DRIVER_CAPABILITIES="compute,video,utility" + COPY --from=ffmpeg /opt/ffmpeg /opt/ffmpeg COPY --from=builder /jellyfin /jellyfin COPY --from=web-builder /dist /jellyfin/jellyfin-web @@ -31,9 +38,16 @@ COPY --from=web-builder /dist /jellyfin/jellyfin-web # mesa-va-drivers: needed for VAAPI RUN apt-get update \ && apt-get install --no-install-recommends --no-install-suggests -y \ - libfontconfig1 libgomp1 libva-drm2 mesa-va-drivers openssl ca-certificates \ - && apt-get clean autoclean \ - && apt-get autoremove \ + libfontconfig1 \ + libgomp1 \ + libva-drm2 \ + mesa-va-drivers \ + openssl \ + ca-certificates \ + vainfo \ + i965-va-driver \ + && apt-get clean autoclean -y\ + && apt-get autoremove -y\ && rm -rf /var/lib/apt/lists/* \ && mkdir -p /cache /config /media \ && chmod 777 /cache /config /media \ diff --git a/Dockerfile.arm b/Dockerfile.arm index 5847de9189..4c7aa6aa70 100644 --- a/Dockerfile.arm +++ b/Dockerfile.arm @@ -27,10 +27,35 @@ RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" FROM multiarch/qemu-user-static:x86_64-arm as qemu FROM arm32v7/debian:buster-slim + +# https://askubuntu.com/questions/972516/debian-frontend-environment-variable +ARG DEBIAN_FRONTEND="noninteractive" +# http://stackoverflow.com/questions/48162574/ddg#49462622 +ARG APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=DontWarn +# https://github.com/NVIDIA/nvidia-docker/wiki/Installation-(Native-GPU-Support) +ENV NVIDIA_DRIVER_CAPABILITIES="compute,video,utility" + COPY --from=qemu /usr/bin/qemu-arm-static /usr/bin RUN apt-get update \ - && apt-get install --no-install-recommends --no-install-suggests -y ffmpeg \ - libssl-dev ca-certificates \ + && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates gnupg curl && \ + curl -s https://repo.jellyfin.org/debian/jellyfin_team.gpg.key | apt-key add - && \ + curl -s https://keyserver.ubuntu.com/pks/lookup?op=get\&search=0x6587ffd6536b8826e88a62547876ae518cbcf2f2 | apt-key add - && \ + echo 'deb [arch=armhf] https://repo.jellyfin.org/debian buster main' > /etc/apt/sources.list.d/jellyfin.list && \ + echo "deb http://ppa.launchpad.net/ubuntu-raspi2/ppa/ubuntu bionic main">> /etc/apt/sources.list.d/raspbins.list && \ + apt-get update && \ + apt-get install --no-install-recommends --no-install-suggests -y \ + jellyfin-ffmpeg \ + libssl-dev \ + libfontconfig1 \ + libfreetype6 \ + libomxil-bellagio0 \ + libomxil-bellagio-bin \ + libraspberrypi0 \ + vainfo \ + libva2 \ + && apt-get remove curl gnupg -y \ + && apt-get clean autoclean -y \ + && apt-get autoremove -y \ && rm -rf /var/lib/apt/lists/* \ && mkdir -p /cache /config /media \ && chmod 777 /cache /config /media @@ -44,4 +69,4 @@ VOLUME /cache /config /media ENTRYPOINT ["./jellyfin/jellyfin", \ "--datadir", "/config", \ "--cachedir", "/cache", \ - "--ffmpeg", "/usr/bin/ffmpeg"] + "--ffmpeg", "/usr/lib/jellyfin-ffmpeg"] diff --git a/Dockerfile.arm64 b/Dockerfile.arm64 index a9f6c50d9f..9dc6fa7edb 100644 --- a/Dockerfile.arm64 +++ b/Dockerfile.arm64 @@ -26,10 +26,25 @@ RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" FROM multiarch/qemu-user-static:x86_64-aarch64 as qemu FROM arm64v8/debian:buster-slim + +# https://askubuntu.com/questions/972516/debian-frontend-environment-variable +ARG DEBIAN_FRONTEND="noninteractive" +# http://stackoverflow.com/questions/48162574/ddg#49462622 +ARG APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=DontWarn +# https://github.com/NVIDIA/nvidia-docker/wiki/Installation-(Native-GPU-Support) +ENV NVIDIA_DRIVER_CAPABILITIES="compute,video,utility" + COPY --from=qemu /usr/bin/qemu-aarch64-static /usr/bin -RUN apt-get update \ - && apt-get install --no-install-recommends --no-install-suggests -y ffmpeg \ - libssl-dev ca-certificates \ +RUN apt-get update && apt-get install --no-install-recommends --no-install-suggests -y \ + ffmpeg \ + libssl-dev \ + ca-certificates \ + libfontconfig1 \ + libfreetype6 \ + libomxil-bellagio0 \ + libomxil-bellagio-bin \ + && apt-get clean autoclean -y \ + && apt-get autoremove -y \ && rm -rf /var/lib/apt/lists/* \ && mkdir -p /cache /config /media \ && chmod 777 /cache /config /media From 9eef0e8ca0e3359239ab68fcadbf2d65084f12e6 Mon Sep 17 00:00:00 2001 From: Vasily Date: Thu, 5 Mar 2020 17:41:32 +0300 Subject: [PATCH 101/144] Implement EnableThrottling migration for pre-10.5.0 to 10.5.0 or newer --- Jellyfin.Server/CoreAppHost.cs | 38 ++++++++++---- Jellyfin.Server/Migrations.cs | 92 ++++++++++++++++++++++++++++++++++ Jellyfin.Server/Program.cs | 1 + 3 files changed, 121 insertions(+), 10 deletions(-) create mode 100644 Jellyfin.Server/Migrations.cs diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs index 59285a5109..cd5a2ce853 100644 --- a/Jellyfin.Server/CoreAppHost.cs +++ b/Jellyfin.Server/CoreAppHost.cs @@ -41,16 +41,6 @@ namespace Jellyfin.Server networkManager, configuration) { - var previousVersion = ConfigurationManager.CommonConfiguration.PreviousVersion; - if (ApplicationVersion.CompareTo(previousVersion) > 0) - { - Logger.LogWarning("Version check shows Jellyfin was updated: previous version={0}, current version={1}", previousVersion, ApplicationVersion); - - // TODO: run update routines - - ConfigurationManager.CommonConfiguration.PreviousVersion = ApplicationVersion; - ConfigurationManager.SaveConfiguration(); - } } /// @@ -67,5 +57,33 @@ namespace Jellyfin.Server /// protected override void ShutdownInternal() => Program.Shutdown(); + + /// + /// Runs the migration routines if necessary. + /// + public void TryMigrate() + { + var previousVersion = ConfigurationManager.CommonConfiguration.PreviousVersion; + switch (ApplicationVersion.CompareTo(previousVersion)) + { + case 1: + Logger.LogWarning("Version check shows Jellyfin was updated: previous version={0}, current version={1}", previousVersion, ApplicationVersion); + + Migrations.Run(this, Logger); + + ConfigurationManager.CommonConfiguration.PreviousVersion = ApplicationVersion; + ConfigurationManager.SaveConfiguration(); + break; + case 0: + // nothing to do, versions match + break; + case -1: + Logger.LogWarning("Version check shows Jellyfin was rolled back, use at your own risk: previous version={0}, current version={1}", previousVersion, ApplicationVersion); + // no "rollback" routines for now + ConfigurationManager.CommonConfiguration.PreviousVersion = ApplicationVersion; + ConfigurationManager.SaveConfiguration(); + break; + } + } } } diff --git a/Jellyfin.Server/Migrations.cs b/Jellyfin.Server/Migrations.cs new file mode 100644 index 0000000000..95fea4ea55 --- /dev/null +++ b/Jellyfin.Server/Migrations.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Model.Configuration; +using Microsoft.Extensions.Logging; + +namespace Jellyfin.Server +{ + /// + /// The class that knows how migrate between different Jellyfin versions. + /// + internal static class Migrations + { + private static readonly IUpdater[] _migrations = + { + new Pre10_5() + }; + + /// + /// Interface that descibes a migration routine. + /// + private interface IUpdater + { + /// + /// Gets maximum version this Updater applies to. + /// If current version is greater or equal to it, skip the updater. + /// + public abstract Version Maximum { get; } + + /// + /// Execute the migration from version "from". + /// + /// Host that hosts current version. + /// Host logger. + /// Version to migrate from. + /// Whether configuration was changed. + public abstract bool Perform(CoreAppHost host, ILogger logger, Version from); + } + + /// + /// Run all needed migrations. + /// + /// CoreAppHost that hosts current version. + /// AppHost logger. + /// Whether anything was changed. + public static bool Run(CoreAppHost host, ILogger logger) + { + bool updated = false; + var version = host.ServerConfigurationManager.CommonConfiguration.PreviousVersion; + + for (var i = 0; i < _migrations.Length; i++) + { + var updater = _migrations[i]; + if (version.CompareTo(updater.Maximum) >= 0) + { + logger.LogDebug("Skipping updater {0} as current version {1} >= its maximum applicable version {2}", updater, version, updater.Maximum); + continue; + } + + if (updater.Perform(host, logger, version)) + { + updated = true; + } + + version = updater.Maximum; + } + + return updated; + } + + private class Pre10_5 : IUpdater + { + public Version Maximum { get => Version.Parse("10.5.0"); } + + public bool Perform(CoreAppHost host, ILogger logger, Version from) + { + // Set EnableThrottling to false as it wasn't used before, and in 10.5.0 it may introduce issues + var encoding = ((IConfigurationManager)host.ServerConfigurationManager).GetConfiguration("encoding"); + if (encoding.EnableThrottling) + { + logger.LogInformation("Disabling transcoding throttling during migration"); + encoding.EnableThrottling = false; + + host.ServerConfigurationManager.SaveConfiguration("encoding", encoding); + return true; + } + + return false; + } + } + } +} diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 484e507a23..aa1bdb1691 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -182,6 +182,7 @@ namespace Jellyfin.Server // A bit hacky to re-use service provider since ASP.NET doesn't allow a custom service collection. appHost.ServiceProvider = host.Services; appHost.FindParts(); + appHost.TryMigrate(); try { From 66e11879efcd2a77476ca9704fa938e89776956c Mon Sep 17 00:00:00 2001 From: Vasily Date: Thu, 5 Mar 2020 18:21:27 +0300 Subject: [PATCH 102/144] Shuffle migrations in a more manageable structure --- Jellyfin.Server/CoreAppHost.cs | 2 +- Jellyfin.Server/Migrations.cs | 92 ------------------- Jellyfin.Server/Migrations/IUpdater.cs | 26 ++++++ Jellyfin.Server/Migrations/MigrationRunner.cs | 46 ++++++++++ Jellyfin.Server/Migrations/Pre_10_5.cs | 33 +++++++ 5 files changed, 106 insertions(+), 93 deletions(-) delete mode 100644 Jellyfin.Server/Migrations.cs create mode 100644 Jellyfin.Server/Migrations/IUpdater.cs create mode 100644 Jellyfin.Server/Migrations/MigrationRunner.cs create mode 100644 Jellyfin.Server/Migrations/Pre_10_5.cs diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs index cd5a2ce853..7f4bd3dea2 100644 --- a/Jellyfin.Server/CoreAppHost.cs +++ b/Jellyfin.Server/CoreAppHost.cs @@ -69,7 +69,7 @@ namespace Jellyfin.Server case 1: Logger.LogWarning("Version check shows Jellyfin was updated: previous version={0}, current version={1}", previousVersion, ApplicationVersion); - Migrations.Run(this, Logger); + Migrations.MigrationRunner.Run(this, Logger); ConfigurationManager.CommonConfiguration.PreviousVersion = ApplicationVersion; ConfigurationManager.SaveConfiguration(); diff --git a/Jellyfin.Server/Migrations.cs b/Jellyfin.Server/Migrations.cs deleted file mode 100644 index 95fea4ea55..0000000000 --- a/Jellyfin.Server/Migrations.cs +++ /dev/null @@ -1,92 +0,0 @@ -using System; -using System.Collections.Generic; -using MediaBrowser.Common.Configuration; -using MediaBrowser.Model.Configuration; -using Microsoft.Extensions.Logging; - -namespace Jellyfin.Server -{ - /// - /// The class that knows how migrate between different Jellyfin versions. - /// - internal static class Migrations - { - private static readonly IUpdater[] _migrations = - { - new Pre10_5() - }; - - /// - /// Interface that descibes a migration routine. - /// - private interface IUpdater - { - /// - /// Gets maximum version this Updater applies to. - /// If current version is greater or equal to it, skip the updater. - /// - public abstract Version Maximum { get; } - - /// - /// Execute the migration from version "from". - /// - /// Host that hosts current version. - /// Host logger. - /// Version to migrate from. - /// Whether configuration was changed. - public abstract bool Perform(CoreAppHost host, ILogger logger, Version from); - } - - /// - /// Run all needed migrations. - /// - /// CoreAppHost that hosts current version. - /// AppHost logger. - /// Whether anything was changed. - public static bool Run(CoreAppHost host, ILogger logger) - { - bool updated = false; - var version = host.ServerConfigurationManager.CommonConfiguration.PreviousVersion; - - for (var i = 0; i < _migrations.Length; i++) - { - var updater = _migrations[i]; - if (version.CompareTo(updater.Maximum) >= 0) - { - logger.LogDebug("Skipping updater {0} as current version {1} >= its maximum applicable version {2}", updater, version, updater.Maximum); - continue; - } - - if (updater.Perform(host, logger, version)) - { - updated = true; - } - - version = updater.Maximum; - } - - return updated; - } - - private class Pre10_5 : IUpdater - { - public Version Maximum { get => Version.Parse("10.5.0"); } - - public bool Perform(CoreAppHost host, ILogger logger, Version from) - { - // Set EnableThrottling to false as it wasn't used before, and in 10.5.0 it may introduce issues - var encoding = ((IConfigurationManager)host.ServerConfigurationManager).GetConfiguration("encoding"); - if (encoding.EnableThrottling) - { - logger.LogInformation("Disabling transcoding throttling during migration"); - encoding.EnableThrottling = false; - - host.ServerConfigurationManager.SaveConfiguration("encoding", encoding); - return true; - } - - return false; - } - } - } -} diff --git a/Jellyfin.Server/Migrations/IUpdater.cs b/Jellyfin.Server/Migrations/IUpdater.cs new file mode 100644 index 0000000000..60d9702567 --- /dev/null +++ b/Jellyfin.Server/Migrations/IUpdater.cs @@ -0,0 +1,26 @@ +using System; +using Microsoft.Extensions.Logging; + +namespace Jellyfin.Server.Migrations +{ + /// + /// Interface that descibes a migration routine. + /// + internal interface IUpdater + { + /// + /// Gets maximum version this Updater applies to. + /// If current version is greater or equal to it, skip the updater. + /// + public abstract Version Maximum { get; } + + /// + /// Execute the migration from version "from". + /// + /// Host that hosts current version. + /// Host logger. + /// Version to migrate from. + /// Whether configuration was changed. + public abstract bool Perform(CoreAppHost host, ILogger logger, Version from); + } +} diff --git a/Jellyfin.Server/Migrations/MigrationRunner.cs b/Jellyfin.Server/Migrations/MigrationRunner.cs new file mode 100644 index 0000000000..ad54fa38e6 --- /dev/null +++ b/Jellyfin.Server/Migrations/MigrationRunner.cs @@ -0,0 +1,46 @@ +using Microsoft.Extensions.Logging; + +namespace Jellyfin.Server.Migrations +{ + /// + /// The class that knows how migrate between different Jellyfin versions. + /// + public static class MigrationRunner + { + private static readonly IUpdater[] _migrations = + { + new Pre_10_5() + }; + + /// + /// Run all needed migrations. + /// + /// CoreAppHost that hosts current version. + /// AppHost logger. + /// Whether anything was changed. + public static bool Run(CoreAppHost host, ILogger logger) + { + bool updated = false; + var version = host.ServerConfigurationManager.CommonConfiguration.PreviousVersion; + + for (var i = 0; i < _migrations.Length; i++) + { + var updater = _migrations[i]; + if (version.CompareTo(updater.Maximum) >= 0) + { + logger.LogDebug("Skipping updater {0} as current version {1} >= its maximum applicable version {2}", updater, version, updater.Maximum); + continue; + } + + if (updater.Perform(host, logger, version)) + { + updated = true; + } + + version = updater.Maximum; + } + + return updated; + } + } +} diff --git a/Jellyfin.Server/Migrations/Pre_10_5.cs b/Jellyfin.Server/Migrations/Pre_10_5.cs new file mode 100644 index 0000000000..5389a2ad98 --- /dev/null +++ b/Jellyfin.Server/Migrations/Pre_10_5.cs @@ -0,0 +1,33 @@ +using System; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Model.Configuration; +using Microsoft.Extensions.Logging; + +namespace Jellyfin.Server.Migrations +{ + /// + /// Updater that takes care of bringing configuration up to 10.5.0 standards. + /// + internal class Pre_10_5 : IUpdater + { + /// + public Version Maximum { get => Version.Parse("10.5.0"); } + + /// + public bool Perform(CoreAppHost host, ILogger logger, Version from) + { + // Set EnableThrottling to false as it wasn't used before, and in 10.5.0 it may introduce issues + var encoding = ((IConfigurationManager)host.ServerConfigurationManager).GetConfiguration("encoding"); + if (encoding.EnableThrottling) + { + logger.LogInformation("Disabling transcoding throttling during migration"); + encoding.EnableThrottling = false; + + host.ServerConfigurationManager.SaveConfiguration("encoding", encoding); + return true; + } + + return false; + } + } +} From ecaa7f8014666a474c87481471ce7cda7006165a Mon Sep 17 00:00:00 2001 From: Vasily Date: Thu, 5 Mar 2020 20:09:33 +0300 Subject: [PATCH 103/144] Improve migration logic --- Jellyfin.Server/CoreAppHost.cs | 28 ----------- ...0_5.cs => DisableTranscodingThrottling.cs} | 11 ++--- .../Migrations/DisableZealousLogging.cs | 29 +++++++++++ Jellyfin.Server/Migrations/IUpdater.cs | 31 ++++++------ .../Migrations/MigrationOptions.cs | 23 +++++++++ Jellyfin.Server/Migrations/MigrationRunner.cs | 49 +++++++++++++------ .../Migrations/MigrationsFactory.cs | 20 ++++++++ .../Migrations/MigrationsListStore.cs | 19 +++++++ Jellyfin.Server/Program.cs | 11 +++-- 9 files changed, 151 insertions(+), 70 deletions(-) rename Jellyfin.Server/Migrations/{Pre_10_5.cs => DisableTranscodingThrottling.cs} (79%) create mode 100644 Jellyfin.Server/Migrations/DisableZealousLogging.cs create mode 100644 Jellyfin.Server/Migrations/MigrationOptions.cs create mode 100644 Jellyfin.Server/Migrations/MigrationsFactory.cs create mode 100644 Jellyfin.Server/Migrations/MigrationsListStore.cs diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs index 7f4bd3dea2..8b4b61e290 100644 --- a/Jellyfin.Server/CoreAppHost.cs +++ b/Jellyfin.Server/CoreAppHost.cs @@ -57,33 +57,5 @@ namespace Jellyfin.Server /// protected override void ShutdownInternal() => Program.Shutdown(); - - /// - /// Runs the migration routines if necessary. - /// - public void TryMigrate() - { - var previousVersion = ConfigurationManager.CommonConfiguration.PreviousVersion; - switch (ApplicationVersion.CompareTo(previousVersion)) - { - case 1: - Logger.LogWarning("Version check shows Jellyfin was updated: previous version={0}, current version={1}", previousVersion, ApplicationVersion); - - Migrations.MigrationRunner.Run(this, Logger); - - ConfigurationManager.CommonConfiguration.PreviousVersion = ApplicationVersion; - ConfigurationManager.SaveConfiguration(); - break; - case 0: - // nothing to do, versions match - break; - case -1: - Logger.LogWarning("Version check shows Jellyfin was rolled back, use at your own risk: previous version={0}, current version={1}", previousVersion, ApplicationVersion); - // no "rollback" routines for now - ConfigurationManager.CommonConfiguration.PreviousVersion = ApplicationVersion; - ConfigurationManager.SaveConfiguration(); - break; - } - } } } diff --git a/Jellyfin.Server/Migrations/Pre_10_5.cs b/Jellyfin.Server/Migrations/DisableTranscodingThrottling.cs similarity index 79% rename from Jellyfin.Server/Migrations/Pre_10_5.cs rename to Jellyfin.Server/Migrations/DisableTranscodingThrottling.cs index 5389a2ad98..83624bdadd 100644 --- a/Jellyfin.Server/Migrations/Pre_10_5.cs +++ b/Jellyfin.Server/Migrations/DisableTranscodingThrottling.cs @@ -1,6 +1,8 @@ using System; +using System.IO; using MediaBrowser.Common.Configuration; using MediaBrowser.Model.Configuration; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; namespace Jellyfin.Server.Migrations @@ -8,13 +10,13 @@ namespace Jellyfin.Server.Migrations /// /// Updater that takes care of bringing configuration up to 10.5.0 standards. /// - internal class Pre_10_5 : IUpdater + internal class DisableTranscodingThrottling : IUpdater { /// - public Version Maximum { get => Version.Parse("10.5.0"); } + public string Name => "DisableTranscodingThrottling"; /// - public bool Perform(CoreAppHost host, ILogger logger, Version from) + public void Perform(CoreAppHost host, ILogger logger) { // Set EnableThrottling to false as it wasn't used before, and in 10.5.0 it may introduce issues var encoding = ((IConfigurationManager)host.ServerConfigurationManager).GetConfiguration("encoding"); @@ -24,10 +26,7 @@ namespace Jellyfin.Server.Migrations encoding.EnableThrottling = false; host.ServerConfigurationManager.SaveConfiguration("encoding", encoding); - return true; } - - return false; } } } diff --git a/Jellyfin.Server/Migrations/DisableZealousLogging.cs b/Jellyfin.Server/Migrations/DisableZealousLogging.cs new file mode 100644 index 0000000000..a0a934d4ab --- /dev/null +++ b/Jellyfin.Server/Migrations/DisableZealousLogging.cs @@ -0,0 +1,29 @@ +using System; +using System.IO; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Model.Configuration; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using Serilog; +using ILogger = Microsoft.Extensions.Logging.ILogger; + +namespace Jellyfin.Server.Migrations +{ + /// + /// Updater that takes care of bringing configuration up to 10.5.0 standards. + /// + internal class DisableZealousLogging : IUpdater + { + /// + public string Name => "DisableZealousLogging"; + + /// + // This tones down logging from some components + public void Perform(CoreAppHost host, ILogger logger) + { + string configPath = Path.Combine(host.ServerConfigurationManager.ApplicationPaths.ConfigurationDirectoryPath, Program.LoggingConfigFile); + // TODO: fix up the config + throw new NotImplementedException("don't know how to fix logging yet"); + } + } +} diff --git a/Jellyfin.Server/Migrations/IUpdater.cs b/Jellyfin.Server/Migrations/IUpdater.cs index 60d9702567..10ada73d5f 100644 --- a/Jellyfin.Server/Migrations/IUpdater.cs +++ b/Jellyfin.Server/Migrations/IUpdater.cs @@ -3,24 +3,21 @@ using Microsoft.Extensions.Logging; namespace Jellyfin.Server.Migrations { + /// + /// Interface that descibes a migration routine. + /// + internal interface IUpdater + { /// - /// Interface that descibes a migration routine. + /// Gets the name of the migration, must be unique. /// - internal interface IUpdater - { - /// - /// Gets maximum version this Updater applies to. - /// If current version is greater or equal to it, skip the updater. - /// - public abstract Version Maximum { get; } + public abstract string Name { get; } - /// - /// Execute the migration from version "from". - /// - /// Host that hosts current version. - /// Host logger. - /// Version to migrate from. - /// Whether configuration was changed. - public abstract bool Perform(CoreAppHost host, ILogger logger, Version from); - } + /// + /// Execute the migration from version "from". + /// + /// Host that hosts current version. + /// Host logger. + public abstract void Perform(CoreAppHost host, ILogger logger); + } } diff --git a/Jellyfin.Server/Migrations/MigrationOptions.cs b/Jellyfin.Server/Migrations/MigrationOptions.cs new file mode 100644 index 0000000000..b96288cc1b --- /dev/null +++ b/Jellyfin.Server/Migrations/MigrationOptions.cs @@ -0,0 +1,23 @@ +namespace Jellyfin.Server.Migrations +{ + /// + /// Configuration part that holds all migrations that were applied. + /// + public class MigrationOptions + { + /// + /// Initializes a new instance of the class. + /// + public MigrationOptions() + { + Applied = System.Array.Empty(); + } + +#pragma warning disable CA1819 // Properties should not return arrays + /// + /// Gets or sets he list of applied migration routine names. + /// + public string[] Applied { get; set; } +#pragma warning restore CA1819 // Properties should not return arrays + } +} diff --git a/Jellyfin.Server/Migrations/MigrationRunner.cs b/Jellyfin.Server/Migrations/MigrationRunner.cs index ad54fa38e6..04d0378521 100644 --- a/Jellyfin.Server/Migrations/MigrationRunner.cs +++ b/Jellyfin.Server/Migrations/MigrationRunner.cs @@ -1,3 +1,6 @@ +using System; +using System.Linq; +using MediaBrowser.Common.Configuration; using Microsoft.Extensions.Logging; namespace Jellyfin.Server.Migrations @@ -5,42 +8,56 @@ namespace Jellyfin.Server.Migrations /// /// The class that knows how migrate between different Jellyfin versions. /// - public static class MigrationRunner + public sealed class MigrationRunner { - private static readonly IUpdater[] _migrations = + /// + /// The list of known migrations, in order of applicability. + /// + internal static readonly IUpdater[] Migrations = { - new Pre_10_5() + new DisableTranscodingThrottling(), + new DisableZealousLogging() }; /// /// Run all needed migrations. /// /// CoreAppHost that hosts current version. - /// AppHost logger. - /// Whether anything was changed. - public static bool Run(CoreAppHost host, ILogger logger) + /// Factory for making the logger. + public static void Run(CoreAppHost host, ILoggerFactory loggerFactory) { - bool updated = false; - var version = host.ServerConfigurationManager.CommonConfiguration.PreviousVersion; + var logger = loggerFactory.CreateLogger(); + var migrationOptions = ((IConfigurationManager)host.ServerConfigurationManager).GetConfiguration("migrations"); + var applied = migrationOptions.Applied.ToList(); - for (var i = 0; i < _migrations.Length; i++) + for (var i = 0; i < Migrations.Length; i++) { - var updater = _migrations[i]; - if (version.CompareTo(updater.Maximum) >= 0) + var updater = Migrations[i]; + if (applied.Contains(updater.Name)) { - logger.LogDebug("Skipping updater {0} as current version {1} >= its maximum applicable version {2}", updater, version, updater.Maximum); + logger.LogDebug("Skipping migration {0} as it is already applied", updater.Name); continue; } - if (updater.Perform(host, logger, version)) + try { - updated = true; + updater.Perform(host, logger); + } + catch (Exception ex) + { + logger.LogError(ex, "Cannot apply migration {0}", updater.Name); + continue; } - version = updater.Maximum; + applied.Add(updater.Name); } - return updated; + if (applied.Count > migrationOptions.Applied.Length) + { + logger.LogInformation("Some migrations were run, saving the state"); + migrationOptions.Applied = applied.ToArray(); + host.ServerConfigurationManager.SaveConfiguration("migrations", migrationOptions); + } } } } diff --git a/Jellyfin.Server/Migrations/MigrationsFactory.cs b/Jellyfin.Server/Migrations/MigrationsFactory.cs new file mode 100644 index 0000000000..ed01dc646a --- /dev/null +++ b/Jellyfin.Server/Migrations/MigrationsFactory.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using MediaBrowser.Common.Configuration; + +namespace Jellyfin.Server.Migrations +{ + /// + /// A factory that teachs Jellyfin how to find a peristent file which lists all applied migrations. + /// + public class MigrationsFactory : IConfigurationFactory + { + /// + public IEnumerable GetConfigurations() + { + return new[] + { + new MigrationsListStore() + }; + } + } +} diff --git a/Jellyfin.Server/Migrations/MigrationsListStore.cs b/Jellyfin.Server/Migrations/MigrationsListStore.cs new file mode 100644 index 0000000000..d91d602c14 --- /dev/null +++ b/Jellyfin.Server/Migrations/MigrationsListStore.cs @@ -0,0 +1,19 @@ +using MediaBrowser.Common.Configuration; + +namespace Jellyfin.Server.Migrations +{ + /// + /// A configuration that lists all the migration routines that were applied. + /// + public class MigrationsListStore : ConfigurationStore + { + /// + /// Initializes a new instance of the class. + /// + public MigrationsListStore() + { + ConfigurationType = typeof(MigrationOptions); + Key = "migrations"; + } + } +} diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index aa1bdb1691..0271861054 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -38,6 +38,11 @@ namespace Jellyfin.Server /// public static class Program { + /// + /// The name of logging configuration file. + /// + public static readonly string LoggingConfigFile = "logging.json"; + private static readonly CancellationTokenSource _tokenSource = new CancellationTokenSource(); private static readonly ILoggerFactory _loggerFactory = new SerilogLoggerFactory(); private static ILogger _logger = NullLogger.Instance; @@ -182,7 +187,7 @@ namespace Jellyfin.Server // A bit hacky to re-use service provider since ASP.NET doesn't allow a custom service collection. appHost.ServiceProvider = host.Services; appHost.FindParts(); - appHost.TryMigrate(); + Migrations.MigrationRunner.Run(appHost, _loggerFactory); try { @@ -438,7 +443,7 @@ namespace Jellyfin.Server private static async Task CreateConfiguration(IApplicationPaths appPaths) { const string ResourcePath = "Jellyfin.Server.Resources.Configuration.logging.json"; - string configPath = Path.Combine(appPaths.ConfigurationDirectoryPath, "logging.json"); + string configPath = Path.Combine(appPaths.ConfigurationDirectoryPath, LoggingConfigFile); if (!File.Exists(configPath)) { @@ -460,7 +465,7 @@ namespace Jellyfin.Server return new ConfigurationBuilder() .SetBasePath(appPaths.ConfigurationDirectoryPath) .AddInMemoryCollection(ConfigurationOptions.Configuration) - .AddJsonFile("logging.json", false, true) + .AddJsonFile(LoggingConfigFile, false, true) .AddEnvironmentVariables("JELLYFIN_") .Build(); } From ccafebca68fc09040572d0a21420ea9e5d6c1088 Mon Sep 17 00:00:00 2001 From: Vasily Date: Thu, 5 Mar 2020 20:37:49 +0300 Subject: [PATCH 104/144] Extract "migrations" config name to a proper constant --- Jellyfin.Server/Migrations/MigrationRunner.cs | 4 ++-- Jellyfin.Server/Migrations/MigrationsListStore.cs | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Jellyfin.Server/Migrations/MigrationRunner.cs b/Jellyfin.Server/Migrations/MigrationRunner.cs index 04d0378521..1db99f5961 100644 --- a/Jellyfin.Server/Migrations/MigrationRunner.cs +++ b/Jellyfin.Server/Migrations/MigrationRunner.cs @@ -27,7 +27,7 @@ namespace Jellyfin.Server.Migrations public static void Run(CoreAppHost host, ILoggerFactory loggerFactory) { var logger = loggerFactory.CreateLogger(); - var migrationOptions = ((IConfigurationManager)host.ServerConfigurationManager).GetConfiguration("migrations"); + var migrationOptions = ((IConfigurationManager)host.ServerConfigurationManager).GetConfiguration(MigrationsListStore.StoreKey); var applied = migrationOptions.Applied.ToList(); for (var i = 0; i < Migrations.Length; i++) @@ -56,7 +56,7 @@ namespace Jellyfin.Server.Migrations { logger.LogInformation("Some migrations were run, saving the state"); migrationOptions.Applied = applied.ToArray(); - host.ServerConfigurationManager.SaveConfiguration("migrations", migrationOptions); + host.ServerConfigurationManager.SaveConfiguration(MigrationsListStore.StoreKey, migrationOptions); } } } diff --git a/Jellyfin.Server/Migrations/MigrationsListStore.cs b/Jellyfin.Server/Migrations/MigrationsListStore.cs index d91d602c14..7a1ca66714 100644 --- a/Jellyfin.Server/Migrations/MigrationsListStore.cs +++ b/Jellyfin.Server/Migrations/MigrationsListStore.cs @@ -7,13 +7,18 @@ namespace Jellyfin.Server.Migrations /// public class MigrationsListStore : ConfigurationStore { + /// + /// The name of the configuration in the storage. + /// + public static readonly string StoreKey = "migrations"; + /// /// Initializes a new instance of the class. /// public MigrationsListStore() { ConfigurationType = typeof(MigrationOptions); - Key = "migrations"; + Key = StoreKey; } } } From 55b429e5e816bea33afbd810d5f1e4f560ef0069 Mon Sep 17 00:00:00 2001 From: Vasily Date: Thu, 5 Mar 2020 20:40:17 +0300 Subject: [PATCH 105/144] Moved migration routines to their own directory --- Jellyfin.Server/Migrations/MigrationRunner.cs | 4 ++-- .../Migrations/{ => Routines}/DisableTranscodingThrottling.cs | 2 +- .../Migrations/{ => Routines}/DisableZealousLogging.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) rename Jellyfin.Server/Migrations/{ => Routines}/DisableTranscodingThrottling.cs (96%) rename Jellyfin.Server/Migrations/{ => Routines}/DisableZealousLogging.cs (95%) diff --git a/Jellyfin.Server/Migrations/MigrationRunner.cs b/Jellyfin.Server/Migrations/MigrationRunner.cs index 1db99f5961..8b72cb467e 100644 --- a/Jellyfin.Server/Migrations/MigrationRunner.cs +++ b/Jellyfin.Server/Migrations/MigrationRunner.cs @@ -15,8 +15,8 @@ namespace Jellyfin.Server.Migrations /// internal static readonly IUpdater[] Migrations = { - new DisableTranscodingThrottling(), - new DisableZealousLogging() + new Routines.DisableTranscodingThrottling(), + new Routines.DisableZealousLogging() }; /// diff --git a/Jellyfin.Server/Migrations/DisableTranscodingThrottling.cs b/Jellyfin.Server/Migrations/Routines/DisableTranscodingThrottling.cs similarity index 96% rename from Jellyfin.Server/Migrations/DisableTranscodingThrottling.cs rename to Jellyfin.Server/Migrations/Routines/DisableTranscodingThrottling.cs index 83624bdadd..eff6469e20 100644 --- a/Jellyfin.Server/Migrations/DisableTranscodingThrottling.cs +++ b/Jellyfin.Server/Migrations/Routines/DisableTranscodingThrottling.cs @@ -5,7 +5,7 @@ using MediaBrowser.Model.Configuration; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; -namespace Jellyfin.Server.Migrations +namespace Jellyfin.Server.Migrations.Routines { /// /// Updater that takes care of bringing configuration up to 10.5.0 standards. diff --git a/Jellyfin.Server/Migrations/DisableZealousLogging.cs b/Jellyfin.Server/Migrations/Routines/DisableZealousLogging.cs similarity index 95% rename from Jellyfin.Server/Migrations/DisableZealousLogging.cs rename to Jellyfin.Server/Migrations/Routines/DisableZealousLogging.cs index a0a934d4ab..501f8f8654 100644 --- a/Jellyfin.Server/Migrations/DisableZealousLogging.cs +++ b/Jellyfin.Server/Migrations/Routines/DisableZealousLogging.cs @@ -7,7 +7,7 @@ using Microsoft.Extensions.Logging; using Serilog; using ILogger = Microsoft.Extensions.Logging.ILogger; -namespace Jellyfin.Server.Migrations +namespace Jellyfin.Server.Migrations.Routines { /// /// Updater that takes care of bringing configuration up to 10.5.0 standards. From 216e425cc55e8de1718df76f89c923cdf54de871 Mon Sep 17 00:00:00 2001 From: Vasily Date: Thu, 5 Mar 2020 20:52:00 +0300 Subject: [PATCH 106/144] Fix comment --- Jellyfin.Server/Migrations/IUpdater.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Server/Migrations/IUpdater.cs b/Jellyfin.Server/Migrations/IUpdater.cs index 10ada73d5f..9b749841cf 100644 --- a/Jellyfin.Server/Migrations/IUpdater.cs +++ b/Jellyfin.Server/Migrations/IUpdater.cs @@ -14,7 +14,7 @@ namespace Jellyfin.Server.Migrations public abstract string Name { get; } /// - /// Execute the migration from version "from". + /// Execute the migration routine. /// /// Host that hosts current version. /// Host logger. From e36c4de9f6bb33fe6ccf3b9c6eae4a2f058424ba Mon Sep 17 00:00:00 2001 From: ferferga Date: Thu, 5 Mar 2020 18:53:04 +0100 Subject: [PATCH 107/144] Replaces uninstaller icon so it's more visible --- deployment/windows/jellyfin.nsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployment/windows/jellyfin.nsi b/deployment/windows/jellyfin.nsi index 86724b8f49..fada62d981 100644 --- a/deployment/windows/jellyfin.nsi +++ b/deployment/windows/jellyfin.nsi @@ -73,7 +73,7 @@ Unicode True ; TODO: Replace with nice Jellyfin Icons !ifdef UXPATH !define MUI_ICON "${UXPATH}\branding\NSIS\modern-install.ico" ; Installer Icon - !define MUI_UNICON "${UXPATH}\branding\NSIS\modern-uninstall.ico" ; Uninstaller Icon + !define MUI_UNICON "${UXPATH}\branding\NSIS\modern-install.ico" ; Uninstaller Icon !define MUI_HEADERIMAGE !define MUI_HEADERIMAGE_BITMAP "${UXPATH}\branding\NSIS\installer-header.bmp" From d4564d8e29274a854bdc46df3ebcaf0c1e39e906 Mon Sep 17 00:00:00 2001 From: Vasily Date: Fri, 6 Mar 2020 13:22:44 +0300 Subject: [PATCH 108/144] More logging, mark all migrations as applied if setup wizard is not complete --- Jellyfin.Server/Migrations/MigrationRunner.cs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/Jellyfin.Server/Migrations/MigrationRunner.cs b/Jellyfin.Server/Migrations/MigrationRunner.cs index 8b72cb467e..00a7c3a00d 100644 --- a/Jellyfin.Server/Migrations/MigrationRunner.cs +++ b/Jellyfin.Server/Migrations/MigrationRunner.cs @@ -28,6 +28,17 @@ namespace Jellyfin.Server.Migrations { var logger = loggerFactory.CreateLogger(); var migrationOptions = ((IConfigurationManager)host.ServerConfigurationManager).GetConfiguration(MigrationsListStore.StoreKey); + + if (!host.ServerConfigurationManager.Configuration.IsStartupWizardCompleted) + { + // If startup wizard is not finished, this is a fresh install. + // Don't run any migrations, just mark all of them as applied. + logger.LogInformation("Marking all known migrations as applied because this is fresh install"); + migrationOptions.Applied = Migrations.Select(m => m.Name).ToArray(); + host.ServerConfigurationManager.SaveConfiguration(MigrationsListStore.StoreKey, migrationOptions); + return; + } + var applied = migrationOptions.Applied.ToList(); for (var i = 0; i < Migrations.Length; i++) @@ -35,20 +46,22 @@ namespace Jellyfin.Server.Migrations var updater = Migrations[i]; if (applied.Contains(updater.Name)) { - logger.LogDebug("Skipping migration {0} as it is already applied", updater.Name); + logger.LogDebug("Skipping migration {Name} as it is already applied", updater.Name); continue; } + logger.LogInformation("Applying migration {Name}", updater.Name); try { updater.Perform(host, logger); } catch (Exception ex) { - logger.LogError(ex, "Cannot apply migration {0}", updater.Name); + logger.LogError(ex, "Cannot apply migration {Name}", updater.Name); continue; } + logger.LogInformation("Migration {Name} applied successfully", updater.Name); applied.Add(updater.Name); } From 098d3538e32f4440bf6ed3a14844ff3a3b2a6fca Mon Sep 17 00:00:00 2001 From: Vasily Date: Fri, 6 Mar 2020 17:22:22 +0300 Subject: [PATCH 109/144] Disable logging.json migration as it is not ready yet --- Jellyfin.Server/Migrations/MigrationRunner.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Jellyfin.Server/Migrations/MigrationRunner.cs b/Jellyfin.Server/Migrations/MigrationRunner.cs index 00a7c3a00d..caaa58ae15 100644 --- a/Jellyfin.Server/Migrations/MigrationRunner.cs +++ b/Jellyfin.Server/Migrations/MigrationRunner.cs @@ -15,8 +15,7 @@ namespace Jellyfin.Server.Migrations /// internal static readonly IUpdater[] Migrations = { - new Routines.DisableTranscodingThrottling(), - new Routines.DisableZealousLogging() + new Routines.DisableTranscodingThrottling() }; /// From 5a0f1fe848aa16176ee1f697af8fe91b92c15c54 Mon Sep 17 00:00:00 2001 From: Vasily Date: Fri, 6 Mar 2020 19:01:07 +0300 Subject: [PATCH 110/144] Implement review suggestion --- Jellyfin.Server/Migrations/MigrationRunner.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Server/Migrations/MigrationRunner.cs b/Jellyfin.Server/Migrations/MigrationRunner.cs index caaa58ae15..0274e68a1d 100644 --- a/Jellyfin.Server/Migrations/MigrationRunner.cs +++ b/Jellyfin.Server/Migrations/MigrationRunner.cs @@ -28,7 +28,7 @@ namespace Jellyfin.Server.Migrations var logger = loggerFactory.CreateLogger(); var migrationOptions = ((IConfigurationManager)host.ServerConfigurationManager).GetConfiguration(MigrationsListStore.StoreKey); - if (!host.ServerConfigurationManager.Configuration.IsStartupWizardCompleted) + if (!host.ServerConfigurationManager.Configuration.IsStartupWizardCompleted && migrationOptions.Applied.Length == 0) { // If startup wizard is not finished, this is a fresh install. // Don't run any migrations, just mark all of them as applied. From f2fdf50b3b7138f47e1777e31ef97b737775fb63 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Fri, 6 Mar 2020 19:07:34 +0100 Subject: [PATCH 111/144] Create separate constants for the two logging file names --- Jellyfin.Server/Program.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 0271861054..970443c8b6 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -39,9 +39,14 @@ namespace Jellyfin.Server public static class Program { /// - /// The name of logging configuration file. + /// The name of logging configuration file containing application defaults. /// - public static readonly string LoggingConfigFile = "logging.json"; + public static readonly string LoggingConfigFileDefault = "logging.default.json"; + + /// + /// The name of the logging configuration file containing user override settings. + /// + public static readonly string LoggingConfigFileUser = "logging.user.json"; private static readonly CancellationTokenSource _tokenSource = new CancellationTokenSource(); private static readonly ILoggerFactory _loggerFactory = new SerilogLoggerFactory(); @@ -443,7 +448,7 @@ namespace Jellyfin.Server private static async Task CreateConfiguration(IApplicationPaths appPaths) { const string ResourcePath = "Jellyfin.Server.Resources.Configuration.logging.json"; - string configPath = Path.Combine(appPaths.ConfigurationDirectoryPath, LoggingConfigFile); + string configPath = Path.Combine(appPaths.ConfigurationDirectoryPath, LoggingConfigFileDefault); if (!File.Exists(configPath)) { @@ -465,7 +470,7 @@ namespace Jellyfin.Server return new ConfigurationBuilder() .SetBasePath(appPaths.ConfigurationDirectoryPath) .AddInMemoryCollection(ConfigurationOptions.Configuration) - .AddJsonFile(LoggingConfigFile, false, true) + .AddJsonFile(LoggingConfigFileDefault, false, true) .AddEnvironmentVariables("JELLYFIN_") .Build(); } From 1a9908d094a83b66c1b56e78bc496a1ff056c7d9 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Fri, 6 Mar 2020 19:11:42 +0100 Subject: [PATCH 112/144] Add migration to create "logging.user.json" --- Jellyfin.Server/Migrations/MigrationRunner.cs | 3 +- .../Routines/CreateUserLoggingConfigFile.cs | 93 +++++++++++++++++++ .../Routines/DisableZealousLogging.cs | 29 ------ 3 files changed, 95 insertions(+), 30 deletions(-) create mode 100644 Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs delete mode 100644 Jellyfin.Server/Migrations/Routines/DisableZealousLogging.cs diff --git a/Jellyfin.Server/Migrations/MigrationRunner.cs b/Jellyfin.Server/Migrations/MigrationRunner.cs index caaa58ae15..ac7f3d77ae 100644 --- a/Jellyfin.Server/Migrations/MigrationRunner.cs +++ b/Jellyfin.Server/Migrations/MigrationRunner.cs @@ -15,7 +15,8 @@ namespace Jellyfin.Server.Migrations /// internal static readonly IUpdater[] Migrations = { - new Routines.DisableTranscodingThrottling() + new Routines.DisableTranscodingThrottling(), + new Routines.CreateUserLoggingConfigFile() }; /// diff --git a/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs b/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs new file mode 100644 index 0000000000..7a089680e4 --- /dev/null +++ b/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using MediaBrowser.Common.Configuration; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json.Linq; + +namespace Jellyfin.Server.Migrations.Routines +{ + /// + /// Migration to initialize the user logging configuration file "logging.user.json". + /// If the deprecated logging.json file exists and has a custom config, it will be used as logging.user.json, + /// otherwise a blank file will be created. + /// + internal class CreateUserLoggingConfigFile : IUpdater + { + /// + /// An empty logging JSON configuration, which will be used as the default contents for the user settings config file. + /// + private const string EmptyLoggingConfig = @"{ ""Serilog"": { } }"; + + /// + /// File history for logging.json as existed during this migration creation. The contents for each has been minified. + /// + private readonly List _defaultConfigHistory = new List + { + // 9a6c27947353585391e211aa88b925f81e8cd7b9 + @"{""Serilog"":{""MinimumLevel"":{""Default"":""Information"",""Override"":{""Microsoft"":""Warning"",""System"":""Warning""}},""WriteTo"":[{""Name"":""Console"",""Args"":{""outputTemplate"":""[{Timestamp:HH:mm:ss}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message:lj}{NewLine}{Exception}""}},{""Name"":""Async"",""Args"":{""configure"":[{""Name"":""File"",""Args"":{""path"":""%JELLYFIN_LOG_DIR%//log_.log"",""rollingInterval"":""Day"",""retainedFileCountLimit"":3,""rollOnFileSizeLimit"":true,""fileSizeLimitBytes"":100000000,""outputTemplate"":""[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message}{NewLine}{Exception}""}}]}}],""Enrich"":[""FromLogContext"",""WithThreadId""]}}", + // 71bdcd730705a714ee208eaad7290b7c68df3885 + @"{""Serilog"":{""MinimumLevel"":""Information"",""WriteTo"":[{""Name"":""Console"",""Args"":{""outputTemplate"":""[{Timestamp:HH:mm:ss}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message:lj}{NewLine}{Exception}""}},{""Name"":""Async"",""Args"":{""configure"":[{""Name"":""File"",""Args"":{""path"":""%JELLYFIN_LOG_DIR%//log_.log"",""rollingInterval"":""Day"",""retainedFileCountLimit"":3,""rollOnFileSizeLimit"":true,""fileSizeLimitBytes"":100000000,""outputTemplate"":""[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message}{NewLine}{Exception}""}}]}}],""Enrich"":[""FromLogContext"",""WithThreadId""]}}", + // a44936f97f8afc2817d3491615a7cfe1e31c251c + @"{""Serilog"":{""MinimumLevel"":""Information"",""WriteTo"":[{""Name"":""Console"",""Args"":{""outputTemplate"":""[{Timestamp:HH:mm:ss}] [{Level:u3}] {Message:lj}{NewLine}{Exception}""}},{""Name"":""File"",""Args"":{""path"":""%JELLYFIN_LOG_DIR%//log_.log"",""rollingInterval"":""Day"",""outputTemplate"":""[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] {Message}{NewLine}{Exception}""}}]}}", + // 7af3754a11ad5a4284f107997fb5419a010ce6f3 + @"{""Serilog"":{""MinimumLevel"":""Information"",""WriteTo"":[{""Name"":""Console"",""Args"":{""outputTemplate"":""[{Timestamp:HH:mm:ss}] [{Level:u3}] {Message:lj}{NewLine}{Exception}""}},{""Name"":""Async"",""Args"":{""configure"":[{""Name"":""File"",""Args"":{""path"":""%JELLYFIN_LOG_DIR%//log_.log"",""rollingInterval"":""Day"",""outputTemplate"":""[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] {Message}{NewLine}{Exception}""}}]}}]}}", + // 60691349a11f541958e0b2247c9abc13cb40c9fb + @"{""Serilog"":{""MinimumLevel"":""Information"",""WriteTo"":[{""Name"":""Console"",""Args"":{""outputTemplate"":""[{Timestamp:HH:mm:ss}] [{Level:u3}] {Message:lj}{NewLine}{Exception}""}},{""Name"":""Async"",""Args"":{""configure"":[{""Name"":""File"",""Args"":{""path"":""%JELLYFIN_LOG_DIR%//log_.log"",""rollingInterval"":""Day"",""retainedFileCountLimit"":3,""rollOnFileSizeLimit"":true,""fileSizeLimitBytes"":100000000,""outputTemplate"":""[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] {Message}{NewLine}{Exception}""}}]}}]}}", + // 65fe243afbcc4b596cf8726708c1965cd34b5f68 + @"{""Serilog"":{""MinimumLevel"":""Information"",""WriteTo"":[{""Name"":""Console"",""Args"":{""outputTemplate"":""[{Timestamp:HH:mm:ss}] [{Level:u3}] {ThreadId} {SourceContext}: {Message:lj} {NewLine}{Exception}""}},{""Name"":""Async"",""Args"":{""configure"":[{""Name"":""File"",""Args"":{""path"":""%JELLYFIN_LOG_DIR%//log_.log"",""rollingInterval"":""Day"",""retainedFileCountLimit"":3,""rollOnFileSizeLimit"":true,""fileSizeLimitBytes"":100000000,""outputTemplate"":""[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] {ThreadId} {SourceContext}:{Message} {NewLine}{Exception}""}}]}}],""Enrich"":[""FromLogContext"",""WithThreadId""]}}", + // 96c9af590494aa8137d5a061aaf1e68feee60b67 + @"{""Serilog"":{""MinimumLevel"":""Information"",""WriteTo"":[{""Name"":""Console"",""Args"":{""outputTemplate"":""[{Timestamp:HH:mm:ss}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message:lj}{NewLine}{Exception}""}},{""Name"":""Async"",""Args"":{""configure"":[{""Name"":""File"",""Args"":{""path"":""%JELLYFIN_LOG_DIR%//log_.log"",""rollingInterval"":""Day"",""retainedFileCountLimit"":3,""rollOnFileSizeLimit"":true,""fileSizeLimitBytes"":100000000,""outputTemplate"":""[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] [{ThreadId}] {SourceContext}:{Message}{NewLine}{Exception}""}}]}}],""Enrich"":[""FromLogContext"",""WithThreadId""]}}", + }; + + /// + public string Name => "CreateLoggingConfigHeirarchy"; + + /// + public void Perform(CoreAppHost host, ILogger logger) + { + var logDirectory = host.Resolve().ConfigurationDirectoryPath; + var oldConfigPath = Path.Combine(logDirectory, "logging.json"); + var userConfigPath = Path.Combine(logDirectory, Program.LoggingConfigFileUser); + + // Check if there are existing settings in the old "logging.json" file that should be migrated + bool shouldMigrateOldFile = ShouldKeepOldConfig(oldConfigPath); + + // Create the user settings file "logging.user.json" + if (shouldMigrateOldFile) + { + // Use the existing logging.json file + File.Copy(oldConfigPath, userConfigPath); + } + else + { + // Write an empty JSON file + File.WriteAllText(userConfigPath, EmptyLoggingConfig); + } + } + + /// + /// Check if the existing logging.json file should be migrated to logging.user.json. + /// + private bool ShouldKeepOldConfig(string oldConfigPath) + { + // Cannot keep the old logging file if it doesn't exist + if (!File.Exists(oldConfigPath)) + { + return false; + } + + // Check if the existing logging.json file has been modified by the user by comparing it to all the + // versions in our git history. Until now, the file has never been migrated after first creation so users + // could have any version from the git history. + var existingConfigJson = JToken.Parse(File.ReadAllText(oldConfigPath)); + var existingConfigIsUnmodified = _defaultConfigHistory + .Select(historicalConfigText => JToken.Parse(historicalConfigText)) + .Any(historicalConfigJson => JToken.DeepEquals(existingConfigJson, historicalConfigJson)); + + // The existing config file should be kept and used only if it has been modified by the user + return !existingConfigIsUnmodified; + } + } +} diff --git a/Jellyfin.Server/Migrations/Routines/DisableZealousLogging.cs b/Jellyfin.Server/Migrations/Routines/DisableZealousLogging.cs deleted file mode 100644 index 501f8f8654..0000000000 --- a/Jellyfin.Server/Migrations/Routines/DisableZealousLogging.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using System.IO; -using MediaBrowser.Common.Configuration; -using MediaBrowser.Model.Configuration; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; -using Serilog; -using ILogger = Microsoft.Extensions.Logging.ILogger; - -namespace Jellyfin.Server.Migrations.Routines -{ - /// - /// Updater that takes care of bringing configuration up to 10.5.0 standards. - /// - internal class DisableZealousLogging : IUpdater - { - /// - public string Name => "DisableZealousLogging"; - - /// - // This tones down logging from some components - public void Perform(CoreAppHost host, ILogger logger) - { - string configPath = Path.Combine(host.ServerConfigurationManager.ApplicationPaths.ConfigurationDirectoryPath, Program.LoggingConfigFile); - // TODO: fix up the config - throw new NotImplementedException("don't know how to fix logging yet"); - } - } -} From 6660006f01aee44ea33d1539000c5e4ea06e1115 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Fri, 6 Mar 2020 19:28:36 +0100 Subject: [PATCH 113/144] Load user logging config file into application configuration --- Jellyfin.Server/Program.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 970443c8b6..2590fdb215 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -470,7 +470,8 @@ namespace Jellyfin.Server return new ConfigurationBuilder() .SetBasePath(appPaths.ConfigurationDirectoryPath) .AddInMemoryCollection(ConfigurationOptions.Configuration) - .AddJsonFile(LoggingConfigFileDefault, false, true) + .AddJsonFile(LoggingConfigFileDefault, optional: false, reloadOnChange: true) + .AddJsonFile(LoggingConfigFileUser, optional: true, reloadOnChange: true) .AddEnvironmentVariables("JELLYFIN_") .Build(); } From 4c2b543b307b55b2220472c59396b9b4a604cfb7 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Fri, 6 Mar 2020 21:51:50 +0100 Subject: [PATCH 114/144] Rename IUpdater to IMigrationRoutine --- .../{IUpdater.cs => IMigrationRoutine.cs} | 8 ++++---- Jellyfin.Server/Migrations/MigrationRunner.cs | 18 +++++++++--------- .../Routines/CreateUserLoggingConfigFile.cs | 2 +- .../Routines/DisableTranscodingThrottling.cs | 2 +- 4 files changed, 15 insertions(+), 15 deletions(-) rename Jellyfin.Server/Migrations/{IUpdater.cs => IMigrationRoutine.cs} (69%) diff --git a/Jellyfin.Server/Migrations/IUpdater.cs b/Jellyfin.Server/Migrations/IMigrationRoutine.cs similarity index 69% rename from Jellyfin.Server/Migrations/IUpdater.cs rename to Jellyfin.Server/Migrations/IMigrationRoutine.cs index 9b749841cf..20a3aa3d6a 100644 --- a/Jellyfin.Server/Migrations/IUpdater.cs +++ b/Jellyfin.Server/Migrations/IMigrationRoutine.cs @@ -4,20 +4,20 @@ using Microsoft.Extensions.Logging; namespace Jellyfin.Server.Migrations { /// - /// Interface that descibes a migration routine. + /// Interface that describes a migration routine. /// - internal interface IUpdater + internal interface IMigrationRoutine { /// /// Gets the name of the migration, must be unique. /// - public abstract string Name { get; } + public string Name { get; } /// /// Execute the migration routine. /// /// Host that hosts current version. /// Host logger. - public abstract void Perform(CoreAppHost host, ILogger logger); + public void Perform(CoreAppHost host, ILogger logger); } } diff --git a/Jellyfin.Server/Migrations/MigrationRunner.cs b/Jellyfin.Server/Migrations/MigrationRunner.cs index ac7f3d77ae..8e786f34ea 100644 --- a/Jellyfin.Server/Migrations/MigrationRunner.cs +++ b/Jellyfin.Server/Migrations/MigrationRunner.cs @@ -13,7 +13,7 @@ namespace Jellyfin.Server.Migrations /// /// The list of known migrations, in order of applicability. /// - internal static readonly IUpdater[] Migrations = + internal static readonly IMigrationRoutine[] Migrations = { new Routines.DisableTranscodingThrottling(), new Routines.CreateUserLoggingConfigFile() @@ -43,26 +43,26 @@ namespace Jellyfin.Server.Migrations for (var i = 0; i < Migrations.Length; i++) { - var updater = Migrations[i]; - if (applied.Contains(updater.Name)) + var migrationRoutine = Migrations[i]; + if (applied.Contains(migrationRoutine.Name)) { - logger.LogDebug("Skipping migration {Name} as it is already applied", updater.Name); + logger.LogDebug("Skipping migration {Name} as it is already applied", migrationRoutine.Name); continue; } - logger.LogInformation("Applying migration {Name}", updater.Name); + logger.LogInformation("Applying migration {Name}", migrationRoutine.Name); try { - updater.Perform(host, logger); + migrationRoutine.Perform(host, logger); } catch (Exception ex) { - logger.LogError(ex, "Cannot apply migration {Name}", updater.Name); + logger.LogError(ex, "Cannot apply migration {Name}", migrationRoutine.Name); continue; } - logger.LogInformation("Migration {Name} applied successfully", updater.Name); - applied.Add(updater.Name); + logger.LogInformation("Migration {Name} applied successfully", migrationRoutine.Name); + applied.Add(migrationRoutine.Name); } if (applied.Count > migrationOptions.Applied.Length) diff --git a/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs b/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs index 7a089680e4..6dbeb27761 100644 --- a/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs +++ b/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs @@ -13,7 +13,7 @@ namespace Jellyfin.Server.Migrations.Routines /// If the deprecated logging.json file exists and has a custom config, it will be used as logging.user.json, /// otherwise a blank file will be created. /// - internal class CreateUserLoggingConfigFile : IUpdater + internal class CreateUserLoggingConfigFile : IMigrationRoutine { /// /// An empty logging JSON configuration, which will be used as the default contents for the user settings config file. diff --git a/Jellyfin.Server/Migrations/Routines/DisableTranscodingThrottling.cs b/Jellyfin.Server/Migrations/Routines/DisableTranscodingThrottling.cs index eff6469e20..db0bef8851 100644 --- a/Jellyfin.Server/Migrations/Routines/DisableTranscodingThrottling.cs +++ b/Jellyfin.Server/Migrations/Routines/DisableTranscodingThrottling.cs @@ -10,7 +10,7 @@ namespace Jellyfin.Server.Migrations.Routines /// /// Updater that takes care of bringing configuration up to 10.5.0 standards. /// - internal class DisableTranscodingThrottling : IUpdater + internal class DisableTranscodingThrottling : IMigrationRoutine { /// public string Name => "DisableTranscodingThrottling"; From b373721d299826bafac8cfadeeacbd04fed436dd Mon Sep 17 00:00:00 2001 From: Marcus Schelin Date: Fri, 6 Mar 2020 22:27:05 +0000 Subject: [PATCH 115/144] Translated using Weblate (Swedish) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/sv/ --- .../Localization/Core/sv.json | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/sv.json b/Emby.Server.Implementations/Localization/Core/sv.json index db4cfde957..b2934545d3 100644 --- a/Emby.Server.Implementations/Localization/Core/sv.json +++ b/Emby.Server.Implementations/Localization/Core/sv.json @@ -1,7 +1,7 @@ { "Albums": "Album", - "AppDeviceValues": "App: {0}, Enhet: {1}", - "Application": "App", + "AppDeviceValues": "Applikation: {0}, Enhet: {1}", + "Application": "Applikation", "Artists": "Artister", "AuthenticationSucceededWithUserName": "{0} har autentiserats", "Books": "Böcker", @@ -16,7 +16,7 @@ "Folders": "Mappar", "Genres": "Genrer", "HeaderAlbumArtists": "Albumartister", - "HeaderCameraUploads": "Kamera Uppladdningar", + "HeaderCameraUploads": "Kamerauppladdningar", "HeaderContinueWatching": "Fortsätt kolla", "HeaderFavoriteAlbums": "Favoritalbum", "HeaderFavoriteArtists": "Favoritartister", @@ -34,9 +34,9 @@ "LabelRunningTimeValue": "Speltid: {0}", "Latest": "Senaste", "MessageApplicationUpdated": "Jellyfin Server har uppdaterats", - "MessageApplicationUpdatedTo": "Jellyfin Server har uppgraderats till {0}", + "MessageApplicationUpdatedTo": "Jellyfin Server har uppdaterats till {0}", "MessageNamedServerConfigurationUpdatedWithValue": "Serverinställningarna {0} har uppdaterats", - "MessageServerConfigurationUpdated": "Server konfigurationen har uppdaterats", + "MessageServerConfigurationUpdated": "Serverkonfigurationen har uppdaterats", "MixedContent": "Blandat innehåll", "Movies": "Filmer", "Music": "Musik", @@ -44,11 +44,11 @@ "NameInstallFailed": "{0} installationen misslyckades", "NameSeasonNumber": "Säsong {0}", "NameSeasonUnknown": "Okänd säsong", - "NewVersionIsAvailable": "En ny version av Jellyfin Server är klar för nedladdning.", + "NewVersionIsAvailable": "En ny version av Jellyfin Server är tillgänglig att hämta.", "NotificationOptionApplicationUpdateAvailable": "Ny programversion tillgänglig", "NotificationOptionApplicationUpdateInstalled": "Programuppdatering installerad", "NotificationOptionAudioPlayback": "Ljuduppspelning har påbörjats", - "NotificationOptionAudioPlaybackStopped": "Ljuduppspelning stoppad", + "NotificationOptionAudioPlaybackStopped": "Ljuduppspelning stoppades", "NotificationOptionCameraImageUploaded": "Kamerabild har laddats upp", "NotificationOptionInstallationFailed": "Fel vid installation", "NotificationOptionNewLibraryContent": "Nytt innehåll har lagts till", @@ -60,7 +60,7 @@ "NotificationOptionTaskFailed": "Schemalagd aktivitet har misslyckats", "NotificationOptionUserLockedOut": "Användare har låsts ut", "NotificationOptionVideoPlayback": "Videouppspelning har påbörjats", - "NotificationOptionVideoPlaybackStopped": "Videouppspelning stoppad", + "NotificationOptionVideoPlaybackStopped": "Videouppspelning stoppades", "Photos": "Bilder", "Playlists": "Spellistor", "Plugin": "Tillägg", @@ -69,13 +69,13 @@ "PluginUpdatedWithName": "{0} uppdaterades", "ProviderValue": "Källa: {0}", "ScheduledTaskFailedWithName": "{0} misslyckades", - "ScheduledTaskStartedWithName": "{0} startad", + "ScheduledTaskStartedWithName": "{0} startades", "ServerNameNeedsToBeRestarted": "{0} behöver startas om", "Shows": "Serier", "Songs": "Låtar", - "StartupEmbyServerIsLoading": "Jellyfin server arbetar. Pröva igen inom kort.", + "StartupEmbyServerIsLoading": "Jellyfin Server arbetar. Pröva igen snart.", "SubtitleDownloadFailureForItem": "Nerladdning av undertexter för {0} misslyckades", - "SubtitleDownloadFailureFromForItem": "Undertexter misslyckades att ladda ner {0} för {1}", + "SubtitleDownloadFailureFromForItem": "Undertexter kunde inte laddas ner från {0} för {1}", "SubtitlesDownloadedForItem": "Undertexter har laddats ner till {0}", "Sync": "Synk", "System": "System", @@ -89,9 +89,9 @@ "UserOnlineFromDevice": "{0} är uppkopplad från {1}", "UserPasswordChangedWithName": "Lösenordet för {0} har ändrats", "UserPolicyUpdatedWithName": "Användarpolicyn har uppdaterats för {0}", - "UserStartedPlayingItemWithValues": "{0} har börjat spela upp {1}", - "UserStoppedPlayingItemWithValues": "{0} har avslutat uppspelningen av {1}", - "ValueHasBeenAddedToLibrary": "{0} har blivit tillagd till ditt mediabibliotek", + "UserStartedPlayingItemWithValues": "{0} spelar upp {1} på {2}", + "UserStoppedPlayingItemWithValues": "{0} har avslutat uppspelningen av {1} på {2}", + "ValueHasBeenAddedToLibrary": "{0} har lagts till i ditt mediebibliotek", "ValueSpecialEpisodeName": "Specialavsnitt - {0}", "VersionNumber": "Version {0}" } From f3db3cacf6c5ee66128305154daaeaca09de9cb2 Mon Sep 17 00:00:00 2001 From: Terrance M Date: Fri, 6 Mar 2020 22:26:36 +0000 Subject: [PATCH 116/144] Translated using Weblate (Chinese (Simplified)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/zh_Hans/ --- Emby.Server.Implementations/Localization/Core/zh-CN.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/zh-CN.json b/Emby.Server.Implementations/Localization/Core/zh-CN.json index dd61686145..68134a1510 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-CN.json +++ b/Emby.Server.Implementations/Localization/Core/zh-CN.json @@ -17,14 +17,14 @@ "Genres": "风格", "HeaderAlbumArtists": "专辑作家", "HeaderCameraUploads": "相机上传", - "HeaderContinueWatching": "继续观看", + "HeaderContinueWatching": "继续观影", "HeaderFavoriteAlbums": "收藏的专辑", "HeaderFavoriteArtists": "最爱的艺术家", "HeaderFavoriteEpisodes": "最爱的剧集", "HeaderFavoriteShows": "最爱的节目", "HeaderFavoriteSongs": "最爱的歌曲", "HeaderLiveTV": "电视直播", - "HeaderNextUp": "下一步", + "HeaderNextUp": "接下来", "HeaderRecordingGroups": "录制组", "HomeVideos": "家庭视频", "Inherit": "继承", From e8c593f413f0856934f446db2cc8aca3693df818 Mon Sep 17 00:00:00 2001 From: MrTimscampi Date: Sat, 7 Mar 2020 17:39:55 +0100 Subject: [PATCH 117/144] Add baseURL to attachments --- MediaBrowser.Api/Playback/MediaInfoService.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index a44e1720fe..08a7e534f9 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -573,7 +573,8 @@ namespace MediaBrowser.Api.Playback { attachment.DeliveryUrl = string.Format( CultureInfo.InvariantCulture, - "/Videos/{0}/{1}/Attachments/{2}", + "{0}/Videos/{1}/{2}/Attachments/{3}", + ServerConfigurationManager.Configuration.BaseUrl, item.Id, mediaSource.Id, attachment.Index); From e0381c88544e214d9c4ed328b550d938818a7357 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sat, 7 Mar 2020 11:55:02 -0500 Subject: [PATCH 118/144] Set EnableHttps disabled by default Prevents issues on first setup when behind a reverse proxy. Also prevents issues saving the Networking page by default if SSL is not fully configured. --- MediaBrowser.Model/Configuration/ServerConfiguration.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index bf10108b8c..c8f18e69e5 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -248,7 +248,7 @@ namespace MediaBrowser.Model.Configuration PublicHttpsPort = DefaultHttpsPort; HttpServerPortNumber = DefaultHttpPort; HttpsPortNumber = DefaultHttpsPort; - EnableHttps = true; + EnableHttps = false; EnableDashboardResponseCaching = true; EnableCaseSensitiveItemIds = true; From 7ecb16a46e44b8e2f8cc23329a2b66dad6aeb2fd Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sat, 7 Mar 2020 18:23:32 +0100 Subject: [PATCH 119/144] do not ignore exceptions during migration execution --- Jellyfin.Server/Migrations/MigrationRunner.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Server/Migrations/MigrationRunner.cs b/Jellyfin.Server/Migrations/MigrationRunner.cs index 0274e68a1d..15c1e75aaf 100644 --- a/Jellyfin.Server/Migrations/MigrationRunner.cs +++ b/Jellyfin.Server/Migrations/MigrationRunner.cs @@ -57,7 +57,7 @@ namespace Jellyfin.Server.Migrations catch (Exception ex) { logger.LogError(ex, "Cannot apply migration {Name}", updater.Name); - continue; + throw; } logger.LogInformation("Migration {Name} applied successfully", updater.Name); From 1295f6c79bdf76274501838c9e42094e4b1dd3c0 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sat, 7 Mar 2020 20:18:45 +0100 Subject: [PATCH 120/144] Documentation and log message cleanup --- Jellyfin.Server/Migrations/MigrationOptions.cs | 2 +- Jellyfin.Server/Migrations/MigrationRunner.cs | 10 +++++----- Jellyfin.Server/Migrations/MigrationsFactory.cs | 2 +- .../Routines/DisableTranscodingThrottling.cs | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Jellyfin.Server/Migrations/MigrationOptions.cs b/Jellyfin.Server/Migrations/MigrationOptions.cs index b96288cc1b..6b7831158f 100644 --- a/Jellyfin.Server/Migrations/MigrationOptions.cs +++ b/Jellyfin.Server/Migrations/MigrationOptions.cs @@ -15,7 +15,7 @@ namespace Jellyfin.Server.Migrations #pragma warning disable CA1819 // Properties should not return arrays /// - /// Gets or sets he list of applied migration routine names. + /// Gets or sets the list of applied migration routine names. /// public string[] Applied { get; set; } #pragma warning restore CA1819 // Properties should not return arrays diff --git a/Jellyfin.Server/Migrations/MigrationRunner.cs b/Jellyfin.Server/Migrations/MigrationRunner.cs index 15c1e75aaf..ca4c79cfd3 100644 --- a/Jellyfin.Server/Migrations/MigrationRunner.cs +++ b/Jellyfin.Server/Migrations/MigrationRunner.cs @@ -6,7 +6,7 @@ using Microsoft.Extensions.Logging; namespace Jellyfin.Server.Migrations { /// - /// The class that knows how migrate between different Jellyfin versions. + /// The class that knows which migrations to apply and how to apply them. /// public sealed class MigrationRunner { @@ -45,22 +45,22 @@ namespace Jellyfin.Server.Migrations var updater = Migrations[i]; if (applied.Contains(updater.Name)) { - logger.LogDebug("Skipping migration {Name} as it is already applied", updater.Name); + logger.LogDebug("Skipping migration '{Name}' since it is already applied", updater.Name); continue; } - logger.LogInformation("Applying migration {Name}", updater.Name); + logger.LogInformation("Applying migration '{Name}'", updater.Name); try { updater.Perform(host, logger); } catch (Exception ex) { - logger.LogError(ex, "Cannot apply migration {Name}", updater.Name); + logger.LogError(ex, "Could not apply migration '{Name}'", updater.Name); throw; } - logger.LogInformation("Migration {Name} applied successfully", updater.Name); + logger.LogInformation("Migration '{Name}' applied successfully", updater.Name); applied.Add(updater.Name); } diff --git a/Jellyfin.Server/Migrations/MigrationsFactory.cs b/Jellyfin.Server/Migrations/MigrationsFactory.cs index ed01dc646a..23c1b1ee6f 100644 --- a/Jellyfin.Server/Migrations/MigrationsFactory.cs +++ b/Jellyfin.Server/Migrations/MigrationsFactory.cs @@ -4,7 +4,7 @@ using MediaBrowser.Common.Configuration; namespace Jellyfin.Server.Migrations { /// - /// A factory that teachs Jellyfin how to find a peristent file which lists all applied migrations. + /// A factory that can find a persistent file of the migration configuration, which lists all applied migrations. /// public class MigrationsFactory : IConfigurationFactory { diff --git a/Jellyfin.Server/Migrations/Routines/DisableTranscodingThrottling.cs b/Jellyfin.Server/Migrations/Routines/DisableTranscodingThrottling.cs index eff6469e20..936c3640e0 100644 --- a/Jellyfin.Server/Migrations/Routines/DisableTranscodingThrottling.cs +++ b/Jellyfin.Server/Migrations/Routines/DisableTranscodingThrottling.cs @@ -8,7 +8,7 @@ using Microsoft.Extensions.Logging; namespace Jellyfin.Server.Migrations.Routines { /// - /// Updater that takes care of bringing configuration up to 10.5.0 standards. + /// Disable transcode throttling for all installations since it is currently broken for certain video formats. /// internal class DisableTranscodingThrottling : IUpdater { @@ -18,7 +18,7 @@ namespace Jellyfin.Server.Migrations.Routines /// public void Perform(CoreAppHost host, ILogger logger) { - // Set EnableThrottling to false as it wasn't used before, and in 10.5.0 it may introduce issues + // Set EnableThrottling to false since it wasn't used before and may introduce issues var encoding = ((IConfigurationManager)host.ServerConfigurationManager).GetConfiguration("encoding"); if (encoding.EnableThrottling) { From 796bdd46df40ce0f964d4c293361205ded3b41cb Mon Sep 17 00:00:00 2001 From: Leo Verto Date: Sat, 7 Mar 2020 20:14:28 +0000 Subject: [PATCH 121/144] Translated using Weblate (German) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/de/ --- Emby.Server.Implementations/Localization/Core/de.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/de.json b/Emby.Server.Implementations/Localization/Core/de.json index 019736c471..69678c2684 100644 --- a/Emby.Server.Implementations/Localization/Core/de.json +++ b/Emby.Server.Implementations/Localization/Core/de.json @@ -1,15 +1,15 @@ { "Albums": "Alben", - "AppDeviceValues": "App: {0}, Gerät: {1}", - "Application": "Anwendung", + "AppDeviceValues": "Anw: {0}, Gerät: {1}", + "Application": "Programm", "Artists": "Interpreten", "AuthenticationSucceededWithUserName": "{0} hat sich erfolgreich angemeldet", "Books": "Bücher", - "CameraImageUploadedFrom": "Ein neues Foto wurde hochgeladen von {0}", + "CameraImageUploadedFrom": "Ein neues Foto wurde von {0} hochgeladen", "Channels": "Kanäle", "ChapterNameValue": "Kapitel {0}", "Collections": "Sammlungen", - "DeviceOfflineWithName": "{0} wurde getrennt", + "DeviceOfflineWithName": "{0} hat die Verbindung getrennt", "DeviceOnlineWithName": "{0} ist verbunden", "FailedLoginAttemptWithUserName": "Fehlgeschlagener Anmeldeversuch von {0}", "Favorites": "Favoriten", @@ -17,7 +17,7 @@ "Genres": "Genres", "HeaderAlbumArtists": "Album-Interpreten", "HeaderCameraUploads": "Kamera-Uploads", - "HeaderContinueWatching": "Weiterschauen", + "HeaderContinueWatching": "Fortsetzen", "HeaderFavoriteAlbums": "Lieblingsalben", "HeaderFavoriteArtists": "Lieblings-Interpreten", "HeaderFavoriteEpisodes": "Lieblingsepisoden", From d38eab50ef086a72471d47835d273354ffa9054f Mon Sep 17 00:00:00 2001 From: sharkykh Date: Sat, 7 Mar 2020 23:13:42 +0000 Subject: [PATCH 122/144] Translated using Weblate (Hebrew) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/he/ --- .../Localization/Core/he.json | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/he.json b/Emby.Server.Implementations/Localization/Core/he.json index f3a7794dae..556937baf5 100644 --- a/Emby.Server.Implementations/Localization/Core/he.json +++ b/Emby.Server.Implementations/Localization/Core/he.json @@ -2,28 +2,28 @@ "Albums": "אלבומים", "AppDeviceValues": "יישום: {0}, מכשיר: {1}", "Application": "אפליקציה", - "Artists": "אמנים", - "AuthenticationSucceededWithUserName": "{0} זוהה בהצלחה", + "Artists": "אומנים", + "AuthenticationSucceededWithUserName": "{0} אומת בהצלחה", "Books": "ספרים", - "CameraImageUploadedFrom": "תמונה חדשה הועלתה מ{0}", + "CameraImageUploadedFrom": "תמונת מצלמה חדשה הועלתה מ {0}", "Channels": "ערוצים", "ChapterNameValue": "פרק {0}", - "Collections": "קולקציות", + "Collections": "אוספים", "DeviceOfflineWithName": "{0} התנתק", "DeviceOnlineWithName": "{0} מחובר", "FailedLoginAttemptWithUserName": "ניסיון כניסה שגוי מ{0}", - "Favorites": "אהובים", + "Favorites": "מועדפים", "Folders": "תיקיות", "Genres": "ז'אנרים", "HeaderAlbumArtists": "אמני האלבום", "HeaderCameraUploads": "העלאות ממצלמה", "HeaderContinueWatching": "המשך לצפות", "HeaderFavoriteAlbums": "אלבומים שאהבתי", - "HeaderFavoriteArtists": "אמנים שאהבתי", - "HeaderFavoriteEpisodes": "פרקים אהובים", - "HeaderFavoriteShows": "תוכניות אהובות", - "HeaderFavoriteSongs": "שירים שאהבתי", - "HeaderLiveTV": "טלוויזיה בשידור חי", + "HeaderFavoriteArtists": "אמנים מועדפים", + "HeaderFavoriteEpisodes": "פרקים מועדפים", + "HeaderFavoriteShows": "סדרות מועדפות", + "HeaderFavoriteSongs": "שירים מועדפים", + "HeaderLiveTV": "שידורים חיים", "HeaderNextUp": "הבא", "HeaderRecordingGroups": "קבוצות הקלטה", "HomeVideos": "סרטונים בייתים", From b4e81f2f0cfceb50d042239771e974e70af0c304 Mon Sep 17 00:00:00 2001 From: Niels van Velzen Date: Sat, 7 Mar 2020 21:29:51 +0000 Subject: [PATCH 123/144] Translated using Weblate (Dutch) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/nl/ --- Emby.Server.Implementations/Localization/Core/nl.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/nl.json b/Emby.Server.Implementations/Localization/Core/nl.json index 4423b7f988..1d6fdde21a 100644 --- a/Emby.Server.Implementations/Localization/Core/nl.json +++ b/Emby.Server.Implementations/Localization/Core/nl.json @@ -3,7 +3,7 @@ "AppDeviceValues": "App: {0}, Apparaat: {1}", "Application": "Applicatie", "Artists": "Artiesten", - "AuthenticationSucceededWithUserName": "{0} is succesvol geverifieerd", + "AuthenticationSucceededWithUserName": "{0} succesvol geauthenticeerd", "Books": "Boeken", "CameraImageUploadedFrom": "Er is een nieuwe foto toegevoegd van {0}", "Channels": "Kanalen", From 01b51388f50433eb0d2e90087148c61c048c9d2f Mon Sep 17 00:00:00 2001 From: IDXK Date: Sat, 7 Mar 2020 21:51:11 +0000 Subject: [PATCH 124/144] Translated using Weblate (Dutch) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/nl/ --- Emby.Server.Implementations/Localization/Core/nl.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/nl.json b/Emby.Server.Implementations/Localization/Core/nl.json index 1d6fdde21a..e22f95ab46 100644 --- a/Emby.Server.Implementations/Localization/Core/nl.json +++ b/Emby.Server.Implementations/Localization/Core/nl.json @@ -9,7 +9,7 @@ "Channels": "Kanalen", "ChapterNameValue": "Hoofdstuk {0}", "Collections": "Verzamelingen", - "DeviceOfflineWithName": "{0} heeft de verbinding verbroken", + "DeviceOfflineWithName": "Verbinding met {0} is verbroken", "DeviceOnlineWithName": "{0} is verbonden", "FailedLoginAttemptWithUserName": "Mislukte aanmeld poging van {0}", "Favorites": "Favorieten", From cbd1e924866cef8aae12ee9afb4342041337f0de Mon Sep 17 00:00:00 2001 From: Tomi Date: Sat, 7 Mar 2020 21:28:08 +0000 Subject: [PATCH 125/144] Translated using Weblate (Finnish) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/fi/ --- .../Localization/Core/fi.json | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/fi.json b/Emby.Server.Implementations/Localization/Core/fi.json index d02f841fd1..a38103d25a 100644 --- a/Emby.Server.Implementations/Localization/Core/fi.json +++ b/Emby.Server.Implementations/Localization/Core/fi.json @@ -17,31 +17,31 @@ "LabelIpAddressValue": "IP-osoite: {0}", "ItemRemovedWithName": "{0} poistettiin kirjastosta", "ItemAddedWithName": "{0} lisättiin kirjastoon", - "Inherit": "Periä", + "Inherit": "Periytyä", "HomeVideos": "Kotivideot", - "HeaderRecordingGroups": "Äänitysryhmät", + "HeaderRecordingGroups": "Nauhoitusryhmät", "HeaderNextUp": "Seuraavaksi", "HeaderFavoriteSongs": "Lempikappaleet", "HeaderFavoriteShows": "Lempisarjat", "HeaderFavoriteEpisodes": "Lempijaksot", - "HeaderCameraUploads": "Kamerasta lähetetyt", + "HeaderCameraUploads": "Kameralataukset", "HeaderFavoriteArtists": "Lempiartistit", "HeaderFavoriteAlbums": "Lempialbumit", "HeaderContinueWatching": "Jatka katsomista", - "HeaderAlbumArtists": "Albumin artistit", - "Genres": "Tyylilaji", + "HeaderAlbumArtists": "Albumin esittäjä", + "Genres": "Tyylilajit", "Folders": "Kansiot", "Favorites": "Suosikit", - "FailedLoginAttemptWithUserName": "Epäonnistunut kirjautumisyritys kohteesta {0}", - "DeviceOnlineWithName": "{0} on yhdistynyt", + "FailedLoginAttemptWithUserName": "Kirjautuminen epäonnistui kohteesta {0}", + "DeviceOnlineWithName": "{0} on yhdistetty", "DeviceOfflineWithName": "{0} on katkaissut yhteytensä", "Collections": "Kokoelmat", "ChapterNameValue": "Luku: {0}", "Channels": "Kanavat", - "CameraImageUploadedFrom": "Uusi kamerakuva on lähetetty kohteesta {0}", + "CameraImageUploadedFrom": "Uusi kamerakuva on ladattu {0}", "Books": "Kirjat", - "AuthenticationSucceededWithUserName": "{0} todennettu onnistuneesti", - "Artists": "Artistit", + "AuthenticationSucceededWithUserName": "{0} todennus onnistui", + "Artists": "Esiintyjät", "Application": "Sovellus", "AppDeviceValues": "Sovellus: {0}, Laite: {1}", "Albums": "Albumit", @@ -76,21 +76,21 @@ "Shows": "Ohjelmat", "ServerNameNeedsToBeRestarted": "{0} vaatii uudelleenkäynnistyksen", "ProviderValue": "Palveluntarjoaja: {0}", - "Plugin": "Laajennus", + "Plugin": "Liitännäinen", "NotificationOptionVideoPlaybackStopped": "Videon toistaminen pysäytetty", "NotificationOptionVideoPlayback": "Videon toistaminen aloitettu", "NotificationOptionUserLockedOut": "Käyttäjä kirjautui ulos", "NotificationOptionTaskFailed": "Ajastetun tehtävän ongelma", "NotificationOptionServerRestartRequired": "Palvelimen uudelleenkäynnistys vaaditaan", - "NotificationOptionPluginUpdateInstalled": "Laajennuksen päivitys asennettu", - "NotificationOptionPluginUninstalled": "Laajennus poistettu", - "NotificationOptionPluginInstalled": "Laajennus asennettu", - "NotificationOptionPluginError": "Ongelma laajennuksessa", - "NotificationOptionNewLibraryContent": "Uusi sisältö lisätty", - "NotificationOptionInstallationFailed": "Asennusvirhe", - "NotificationOptionCameraImageUploaded": "Kameran kuva lisätty", - "NotificationOptionAudioPlaybackStopped": "Äänen toistaminen pysäytetty", - "NotificationOptionAudioPlayback": "Äänen toistaminen aloitettu", + "NotificationOptionPluginUpdateInstalled": "Liitännäinen päivitetty", + "NotificationOptionPluginUninstalled": "Liitännäinen poistettu", + "NotificationOptionPluginInstalled": "Liitännäinen asennettu", + "NotificationOptionPluginError": "Ongelma liitännäisessä", + "NotificationOptionNewLibraryContent": "Uutta sisältöä lisätty", + "NotificationOptionInstallationFailed": "Asennus epäonnistui", + "NotificationOptionCameraImageUploaded": "Kuva ladattu kamerasta", + "NotificationOptionAudioPlaybackStopped": "Audion toisto pysäytetty", + "NotificationOptionAudioPlayback": "Audion toisto aloitettu", "NotificationOptionApplicationUpdateInstalled": "Ohjelmistopäivitys asennettu", "NotificationOptionApplicationUpdateAvailable": "Ohjelmistopäivitys saatavilla" } From 26c778eb166ead97f32a736e22d99513f9784654 Mon Sep 17 00:00:00 2001 From: dkanada Date: Sun, 8 Mar 2020 12:10:25 +0900 Subject: [PATCH 126/144] implement option to disable audiodb for now --- .../Plugins/AudioDb/AlbumImageProvider.cs | 3 ++- .../Plugins/AudioDb/AlbumProvider.cs | 11 +++++++++++ .../Plugins/AudioDb/ArtistImageProvider.cs | 3 ++- .../Plugins/AudioDb/ArtistProvider.cs | 6 ++++++ .../AudioDb/Configuration/PluginConfiguration.cs | 2 ++ .../Plugins/AudioDb/Configuration/config.html | 4 ++++ 6 files changed, 27 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs index 85719fa51a..dee2d59f0f 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs @@ -102,6 +102,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb } /// - public bool Supports(BaseItem item) => item is MusicAlbum; + public bool Supports(BaseItem item) + => Plugin.Instance.Configuration.Enable && item is MusicAlbum; } } diff --git a/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs index 7f9f8c628d..1a0e878719 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs @@ -55,6 +55,12 @@ namespace MediaBrowser.Providers.Plugins.AudioDb { var result = new MetadataResult(); + // TODO maybe remove when artist metadata can be disabled + if (!Plugin.Instance.Configuration.Enable) + { + return result; + } + var id = info.GetReleaseGroupId(); if (!string.IsNullOrWhiteSpace(id)) @@ -78,6 +84,11 @@ namespace MediaBrowser.Providers.Plugins.AudioDb private void ProcessResult(MusicAlbum item, Album result, string preferredLanguage) { + if (Plugin.Instance.Configuration.ReplaceAlbumName && !string.IsNullOrWhiteSpace(result.strAlbum)) + { + item.Album = result.strAlbum; + } + if (!string.IsNullOrWhiteSpace(result.strArtist)) { item.AlbumArtists = new string[] { result.strArtist }; diff --git a/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs index 51b0eacfc8..18afd5dd5c 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs @@ -143,6 +143,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb } /// - public bool Supports(BaseItem item) => item is MusicArtist; + public bool Supports(BaseItem item) + => Plugin.Instance.Configuration.Enable && item is MusicArtist; } } diff --git a/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs index a51c107df3..df0f3df8fa 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs @@ -56,6 +56,12 @@ namespace MediaBrowser.Providers.Plugins.AudioDb { var result = new MetadataResult(); + // TODO maybe remove when artist metadata can be disabled + if (!Plugin.Instance.Configuration.Enable) + { + return result; + } + var id = info.GetMusicBrainzArtistId(); if (!string.IsNullOrWhiteSpace(id)) diff --git a/MediaBrowser.Providers/Plugins/AudioDb/Configuration/PluginConfiguration.cs b/MediaBrowser.Providers/Plugins/AudioDb/Configuration/PluginConfiguration.cs index 90790d26fc..ad3c7eb4bd 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/Configuration/PluginConfiguration.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/Configuration/PluginConfiguration.cs @@ -5,5 +5,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb public class PluginConfiguration : BasePluginConfiguration { public bool Enable { get; set; } + + public bool ReplaceAlbumName { get; set; } } } diff --git a/MediaBrowser.Providers/Plugins/AudioDb/Configuration/config.html b/MediaBrowser.Providers/Plugins/AudioDb/Configuration/config.html index 56c32e13ea..c50dba71f7 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/Configuration/config.html +++ b/MediaBrowser.Providers/Plugins/AudioDb/Configuration/config.html @@ -12,6 +12,10 @@ Enable this provider for metadata searches on artists and albums. +
From acf1698d2b9277afea001555c2bb8e8d90f275ae Mon Sep 17 00:00:00 2001 From: dkanada Date: Sun, 8 Mar 2020 12:17:49 +0900 Subject: [PATCH 127/144] include audiodb config page in release --- MediaBrowser.Providers/MediaBrowser.Providers.csproj | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 48c16b6433..dfe3eb2ef6 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -24,6 +24,11 @@ true + + + + + From f8b391538d9283879e962e0dbffed12dc6024a7d Mon Sep 17 00:00:00 2001 From: dkanada Date: Sun, 8 Mar 2020 12:19:38 +0900 Subject: [PATCH 128/144] update audiodb config page --- .../Plugins/AudioDb/Configuration/config.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.Providers/Plugins/AudioDb/Configuration/config.html b/MediaBrowser.Providers/Plugins/AudioDb/Configuration/config.html index c50dba71f7..34494644d4 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/Configuration/config.html +++ b/MediaBrowser.Providers/Plugins/AudioDb/Configuration/config.html @@ -13,7 +13,7 @@ Enable this provider for metadata searches on artists and albums.
@@ -32,6 +32,7 @@ Dashboard.showLoadingMsg(); ApiClient.getPluginConfiguration(PluginConfig.pluginId).then(function (config) { $('#enable').checked(config.Enable); + $('#replaceAlbumName').checked(config.ReplaceAlbumName); Dashboard.hideLoadingMsg(); }); @@ -43,6 +44,7 @@ var form = this; ApiClient.getPluginConfiguration(PluginConfig.pluginId).then(function (config) { config.Enable = $('#enable', form).checked(); + config.ReplaceAlbumName = $('#replaceAlbumName', form).checked(); ApiClient.updatePluginConfiguration(PluginConfig.pluginId, config).then(Dashboard.processPluginConfigurationUpdateResult); }); From 86190aa7e995c490509e65159e4c1f76cda61cc3 Mon Sep 17 00:00:00 2001 From: sharkykh Date: Sun, 8 Mar 2020 10:52:43 +0000 Subject: [PATCH 129/144] Translated using Weblate (Hebrew) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/he/ --- Emby.Server.Implementations/Localization/Core/he.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/he.json b/Emby.Server.Implementations/Localization/Core/he.json index 556937baf5..5618719dd3 100644 --- a/Emby.Server.Implementations/Localization/Core/he.json +++ b/Emby.Server.Implementations/Localization/Core/he.json @@ -40,8 +40,8 @@ "MixedContent": "תוכן מעורב", "Movies": "סרטים", "Music": "מוזיקה", - "MusicVideos": "Music videos", - "NameInstallFailed": "{0} installation failed", + "MusicVideos": "קליפים", + "NameInstallFailed": "התקנת {0} נכשלה", "NameSeasonNumber": "עונה {0}", "NameSeasonUnknown": "עונה לא ידועה", "NewVersionIsAvailable": "גרסה חדשה של שרת Jellyfin זמינה להורדה.", @@ -89,8 +89,8 @@ "UserOnlineFromDevice": "{0} is online from {1}", "UserPasswordChangedWithName": "Password has been changed for user {0}", "UserPolicyUpdatedWithName": "User policy has been updated for {0}", - "UserStartedPlayingItemWithValues": "{0} is playing {1} on {2}", - "UserStoppedPlayingItemWithValues": "{0} has finished playing {1} on {2}", + "UserStartedPlayingItemWithValues": "{0} מנגן את {1} על {2}", + "UserStoppedPlayingItemWithValues": "{0} סיים לנגן את {1} על {2}", "ValueHasBeenAddedToLibrary": "{0} has been added to your media library", "ValueSpecialEpisodeName": "מיוחד- {0}", "VersionNumber": "Version {0}" From a0fdceb4bcf672bb87bed9f89921f60e8213080b Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sun, 8 Mar 2020 15:02:42 +0100 Subject: [PATCH 130/144] Throw exception on migration failure to halt application Also save migration configuration after each migration instead of at the end in case an exception is thrown part way through the list --- Jellyfin.Server/Migrations/MigrationRunner.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Jellyfin.Server/Migrations/MigrationRunner.cs b/Jellyfin.Server/Migrations/MigrationRunner.cs index 8bc29d8ac8..0f9c2a3914 100644 --- a/Jellyfin.Server/Migrations/MigrationRunner.cs +++ b/Jellyfin.Server/Migrations/MigrationRunner.cs @@ -59,16 +59,12 @@ namespace Jellyfin.Server.Migrations catch (Exception ex) { logger.LogError(ex, "Could not apply migration {Name}", migrationRoutine.Name); - continue; + throw; } + // Mark the migration as completed logger.LogInformation("Migration {Name} applied successfully", migrationRoutine.Name); applied.Add(migrationRoutine.Name); - } - - if (applied.Count > migrationOptions.Applied.Length) - { - logger.LogInformation("Some migrations were run, saving the state"); migrationOptions.Applied = applied.ToArray(); host.ServerConfigurationManager.SaveConfiguration(MigrationsListStore.StoreKey, migrationOptions); } From 2f0b4cc24c84d98820c8a6755191d4fc1dab74e7 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sun, 8 Mar 2020 15:02:59 +0100 Subject: [PATCH 131/144] Clean up migration logging messages --- Jellyfin.Server/Migrations/MigrationRunner.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Jellyfin.Server/Migrations/MigrationRunner.cs b/Jellyfin.Server/Migrations/MigrationRunner.cs index 0f9c2a3914..821f51c02f 100644 --- a/Jellyfin.Server/Migrations/MigrationRunner.cs +++ b/Jellyfin.Server/Migrations/MigrationRunner.cs @@ -46,11 +46,11 @@ namespace Jellyfin.Server.Migrations var migrationRoutine = Migrations[i]; if (applied.Contains(migrationRoutine.Name)) { - logger.LogDebug("Skipping migration {Name} as it is already applied", migrationRoutine.Name); + logger.LogDebug("Skipping migration '{Name}' since it is already applied", migrationRoutine.Name); continue; } - logger.LogInformation("Applying migration {Name}", migrationRoutine.Name); + logger.LogInformation("Applying migration '{Name}'", migrationRoutine.Name); try { @@ -58,15 +58,16 @@ namespace Jellyfin.Server.Migrations } catch (Exception ex) { - logger.LogError(ex, "Could not apply migration {Name}", migrationRoutine.Name); + logger.LogError(ex, "Could not apply migration '{Name}'", migrationRoutine.Name); throw; } // Mark the migration as completed - logger.LogInformation("Migration {Name} applied successfully", migrationRoutine.Name); + logger.LogInformation("Migration '{Name}' applied successfully", migrationRoutine.Name); applied.Add(migrationRoutine.Name); migrationOptions.Applied = applied.ToArray(); host.ServerConfigurationManager.SaveConfiguration(MigrationsListStore.StoreKey, migrationOptions); + logger.LogDebug("Migration '{Name}' marked as applied in configuration.", migrationRoutine.Name); } } } From 8dbb1c92573ba5cf3e4f8d953ffd6083a8ccbde4 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sun, 8 Mar 2020 15:46:13 +0100 Subject: [PATCH 132/144] Use logging.json instead of logging.user.json for override settings --- .../Routines/CreateUserLoggingConfigFile.cs | 45 +++++-------------- Jellyfin.Server/Program.cs | 6 +-- 2 files changed, 14 insertions(+), 37 deletions(-) diff --git a/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs b/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs index 6dbeb27761..834099ea05 100644 --- a/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs +++ b/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs @@ -15,11 +15,6 @@ namespace Jellyfin.Server.Migrations.Routines ///
internal class CreateUserLoggingConfigFile : IMigrationRoutine { - /// - /// An empty logging JSON configuration, which will be used as the default contents for the user settings config file. - /// - private const string EmptyLoggingConfig = @"{ ""Serilog"": { } }"; - /// /// File history for logging.json as existed during this migration creation. The contents for each has been minified. /// @@ -48,46 +43,28 @@ namespace Jellyfin.Server.Migrations.Routines public void Perform(CoreAppHost host, ILogger logger) { var logDirectory = host.Resolve().ConfigurationDirectoryPath; - var oldConfigPath = Path.Combine(logDirectory, "logging.json"); - var userConfigPath = Path.Combine(logDirectory, Program.LoggingConfigFileUser); + var existingConfigPath = Path.Combine(logDirectory, "logging.json"); - // Check if there are existing settings in the old "logging.json" file that should be migrated - bool shouldMigrateOldFile = ShouldKeepOldConfig(oldConfigPath); - - // Create the user settings file "logging.user.json" - if (shouldMigrateOldFile) + // If the existing logging.json config file is unmodified, then 'reset' it by moving it to 'logging.old.json' + // NOTE: This config file has 'reloadOnChange: true', so this change will take effect immediately even though it has already been loaded + if (File.Exists(existingConfigPath) && ExistingConfigUnmodified(existingConfigPath)) { - // Use the existing logging.json file - File.Copy(oldConfigPath, userConfigPath); - } - else - { - // Write an empty JSON file - File.WriteAllText(userConfigPath, EmptyLoggingConfig); + File.Move(existingConfigPath, Path.Combine(logDirectory, "logging.old.json")); } } /// - /// Check if the existing logging.json file should be migrated to logging.user.json. + /// Check if the existing logging.json file has not been modified by the user by comparing it to all the + /// versions in our git history. Until now, the file has never been migrated after first creation so users + /// could have any version from the git history. /// - private bool ShouldKeepOldConfig(string oldConfigPath) + /// does not exist or could not be read. + private bool ExistingConfigUnmodified(string oldConfigPath) { - // Cannot keep the old logging file if it doesn't exist - if (!File.Exists(oldConfigPath)) - { - return false; - } - - // Check if the existing logging.json file has been modified by the user by comparing it to all the - // versions in our git history. Until now, the file has never been migrated after first creation so users - // could have any version from the git history. var existingConfigJson = JToken.Parse(File.ReadAllText(oldConfigPath)); - var existingConfigIsUnmodified = _defaultConfigHistory + return _defaultConfigHistory .Select(historicalConfigText => JToken.Parse(historicalConfigText)) .Any(historicalConfigJson => JToken.DeepEquals(existingConfigJson, historicalConfigJson)); - - // The existing config file should be kept and used only if it has been modified by the user - return !existingConfigIsUnmodified; } } } diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 2590fdb215..7c3d0f2771 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -44,9 +44,9 @@ namespace Jellyfin.Server public static readonly string LoggingConfigFileDefault = "logging.default.json"; /// - /// The name of the logging configuration file containing user override settings. + /// The name of the logging configuration file containing the system-specific override settings. /// - public static readonly string LoggingConfigFileUser = "logging.user.json"; + public static readonly string LoggingConfigFileSystem = "logging.json"; private static readonly CancellationTokenSource _tokenSource = new CancellationTokenSource(); private static readonly ILoggerFactory _loggerFactory = new SerilogLoggerFactory(); @@ -471,7 +471,7 @@ namespace Jellyfin.Server .SetBasePath(appPaths.ConfigurationDirectoryPath) .AddInMemoryCollection(ConfigurationOptions.Configuration) .AddJsonFile(LoggingConfigFileDefault, optional: false, reloadOnChange: true) - .AddJsonFile(LoggingConfigFileUser, optional: true, reloadOnChange: true) + .AddJsonFile(LoggingConfigFileSystem, optional: true, reloadOnChange: true) .AddEnvironmentVariables("JELLYFIN_") .Build(); } From 72bf920291d1c486aaf66544ccd6eb64c13e254b Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sun, 8 Mar 2020 16:05:31 +0100 Subject: [PATCH 133/144] Use a Guid to uniquely identify migrations instead of a string name Also use a list instead of an array to store executed migrations in the configuration class --- Jellyfin.Server/Migrations/IMigrationRoutine.cs | 7 ++++++- Jellyfin.Server/Migrations/MigrationOptions.cs | 11 ++++++----- Jellyfin.Server/Migrations/MigrationRunner.cs | 11 ++++------- .../Routines/CreateUserLoggingConfigFile.cs | 3 +++ .../Routines/DisableTranscodingThrottling.cs | 3 +++ 5 files changed, 22 insertions(+), 13 deletions(-) diff --git a/Jellyfin.Server/Migrations/IMigrationRoutine.cs b/Jellyfin.Server/Migrations/IMigrationRoutine.cs index 20a3aa3d6a..eab995d67e 100644 --- a/Jellyfin.Server/Migrations/IMigrationRoutine.cs +++ b/Jellyfin.Server/Migrations/IMigrationRoutine.cs @@ -9,7 +9,12 @@ namespace Jellyfin.Server.Migrations internal interface IMigrationRoutine { /// - /// Gets the name of the migration, must be unique. + /// Gets the unique id for this migration. This should never be modified after the migration has been created. + /// + public Guid Id { get; } + + /// + /// Gets the display name of the migration. /// public string Name { get; } diff --git a/Jellyfin.Server/Migrations/MigrationOptions.cs b/Jellyfin.Server/Migrations/MigrationOptions.cs index 6b7831158f..d95354145e 100644 --- a/Jellyfin.Server/Migrations/MigrationOptions.cs +++ b/Jellyfin.Server/Migrations/MigrationOptions.cs @@ -1,3 +1,6 @@ +using System; +using System.Collections.Generic; + namespace Jellyfin.Server.Migrations { /// @@ -10,14 +13,12 @@ namespace Jellyfin.Server.Migrations /// public MigrationOptions() { - Applied = System.Array.Empty(); + Applied = new List(); } -#pragma warning disable CA1819 // Properties should not return arrays /// - /// Gets or sets the list of applied migration routine names. + /// Gets the list of applied migration routine names. /// - public string[] Applied { get; set; } -#pragma warning restore CA1819 // Properties should not return arrays + public List Applied { get; } } } diff --git a/Jellyfin.Server/Migrations/MigrationRunner.cs b/Jellyfin.Server/Migrations/MigrationRunner.cs index 821f51c02f..2081143d20 100644 --- a/Jellyfin.Server/Migrations/MigrationRunner.cs +++ b/Jellyfin.Server/Migrations/MigrationRunner.cs @@ -29,22 +29,20 @@ namespace Jellyfin.Server.Migrations var logger = loggerFactory.CreateLogger(); var migrationOptions = ((IConfigurationManager)host.ServerConfigurationManager).GetConfiguration(MigrationsListStore.StoreKey); - if (!host.ServerConfigurationManager.Configuration.IsStartupWizardCompleted && migrationOptions.Applied.Length == 0) + if (!host.ServerConfigurationManager.Configuration.IsStartupWizardCompleted && migrationOptions.Applied.Count == 0) { // If startup wizard is not finished, this is a fresh install. // Don't run any migrations, just mark all of them as applied. logger.LogInformation("Marking all known migrations as applied because this is fresh install"); - migrationOptions.Applied = Migrations.Select(m => m.Name).ToArray(); + migrationOptions.Applied.AddRange(Migrations.Select(m => m.Id)); host.ServerConfigurationManager.SaveConfiguration(MigrationsListStore.StoreKey, migrationOptions); return; } - var applied = migrationOptions.Applied.ToList(); - for (var i = 0; i < Migrations.Length; i++) { var migrationRoutine = Migrations[i]; - if (applied.Contains(migrationRoutine.Name)) + if (migrationOptions.Applied.Contains(migrationRoutine.Id)) { logger.LogDebug("Skipping migration '{Name}' since it is already applied", migrationRoutine.Name); continue; @@ -64,8 +62,7 @@ namespace Jellyfin.Server.Migrations // Mark the migration as completed logger.LogInformation("Migration '{Name}' applied successfully", migrationRoutine.Name); - applied.Add(migrationRoutine.Name); - migrationOptions.Applied = applied.ToArray(); + migrationOptions.Applied.Add(migrationRoutine.Id); host.ServerConfigurationManager.SaveConfiguration(MigrationsListStore.StoreKey, migrationOptions); logger.LogDebug("Migration '{Name}' marked as applied in configuration.", migrationRoutine.Name); } diff --git a/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs b/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs index 834099ea05..3bc32c0478 100644 --- a/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs +++ b/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs @@ -36,6 +36,9 @@ namespace Jellyfin.Server.Migrations.Routines @"{""Serilog"":{""MinimumLevel"":""Information"",""WriteTo"":[{""Name"":""Console"",""Args"":{""outputTemplate"":""[{Timestamp:HH:mm:ss}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message:lj}{NewLine}{Exception}""}},{""Name"":""Async"",""Args"":{""configure"":[{""Name"":""File"",""Args"":{""path"":""%JELLYFIN_LOG_DIR%//log_.log"",""rollingInterval"":""Day"",""retainedFileCountLimit"":3,""rollOnFileSizeLimit"":true,""fileSizeLimitBytes"":100000000,""outputTemplate"":""[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] [{ThreadId}] {SourceContext}:{Message}{NewLine}{Exception}""}}]}}],""Enrich"":[""FromLogContext"",""WithThreadId""]}}", }; + /// + public Guid Id => Guid.Parse("{EF103419-8451-40D8-9F34-D1A8E93A1679}"); + /// public string Name => "CreateLoggingConfigHeirarchy"; diff --git a/Jellyfin.Server/Migrations/Routines/DisableTranscodingThrottling.cs b/Jellyfin.Server/Migrations/Routines/DisableTranscodingThrottling.cs index 279e7bbea5..673f0e4155 100644 --- a/Jellyfin.Server/Migrations/Routines/DisableTranscodingThrottling.cs +++ b/Jellyfin.Server/Migrations/Routines/DisableTranscodingThrottling.cs @@ -12,6 +12,9 @@ namespace Jellyfin.Server.Migrations.Routines ///
internal class DisableTranscodingThrottling : IMigrationRoutine { + /// + public Guid Id => Guid.Parse("{4124C2CD-E939-4FFB-9BE9-9B311C413638}"); + /// public string Name => "DisableTranscodingThrottling"; From 9e89cbbc3ad451b510a00fd7e214f6b942176f47 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sun, 8 Mar 2020 17:40:30 +0100 Subject: [PATCH 134/144] Store migration names alongside Ids in configuration in order to assist with development/debugging --- Jellyfin.Server/Migrations/MigrationOptions.cs | 4 ++-- Jellyfin.Server/Migrations/MigrationRunner.cs | 10 ++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Jellyfin.Server/Migrations/MigrationOptions.cs b/Jellyfin.Server/Migrations/MigrationOptions.cs index d95354145e..816dd9ee74 100644 --- a/Jellyfin.Server/Migrations/MigrationOptions.cs +++ b/Jellyfin.Server/Migrations/MigrationOptions.cs @@ -13,12 +13,12 @@ namespace Jellyfin.Server.Migrations ///
public MigrationOptions() { - Applied = new List(); + Applied = new List<(Guid Id, string Name)>(); } /// /// Gets the list of applied migration routine names. /// - public List Applied { get; } + public List<(Guid Id, string Name)> Applied { get; } } } diff --git a/Jellyfin.Server/Migrations/MigrationRunner.cs b/Jellyfin.Server/Migrations/MigrationRunner.cs index 2081143d20..b5ea04dcac 100644 --- a/Jellyfin.Server/Migrations/MigrationRunner.cs +++ b/Jellyfin.Server/Migrations/MigrationRunner.cs @@ -33,16 +33,18 @@ namespace Jellyfin.Server.Migrations { // If startup wizard is not finished, this is a fresh install. // Don't run any migrations, just mark all of them as applied. - logger.LogInformation("Marking all known migrations as applied because this is fresh install"); - migrationOptions.Applied.AddRange(Migrations.Select(m => m.Id)); + logger.LogInformation("Marking all known migrations as applied because this is a fresh install"); + migrationOptions.Applied.AddRange(Migrations.Select(m => (m.Id, m.Name))); host.ServerConfigurationManager.SaveConfiguration(MigrationsListStore.StoreKey, migrationOptions); return; } + var appliedMigrationIds = migrationOptions.Applied.Select(m => m.Id).ToHashSet(); + for (var i = 0; i < Migrations.Length; i++) { var migrationRoutine = Migrations[i]; - if (migrationOptions.Applied.Contains(migrationRoutine.Id)) + if (appliedMigrationIds.Contains(migrationRoutine.Id)) { logger.LogDebug("Skipping migration '{Name}' since it is already applied", migrationRoutine.Name); continue; @@ -62,7 +64,7 @@ namespace Jellyfin.Server.Migrations // Mark the migration as completed logger.LogInformation("Migration '{Name}' applied successfully", migrationRoutine.Name); - migrationOptions.Applied.Add(migrationRoutine.Id); + migrationOptions.Applied.Add((migrationRoutine.Id, migrationRoutine.Name)); host.ServerConfigurationManager.SaveConfiguration(MigrationsListStore.StoreKey, migrationOptions); logger.LogDebug("Migration '{Name}' marked as applied in configuration.", migrationRoutine.Name); } From 3741348fb9330a36121ec00b319cb185e2d50baf Mon Sep 17 00:00:00 2001 From: Mednis Date: Sun, 8 Mar 2020 21:24:14 +0000 Subject: [PATCH 135/144] Added translation using Weblate (Latvian) --- Emby.Server.Implementations/Localization/Core/lv.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 Emby.Server.Implementations/Localization/Core/lv.json diff --git a/Emby.Server.Implementations/Localization/Core/lv.json b/Emby.Server.Implementations/Localization/Core/lv.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/Emby.Server.Implementations/Localization/Core/lv.json @@ -0,0 +1 @@ +{} From 3e3c47407b71ffe6281777b50ffe3451daf4bbe2 Mon Sep 17 00:00:00 2001 From: Mednis Date: Sun, 8 Mar 2020 21:25:39 +0000 Subject: [PATCH 136/144] Translated using Weblate (Latvian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/lv/ --- .../Localization/Core/lv.json | 91 ++++++++++++++++++- 1 file changed, 90 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/lv.json b/Emby.Server.Implementations/Localization/Core/lv.json index 0967ef424b..6c48cc0059 100644 --- a/Emby.Server.Implementations/Localization/Core/lv.json +++ b/Emby.Server.Implementations/Localization/Core/lv.json @@ -1 +1,90 @@ -{} +{ + "ServerNameNeedsToBeRestarted": "{0} ir vajadzīgs restarts", + "NotificationOptionTaskFailed": "Plānota uzdevuma kļūme", + "HeaderRecordingGroups": "Ierakstu Grupas", + "UserPolicyUpdatedWithName": "Lietotāju politika atjaunota priekš {0}", + "SubtitleDownloadFailureFromForItem": "Subtitru lejupielāde no {0} priekš {1} neizdevās", + "NotificationOptionVideoPlaybackStopped": "Video atskaņošana apturēta", + "NotificationOptionVideoPlayback": "Video atskaņošana sākta", + "NotificationOptionInstallationFailed": "Instalācija neizdevās", + "AuthenticationSucceededWithUserName": "{0} veiksmīgi autentificējies", + "ValueSpecialEpisodeName": "Speciālais - {0}", + "ScheduledTaskStartedWithName": "{0} iesākts", + "ScheduledTaskFailedWithName": "{0} neizdevās", + "Photos": "Attēli", + "NotificationOptionUserLockedOut": "Lietotājs bloķēts", + "LabelRunningTimeValue": "Garums: {0}", + "Inherit": "Mantot", + "AppDeviceValues": "Lietotne:{0}, Ierīce:{1}", + "VersionNumber": "Versija {0}", + "ValueHasBeenAddedToLibrary": "{0} ir ticis pievienots tavai multvides bibliotēkai", + "UserStoppedPlayingItemWithValues": "{0} ir beidzis atskaņot {1} uz {2}", + "UserStartedPlayingItemWithValues": "{0} atskaņo {1} uz {2}", + "UserPasswordChangedWithName": "Parole nomainīta lietotājam {0}", + "UserOnlineFromDevice": "{0} ir tiešsaistē no {1}", + "UserOfflineFromDevice": "{0} ir atvienojies no {1}", + "UserLockedOutWithName": "Lietotājs {0} ir ticis bloķēts", + "UserDownloadingItemWithValues": "{0} lejupielādē {1}", + "UserDeletedWithName": "Lietotājs {0} ir izdzēsts", + "UserCreatedWithName": "Lietotājs {0} ir ticis izveidots", + "User": "Lietotājs", + "TvShows": "TV Raidījumi", + "Sync": "Sinhronizācija", + "System": "Sistēma", + "SubtitlesDownloadedForItem": "Subtitri lejupielādēti priekš {0}", + "StartupEmbyServerIsLoading": "Jellyfin Serveris lādējas. Lūdzu mēģiniet vēlreiz pēc brīža.", + "Songs": "Dziesmas", + "Shows": "Raidījumi", + "PluginUpdatedWithName": "{0} tika atjaunots", + "PluginUninstalledWithName": "{0} tika noņemts", + "PluginInstalledWithName": "{0} tika uzstādīts", + "Plugin": "Paplašinājums", + "Playlists": "Atskaņošanas Saraksti", + "MixedContent": "Jaukts saturs", + "HomeVideos": "Mājas Video", + "HeaderNextUp": "Nākamais", + "ChapterNameValue": "Nodaļa {0}", + "Application": "Lietotne", + "NotificationOptionServerRestartRequired": "Vajadzīgs servera restarts", + "NotificationOptionPluginUpdateInstalled": "Paplašinājuma atjauninājums uzstādīts", + "NotificationOptionPluginUninstalled": "Paplašinājums noņemts", + "NotificationOptionPluginInstalled": "Paplašinājums uzstādīts", + "NotificationOptionPluginError": "Paplašinājuma kļūda", + "NotificationOptionNewLibraryContent": "Jauns saturs pievienots", + "NotificationOptionCameraImageUploaded": "Kameras attēls augšupielādēts", + "NotificationOptionAudioPlaybackStopped": "Audio atskaņošana apturēta", + "NotificationOptionAudioPlayback": "Audio atskaņošana sākta", + "NotificationOptionApplicationUpdateInstalled": "Lietotnes atjauninājums uzstādīts", + "NotificationOptionApplicationUpdateAvailable": "Lietotnes atjauninājums pieejams", + "NewVersionIsAvailable": "Lejupielādei ir pieejama jauna Jellyfin Server versija.", + "NameSeasonUnknown": "Nezināma Sezona", + "NameSeasonNumber": "Sezona {0}", + "NameInstallFailed": "{0} instalācija neizdevās", + "MusicVideos": "Mūzikas video", + "Music": "Mūzika", + "Movies": "Filmas", + "MessageServerConfigurationUpdated": "Servera konfigurācija ir tikusi atjaunota", + "MessageNamedServerConfigurationUpdatedWithValue": "Servera konfigurācijas sadaļa {0} ir tikusi atjaunota", + "MessageApplicationUpdatedTo": "Jellyfin Server ir ticis atjaunots uz {0}", + "MessageApplicationUpdated": "Jellyfin Server ir ticis atjaunots", + "Latest": "Jaunākais", + "LabelIpAddressValue": "IP adrese: {0}", + "ItemRemovedWithName": "{0} tika noņemts no bibliotēkas", + "ItemAddedWithName": "{0} tika pievienots bibliotēkai", + "HeaderLiveTV": "Tiešraides TV", + "HeaderContinueWatching": "Turpini skatīties", + "HeaderCameraUploads": "Kameras augšupielādes", + "HeaderAlbumArtists": "Albumu izpildītāji", + "Genres": "Žanri", + "Folders": "Mapes", + "Favorites": "Favorīti", + "FailedLoginAttemptWithUserName": "Neizdevies pieslēgšanās mēģinājums no {0}", + "DeviceOnlineWithName": "{0} ir pievienojies", + "DeviceOfflineWithName": "{0} ir atvienojies", + "Collections": "Kolekcijas", + "Channels": "Kanāli", + "CameraImageUploadedFrom": "Jauns kameras attēls ir ticis augšupielādēts no {0}", + "Books": "Grāmatas", + "Artists": "Izpildītāji", + "Albums": "Albumi" +} From c257d6071c3a8dd141d1191062e892d912177d9a Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sun, 8 Mar 2020 20:53:11 -0400 Subject: [PATCH 137/144] Fix curl for Jellyfin GPG key This curl command seems to fail inexplicably with an "unable to get local issuer" error. Use `-k` instead so it doesn't complain. --- Dockerfile.arm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile.arm b/Dockerfile.arm index 4c7aa6aa70..07780e27bf 100644 --- a/Dockerfile.arm +++ b/Dockerfile.arm @@ -38,7 +38,7 @@ ENV NVIDIA_DRIVER_CAPABILITIES="compute,video,utility" COPY --from=qemu /usr/bin/qemu-arm-static /usr/bin RUN apt-get update \ && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates gnupg curl && \ - curl -s https://repo.jellyfin.org/debian/jellyfin_team.gpg.key | apt-key add - && \ + curl -ks https://repo.jellyfin.org/debian/jellyfin_team.gpg.key | apt-key add - && \ curl -s https://keyserver.ubuntu.com/pks/lookup?op=get\&search=0x6587ffd6536b8826e88a62547876ae518cbcf2f2 | apt-key add - && \ echo 'deb [arch=armhf] https://repo.jellyfin.org/debian buster main' > /etc/apt/sources.list.d/jellyfin.list && \ echo "deb http://ppa.launchpad.net/ubuntu-raspi2/ppa/ubuntu bionic main">> /etc/apt/sources.list.d/raspbins.list && \ From ad93f3b1468ce4f8a536e2f7b15b769043f32efe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Fonseca?= Date: Mon, 9 Mar 2020 10:01:18 +0000 Subject: [PATCH 138/144] Translated using Weblate (Portuguese) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/pt/ --- .../Localization/Core/pt.json | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/pt.json b/Emby.Server.Implementations/Localization/Core/pt.json index ef8d988c87..9ee3c37a8a 100644 --- a/Emby.Server.Implementations/Localization/Core/pt.json +++ b/Emby.Server.Implementations/Localization/Core/pt.json @@ -1,5 +1,5 @@ { - "HeaderLiveTV": "TV ao Vivo", + "HeaderLiveTV": "TV em Directo", "Collections": "Colecções", "Books": "Livros", "Artists": "Artistas", @@ -10,13 +10,13 @@ "HeaderFavoriteAlbums": "Álbuns Favoritos", "HeaderFavoriteEpisodes": "Episódios Favoritos", "HeaderFavoriteShows": "Séries Favoritas", - "HeaderContinueWatching": "Continuar a Ver", + "HeaderContinueWatching": "Continuar a Assistir", "HeaderAlbumArtists": "Artistas do Álbum", "Genres": "Géneros", - "Folders": "Pastas", + "Folders": "Directórios", "Favorites": "Favoritos", "Channels": "Canais", - "UserDownloadingItemWithValues": "{0} está a transferir {1}", + "UserDownloadingItemWithValues": "{0} está a ser transferido {1}", "VersionNumber": "Versão {0}", "ValueHasBeenAddedToLibrary": "{0} foi adicionado à sua biblioteca multimédia", "UserStoppedPlayingItemWithValues": "{0} terminou a reprodução de {1} em {2}", @@ -24,12 +24,12 @@ "UserPolicyUpdatedWithName": "A política do utilizador {0} foi alterada", "UserPasswordChangedWithName": "A palavra-passe do utilizador {0} foi alterada", "UserOnlineFromDevice": "{0} ligou-se a partir de {1}", - "UserOfflineFromDevice": "{0} desligou-se a partir de {1}", - "UserLockedOutWithName": "Utilizador {0} bloqueado", - "UserDeletedWithName": "Utilizador {0} removido", - "UserCreatedWithName": "Utilizador {0} criado", + "UserOfflineFromDevice": "{0} desconectou-se a partir de {1}", + "UserLockedOutWithName": "O utilizador {0} foi bloqueado", + "UserDeletedWithName": "O utilizador {0} foi removido", + "UserCreatedWithName": "O utilizador {0} foi criado", "User": "Utilizador", - "TvShows": "Programas", + "TvShows": "Séries", "System": "Sistema", "SubtitlesDownloadedForItem": "Legendas transferidas para {0}", "SubtitleDownloadFailureFromForItem": "Falha na transferência de legendas de {0} para {1}", @@ -38,22 +38,22 @@ "ScheduledTaskStartedWithName": "{0} iniciou", "ScheduledTaskFailedWithName": "{0} falhou", "ProviderValue": "Fornecedor: {0}", - "PluginUpdatedWithName": "{0} foi actualizado", + "PluginUpdatedWithName": "{0} foi atualizado", "PluginUninstalledWithName": "{0} foi desinstalado", "PluginInstalledWithName": "{0} foi instalado", - "Plugin": "Extensão", + "Plugin": "Plugin", "NotificationOptionVideoPlaybackStopped": "Reprodução de vídeo parada", "NotificationOptionVideoPlayback": "Reprodução de vídeo iniciada", "NotificationOptionUserLockedOut": "Utilizador bloqueado", "NotificationOptionTaskFailed": "Falha em tarefa agendada", "NotificationOptionServerRestartRequired": "É necessário reiniciar o servidor", - "NotificationOptionPluginUpdateInstalled": "Extensão actualizada", - "NotificationOptionPluginUninstalled": "Extensão desinstalada", - "NotificationOptionPluginInstalled": "Extensão instalada", - "NotificationOptionPluginError": "Falha na extensão", + "NotificationOptionPluginUpdateInstalled": "Plugin actualizado", + "NotificationOptionPluginUninstalled": "Plugin desinstalado", + "NotificationOptionPluginInstalled": "Plugin instalado", + "NotificationOptionPluginError": "Falha no plugin", "NotificationOptionNewLibraryContent": "Novo conteúdo adicionado", "NotificationOptionInstallationFailed": "Falha de instalação", - "NotificationOptionCameraImageUploaded": "Imagem da câmara enviada", + "NotificationOptionCameraImageUploaded": "Imagem de câmara enviada", "NotificationOptionAudioPlaybackStopped": "Reprodução Parada", "NotificationOptionAudioPlayback": "Reprodução Iniciada", "NotificationOptionApplicationUpdateInstalled": "A actualização da aplicação foi instalada", @@ -66,30 +66,30 @@ "Music": "Música", "MixedContent": "Conteúdo Misto", "MessageServerConfigurationUpdated": "A configuração do servidor foi actualizada", - "MessageNamedServerConfigurationUpdatedWithValue": "Configurações do servidor na secção {0} foram atualizadas", - "MessageApplicationUpdatedTo": "O servidor Jellyfin foi actualizado para a versão {0}", + "MessageNamedServerConfigurationUpdatedWithValue": "As configurações do servidor na secção {0} foram atualizadas", + "MessageApplicationUpdatedTo": "O servidor Jellyfin foi atualizado para a versão {0}", "MessageApplicationUpdated": "O servidor Jellyfin foi actualizado", "Latest": "Mais Recente", "LabelRunningTimeValue": "Duração: {0}", - "LabelIpAddressValue": "Endereço IP: {0}", + "LabelIpAddressValue": "Endereço de IP: {0}", "ItemRemovedWithName": "{0} foi removido da biblioteca", "ItemAddedWithName": "{0} foi adicionado à biblioteca", "Inherit": "Herdar", "HomeVideos": "Vídeos Caseiros", "HeaderRecordingGroups": "Grupos de Gravação", - "ValueSpecialEpisodeName": "Especial - {0}", + "ValueSpecialEpisodeName": "Episódio Especial - {0}", "Sync": "Sincronização", "Songs": "Músicas", "Shows": "Séries", "Playlists": "Listas de Reprodução", "Photos": "Fotografias", "Movies": "Filmes", - "HeaderCameraUploads": "Envios a partir da câmara", - "FailedLoginAttemptWithUserName": "Tentativa de ligação a partir de {0} falhou", - "DeviceOnlineWithName": "{0} ligou-se", - "DeviceOfflineWithName": "{0} desligou-se", + "HeaderCameraUploads": "Carregamentos a partir da câmara", + "FailedLoginAttemptWithUserName": "Tentativa de ligação falhada a partir de {0}", + "DeviceOnlineWithName": "{0} está connectado", + "DeviceOfflineWithName": "{0} desconectou-se", "ChapterNameValue": "Capítulo {0}", - "CameraImageUploadedFrom": "Uma nova imagem de câmara foi enviada a partir de {0}", + "CameraImageUploadedFrom": "Uma nova imagem da câmara foi enviada a partir de {0}", "AuthenticationSucceededWithUserName": "{0} autenticado com sucesso", "Application": "Aplicação", "AppDeviceValues": "Aplicação {0}, Dispositivo: {1}" From 6ec1be219d0f35feffb267e41937ce83c0ebc4c1 Mon Sep 17 00:00:00 2001 From: Nutjob Date: Mon, 9 Mar 2020 14:49:22 +0000 Subject: [PATCH 139/144] Translated using Weblate (Italian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/it/ --- Emby.Server.Implementations/Localization/Core/it.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/it.json b/Emby.Server.Implementations/Localization/Core/it.json index c1c40c18e1..395924af4b 100644 --- a/Emby.Server.Implementations/Localization/Core/it.json +++ b/Emby.Server.Implementations/Localization/Core/it.json @@ -5,7 +5,7 @@ "Artists": "Artisti", "AuthenticationSucceededWithUserName": "{0} autenticato con successo", "Books": "Libri", - "CameraImageUploadedFrom": "È stata caricata una nuova immagine della fotocamera {0}", + "CameraImageUploadedFrom": "È stata caricata una nuova immagine della fotocamera da {0}", "Channels": "Canali", "ChapterNameValue": "Capitolo {0}", "Collections": "Collezioni", @@ -18,7 +18,7 @@ "HeaderAlbumArtists": "Artisti dell' Album", "HeaderCameraUploads": "Caricamenti Fotocamera", "HeaderContinueWatching": "Continua a guardare", - "HeaderFavoriteAlbums": "Album preferiti", + "HeaderFavoriteAlbums": "Album Preferiti", "HeaderFavoriteArtists": "Artisti Preferiti", "HeaderFavoriteEpisodes": "Episodi Preferiti", "HeaderFavoriteShows": "Serie TV Preferite", From cc66a40ae349c14921a37c9aa231342bbd57a866 Mon Sep 17 00:00:00 2001 From: Falke Carlsen Date: Mon, 9 Mar 2020 18:02:09 +0000 Subject: [PATCH 140/144] Translated using Weblate (Danish) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/da/ --- Emby.Server.Implementations/Localization/Core/da.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/da.json b/Emby.Server.Implementations/Localization/Core/da.json index cc8b7dbd53..c421db87d4 100644 --- a/Emby.Server.Implementations/Localization/Core/da.json +++ b/Emby.Server.Implementations/Localization/Core/da.json @@ -3,7 +3,7 @@ "AppDeviceValues": "App: {0}, Enhed: {1}", "Application": "Applikation", "Artists": "Kunstnere", - "AuthenticationSucceededWithUserName": "{0} bekræftet med succes", + "AuthenticationSucceededWithUserName": "{0} succesfuldt autentificeret", "Books": "Bøger", "CameraImageUploadedFrom": "Et nyt kamerabillede er blevet uploadet fra {0}", "Channels": "Kanaler", From f6cfdcf7d74741e6dd8719109355285cf14b80dc Mon Sep 17 00:00:00 2001 From: Mednis Date: Mon, 9 Mar 2020 21:08:01 +0000 Subject: [PATCH 141/144] Translated using Weblate (Latvian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/lv/ --- Emby.Server.Implementations/Localization/Core/lv.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/lv.json b/Emby.Server.Implementations/Localization/Core/lv.json index 6c48cc0059..207d5265dd 100644 --- a/Emby.Server.Implementations/Localization/Core/lv.json +++ b/Emby.Server.Implementations/Localization/Core/lv.json @@ -86,5 +86,11 @@ "CameraImageUploadedFrom": "Jauns kameras attēls ir ticis augšupielādēts no {0}", "Books": "Grāmatas", "Artists": "Izpildītāji", - "Albums": "Albumi" + "Albums": "Albumi", + "ProviderValue": "Provider: {0}", + "HeaderFavoriteSongs": "Dziesmu Favorīti", + "HeaderFavoriteShows": "Raidījumu Favorīti", + "HeaderFavoriteEpisodes": "Episožu Favorīti", + "HeaderFavoriteArtists": "Izpildītāju Favorīti", + "HeaderFavoriteAlbums": "Albumu Favorīti" } From 12f2baa3d812d43d055237ef72496cb77ef652ad Mon Sep 17 00:00:00 2001 From: Mednis Date: Mon, 9 Mar 2020 21:55:06 +0000 Subject: [PATCH 142/144] Translated using Weblate (Latvian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/lv/ --- Emby.Server.Implementations/Localization/Core/lv.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/lv.json b/Emby.Server.Implementations/Localization/Core/lv.json index 207d5265dd..8b8d46b2e0 100644 --- a/Emby.Server.Implementations/Localization/Core/lv.json +++ b/Emby.Server.Implementations/Localization/Core/lv.json @@ -72,9 +72,9 @@ "ItemRemovedWithName": "{0} tika noņemts no bibliotēkas", "ItemAddedWithName": "{0} tika pievienots bibliotēkai", "HeaderLiveTV": "Tiešraides TV", - "HeaderContinueWatching": "Turpini skatīties", + "HeaderContinueWatching": "Turpināt Skatīšanos", "HeaderCameraUploads": "Kameras augšupielādes", - "HeaderAlbumArtists": "Albumu izpildītāji", + "HeaderAlbumArtists": "Albumu Izpildītāji", "Genres": "Žanri", "Folders": "Mapes", "Favorites": "Favorīti", From 8999a5f6a2909e139a2b90c9d7b28714e995b69b Mon Sep 17 00:00:00 2001 From: zixaar Date: Tue, 10 Mar 2020 16:19:05 +0000 Subject: [PATCH 143/144] Translated using Weblate (Arabic) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ar/ --- .../Localization/Core/ar.json | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/ar.json b/Emby.Server.Implementations/Localization/Core/ar.json index f0f165b22a..bdc17a47b6 100644 --- a/Emby.Server.Implementations/Localization/Core/ar.json +++ b/Emby.Server.Implementations/Localization/Core/ar.json @@ -2,22 +2,22 @@ "Albums": "ألبومات", "AppDeviceValues": "تطبيق: {0}, جهاز: {1}", "Application": "التطبيق", - "Artists": "الفنان", + "Artists": "الفنانين", "AuthenticationSucceededWithUserName": "{0} سجل الدخول بنجاح", "Books": "كتب", "CameraImageUploadedFrom": "صورة كاميرا جديدة تم رفعها من {0}", "Channels": "القنوات", - "ChapterNameValue": "الباب {0}", + "ChapterNameValue": "فصل {0}", "Collections": "مجموعات", - "DeviceOfflineWithName": "تم قطع اتصال {0}", + "DeviceOfflineWithName": "قُطِع الاتصال بـ{0}", "DeviceOnlineWithName": "{0} متصل", "FailedLoginAttemptWithUserName": "عملية تسجيل الدخول فشلت من {0}", - "Favorites": "التفضيلات", + "Favorites": "المفضلة", "Folders": "المجلدات", - "Genres": "أنواع الأفلام", + "Genres": "الأنواع", "HeaderAlbumArtists": "فناني الألبومات", "HeaderCameraUploads": "تحميلات الكاميرا", - "HeaderContinueWatching": "استئناف المشاهدة", + "HeaderContinueWatching": "استئناف", "HeaderFavoriteAlbums": "الألبومات المفضلة", "HeaderFavoriteArtists": "الفنانون المفضلون", "HeaderFavoriteEpisodes": "الحلقات المفضلة", @@ -31,20 +31,20 @@ "ItemAddedWithName": "تم إضافة {0} للمكتبة", "ItemRemovedWithName": "تم إزالة {0} من المكتبة", "LabelIpAddressValue": "عنوان الآي بي: {0}", - "LabelRunningTimeValue": "وقت التشغيل: {0}", + "LabelRunningTimeValue": "المدة: {0}", "Latest": "الأحدث", - "MessageApplicationUpdated": "لقد تم تحديث خادم أمبي", + "MessageApplicationUpdated": "لقد تم تحديث خادم Jellyfin", "MessageApplicationUpdatedTo": "تم تحديث سيرفر Jellyfin الى {0}", "MessageNamedServerConfigurationUpdatedWithValue": "تم تحديث إعدادات الخادم في قسم {0}", "MessageServerConfigurationUpdated": "تم تحديث إعدادات الخادم", - "MixedContent": "محتوى مخلوط", + "MixedContent": "محتوى مختلط", "Movies": "الأفلام", "Music": "الموسيقى", "MusicVideos": "الفيديوهات الموسيقية", "NameInstallFailed": "فشل التثبيت {0}", "NameSeasonNumber": "الموسم {0}", "NameSeasonUnknown": "الموسم غير معروف", - "NewVersionIsAvailable": "نسخة حديثة من سيرفر Jellyfin متوفرة للتحميل .", + "NewVersionIsAvailable": "نسخة جديدة من سيرفر Jellyfin متوفرة للتحميل.", "NotificationOptionApplicationUpdateAvailable": "يوجد تحديث للتطبيق", "NotificationOptionApplicationUpdateInstalled": "تم تحديث التطبيق", "NotificationOptionAudioPlayback": "بدأ تشغيل المقطع الصوتي", From 97bca5a9008ba94411c45572e690decc58805b85 Mon Sep 17 00:00:00 2001 From: zixaar Date: Tue, 10 Mar 2020 16:35:12 +0000 Subject: [PATCH 144/144] Translated using Weblate (Arabic) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ar/ --- Emby.Server.Implementations/Localization/Core/ar.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/ar.json b/Emby.Server.Implementations/Localization/Core/ar.json index bdc17a47b6..24da1aa567 100644 --- a/Emby.Server.Implementations/Localization/Core/ar.json +++ b/Emby.Server.Implementations/Localization/Core/ar.json @@ -49,10 +49,10 @@ "NotificationOptionApplicationUpdateInstalled": "تم تحديث التطبيق", "NotificationOptionAudioPlayback": "بدأ تشغيل المقطع الصوتي", "NotificationOptionAudioPlaybackStopped": "تم إيقاف تشغيل المقطع الصوتي", - "NotificationOptionCameraImageUploaded": "تم رقع صورة الكاميرا", + "NotificationOptionCameraImageUploaded": "تم رفع صورة الكاميرا", "NotificationOptionInstallationFailed": "فشل في التثبيت", - "NotificationOptionNewLibraryContent": "تم إضافة محتوى جديد", - "NotificationOptionPluginError": "فشل في الملحق", + "NotificationOptionNewLibraryContent": "أُضِيفَ محتوى جديد", + "NotificationOptionPluginError": "فشل في الـPlugin", "NotificationOptionPluginInstalled": "تم تثبيت الملحق", "NotificationOptionPluginUninstalled": "تمت إزالة الملحق", "NotificationOptionPluginUpdateInstalled": "تم تثبيت تحديثات الملحق",