From b2ce70987c2f724436514e8f91f9f455be7d13ff Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 30 Nov 2022 22:07:34 +0100 Subject: [PATCH 01/35] Change log level for slow HTTP responses from WRN TO DBG The added log level check is there because Request.GetDisplayUrl() is a pretty expensive call, creating a StringBuilder and string which doesn't need to happen on most installs where debug logging is disabled --- Jellyfin.Server/Middleware/ResponseTimeMiddleware.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Jellyfin.Server/Middleware/ResponseTimeMiddleware.cs b/Jellyfin.Server/Middleware/ResponseTimeMiddleware.cs index 1c25696cd1..65b64da4f1 100644 --- a/Jellyfin.Server/Middleware/ResponseTimeMiddleware.cs +++ b/Jellyfin.Server/Middleware/ResponseTimeMiddleware.cs @@ -47,9 +47,10 @@ namespace Jellyfin.Server.Middleware context.Response.OnStarting(() => { watch.Stop(); - if (enableWarning && watch.ElapsedMilliseconds > warningThreshold) + var responseTimeForCompleteRequest = watch.ElapsedMilliseconds; + if (enableWarning && responseTimeForCompleteRequest > warningThreshold && _logger.IsEnabled(LogLevel.Debug)) { - _logger.LogWarning( + _logger.LogDebug( "Slow HTTP Response from {Url} to {RemoteIp} in {Elapsed:g} with Status Code {StatusCode}", context.Request.GetDisplayUrl(), context.GetNormalizedRemoteIp(), @@ -57,7 +58,6 @@ namespace Jellyfin.Server.Middleware context.Response.StatusCode); } - var responseTimeForCompleteRequest = watch.ElapsedMilliseconds; context.Response.Headers[ResponseHeaderResponseTime] = responseTimeForCompleteRequest.ToString(CultureInfo.InvariantCulture); return Task.CompletedTask; }); From fd73f346dc94a2b1a2c3421e9d83c0f6d9346d29 Mon Sep 17 00:00:00 2001 From: Niels van Velzen Date: Sat, 12 Nov 2022 10:19:52 +0100 Subject: [PATCH 02/35] Add userId parameter to AuthorizeQuickConnect --- Jellyfin.Api/Controllers/QuickConnectController.cs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/Jellyfin.Api/Controllers/QuickConnectController.cs b/Jellyfin.Api/Controllers/QuickConnectController.cs index 77d88475ff..aed4d93415 100644 --- a/Jellyfin.Api/Controllers/QuickConnectController.cs +++ b/Jellyfin.Api/Controllers/QuickConnectController.cs @@ -1,3 +1,4 @@ +using System; using System.ComponentModel.DataAnnotations; using System.Threading.Tasks; using Jellyfin.Api.Constants; @@ -96,6 +97,7 @@ namespace Jellyfin.Api.Controllers /// Authorizes a pending quick connect request. /// /// Quick connect code to authorize. + /// The user the authorize. Access to the requested user is required. /// Quick connect result authorized successfully. /// Unknown user id. /// Boolean indicating if the authorization was successful. @@ -103,17 +105,19 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status403Forbidden)] - public async Task> AuthorizeQuickConnect([FromQuery, Required] string code) + public async Task> AuthorizeQuickConnect([FromQuery, Required] string code, [FromQuery] Guid? userId = null) { - var userId = User.GetUserId(); - if (userId.Equals(default)) + var currentUserId = User.GetUserId(); + var actualUserId = userId ?? currentUserId; + + if (actualUserId.Equals(default) || (!userId.Equals(currentUserId) && !User.IsInRole(UserRoles.Administrator))) { - return StatusCode(StatusCodes.Status403Forbidden, "Unknown user id"); + return Forbid("Unknown user id"); } try { - return await _quickConnect.AuthorizeRequest(userId, code).ConfigureAwait(false); + return await _quickConnect.AuthorizeRequest(actualUserId, code).ConfigureAwait(false); } catch (AuthenticationException) { From 722ad3fe97e6fb1ef2bc99603c8fd84efe36ca79 Mon Sep 17 00:00:00 2001 From: Niels van Velzen Date: Sat, 12 Nov 2022 10:20:40 +0100 Subject: [PATCH 03/35] Change InitiateQuickConnect to use POST request Keep the GET request for compatibility --- Jellyfin.Api/Controllers/QuickConnectController.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Jellyfin.Api/Controllers/QuickConnectController.cs b/Jellyfin.Api/Controllers/QuickConnectController.cs index aed4d93415..6dbcdae228 100644 --- a/Jellyfin.Api/Controllers/QuickConnectController.cs +++ b/Jellyfin.Api/Controllers/QuickConnectController.cs @@ -52,7 +52,7 @@ namespace Jellyfin.Api.Controllers /// Quick connect request successfully created. /// Quick connect is not active on this server. /// A with a secret and code for future use or an error message. - [HttpGet("Initiate")] + [HttpPost("Initiate")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> InitiateQuickConnect() { @@ -67,6 +67,16 @@ namespace Jellyfin.Api.Controllers } } + /// + /// Old version of using a GET method. + /// Still available to avoid breaking compatibility. + /// + /// The result of . + [Obsolete("Use POST request instead")] + [HttpGet("Initiate")] + [ApiExplorerSettings(IgnoreApi = true)] + public Task> InitiateQuickConnectLegacy() => InitiateQuickConnect(); + /// /// Attempts to retrieve authentication information. /// From ab145c5ddcf9cb62bacafe690b4e8341c777dfb4 Mon Sep 17 00:00:00 2001 From: Brad Beattie Date: Mon, 5 Dec 2022 17:38:51 -0800 Subject: [PATCH 04/35] Search tags as well --- Emby.Server.Implementations/Data/SqliteItemRepository.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 4f0a15df18..481939f4a2 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -2462,6 +2462,7 @@ namespace Emby.Server.Implementations.Data if (query.SearchTerm.Length > 1) { builder.Append("+ ((CleanName like @SearchTermContains or (OriginalTitle not null and OriginalTitle like @SearchTermContains)) * 10)"); + builder.Append("+ ((Tags not null and Tags like @SearchTermContains) * 5)"); } builder.Append(") as SearchScore"); From ec6b7efe23a98be73bb33774e361f9772fac4cc9 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 7 Dec 2022 16:04:58 +0100 Subject: [PATCH 05/35] Always log when starting ffprobe Now when we fail we can always see the filename in the logs Don't log 2x in debug mode --- .../Encoder/MediaEncoder.cs | 20 ++++--------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index e50aa679a3..f330ad32fb 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -415,8 +415,6 @@ namespace MediaBrowser.MediaEncoding.Encoder analyzeDuration = "-analyzeduration " + ffmpegAnalyzeDuration; } - var forceEnableLogging = request.MediaSource.Protocol != MediaProtocol.File; - return GetMediaInfoInternal( GetInputArgument(inputFile, request.MediaSource), request.MediaSource.Path, @@ -425,7 +423,6 @@ namespace MediaBrowser.MediaEncoding.Encoder analyzeDuration, request.MediaType == DlnaProfileType.Audio, request.MediaSource.VideoType, - forceEnableLogging, cancellationToken); } @@ -473,7 +470,6 @@ namespace MediaBrowser.MediaEncoding.Encoder string probeSizeArgument, bool isAudio, VideoType? videoType, - bool forceEnableLogging, CancellationToken cancellationToken) { var args = extractChapters @@ -488,7 +484,7 @@ namespace MediaBrowser.MediaEncoding.Encoder CreateNoWindow = true, UseShellExecute = false, - // Must consume both or ffmpeg may hang due to deadlocks. See comments below. + // Must consume both or ffmpeg may hang due to deadlocks. RedirectStandardOutput = true, FileName = _ffprobePath, @@ -500,21 +496,13 @@ namespace MediaBrowser.MediaEncoding.Encoder EnableRaisingEvents = true }; - if (forceEnableLogging) - { - _logger.LogInformation("{ProcessFileName} {ProcessArgs}", process.StartInfo.FileName, process.StartInfo.Arguments); - } - else - { - _logger.LogDebug("{ProcessFileName} {ProcessArgs}", process.StartInfo.FileName, process.StartInfo.Arguments); - } + _logger.LogInformation("Starting {ProcessFileName} with args {ProcessArgs}", _ffprobePath, args); using (var processWrapper = new ProcessWrapper(process, this)) { await using var memoryStream = new MemoryStream(); - _logger.LogDebug("Starting ffprobe with args {Args}", args); StartProcess(processWrapper); - await process.StandardOutput.BaseStream.CopyToAsync(memoryStream, cancellationToken: cancellationToken); + await process.StandardOutput.BaseStream.CopyToAsync(memoryStream, cancellationToken); memoryStream.Seek(0, SeekOrigin.Begin); InternalMediaInfoResult result; try @@ -522,7 +510,7 @@ namespace MediaBrowser.MediaEncoding.Encoder result = await JsonSerializer.DeserializeAsync( memoryStream, _jsonSerializerOptions, - cancellationToken: cancellationToken).ConfigureAwait(false); + cancellationToken).ConfigureAwait(false); } catch { From 7d6ec0a5bd1bc62b764bd873f29ba542ce734729 Mon Sep 17 00:00:00 2001 From: Stanislav Ionascu Date: Wed, 7 Dec 2022 16:40:26 +0000 Subject: [PATCH 06/35] Fix to make sure that UDF streams are opened with Share.Read (#8276) Make sure that any subsequent requests to open the file for read will succeed. --- .../Library/Resolvers/BaseVideoResolver.cs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs index cb377136ad..e8615e7db7 100644 --- a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs @@ -163,17 +163,15 @@ namespace Emby.Server.Implementations.Library.Resolvers try { // use disc-utils, both DVDs and BDs use UDF filesystem - using (var videoFileStream = File.Open(video.Path, FileMode.Open, FileAccess.Read)) - using (UdfReader udfReader = new UdfReader(videoFileStream)) + using var videoFileStream = File.Open(video.Path, FileMode.Open, FileAccess.Read, FileShare.Read); + using UdfReader udfReader = new UdfReader(videoFileStream); + if (udfReader.DirectoryExists("VIDEO_TS")) { - if (udfReader.DirectoryExists("VIDEO_TS")) - { - video.IsoType = IsoType.Dvd; - } - else if (udfReader.DirectoryExists("BDMV")) - { - video.IsoType = IsoType.BluRay; - } + video.IsoType = IsoType.Dvd; + } + else if (udfReader.DirectoryExists("BDMV")) + { + video.IsoType = IsoType.BluRay; } } catch (Exception ex) From 08a43d80391b3c48e75c46da08e4253d294c89d4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 7 Dec 2022 17:56:01 +0100 Subject: [PATCH 07/35] chore(deps): update mcr.microsoft.com/dotnet/sdk docker tag to v7 (#8686) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Dockerfile | 2 +- Dockerfile.arm | 2 +- Dockerfile.arm64 | 2 +- deployment/Dockerfile.debian.amd64 | 2 +- deployment/Dockerfile.debian.arm64 | 2 +- deployment/Dockerfile.debian.armhf | 2 +- deployment/Dockerfile.docker.amd64 | 2 +- deployment/Dockerfile.docker.arm64 | 2 +- deployment/Dockerfile.docker.armhf | 2 +- deployment/Dockerfile.linux.amd64 | 2 +- deployment/Dockerfile.linux.amd64-musl | 2 +- deployment/Dockerfile.linux.arm64 | 2 +- deployment/Dockerfile.linux.armhf | 2 +- deployment/Dockerfile.linux.musl-linux-arm64 | 2 +- deployment/Dockerfile.macos.amd64 | 2 +- deployment/Dockerfile.macos.arm64 | 2 +- deployment/Dockerfile.portable | 2 +- deployment/Dockerfile.windows.amd64 | 2 +- 18 files changed, 18 insertions(+), 18 deletions(-) diff --git a/Dockerfile b/Dockerfile index 7b69a186ff..304f794631 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ ##################################### # Requires binfm_misc registration # https://github.com/multiarch/qemu-user-static#binfmt_misc-register -ARG DOTNET_VERSION=6.0 +ARG DOTNET_VERSION=7.0 FROM node:lts-alpine as web-builder ARG JELLYFIN_WEB_VERSION=master diff --git a/Dockerfile.arm b/Dockerfile.arm index 84ddf499a1..bbb84a461c 100644 --- a/Dockerfile.arm +++ b/Dockerfile.arm @@ -2,7 +2,7 @@ ##################################### # Requires binfm_misc registration # https://github.com/multiarch/qemu-user-static#binfmt_misc-register -ARG DOTNET_VERSION=6.0 +ARG DOTNET_VERSION=7.0 FROM node:lts-alpine as web-builder diff --git a/Dockerfile.arm64 b/Dockerfile.arm64 index d4ae5802c0..5572586ae9 100644 --- a/Dockerfile.arm64 +++ b/Dockerfile.arm64 @@ -2,7 +2,7 @@ ##################################### # Requires binfm_misc registration # https://github.com/multiarch/qemu-user-static#binfmt_misc-register -ARG DOTNET_VERSION=6.0 +ARG DOTNET_VERSION=7.0 FROM node:lts-alpine as web-builder diff --git a/deployment/Dockerfile.debian.amd64 b/deployment/Dockerfile.debian.amd64 index c7bb5f7687..1e1f6e54e3 100644 --- a/deployment/Dockerfile.debian.amd64 +++ b/deployment/Dockerfile.debian.amd64 @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim +FROM mcr.microsoft.com/dotnet/sdk:7.0-bullseye-slim # Docker build arguments ARG SOURCE_DIR=/jellyfin ARG ARTIFACT_DIR=/dist diff --git a/deployment/Dockerfile.debian.arm64 b/deployment/Dockerfile.debian.arm64 index a0ca9b3f35..bbed2c5340 100644 --- a/deployment/Dockerfile.debian.arm64 +++ b/deployment/Dockerfile.debian.arm64 @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim +FROM mcr.microsoft.com/dotnet/sdk:7.0-bullseye-slim # Docker build arguments ARG SOURCE_DIR=/jellyfin ARG ARTIFACT_DIR=/dist diff --git a/deployment/Dockerfile.debian.armhf b/deployment/Dockerfile.debian.armhf index 42a55ebfee..79373519cd 100644 --- a/deployment/Dockerfile.debian.armhf +++ b/deployment/Dockerfile.debian.armhf @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim +FROM mcr.microsoft.com/dotnet/sdk:7.0-bullseye-slim # Docker build arguments ARG SOURCE_DIR=/jellyfin ARG ARTIFACT_DIR=/dist diff --git a/deployment/Dockerfile.docker.amd64 b/deployment/Dockerfile.docker.amd64 index 3fd3fa33ce..3a6ad95e8e 100644 --- a/deployment/Dockerfile.docker.amd64 +++ b/deployment/Dockerfile.docker.amd64 @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim +FROM mcr.microsoft.com/dotnet/sdk:7.0-bullseye-slim ARG SOURCE_DIR=/src ARG ARTIFACT_DIR=/jellyfin diff --git a/deployment/Dockerfile.docker.arm64 b/deployment/Dockerfile.docker.arm64 index e3cc92bcb3..ca72393047 100644 --- a/deployment/Dockerfile.docker.arm64 +++ b/deployment/Dockerfile.docker.arm64 @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim +FROM mcr.microsoft.com/dotnet/sdk:7.0-bullseye-slim ARG SOURCE_DIR=/src ARG ARTIFACT_DIR=/jellyfin diff --git a/deployment/Dockerfile.docker.armhf b/deployment/Dockerfile.docker.armhf index 3a5df2e246..26cce19584 100644 --- a/deployment/Dockerfile.docker.armhf +++ b/deployment/Dockerfile.docker.armhf @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim +FROM mcr.microsoft.com/dotnet/sdk:7.0-bullseye-slim ARG SOURCE_DIR=/src ARG ARTIFACT_DIR=/jellyfin diff --git a/deployment/Dockerfile.linux.amd64 b/deployment/Dockerfile.linux.amd64 index 14b580d11d..39169bd2ac 100644 --- a/deployment/Dockerfile.linux.amd64 +++ b/deployment/Dockerfile.linux.amd64 @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim +FROM mcr.microsoft.com/dotnet/sdk:7.0-bullseye-slim # Docker build arguments ARG SOURCE_DIR=/jellyfin ARG ARTIFACT_DIR=/dist diff --git a/deployment/Dockerfile.linux.amd64-musl b/deployment/Dockerfile.linux.amd64-musl index 672c3f2696..636a34544b 100644 --- a/deployment/Dockerfile.linux.amd64-musl +++ b/deployment/Dockerfile.linux.amd64-musl @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim +FROM mcr.microsoft.com/dotnet/sdk:7.0-bullseye-slim # Docker build arguments ARG SOURCE_DIR=/jellyfin ARG ARTIFACT_DIR=/dist diff --git a/deployment/Dockerfile.linux.arm64 b/deployment/Dockerfile.linux.arm64 index f2a178be37..ba8ce82f08 100644 --- a/deployment/Dockerfile.linux.arm64 +++ b/deployment/Dockerfile.linux.arm64 @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim +FROM mcr.microsoft.com/dotnet/sdk:7.0-bullseye-slim # Docker build arguments ARG SOURCE_DIR=/jellyfin ARG ARTIFACT_DIR=/dist diff --git a/deployment/Dockerfile.linux.armhf b/deployment/Dockerfile.linux.armhf index 025716f457..d771e9991d 100644 --- a/deployment/Dockerfile.linux.armhf +++ b/deployment/Dockerfile.linux.armhf @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim +FROM mcr.microsoft.com/dotnet/sdk:7.0-bullseye-slim # Docker build arguments ARG SOURCE_DIR=/jellyfin ARG ARTIFACT_DIR=/dist diff --git a/deployment/Dockerfile.linux.musl-linux-arm64 b/deployment/Dockerfile.linux.musl-linux-arm64 index 2da72e4ae0..8465611817 100644 --- a/deployment/Dockerfile.linux.musl-linux-arm64 +++ b/deployment/Dockerfile.linux.musl-linux-arm64 @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim +FROM mcr.microsoft.com/dotnet/sdk:7.0-bullseye-slim # Docker build arguments ARG SOURCE_DIR=/jellyfin ARG ARTIFACT_DIR=/dist diff --git a/deployment/Dockerfile.macos.amd64 b/deployment/Dockerfile.macos.amd64 index 62f807687c..7ebf354421 100644 --- a/deployment/Dockerfile.macos.amd64 +++ b/deployment/Dockerfile.macos.amd64 @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim +FROM mcr.microsoft.com/dotnet/sdk:7.0-bullseye-slim # Docker build arguments ARG SOURCE_DIR=/jellyfin ARG ARTIFACT_DIR=/dist diff --git a/deployment/Dockerfile.macos.arm64 b/deployment/Dockerfile.macos.arm64 index 2dfbab9b33..5041ff967c 100644 --- a/deployment/Dockerfile.macos.arm64 +++ b/deployment/Dockerfile.macos.arm64 @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim +FROM mcr.microsoft.com/dotnet/sdk:7.0-bullseye-slim # Docker build arguments ARG SOURCE_DIR=/jellyfin ARG ARTIFACT_DIR=/dist diff --git a/deployment/Dockerfile.portable b/deployment/Dockerfile.portable index e48e2d41a0..822b66ee69 100644 --- a/deployment/Dockerfile.portable +++ b/deployment/Dockerfile.portable @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim +FROM mcr.microsoft.com/dotnet/sdk:7.0-bullseye-slim # Docker build arguments ARG SOURCE_DIR=/jellyfin ARG ARTIFACT_DIR=/dist diff --git a/deployment/Dockerfile.windows.amd64 b/deployment/Dockerfile.windows.amd64 index 655300d477..805c63f8ce 100644 --- a/deployment/Dockerfile.windows.amd64 +++ b/deployment/Dockerfile.windows.amd64 @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim +FROM mcr.microsoft.com/dotnet/sdk:7.0-bullseye-slim # Docker build arguments ARG SOURCE_DIR=/jellyfin ARG ARTIFACT_DIR=/dist From 9bb1bc5a3e744f8f7a4d4b63067c5db4e36d68c4 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 7 Dec 2022 18:02:12 +0100 Subject: [PATCH 08/35] Remove dependency on OptimizedPriorityQueue --- .../Library/LibraryManager.cs | 18 +++++++------- .../Providers/IProviderManager.cs | 2 +- .../Manager/ProviderManager.cs | 24 +++++++------------ .../MediaBrowser.Providers.csproj | 1 - 4 files changed, 18 insertions(+), 27 deletions(-) diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 70439d258d..4bbb391cca 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -1154,7 +1154,7 @@ namespace Emby.Server.Implementations.Library .ToList(); } - private VirtualFolderInfo GetVirtualFolderInfo(string dir, List allCollectionFolders, Dictionary refreshQueue) + private VirtualFolderInfo GetVirtualFolderInfo(string dir, List allCollectionFolders, HashSet refreshQueue) { var info = new VirtualFolderInfo { @@ -1182,22 +1182,22 @@ namespace Emby.Server.Implementations.Library }; var libraryFolder = allCollectionFolders.FirstOrDefault(i => string.Equals(i.Path, dir, StringComparison.OrdinalIgnoreCase)); - - if (libraryFolder is not null && libraryFolder.HasImage(ImageType.Primary)) - { - info.PrimaryImageItemId = libraryFolder.Id.ToString("N", CultureInfo.InvariantCulture); - } - if (libraryFolder is not null) { - info.ItemId = libraryFolder.Id.ToString("N", CultureInfo.InvariantCulture); + var libraryFolderId = libraryFolder.Id.ToString("N", CultureInfo.InvariantCulture); + info.ItemId = libraryFolderId; + if (libraryFolder.HasImage(ImageType.Primary)) + { + info.PrimaryImageItemId = libraryFolderId; + } + info.LibraryOptions = GetLibraryOptions(libraryFolder); if (refreshQueue is not null) { info.RefreshProgress = libraryFolder.GetRefreshProgress(); - info.RefreshStatus = info.RefreshProgress.HasValue ? "Active" : refreshQueue.ContainsKey(libraryFolder.Id) ? "Queued" : "Idle"; + info.RefreshStatus = info.RefreshProgress.HasValue ? "Active" : refreshQueue.Contains(libraryFolder.Id) ? "Queued" : "Idle"; } } diff --git a/MediaBrowser.Controller/Providers/IProviderManager.cs b/MediaBrowser.Controller/Providers/IProviderManager.cs index 32a7951f62..7e0a69586c 100644 --- a/MediaBrowser.Controller/Providers/IProviderManager.cs +++ b/MediaBrowser.Controller/Providers/IProviderManager.cs @@ -216,7 +216,7 @@ namespace MediaBrowser.Controller.Providers /// Task{HttpResponseInfo}. Task GetSearchImage(string providerName, string url, CancellationToken cancellationToken); - Dictionary GetRefreshQueue(); + HashSet GetRefreshQueue(); void OnRefreshStart(BaseItem item); diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs index d3ac2f6cda..914da33a96 100644 --- a/MediaBrowser.Providers/Manager/ProviderManager.cs +++ b/MediaBrowser.Providers/Manager/ProviderManager.cs @@ -31,7 +31,6 @@ using MediaBrowser.Model.IO; using MediaBrowser.Model.Net; using MediaBrowser.Model.Providers; using Microsoft.Extensions.Logging; -using Priority_Queue; using Book = MediaBrowser.Controller.Entities.Book; using Episode = MediaBrowser.Controller.Entities.TV.Episode; using Movie = MediaBrowser.Controller.Entities.Movies.Movie; @@ -58,7 +57,7 @@ namespace MediaBrowser.Providers.Manager private readonly IBaseItemManager _baseItemManager; private readonly ConcurrentDictionary _activeRefreshes = new(); private readonly CancellationTokenSource _disposeCancellationTokenSource = new(); - private readonly SimplePriorityQueue> _refreshQueue = new(); + private readonly PriorityQueue<(Guid ItemId, MetadataRefreshOptions RefreshOptions), RefreshPriority> _refreshQueue = new(); private IImageProvider[] _imageProviders = Array.Empty(); private IMetadataService[] _metadataServices = Array.Empty(); @@ -897,18 +896,11 @@ namespace MediaBrowser.Providers.Manager } /// - public Dictionary GetRefreshQueue() + public HashSet GetRefreshQueue() { lock (_refreshQueueLock) { - var dict = new Dictionary(); - - foreach (var item in _refreshQueue) - { - dict[item.Item1] = item.Item1; - } - - return dict; + return _refreshQueue.UnorderedItems.Select(x => x.Element.ItemId).ToHashSet(); } } @@ -969,7 +961,7 @@ namespace MediaBrowser.Providers.Manager return; } - _refreshQueue.Enqueue(new Tuple(itemId, options), (int)priority); + _refreshQueue.Enqueue((itemId, options), priority); lock (_refreshQueueLock) { @@ -992,7 +984,7 @@ namespace MediaBrowser.Providers.Manager var cancellationToken = _disposeCancellationTokenSource.Token; - while (_refreshQueue.TryDequeue(out Tuple refreshItem)) + while (_refreshQueue.TryDequeue(out var refreshItem, out _)) { if (_disposed) { @@ -1001,15 +993,15 @@ namespace MediaBrowser.Providers.Manager try { - var item = libraryManager.GetItemById(refreshItem.Item1); + var item = libraryManager.GetItemById(refreshItem.ItemId); if (item is null) { continue; } var task = item is MusicArtist artist - ? RefreshArtist(artist, refreshItem.Item2, cancellationToken) - : RefreshItem(item, refreshItem.Item2, cancellationToken); + ? RefreshArtist(artist, refreshItem.RefreshOptions, cancellationToken) + : RefreshItem(item, refreshItem.RefreshOptions, cancellationToken); await task.ConfigureAwait(false); } diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index d91402f51a..3712b50c6e 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -22,7 +22,6 @@ - From 16fdb127ab9c97ec9366b9e740ea4ab929f97f0f Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Wed, 7 Dec 2022 18:14:32 +0100 Subject: [PATCH 09/35] Check HTTP status code before writing response to file (#8863) fix https://github.com/jellyfin/jellyfin/issues/8084 --- .../Plugins/AudioDb/AudioDbArtistProvider.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistProvider.cs index 40c4898852..1565a8c515 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistProvider.cs @@ -149,11 +149,11 @@ namespace MediaBrowser.Providers.Plugins.AudioDb var url = BaseUrl + "/artist-mb.php?i=" + musicBrainzId; - var path = GetArtistInfoPath(_config.ApplicationPaths, musicBrainzId); - using var response = await _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken).ConfigureAwait(false); + response.EnsureSuccessStatusCode(); await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); + var path = GetArtistInfoPath(_config.ApplicationPaths, musicBrainzId); Directory.CreateDirectory(Path.GetDirectoryName(path)); var fileStreamOptions = AsyncFile.WriteOptions; From a2babfd0d3481d832b4289ef3416d6ba11771db8 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Thu, 8 Dec 2022 01:51:29 +0100 Subject: [PATCH 10/35] Fix nightly builds (#8870) --- .ci/azure-pipelines-package.yml | 6 ++++-- debian/control | 2 +- deployment/Dockerfile.centos.amd64 | 2 +- deployment/Dockerfile.fedora.amd64 | 2 +- deployment/Dockerfile.ubuntu.amd64 | 2 +- deployment/Dockerfile.ubuntu.arm64 | 2 +- deployment/Dockerfile.ubuntu.armhf | 2 +- deployment/build.centos.amd64 | 4 ++-- deployment/build.debian.amd64 | 4 ++-- deployment/build.debian.arm64 | 4 ++-- deployment/build.debian.armhf | 4 ++-- deployment/build.fedora.amd64 | 4 ++-- deployment/build.ubuntu.amd64 | 4 ++-- deployment/build.ubuntu.arm64 | 4 ++-- deployment/build.ubuntu.armhf | 4 ++-- fedora/jellyfin.spec | 2 +- 16 files changed, 27 insertions(+), 25 deletions(-) diff --git a/.ci/azure-pipelines-package.yml b/.ci/azure-pipelines-package.yml index 83504fefef..1618237f1a 100644 --- a/.ci/azure-pipelines-package.yml +++ b/.ci/azure-pipelines-package.yml @@ -32,8 +32,10 @@ jobs: BuildConfiguration: linux.armhf Windows.amd64: BuildConfiguration: windows.amd64 - MacOS: - BuildConfiguration: macos + MacOS.amd64: + BuildConfiguration: macos.amd64 + MacOS.arm64: + BuildConfiguration: macos.arm64 Portable: BuildConfiguration: portable diff --git a/debian/control b/debian/control index dea48d9482..08c0dcda62 100644 --- a/debian/control +++ b/debian/control @@ -3,7 +3,7 @@ Section: misc Priority: optional Maintainer: Jellyfin Team Build-Depends: debhelper (>= 9), - dotnet-sdk-6.0, + dotnet-sdk-7.0, libc6-dev, libcurl4-openssl-dev, libfontconfig1-dev, diff --git a/deployment/Dockerfile.centos.amd64 b/deployment/Dockerfile.centos.amd64 index fcb8802836..646515ddc2 100644 --- a/deployment/Dockerfile.centos.amd64 +++ b/deployment/Dockerfile.centos.amd64 @@ -13,7 +13,7 @@ RUN yum update -yq \ && yum install -yq @buildsys-build rpmdevtools yum-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel git wget # Install DotNET SDK -RUN wget -q https://download.visualstudio.microsoft.com/download/pr/1d2007d3-da35-48ad-80cc-a39cbc726908/1f3555baa8b14c3327bb4eaa570d7d07/dotnet-sdk-6.0.403-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget -q https://download.visualstudio.microsoft.com/download/pr/253e5af8-41aa-48c6-86f1-39a51b44afdc/5bb2cb9380c5b1a7f0153e0a2775727b/dotnet-sdk-7.0.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.fedora.amd64 b/deployment/Dockerfile.fedora.amd64 index c18db72133..3bbd758c1f 100644 --- a/deployment/Dockerfile.fedora.amd64 +++ b/deployment/Dockerfile.fedora.amd64 @@ -12,7 +12,7 @@ RUN dnf update -yq \ && dnf install -yq @buildsys-build rpmdevtools git dnf-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel systemd wget make # Install DotNET SDK -RUN wget -q https://download.visualstudio.microsoft.com/download/pr/1d2007d3-da35-48ad-80cc-a39cbc726908/1f3555baa8b14c3327bb4eaa570d7d07/dotnet-sdk-6.0.403-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget -q https://download.visualstudio.microsoft.com/download/pr/253e5af8-41aa-48c6-86f1-39a51b44afdc/5bb2cb9380c5b1a7f0153e0a2775727b/dotnet-sdk-7.0.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.ubuntu.amd64 b/deployment/Dockerfile.ubuntu.amd64 index 01402184ad..f4787c693d 100644 --- a/deployment/Dockerfile.ubuntu.amd64 +++ b/deployment/Dockerfile.ubuntu.amd64 @@ -17,7 +17,7 @@ RUN apt-get update -yqq \ libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 # Install dotnet repository -RUN wget -q https://download.visualstudio.microsoft.com/download/pr/1d2007d3-da35-48ad-80cc-a39cbc726908/1f3555baa8b14c3327bb4eaa570d7d07/dotnet-sdk-6.0.403-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget -q https://download.visualstudio.microsoft.com/download/pr/253e5af8-41aa-48c6-86f1-39a51b44afdc/5bb2cb9380c5b1a7f0153e0a2775727b/dotnet-sdk-7.0.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.ubuntu.arm64 b/deployment/Dockerfile.ubuntu.arm64 index 6af22eed94..1cf81c60ad 100644 --- a/deployment/Dockerfile.ubuntu.arm64 +++ b/deployment/Dockerfile.ubuntu.arm64 @@ -16,7 +16,7 @@ RUN apt-get update -yqq \ mmv build-essential lsb-release # Install dotnet repository -RUN wget -q https://download.visualstudio.microsoft.com/download/pr/1d2007d3-da35-48ad-80cc-a39cbc726908/1f3555baa8b14c3327bb4eaa570d7d07/dotnet-sdk-6.0.403-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget -q https://download.visualstudio.microsoft.com/download/pr/253e5af8-41aa-48c6-86f1-39a51b44afdc/5bb2cb9380c5b1a7f0153e0a2775727b/dotnet-sdk-7.0.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.ubuntu.armhf b/deployment/Dockerfile.ubuntu.armhf index a7e70a35a3..54b8949150 100644 --- a/deployment/Dockerfile.ubuntu.armhf +++ b/deployment/Dockerfile.ubuntu.armhf @@ -16,7 +16,7 @@ RUN apt-get update -yqq \ mmv build-essential lsb-release # Install dotnet repository -RUN wget -q https://download.visualstudio.microsoft.com/download/pr/1d2007d3-da35-48ad-80cc-a39cbc726908/1f3555baa8b14c3327bb4eaa570d7d07/dotnet-sdk-6.0.403-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget -q https://download.visualstudio.microsoft.com/download/pr/253e5af8-41aa-48c6-86f1-39a51b44afdc/5bb2cb9380c5b1a7f0153e0a2775727b/dotnet-sdk-7.0.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/build.centos.amd64 b/deployment/build.centos.amd64 index bfdc6e591e..0374624d80 100755 --- a/deployment/build.centos.amd64 +++ b/deployment/build.centos.amd64 @@ -9,7 +9,7 @@ set -o xtrace pushd ${SOURCE_DIR} if [[ ${IS_DOCKER} == YES ]]; then - # Remove BuildRequires for dotnet-sdk-6.0, since it's installed manually + # Remove BuildRequires for dotnet, since it's installed manually pushd fedora cp -a jellyfin.spec /tmp/spec.orig @@ -52,7 +52,7 @@ if [[ ${IS_DOCKER} == YES ]]; then cp -a /tmp/spec.orig jellyfin.spec chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} - + popd fi diff --git a/deployment/build.debian.amd64 b/deployment/build.debian.amd64 index b2bbf9c29e..d92953ad19 100755 --- a/deployment/build.debian.amd64 +++ b/deployment/build.debian.amd64 @@ -9,9 +9,9 @@ set -o xtrace pushd ${SOURCE_DIR} if [[ ${IS_DOCKER} == YES ]]; then - # Remove build-dep for dotnet-sdk-6.0, since it's installed manually + # Remove build-dep for dotnet-sdk-7.0, since it's installed manually cp -a debian/control /tmp/control.orig - sed -i '/dotnet-sdk-6.0,/d' debian/control + sed -i '/dotnet-sdk-7.0,/d' debian/control fi # Modify changelog to unstable configuration if IS_UNSTABLE diff --git a/deployment/build.debian.arm64 b/deployment/build.debian.arm64 index 02f84471e6..618a121b65 100755 --- a/deployment/build.debian.arm64 +++ b/deployment/build.debian.arm64 @@ -9,9 +9,9 @@ set -o xtrace pushd ${SOURCE_DIR} if [[ ${IS_DOCKER} == YES ]]; then - # Remove build-dep for dotnet-sdk-6.0, since it's installed manually + # Remove build-dep for dotnet-sdk-7.0, since it's installed manually cp -a debian/control /tmp/control.orig - sed -i '/dotnet-sdk-6.0,/d' debian/control + sed -i '/dotnet-sdk-7.0,/d' debian/control fi # Modify changelog to unstable configuration if IS_UNSTABLE diff --git a/deployment/build.debian.armhf b/deployment/build.debian.armhf index 92779cb594..d1631d022c 100755 --- a/deployment/build.debian.armhf +++ b/deployment/build.debian.armhf @@ -9,9 +9,9 @@ set -o xtrace pushd ${SOURCE_DIR} if [[ ${IS_DOCKER} == YES ]]; then - # Remove build-dep for dotnet-sdk-6.0, since it's installed manually + # Remove build-dep for dotnet-sdk-7.0, since it's installed manually cp -a debian/control /tmp/control.orig - sed -i '/dotnet-sdk-6.0,/d' debian/control + sed -i '/dotnet-sdk-7.0,/d' debian/control fi # Modify changelog to unstable configuration if IS_UNSTABLE diff --git a/deployment/build.fedora.amd64 b/deployment/build.fedora.amd64 index 23c5ed86a2..1b629289f0 100755 --- a/deployment/build.fedora.amd64 +++ b/deployment/build.fedora.amd64 @@ -9,7 +9,7 @@ set -o xtrace pushd ${SOURCE_DIR} if [[ ${IS_DOCKER} == YES ]]; then - # Remove BuildRequires for dotnet-sdk-6.0, since it's installed manually + # Remove BuildRequires for dotnet, since it's installed manually pushd fedora cp -a jellyfin.spec /tmp/spec.orig @@ -52,7 +52,7 @@ if [[ ${IS_DOCKER} == YES ]]; then cp -a /tmp/spec.orig jellyfin.spec chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} - + popd fi diff --git a/deployment/build.ubuntu.amd64 b/deployment/build.ubuntu.amd64 index c36978c9e3..4254103fa8 100755 --- a/deployment/build.ubuntu.amd64 +++ b/deployment/build.ubuntu.amd64 @@ -9,9 +9,9 @@ set -o xtrace pushd ${SOURCE_DIR} if [[ ${IS_DOCKER} == YES ]]; then - # Remove build-dep for dotnet-sdk-6.0, since it's installed manually + # Remove build-dep for dotnet-sdk-7.0, since it's installed manually cp -a debian/control /tmp/control.orig - sed -i '/dotnet-sdk-6.0,/d' debian/control + sed -i '/dotnet-sdk-7.0,/d' debian/control fi # Modify changelog to unstable configuration if IS_UNSTABLE diff --git a/deployment/build.ubuntu.arm64 b/deployment/build.ubuntu.arm64 index 76d51e321f..42f111a010 100755 --- a/deployment/build.ubuntu.arm64 +++ b/deployment/build.ubuntu.arm64 @@ -9,9 +9,9 @@ set -o xtrace pushd ${SOURCE_DIR} if [[ ${IS_DOCKER} == YES ]]; then - # Remove build-dep for dotnet-sdk-6.0, since it's installed manually + # Remove build-dep for dotnet-sdk-7.0, since it's installed manually cp -a debian/control /tmp/control.orig - sed -i '/dotnet-sdk-6.0,/d' debian/control + sed -i '/dotnet-sdk-7.0,/d' debian/control fi # Modify changelog to unstable configuration if IS_UNSTABLE diff --git a/deployment/build.ubuntu.armhf b/deployment/build.ubuntu.armhf index 0ff5ab0662..357d63626c 100755 --- a/deployment/build.ubuntu.armhf +++ b/deployment/build.ubuntu.armhf @@ -9,9 +9,9 @@ set -o xtrace pushd ${SOURCE_DIR} if [[ ${IS_DOCKER} == YES ]]; then - # Remove build-dep for dotnet-sdk-6.0, since it's installed manually + # Remove build-dep for dotnet-sdk-7.0, since it's installed manually cp -a debian/control /tmp/control.orig - sed -i '/dotnet-sdk-6.0,/d' debian/control + sed -i '/dotnet-sdk-7.0,/d' debian/control fi # Modify changelog to unstable configuration if IS_UNSTABLE diff --git a/fedora/jellyfin.spec b/fedora/jellyfin.spec index a6771e3896..e39040a190 100644 --- a/fedora/jellyfin.spec +++ b/fedora/jellyfin.spec @@ -27,7 +27,7 @@ BuildRequires: systemd BuildRequires: libcurl-devel, fontconfig-devel, freetype-devel, openssl-devel, glibc-devel, libicu-devel # Requirements not packaged in RHEL 7 main repos, added via Makefile # https://packages.microsoft.com/rhel/7/prod/ -BuildRequires: dotnet-runtime-6.0, dotnet-sdk-6.0 +BuildRequires: dotnet-runtime-7.0, dotnet-sdk-7.0 Requires: %{name}-server = %{version}-%{release}, %{name}-web = %{version}-%{release} # Temporary (hopefully?) fix for https://github.com/jellyfin/jellyfin/issues/7471 From e4040ab812f30cc72436ce01b845f10717019276 Mon Sep 17 00:00:00 2001 From: Joe Rogers <1337joe@gmail.com> Date: Tue, 17 May 2022 22:58:49 +0200 Subject: [PATCH 11/35] Allow video extras to use owner library options --- .../Library/LibraryManager.cs | 39 +++++++++---------- MediaBrowser.Controller/Entities/BaseItem.cs | 5 +++ .../Library/ILibraryManager.cs | 10 ++++- 3 files changed, 31 insertions(+), 23 deletions(-) diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 4bbb391cca..bf12e4e0aa 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -1999,38 +1999,35 @@ namespace Emby.Server.Implementations.Library public List GetCollectionFolders(BaseItem item) { - while (item is not null) - { - var parent = item.GetParent(); - - if (parent is null || parent is AggregateFolder) - { - break; - } - - item = parent; - } - - if (item is null) - { - return new List(); - } - - return GetCollectionFoldersInternal(item, GetUserRootFolder().Children.OfType()); + return GetCollectionFolders(item, GetUserRootFolder().Children.OfType()); } - public List GetCollectionFolders(BaseItem item, List allUserRootChildren) + public List GetCollectionFolders(BaseItem item, IEnumerable allUserRootChildren) { while (item is not null) { var parent = item.GetParent(); - if (parent is null || parent is AggregateFolder) + if (parent is AggregateFolder) { break; } - item = parent; + if (parent is null) + { + var owner = item.GetOwner(); + + if (owner is null) + { + break; + } + + item = owner; + } + else + { + item = parent; + } } if (item is null) diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 32fe1b3b02..49dd151f36 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -2451,6 +2451,11 @@ namespace MediaBrowser.Controller.Entities return Task.FromResult(true); } + if (video.OwnerId.Equals(default)) + { + video.OwnerId = this.Id; + } + return RefreshMetadataForOwnedItem(video, copyTitleMetadata, newOptions, cancellationToken); } diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index 5905c25a57..f34e3d68d9 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -429,10 +429,16 @@ namespace MediaBrowser.Controller.Library /// Gets the collection folders. /// /// The item. - /// IEnumerable<Folder>. + /// The folders that contain the item. List GetCollectionFolders(BaseItem item); - List GetCollectionFolders(BaseItem item, List allUserRootChildren); + /// + /// Gets the collection folders. + /// + /// The item. + /// The root folders to consider. + /// The folders that contain the item. + List GetCollectionFolders(BaseItem item, IEnumerable allUserRootChildren); LibraryOptions GetLibraryOptions(BaseItem item); From a74b8b99af6e4735367a5e7812db1c9e80bcc63b Mon Sep 17 00:00:00 2001 From: Joe Rogers <1337joe@gmail.com> Date: Tue, 17 May 2022 23:00:13 +0200 Subject: [PATCH 12/35] Add option to allow extras to use embedded titles --- MediaBrowser.Model/Configuration/LibraryOptions.cs | 2 ++ MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Model/Configuration/LibraryOptions.cs b/MediaBrowser.Model/Configuration/LibraryOptions.cs index c4d313bdba..81f2f02bc5 100644 --- a/MediaBrowser.Model/Configuration/LibraryOptions.cs +++ b/MediaBrowser.Model/Configuration/LibraryOptions.cs @@ -45,6 +45,8 @@ namespace MediaBrowser.Model.Configuration public bool EnableEmbeddedTitles { get; set; } + public bool EnableEmbeddedExtrasTitles { get; set; } + public bool EnableEmbeddedEpisodeInfos { get; set; } public int AutomaticRefreshIntervalDays { get; set; } diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index 58b23a36df..751135a2c8 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -484,8 +484,8 @@ namespace MediaBrowser.Providers.MediaInfo { if (!string.IsNullOrWhiteSpace(data.Name) && libraryOptions.EnableEmbeddedTitles) { - // Don't use the embedded name for extras because it will often be the same name as the movie - if (!video.ExtraType.HasValue) + // Separate option to use the embedded name for extras because it will often be the same name as the movie + if (!video.ExtraType.HasValue || libraryOptions.EnableEmbeddedExtrasTitles) { video.Name = data.Name; } From 622dfaaedff649a058ef6544dcfb626b160c5f7f Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Thu, 8 Dec 2022 13:19:42 +0100 Subject: [PATCH 13/35] Fix fedora and centos nightly (#8875) --- fedora/jellyfin.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fedora/jellyfin.spec b/fedora/jellyfin.spec index e39040a190..416d883607 100644 --- a/fedora/jellyfin.spec +++ b/fedora/jellyfin.spec @@ -74,7 +74,7 @@ dotnet publish --configuration Release --self-contained --runtime %{dotnet_runti %install # Jellyfin files %{__mkdir} -p %{buildroot}%{_libdir}/jellyfin %{buildroot}%{_bindir} -%{__cp} -r Jellyfin.Server/bin/Release/net6.0/%{dotnet_runtime}/publish/* %{buildroot}%{_libdir}/jellyfin +%{__cp} -r Jellyfin.Server/bin/Release/net7.0/%{dotnet_runtime}/publish/* %{buildroot}%{_libdir}/jellyfin ln -srf %{_libdir}/jellyfin/jellyfin %{buildroot}%{_bindir}/jellyfin %{__install} -D %{SOURCE14} %{buildroot}%{_libexecdir}/jellyfin/restart.sh From 86093611025b395474d538fcd90e8b4f27ea4f47 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 8 Dec 2022 18:49:24 -0700 Subject: [PATCH 14/35] chore(deps): update github/codeql-action digest to a669cc5 (#8877) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index f385aecb64..e4ab8e6439 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -27,11 +27,11 @@ jobs: dotnet-version: '7.0.x' - name: Initialize CodeQL - uses: github/codeql-action/init@b2a92eb56d8cb930006a1c6ed86b0782dd8a4297 # v2 + uses: github/codeql-action/init@a669cc5936cc5e1b6a362ec1ff9e410dc570d190 # v2 with: languages: ${{ matrix.language }} queries: +security-extended - name: Autobuild - uses: github/codeql-action/autobuild@b2a92eb56d8cb930006a1c6ed86b0782dd8a4297 # v2 + uses: github/codeql-action/autobuild@a669cc5936cc5e1b6a362ec1ff9e410dc570d190 # v2 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@b2a92eb56d8cb930006a1c6ed86b0782dd8a4297 # v2 + uses: github/codeql-action/analyze@a669cc5936cc5e1b6a362ec1ff9e410dc570d190 # v2 From 42f768faf356fbe25752b8e9a0b56abf124c8ec9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 9 Dec 2022 21:21:11 -0700 Subject: [PATCH 15/35] chore(deps): update dependency efcoresecondlevelcacheinterceptor to v3.8.1 (#8881) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .../Jellyfin.Server.Implementations.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj index d233c00a0b..ec08561113 100644 --- a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj +++ b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj @@ -26,7 +26,7 @@ - + From 1987b138f9d84c97694e895d512a81d2bb7fc77e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 10 Dec 2022 14:44:24 +0000 Subject: [PATCH 16/35] chore(deps): update dependency playlistsnet to v1.3.1 --- MediaBrowser.Providers/MediaBrowser.Providers.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 3712b50c6e..dbacc2a821 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -22,7 +22,7 @@ - + From 78d3bc9c8bf8586a3ea1b8eafcd118c952aa6b39 Mon Sep 17 00:00:00 2001 From: Hyper-Jedi Date: Sat, 10 Dec 2022 04:05:35 +0000 Subject: [PATCH 17/35] Translated using Weblate (Urdu (Pakistan)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ur_PK/ --- .../Localization/Core/ur_PK.json | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/ur_PK.json b/Emby.Server.Implementations/Localization/Core/ur_PK.json index 5413346d41..7fe0c4c4be 100644 --- a/Emby.Server.Implementations/Localization/Core/ur_PK.json +++ b/Emby.Server.Implementations/Localization/Core/ur_PK.json @@ -102,8 +102,8 @@ "LabelIpAddressValue": "آئ پی ایڈریس {0}", "ItemRemovedWithName": "لائبریری سے ہٹا دیا گیا ھے", "ItemAddedWithName": "[0} لائبریری میں شامل کیا گیا ھے", - "Inherit": "وراثت میں", - "HomeVideos": "ہوم ویڈیو", + "Inherit": "وراثت", + "HomeVideos": "ہوم ویڈیوز", "HeaderRecordingGroups": "ریکارڈنگ گروپس", "FailedLoginAttemptWithUserName": "{0} سے لاگ ان کی ناکام کوشش", "DeviceOnlineWithName": "{0} متصل ھو چکا ھے", @@ -115,5 +115,13 @@ "AppDeviceValues": "پروگرام:{0}, ڈیوائس:{1}", "Forced": "جَبری", "Undefined": "غير وضاحتى", - "Default": "طے شدہ" + "Default": "طے شدہ", + "TaskKeyframeExtractorDescription": "زیادہ درست HLS پلے لسٹس بنانے کے لیے ویڈیو فائلوں سے کلیدی فریم نکالتا ہے۔ یہ کام طویل عرصے تک چل سکتا ہے۔", + "TaskOptimizeDatabase": "ڈیٹا بیس کو بہتر بنائیں", + "TaskOptimizeDatabaseDescription": "ڈیٹا بیس کو کمپیکٹ کرتا ہے اور خالی جگہ کو چھوٹا کرتا ہے۔ لائبریری کو اسکین کرنے یا دیگر تبدیلیاں کرنے کے بعد اس کام کو چلانے سے کارکردگی بہتر ہو سکتی ہے۔", + "TaskKeyframeExtractor": "کی فریم ایکسٹریکٹر", + "TaskCleanActivityLogDescription": "تشکیل شدہ عمر سے زیادہ پرانی سرگرمی لاگ اندراجات کو حذف کرتا ہے۔", + "External": "بیرونی", + "HearingImpaired": "قوت سماعت سے محروم", + "TaskCleanActivityLog": "سرگرمی لاگ کو صاف کریں۔" } From 9f9e795d1f654c39b0bdad70f0ec2c53171011aa Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 13 Dec 2022 05:45:54 -0700 Subject: [PATCH 18/35] chore(deps): update actions/checkout digest to 7dd9e2a (#8895) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/commands.yml | 4 ++-- .github/workflows/openapi.yml | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index e4ab8e6439..3349836529 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -20,7 +20,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3 + uses: actions/checkout@7dd9e2a3dc350cf687eb1b2a4fadfee8c8e49675 # v3 - name: Setup .NET uses: actions/setup-dotnet@607fce577a46308457984d59e4954e075820f10a # tag=v3 with: diff --git a/.github/workflows/commands.yml b/.github/workflows/commands.yml index f7fbc4706d..ec00a279bf 100644 --- a/.github/workflows/commands.yml +++ b/.github/workflows/commands.yml @@ -24,7 +24,7 @@ jobs: reactions: '+1' - name: Checkout the latest code - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3 + uses: actions/checkout@7dd9e2a3dc350cf687eb1b2a4fadfee8c8e49675 # v3 with: token: ${{ secrets.JF_BOT_TOKEN }} fetch-depth: 0 @@ -51,7 +51,7 @@ jobs: reactions: eyes - name: Checkout the latest code - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3 + uses: actions/checkout@7dd9e2a3dc350cf687eb1b2a4fadfee8c8e49675 # v3 with: token: ${{ secrets.JF_BOT_TOKEN }} fetch-depth: 0 diff --git a/.github/workflows/openapi.yml b/.github/workflows/openapi.yml index d7ace118b2..322486d7a1 100644 --- a/.github/workflows/openapi.yml +++ b/.github/workflows/openapi.yml @@ -14,7 +14,7 @@ jobs: permissions: read-all steps: - name: Checkout repository - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3 + uses: actions/checkout@7dd9e2a3dc350cf687eb1b2a4fadfee8c8e49675 # v3 with: ref: ${{ github.event.pull_request.head.sha }} repository: ${{ github.event.pull_request.head.repo.full_name }} @@ -39,7 +39,7 @@ jobs: permissions: read-all steps: - name: Checkout repository - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3 + uses: actions/checkout@7dd9e2a3dc350cf687eb1b2a4fadfee8c8e49675 # v3 with: ref: ${{ github.base_ref }} - name: Setup .NET From ca73b4488748ed2489fec72f06d447305f39aaaf Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Tue, 13 Dec 2022 16:39:38 +0100 Subject: [PATCH 19/35] Fix Windows FFmpeg download link (#8901) --- deployment/build.windows.amd64 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployment/build.windows.amd64 b/deployment/build.windows.amd64 index 30b252beb5..0786358bd5 100755 --- a/deployment/build.windows.amd64 +++ b/deployment/build.windows.amd64 @@ -8,7 +8,7 @@ set -o xtrace # Version variables NSSM_VERSION="nssm-2.24-101-g897c7ad" NSSM_URL="http://files.evilt.win/nssm/${NSSM_VERSION}.zip" -FFMPEG_URL="https://repo.jellyfin.org/releases/server/windows/ffmpeg/jellyfin-ffmpeg.zip"; +FFMPEG_URL="https://repo.jellyfin.org/releases/server/windows/ffmpeg/jellyfin-ffmpeg-portable_win64.zip"; # Move to source directory pushd ${SOURCE_DIR} From 39289ae8145a06cca89a24ca867690af131b19fc Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 14 Dec 2022 15:57:20 +0100 Subject: [PATCH 20/35] chore(deps): update actions/checkout digest to 755da8c (#8899) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/commands.yml | 4 ++-- .github/workflows/openapi.yml | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 3349836529..abc74918a4 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -20,7 +20,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@7dd9e2a3dc350cf687eb1b2a4fadfee8c8e49675 # v3 + uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # v3 - name: Setup .NET uses: actions/setup-dotnet@607fce577a46308457984d59e4954e075820f10a # tag=v3 with: diff --git a/.github/workflows/commands.yml b/.github/workflows/commands.yml index ec00a279bf..edd349d48a 100644 --- a/.github/workflows/commands.yml +++ b/.github/workflows/commands.yml @@ -24,7 +24,7 @@ jobs: reactions: '+1' - name: Checkout the latest code - uses: actions/checkout@7dd9e2a3dc350cf687eb1b2a4fadfee8c8e49675 # v3 + uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # v3 with: token: ${{ secrets.JF_BOT_TOKEN }} fetch-depth: 0 @@ -51,7 +51,7 @@ jobs: reactions: eyes - name: Checkout the latest code - uses: actions/checkout@7dd9e2a3dc350cf687eb1b2a4fadfee8c8e49675 # v3 + uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # v3 with: token: ${{ secrets.JF_BOT_TOKEN }} fetch-depth: 0 diff --git a/.github/workflows/openapi.yml b/.github/workflows/openapi.yml index 322486d7a1..889133aedf 100644 --- a/.github/workflows/openapi.yml +++ b/.github/workflows/openapi.yml @@ -14,7 +14,7 @@ jobs: permissions: read-all steps: - name: Checkout repository - uses: actions/checkout@7dd9e2a3dc350cf687eb1b2a4fadfee8c8e49675 # v3 + uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # v3 with: ref: ${{ github.event.pull_request.head.sha }} repository: ${{ github.event.pull_request.head.repo.full_name }} @@ -39,7 +39,7 @@ jobs: permissions: read-all steps: - name: Checkout repository - uses: actions/checkout@7dd9e2a3dc350cf687eb1b2a4fadfee8c8e49675 # v3 + uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # v3 with: ref: ${{ github.base_ref }} - name: Setup .NET From 3462676a8f288358d65484ce2022b66ef9da5ee9 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Wed, 14 Dec 2022 22:03:03 +0100 Subject: [PATCH 21/35] Fix debug builds (#8909) --- Jellyfin.Networking/Manager/NetworkManager.cs | 3 ++- .../Json/Converters/JsonLowerCaseConverterTests.cs | 2 +- tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs | 2 +- .../Entities/ProviderIdsExtensionsTests.cs | 2 +- .../Data/SqliteItemRepositoryTests.cs | 2 +- .../HttpServer/WebSocketConnectionTests.cs | 2 +- .../Library/EpisodeResolverTest.cs | 2 +- .../Sorting/AiredEpisodeOrderComparerTests.cs | 4 ++-- tests/Jellyfin.Server.Integration.Tests/AuthHelper.cs | 2 +- 9 files changed, 11 insertions(+), 10 deletions(-) diff --git a/Jellyfin.Networking/Manager/NetworkManager.cs b/Jellyfin.Networking/Manager/NetworkManager.cs index 366428e78e..5520e2f04e 100644 --- a/Jellyfin.Networking/Manager/NetworkManager.cs +++ b/Jellyfin.Networking/Manager/NetworkManager.cs @@ -1301,7 +1301,8 @@ namespace Jellyfin.Networking.Manager var extResult = _interfaceAddresses .Exclude(_bindExclusions, false) .Where(p => !IsInLocalNetwork(p)) - .OrderBy(p => p.Tag); + .OrderBy(p => p.Tag) + .ToList(); if (extResult.Any()) { diff --git a/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonLowerCaseConverterTests.cs b/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonLowerCaseConverterTests.cs index af9227de27..16c69ca489 100644 --- a/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonLowerCaseConverterTests.cs +++ b/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonLowerCaseConverterTests.cs @@ -57,7 +57,7 @@ namespace Jellyfin.Extensions.Tests.Json.Converters Assert.Equal(json, res); } - private class TestContainer + private sealed class TestContainer { public TestContainer(CollectionTypeOptions? collectionType) { diff --git a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs index c0c363d3da..1b27e344ba 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs @@ -32,7 +32,7 @@ namespace Jellyfin.MediaEncoding.Tests Assert.Equal(valid, _encoderValidator.ValidateVersionInternal(versionOutput)); } - private class GetFFmpegVersionTestData : TheoryData + private sealed class GetFFmpegVersionTestData : TheoryData { public GetFFmpegVersionTestData() { diff --git a/tests/Jellyfin.Model.Tests/Entities/ProviderIdsExtensionsTests.cs b/tests/Jellyfin.Model.Tests/Entities/ProviderIdsExtensionsTests.cs index a1ace84769..2a62ab74c3 100644 --- a/tests/Jellyfin.Model.Tests/Entities/ProviderIdsExtensionsTests.cs +++ b/tests/Jellyfin.Model.Tests/Entities/ProviderIdsExtensionsTests.cs @@ -186,7 +186,7 @@ namespace Jellyfin.Model.Tests.Entities Assert.Null(nullProvider.ProviderIds); } - private class ProviderIdsExtensionsTestsObject : IHasProviderIds + private sealed class ProviderIdsExtensionsTestsObject : IHasProviderIds { public static readonly ProviderIdsExtensionsTestsObject Empty = new ProviderIdsExtensionsTestsObject(); diff --git a/tests/Jellyfin.Server.Implementations.Tests/Data/SqliteItemRepositoryTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Data/SqliteItemRepositoryTests.cs index 4c7c56311b..7d92e7b261 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Data/SqliteItemRepositoryTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Data/SqliteItemRepositoryTests.cs @@ -282,7 +282,7 @@ namespace Jellyfin.Server.Implementations.Tests.Data Assert.Equal(expected, SqliteItemRepository.SerializeProviderIds(values)); } - private class ProviderIdsExtensionsTestsObject : IHasProviderIds + private sealed class ProviderIdsExtensionsTestsObject : IHasProviderIds { public Dictionary ProviderIds { get; set; } = new Dictionary(); } diff --git a/tests/Jellyfin.Server.Implementations.Tests/HttpServer/WebSocketConnectionTests.cs b/tests/Jellyfin.Server.Implementations.Tests/HttpServer/WebSocketConnectionTests.cs index ef8f7cd901..f016118192 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/HttpServer/WebSocketConnectionTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/HttpServer/WebSocketConnectionTests.cs @@ -48,7 +48,7 @@ namespace Jellyfin.Server.Implementations.Tests.HttpServer Assert.Throws(() => con.DeserializeWebSocketMessage(new ReadOnlySequence(bytes), out var bytesConsumed)); } - internal class BufferSegment : ReadOnlySequenceSegment + internal sealed class BufferSegment : ReadOnlySequenceSegment { public BufferSegment(Memory memory) { diff --git a/tests/Jellyfin.Server.Implementations.Tests/Library/EpisodeResolverTest.cs b/tests/Jellyfin.Server.Implementations.Tests/Library/EpisodeResolverTest.cs index c21871297d..286ba04059 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Library/EpisodeResolverTest.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Library/EpisodeResolverTest.cs @@ -60,7 +60,7 @@ namespace Jellyfin.Server.Implementations.Tests.Library Assert.NotNull(episodeResolver.Resolve(itemResolveArgs)); } - private class EpisodeResolverMock : EpisodeResolver + private sealed class EpisodeResolverMock : EpisodeResolver { public EpisodeResolverMock(ILogger logger, NamingOptions namingOptions) : base(logger, namingOptions) { diff --git a/tests/Jellyfin.Server.Implementations.Tests/Sorting/AiredEpisodeOrderComparerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Sorting/AiredEpisodeOrderComparerTests.cs index 59d82678e4..1dd49b2cfa 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Sorting/AiredEpisodeOrderComparerTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Sorting/AiredEpisodeOrderComparerTests.cs @@ -27,7 +27,7 @@ namespace Jellyfin.Server.Implementations.Tests.Sorting Assert.Equal(-expected, cmp.Compare(y, x)); } - private class EpisodeBadData : TheoryData + private sealed class EpisodeBadData : TheoryData { public EpisodeBadData() { @@ -36,7 +36,7 @@ namespace Jellyfin.Server.Implementations.Tests.Sorting } } - private class EpisodeTestData : TheoryData + private sealed class EpisodeTestData : TheoryData { public EpisodeTestData() { diff --git a/tests/Jellyfin.Server.Integration.Tests/AuthHelper.cs b/tests/Jellyfin.Server.Integration.Tests/AuthHelper.cs index 79c11a8656..9eb0beda44 100644 --- a/tests/Jellyfin.Server.Integration.Tests/AuthHelper.cs +++ b/tests/Jellyfin.Server.Integration.Tests/AuthHelper.cs @@ -48,7 +48,7 @@ namespace Jellyfin.Server.Integration.Tests headers.Add(AuthHeaderName, DummyAuthHeader + $", Token={accessToken}"); } - private class AuthenticationResultDto + private sealed class AuthenticationResultDto { public string AccessToken { get; set; } = string.Empty; From a455e580be2e650fafb8e768c4978d9ff83ca3c4 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Wed, 14 Dec 2022 22:03:26 +0100 Subject: [PATCH 22/35] Use static methods StopWatch where it makes sense (#8898) --- .../Middleware/ResponseTimeMiddleware.cs | 14 +++++++------- Jellyfin.Server/Program.cs | 7 ++----- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/Jellyfin.Server/Middleware/ResponseTimeMiddleware.cs b/Jellyfin.Server/Middleware/ResponseTimeMiddleware.cs index 65b64da4f1..531897cd49 100644 --- a/Jellyfin.Server/Middleware/ResponseTimeMiddleware.cs +++ b/Jellyfin.Server/Middleware/ResponseTimeMiddleware.cs @@ -40,25 +40,25 @@ namespace Jellyfin.Server.Middleware /// Task. public async Task Invoke(HttpContext context, IServerConfigurationManager serverConfigurationManager) { - var watch = new Stopwatch(); - watch.Start(); + var startTimestamp = Stopwatch.GetTimestamp(); + var enableWarning = serverConfigurationManager.Configuration.EnableSlowResponseWarning; var warningThreshold = serverConfigurationManager.Configuration.SlowResponseThresholdMs; context.Response.OnStarting(() => { - watch.Stop(); - var responseTimeForCompleteRequest = watch.ElapsedMilliseconds; - if (enableWarning && responseTimeForCompleteRequest > warningThreshold && _logger.IsEnabled(LogLevel.Debug)) + var responseTime = Stopwatch.GetElapsedTime(startTimestamp); + var responseTimeMs = responseTime.TotalMilliseconds; + if (enableWarning && responseTimeMs > warningThreshold && _logger.IsEnabled(LogLevel.Debug)) { _logger.LogDebug( "Slow HTTP Response from {Url} to {RemoteIp} in {Elapsed:g} with Status Code {StatusCode}", context.Request.GetDisplayUrl(), context.GetNormalizedRemoteIp(), - watch.Elapsed, + responseTime, context.Response.StatusCode); } - context.Response.Headers[ResponseHeaderResponseTime] = responseTimeForCompleteRequest.ToString(CultureInfo.InvariantCulture); + context.Response.Headers[ResponseHeaderResponseTime] = responseTimeMs.ToString(CultureInfo.InvariantCulture); return Task.CompletedTask; }); diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 46f45b9adf..7052f4d2bf 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -94,8 +94,7 @@ namespace Jellyfin.Server private static async Task StartApp(StartupOptions options) { - var stopWatch = new Stopwatch(); - stopWatch.Start(); + var startTimestamp = Stopwatch.GetTimestamp(); // Log all uncaught exceptions to std error static void UnhandledExceptionToConsole(object sender, UnhandledExceptionEventArgs e) => @@ -217,9 +216,7 @@ namespace Jellyfin.Server await appHost.RunStartupTasksAsync(_tokenSource.Token).ConfigureAwait(false); - stopWatch.Stop(); - - _logger.LogInformation("Startup complete {Time:g}", stopWatch.Elapsed); + _logger.LogInformation("Startup complete {Time:g}", Stopwatch.GetElapsedTime(startTimestamp)); // Block main thread until shutdown await Task.Delay(-1, _tokenSource.Token).ConfigureAwait(false); From 3f82c90c48f2ec3ada36259aae7f2ab69d6cd77b Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Wed, 14 Dec 2022 22:03:38 +0100 Subject: [PATCH 23/35] Improve DB perf for everyone not using debug logging (#8827) --- .../Data/SqliteItemRepository.cs | 230 +++++++++--------- 1 file changed, 115 insertions(+), 115 deletions(-) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 1514762608..9bdc4e5c8e 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -5,9 +5,11 @@ using System; using System.Buffers.Text; using System.Collections.Generic; +using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; +using System.Runtime.CompilerServices; using System.Text; using System.Text.Json; using System.Threading; @@ -2558,8 +2560,6 @@ namespace Emby.Server.Implementations.Data CheckDisposed(); - var now = DateTime.UtcNow; - // Hack for right now since we currently don't support filtering out these duplicates within a query if (query.Limit.HasValue && query.EnableGroupByMetadataKey) { @@ -2581,28 +2581,24 @@ namespace Emby.Server.Implementations.Data } var commandText = commandTextBuilder.ToString(); - int count; + + using (new QueryTimeLogger(Logger, commandText)) using (var connection = GetConnection(true)) + using (var statement = PrepareStatement(connection, commandText)) { - using (var statement = PrepareStatement(connection, commandText)) + if (EnableJoinUserData(query)) { - if (EnableJoinUserData(query)) - { - statement.TryBind("@UserId", query.User.InternalId); - } - - BindSimilarParams(query, statement); - BindSearchParams(query, statement); - - // Running this again will bind the params - GetWhereClauses(query, statement); - - count = statement.ExecuteQuery().SelectScalarInt().First(); + statement.TryBind("@UserId", query.User.InternalId); } - } - LogQueryTime("GetCount", commandText, now); - return count; + BindSimilarParams(query, statement); + BindSearchParams(query, statement); + + // Running this again will bind the params + GetWhereClauses(query, statement); + + return statement.ExecuteQuery().SelectScalarInt().First(); + } } public List GetItemList(InternalItemsQuery query) @@ -2611,8 +2607,6 @@ namespace Emby.Server.Implementations.Data CheckDisposed(); - var now = DateTime.UtcNow; - // Hack for right now since we currently don't support filtering out these duplicates within a query if (query.Limit.HasValue && query.EnableGroupByMetadataKey) { @@ -2656,61 +2650,58 @@ namespace Emby.Server.Implementations.Data var commandText = commandTextBuilder.ToString(); var items = new List(); + using (new QueryTimeLogger(Logger, commandText)) using (var connection = GetConnection(true)) + using (var statement = PrepareStatement(connection, commandText)) { - using (var statement = PrepareStatement(connection, commandText)) + if (EnableJoinUserData(query)) { - if (EnableJoinUserData(query)) - { - statement.TryBind("@UserId", query.User.InternalId); - } - - BindSimilarParams(query, statement); - BindSearchParams(query, statement); - - // Running this again will bind the params - GetWhereClauses(query, statement); - - var hasEpisodeAttributes = HasEpisodeAttributes(query); - var hasServiceName = HasServiceName(query); - var hasProgramAttributes = HasProgramAttributes(query); - var hasStartDate = HasStartDate(query); - var hasTrailerTypes = HasTrailerTypes(query); - var hasArtistFields = HasArtistFields(query); - var hasSeriesFields = HasSeriesFields(query); - - foreach (var row in statement.ExecuteQuery()) - { - var item = GetItem(row, query, hasProgramAttributes, hasEpisodeAttributes, hasServiceName, hasStartDate, hasTrailerTypes, hasArtistFields, hasSeriesFields); - if (item is not null) - { - items.Add(item); - } - } + statement.TryBind("@UserId", query.User.InternalId); } - // Hack for right now since we currently don't support filtering out these duplicates within a query - if (query.EnableGroupByMetadataKey) + BindSimilarParams(query, statement); + BindSearchParams(query, statement); + + // Running this again will bind the params + GetWhereClauses(query, statement); + + var hasEpisodeAttributes = HasEpisodeAttributes(query); + var hasServiceName = HasServiceName(query); + var hasProgramAttributes = HasProgramAttributes(query); + var hasStartDate = HasStartDate(query); + var hasTrailerTypes = HasTrailerTypes(query); + var hasArtistFields = HasArtistFields(query); + var hasSeriesFields = HasSeriesFields(query); + + foreach (var row in statement.ExecuteQuery()) { - var limit = query.Limit ?? int.MaxValue; - limit -= 4; - var newList = new List(); - - foreach (var item in items) + var item = GetItem(row, query, hasProgramAttributes, hasEpisodeAttributes, hasServiceName, hasStartDate, hasTrailerTypes, hasArtistFields, hasSeriesFields); + if (item is not null) { - AddItem(newList, item); - - if (newList.Count >= limit) - { - break; - } + items.Add(item); } - - items = newList; } } - LogQueryTime("GetItemList", commandText, now); + // Hack for right now since we currently don't support filtering out these duplicates within a query + if (query.EnableGroupByMetadataKey) + { + var limit = query.Limit ?? int.MaxValue; + limit -= 4; + var newList = new List(); + + foreach (var item in items) + { + AddItem(newList, item); + + if (newList.Count >= limit) + { + break; + } + } + + items = newList; + } return items; } @@ -2763,26 +2754,6 @@ namespace Emby.Server.Implementations.Data items.Add(newItem); } - private void LogQueryTime(string methodName, string commandText, DateTime startDate) - { - var elapsed = (DateTime.UtcNow - startDate).TotalMilliseconds; - -#if DEBUG - const int SlowThreshold = 100; -#else - const int SlowThreshold = 10; -#endif - - if (elapsed >= SlowThreshold) - { - Logger.LogDebug( - "{Method} query time (slow): {ElapsedMs}ms. Query: {Query}", - methodName, - elapsed, - commandText); - } - } - public QueryResult GetItems(InternalItemsQuery query) { ArgumentNullException.ThrowIfNull(query); @@ -2798,8 +2769,6 @@ namespace Emby.Server.Implementations.Data returnList); } - var now = DateTime.UtcNow; - // Hack for right now since we currently don't support filtering out these duplicates within a query if (query.Limit.HasValue && query.EnableGroupByMetadataKey) { @@ -2900,6 +2869,7 @@ namespace Emby.Server.Implementations.Data if (!isReturningZeroItems) { + using (new QueryTimeLogger(Logger, itemQuery, "GetItems.ItemQuery")) using (var statement = itemQueryStatement) { if (EnableJoinUserData(query)) @@ -2930,13 +2900,11 @@ namespace Emby.Server.Implementations.Data } } } - - LogQueryTime("GetItems.ItemQuery", itemQuery, now); } - now = DateTime.UtcNow; if (query.EnableTotalRecordCount) { + using (new QueryTimeLogger(Logger, totalRecordCountQuery, "GetItems.TotalRecordCount")) using (var statement = totalRecordCountQueryStatement) { if (EnableJoinUserData(query)) @@ -2952,8 +2920,6 @@ namespace Emby.Server.Implementations.Data result.TotalRecordCount = statement.ExecuteQuery().SelectScalarInt().First(); } - - LogQueryTime("GetItems.TotalRecordCount", totalRecordCountQuery, now); } }, ReadTransactionMode); @@ -3171,8 +3137,6 @@ namespace Emby.Server.Implementations.Data CheckDisposed(); - var now = DateTime.UtcNow; - var columns = new List { "guid" }; SetFinalColumnsToSelect(query, columns); var commandTextBuilder = new StringBuilder("select ", 256) @@ -3209,29 +3173,27 @@ namespace Emby.Server.Implementations.Data var commandText = commandTextBuilder.ToString(); var list = new List(); + using (new QueryTimeLogger(Logger, commandText)) using (var connection = GetConnection(true)) + using (var statement = PrepareStatement(connection, commandText)) { - using (var statement = PrepareStatement(connection, commandText)) + if (EnableJoinUserData(query)) { - if (EnableJoinUserData(query)) - { - statement.TryBind("@UserId", query.User.InternalId); - } + statement.TryBind("@UserId", query.User.InternalId); + } - BindSimilarParams(query, statement); - BindSearchParams(query, statement); + BindSimilarParams(query, statement); + BindSearchParams(query, statement); - // Running this again will bind the params - GetWhereClauses(query, statement); + // Running this again will bind the params + GetWhereClauses(query, statement); - foreach (var row in statement.ExecuteQuery()) - { - list.Add(row[0].ReadGuidFromBlob()); - } + foreach (var row in statement.ExecuteQuery()) + { + list.Add(row[0].ReadGuidFromBlob()); } } - LogQueryTime("GetItemList", commandText, now); return list; } @@ -5112,8 +5074,6 @@ AND Type = @InternalPersonType)"); { CheckDisposed(); - var now = DateTime.UtcNow; - var stringBuilder = new StringBuilder("Select Value From ItemValues where Type", 128); if (itemValueTypes.Length == 1) { @@ -5145,6 +5105,7 @@ AND Type = @InternalPersonType)"); var commandText = stringBuilder.ToString(); var list = new List(); + using (new QueryTimeLogger(Logger, commandText)) using (var connection = GetConnection(true)) using (var statement = PrepareStatement(connection, commandText)) { @@ -5157,7 +5118,6 @@ AND Type = @InternalPersonType)"); } } - LogQueryTime("GetItemValueNames", commandText, now); return list; } @@ -5172,8 +5132,6 @@ AND Type = @InternalPersonType)"); CheckDisposed(); - var now = DateTime.UtcNow; - var typeClause = itemValueTypes.Length == 1 ? ("Type=" + itemValueTypes[0]) : ("Type in (" + string.Join(',', itemValueTypes) + ")"); @@ -5347,6 +5305,7 @@ AND Type = @InternalPersonType)"); var list = new List<(BaseItem, ItemCounts)>(); var result = new QueryResult<(BaseItem, ItemCounts)>(); + using (new QueryTimeLogger(Logger, commandText)) using (var connection = GetConnection(true)) { connection.RunInTransaction( @@ -5420,8 +5379,6 @@ AND Type = @InternalPersonType)"); ReadTransactionMode); } - LogQueryTime("GetItemValues", commandText, now); - if (result.TotalRecordCount == 0) { result.TotalRecordCount = list.Count; @@ -6246,5 +6203,48 @@ AND Type = @InternalPersonType)"); return item; } + +#nullable enable + + private readonly struct QueryTimeLogger : IDisposable + { + private readonly ILogger _logger; + private readonly string _commandText; + private readonly string _methodName; + private readonly long _startTimestamp; + + public QueryTimeLogger(ILogger logger, string commandText, [CallerMemberName] string methodName = "") + { + _logger = logger; + _commandText = commandText; + _methodName = methodName; + _startTimestamp = logger.IsEnabled(LogLevel.Debug) ? Stopwatch.GetTimestamp() : -1; + } + + public void Dispose() + { + if (_startTimestamp == -1) + { + return; + } + + var elapsedMs = Stopwatch.GetElapsedTime(_startTimestamp).TotalMilliseconds; + +#if DEBUG + const int SlowThreshold = 100; +#else + const int SlowThreshold = 10; +#endif + + if (elapsedMs >= SlowThreshold) + { + _logger.LogDebug( + "{Method} query time (slow): {ElapsedMs}ms. Query: {Query}", + _methodName, + elapsedMs, + _commandText); + } + } + } } } From 81a7261980a327e9095ca86bd6c7e4355cbba01e Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Wed, 14 Dec 2022 22:04:05 +0100 Subject: [PATCH 24/35] Default to no bitrate limit if no maxBitrate is set (#8850) Fixes https://github.com/jellyfin/jellyfin/issues/3277 --- MediaBrowser.Model/Dlna/StreamBuilder.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index af35e98eef..4c964c21a6 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -1450,15 +1450,16 @@ namespace MediaBrowser.Model.Dlna private bool IsItemBitrateEligibleForDirectPlayback(MediaSourceInfo item, long maxBitrate, PlayMethod playMethod) { - // Don't restrict by bitrate if coming from an external domain + // Don't restrict bitrate if item is remote. if (item.IsRemote) { return true; } - long requestedMaxBitrate = maxBitrate > 0 ? maxBitrate : 1000000; + // If no maximum bitrate is set, default to no maximum bitrate. + long requestedMaxBitrate = maxBitrate > 0 ? maxBitrate : int.MaxValue; - // If we don't know the bitrate, then force a transcode if requested max bitrate is under 40 mbps + // If we don't know the item bitrate, then force a transcode if requested max bitrate is under 40 mbps int itemBitrate = item.Bitrate ?? 40000000; if (itemBitrate > requestedMaxBitrate) From e15337d46e47c3c1017c80e1c6a4177f965f5e52 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 14 Dec 2022 14:06:58 -0700 Subject: [PATCH 25/35] chore(deps): update cirrus-actions/rebase action to v1.8 (#8905) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/commands.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/commands.yml b/.github/workflows/commands.yml index edd349d48a..f62ae853dd 100644 --- a/.github/workflows/commands.yml +++ b/.github/workflows/commands.yml @@ -30,7 +30,7 @@ jobs: fetch-depth: 0 - name: Automatic Rebase - uses: cirrus-actions/rebase@6e572f08c244e2f04f9beb85a943eb618218714d # tag=1.7 + uses: cirrus-actions/rebase@b87d48154a87a85666003575337e27b8cd65f691 # 1.8 env: GITHUB_TOKEN: ${{ secrets.JF_BOT_TOKEN }} From 43d904c55324320c78fc62884ce6dc72e1792800 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 14 Dec 2022 21:28:05 -0700 Subject: [PATCH 26/35] chore(deps): update github/codeql-action digest to 959cbb7 (#8908) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index abc74918a4..5aebbae4d6 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -27,11 +27,11 @@ jobs: dotnet-version: '7.0.x' - name: Initialize CodeQL - uses: github/codeql-action/init@a669cc5936cc5e1b6a362ec1ff9e410dc570d190 # v2 + uses: github/codeql-action/init@959cbb7472c4d4ad70cdfe6f4976053fe48ab394 # v2 with: languages: ${{ matrix.language }} queries: +security-extended - name: Autobuild - uses: github/codeql-action/autobuild@a669cc5936cc5e1b6a362ec1ff9e410dc570d190 # v2 + uses: github/codeql-action/autobuild@959cbb7472c4d4ad70cdfe6f4976053fe48ab394 # v2 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@a669cc5936cc5e1b6a362ec1ff9e410dc570d190 # v2 + uses: github/codeql-action/analyze@959cbb7472c4d4ad70cdfe6f4976053fe48ab394 # v2 From 02f9e60e7c497e033f91905bc48fe339eb6f5bb4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 15 Dec 2022 20:13:11 -0700 Subject: [PATCH 27/35] chore(deps): update dotnet monorepo to v7.0.1 (#8900) * chore(deps): update dotnet monorepo to v7.0.1 * Update docker sdk Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Cody Robibero --- .../Emby.Server.Implementations.csproj | 2 +- Jellyfin.Api/Jellyfin.Api.csproj | 2 +- .../Jellyfin.Server.Implementations.csproj | 8 ++++---- Jellyfin.Server/Jellyfin.Server.csproj | 4 ++-- MediaBrowser.Controller/MediaBrowser.Controller.csproj | 2 +- MediaBrowser.Model/MediaBrowser.Model.csproj | 2 +- deployment/Dockerfile.centos.amd64 | 2 +- deployment/Dockerfile.fedora.amd64 | 2 +- deployment/Dockerfile.ubuntu.amd64 | 2 +- deployment/Dockerfile.ubuntu.arm64 | 2 +- deployment/Dockerfile.ubuntu.armhf | 2 +- 11 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index e3d430c693..f46affc73b 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -29,7 +29,7 @@ - + diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj index 0317a2f2b2..889f7dc9ab 100644 --- a/Jellyfin.Api/Jellyfin.Api.csproj +++ b/Jellyfin.Api/Jellyfin.Api.csproj @@ -17,7 +17,7 @@ - + diff --git a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj index ec08561113..e982906737 100644 --- a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj +++ b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj @@ -28,13 +28,13 @@ - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index 58b4164809..ac20869353 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -37,8 +37,8 @@ - - + + diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 7e3d7a981b..4a66edb16f 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -19,7 +19,7 @@ - + diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index 98179e486b..284e89f1cb 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -40,7 +40,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/deployment/Dockerfile.centos.amd64 b/deployment/Dockerfile.centos.amd64 index 646515ddc2..f7b7e30253 100644 --- a/deployment/Dockerfile.centos.amd64 +++ b/deployment/Dockerfile.centos.amd64 @@ -13,7 +13,7 @@ RUN yum update -yq \ && yum install -yq @buildsys-build rpmdevtools yum-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel git wget # Install DotNET SDK -RUN wget -q https://download.visualstudio.microsoft.com/download/pr/253e5af8-41aa-48c6-86f1-39a51b44afdc/5bb2cb9380c5b1a7f0153e0a2775727b/dotnet-sdk-7.0.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget -q https://download.visualstudio.microsoft.com/download/pr/7fe73a07-575d-4cb4-b2d3-c23d89e5085f/d8b2b7e1c0ed99c1144638d907c6d152/dotnet-sdk-7.0.101-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.fedora.amd64 b/deployment/Dockerfile.fedora.amd64 index 3bbd758c1f..666937e5ca 100644 --- a/deployment/Dockerfile.fedora.amd64 +++ b/deployment/Dockerfile.fedora.amd64 @@ -12,7 +12,7 @@ RUN dnf update -yq \ && dnf install -yq @buildsys-build rpmdevtools git dnf-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel systemd wget make # Install DotNET SDK -RUN wget -q https://download.visualstudio.microsoft.com/download/pr/253e5af8-41aa-48c6-86f1-39a51b44afdc/5bb2cb9380c5b1a7f0153e0a2775727b/dotnet-sdk-7.0.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget -q https://download.visualstudio.microsoft.com/download/pr/7fe73a07-575d-4cb4-b2d3-c23d89e5085f/d8b2b7e1c0ed99c1144638d907c6d152/dotnet-sdk-7.0.101-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.ubuntu.amd64 b/deployment/Dockerfile.ubuntu.amd64 index f4787c693d..0ad0132ccf 100644 --- a/deployment/Dockerfile.ubuntu.amd64 +++ b/deployment/Dockerfile.ubuntu.amd64 @@ -17,7 +17,7 @@ RUN apt-get update -yqq \ libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 # Install dotnet repository -RUN wget -q https://download.visualstudio.microsoft.com/download/pr/253e5af8-41aa-48c6-86f1-39a51b44afdc/5bb2cb9380c5b1a7f0153e0a2775727b/dotnet-sdk-7.0.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget -q https://download.visualstudio.microsoft.com/download/pr/7fe73a07-575d-4cb4-b2d3-c23d89e5085f/d8b2b7e1c0ed99c1144638d907c6d152/dotnet-sdk-7.0.101-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.ubuntu.arm64 b/deployment/Dockerfile.ubuntu.arm64 index 1cf81c60ad..4f7ac20999 100644 --- a/deployment/Dockerfile.ubuntu.arm64 +++ b/deployment/Dockerfile.ubuntu.arm64 @@ -16,7 +16,7 @@ RUN apt-get update -yqq \ mmv build-essential lsb-release # Install dotnet repository -RUN wget -q https://download.visualstudio.microsoft.com/download/pr/253e5af8-41aa-48c6-86f1-39a51b44afdc/5bb2cb9380c5b1a7f0153e0a2775727b/dotnet-sdk-7.0.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget -q https://download.visualstudio.microsoft.com/download/pr/7fe73a07-575d-4cb4-b2d3-c23d89e5085f/d8b2b7e1c0ed99c1144638d907c6d152/dotnet-sdk-7.0.101-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.ubuntu.armhf b/deployment/Dockerfile.ubuntu.armhf index 54b8949150..af439e6eb7 100644 --- a/deployment/Dockerfile.ubuntu.armhf +++ b/deployment/Dockerfile.ubuntu.armhf @@ -16,7 +16,7 @@ RUN apt-get update -yqq \ mmv build-essential lsb-release # Install dotnet repository -RUN wget -q https://download.visualstudio.microsoft.com/download/pr/253e5af8-41aa-48c6-86f1-39a51b44afdc/5bb2cb9380c5b1a7f0153e0a2775727b/dotnet-sdk-7.0.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget -q https://download.visualstudio.microsoft.com/download/pr/7fe73a07-575d-4cb4-b2d3-c23d89e5085f/d8b2b7e1c0ed99c1144638d907c6d152/dotnet-sdk-7.0.101-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet From 411246e90f19f652168878e0edc703fdc9ff32d9 Mon Sep 17 00:00:00 2001 From: Bas Date: Fri, 16 Dec 2022 10:57:20 +0000 Subject: [PATCH 28/35] Translated using Weblate (Dutch) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/nl/ --- .../Localization/Core/nl.json | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/nl.json b/Emby.Server.Implementations/Localization/Core/nl.json index c05114f013..e03747cbec 100644 --- a/Emby.Server.Implementations/Localization/Core/nl.json +++ b/Emby.Server.Implementations/Localization/Core/nl.json @@ -92,37 +92,37 @@ "ValueHasBeenAddedToLibrary": "{0} is toegevoegd aan je mediabibliotheek", "ValueSpecialEpisodeName": "Speciaal - {0}", "VersionNumber": "Versie {0}", - "TaskDownloadMissingSubtitlesDescription": "Zoekt op het internet naar missende ondertiteling gebaseerd op metadata configuratie.", - "TaskDownloadMissingSubtitles": "Download missende ondertiteling", + "TaskDownloadMissingSubtitlesDescription": "Zoekt op het internet naar ontbrekende ondertiteling gebaseerd op metadataconfiguratie.", + "TaskDownloadMissingSubtitles": "Ontbrekende ondertiteling downloaden", "TaskRefreshChannelsDescription": "Vernieuwt informatie van internet kanalen.", "TaskRefreshChannels": "Vernieuw Kanalen", "TaskCleanTranscodeDescription": "Verwijdert transcode bestanden ouder dan 1 dag.", - "TaskCleanLogs": "Log Folder Opschonen", - "TaskCleanTranscode": "Transcode Folder Opschonen", - "TaskUpdatePluginsDescription": "Download en installeert updates voor plugins waar automatisch updaten aan staat.", - "TaskUpdatePlugins": "Update Plugins", + "TaskCleanLogs": "Logboekmap opschonen", + "TaskCleanTranscode": "Transcoderingsmap opschonen", + "TaskUpdatePluginsDescription": "Downloadt en installeert updates van plug-ins waarvoor automatisch bijwerken is ingeschakeld.", + "TaskUpdatePlugins": "Plug-ins bijwerken", "TaskRefreshPeopleDescription": "Update metadata for acteurs en regisseurs in de media bibliotheek.", - "TaskRefreshPeople": "Vernieuw Personen", + "TaskRefreshPeople": "Personen vernieuwen", "TaskCleanLogsDescription": "Verwijdert log bestanden ouder dan {0} dagen.", - "TaskRefreshLibraryDescription": "Scant de media bibliotheek voor nieuwe bestanden en vernieuwt de metadata.", - "TaskRefreshLibrary": "Scan Media Bibliotheek", + "TaskRefreshLibraryDescription": "Scant de mediabibliotheek op nieuwe bestanden en vernieuwt de metadata.", + "TaskRefreshLibrary": "Mediabibliotheek scannen", "TaskRefreshChapterImagesDescription": "Maakt thumbnails aan voor videos met hoofdstukken.", - "TaskRefreshChapterImages": "Hoofdstukafbeeldingen Uitpakken", + "TaskRefreshChapterImages": "Hoofdstukafbeeldingen uitpakken", "TaskCleanCacheDescription": "Verwijdert gecachte bestanden die het systeem niet langer nodig heeft.", - "TaskCleanCache": "Cache Folder Opschonen", + "TaskCleanCache": "Cache-map opschonen", "TasksChannelsCategory": "Internet Kanalen", - "TasksApplicationCategory": "Applicatie", + "TasksApplicationCategory": "Toepassing", "TasksLibraryCategory": "Bibliotheek", "TasksMaintenanceCategory": "Onderhoud", "TaskCleanActivityLogDescription": "Verwijdert activiteiten logs ouder dan de ingestelde tijd.", - "TaskCleanActivityLog": "Leeg activiteiten logboek", + "TaskCleanActivityLog": "Activiteitenlogboek legen", "Undefined": "Niet gedefinieerd", "Forced": "Geforceerd", "Default": "Standaard", "TaskOptimizeDatabaseDescription": "Comprimeert de database en trimt vrije ruimte. Het uitvoeren van deze taak kan de prestaties verbeteren, na het scannen van de bibliotheek of andere aanpassingen die invloed hebben op de database.", "TaskOptimizeDatabase": "Database optimaliseren", - "TaskKeyframeExtractorDescription": "Haalt keyframes uit videobestanden om preciezere HLS afspeellijsten te maken. Dit kan lang duren.", - "TaskKeyframeExtractor": "Keyframe Extractor", + "TaskKeyframeExtractorDescription": "Haalt keyframes uit videobestanden om preciezere HLS-afspeellijsten te maken. Deze taak kan lang duren.", + "TaskKeyframeExtractor": "Keyframe-uitpakker", "External": "Extern", "HearingImpaired": "Slechthorend" } From 6481376b81c52af0b29eea997ce44de0de862ac8 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Mon, 19 Dec 2022 15:21:42 +0100 Subject: [PATCH 29/35] Use DistinctBy introduced in .NET 6 --- Emby.Server.Implementations/Dto/DtoService.cs | 3 +-- .../EntryPoints/LibraryChangedNotifier.cs | 9 +++------ .../EntryPoints/UserDataChangeNotifier.cs | 3 +-- Emby.Server.Implementations/IO/FileRefresher.cs | 3 +-- .../Images/DynamicImageProvider.cs | 3 +-- .../Images/PlaylistImageProvider.cs | 3 +-- .../LiveTv/LiveTvManager.cs | 3 +-- Jellyfin.Api/Controllers/LibraryController.cs | 15 +++++---------- Jellyfin.Api/Controllers/MoviesController.cs | 6 ++---- .../Entities/CollectionFolder.cs | 3 +-- MediaBrowser.Controller/Entities/TV/Series.cs | 2 +- MediaBrowser.Controller/Library/NameExtensions.cs | 3 +-- 12 files changed, 19 insertions(+), 37 deletions(-) diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index 0c6c31982e..5103b1fbfb 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -574,8 +574,7 @@ namespace Emby.Server.Implementations.Dto .Where(i => user is null ? true : i.IsVisible(user)) - .GroupBy(i => i.Name, StringComparer.OrdinalIgnoreCase) - .Select(x => x.First()) + .DistinctBy(x => x.Name, StringComparer.OrdinalIgnoreCase) .ToDictionary(i => i.Name, StringComparer.OrdinalIgnoreCase); for (var i = 0; i < people.Count; i++) diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs index 25a7029c90..05d0a9b794 100644 --- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs @@ -282,19 +282,16 @@ namespace Emby.Server.Implementations.EntryPoints { // Remove dupes in case some were saved multiple times var foldersAddedTo = _foldersAddedTo - .GroupBy(x => x.Id) - .Select(x => x.First()) + .DistinctBy(x => x.Id) .ToList(); var foldersRemovedFrom = _foldersRemovedFrom - .GroupBy(x => x.Id) - .Select(x => x.First()) + .DistinctBy(x => x.Id) .ToList(); var itemsUpdated = _itemsUpdated .Where(i => !_itemsAdded.Contains(i)) - .GroupBy(x => x.Id) - .Select(x => x.First()) + .DistinctBy(x => x.Id) .ToList(); SendChangeNotifications(_itemsAdded.ToList(), itemsUpdated, _itemsRemoved.ToList(), foldersAddedTo, foldersRemovedFrom, CancellationToken.None).GetAwaiter().GetResult(); diff --git a/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs b/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs index 42c8f24a14..e724618b3a 100644 --- a/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs @@ -123,8 +123,7 @@ namespace Emby.Server.Implementations.EntryPoints var user = _userManager.GetUserById(userId); var dtoList = changedItems - .GroupBy(x => x.Id) - .Select(x => x.First()) + .DistinctBy(x => x.Id) .Select(i => { var dto = _userDataManager.GetUserDataDto(i, user); diff --git a/Emby.Server.Implementations/IO/FileRefresher.cs b/Emby.Server.Implementations/IO/FileRefresher.cs index ec8590929e..0ad81b653c 100644 --- a/Emby.Server.Implementations/IO/FileRefresher.cs +++ b/Emby.Server.Implementations/IO/FileRefresher.cs @@ -133,8 +133,7 @@ namespace Emby.Server.Implementations.IO .Distinct(StringComparer.OrdinalIgnoreCase) .Select(GetAffectedBaseItem) .Where(item => item is not null) - .GroupBy(x => x!.Id) // Removed null values in the previous .Where() - .Select(x => x.First())!; + .DistinctBy(x => x!.Id)!; // Removed null values in the previous .Where() foreach (var item in itemsToRefresh) { diff --git a/Emby.Server.Implementations/Images/DynamicImageProvider.cs b/Emby.Server.Implementations/Images/DynamicImageProvider.cs index 82690f8a95..0bd5fdce0a 100644 --- a/Emby.Server.Implementations/Images/DynamicImageProvider.cs +++ b/Emby.Server.Implementations/Images/DynamicImageProvider.cs @@ -81,8 +81,7 @@ namespace Emby.Server.Implementations.Images } return i; - }).GroupBy(x => x.Id) - .Select(x => x.First()); + }).DistinctBy(x => x.Id); List returnItems; if (isUsingCollectionStrip) diff --git a/Emby.Server.Implementations/Images/PlaylistImageProvider.cs b/Emby.Server.Implementations/Images/PlaylistImageProvider.cs index 5801512873..3326d21ac9 100644 --- a/Emby.Server.Implementations/Images/PlaylistImageProvider.cs +++ b/Emby.Server.Implementations/Images/PlaylistImageProvider.cs @@ -58,8 +58,7 @@ namespace Emby.Server.Implementations.Images return null; }) .Where(i => i is not null) - .GroupBy(x => x.Id) - .Select(x => x.First()) + .DistinctBy(x => x.Id) .ToList(); } } diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs index 7afc7959c1..4003468d0d 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs @@ -2392,8 +2392,7 @@ namespace Emby.Server.Implementations.LiveTv .Select(i => _libraryManager.FindByPath(i, true)) .Where(i => i is not null && i.IsVisibleStandalone(user)) .SelectMany(i => _libraryManager.GetCollectionFolders(i)) - .GroupBy(x => x.Id) - .Select(x => x.First()) + .DistinctBy(x => x.Id) .OrderBy(i => i.SortName) .ToList(); diff --git a/Jellyfin.Api/Controllers/LibraryController.cs b/Jellyfin.Api/Controllers/LibraryController.cs index ab20208302..196d509fbc 100644 --- a/Jellyfin.Api/Controllers/LibraryController.cs +++ b/Jellyfin.Api/Controllers/LibraryController.cs @@ -770,8 +770,7 @@ namespace Jellyfin.Api.Controllers Name = i.Name, DefaultEnabled = IsSaverEnabledByDefault(i.Name, types, isNewLibrary) }) - .GroupBy(i => i.Name, StringComparer.OrdinalIgnoreCase) - .Select(x => x.First()) + .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase) .ToArray(); result.MetadataReaders = plugins @@ -781,8 +780,7 @@ namespace Jellyfin.Api.Controllers Name = i.Name, DefaultEnabled = true }) - .GroupBy(i => i.Name, StringComparer.OrdinalIgnoreCase) - .Select(x => x.First()) + .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase) .ToArray(); result.SubtitleFetchers = plugins @@ -792,8 +790,7 @@ namespace Jellyfin.Api.Controllers Name = i.Name, DefaultEnabled = true }) - .GroupBy(i => i.Name, StringComparer.OrdinalIgnoreCase) - .Select(x => x.First()) + .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase) .ToArray(); var typeOptions = new List(); @@ -814,8 +811,7 @@ namespace Jellyfin.Api.Controllers Name = i.Name, DefaultEnabled = IsMetadataFetcherEnabledByDefault(i.Name, type, isNewLibrary) }) - .GroupBy(i => i.Name, StringComparer.OrdinalIgnoreCase) - .Select(x => x.First()) + .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase) .ToArray(), ImageFetchers = plugins @@ -826,8 +822,7 @@ namespace Jellyfin.Api.Controllers Name = i.Name, DefaultEnabled = IsImageFetcherEnabledByDefault(i.Name, type, isNewLibrary) }) - .GroupBy(i => i.Name, StringComparer.OrdinalIgnoreCase) - .Select(x => x.First()) + .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase) .ToArray(), SupportedImageTypes = plugins diff --git a/Jellyfin.Api/Controllers/MoviesController.cs b/Jellyfin.Api/Controllers/MoviesController.cs index 03f864b4a7..3cf079362b 100644 --- a/Jellyfin.Api/Controllers/MoviesController.cs +++ b/Jellyfin.Api/Controllers/MoviesController.cs @@ -200,8 +200,7 @@ namespace Jellyfin.Api.Controllers IsMovie = true, EnableGroupByMetadataKey = true, DtoOptions = dtoOptions - }).GroupBy(i => i.GetProviderId(MediaBrowser.Model.Entities.MetadataProvider.Imdb) ?? Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture)) - .Select(x => x.First()) + }).DistinctBy(i => i.GetProviderId(MediaBrowser.Model.Entities.MetadataProvider.Imdb) ?? Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture)) .Take(itemLimit) .ToList(); @@ -240,8 +239,7 @@ namespace Jellyfin.Api.Controllers IsMovie = true, EnableGroupByMetadataKey = true, DtoOptions = dtoOptions - }).GroupBy(i => i.GetProviderId(MediaBrowser.Model.Entities.MetadataProvider.Imdb) ?? Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture)) - .Select(x => x.First()) + }).DistinctBy(i => i.GetProviderId(MediaBrowser.Model.Entities.MetadataProvider.Imdb) ?? Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture)) .Take(itemLimit) .ToList(); diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs index 7dc7f774d0..5ac619d8f5 100644 --- a/MediaBrowser.Controller/Entities/CollectionFolder.cs +++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs @@ -355,8 +355,7 @@ namespace MediaBrowser.Controller.Entities return PhysicalLocations .Where(i => !FileSystem.AreEqual(i, Path)) .SelectMany(i => GetPhysicalParents(i, rootChildren)) - .GroupBy(x => x.Id) - .Select(x => x.First()); + .DistinctBy(x => x.Id); } private IEnumerable GetPhysicalParents(string path, List rootChildren) diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs index 02312757ce..e7a8a773ec 100644 --- a/MediaBrowser.Controller/Entities/TV/Series.cs +++ b/MediaBrowser.Controller/Entities/TV/Series.cs @@ -283,7 +283,7 @@ namespace MediaBrowser.Controller.Entities.TV // This depends on settings for that series // When this happens, remove the duplicate from season 0 - return allEpisodes.GroupBy(i => i.Id).Select(x => x.First()).Reverse(); + return allEpisodes.DistinctBy(i => i.Id).Reverse(); } public async Task RefreshAllMetadata(MetadataRefreshOptions refreshOptions, IProgress progress, CancellationToken cancellationToken) diff --git a/MediaBrowser.Controller/Library/NameExtensions.cs b/MediaBrowser.Controller/Library/NameExtensions.cs index 919570e893..ee37fb2dc2 100644 --- a/MediaBrowser.Controller/Library/NameExtensions.cs +++ b/MediaBrowser.Controller/Library/NameExtensions.cs @@ -10,8 +10,7 @@ namespace MediaBrowser.Controller.Library public static class NameExtensions { public static IEnumerable DistinctNames(this IEnumerable names) - => names.GroupBy(RemoveDiacritics, StringComparer.OrdinalIgnoreCase) - .Select(x => x.First()); + => names.DistinctBy(RemoveDiacritics, StringComparer.OrdinalIgnoreCase); private static string RemoveDiacritics(string? name) { From e13cfe095ffaa588d3fb3a85efdbcd72592bf6ed Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Mon, 19 Dec 2022 09:31:53 -0500 Subject: [PATCH 30/35] Correct systemd dynamic directory Fixes #8921 --- debian/postinst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/postinst b/debian/postinst index 2f9c4cffbf..47173855f7 100644 --- a/debian/postinst +++ b/debian/postinst @@ -83,7 +83,7 @@ fi # End automatically added section # Automatically added by dh_installinit if [[ "$1" == "configure" ]] || [[ "$1" == "abort-upgrade" ]]; then - if [[ -d "/run/systemd/systemd" ]]; then + if [[ -d "/run/systemd/system" ]]; then systemctl --system daemon-reload >/dev/null || true deb-systemd-invoke start jellyfin >/dev/null || true elif [[ -x "/etc/init.d/jellyfin" ]] || [[ -e "/etc/init/jellyfin.conf" ]]; then From 497d8c495797e1bb37fbd92e7298439c1921a5fa Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Mon, 19 Dec 2022 17:30:00 +0100 Subject: [PATCH 31/35] Use Order() introduced in .NET 7 (#8923) --- Emby.Server.Implementations/IO/LibraryMonitor.cs | 2 +- Emby.Server.Implementations/Library/LibraryManager.cs | 2 +- .../Library/Resolvers/Movies/MovieResolver.cs | 2 +- Jellyfin.Api/Controllers/FilterController.cs | 8 ++++---- MediaBrowser.Providers/Manager/MetadataService.cs | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Emby.Server.Implementations/IO/LibraryMonitor.cs b/Emby.Server.Implementations/IO/LibraryMonitor.cs index 4b999d40b8..f67a02be83 100644 --- a/Emby.Server.Implementations/IO/LibraryMonitor.cs +++ b/Emby.Server.Implementations/IO/LibraryMonitor.cs @@ -131,7 +131,7 @@ namespace Emby.Server.Implementations.IO .OfType() .SelectMany(f => f.PhysicalLocations) .Distinct(StringComparer.OrdinalIgnoreCase) - .OrderBy(i => i); + .Order(); foreach (var path in paths) { diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index bf12e4e0aa..a3c66dc798 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -1175,7 +1175,7 @@ namespace Emby.Server.Implementations.Library } }) .Where(i => i is not null) - .OrderBy(i => i) + .Order() .ToArray(), CollectionType = GetCollectionType(dir) diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs index 5f1a3ec6de..1522cd3aef 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs @@ -529,7 +529,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies } return false; - }).OrderBy(i => i).ToList(); + }).Order().ToList(); // If different video types were found, don't allow this if (videoTypes.Distinct().Count() > 1) diff --git a/Jellyfin.Api/Controllers/FilterController.cs b/Jellyfin.Api/Controllers/FilterController.cs index b6780ee200..17d136384e 100644 --- a/Jellyfin.Api/Controllers/FilterController.cs +++ b/Jellyfin.Api/Controllers/FilterController.cs @@ -92,25 +92,25 @@ namespace Jellyfin.Api.Controllers Years = itemList.Select(i => i.ProductionYear ?? -1) .Where(i => i > 0) .Distinct() - .OrderBy(i => i) + .Order() .ToArray(), Genres = itemList.SelectMany(i => i.Genres) .DistinctNames() - .OrderBy(i => i) + .Order() .ToArray(), Tags = itemList .SelectMany(i => i.Tags) .Distinct(StringComparer.OrdinalIgnoreCase) - .OrderBy(i => i) + .Order() .ToArray(), OfficialRatings = itemList .Select(i => i.OfficialRating) .Where(i => !string.IsNullOrWhiteSpace(i)) .Distinct(StringComparer.OrdinalIgnoreCase) - .OrderBy(i => i) + .Order() .ToArray() }; } diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs index 5b5ca0fca1..ff06c7ce4d 100644 --- a/MediaBrowser.Providers/Manager/MetadataService.cs +++ b/MediaBrowser.Providers/Manager/MetadataService.cs @@ -465,7 +465,7 @@ namespace MediaBrowser.Providers.Manager .Distinct(StringComparer.OrdinalIgnoreCase) .ToArray(); - if (currentList.Length != item.Genres.Length || !currentList.OrderBy(i => i).SequenceEqual(item.Genres.OrderBy(i => i), StringComparer.OrdinalIgnoreCase)) + if (currentList.Length != item.Genres.Length || !currentList.Order().SequenceEqual(item.Genres.Order(), StringComparer.OrdinalIgnoreCase)) { updateType |= ItemUpdateType.MetadataEdit; } @@ -486,7 +486,7 @@ namespace MediaBrowser.Providers.Manager .Distinct(StringComparer.OrdinalIgnoreCase) .ToArray(); - if (currentList.Length != item.Studios.Length || !currentList.OrderBy(i => i).SequenceEqual(item.Studios.OrderBy(i => i), StringComparer.OrdinalIgnoreCase)) + if (currentList.Length != item.Studios.Length || !currentList.Order().SequenceEqual(item.Studios.Order(), StringComparer.OrdinalIgnoreCase)) { updateType |= ItemUpdateType.MetadataEdit; } From f20dee8e0d1160ff9e3fac127ebba66e6ecac5da Mon Sep 17 00:00:00 2001 From: bradvolen Date: Mon, 19 Dec 2022 10:52:09 -0800 Subject: [PATCH 32/35] Adding "creation_time" as a tag lookup for FFProbe for premiere date (#8884) --- MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index 7a3462b970..18e248a1bd 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -144,7 +144,8 @@ namespace MediaBrowser.MediaEncoding.Probing FFProbeHelpers.GetDictionaryDateTime(tags, "retail date") ?? FFProbeHelpers.GetDictionaryDateTime(tags, "retail_date") ?? FFProbeHelpers.GetDictionaryDateTime(tags, "date_released") ?? - FFProbeHelpers.GetDictionaryDateTime(tags, "date"); + FFProbeHelpers.GetDictionaryDateTime(tags, "date") ?? + FFProbeHelpers.GetDictionaryDateTime(tags, "creation_time"); // Set common metadata for music (audio) and music videos (video) info.Album = tags.GetValueOrDefault("album"); From 7a3a9fb7e470c80b5ca1aecdb7da9c2f0fe6d8ce Mon Sep 17 00:00:00 2001 From: Niels van Velzen Date: Mon, 19 Dec 2022 21:44:54 +0100 Subject: [PATCH 33/35] Remove deprecated Password field from AuthenticateUserByName --- Jellyfin.Api/Models/UserDtos/AuthenticateUserByName.cs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/Jellyfin.Api/Models/UserDtos/AuthenticateUserByName.cs b/Jellyfin.Api/Models/UserDtos/AuthenticateUserByName.cs index 41f7b169eb..31208264fb 100644 --- a/Jellyfin.Api/Models/UserDtos/AuthenticateUserByName.cs +++ b/Jellyfin.Api/Models/UserDtos/AuthenticateUserByName.cs @@ -1,6 +1,4 @@ -using System; - -namespace Jellyfin.Api.Models.UserDtos +namespace Jellyfin.Api.Models.UserDtos { /// /// The authenticate user by name request body. @@ -16,11 +14,5 @@ namespace Jellyfin.Api.Models.UserDtos /// Gets or sets the plain text password. /// public string? Pw { get; set; } - - /// - /// Gets or sets the sha1-hashed password. - /// - [Obsolete("Send password using pw field")] - public string? Password { get; set; } } } From 479d477e01039e95f4647e5b85f2bcd5ec560e81 Mon Sep 17 00:00:00 2001 From: Niels van Velzen Date: Mon, 19 Dec 2022 21:46:12 +0100 Subject: [PATCH 34/35] Deprecate user id based authentication endpoint --- Jellyfin.Api/Controllers/UserController.cs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs index 002327d741..568224a424 100644 --- a/Jellyfin.Api/Controllers/UserController.cs +++ b/Jellyfin.Api/Controllers/UserController.cs @@ -157,7 +157,6 @@ namespace Jellyfin.Api.Controllers /// /// The user id. /// The password as plain text. - /// The password sha1-hash. /// User authenticated. /// Sha1-hashed password only is not allowed. /// User not found. @@ -166,10 +165,10 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] + [Obsolete("Authenticate with username instead")] public async Task> AuthenticateUser( [FromRoute, Required] Guid userId, - [FromQuery, Required] string pw, - [FromQuery] string? password) + [FromQuery, Required] string pw) { var user = _userManager.GetUserById(userId); @@ -178,11 +177,6 @@ namespace Jellyfin.Api.Controllers return NotFound("User not found"); } - if (!string.IsNullOrEmpty(password) && string.IsNullOrEmpty(pw)) - { - return StatusCode(StatusCodes.Status403Forbidden, "Only sha1 password is not allowed."); - } - AuthenticateUserByName request = new AuthenticateUserByName { Username = user.Username, From 180d17c991d58757e6b96e66f7ff68aedd55b463 Mon Sep 17 00:00:00 2001 From: Niels van Velzen Date: Tue, 20 Dec 2022 02:44:13 +0100 Subject: [PATCH 35/35] Remove unused ImageByName API (#8928) --- .../ServerApplicationPaths.cs | 18 -- .../Controllers/ImageByNameController.cs | 252 ------------------ .../IServerApplicationPaths.cs | 18 -- 3 files changed, 288 deletions(-) delete mode 100644 Jellyfin.Api/Controllers/ImageByNameController.cs diff --git a/Emby.Server.Implementations/ServerApplicationPaths.cs b/Emby.Server.Implementations/ServerApplicationPaths.cs index 369a2b0d88..725df98da5 100644 --- a/Emby.Server.Implementations/ServerApplicationPaths.cs +++ b/Emby.Server.Implementations/ServerApplicationPaths.cs @@ -82,24 +82,6 @@ namespace Emby.Server.Implementations /// The year path. public string YearPath => Path.Combine(InternalMetadataPath, "Year"); - /// - /// Gets the path to the General IBN directory. - /// - /// The general path. - public string GeneralPath => Path.Combine(InternalMetadataPath, "general"); - - /// - /// Gets the path to the Ratings IBN directory. - /// - /// The ratings path. - public string RatingsPath => Path.Combine(InternalMetadataPath, "ratings"); - - /// - /// Gets the media info images path. - /// - /// The media info images path. - public string MediaInfoImagesPath => Path.Combine(InternalMetadataPath, "mediainfo"); - /// /// Gets the path to the user configuration directory. /// diff --git a/Jellyfin.Api/Controllers/ImageByNameController.cs b/Jellyfin.Api/Controllers/ImageByNameController.cs deleted file mode 100644 index c54851b96e..0000000000 --- a/Jellyfin.Api/Controllers/ImageByNameController.cs +++ /dev/null @@ -1,252 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.IO; -using System.Linq; -using System.Net.Mime; -using Jellyfin.Api.Attributes; -using Jellyfin.Api.Constants; -using MediaBrowser.Controller; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Model.Dto; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Net; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; - -namespace Jellyfin.Api.Controllers -{ - /// - /// Images By Name Controller. - /// - [Route("Images")] - public class ImageByNameController : BaseJellyfinApiController - { - private readonly IServerApplicationPaths _applicationPaths; - private readonly IFileSystem _fileSystem; - - /// - /// Initializes a new instance of the class. - /// - /// Instance of the interface. - /// Instance of the interface. - public ImageByNameController( - IServerConfigurationManager serverConfigurationManager, - IFileSystem fileSystem) - { - _applicationPaths = serverConfigurationManager.ApplicationPaths; - _fileSystem = fileSystem; - } - - /// - /// Get all general images. - /// - /// Retrieved list of images. - /// An containing the list of images. - [HttpGet("General")] - [Authorize(Policy = Policies.DefaultAuthorization)] - [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult> GetGeneralImages() - { - return GetImageList(_applicationPaths.GeneralPath, false); - } - - /// - /// Get General Image. - /// - /// The name of the image. - /// Image Type (primary, backdrop, logo, etc). - /// Image stream retrieved. - /// Image not found. - /// A containing the image contents on success, or a if the image could not be found. - [HttpGet("General/{name}/{type}")] - [AllowAnonymous] - [Produces(MediaTypeNames.Application.Octet)] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - [ProducesImageFile] - public ActionResult GetGeneralImage([FromRoute, Required] string name, [FromRoute, Required] string type) - { - var filename = string.Equals(type, "primary", StringComparison.OrdinalIgnoreCase) - ? "folder" - : type; - - var path = BaseItem.SupportedImageExtensions - .Select(i => Path.GetFullPath(Path.Combine(_applicationPaths.GeneralPath, name, filename + i))) - .FirstOrDefault(System.IO.File.Exists); - - if (path is null) - { - return NotFound(); - } - - if (!path.StartsWith(_applicationPaths.GeneralPath, StringComparison.InvariantCulture)) - { - return BadRequest("Invalid image path."); - } - - var contentType = MimeTypes.GetMimeType(path); - return File(AsyncFile.OpenRead(path), contentType); - } - - /// - /// Get all general images. - /// - /// Retrieved list of images. - /// An containing the list of images. - [HttpGet("Ratings")] - [Authorize(Policy = Policies.DefaultAuthorization)] - [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult> GetRatingImages() - { - return GetImageList(_applicationPaths.RatingsPath, false); - } - - /// - /// Get rating image. - /// - /// The theme to get the image from. - /// The name of the image. - /// Image stream retrieved. - /// Image not found. - /// A containing the image contents on success, or a if the image could not be found. - [HttpGet("Ratings/{theme}/{name}")] - [AllowAnonymous] - [Produces(MediaTypeNames.Application.Octet)] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - [ProducesImageFile] - public ActionResult GetRatingImage( - [FromRoute, Required] string theme, - [FromRoute, Required] string name) - { - return GetImageFile(_applicationPaths.RatingsPath, theme, name); - } - - /// - /// Get all media info images. - /// - /// Image list retrieved. - /// An containing the list of images. - [HttpGet("MediaInfo")] - [Authorize(Policy = Policies.DefaultAuthorization)] - [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult> GetMediaInfoImages() - { - return GetImageList(_applicationPaths.MediaInfoImagesPath, false); - } - - /// - /// Get media info image. - /// - /// The theme to get the image from. - /// The name of the image. - /// Image stream retrieved. - /// Image not found. - /// A containing the image contents on success, or a if the image could not be found. - [HttpGet("MediaInfo/{theme}/{name}")] - [AllowAnonymous] - [Produces(MediaTypeNames.Application.Octet)] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - [ProducesImageFile] - public ActionResult GetMediaInfoImage( - [FromRoute, Required] string theme, - [FromRoute, Required] string name) - { - return GetImageFile(_applicationPaths.MediaInfoImagesPath, theme, name); - } - - /// - /// Internal FileHelper. - /// - /// Path to begin search. - /// Theme to search. - /// File name to search for. - /// A containing the image contents on success, or a if the image could not be found. - private ActionResult GetImageFile(string basePath, string theme, string? name) - { - var themeFolder = Path.GetFullPath(Path.Combine(basePath, theme)); - - if (Directory.Exists(themeFolder)) - { - var path = BaseItem.SupportedImageExtensions.Select(i => Path.Combine(themeFolder, name + i)) - .FirstOrDefault(System.IO.File.Exists); - - if (!string.IsNullOrEmpty(path) && System.IO.File.Exists(path)) - { - if (!path.StartsWith(basePath, StringComparison.InvariantCulture)) - { - return BadRequest("Invalid image path."); - } - - var contentType = MimeTypes.GetMimeType(path); - - return PhysicalFile(path, contentType); - } - } - - var allFolder = Path.GetFullPath(Path.Combine(basePath, "all")); - if (Directory.Exists(allFolder)) - { - var path = BaseItem.SupportedImageExtensions.Select(i => Path.Combine(allFolder, name + i)) - .FirstOrDefault(System.IO.File.Exists); - - if (!string.IsNullOrEmpty(path) && System.IO.File.Exists(path)) - { - if (!path.StartsWith(basePath, StringComparison.InvariantCulture)) - { - return BadRequest("Invalid image path."); - } - - var contentType = MimeTypes.GetMimeType(path); - return PhysicalFile(path, contentType); - } - } - - return NotFound(); - } - - private List GetImageList(string path, bool supportsThemes) - { - try - { - return _fileSystem.GetFiles(path, BaseItem.SupportedImageExtensions, false, true) - .Select(i => new ImageByNameInfo - { - Name = _fileSystem.GetFileNameWithoutExtension(i), - FileLength = i.Length, - - // For themeable images, use the Theme property - // For general images, the same object structure is fine, - // but it's not owned by a theme, so call it Context - Theme = supportsThemes ? GetThemeName(i.FullName, path) : null, - Context = supportsThemes ? null : GetThemeName(i.FullName, path), - Format = i.Extension.ToLowerInvariant().TrimStart('.') - }) - .OrderBy(i => i.Name) - .ToList(); - } - catch (IOException) - { - return new List(); - } - } - - private string? GetThemeName(string path, string rootImagePath) - { - var parentName = Path.GetDirectoryName(path); - - if (string.Equals(parentName, rootImagePath, StringComparison.OrdinalIgnoreCase)) - { - return null; - } - - parentName = Path.GetFileName(parentName); - - return string.Equals(parentName, "all", StringComparison.OrdinalIgnoreCase) ? null : parentName; - } - } -} diff --git a/MediaBrowser.Controller/IServerApplicationPaths.cs b/MediaBrowser.Controller/IServerApplicationPaths.cs index 1890dbb360..608286cd8a 100644 --- a/MediaBrowser.Controller/IServerApplicationPaths.cs +++ b/MediaBrowser.Controller/IServerApplicationPaths.cs @@ -50,24 +50,6 @@ namespace MediaBrowser.Controller /// The year path. string YearPath { get; } - /// - /// Gets the path to the General IBN directory. - /// - /// The general path. - string GeneralPath { get; } - - /// - /// Gets the path to the Ratings IBN directory. - /// - /// The ratings path. - string RatingsPath { get; } - - /// - /// Gets the media info images path. - /// - /// The media info images path. - string MediaInfoImagesPath { get; } - /// /// Gets the path to the user configuration directory. ///